diff --git a/.github/actions/build-jtreg/action.yml b/.github/actions/build-jtreg/action.yml index 3abfa260c1767..0ba9937fb4525 100644 --- a/.github/actions/build-jtreg/action.yml +++ b/.github/actions/build-jtreg/action.yml @@ -65,4 +65,4 @@ runs: with: name: bundles-jtreg-${{ steps.version.outputs.value }} path: jtreg/installed - retention-days: 1 + retention-days: 5 diff --git a/.github/actions/do-build/action.yml b/.github/actions/do-build/action.yml index 79eddf8c70f0e..6f2c2ce02180c 100644 --- a/.github/actions/do-build/action.yml +++ b/.github/actions/do-build/action.yml @@ -1,5 +1,5 @@ # -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -42,7 +42,7 @@ runs: - name: 'Build' id: build run: > - make LOG=info ${{ inputs.make-target }} + make -k LOG=info ${{ inputs.make-target }} || bash ./.github/scripts/gen-build-failure-report.sh "$GITHUB_STEP_SUMMARY" shell: bash diff --git a/.github/actions/get-msys2/action.yml b/.github/actions/get-msys2/action.yml index 843b77ac06456..7ac3172f7089a 100644 --- a/.github/actions/get-msys2/action.yml +++ b/.github/actions/get-msys2/action.yml @@ -30,15 +30,15 @@ runs: using: composite steps: - name: 'Install MSYS2' - uses: msys2/setup-msys2@v2.22.0 + id: msys2 + uses: msys2/setup-msys2@v2.28.0 with: install: 'autoconf tar unzip zip make' path-type: minimal - location: ${{ runner.tool_cache }}/msys2 + release: false # We can't run bash until this is completed, so stick with pwsh - name: 'Set MSYS2 path' run: | - # Prepend msys2/msys64/usr/bin to the PATH - echo "$env:RUNNER_TOOL_CACHE/msys2/msys64/usr/bin" >> $env:GITHUB_PATH + echo "${{ steps.msys2.outputs.msys2-location }}/usr/bin" >> $env:GITHUB_PATH shell: pwsh diff --git a/.github/actions/upload-bundles/action.yml b/.github/actions/upload-bundles/action.yml index b35ee3a42e98c..62a8fa58865c0 100644 --- a/.github/actions/upload-bundles/action.yml +++ b/.github/actions/upload-bundles/action.yml @@ -73,5 +73,5 @@ runs: with: name: bundles-${{ inputs.platform }}${{ inputs.debug-suffix }} path: bundles - retention-days: 1 + retention-days: 5 if: steps.bundles.outputs.bundles-found == 'true' diff --git a/.github/workflows/build-cross-compile.yml b/.github/workflows/build-cross-compile.yml index e79c408b6567b..59efc7586ad49 100644 --- a/.github/workflows/build-cross-compile.yml +++ b/.github/workflows/build-cross-compile.yml @@ -60,33 +60,33 @@ jobs: gnu-arch: aarch64 debian-arch: arm64 debian-repository: https://httpredir.debian.org/debian/ - debian-version: bullseye + debian-version: trixie tolerate-sysroot-errors: false - target-cpu: arm gnu-arch: arm debian-arch: armhf debian-repository: https://httpredir.debian.org/debian/ - debian-version: bullseye + debian-version: trixie tolerate-sysroot-errors: false gnu-abi: eabihf - target-cpu: s390x gnu-arch: s390x debian-arch: s390x debian-repository: https://httpredir.debian.org/debian/ - debian-version: bullseye + debian-version: trixie tolerate-sysroot-errors: false - target-cpu: ppc64le gnu-arch: powerpc64le debian-arch: ppc64el debian-repository: https://httpredir.debian.org/debian/ - debian-version: bullseye + debian-version: trixie tolerate-sysroot-errors: false - target-cpu: riscv64 gnu-arch: riscv64 debian-arch: riscv64 debian-repository: https://httpredir.debian.org/debian/ - debian-version: sid - tolerate-sysroot-errors: true + debian-version: trixie + tolerate-sysroot-errors: false steps: - name: 'Checkout the JDK source' diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 8e2c0c8e63c14..6b8f084394097 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -63,7 +63,7 @@ env: jobs: build-windows: name: build - runs-on: windows-2019 + runs-on: windows-2025 defaults: run: shell: bash @@ -102,7 +102,7 @@ jobs: id: toolchain-check run: | set +e - '/c/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/vc/auxiliary/build/vcvars64.bat' -vcvars_ver=${{ inputs.msvc-toolset-version }} + '/c/Program Files/Microsoft Visual Studio/2022/Enterprise/vc/auxiliary/build/vcvars64.bat' -vcvars_ver=${{ inputs.msvc-toolset-version }} if [ $? -eq 0 ]; then echo "Toolchain is already installed" echo "toolchain-installed=true" >> $GITHUB_OUTPUT @@ -115,7 +115,7 @@ jobs: run: | # Run Visual Studio Installer '/c/Program Files (x86)/Microsoft Visual Studio/Installer/vs_installer.exe' \ - modify --quiet --installPath 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise' \ + modify --quiet --installPath 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise' \ --add Microsoft.VisualStudio.Component.VC.${{ inputs.msvc-toolset-version }}.${{ inputs.msvc-toolset-architecture }} if: steps.toolchain-check.outputs.toolchain-installed != 'true' diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 164b5cf6987bf..8b0757eef9c1b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -235,8 +235,8 @@ jobs: uses: ./.github/workflows/build-macos.yml with: platform: macos-x64 - runs-on: 'macos-13' - xcode-toolset-version: '14.3.1' + runs-on: 'macos-15-intel' + xcode-toolset-version: '16.4' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} if: needs.prepare.outputs.macos-x64 == 'true' @@ -247,8 +247,8 @@ jobs: uses: ./.github/workflows/build-macos.yml with: platform: macos-aarch64 - runs-on: 'macos-14' - xcode-toolset-version: '15.4' + runs-on: 'macos-15' + xcode-toolset-version: '16.4' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} if: needs.prepare.outputs.macos-aarch64 == 'true' @@ -259,7 +259,7 @@ jobs: uses: ./.github/workflows/build-windows.yml with: platform: windows-x64 - msvc-toolset-version: '14.29' + msvc-toolset-version: '14.44' msvc-toolset-architecture: 'x86.x64' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} @@ -271,7 +271,7 @@ jobs: uses: ./.github/workflows/build-windows.yml with: platform: windows-aarch64 - msvc-toolset-version: '14.29' + msvc-toolset-version: '14.44' msvc-toolset-architecture: 'arm64' make-target: 'hotspot' extra-conf-options: '--openjdk-target=aarch64-unknown-cygwin' @@ -301,29 +301,8 @@ jobs: with: platform: linux-x64 bootjdk-platform: linux-x64 - runs-on: ubuntu-20.04 - - test-linux-x86: - name: linux-x86 - needs: - - build-linux-x86 - uses: ./.github/workflows/test.yml - with: - platform: linux-x86 - bootjdk-platform: linux-x64 runs-on: ubuntu-22.04 - test-macos-x64: - name: macos-x64 - needs: - - build-macos-x64 - uses: ./.github/workflows/test.yml - with: - platform: macos-x64 - bootjdk-platform: macos-x64 - runs-on: macos-13 - xcode-toolset-version: '14.3.1' - test-macos-aarch64: name: macos-aarch64 needs: @@ -332,8 +311,8 @@ jobs: with: platform: macos-aarch64 bootjdk-platform: macos-aarch64 - runs-on: macos-14 - xcode-toolset-version: '15.4' + runs-on: macos-15 + xcode-toolset-version: '16.4' test-windows-x64: name: windows-x64 @@ -343,50 +322,4 @@ jobs: with: platform: windows-x64 bootjdk-platform: windows-x64 - runs-on: windows-2019 - - # Remove bundles so they are not misconstrued as binary distributions from the JDK project - remove-bundles: - name: 'Remove bundle artifacts' - runs-on: ubuntu-22.04 - if: always() - needs: - - build-linux-x64 - - build-linux-x86-hs - - build-linux-x64-hs-nopch - - build-linux-x64-hs-zero - - build-linux-x64-hs-minimal - - build-linux-x64-hs-optimized - - build-linux-cross-compile - - build-macos-x64 - - build-macos-aarch64 - - build-windows-x64 - - build-windows-aarch64 - - test-linux-x64 - - test-linux-x64-fips - - test-linux-x86 - - test-macos-x64 - - test-macos-aarch64 - - test-windows-x64 - - steps: - - name: 'Remove bundle artifacts' - run: | - # Find and remove all bundle artifacts - # See: https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28 - ALL_ARTIFACT_IDS="$(curl -sL \ - -H 'Accept: application/vnd.github+json' \ - -H 'Authorization: Bearer ${{ github.token }}' \ - -H 'X-GitHub-Api-Version: 2022-11-28' \ - '${{ github.api_url }}/repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts?per_page=100')" - BUNDLE_ARTIFACT_IDS="$(echo "$ALL_ARTIFACT_IDS" | jq -r -c '.artifacts | map(select(.name|startswith("bundles-"))) | .[].id')" - for id in $BUNDLE_ARTIFACT_IDS; do - echo "Removing $id" - curl -sL \ - -X DELETE \ - -H 'Accept: application/vnd.github+json' \ - -H 'Authorization: Bearer ${{ github.token }}' \ - -H 'X-GitHub-Api-Version: 2022-11-28' \ - "${{ github.api_url }}/repos/${{ github.repository }}/actions/artifacts/$id" \ - || echo "Failed to remove bundle" - done + runs-on: windows-2025 diff --git a/.github/workflows/test-fips.yml b/.github/workflows/test-fips.yml index 0b541ab3ccf71..ebe4bea217763 100644 --- a/.github/workflows/test-fips.yml +++ b/.github/workflows/test-fips.yml @@ -1,5 +1,5 @@ # -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -37,6 +37,9 @@ on: runs-on: required: true type: string + xcode-toolset-version: + required: false + type: string env: # These are needed to make the MSYS2 bash work properly @@ -71,7 +74,7 @@ jobs: steps: - name: 'Checkout the JDK source' - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: 'Get MSYS2' uses: ./.github/actions/get-msys2 @@ -98,7 +101,7 @@ jobs: run: | # On macOS we need to install some dependencies for testing brew install make - sudo xcode-select --switch /Applications/Xcode_11.7.app/Contents/Developer + sudo xcode-select --switch /Applications/Xcode_${{ inputs.xcode-toolset-version }}.app/Contents/Developer # This will make GNU make available as 'make' and not only as 'gmake' echo '/usr/local/opt/make/libexec/gnubin' >> $GITHUB_PATH if: runner.os == 'macOS' @@ -109,9 +112,9 @@ jobs: # We need a minimal PATH on Windows # Set PATH to "", so just GITHUB_PATH is included if [[ '${{ runner.os }}' == 'Windows' ]]; then - echo "::set-output name=value::" + echo "value=" >> $GITHUB_OUTPUT else - echo "::set-output name=value::$PATH" + echo "value=$PATH" >> $GITHUB_OUTPUT fi - name: Turn on system security properties and FIPS mode support @@ -129,7 +132,7 @@ jobs: SYMBOLS_IMAGE_DIR=${{ steps.bundles.outputs.symbols-path }} TEST_IMAGE_DIR=${{ steps.bundles.outputs.tests-path }} JTREG='JAVA_OPTIONS=-XX:-CreateCoredumpOnCrash;VERBOSE=fail,error,time;KEYWORDS=!headful' - && bash ./.github/scripts/gen-test-summary.sh "$GITHUB_STEP_SUMMARY" + && bash ./.github/scripts/gen-test-summary.sh "$GITHUB_STEP_SUMMARY" "$GITHUB_OUTPUT" env: PATH: ${{ steps.path.outputs.value }} @@ -161,12 +164,12 @@ jobs: echo '::warning ::Missing test-support directory' fi - artifact_name="results-${{ inputs.platform }}-$(echo ${{ matrix.test-name }} | tr '/ ' '__')" - echo "::set-output name=artifact-name::$artifact_name" + artifact_name="fips-results-${{ inputs.platform }}-$(echo ${{ matrix.test-name }} | tr '/ ' '__')" + echo "artifact-name=$artifact_name" >> $GITHUB_OUTPUT if: always() - name: 'Upload test results' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: path: results name: ${{ steps.package.outputs.artifact-name }} @@ -174,7 +177,7 @@ jobs: # This is the best way I found to abort the job with an error message - name: 'Notify about test failures' - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: core.setFailed('${{ steps.run-tests.outputs.error-message }}') if: steps.run-tests.outputs.failure == 'true' diff --git a/.jcheck/conf b/.jcheck/conf index 70d144818091a..e9f7f361435c4 100644 --- a/.jcheck/conf +++ b/.jcheck/conf @@ -1,7 +1,7 @@ [general] project=jdk-updates jbs=JDK -version=17.0.15 +version=17.0.18 [checks] error=author,committer,reviewers,merge,issues,executable,symlink,message,hg-tag,whitespace,problemlists diff --git a/make/Bundles.gmk b/make/Bundles.gmk index 9dc5f9602d5fe..904b371c706ce 100644 --- a/make/Bundles.gmk +++ b/make/Bundles.gmk @@ -242,7 +242,10 @@ ifneq ($(filter product-bundles% legacy-bundles, $(MAKECMDGOALS)), ) ) JDK_SYMBOLS_BUNDLE_FILES := \ - $(call FindFiles, $(SYMBOLS_IMAGE_DIR)) + $(filter-out \ + %.stripped.pdb, \ + $(call FindFiles, $(SYMBOLS_IMAGE_DIR)) \ + ) TEST_DEMOS_BUNDLE_FILES := $(filter $(JDK_DEMOS_IMAGE_HOMEDIR)/demo/%, \ $(ALL_JDK_DEMOS_FILES)) diff --git a/make/RunTests.gmk b/make/RunTests.gmk index fc835158ed7ec..468932b35db73 100644 --- a/make/RunTests.gmk +++ b/make/RunTests.gmk @@ -1055,7 +1055,7 @@ UseSpecialTestHandler = \ # Now process each test to run and setup a proper make rule $(foreach test, $(TESTS_TO_RUN), \ $(eval TEST_ID := $(shell $(ECHO) $(strip $(test)) | \ - $(TR) -cs '[a-z][A-Z][0-9]\n' '[_*1000]')) \ + $(TR) -cs '[a-z][A-Z][0-9]\n' '_')) \ $(eval ALL_TEST_IDS += $(TEST_ID)) \ $(if $(call UseCustomTestHandler, $(test)), \ $(eval $(call SetupRunCustomTest, $(TEST_ID), \ @@ -1135,9 +1135,9 @@ run-test-report: post-run-test TEST TOTAL PASS FAIL ERROR " " $(foreach test, $(TESTS_TO_RUN), \ $(eval TEST_ID := $(shell $(ECHO) $(strip $(test)) | \ - $(TR) -cs '[a-z][A-Z][0-9]\n' '[_*1000]')) \ + $(TR) -cs '[a-z][A-Z][0-9]\n' '_')) \ $(ECHO) >> $(TEST_LAST_IDS) $(TEST_ID) $(NEWLINE) \ - $(eval NAME_PATTERN := $(shell $(ECHO) $(test) | $(TR) -c '\n' '[_*1000]')) \ + $(eval NAME_PATTERN := $(shell $(ECHO) $(test) | $(TR) -c '\n' '_')) \ $(if $(filter __________________________________________________%, $(NAME_PATTERN)), \ $(eval TEST_NAME := ) \ $(PRINTF) >> $(TEST_SUMMARY) "%2s %-49s\n" " " "$(test)" $(NEWLINE) \ diff --git a/make/RunTestsPrebuilt.gmk b/make/RunTestsPrebuilt.gmk index 7c1c55b2070c5..79c2828992a99 100644 --- a/make/RunTestsPrebuilt.gmk +++ b/make/RunTestsPrebuilt.gmk @@ -216,9 +216,9 @@ else ifeq ($(OPENJDK_TARGET_OS), macosx) else ifeq ($(OPENJDK_TARGET_OS), windows) NUM_CORES := $(NUMBER_OF_PROCESSORS) MEMORY_SIZE := $(shell \ - $(EXPR) `wmic computersystem get totalphysicalmemory -value \ - | $(GREP) = | $(SED) 's/\\r//g' \ - | $(CUT) -d "=" -f 2-` / 1024 / 1024 \ + $(EXPR) `powershell -Command \ + "(Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory" \ + | $(SED) 's/\\r//g' ` / 1024 / 1024 \ ) endif ifeq ($(NUM_CORES), ) diff --git a/make/autoconf/basic.m4 b/make/autoconf/basic.m4 index 3e5e22b53af43..08a54796641fa 100644 --- a/make/autoconf/basic.m4 +++ b/make/autoconf/basic.m4 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -452,7 +452,9 @@ AC_DEFUN([BASIC_CHECK_DIR_ON_LOCAL_DISK], AC_DEFUN_ONCE([BASIC_CHECK_SRC_PERMS], [ if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then - file_to_test="$TOPDIR/LICENSE" + # The choice of file here is somewhat arbitrary, it just needs to be there + # in the source tree when configure runs + file_to_test="$TOPDIR/Makefile" if test `$STAT -c '%a' "$file_to_test"` -lt 400; then AC_MSG_ERROR([Bad file permissions on src files. This is usually caused by cloning the repositories with a non cygwin hg in a directory not created in cygwin.]) fi diff --git a/make/autoconf/basic_tools.m4 b/make/autoconf/basic_tools.m4 index 8c518d05d41d1..07aa832d1834a 100644 --- a/make/autoconf/basic_tools.m4 +++ b/make/autoconf/basic_tools.m4 @@ -373,7 +373,7 @@ AC_DEFUN_ONCE([BASIC_SETUP_COMPLEX_TOOLS], # Check if it's a GNU date compatible version AC_MSG_CHECKING([if date is a GNU compatible version]) - check_date=`$DATE --version 2>&1 | $GREP "GNU\|BusyBox"` + check_date=`$DATE --version 2>&1 | $GREP "GNU\|BusyBox\|uutils"` if test "x$check_date" != x; then AC_MSG_RESULT([yes]) IS_GNU_DATE=yes diff --git a/make/autoconf/build-performance.m4 b/make/autoconf/build-performance.m4 index 52d143e5ab989..9f3bbaa6f438a 100644 --- a/make/autoconf/build-performance.m4 +++ b/make/autoconf/build-performance.m4 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -85,7 +85,8 @@ AC_DEFUN([BPERF_CHECK_MEMORY_SIZE], FOUND_MEM=yes elif test "x$OPENJDK_BUILD_OS" = xwindows; then # Windows, but without cygwin - MEMORY_SIZE=`wmic computersystem get totalphysicalmemory -value | grep = | cut -d "=" -f 2-` + MEMORY_SIZE=`powershell -Command \ + "(Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory" | $SED 's/\\r//g' ` MEMORY_SIZE=`expr $MEMORY_SIZE / 1024 / 1024` FOUND_MEM=yes fi diff --git a/make/autoconf/flags-cflags.m4 b/make/autoconf/flags-cflags.m4 index f7f2ad53000e5..05b9a774b2882 100644 --- a/make/autoconf/flags-cflags.m4 +++ b/make/autoconf/flags-cflags.m4 @@ -316,7 +316,7 @@ AC_DEFUN([FLAGS_SETUP_OPTIMIZATION], C_O_FLAG_DEBUG="-Od" C_O_FLAG_DEBUG_JVM="" C_O_FLAG_NONE="-Od" - C_O_FLAG_SIZE="-Os" + C_O_FLAG_SIZE="-O1" fi # Now copy to C++ flags @@ -544,8 +544,8 @@ AC_DEFUN([FLAGS_SETUP_CFLAGS_HELPER], TOOLCHAIN_CFLAGS_JVM="-qtbtable=full -qtune=balanced \ -qalias=noansi -qstrict -qtls=default -qnortti -qnoeh -qignerrno -qstackprotect" elif test "x$TOOLCHAIN_TYPE" = xmicrosoft; then - TOOLCHAIN_CFLAGS_JVM="-nologo -MD -MP" - TOOLCHAIN_CFLAGS_JDK="-nologo -MD -Zc:wchar_t-" + TOOLCHAIN_CFLAGS_JVM="-nologo -MD -Zc:strictStrings -MP" + TOOLCHAIN_CFLAGS_JDK="-nologo -MD -Zc:strictStrings -Zc:wchar_t-" fi # CFLAGS C language level for JDK sources (hotspot only uses C++) diff --git a/make/autoconf/flags.m4 b/make/autoconf/flags.m4 index 1dbcbe05ed649..f5e71fa2aff7a 100644 --- a/make/autoconf/flags.m4 +++ b/make/autoconf/flags.m4 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -128,16 +128,12 @@ AC_DEFUN([FLAGS_SETUP_MACOSX_VERSION], # The expected format for is either nn.n.n or nn.nn.nn. See # /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/AvailabilityVersions.h - # MACOSX_VERSION_MIN specifies the lowest version of Macosx that the built + # MACOSX_VERSION_MIN specifies the lowest version of macOS that the built # binaries should be compatible with, even if compiled on a newer version # of the OS. It currently has a hard coded value. Setting this also limits # exposure to API changes in header files. Bumping this is likely to # require code changes to build. - if test "x$OPENJDK_TARGET_CPU_ARCH" = xaarch64; then - MACOSX_VERSION_MIN=11.00.00 - else - MACOSX_VERSION_MIN=10.12.0 - fi + MACOSX_VERSION_MIN=11.00.00 MACOSX_VERSION_MIN_NODOTS=${MACOSX_VERSION_MIN//\./} AC_SUBST(MACOSX_VERSION_MIN) diff --git a/make/autoconf/jdk-options.m4 b/make/autoconf/jdk-options.m4 index e8d8565ff6491..c20e8c7e29127 100644 --- a/make/autoconf/jdk-options.m4 +++ b/make/autoconf/jdk-options.m4 @@ -211,9 +211,8 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_JDK_OPTIONS], # three different page sizes: 4K, 64K, and if run on Mac m1 hardware, 16K. COMPATIBLE_CDS_ALIGNMENT_DEFAULT=false if test "x$OPENJDK_TARGET_OS" = "xlinux" && test "x$OPENJDK_TARGET_CPU" = "xaarch64"; then - COMPATIBLE_CDS_ALIGNMENT_DEFAULT=true + COMPATIBLE_CDS_ALIGNMENT_DEFAULT=auto fi - AC_SUBST(COMPATIBLE_CDS_ALIGNMENT_DEFAULT) # Compress jars COMPRESS_JARS=false @@ -596,7 +595,7 @@ AC_DEFUN([JDKOPT_ENABLE_DISABLE_COMPATIBLE_CDS_ALIGNMENT], UTIL_ARG_ENABLE(NAME: compatible-cds-alignment, DEFAULT: $COMPATIBLE_CDS_ALIGNMENT_DEFAULT, RESULT: ENABLE_COMPATIBLE_CDS_ALIGNMENT, DESC: [enable use alternative compatible cds core region alignment], - DEFAULT_DESC: [disabled], + DEFAULT_DESC: [disabled except on linux-aarch64], CHECKING_MSG: [if compatible cds region alignment enabled], CHECK_AVAILABLE: [ AC_MSG_CHECKING([if CDS archive is available]) diff --git a/make/common/MakeBase.gmk b/make/common/MakeBase.gmk index 2a06d8eb8b3c5..10ad3fa391624 100644 --- a/make/common/MakeBase.gmk +++ b/make/common/MakeBase.gmk @@ -199,7 +199,7 @@ $(eval $(call SetupLogging)) ################################################################################ -MAX_PARAMS := 36 +MAX_PARAMS := 96 PARAM_SEQUENCE := $(call sequence, 2, $(MAX_PARAMS)) # Template for creating a macro taking named parameters. To use it, assign the diff --git a/make/common/NativeCompilation.gmk b/make/common/NativeCompilation.gmk index af601bac1d42b..75b93cef19c83 100644 --- a/make/common/NativeCompilation.gmk +++ b/make/common/NativeCompilation.gmk @@ -363,18 +363,20 @@ define SetupCompileNativeFileBody endif $1_BASE_CFLAGS := $$($$($1_BASE)_CFLAGS) $$($$($1_BASE)_EXTRA_CFLAGS) \ - $$($$($1_BASE)_SYSROOT_CFLAGS) $$($1_WARNINGS_FLAGS) + $$($$($1_BASE)_SYSROOT_CFLAGS) $1_BASE_CXXFLAGS := $$($$($1_BASE)_CXXFLAGS) $$($$($1_BASE)_EXTRA_CXXFLAGS) \ - $$($$($1_BASE)_SYSROOT_CFLAGS) $$($1_EXTRA_CXXFLAGS) $$($1_WARNINGS_FLAGS) + $$($$($1_BASE)_SYSROOT_CFLAGS) $$($1_EXTRA_CXXFLAGS) $1_BASE_ASFLAGS := $$($$($1_BASE)_ASFLAGS) $$($$($1_BASE)_EXTRA_ASFLAGS) ifneq ($$(filter %.c, $$($1_FILENAME)), ) # Compile as a C file + $1_CFLAGS += $$($1_WARNINGS_FLAGS) $1_FLAGS := $(CFLAGS_CCACHE) $$($1_USE_PCH_FLAGS) $$($1_BASE_CFLAGS) \ $$($1_OPT_CFLAGS) $$($1_CFLAGS) -c $1_COMPILER := $$($$($1_BASE)_CC) else ifneq ($$(filter %.m, $$($1_FILENAME)), ) # Compile as an Objective-C file + $1_CFLAGS += $$($1_WARNINGS_FLAGS) $1_FLAGS := -x objective-c $(CFLAGS_CCACHE) $$($1_USE_PCH_FLAGS) \ $$($1_BASE_CFLAGS) $$($1_OPT_CFLAGS) $$($1_CFLAGS) -c $1_COMPILER := $$($$($1_BASE)_CC) @@ -398,6 +400,7 @@ define SetupCompileNativeFileBody endif else ifneq ($$(filter %.cpp %.cc %.mm, $$($1_FILENAME)), ) # Compile as a C++ or Objective-C++ file + $1_CXXFLAGS += $$($1_WARNINGS_FLAGS) $1_FLAGS := $(CFLAGS_CCACHE) $$($1_USE_PCH_FLAGS) $$($1_BASE_CXXFLAGS) \ $$($1_OPT_CXXFLAGS) $$($1_CXXFLAGS) -c $1_COMPILER := $$($$($1_BASE)_CXX) diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index 64dcb7723a9b5..4c957fb0eef21 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -446,7 +446,7 @@ var getJibProfilesProfiles = function (input, common, data) { target_cpu: "x64", dependencies: ["devkit", "gtest", "pandoc"], configure_args: concat(common.configure_args_64bit, "--with-zlib=system", - "--with-macosx-version-max=10.12.00", + "--with-macosx-version-max=11.00.00", "--enable-compatible-cds-alignment", // Use system SetFile instead of the one in the devkit as the // devkit one may not work on Catalina. diff --git a/make/conf/version-numbers.conf b/make/conf/version-numbers.conf index 29ccfb0801b37..66bc986fd1415 100644 --- a/make/conf/version-numbers.conf +++ b/make/conf/version-numbers.conf @@ -28,12 +28,12 @@ DEFAULT_VERSION_FEATURE=17 DEFAULT_VERSION_INTERIM=0 -DEFAULT_VERSION_UPDATE=15 +DEFAULT_VERSION_UPDATE=18 DEFAULT_VERSION_PATCH=0 DEFAULT_VERSION_EXTRA1=0 DEFAULT_VERSION_EXTRA2=0 DEFAULT_VERSION_EXTRA3=0 -DEFAULT_VERSION_DATE=2025-04-15 +DEFAULT_VERSION_DATE=2026-01-20 DEFAULT_VERSION_CLASSFILE_MAJOR=61 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`" DEFAULT_VERSION_CLASSFILE_MINOR=0 DEFAULT_VERSION_DOCS_API_SINCE=11 diff --git a/make/data/cacerts/affirmtrustcommercialca b/make/data/cacerts/affirmtrustcommercialca deleted file mode 100644 index 5caddfd3a0a5a..0000000000000 --- a/make/data/cacerts/affirmtrustcommercialca +++ /dev/null @@ -1,27 +0,0 @@ -Owner: CN=AffirmTrust Commercial, O=AffirmTrust, C=US -Issuer: CN=AffirmTrust Commercial, O=AffirmTrust, C=US -Serial number: 7777062726a9b17c -Valid from: Fri Jan 29 14:06:06 GMT 2010 until: Tue Dec 31 14:06:06 GMT 2030 -Signature algorithm name: SHA256withRSA -Subject Public Key Algorithm: 2048-bit RSA key -Version: 3 ------BEGIN CERTIFICATE----- -MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE -BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz -dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL -MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp -cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC -AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP -Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr -ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL -MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1 -yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr -VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/ -nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ -KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG -XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj -vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt -Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g -N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC -nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= ------END CERTIFICATE----- diff --git a/make/data/cacerts/affirmtrustnetworkingca b/make/data/cacerts/affirmtrustnetworkingca deleted file mode 100644 index c773326d4b950..0000000000000 --- a/make/data/cacerts/affirmtrustnetworkingca +++ /dev/null @@ -1,27 +0,0 @@ -Owner: CN=AffirmTrust Networking, O=AffirmTrust, C=US -Issuer: CN=AffirmTrust Networking, O=AffirmTrust, C=US -Serial number: 7c4f04391cd4992d -Valid from: Fri Jan 29 14:08:24 GMT 2010 until: Tue Dec 31 14:08:24 GMT 2030 -Signature algorithm name: SHA1withRSA -Subject Public Key Algorithm: 2048-bit RSA key -Version: 3 ------BEGIN CERTIFICATE----- -MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE -BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz -dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL -MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp -cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC -AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y -YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua -kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL -QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp -6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG -yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i -QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ -KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO -tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu -QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ -Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u -olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48 -x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= ------END CERTIFICATE----- diff --git a/make/data/cacerts/affirmtrustpremiumca b/make/data/cacerts/affirmtrustpremiumca deleted file mode 100644 index 275b495a25e84..0000000000000 --- a/make/data/cacerts/affirmtrustpremiumca +++ /dev/null @@ -1,38 +0,0 @@ -Owner: CN=AffirmTrust Premium, O=AffirmTrust, C=US -Issuer: CN=AffirmTrust Premium, O=AffirmTrust, C=US -Serial number: 6d8c1446b1a60aee -Valid from: Fri Jan 29 14:10:36 GMT 2010 until: Mon Dec 31 14:10:36 GMT 2040 -Signature algorithm name: SHA384withRSA -Subject Public Key Algorithm: 4096-bit RSA key -Version: 3 ------BEGIN CERTIFICATE----- -MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE -BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz -dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG -A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U -cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf -qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ -JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ -+jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS -s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5 -HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7 -70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG -V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S -qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S -5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia -C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX -OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE -FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ -BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2 -KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg -Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B -8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ -MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc -0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ -u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF -u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH -YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8 -GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO -RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e -KeC2uAloGRwYQw== ------END CERTIFICATE----- diff --git a/make/data/cacerts/affirmtrustpremiumeccca b/make/data/cacerts/affirmtrustpremiumeccca deleted file mode 100644 index d0fcc1e7793aa..0000000000000 --- a/make/data/cacerts/affirmtrustpremiumeccca +++ /dev/null @@ -1,20 +0,0 @@ -Owner: CN=AffirmTrust Premium ECC, O=AffirmTrust, C=US -Issuer: CN=AffirmTrust Premium ECC, O=AffirmTrust, C=US -Serial number: 7497258ac73f7a54 -Valid from: Fri Jan 29 14:20:24 GMT 2010 until: Mon Dec 31 14:20:24 GMT 2040 -Signature algorithm name: SHA384withECDSA -Subject Public Key Algorithm: 384-bit EC (secp384r1) key -Version: 3 ------BEGIN CERTIFICATE----- -MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC -VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ -cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ -BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt -VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D -0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9 -ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G -A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G -A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs -aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I -flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ== ------END CERTIFICATE----- diff --git a/make/data/cacerts/baltimorecybertrustca b/make/data/cacerts/baltimorecybertrustca deleted file mode 100644 index b3cf6547c1661..0000000000000 --- a/make/data/cacerts/baltimorecybertrustca +++ /dev/null @@ -1,28 +0,0 @@ -Owner: CN=Baltimore CyberTrust Root, OU=CyberTrust, O=Baltimore, C=IE -Issuer: CN=Baltimore CyberTrust Root, OU=CyberTrust, O=Baltimore, C=IE -Serial number: 20000b9 -Valid from: Fri May 12 18:46:00 GMT 2000 until: Mon May 12 23:59:00 GMT 2025 -Signature algorithm name: SHA1withRSA -Subject Public Key Algorithm: 2048-bit RSA key -Version: 3 ------BEGIN CERTIFICATE----- -MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ -RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD -VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX -DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y -ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy -VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr -mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr -IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK -mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu -XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy -dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye -jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 -BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 -DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 -9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx -jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 -Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz -ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS -R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp ------END CERTIFICATE----- diff --git a/make/data/cacerts/camerfirmachamberscommerceca b/make/data/cacerts/camerfirmachamberscommerceca deleted file mode 100644 index b92255f770cdf..0000000000000 --- a/make/data/cacerts/camerfirmachamberscommerceca +++ /dev/null @@ -1,35 +0,0 @@ -Owner: CN=Chambers of Commerce Root, OU=http://www.chambersign.org, O=AC Camerfirma SA CIF A82743287, C=EU -Issuer: CN=Chambers of Commerce Root, OU=http://www.chambersign.org, O=AC Camerfirma SA CIF A82743287, C=EU -Serial number: 0 -Valid from: Tue Sep 30 16:13:43 GMT 2003 until: Wed Sep 30 16:13:44 GMT 2037 -Signature algorithm name: SHA1withRSA -Subject Public Key Algorithm: 2048-bit RSA key -Version: 3 ------BEGIN CERTIFICATE----- -MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEn -MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL -ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMg -b2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAxNjEzNDNaFw0zNzA5MzAxNjEzNDRa -MH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZpcm1hIFNBIENJRiBB -ODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3JnMSIw -IAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0B -AQEFAAOCAQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtb -unXF/KGIJPov7coISjlUxFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0d -BmpAPrMMhe5cG3nCYsS4No41XQEMIwRHNaqbYE6gZj3LJgqcQKH0XZi/caulAGgq -7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jWDA+wWFjbw2Y3npuRVDM3 -0pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFVd9oKDMyX -roDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIG -A1UdEwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5j -aGFtYmVyc2lnbi5vcmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p -26EpW1eLTXYGduHRooowDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIA -BzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hhbWJlcnNpZ24ub3JnMCcGA1Ud -EgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYDVR0gBFEwTzBN -BgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz -aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEB -AAxBl8IahsAifJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZd -p0AJPaxJRUXcLo0waLIJuvvDL8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi -1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wNUPf6s+xCX6ndbcj0dc97wXImsQEc -XCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/nADydb47kMgkdTXg0 -eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1erfu -tGWaIZDgqtCYvDi1czyL+Nw= ------END CERTIFICATE----- diff --git a/make/data/cacerts/camerfirmachambersignca b/make/data/cacerts/camerfirmachambersignca deleted file mode 100644 index 935eea9c2121e..0000000000000 --- a/make/data/cacerts/camerfirmachambersignca +++ /dev/null @@ -1,48 +0,0 @@ -Owner: CN=Global Chambersign Root - 2008, O=AC Camerfirma S.A., SERIALNUMBER=A82743287, L=Madrid (see current address at www.camerfirma.com/address), C=EU -Issuer: CN=Global Chambersign Root - 2008, O=AC Camerfirma S.A., SERIALNUMBER=A82743287, L=Madrid (see current address at www.camerfirma.com/address), C=EU -Serial number: c9cdd3e9d57d23ce -Valid from: Fri Aug 01 12:31:40 GMT 2008 until: Sat Jul 31 12:31:40 GMT 2038 -Signature algorithm name: SHA1withRSA -Subject Public Key Algorithm: 4096-bit RSA key -Version: 3 ------BEGIN CERTIFICATE----- -MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYD -VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0 -IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3 -MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD -aGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMxNDBaFw0zODA3MzEx -MjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3Vy -cmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAG -A1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAl -BgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZI -hvcNAQEBBQADggIPADCCAgoCggIBAMDfVtPkOpt2RbQT2//BthmLN0EYlVJH6xed -KYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXfXjaOcNFccUMd2drvXNL7 -G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0ZJJ0YPP2 -zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4 -ddPB/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyG -HoiMvvKRhI9lNNgATH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2 -Id3UwD2ln58fQ1DJu7xsepeY7s2MH/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3V -yJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfeOx2YItaswTXbo6Al/3K1dh3e -beksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSFHTynyQbehP9r -6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh -wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsog -zCtLkykPAgMBAAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQW -BBS5CcqcHtvTbDprru1U8VuTBjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDpr -ru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UEBhMCRVUxQzBBBgNVBAcTOk1hZHJp -ZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJmaXJtYS5jb20vYWRk -cmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJmaXJt -YSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiC -CQDJzdPp1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCow -KAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZI -hvcNAQEFBQADggIBAICIf3DekijZBZRG/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZ -UohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6ReAJ3spED8IXDneRRXoz -X1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/sdZ7LoR/x -fxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVz -a2Mg9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yyd -Yhz2rXzdpjEetrHHfoUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMd -SqlapskD7+3056huirRXhOukP9DuqqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9O -AP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETrP3iZ8ntxPjzxmKfFGBI/5rso -M0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVqc5iJWzouE4ge -v8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z -09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B ------END CERTIFICATE----- diff --git a/make/data/cacerts/sectigocodesignroote46 b/make/data/cacerts/sectigocodesignroote46 new file mode 100644 index 0000000000000..7f13b28a40181 --- /dev/null +++ b/make/data/cacerts/sectigocodesignroote46 @@ -0,0 +1,21 @@ +Owner: CN=Sectigo Public Code Signing Root E46, O=Sectigo Limited, C=GB +Issuer: CN=Sectigo Public Code Signing Root E46, O=Sectigo Limited, C=GB +Serial number: 50249ba2ef8ea6bf6c2c1f1a6385d4c3 +Valid from: Mon Mar 22 00:00:00 GMT 2021 until: Wed Mar 21 23:59:59 GMT 2046 +Signature algorithm name: SHA384withECDSA +Subject Public Key Algorithm: 384-bit EC (secp384r1) key +Version: 3 +-----BEGIN CERTIFICATE----- +MIICKDCCAa+gAwIBAgIQUCSbou+Opr9sLB8aY4XUwzAKBggqhkjOPQQDAzBWMQsw +CQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMS0wKwYDVQQDEyRT +ZWN0aWdvIFB1YmxpYyBDb2RlIFNpZ25pbmcgUm9vdCBFNDYwHhcNMjEwMzIyMDAw +MDAwWhcNNDYwMzIxMjM1OTU5WjBWMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2Vj +dGlnbyBMaW1pdGVkMS0wKwYDVQQDEyRTZWN0aWdvIFB1YmxpYyBDb2RlIFNpZ25p +bmcgUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQIMoEDH487om+BR4zl +e7m6wWmyW0nAKLkUWG8kM85Qm3PZO8FoOZx6Yc5c0iJHRKuAhanllayqrmZYhlan +uIODzLTRDqlR+EtnOX+MubY5aDSPGUq6jiHrQrisVp0J3AejQjBAMB0GA1UdDgQW +BBTPfSygkHqYHd22XoXC4NoVcdLlXjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/ +BAUwAwEB/zAKBggqhkjOPQQDAwNnADBkAjACd++zAerlV83j8HflRwwwlLmgchbs +aGX/4g44dv/oG8KfzCVTRg6sZHMobtK0IqYCMGk5W6+oBFyZMtOebrSwXs8lGjll +/zHz43Zy8DMXO+iiqzSEwWGneZ6KupkGGqfVKw== +-----END CERTIFICATE----- diff --git a/make/data/cacerts/sectigocodesignrootr46 b/make/data/cacerts/sectigocodesignrootr46 new file mode 100644 index 0000000000000..38270cb0c5265 --- /dev/null +++ b/make/data/cacerts/sectigocodesignrootr46 @@ -0,0 +1,39 @@ +Owner: CN=Sectigo Public Code Signing Root R46, O=Sectigo Limited, C=GB +Issuer: CN=Sectigo Public Code Signing Root R46, O=Sectigo Limited, C=GB +Serial number: 4b2c3b01018bad2abc8c7b5b3eed9057 +Valid from: Mon Mar 22 00:00:00 GMT 2021 until: Wed Mar 21 23:59:59 GMT 2046 +Signature algorithm name: SHA384withRSA +Subject Public Key Algorithm: 4096-bit RSA key +Version: 3 +-----BEGIN CERTIFICATE----- +MIIFeDCCA2CgAwIBAgIQSyw7AQGLrSq8jHtbPu2QVzANBgkqhkiG9w0BAQwFADBW +MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMS0wKwYDVQQD +EyRTZWN0aWdvIFB1YmxpYyBDb2RlIFNpZ25pbmcgUm9vdCBSNDYwHhcNMjEwMzIy +MDAwMDAwWhcNNDYwMzIxMjM1OTU5WjBWMQswCQYDVQQGEwJHQjEYMBYGA1UEChMP +U2VjdGlnbyBMaW1pdGVkMS0wKwYDVQQDEyRTZWN0aWdvIFB1YmxpYyBDb2RlIFNp +Z25pbmcgUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCN +55QSIgQkdC7/FiMCkoq2rjaFrEfUI5ErPtx94jGgUW+shJHjUoq14pbe0IdjJImK +/+8Skzt9u7aKvb0Ffyeba2XTpQxpsbxJOZrxbW6q5KCDJ9qaDStQ6Utbs7hkNqR+ +Sj2pcaths3OzPAsM79szV+W+NDfjlxtd/R8SPYIDdub7P2bSlDFp+m2zNKzBenjc +klDyZMeqLQSrw2rq4C+np9xu1+j/2iGrQL+57g2extmeme/G3h+pDHazJyCh1rr9 +gOcB0u/rgimVcI3/uxXP/tEPNqIuTzKQdEZrRzUTdwUzT2MuuC3hv2WnBGsY2HH6 +zAjybYmZELGt2z4s5KoYsMYHAXVn3m3pY2MeNn9pib6qRT5uWl+PoVvLnTCGMOgD +s0DGDQ84zWeoU4j6uDBl+m/H5x2xg3RpPqzEaDux5mczmrYI4IAFSEDu9oJkRqj1 +c7AGlfJsZZ+/VVscnFcax3hGfHCqlBuCF6yH6bbJDoEcQNYWFyn8XJwYK+pF9e+9 +1WdPKF4F7pBMeufG9ND8+s0+MkYTIDaKBOq3qgdGnA2TOglmmVhcKaO5DKYwODzQ +RjY1fJy67sPV+Qp2+n4FG0DKkjXp1XrRtX8ArqmQqsV/AZwQsRb8zG4Y3G9i/qZQ +p7h7uJ0VP/4gDHXIIloTlRmQAOka1cKG8eOO7F/05QIDAQABo0IwQDAdBgNVHQ4E +FgQUMuuSmv81lkgvKEBCcCA2kVwXheYwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAHZlwuPXIkrXHYle/2lexhQCTXOm +zc0oyrA36r+nySGqql/av/aDbNCA0QpcAKTL88w5D55BcYjVPOiKe4wXI/fKNHSR +bAauUD8AWbImPDwXg1cDPi3RGj3UzwdUskMLUnKoiPXEF/Jv0Vil0WjkPZgIGO42 +9EhImvpUcPCI1HAWMEJJ0Nk/dUtFcdiuorthDoiFUFe5uhErNikfjyBynlyeidGC +2kWNapnahHFrM6UQu3nwl/Z0gaA/V8eGjDCMDjiVrgHGHqvcqB9vL9f/dh6uF3Nt +5bl1s2EGqJUzwk5vsjfylb6FVBK5yL1iQnb3Kvz1NzEDJlf+0ebb8BYCcoOMCLOE +rKnkB/ihiMQTWlBHVEKm7dBBNCyYsT6iNKEMXb2s9395p79tDFYyhRtLl7jhrOSk +PHHxo+FOY9b0Rrr1CwjhYzztolkvCtQsayOinqFN7tESzRgzUO1Bbst/PUFgC2ML +ePV170MVtzYLEK/cXBipmNk22R3YhLMGioLjexskp0LO7g8+VlwyfexL3lYrOzu6 ++XpY0FG2bNb2WKJSJHpEhqEcYD9J0/z6+YQcBcI0v+Lm8RkqmS9WVzWctfUHw0Yv +3jg9GQ37o/HfE57nqXJYMa+96trX1m13MzOO9Kz9wb9Jh9JwBWd0Bqb2eEAtFgSR +Dx/TFsS4ehcNJMmy +-----END CERTIFICATE----- diff --git a/make/data/cacerts/sectigotlsroote46 b/make/data/cacerts/sectigotlsroote46 new file mode 100644 index 0000000000000..60e0b83c97e43 --- /dev/null +++ b/make/data/cacerts/sectigotlsroote46 @@ -0,0 +1,21 @@ +Owner: CN=Sectigo Public Server Authentication Root E46, O=Sectigo Limited, C=GB +Issuer: CN=Sectigo Public Server Authentication Root E46, O=Sectigo Limited, C=GB +Serial number: 42f2ccda1b6937445f15fe752810b8f4 +Valid from: Mon Mar 22 00:00:00 GMT 2021 until: Wed Mar 21 23:59:59 GMT 2046 +Signature algorithm name: SHA384withECDSA +Subject Public Key Algorithm: 384-bit EC (secp384r1) key +Version: 3 +-----BEGIN CERTIFICATE----- +MIICOjCCAcGgAwIBAgIQQvLM2htpN0RfFf51KBC49DAKBggqhkjOPQQDAzBfMQsw +CQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1T +ZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwHhcN +MjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5WjBfMQswCQYDVQQGEwJHQjEYMBYG +A1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1YmxpYyBT +ZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAR2+pmpbiDt+dd34wc7qNs9Xzjoq1WmVk/WSOrsfy2qw7LFeeyZYX8QeccC +WvkEN/U0NSt3zn8gj1KjAIns1aeibVvjS5KToID1AZTc8GgHHs3u/iVStSBDHBv+ +6xnOQ6OjQjBAMB0GA1UdDgQWBBTRItpMWfFLXyY4qp3W7usNw/upYTAOBgNVHQ8B +Af8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNnADBkAjAn7qRa +qCG76UeXlImldCBteU/IvZNeWBj7LRoAasm4PdCkT0RHlAFWovgzJQxC36oCMB3q +4S6ILuH5px0CMk7yn2xVdOOurvulGu7t0vzCAxHrRVxgED1cf5kDW21USAGKcw== +-----END CERTIFICATE----- diff --git a/make/data/cacerts/sectigotlsrootr46 b/make/data/cacerts/sectigotlsrootr46 new file mode 100644 index 0000000000000..7ec1b263de743 --- /dev/null +++ b/make/data/cacerts/sectigotlsrootr46 @@ -0,0 +1,39 @@ +Owner: CN=Sectigo Public Server Authentication Root R46, O=Sectigo Limited, C=GB +Issuer: CN=Sectigo Public Server Authentication Root R46, O=Sectigo Limited, C=GB +Serial number: 758dfd8bae7c0700faa925a7e1c7ad14 +Valid from: Mon Mar 22 00:00:00 GMT 2021 until: Wed Mar 21 23:59:59 GMT 2046 +Signature algorithm name: SHA384withRSA +Subject Public Key Algorithm: 4096-bit RSA key +Version: 3 +-----BEGIN CERTIFICATE----- +MIIFijCCA3KgAwIBAgIQdY39i658BwD6qSWn4cetFDANBgkqhkiG9w0BAQwFADBf +MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQD +Ey1TZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYw +HhcNMjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5WjBfMQswCQYDVQQGEwJHQjEY +MBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1Ymxp +YyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCTvtU2UnXYASOgHEdCSe5jtrch/cSV1UgrJnwUUxDa +ef0rty2k1Cz66jLdScK5vQ9IPXtamFSvnl0xdE8H/FAh3aTPaE8bEmNtJZlMKpnz +SDBh+oF8HqcIStw+KxwfGExxqjWMrfhu6DtK2eWUAtaJhBOqbchPM8xQljeSM9xf +iOefVNlI8JhD1mb9nxc4Q8UBUQvX4yMPFF1bFOdLvt30yNoDN9HWOaEhUTCDsG3X +ME6WW5HwcCSrv0WBZEMNvSE6Lzzpng3LILVCJ8zab5vuZDCQOc2TZYEhMbUjUDM3 +IuM47fgxMMxF/mL50V0yeUKH32rMVhlATc6qu/m1dkmU8Sf4kaWD5QazYw6A3OAS +VYCmO2a0OYctyPDQ0RTp5A1NDvZdV3LFOxxHVp3i1fuBYYzMTYCQNFu31xR13NgE +SJ/AwSiItOkcyqex8Va3e0lMWeUgFaiEAin6OJRpmkkGj80feRQXEgyDet4fsZfu ++Zd4KKTIRJLpfSYFplhym3kT2BFfrsU4YjRosoYwjviQYZ4ybPUHNs2iTG7sijbt +8uaZFURww3y8nDnAtOFr94MlI1fZEoDlSfB1D++N6xybVCi0ITz8fAr/73trdf+L +HaAZBav6+CuBQug4urv7qv094PPK306Xlynt8xhW6aWWrL3DkJiy4Pmi1KZHQ3xt +zwIDAQABo0IwQDAdBgNVHQ4EFgQUVnNYZJX5khqwEioEYnmhQBWIIUkwDgYDVR0P +AQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAC9c +mTz8Bl6MlC5w6tIyMY208FHVvArzZJ8HXtXBc2hkeqK5Duj5XYUtqDdFqij0lgVQ +YKlJfp/imTYpE0RHap1VIDzYm/EDMrraQKFz6oOht0SmDpkBm+S8f74TlH7Kph52 +gDY9hAaLMyZlbcp+nv4fjFg4exqDsQ+8FxG75gbMY/qB8oFM2gsQa6H61SilzwZA +Fv97fRheORKkU55+MkIQpiGRqRxOF3yEvJ+M0ejf5lG5Nkc/kLnHvALcWxxPDkjB +JYOcCj+esQMzEhonrPcibCTRAUH4WAP+JWgiH5paPHxsnnVI84HxZmduTILA7rpX +DhjvLpr3Etiga+kFpaHpaPi8TD8SHkXoUsCjvxInebnMMTzD9joiFgOgyY9mpFui +TdaBJQbpdqQACj7LzTWb4OE4y2BThihCQRxEV+ioratF4yUQvNs+ZUH7G6aXD+u5 +dHn5HrwdVw1Hr8Mvn4dGp+smWg9WY7ViYG4A++MnESLn/pmPNPW56MORcr3Ywx65 +LvKRRFHQV80MNNVIIb/bE/FmJUNS0nAiNs2fxBx1IK1jcmMGDw4nztJqDby1ORrp +0XZ60Vzk50lJLVU3aPAaOpg+VBeHVOmmJ1CJeyAvP/+/oYtKR5j/K3tJPsMpRmAY +QqszKbrAKbkTidOIijlBO8n9pu0f9GBj39ItVQGL +-----END CERTIFICATE----- diff --git a/make/data/currency/CurrencyData.properties b/make/data/currency/CurrencyData.properties index 550662ec38a14..6e21fffdeb973 100644 --- a/make/data/currency/CurrencyData.properties +++ b/make/data/currency/CurrencyData.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -32,7 +32,7 @@ formatVersion=3 # Version of the currency code information in this class. # It is a serial number that accompanies with each amendment. -dataVersion=177 +dataVersion=180 # List of all valid ISO 4217 currency codes. # To ensure compatibility, do not remove codes. @@ -54,7 +54,7 @@ all=ADP020-AED784-AFA004-AFN971-ALL008-AMD051-ANG532-AOA973-ARS032-ATS040-AUD036 SBD090-SCR690-SDD736-SDG938-SEK752-SGD702-SHP654-SIT705-SKK703-SLE925-SLL694-SOS706-\ SRD968-SRG740-SSP728-STD678-STN930-SVC222-SYP760-SZL748-THB764-TJS972-TMM795-TMT934-TND788-TOP776-\ TPE626-TRL792-TRY949-TTD780-TWD901-TZS834-UAH980-UGX800-USD840-USN997-USS998-UYI940-\ - UYU858-UZS860-VEB862-VED926-VEF937-VES928-VND704-VUV548-WST882-XAF950-XAG961-XAU959-XBA955-\ + UYU858-UZS860-VEB862-VED926-VEF937-VES928-VND704-VUV548-WST882-XAD396-XAF950-XAG961-XAU959-XBA955-\ XBB956-XBC957-XBD958-XCD951-XCG532-XDR960-XFO000-XFU000-XOF952-XPD964-XPF953-\ XPT962-XSU994-XTS963-XUA965-XXX999-YER886-YUM891-ZAR710-ZMK894-ZMW967-ZWD716-ZWG924-\ ZWL932-ZWN942-ZWR935 @@ -147,7 +147,7 @@ IO=USD # BRUNEI DARUSSALAM BN=BND # BULGARIA -BG=BGN +BG=BGN;2025-12-31-22-00-00;EUR # BURKINA FASO BF=XOF # BURUNDI @@ -193,7 +193,7 @@ HR=EUR # CUBA CU=CUP # Cura\u00e7ao -CW=ANG;2025-04-01-04-00-00;XCG +CW=XCG # CYPRUS CY=EUR # CZECHIA @@ -510,7 +510,7 @@ SR=SRD # SVALBARD AND JAN MAYEN SJ=NOK # Sint Maarten (Dutch part) -SX=ANG;2025-04-01-04-00-00;XCG +SX=XCG # ESWATINI SZ=SZL # SWEDEN diff --git a/make/data/lsrdata/language-subtag-registry.txt b/make/data/lsrdata/language-subtag-registry.txt index b00ea67e7e8ca..64c40f28162c7 100644 --- a/make/data/lsrdata/language-subtag-registry.txt +++ b/make/data/lsrdata/language-subtag-registry.txt @@ -1,4 +1,4 @@ -File-Date: 2024-11-19 +File-Date: 2025-05-15 %% Type: language Subtag: aa @@ -5950,6 +5950,7 @@ Added: 2009-07-29 %% Type: language Subtag: bql +Description: Karian Description: Bilakura Added: 2009-07-29 %% @@ -9083,6 +9084,7 @@ Scope: collection %% Type: language Subtag: daz +Description: Moi-Wadea Description: Dao Added: 2009-07-29 %% @@ -9290,6 +9292,8 @@ Type: language Subtag: dek Description: Dek Added: 2009-07-29 +Deprecated: 2024-12-12 +Preferred-Value: sqm %% Type: language Subtag: del @@ -14082,6 +14086,12 @@ Added: 2009-07-29 Macrolanguage: hmn %% Type: language +Subtag: hnm +Description: Hainanese +Added: 2024-12-12 +Macrolanguage: zh +%% +Type: language Subtag: hnn Description: Hanunoo Added: 2009-07-29 @@ -16421,6 +16431,7 @@ Added: 2009-07-29 %% Type: language Subtag: kci +Description: Ngyian Description: Kamantan Added: 2009-07-29 %% @@ -21081,6 +21092,12 @@ Description: Laua Added: 2009-07-29 %% Type: language +Subtag: luh +Description: Leizhou Chinese +Added: 2024-12-12 +Macrolanguage: zh +%% +Type: language Subtag: lui Description: Luiseno Added: 2005-10-16 @@ -22850,6 +22867,8 @@ Added: 2009-07-29 %% Type: language Subtag: mmi +Description: Hember Avu +Description: Amben Description: Musar Added: 2009-07-29 %% @@ -25197,8 +25216,9 @@ Added: 2009-07-29 %% Type: language Subtag: new -Description: Newari Description: Nepal Bhasa +Description: Newar +Description: Newari Added: 2005-10-16 %% Type: language @@ -26641,6 +26661,8 @@ Type: language Subtag: nte Description: Nathembo Added: 2009-07-29 +Deprecated: 2024-12-12 +Preferred-Value: eko %% Type: language Subtag: ntg @@ -27185,6 +27207,12 @@ Description: Oroch Added: 2009-07-29 %% Type: language +Subtag: oak +Description: Noakhali +Description: Noakhailla +Added: 2025-05-14 +%% +Type: language Subtag: oar Description: Old Aramaic (up to 700 BCE) Description: Ancient Aramaic (up to 700 BCE) @@ -32147,6 +32175,12 @@ Description: Sajau Basap Added: 2009-07-29 %% Type: language +Subtag: sjc +Description: Shaojiang Chinese +Added: 2024-12-12 +Macrolanguage: zh +%% +Type: language Subtag: sjd Description: Kildin Sami Added: 2009-07-29 @@ -41302,6 +41336,11 @@ Description: Aluo Added: 2009-07-29 %% Type: language +Subtag: ynb +Description: Yamben +Added: 2025-02-06 +%% +Type: language Subtag: ynd Description: Yandruwandha Added: 2009-07-29 @@ -43616,6 +43655,14 @@ Preferred-Value: hks Prefix: sgn %% Type: extlang +Subtag: hnm +Description: Hainanese +Added: 2024-12-12 +Preferred-Value: hnm +Prefix: zh +Macrolanguage: zh +%% +Type: extlang Subtag: hos Description: Ho Chi Minh City Sign Language Added: 2009-07-29 @@ -43958,6 +44005,14 @@ Prefix: lv Macrolanguage: lv %% Type: extlang +Subtag: luh +Description: Leizhou Chinese +Added: 2024-12-12 +Preferred-Value: luh +Prefix: zh +Macrolanguage: zh +%% +Type: extlang Subtag: lvs Description: Standard Latvian Added: 2010-03-11 @@ -44393,6 +44448,14 @@ Prefix: ar Macrolanguage: ar %% Type: extlang +Subtag: sjc +Description: Shaojiang Chinese +Added: 2024-12-12 +Preferred-Value: sjc +Prefix: zh +Macrolanguage: zh +%% +Type: extlang Subtag: slf Description: Swiss-Italian Sign Language Added: 2009-07-29 @@ -44844,6 +44907,11 @@ Description: Bangla Added: 2005-10-16 %% Type: script +Subtag: Berf +Description: Beria Erfe +Added: 2025-02-06 +%% +Type: script Subtag: Bhks Description: Bhaiksuki Added: 2015-07-24 @@ -45132,6 +45200,12 @@ Description: Nyiakeng Puachue Hmong Added: 2017-08-13 %% Type: script +Subtag: Hntl +Description: Han (Traditional variant) with Latin (alias for Hant + + Latn) +Added: 2025-05-14 +%% +Type: script Subtag: Hrkt Description: Japanese syllabaries (alias for Hiragana + Katakana) Added: 2005-10-16 @@ -45636,6 +45710,12 @@ Description: Saurashtra Added: 2006-07-21 %% Type: script +Subtag: Seal +Description: Seal +Description: Small Seal +Added: 2025-05-14 +%% +Type: script Subtag: Sgnw Description: SignWriting Added: 2006-10-17 @@ -47919,6 +47999,12 @@ Comments: Written standard developed by Romanilha in 1853 and used by dóu Po, Escolo Gaston Febus, and others %% Type: variant +Subtag: hanoi +Description: The Hà Nội variant of Vietnamese +Added: 2025-03-10 +Prefix: vi +%% +Type: variant Subtag: hepburn Description: Hepburn romanization Added: 2009-10-01 @@ -47949,6 +48035,12 @@ Added: 2017-03-14 Prefix: eo %% Type: variant +Subtag: huett +Description: The Huế (province Thừa Thiên) variant of Vietnamese +Added: 2025-03-10 +Prefix: vi +%% +Type: variant Subtag: ijekavsk Description: Serbian with Ijekavian pronunciation Prefix: sr @@ -48024,6 +48116,13 @@ Prefix: sa Comments: Preferred tag is cls %% Type: variant +Subtag: leidentr +Description: Ancient Egyptian in Leiden Unified Transliteration +Added: 2025-02-06 +Prefix: egy +Comments: Recommended by the International Association of Egyptologists +%% +Type: variant Subtag: lemosin Description: Limousin Added: 2018-04-22 @@ -48068,6 +48167,19 @@ Comments: Russian orthography as established by the 1917/1918 orthographic reforms %% Type: variant +Subtag: mdcegyp +Description: Ancient Egyptian hieroglyphs encoded in Manuel de Codage +Added: 2025-02-06 +Prefix: egy +%% +Type: variant +Subtag: mdctrans +Description: Ancient Egyptian transliteration encoded in Manuel de + Codage +Added: 2025-02-06 +Prefix: egy +%% +Type: variant Subtag: metelko Description: Slovene in Metelko alphabet Added: 2012-06-27 @@ -48255,6 +48367,12 @@ Prefix: rm Comments: Supraregional Romansh written standard %% Type: variant +Subtag: saigon +Description: The Sài Gòn variant of Vietnamese +Added: 2025-03-10 +Prefix: vi +%% +Type: variant Subtag: scotland Description: Scottish Standard English Added: 2007-08-31 diff --git a/make/data/tzdata/VERSION b/make/data/tzdata/VERSION index 9c056fac34575..4bd54efbcdcf3 100644 --- a/make/data/tzdata/VERSION +++ b/make/data/tzdata/VERSION @@ -21,4 +21,4 @@ # or visit www.oracle.com if you need additional information or have any # questions. # -tzdata2025a +tzdata2025b diff --git a/make/data/tzdata/asia b/make/data/tzdata/asia index b0a6fa01d2081..55b13134f9c6d 100644 --- a/make/data/tzdata/asia +++ b/make/data/tzdata/asia @@ -1523,6 +1523,16 @@ Zone Asia/Jayapura 9:22:48 - LMT 1932 Nov # (UIT No. 143 17.XI.1977) and not 23 September (UIT No. 141 13.IX.1977). # UIT is the Operational Bulletin of International Telecommunication Union. +# From Roozbeh Pournader (2025-03-18): +# ... the exact time of Iran's transition from +0400 to +0330 ... was Friday +# 1357/8/19 AP=1978-11-10. Here's a newspaper clip from the Ettela'at +# newspaper, dated 1357/8/14 AP=1978-11-05, translated from Persian +# (at https://w.wiki/DUEY): +# Following the government's decision about returning the official time +# to the previous status, the spokesperson for the Ministry of Energy +# announced today: At the hour 24 of Friday 19th of Aban (=1978-11-10), +# the country's time will be pulled back half an hour. +# # From Roozbeh Pournader (2003-03-15): # This is an English translation of what I just found (originally in Persian). # The Gregorian dates in brackets are mine: @@ -1650,7 +1660,7 @@ Rule Iran 2021 2022 - Sep 21 24:00 0 - Zone Asia/Tehran 3:25:44 - LMT 1916 3:25:44 - TMT 1935 Jun 13 # Tehran Mean Time 3:30 Iran %z 1977 Oct 20 24:00 - 4:00 Iran %z 1979 + 4:00 Iran %z 1978 Nov 10 24:00 3:30 Iran %z diff --git a/make/data/tzdata/northamerica b/make/data/tzdata/northamerica index 0a54e63becc86..21f178ee8cb05 100644 --- a/make/data/tzdata/northamerica +++ b/make/data/tzdata/northamerica @@ -1634,6 +1634,15 @@ Zone America/Moncton -4:19:08 - LMT 1883 Dec 9 # For more on Orillia, see: Daubs K. Bold attempt at daylight saving # time became a comic failure in Orillia. Toronto Star 2017-07-08. # https://www.thestar.com/news/insight/2017/07/08/bold-attempt-at-daylight-saving-time-became-a-comic-failure-in-orillia.html +# From Paul Eggert (2025-03-20): +# Also see the 1912-06-17 front page of The Evening Sunbeam, +# reproduced in: Richardson M. "Daylight saving was a confusing +# time in Orillia" in the 2025-03-15 Orillia Matters. Richardson writes, +# "The first Sunday after the switch was made, [DST proponent and +# Orillia mayor William Sword] Frost walked into church an hour late. +# This became a symbol of the downfall of daylight saving in Orillia." +# The mayor became known as "Daylight Bill". +# https://www.orilliamatters.com/local-news/column-daylight-saving-was-a-confusing-time-in-orillia-10377529 # From Mark Brader (2010-03-06): # diff --git a/make/data/tzdata/southamerica b/make/data/tzdata/southamerica index 0a5859600e8ff..ca3c338591fd8 100644 --- a/make/data/tzdata/southamerica +++ b/make/data/tzdata/southamerica @@ -1269,35 +1269,45 @@ Zone America/Rio_Branco -4:31:12 - LMT 1914 # dates to 2014. # DST End: last Saturday of April 2014 (Sun 27 Apr 2014 03:00 UTC) # DST Start: first Saturday of September 2014 (Sun 07 Sep 2014 04:00 UTC) -# http://www.diariooficial.interior.gob.cl//media/2014/02/19/do-20140219.pdf +# From Tim Parenti (2025-03-22): +# Decreto 307 of 2014 of the Ministry of the Interior and Public Security, +# promulgated 2014-01-30 and published 2014-02-19: +# https://www.diariooficial.interior.gob.cl/media/2014/02/19/do-20140219.pdf#page=1 +# https://www.bcn.cl/leychile/navegar?idNorma=1059557 # From Eduardo Romero Urra (2015-03-03): # Today has been published officially that Chile will use the DST time # permanently until March 25 of 2017 -# http://www.diariooficial.interior.gob.cl/media/2015/03/03/1-large.jpg -# -# From Paul Eggert (2015-03-03): -# For now, assume that the extension will persist indefinitely. +# From Tim Parenti (2025-03-22): +# Decreto 106 of 2015 of the Ministry of the Interior and Public Security, +# promulgated 2015-01-27 and published 2015-03-03: +# https://www.diariooficial.interior.gob.cl/media/2015/03/03/do-20150303.pdf#page=1 +# https://www.bcn.cl/leychile/navegar?idNorma=1075157 # From Juan Correa (2016-03-18): -# The decree regarding DST has been published in today's Official Gazette: -# http://www.diariooficial.interior.gob.cl/versiones-anteriores/do/20160318/ -# http://www.leychile.cl/Navegar?idNorma=1088502 +# The decree regarding DST has been published in today's Official Gazette... # It does consider the second Saturday of May and August as the dates # for the transition; and it lists DST dates until 2019, but I think # this scheme will stick. -# # From Paul Eggert (2016-03-18): -# For now, assume the pattern holds for the indefinite future. # The decree says transitions occur at 24:00; in practice this appears # to mean 24:00 mainland time, not 24:00 local time, so that Easter # Island is always two hours behind the mainland. +# From Tim Parenti (2025-03-22): +# Decreto 253 of 2016 of the Ministry of the Interior and Public Security, +# promulgated 2016-03-16 and published 2016-03-18. +# https://www.diariooficial.interior.gob.cl/media/2016/03/18/do-20160318.pdf#page=1 +# https://www.bcn.cl/leychile/navegar?idNorma=1088502 # From Juan Correa (2016-12-04): # Magallanes region ... will keep DST (UTC -3) all year round.... # http://www.soychile.cl/Santiago/Sociedad/2016/12/04/433428/Bachelet-firmo-el-decreto-para-establecer-un-horario-unico-para-la-Region-de-Magallanes.aspx -# From Deborah Goldsmith (2017-01-19): -# http://www.diariooficial.interior.gob.cl/publicaciones/2017/01/17/41660/01/1169626.pdf +# From Tim Parenti (2025-03-22), via Deborah Goldsmith (2017-01-19): +# Decreto 1820 of 2016 of the Ministry of the Interior and Public Security, +# promulgated 2016-12-02 and published 2017-01-17: +# https://www.diariooficial.interior.gob.cl/publicaciones/2017/01/17/41660/01/1169626.pdf +# https://www.bcn.cl/leychile/Navegar?idNorma=1099217 +# Model this as a change to standard offset effective 2016-12-04. # From Juan Correa (2018-08-13): # As of moments ago, the Ministry of Energy in Chile has announced the new @@ -1316,13 +1326,20 @@ Zone America/Rio_Branco -4:31:12 - LMT 1914 # https://twitter.com/MinEnergia/status/1029009354001973248 # "We will keep the new time policy unchanged for at least the next 4 years." # So we extend the new rules on Saturdays at 24:00 mainland time indefinitely. -# From Juan Correa (2019-02-04): -# http://www.diariooficial.interior.gob.cl/publicaciones/2018/11/23/42212/01/1498738.pdf +# From Tim Parenti (2025-03-22), via Juan Correa (2019-02-04): +# Decreto 1286 of 2018 of the Ministry of the Interior and Public Security, +# promulgated 2018-09-21 and published 2018-11-23: +# https://www.diariooficial.interior.gob.cl/publicaciones/2018/11/23/42212/01/1498738.pdf +# https://www.bcn.cl/leychile/Navegar?idNorma=1125760 # From Juan Correa (2022-04-02): # I found there was a decree published last Thursday that will keep -# Magallanes region to UTC -3 "indefinitely". The decree is available at +# Magallanes region to UTC -3 "indefinitely". +# From Tim Parenti (2025-03-22): +# Decreto 143 of 2022 of the Ministry of the Interior and Public Security, +# promulgated 2022-03-29 and published 2022-03-31: # https://www.diariooficial.interior.gob.cl/publicaciones/2022/03/31/43217-B/01/2108910.pdf +# https://www.bcn.cl/leychile/Navegar?idNorma=1174342 # From Juan Correa (2022-08-09): # the Internal Affairs Ministry (Ministerio del Interior) informed DST @@ -1331,13 +1348,36 @@ Zone America/Rio_Branco -4:31:12 - LMT 1914 # will keep UTC -3 "indefinitely"... This is because on September 4th # we will have a voting whether to approve a new Constitution. # -# From Eduardo Romero Urra (2022-08-17): +# From Tim Parenti (2025-03-22), via Eduardo Romero Urra (2022-08-17): +# Decreto 224 of 2022 of the Ministry of the Interior and Public Security, +# promulgated 2022-07-14 and published 2022-08-13: # https://www.diariooficial.interior.gob.cl/publicaciones/2022/08/13/43327/01/2172567.pdf +# https://www.bcn.cl/leychile/navegar?idNorma=1179983 # # From Paul Eggert (2022-08-17): # Although the presidential decree stops at fall 2026, assume that # similar DST rules will continue thereafter. +# From Paul Eggert (2025-01-15): +# Diario Regional Aysén's Sebastián Martel reports that 94% of Aysén +# citizens polled in November favored changing the rules from +# -04/-03-with-DST to -03 all year... +# https://www.diarioregionalaysen.cl/noticia/actualidad/2024/12/presentan-decision-que-gano-la-votacion-sobre-el-cambio-del-huso-horario-en-aysen +# +# From Yonathan Dossow (2025-03-20): +# [T]oday we have more confirmation of the change. [Aysén] region will keep +# UTC-3 all year... +# https://www.cnnchile.com/pais/region-de-aysen-mantendra-horario-de-verano-todo-el-ano_20250320/ +# https://www.latercera.com/nacional/noticia/tras-consulta-ciudadana-region-de-aysen-mantendra-el-horario-de-verano-durante-todo-el-ano/ +# https://x.com/min_interior/status/1902692504270672098 +# +# From Tim Parenti (2025-03-22), via Eduardo Romero Urra (2025-03-20): +# Decreto 93 of 2025 of the Ministry of the Interior and Public Security, +# promulgated 2025-03-11 and published 2025-03-20: +# https://www.diariooficial.interior.gob.cl/publicaciones/2025/03/20/44104/01/2624263.pdf +# https://www.bcn.cl/leychile/Navegar?idNorma=1211955 +# Model this as a change to standard offset effective 2025-03-20. + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Chile 1927 1931 - Sep 1 0:00 1:00 - Rule Chile 1928 1932 - Apr 1 0:00 0 - @@ -1394,6 +1434,20 @@ Zone America/Santiago -4:42:45 - LMT 1890 -5:00 1:00 %z 1947 Mar 31 24:00 -5:00 - %z 1947 May 21 23:00 -4:00 Chile %z +Zone America/Coyhaique -4:48:16 - LMT 1890 + -4:42:45 - SMT 1910 Jan 10 + -5:00 - %z 1916 Jul 1 + -4:42:45 - SMT 1918 Sep 10 + -4:00 - %z 1919 Jul 1 + -4:42:45 - SMT 1927 Sep 1 + -5:00 Chile %z 1932 Sep 1 + -4:00 - %z 1942 Jun 1 + -5:00 - %z 1942 Aug 1 + -4:00 - %z 1946 Aug 28 24:00 + -5:00 1:00 %z 1947 Mar 31 24:00 + -5:00 - %z 1947 May 21 23:00 + -4:00 Chile %z 2025 Mar 20 + -3:00 - %z Zone America/Punta_Arenas -4:43:40 - LMT 1890 -4:42:45 - SMT 1910 Jan 10 -5:00 - %z 1916 Jul 1 diff --git a/make/data/tzdata/zone.tab b/make/data/tzdata/zone.tab index e7a4868c39d0e..c8fc601041bca 100644 --- a/make/data/tzdata/zone.tab +++ b/make/data/tzdata/zone.tab @@ -162,7 +162,8 @@ CH +4723+00832 Europe/Zurich CI +0519-00402 Africa/Abidjan CK -2114-15946 Pacific/Rarotonga CL -3327-07040 America/Santiago most of Chile -CL -5309-07055 America/Punta_Arenas Region of Magallanes +CL -4534-07204 America/Coyhaique Aysen Region +CL -5309-07055 America/Punta_Arenas Magallanes Region CL -2709-10926 Pacific/Easter Easter Island CM +0403+00942 Africa/Douala CN +3114+12128 Asia/Shanghai Beijing Time diff --git a/make/hotspot/lib/CompileGtest.gmk b/make/hotspot/lib/CompileGtest.gmk index f16b9a747bcc4..500c3f5b02ae6 100644 --- a/make/hotspot/lib/CompileGtest.gmk +++ b/make/hotspot/lib/CompileGtest.gmk @@ -49,7 +49,8 @@ $(eval $(call SetupJdkLibrary, BUILD_GTEST_LIBGTEST, \ $(GTEST_FRAMEWORK_SRC)/googletest/src \ $(GTEST_FRAMEWORK_SRC)/googlemock/src, \ INCLUDE_FILES := gtest-all.cc gmock-all.cc, \ - DISABLED_WARNINGS_gcc := undef unused-result format-nonliteral maybe-uninitialized, \ + DISABLED_WARNINGS_gcc := undef unused-result format-nonliteral \ + maybe-uninitialized zero-as-null-pointer-constant, \ DISABLED_WARNINGS_clang := undef unused-result format-nonliteral, \ CFLAGS := $(JVM_CFLAGS) \ -I$(GTEST_FRAMEWORK_SRC)/googletest \ @@ -102,9 +103,12 @@ $(eval $(call SetupJdkLibrary, BUILD_GTEST_LIBJVM, \ CFLAGS_macosx := -DGTEST_OS_MAC=1, \ DISABLED_WARNINGS_gcc := $(DISABLED_WARNINGS_gcc) \ undef stringop-overflow, \ + DISABLED_WARNINGS_gcc_test_threadCpuLoad.cpp := address, \ DISABLED_WARNINGS_clang := $(DISABLED_WARNINGS_clang) \ undef switch format-nonliteral tautological-undefined-compare \ self-assign-overloaded, \ + DISABLED_WARNINGS_clang_test_g1ServiceThread.cpp := delete-abstract-non-virtual-dtor, \ + DISABLED_WARNINGS_clang_test_logDecorations.cpp := missing-field-initializers, \ DISABLED_WARNINGS_microsoft := $(DISABLED_WARNINGS_microsoft), \ LDFLAGS := $(JVM_LDFLAGS), \ LDFLAGS_unix := -L$(JVM_OUTPUTDIR)/libgtest, \ diff --git a/make/hotspot/lib/CompileJvm.gmk b/make/hotspot/lib/CompileJvm.gmk index 508be1090925b..92a0b430c7473 100644 --- a/make/hotspot/lib/CompileJvm.gmk +++ b/make/hotspot/lib/CompileJvm.gmk @@ -77,14 +77,17 @@ endif ################################################################################ # Disabled warnings -DISABLED_WARNINGS_gcc := parentheses comment unknown-pragmas address \ - delete-non-virtual-dtor char-subscripts array-bounds int-in-bool-context \ - ignored-qualifiers missing-field-initializers implicit-fallthrough \ - empty-body strict-overflow sequence-point maybe-uninitialized \ - misleading-indentation cast-function-type shift-negative-value - -ifeq ($(call check-jvm-feature, zero), true) - DISABLED_WARNINGS_gcc += return-type switch clobbered +DISABLED_WARNINGS_gcc := array-bounds comment delete-non-virtual-dtor \ + empty-body ignored-qualifiers implicit-fallthrough int-in-bool-context \ + maybe-uninitialized missing-field-initializers parentheses \ + shift-negative-value unknown-pragmas strict-overflow + +DISABLED_WARNINGS_clang := ignored-qualifiers sometimes-uninitialized \ + missing-braces delete-non-abstract-non-virtual-dtor unknown-pragmas + +ifneq ($(DEBUG_LEVEL), release) + # Assert macro gives warning + DISABLED_WARNINGS_clang += tautological-constant-out-of-range-compare endif ifeq ($(DEBUG_LEVEL), fastdebug) @@ -94,16 +97,9 @@ ifeq ($(DEBUG_LEVEL), fastdebug) endif endif -DISABLED_WARNINGS_clang := tautological-compare \ - undefined-var-template sometimes-uninitialized unknown-pragmas \ - delete-non-virtual-dtor missing-braces char-subscripts \ - ignored-qualifiers missing-field-initializers mismatched-tags \ - shift-negative-value misleading-indentation - DISABLED_WARNINGS_xlc := tautological-compare shift-negative-value -DISABLED_WARNINGS_microsoft := 4100 4127 4146 4201 4244 4291 4351 \ - 4511 4512 4514 4624 4996 +DISABLED_WARNINGS_microsoft := 4624 4244 4291 4146 4127 4996 ################################################################################ # Platform specific setup @@ -157,9 +153,42 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJVM, \ abstract_vm_version.cpp_CXXFLAGS := $(CFLAGS_VM_VERSION), \ arguments.cpp_CXXFLAGS := $(CFLAGS_VM_VERSION), \ DISABLED_WARNINGS_gcc := $(DISABLED_WARNINGS_gcc), \ + DISABLED_WARNINGS_gcc_ad_$(HOTSPOT_TARGET_CPU_ARCH).cpp := nonnull, \ + DISABLED_WARNINGS_gcc_assembler_aarch64.cpp := misleading-indentation, \ + DISABLED_WARNINGS_gcc_c1_LIR.cpp := misleading-indentation, \ + DISABLED_WARNINGS_gcc_cgroupV1Subsystem_linux.cpp := address, \ + DISABLED_WARNINGS_gcc_cgroupV2Subsystem_linux.cpp := address, \ + DISABLED_WARNINGS_gcc_dict.cpp := char-subscripts, \ + DISABLED_WARNINGS_gcc_interp_masm_x86.cpp := uninitialized, \ + DISABLED_WARNINGS_gcc_javaClasses.cpp := misleading-indentation, \ + DISABLED_WARNINGS_gcc_loopnode.cpp := sequence-point, \ + DISABLED_WARNINGS_gcc_postaloc.cpp := address, \ + DISABLED_WARNINGS_gcc_sharedRuntimeTrig.cpp := misleading-indentation, \ + DISABLED_WARNINGS_gcc_shenandoahBarrierSet.cpp := misleading-indentation, \ + DISABLED_WARNINGS_gcc_shenandoahBarrierSetAssembler_aarch64.cpp := misleading-indentation, \ + DISABLED_WARNINGS_gcc_shenandoahBarrierSetAssembler_ppc.cpp := misleading-indentation, \ + DISABLED_WARNINGS_gcc_shenandoahBarrierSetAssembler_riscv.cpp := misleading-indentation, \ + DISABLED_WARNINGS_gcc_shenandoahBarrierSetAssembler_x86.cpp := misleading-indentation, \ + DISABLED_WARNINGS_gcc_shenandoahBarrierSetC1_aarch64.cpp := misleading-indentation, \ + DISABLED_WARNINGS_gcc_shenandoahBarrierSetC1_ppc.cpp := misleading-indentation, \ + DISABLED_WARNINGS_gcc_shenandoahBarrierSetC1_riscv.cpp := misleading-indentation, \ + DISABLED_WARNINGS_gcc_shenandoahBarrierSetC1_x86.cpp := misleading-indentation, \ + DISABLED_WARNINGS_gcc_shenandoahBarrierSetC1.cpp := misleading-indentation, \ DISABLED_WARNINGS_clang := $(DISABLED_WARNINGS_clang), \ + DISABLED_WARNINGS_clang_arguments.cpp := missing-field-initializers, \ + DISABLED_WARNINGS_clang_codeBuffer.cpp := tautological-undefined-compare, \ + DISABLED_WARNINGS_clang_dict.cpp := char-subscripts, \ + DISABLED_WARNINGS_clang_directivesParser.cpp := missing-field-initializers, \ + DISABLED_WARNINGS_clang_g1ParScanThreadState.cpp := delete-abstract-non-virtual-dtor, \ + DISABLED_WARNINGS_clang_g1YoungGCPostEvacuateTasks.cpp := delete-abstract-non-virtual-dtor, \ + DISABLED_WARNINGS_clang_heapShared.cpp := missing-field-initializers, \ + DISABLED_WARNINGS_clang_management.cpp := missing-field-initializers, \ DISABLED_WARNINGS_clang_notificationThread.cpp := bitwise-instead-of-logical, \ + DISABLED_WARNINGS_clang_os_posix.cpp := mismatched-tags missing-field-initializers, \ + DISABLED_WARNINGS_clang_postaloc.cpp := tautological-undefined-compare, \ DISABLED_WARNINGS_clang_serviceThread.cpp := bitwise-instead-of-logical, \ + DISABLED_WARNINGS_clang_vm_version_x86.cpp := missing-field-initializers, \ + DISABLED_WARNINGS_clang_zTracer.cpp := undefined-var-template, \ DISABLED_WARNINGS_xlc := $(DISABLED_WARNINGS_xlc), \ DISABLED_WARNINGS_microsoft := $(DISABLED_WARNINGS_microsoft), \ ASFLAGS := $(JVM_ASFLAGS), \ diff --git a/make/hotspot/lib/JvmOverrideFiles.gmk b/make/hotspot/lib/JvmOverrideFiles.gmk index a9f8a0e54edee..377018cdb518b 100644 --- a/make/hotspot/lib/JvmOverrideFiles.gmk +++ b/make/hotspot/lib/JvmOverrideFiles.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -33,9 +33,6 @@ ifeq ($(TOOLCHAIN_TYPE), gcc) BUILD_LIBJVM_vmStructs.cpp_CXXFLAGS := -fno-var-tracking-assignments BUILD_LIBJVM_jvmciCompilerToVM.cpp_CXXFLAGS := -fno-var-tracking-assignments BUILD_LIBJVM_jvmciCompilerToVMInit.cpp_CXXFLAGS := -fno-var-tracking-assignments - BUILD_LIBJVM_assembler_x86.cpp_CXXFLAGS := -Wno-maybe-uninitialized - BUILD_LIBJVM_cardTableBarrierSetAssembler_x86.cpp_CXXFLAGS := -Wno-maybe-uninitialized - BUILD_LIBJVM_interp_masm_x86.cpp_CXXFLAGS := -Wno-uninitialized ifeq ($(DEBUG_LEVEL), release) # Need extra inlining to collapse shared marking code into the hot marking loop BUILD_LIBJVM_shenandoahMark.cpp_CXXFLAGS := --param inline-unit-growth=1000 diff --git a/make/modules/java.desktop/lib/Awt2dLibraries.gmk b/make/modules/java.desktop/lib/Awt2dLibraries.gmk index bf6a987149af4..2090ca56a69d0 100644 --- a/make/modules/java.desktop/lib/Awt2dLibraries.gmk +++ b/make/modules/java.desktop/lib/Awt2dLibraries.gmk @@ -205,8 +205,12 @@ ifeq ($(call isTargetOs, windows macosx), false) common/awt/systemscale \ common/font \ common/java2d/opengl \ - common/java2d/x11 \ - $(LIBPIPEWIRE_HEADER_DIRS) + common/java2d/x11 + + # exclude pipewire from the AIX build, no Wayland support + ifeq ($(call isTargetOs, aix), false) + LIBAWT_XAWT_EXTRA_HEADER_DIRS += $(LIBPIPEWIRE_HEADER_DIRS) + endif LIBAWT_XAWT_CFLAGS += -DXAWT -DXAWT_HACK \ $(FONTCONFIG_CFLAGS) \ @@ -299,7 +303,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBLCMS, \ common/awt/debug \ libawt/java2d, \ HEADERS_FROM_SRC := $(LIBLCMS_HEADERS_FROM_SRC), \ - DISABLED_WARNINGS_gcc := format-nonliteral type-limits \ + DISABLED_WARNINGS_gcc := format-nonliteral \ misleading-indentation undef unused-function stringop-truncation, \ DISABLED_WARNINGS_clang := tautological-compare format-nonliteral undef, \ DISABLED_WARNINGS_microsoft := 4819, \ @@ -470,7 +474,7 @@ else # range-loop-analysis -> clang on Xcode12 HARFBUZZ_DISABLED_WARNINGS_gcc := type-limits missing-field-initializers strict-aliasing \ - array-bounds parentheses + array-bounds parentheses dangling-pointer # noexcept-type required for GCC 7 builds. Not required for GCC 8+. # expansion-to-defined required for GCC 9 builds. Not required for GCC 10+. # maybe-uninitialized required for GCC 8 builds. Not required for GCC 9+. @@ -773,7 +777,7 @@ ifeq ($(ENABLE_HEADLESS_ONLY), false) maybe-uninitialized shift-negative-value implicit-fallthrough \ unused-function, \ DISABLED_WARNINGS_clang := incompatible-pointer-types sign-compare \ - deprecated-declarations null-pointer-subtraction deprecated-non-prototype $(LIBZ_DISABLED_WARNINGS_CLANG), \ + deprecated-declarations null-pointer-subtraction deprecated-non-prototype unused-function $(LIBZ_DISABLED_WARNINGS_CLANG), \ DISABLED_WARNINGS_microsoft := 4018 4244 4267, \ LDFLAGS := $(LDFLAGS_JDKLIB) \ $(call SET_SHARED_LIBRARY_ORIGIN), \ @@ -866,19 +870,6 @@ endif ################################################################################ -# MACOSX_METAL_VERSION_MIN specifies the lowest version of Macosx -# that should be used to compile Metal shaders. We support Metal -# pipeline only on Macosx >=10.14. For Macosx versions <10.14 even if -# we enable Metal pipeline using -Dsun.java2d.metal=true, at -# runtime we force it to use OpenGL pipeline. And MACOSX_VERSION_MIN -# for aarch64 has always been >10.14 so we use continue to use -# MACOSX_VERSION_MIN for aarch64. -ifeq ($(OPENJDK_TARGET_CPU_ARCH), xaarch64) - MACOSX_METAL_VERSION_MIN=$(MACOSX_VERSION_MIN) -else - MACOSX_METAL_VERSION_MIN=10.14.0 -endif - ifeq ($(call isTargetOs, macosx), true) SHADERS_SRC := $(TOPDIR)/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/shaders.metal SHADERS_SUPPORT_DIR := $(SUPPORT_OUTPUTDIR)/native/java.desktop/libosxui @@ -891,7 +882,7 @@ ifeq ($(call isTargetOs, macosx), true) OUTPUT_FILE := $(SHADERS_AIR), \ SUPPORT_DIR := $(SHADERS_SUPPORT_DIR), \ COMMAND := $(METAL) -c -std=osx-metal2.0 \ - -mmacosx-version-min=$(MACOSX_METAL_VERSION_MIN) \ + -mmacosx-version-min=$(MACOSX_VERSION_MIN) \ -o $(SHADERS_AIR) $(SHADERS_SRC), \ )) diff --git a/make/modules/jdk.jdwp.agent/Lib.gmk b/make/modules/jdk.jdwp.agent/Lib.gmk index aef358c14bd5f..0a041fed9c888 100644 --- a/make/modules/jdk.jdwp.agent/Lib.gmk +++ b/make/modules/jdk.jdwp.agent/Lib.gmk @@ -55,6 +55,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJDWP, \ DISABLED_WARNINGS_gcc := unused-function, \ DISABLED_WARNINGS_clang := sometimes-uninitialized format-nonliteral \ self-assign, \ + DISABLED_WARNINGS_microsoft_debugInit.c := 5287, \ EXTRA_HEADER_DIRS := \ include \ libjdwp/export, \ diff --git a/make/test/BuildTestLib.gmk b/make/test/BuildTestLib.gmk index f1574988b6fee..1b40bafe5a9b1 100644 --- a/make/test/BuildTestLib.gmk +++ b/make/test/BuildTestLib.gmk @@ -54,11 +54,9 @@ $(eval $(call SetupJavaCompilation, BUILD_TEST_LIB_JAR, \ JAR := $(TEST_LIB_SUPPORT)/test-lib.jar, \ DISABLED_WARNINGS := try deprecation rawtypes unchecked serial cast removal preview, \ JAVAC_FLAGS := --add-exports java.base/sun.security.util=ALL-UNNAMED \ - --add-exports java.base/jdk.internal.classfile=ALL-UNNAMED \ - --add-exports java.base/jdk.internal.classfile.attribute=ALL-UNNAMED \ - --add-exports java.base/jdk.internal.classfile.constantpool=ALL-UNNAMED \ - --add-exports java.base/jdk.internal.classfile.java.lang.constant=ALL-UNNAMED \ --add-exports java.base/jdk.internal.module=ALL-UNNAMED \ + --add-exports java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED \ + --add-exports java.base/jdk.internal.org.objectweb.asm.commons=ALL-UNNAMED \ --enable-preview, \ )) diff --git a/make/test/JtregNativeHotspot.gmk b/make/test/JtregNativeHotspot.gmk index cd03747d98d8c..27623c3db853c 100644 --- a/make/test/JtregNativeHotspot.gmk +++ b/make/test/JtregNativeHotspot.gmk @@ -873,7 +873,7 @@ BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exesigtest := -ljvm ifeq ($(call isTargetOs, windows), true) BUILD_HOTSPOT_JTREG_EXECUTABLES_CFLAGS_exeFPRegs := -MT - BUILD_HOTSPOT_JTREG_EXCLUDE += exesigtest.c libterminatedThread.c libTestJNI.c libnativeStack.c exeGetCreatedJavaVMs.c + BUILD_HOTSPOT_JTREG_EXCLUDE += exesigtest.c libterminatedThread.c libTestJNI.c libnativeStack.c exeGetCreatedJavaVMs.c libTestUnloadedClass.cpp BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libatExit := jvm.lib else BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libbootclssearch_agent += -lpthread @@ -1511,6 +1511,7 @@ else BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libterminatedThread += -lpthread BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libatExit += -ljvm BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libnativeStack += -lpthread + BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libTestUnloadedClass += -lpthread BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exeGetCreatedJavaVMs := -ljvm -lpthread endif diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index bf03fbe0303a4..6f82608fc3227 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -1761,8 +1761,8 @@ int MachCallRuntimeNode::ret_addr_offset() { // for real runtime callouts it will be six instructions // see aarch64_enc_java_to_runtime // adr(rscratch2, retaddr) + // str(rscratch2, Address(rthread, JavaThread::last_Java_pc_offset())); // lea(rscratch1, RuntimeAddress(addr) - // stp(zr, rscratch2, Address(__ pre(sp, -2 * wordSize))) // blr(rscratch1) CodeBlob *cb = CodeCache::find_blob(_entry_point); if (cb) { @@ -2736,10 +2736,6 @@ typedef void (MacroAssembler::* mem_vector_insn)(FloatRegister Rt, { Address addr = mem2address(opcode, base, index, scale, disp); if (addr.getMode() == Address::base_plus_offset) { - /* If we get an out-of-range offset it is a bug in the compiler, - so we assert here. */ - assert(Address::offset_ok_for_immed(addr.offset(), exact_log2(size_in_memory)), - "c2 compiler bug"); /* Fix up any out-of-range offsets. */ assert_different_registers(rscratch1, base); assert_different_registers(rscratch1, reg); @@ -3402,7 +3398,7 @@ encode %{ } %} - /// mov envcodings + // mov encodings enc_class aarch64_enc_movw_imm(iRegI dst, immI src) %{ C2_MacroAssembler _masm(&cbuf); @@ -3755,13 +3751,12 @@ encode %{ } } else { Label retaddr; + // Make the anchor frame walkable __ adr(rscratch2, retaddr); + __ str(rscratch2, Address(rthread, JavaThread::last_Java_pc_offset())); __ lea(rscratch1, RuntimeAddress(entry)); - // Leave a breadcrumb for JavaFrameAnchor::capture_last_Java_pc() - __ stp(zr, rscratch2, Address(__ pre(sp, -2 * wordSize))); __ blr(rscratch1); __ bind(retaddr); - __ add(sp, sp, 2 * wordSize); } if (Compile::current()->max_vector_size() >= 16) { __ reinitialize_ptrue(); diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.cpp b/src/hotspot/cpu/aarch64/assembler_aarch64.cpp index d31f638832b17..70a750f0043bb 100644 --- a/src/hotspot/cpu/aarch64/assembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/assembler_aarch64.cpp @@ -134,7 +134,16 @@ void Assembler::adrp(Register reg1, const Address &dest, uint64_t &byte_offset) #undef __ -#define starti Instruction_aarch64 do_not_use(this); set_current(&do_not_use) +#define starti Instruction_aarch64 current_insn(this); + +#define f current_insn.f +#define sf current_insn.sf +#define rf current_insn.rf +#define srf current_insn.srf +#define zrf current_insn.zrf +#define prf current_insn.prf +#define pgrf current_insn.pgrf +#define fixed current_insn.fixed void Assembler::adr(Register Rd, address adr) { intptr_t offset = adr - pc(); @@ -156,6 +165,53 @@ void Assembler::adrp(Register reg1, const Address &dest, uint64_t &byte_offset) rf(Rd, 0); } +// An "all-purpose" add/subtract immediate, per ARM documentation: +// A "programmer-friendly" assembler may accept a negative immediate +// between -(2^24 -1) and -1 inclusive, causing it to convert a +// requested ADD operation to a SUB, or vice versa, and then encode +// the absolute value of the immediate as for uimm24. +void Assembler::add_sub_immediate(Instruction_aarch64 ¤t_insn, + Register Rd, Register Rn, unsigned uimm, int op, + int negated_op) { + bool sets_flags = op & 1; // this op sets flags + union { + unsigned u; + int imm; + }; + u = uimm; + bool shift = false; + bool neg = imm < 0; + if (neg) { + imm = -imm; + op = negated_op; + } + assert(Rd != sp || imm % 16 == 0, "misaligned stack"); + if (imm >= (1 << 11) + && ((imm >> 12) << 12 == imm)) { + imm >>= 12; + shift = true; + } + f(op, 31, 29), f(0b10001, 28, 24), f(shift, 23, 22), f(imm, 21, 10); + + // add/subtract immediate ops with the S bit set treat r31 as zr; + // with S unset they use sp. + if (sets_flags) + zrf(Rd, 0); + else + srf(Rd, 0); + + srf(Rn, 5); +} + +#undef f +#undef sf +#undef rf +#undef srf +#undef zrf +#undef prf +#undef pgrf +#undef fixed + #undef starti Address::Address(address target, relocInfo::relocType rtype) : _mode(literal){ @@ -260,46 +316,9 @@ void Assembler::wrap_label(Label &L, prfop op, prefetch_insn insn) { } } -// An "all-purpose" add/subtract immediate, per ARM documentation: -// A "programmer-friendly" assembler may accept a negative immediate -// between -(2^24 -1) and -1 inclusive, causing it to convert a -// requested ADD operation to a SUB, or vice versa, and then encode -// the absolute value of the immediate as for uimm24. -void Assembler::add_sub_immediate(Register Rd, Register Rn, unsigned uimm, int op, - int negated_op) { - bool sets_flags = op & 1; // this op sets flags - union { - unsigned u; - int imm; - }; - u = uimm; - bool shift = false; - bool neg = imm < 0; - if (neg) { - imm = -imm; - op = negated_op; - } - assert(Rd != sp || imm % 16 == 0, "misaligned stack"); - if (imm >= (1 << 11) - && ((imm >> 12) << 12 == imm)) { - imm >>= 12; - shift = true; - } - f(op, 31, 29), f(0b10001, 28, 24), f(shift, 23, 22), f(imm, 21, 10); - - // add/subtract immediate ops with the S bit set treat r31 as zr; - // with S unset they use sp. - if (sets_flags) - zrf(Rd, 0); - else - srf(Rd, 0); - - srf(Rn, 5); -} - bool Assembler::operand_valid_for_add_sub_immediate(int64_t imm) { bool shift = false; - uint64_t uimm = (uint64_t)uabs((jlong)imm); + uint64_t uimm = (uint64_t)g_uabs((jlong)imm); if (uimm < (1 << 12)) return true; if (uimm < (1 << 24) diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp index e755d4cee8819..12fff1972bd90 100644 --- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp @@ -247,12 +247,12 @@ class Instruction_aarch64 { int nbits = msb - lsb + 1; guarantee(val < (1ULL << nbits), "Field too big for insn"); assert_cond(msb >= lsb); - unsigned mask = checked_cast(right_n_bits(nbits)); val <<= lsb; - mask <<= lsb; insn |= val; - assert_cond((bits & mask) == 0); #ifdef ASSERT + unsigned mask = checked_cast(right_n_bits(nbits)); + mask <<= lsb; + assert_cond((bits & mask) == 0); bits |= mask; #endif } @@ -313,7 +313,7 @@ class Instruction_aarch64 { } }; -#define starti Instruction_aarch64 do_not_use(this); set_current(&do_not_use) +#define starti Instruction_aarch64 current_insn(this); class PrePost { int _offset; @@ -695,46 +695,14 @@ class Assembler : public AbstractAssembler { static address locate_next_instruction(address inst); - Instruction_aarch64* current; - - void set_current(Instruction_aarch64* i) { current = i; } - - void f(unsigned val, int msb, int lsb) { - current->f(val, msb, lsb); - } - void f(unsigned val, int msb) { - current->f(val, msb, msb); - } - void sf(int64_t val, int msb, int lsb) { - current->sf(val, msb, lsb); - } - void rf(Register reg, int lsb) { - current->rf(reg, lsb); - } - void srf(Register reg, int lsb) { - current->srf(reg, lsb); - } - void zrf(Register reg, int lsb) { - current->zrf(reg, lsb); - } - void rf(FloatRegister reg, int lsb) { - current->rf(reg, lsb); - } - void prf(PRegister reg, int lsb) { - current->prf(reg, lsb); - } - void pgrf(PRegister reg, int lsb) { - current->pgrf(reg, lsb); - } - void fixed(unsigned value, unsigned mask) { - current->fixed(value, mask); - } - - void emit() { - emit_long(current->get_insn()); - assert_cond(current->get_bits() == 0xffffffff); - current = NULL; - } +#define f current_insn.f +#define sf current_insn.sf +#define rf current_insn.rf +#define srf current_insn.srf +#define zrf current_insn.zrf +#define prf current_insn.prf +#define pgrf current_insn.pgrf +#define fixed current_insn.fixed typedef void (Assembler::* uncond_branch_insn)(address dest); typedef void (Assembler::* compare_and_branch_insn)(Register Rt, address dest); @@ -765,8 +733,8 @@ class Assembler : public AbstractAssembler { #undef INSN - void add_sub_immediate(Register Rd, Register Rn, unsigned uimm, int op, - int negated_op); + void add_sub_immediate(Instruction_aarch64 ¤t_insn, Register Rd, Register Rn, + unsigned uimm, int op, int negated_op); // Add/subtract (immediate) #define INSN(NAME, decode, negated) \ @@ -778,7 +746,7 @@ class Assembler : public AbstractAssembler { \ void NAME(Register Rd, Register Rn, unsigned imm) { \ starti; \ - add_sub_immediate(Rd, Rn, imm, decode, negated); \ + add_sub_immediate(current_insn, Rd, Rn, imm, decode, negated); \ } INSN(addsw, 0b001, 0b011); @@ -791,7 +759,7 @@ class Assembler : public AbstractAssembler { #define INSN(NAME, decode, negated) \ void NAME(Register Rd, Register Rn, unsigned imm) { \ starti; \ - add_sub_immediate(Rd, Rn, imm, decode, negated); \ + add_sub_immediate(current_insn, Rd, Rn, imm, decode, negated); \ } INSN(addw, 0b000, 0b010); @@ -890,7 +858,7 @@ class Assembler : public AbstractAssembler { static const uint64_t branch_range = NOT_DEBUG(128 * M) DEBUG_ONLY(2 * M); static bool reachable_from_branch_at(address branch, address target) { - return uabs(target - branch) < branch_range; + return g_uabs(target - branch) < branch_range; } // Unconditional branch (immediate) @@ -1093,7 +1061,7 @@ class Assembler : public AbstractAssembler { } void sys(int op1, int CRn, int CRm, int op2, - Register rt = (Register)0b11111) { + Register rt = as_Register(0b11111)) { system(0b01, op1, CRn, CRm, op2, rt); } @@ -1362,7 +1330,7 @@ class Assembler : public AbstractAssembler { starti; \ f(opc, 31, 30), f(0b011, 29, 27), f(V, 26), f(0b00, 25, 24), \ sf(offset, 23, 5); \ - rf((Register)Rt, 0); \ + rf(as_Register(Rt), 0); \ } INSN(ldrs, 0b00, 1); @@ -1376,7 +1344,7 @@ class Assembler : public AbstractAssembler { starti; \ f(size, 31, 30), f(0b111100, 29, 24), f(opc, 23, 22), f(0, 21); \ f(0, 20, 12), f(0b01, 11, 10); \ - rf(Rn, 5), rf((Register)Rt, 0); \ + rf(Rn, 5), rf(as_Register(Rt), 0); \ } INSN(ldrs, 0b10, 0b01); @@ -1409,9 +1377,9 @@ class Assembler : public AbstractAssembler { f(opc, 31, 30), f(p1, 29, 27), f(V, 26), f(L, 22); zrf(Rt2, 10), zrf(Rt1, 0); if (no_allocate) { - adr.encode_nontemporal_pair(current); + adr.encode_nontemporal_pair(¤t_insn); } else { - adr.encode_pair(current); + adr.encode_pair(¤t_insn); } } @@ -1437,7 +1405,8 @@ class Assembler : public AbstractAssembler { #define INSN(NAME, size, p1, V, L, no_allocate) \ void NAME(FloatRegister Rt1, FloatRegister Rt2, Address adr) { \ - ld_st1(size, p1, V, L, (Register)Rt1, (Register)Rt2, adr, no_allocate); \ + ld_st1(size, p1, V, L, \ + as_Register(Rt1), as_Register(Rt2), adr, no_allocate); \ } INSN(stps, 0b00, 0b101, 1, 0, false); @@ -1472,7 +1441,7 @@ class Assembler : public AbstractAssembler { f(size, 31, 30); f(op, 23, 22); // str - adr.encode(current); + adr.encode(¤t_insn); } #define INSN(NAME, size, op) \ @@ -1500,7 +1469,7 @@ class Assembler : public AbstractAssembler { #define INSN(NAME, size, op) \ void NAME(const Address &adr, prfop pfop = PLDL1KEEP) { \ - ld_st2((Register)pfop, adr, size, op); \ + ld_st2(as_Register(pfop), adr, size, op); \ } INSN(prfm, 0b11, 0b10); // FIXME: PRFM should not be used with @@ -1511,7 +1480,7 @@ class Assembler : public AbstractAssembler { #define INSN(NAME, size, op) \ void NAME(FloatRegister Rt, const Address &adr) { \ - ld_st2((Register)Rt, adr, size, op, 1); \ + ld_st2(as_Register(Rt), adr, size, op, 1); \ } INSN(strd, 0b11, 0b00); @@ -1548,7 +1517,7 @@ class Assembler : public AbstractAssembler { enum shift_kind { LSL, LSR, ASR, ROR }; - void op_shifted_reg(unsigned decode, + void op_shifted_reg(Instruction_aarch64 ¤t_insn, unsigned decode, enum shift_kind kind, unsigned shift, unsigned size, unsigned op) { f(size, 31); @@ -1559,14 +1528,14 @@ class Assembler : public AbstractAssembler { } // Logical (shifted register) -#define INSN(NAME, size, op, N) \ - void NAME(Register Rd, Register Rn, Register Rm, \ - enum shift_kind kind = LSL, unsigned shift = 0) { \ - starti; \ - guarantee(size == 1 || shift < 32, "incorrect shift"); \ - f(N, 21); \ - zrf(Rm, 16), zrf(Rn, 5), zrf(Rd, 0); \ - op_shifted_reg(0b01010, kind, shift, size, op); \ +#define INSN(NAME, size, op, N) \ + void NAME(Register Rd, Register Rn, Register Rm, \ + enum shift_kind kind = LSL, unsigned shift = 0) { \ + starti; \ + guarantee(size == 1 || shift < 32, "incorrect shift"); \ + f(N, 21); \ + zrf(Rm, 16), zrf(Rn, 5), zrf(Rd, 0); \ + op_shifted_reg(current_insn, 0b01010, kind, shift, size, op); \ } INSN(andr, 1, 0b00, 0); @@ -1586,7 +1555,7 @@ class Assembler : public AbstractAssembler { starti; \ f(N, 21); \ zrf(Rm, 16), zrf(Rn, 5), zrf(Rd, 0); \ - op_shifted_reg(0b01010, kind, shift, size, op); \ + op_shifted_reg(current_insn, 0b01010, kind, shift, size, op); \ } \ \ /* These instructions have no immediate form. Provide an overload so \ @@ -1633,7 +1602,7 @@ void mvnw(Register Rd, Register Rm, assert_cond(kind != ROR); \ guarantee(size == 1 || shift < 32, "incorrect shift");\ zrf(Rd, 0), zrf(Rn, 5), zrf(Rm, 16); \ - op_shifted_reg(0b01011, kind, shift, size, op); \ + op_shifted_reg(current_insn, 0b01011, kind, shift, size, op); \ } INSN(add, 1, 0b000); @@ -1654,10 +1623,10 @@ void mvnw(Register Rd, Register Rm, ext::operation option, int amount = 0) { \ starti; \ zrf(Rm, 16), srf(Rn, 5), srf(Rd, 0); \ - add_sub_extended_reg(op, 0b01011, Rd, Rn, Rm, 0b00, option, amount); \ + add_sub_extended_reg(current_insn, op, 0b01011, Rd, Rn, Rm, 0b00, option, amount); \ } - void add_sub_extended_reg(unsigned op, unsigned decode, + void add_sub_extended_reg(Instruction_aarch64 ¤t_insn, unsigned op, unsigned decode, Register Rd, Register Rn, Register Rm, unsigned opt, ext::operation option, unsigned imm) { guarantee(imm <= 4, "shift amount must be <= 4"); @@ -1677,7 +1646,7 @@ void mvnw(Register Rd, Register Rm, ext::operation option, int amount = 0) { \ starti; \ zrf(Rm, 16), srf(Rn, 5), zrf(Rd, 0); \ - add_sub_extended_reg(op, 0b01011, Rd, Rn, Rm, 0b00, option, amount); \ + add_sub_extended_reg(current_insn, op, 0b01011, Rd, Rn, Rm, 0b00, option, amount); \ } INSN(addsw, 0b001); @@ -1778,7 +1747,7 @@ void mvnw(Register Rd, Register Rm, } #define INSN(NAME, op, op2) \ - void NAME(Register Rd, Register Rn, Register Rm, Condition cond) { \ + void NAME(Register Rd, Register Rn, Register Rm, Condition cond) { \ conditional_select(op, op2, Rd, Rn, Rm, cond); \ } @@ -1794,7 +1763,7 @@ void mvnw(Register Rd, Register Rm, #undef INSN // Data processing - void data_processing(unsigned op29, unsigned opcode, + void data_processing(Instruction_aarch64 ¤t_insn, unsigned op29, unsigned opcode, Register Rd, Register Rn) { f(op29, 31, 29), f(0b11010110, 28, 21); f(opcode, 15, 10); @@ -1802,11 +1771,11 @@ void mvnw(Register Rd, Register Rm, } // (1 source) -#define INSN(NAME, op29, opcode2, opcode) \ - void NAME(Register Rd, Register Rn) { \ - starti; \ - f(opcode2, 20, 16); \ - data_processing(op29, opcode, Rd, Rn); \ +#define INSN(NAME, op29, opcode2, opcode) \ + void NAME(Register Rd, Register Rn) { \ + starti; \ + f(opcode2, 20, 16); \ + data_processing(current_insn, op29, opcode, Rd, Rn); \ } INSN(rbitw, 0b010, 0b00000, 0b00000); @@ -1825,11 +1794,11 @@ void mvnw(Register Rd, Register Rm, #undef INSN // (2 sources) -#define INSN(NAME, op29, opcode) \ - void NAME(Register Rd, Register Rn, Register Rm) { \ - starti; \ - rf(Rm, 16); \ - data_processing(op29, opcode, Rd, Rn); \ +#define INSN(NAME, op29, opcode) \ + void NAME(Register Rd, Register Rn, Register Rm) { \ + starti; \ + rf(Rm, 16); \ + data_processing(current_insn, op29, opcode, Rd, Rn); \ } INSN(udivw, 0b000, 0b000010); @@ -1874,9 +1843,9 @@ void mvnw(Register Rd, Register Rm, #undef INSN -#define INSN(NAME, op54, op31, o0) \ - void NAME(Register Rd, Register Rn, Register Rm) { \ - data_processing(op54, op31, o0, Rd, Rn, Rm, (Register)31); \ +#define INSN(NAME, op54, op31, o0) \ + void NAME(Register Rd, Register Rn, Register Rm) { \ + data_processing(op54, op31, o0, Rd, Rn, Rm, as_Register(31)); \ } INSN(smulh, 0b100, 0b010, 0); @@ -2055,7 +2024,7 @@ void mvnw(Register Rd, Register Rm, #define INSN(NAME, op31, type, rmode, opcode) \ void NAME(Register Rd, FloatRegister Vn) { \ - float_int_convert(op31, type, rmode, opcode, Rd, (Register)Vn); \ + float_int_convert(op31, type, rmode, opcode, Rd, as_Register(Vn)); \ } INSN(fcvtzsw, 0b000, 0b00, 0b11, 0b000); @@ -2072,7 +2041,7 @@ void mvnw(Register Rd, Register Rm, #define INSN(NAME, op31, type, rmode, opcode) \ void NAME(FloatRegister Vd, Register Rn) { \ - float_int_convert(op31, type, rmode, opcode, (Register)Vd, Rn); \ + float_int_convert(op31, type, rmode, opcode, as_Register(Vd), Rn); \ } INSN(fmovs, 0b000, 0b00, 0b00, 0b111); @@ -2127,7 +2096,7 @@ void mvnw(Register Rd, Register Rm, // Floating-point compare void float_compare(unsigned op31, unsigned type, unsigned op, unsigned op2, - FloatRegister Vn, FloatRegister Vm = (FloatRegister)0) { + FloatRegister Vn, FloatRegister Vm = as_FloatRegister(0)) { starti; f(op31, 31, 29); f(0b11110, 28, 24); @@ -2257,10 +2226,10 @@ void mvnw(Register Rd, Register Rm, static short SIMD_Size_in_bytes[]; public: -#define INSN(NAME, op) \ - void NAME(FloatRegister Rt, SIMD_RegVariant T, const Address &adr) { \ - ld_st2((Register)Rt, adr, (int)T & 3, op + ((T==Q) ? 0b10:0b00), 1); \ - } \ +#define INSN(NAME, op) \ + void NAME(FloatRegister Rt, SIMD_RegVariant T, const Address &adr) { \ + ld_st2(as_Register(Rt), adr, (int)T & 3, op + ((T==Q) ? 0b10:0b00), 1); \ + } INSN(ldr, 1); INSN(str, 0); @@ -3273,9 +3242,19 @@ inline Assembler::Membar_mask_bits operator|(Assembler::Membar_mask_bits a, } Instruction_aarch64::~Instruction_aarch64() { - assem->emit(); + assem->emit_int32(insn); + assert_cond(get_bits() == 0xffffffff); } +#undef f +#undef sf +#undef rf +#undef srf +#undef zrf +#undef prf +#undef pgrf +#undef fixed + #undef starti // Invert a condition diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp index ae5ee625b8c60..450bd28fbcebf 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -276,14 +276,14 @@ void LIR_Assembler::osr_entry() { // verify the interpreter's monitor has a non-null object { Label L; - __ ldr(rscratch1, Address(OSR_buf, slot_offset + 1*BytesPerWord)); + __ ldr(rscratch1, __ form_address(rscratch1, OSR_buf, slot_offset + 1*BytesPerWord, 0)); __ cbnz(rscratch1, L); __ stop("locked object is NULL"); __ bind(L); } #endif - __ ldr(r19, Address(OSR_buf, slot_offset)); - __ ldr(r20, Address(OSR_buf, slot_offset + BytesPerWord)); + __ ldr(r19, __ form_address(rscratch1, OSR_buf, slot_offset, 0)); + __ ldr(r20, __ form_address(rscratch1, OSR_buf, slot_offset + BytesPerWord, 0)); __ str(r19, frame_map()->address_for_monitor_lock(i)); __ str(r20, frame_map()->address_for_monitor_object(i)); } diff --git a/src/hotspot/cpu/aarch64/foreign_globals_aarch64.cpp b/src/hotspot/cpu/aarch64/foreign_globals_aarch64.cpp index 6531eb03edc47..d08afc79a5245 100644 --- a/src/hotspot/cpu/aarch64/foreign_globals_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/foreign_globals_aarch64.cpp @@ -45,17 +45,18 @@ bool ABIDescriptor::is_volatile_reg(FloatRegister reg) const { const ABIDescriptor ForeignGlobals::parse_abi_descriptor_impl(jobject jabi) const { oop abi_oop = JNIHandles::resolve_non_null(jabi); ABIDescriptor abi; + const Register (*to_Register)(int) = as_Register; objArrayOop inputStorage = cast(abi_oop->obj_field(ABI.inputStorage_offset)); - loadArray(inputStorage, INTEGER_TYPE, abi._integer_argument_registers, as_Register); + loadArray(inputStorage, INTEGER_TYPE, abi._integer_argument_registers, to_Register); loadArray(inputStorage, VECTOR_TYPE, abi._vector_argument_registers, as_FloatRegister); objArrayOop outputStorage = cast(abi_oop->obj_field(ABI.outputStorage_offset)); - loadArray(outputStorage, INTEGER_TYPE, abi._integer_return_registers, as_Register); + loadArray(outputStorage, INTEGER_TYPE, abi._integer_return_registers, to_Register); loadArray(outputStorage, VECTOR_TYPE, abi._vector_return_registers, as_FloatRegister); objArrayOop volatileStorage = cast(abi_oop->obj_field(ABI.volatileStorage_offset)); - loadArray(volatileStorage, INTEGER_TYPE, abi._integer_additional_volatile_registers, as_Register); + loadArray(volatileStorage, INTEGER_TYPE, abi._integer_additional_volatile_registers, to_Register); loadArray(volatileStorage, VECTOR_TYPE, abi._vector_additional_volatile_registers, as_FloatRegister); abi._stack_alignment_bytes = abi_oop->int_field(ABI.stackAlignment_offset); diff --git a/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp b/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp index 3c779bb11b15c..f9cc3a61f13ed 100644 --- a/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2015, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -67,4 +67,6 @@ const bool CCallingConventionRequiresIntsAsLongs = false; #define NOT_R18_RESERVED(code) code #endif +#define USE_TRAMPOLINE_STUB_FIX_OWNER + #endif // CPU_AARCH64_GLOBALDEFINITIONS_AARCH64_HPP diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp index 1db103730f19f..31076a35c4f21 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp @@ -1850,3 +1850,15 @@ void InterpreterMacroAssembler::profile_parameters_type(Register mdp, Register t bind(profile_continue); } } + +#ifdef ASSERT +void InterpreterMacroAssembler::verify_field_offset(Register reg) { + // Verify the field offset is not in the header, implicitly checks for 0 + Label L; + subs(zr, reg, static_cast(sizeof(markWord) + (UseCompressedClassPointers ? sizeof(narrowKlass) : sizeof(Klass*)))); + br(Assembler::GE, L); + stop("bad field offset"); + bind(L); +} +#endif + diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp index f8d0b4adf923e..b111ca69d9286 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp @@ -292,6 +292,8 @@ class InterpreterMacroAssembler: public MacroAssembler { set_last_Java_frame(esp, rfp, (address) pc(), rscratch1); MacroAssembler::_call_Unimplemented(call_site); } + + void verify_field_offset(Register reg) NOT_DEBUG_RETURN; }; #endif // CPU_AARCH64_INTERP_MASM_AARCH64_HPP diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index 7abb2205414fd..c9a8db901e44a 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -2410,7 +2410,7 @@ void MacroAssembler::wrap_add_sub_imm_insn(Register Rd, Register Rn, uint64_t im if (fits) { (this->*insn1)(Rd, Rn, imm); } else { - if (uabs(imm) < (1 << 24)) { + if (g_uabs(imm) < (1 << 24)) { (this->*insn1)(Rd, Rn, imm & -(1 << 12)); (this->*insn1)(Rd, Rd, imm & ((1 << 12)-1)); } else { diff --git a/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp index 66b2769aca5db..9795d6a7d8339 100644 --- a/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -158,13 +158,18 @@ void NativeGotJump::verify() const { } address NativeCall::destination() const { - address addr = (address)this; - address destination = instruction_address() + displacement(); + address addr = instruction_address(); + address destination = addr + displacement(); + + // Performance optimization: no need to call find_blob() if it is a self-call + if (destination == addr) { + return destination; + } // Do we use a trampoline stub for this call? CodeBlob* cb = CodeCache::find_blob_unsafe(addr); // Else we get assertion if nmethod is zombie. - assert(cb && cb->is_nmethod(), "sanity"); - nmethod *nm = (nmethod *)cb; + assert(cb != nullptr && cb->is_nmethod(), "nmethod expected"); + nmethod *nm = cb->as_nmethod(); if (nm->stub_contains(destination) && is_NativeCallTrampolineStub_at(destination)) { // Yes we do, so get the destination from the trampoline stub. const address trampoline_stub_addr = destination; @@ -179,12 +184,8 @@ address NativeCall::destination() const { // call instruction at all times. // // Used in the runtime linkage of calls; see class CompiledIC. -// -// Add parameter assert_lock to switch off assertion -// during code generation, where no patching lock is needed. -void NativeCall::set_destination_mt_safe(address dest, bool assert_lock) { - assert(!assert_lock || - (Patching_lock->is_locked() || SafepointSynchronize::is_at_safepoint()) || +void NativeCall::set_destination_mt_safe(address dest) { + assert((Patching_lock->is_locked() || SafepointSynchronize::is_at_safepoint()) || CompiledICLocker::is_safe(addr_at(0)), "concurrent code patching"); @@ -211,22 +212,18 @@ void NativeCall::set_destination_mt_safe(address dest, bool assert_lock) { } address NativeCall::get_trampoline() { - address call_addr = addr_at(0); + address call_addr = instruction_address(); CodeBlob *code = CodeCache::find_blob(call_addr); - assert(code != NULL, "Could not find the containing code blob"); + assert(code != nullptr && code->is_nmethod(), "nmethod expected"); + nmethod* nm = code->as_nmethod(); - address bl_destination - = MacroAssembler::pd_call_destination(call_addr); - if (code->contains(bl_destination) && + address bl_destination = call_addr + displacement(); + if (nm->stub_contains(bl_destination) && is_NativeCallTrampolineStub_at(bl_destination)) return bl_destination; - if (code->is_nmethod()) { - return trampoline_stub_Relocation::get_trampoline_for(call_addr, (nmethod*)code); - } - - return NULL; + return trampoline_stub_Relocation::get_trampoline_for(call_addr, nm); } // Inserts a native call instruction at a given pc diff --git a/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp b/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp index 57366ad3c9c81..6a6834fbb9832 100644 --- a/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2108, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -212,6 +212,7 @@ class NativeCall: public NativeInstruction { int displacement() const { return (int_at(displacement_offset) << 6) >> 4; } address displacement_address() const { return addr_at(displacement_offset); } address return_address() const { return addr_at(return_address_offset); } + address raw_destination() const { return instruction_address() + displacement(); } address destination() const; void set_destination(address dest) { @@ -251,9 +252,7 @@ class NativeCall: public NativeInstruction { // // Used in the runtime linkage of calls; see class CompiledIC. // (Cf. 4506997 and 4479829, where threads witnessed garbage displacements.) - - // The parameter assert_lock disables the assertion during code generation. - void set_destination_mt_safe(address dest, bool assert_lock = true); + void set_destination_mt_safe(address dest); address get_trampoline(); #if INCLUDE_JVMCI diff --git a/src/hotspot/cpu/aarch64/register_aarch64.hpp b/src/hotspot/cpu/aarch64/register_aarch64.hpp index 459e24639fd83..9a4749e8673d1 100644 --- a/src/hotspot/cpu/aarch64/register_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/register_aarch64.hpp @@ -36,7 +36,7 @@ typedef VMRegImpl* VMReg; class RegisterImpl; typedef RegisterImpl* Register; -inline Register as_Register(int encoding) { +inline const Register as_Register(int encoding) { return (Register)(intptr_t) encoding; } @@ -53,7 +53,7 @@ class RegisterImpl: public AbstractRegisterImpl { Register successor() const { return as_Register(encoding() + 1); } // construction - inline friend Register as_Register(int encoding); + inline friend const Register as_Register(int encoding); VMReg as_VMReg(); @@ -426,4 +426,8 @@ inline FloatRegister AbstractRegSet::first() { return first ? as_FloatRegister(exact_log2(first)) : fnoreg; } +inline Register as_Register(FloatRegister reg) { + return as_Register(reg->encoding()); +} + #endif // CPU_AARCH64_REGISTER_AARCH64_HPP diff --git a/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp b/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp index 49cc320709850..06c11af8a0195 100644 --- a/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp @@ -60,13 +60,12 @@ void Relocation::pd_set_data_value(address x, intptr_t o, bool verify_only) { address Relocation::pd_call_destination(address orig_addr) { assert(is_call(), "should be a call here"); - if (NativeCall::is_call_at(addr())) { - address trampoline = nativeCall_at(addr())->get_trampoline(); - if (trampoline) { - return nativeCallTrampolineStub_at(trampoline)->destination(); + if (orig_addr == nullptr) { + if (NativeCall::is_call_at(addr())) { + NativeCall* call = nativeCall_at(addr()); + return call->destination(); } - } - if (orig_addr != NULL) { + } else { address new_addr = MacroAssembler::pd_call_destination(orig_addr); // If call is branch to self, don't try to relocate it, just leave it // as branch to self. This happens during code generation if the code @@ -82,16 +81,26 @@ address Relocation::pd_call_destination(address orig_addr) { void Relocation::pd_set_call_destination(address x) { assert(is_call(), "should be a call here"); if (NativeCall::is_call_at(addr())) { - address trampoline = nativeCall_at(addr())->get_trampoline(); - if (trampoline) { - nativeCall_at(addr())->set_destination_mt_safe(x, /* assert_lock */false); - return; - } + NativeCall* call = nativeCall_at(addr()); + call->set_destination(x); + } else { + MacroAssembler::pd_patch_instruction(addr(), x); } - MacroAssembler::pd_patch_instruction(addr(), x); assert(pd_call_destination(addr()) == x, "fail in reloc"); } +void trampoline_stub_Relocation::pd_fix_owner_after_move() { + NativeCall* call = nativeCall_at(owner()); + assert(call->raw_destination() == owner(), "destination should be empty"); + address trampoline = addr(); + address dest = nativeCallTrampolineStub_at(trampoline)->destination(); + if (!Assembler::reachable_from_branch_at(owner(), dest)) { + dest = trampoline; + } + call->set_destination(dest); +} + + address* Relocation::pd_address_in_code() { return (address*)(addr() + 8); } diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index c92f0cf05bd66..4920b7cb47c03 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -1042,7 +1042,7 @@ class StubGenerator: public StubCodeGenerator { void copy_memory_small(Register s, Register d, Register count, Register tmp, int step) { bool is_backwards = step < 0; - size_t granularity = uabs(step); + size_t granularity = g_uabs(step); int direction = is_backwards ? -1 : 1; int unit = wordSize * direction; @@ -1098,7 +1098,7 @@ class StubGenerator: public StubCodeGenerator { Register count, Register tmp, int step) { copy_direction direction = step < 0 ? copy_backwards : copy_forwards; bool is_backwards = step < 0; - unsigned int granularity = uabs(step); + unsigned int granularity = g_uabs(step); const Register t0 = r3, t1 = r4; // <= 80 (or 96 for SIMD) bytes do inline. Direction doesn't matter because we always diff --git a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp index ec577c261d34a..1b24354604282 100644 --- a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp @@ -165,6 +165,7 @@ void TemplateTable::patch_bytecode(Bytecodes::Code bc, Register bc_reg, Register temp_reg, bool load_bc_into_bc_reg/*=true*/, int byte_no) { + assert_different_registers(bc_reg, temp_reg); if (!RewriteBytecodes) return; Label L_patch_done; @@ -222,8 +223,12 @@ void TemplateTable::patch_bytecode(Bytecodes::Code bc, Register bc_reg, __ bind(L_okay); #endif - // patch bytecode - __ strb(bc_reg, at_bcp(0)); + // Patch bytecode with release store to coordinate with ConstantPoolCacheEntry loads + // in fast bytecode codelets. The fast bytecode codelets have a memory barrier that gains + // the needed ordering, together with control dependency on entering the fast codelet + // itself. + __ lea(temp_reg, at_bcp(0)); + __ stlrb(bc_reg, temp_reg); __ bind(L_patch_done); } @@ -309,12 +314,12 @@ void TemplateTable::sipush() __ asrw(r0, r0, 16); } -void TemplateTable::ldc(bool wide) +void TemplateTable::ldc(LdcType type) { transition(vtos, vtos); Label call_ldc, notFloat, notClass, notInt, Done; - if (wide) { + if (is_ldc_wide(type)) { __ get_unsigned_2_byte_index_at_bcp(r1, 1); } else { __ load_unsigned_byte(r1, at_bcp(1)); @@ -343,7 +348,7 @@ void TemplateTable::ldc(bool wide) __ br(Assembler::NE, notClass); __ bind(call_ldc); - __ mov(c_rarg1, wide); + __ mov(c_rarg1, is_ldc_wide(type) ? 1 : 0); call_VM(r0, CAST_FROM_FN_PTR(address, InterpreterRuntime::ldc), c_rarg1); __ push_ptr(r0); __ verify_oop(r0); @@ -376,7 +381,7 @@ void TemplateTable::ldc(bool wide) } // Fast path for caching oop constants. -void TemplateTable::fast_aldc(bool wide) +void TemplateTable::fast_aldc(LdcType type) { transition(vtos, atos); @@ -384,7 +389,7 @@ void TemplateTable::fast_aldc(bool wide) Register tmp = r1; Register rarg = r2; - int index_size = wide ? sizeof(u2) : sizeof(u1); + int index_size = is_ldc_wide(type) ? sizeof(u2) : sizeof(u1); Label resolved; @@ -1136,6 +1141,7 @@ void TemplateTable::aastore() { // Get the value we will store __ ldr(r0, at_tos()); // Now store using the appropriate barrier + // Clobbers: r10, r11, r3 do_oop_store(_masm, element_address, r0, IS_ARRAY); __ b(done); @@ -1144,6 +1150,7 @@ void TemplateTable::aastore() { __ profile_null_seen(r2); // Store a NULL + // Clobbers: r10, r11, r3 do_oop_store(_masm, element_address, noreg, IS_ARRAY); // Pop stack arguments @@ -2706,6 +2713,7 @@ void TemplateTable::putfield_or_static(int byte_no, bool is_static, RewriteContr __ pop(atos); if (!is_static) pop_and_check_object(obj); // Store into the field + // Clobbers: r10, r11, r3 do_oop_store(_masm, field, r0, IN_HEAP); if (rc == may_rewrite) { patch_bytecode(Bytecodes::_fast_aputfield, bc, r1, true, byte_no); @@ -2905,16 +2913,17 @@ void TemplateTable::fast_storefield(TosState state) // Must prevent reordering of the following cp cache loads with bytecode load __ membar(MacroAssembler::LoadLoad); - // test for volatile with r3 - __ ldrw(r3, Address(r2, in_bytes(base + + // test for volatile with r5 + __ ldrw(r5, Address(r2, in_bytes(base + ConstantPoolCacheEntry::flags_offset()))); // replace index with field offset from cache entry __ ldr(r1, Address(r2, in_bytes(base + ConstantPoolCacheEntry::f2_offset()))); + __ verify_field_offset(r1); { Label notVolatile; - __ tbz(r3, ConstantPoolCacheEntry::is_volatile_shift, notVolatile); + __ tbz(r5, ConstantPoolCacheEntry::is_volatile_shift, notVolatile); __ membar(MacroAssembler::StoreStore | MacroAssembler::LoadStore); __ bind(notVolatile); } @@ -2930,6 +2939,7 @@ void TemplateTable::fast_storefield(TosState state) // access field switch (bytecode()) { case Bytecodes::_fast_aputfield: + // Clobbers: r10, r11, r3 do_oop_store(_masm, field, r0, IN_HEAP); break; case Bytecodes::_fast_lputfield: @@ -2962,7 +2972,7 @@ void TemplateTable::fast_storefield(TosState state) { Label notVolatile; - __ tbz(r3, ConstantPoolCacheEntry::is_volatile_shift, notVolatile); + __ tbz(r5, ConstantPoolCacheEntry::is_volatile_shift, notVolatile); __ membar(MacroAssembler::StoreLoad | MacroAssembler::StoreStore); __ bind(notVolatile); } @@ -3003,6 +3013,8 @@ void TemplateTable::fast_accessfield(TosState state) __ ldr(r1, Address(r2, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::f2_offset()))); + __ verify_field_offset(r1); + __ ldrw(r3, Address(r2, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset()))); @@ -3070,8 +3082,13 @@ void TemplateTable::fast_xaccess(TosState state) __ ldr(r0, aaddress(0)); // access constant pool cache __ get_cache_and_index_at_bcp(r2, r3, 2); + + // Must prevent reordering of the following cp cache loads with bytecode load + __ membar(MacroAssembler::LoadLoad); + __ ldr(r1, Address(r2, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::f2_offset()))); + __ verify_field_offset(r1); // 8179954: We need to make sure that the code generated for // volatile accesses forms a sequentially-consistent set of diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index 8e3dbfd5db779..55444db3da51f 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -157,6 +157,9 @@ void VM_Version::initialize() { if (FLAG_IS_DEFAULT(OnSpinWaitInstCount)) { FLAG_SET_DEFAULT(OnSpinWaitInstCount, 2); } + if (FLAG_IS_DEFAULT(UseSignumIntrinsic)) { + FLAG_SET_DEFAULT(UseSignumIntrinsic, true); + } } // ThunderX diff --git a/src/hotspot/cpu/arm/templateTable_arm.cpp b/src/hotspot/cpu/arm/templateTable_arm.cpp index 1e3d12e219c1f..efbdc3ca1249a 100644 --- a/src/hotspot/cpu/arm/templateTable_arm.cpp +++ b/src/hotspot/cpu/arm/templateTable_arm.cpp @@ -364,7 +364,7 @@ void TemplateTable::sipush() { } -void TemplateTable::ldc(bool wide) { +void TemplateTable::ldc(LdcType type) { transition(vtos, vtos); Label fastCase, Condy, Done; @@ -373,7 +373,7 @@ void TemplateTable::ldc(bool wide) { const Register Rtags = R3_tmp; const Register RtagType = R3_tmp; - if (wide) { + if (is_ldc_wide(type)) { __ get_unsigned_2_byte_index_at_bcp(Rindex, 1); } else { __ ldrb(Rindex, at_bcp(1)); @@ -401,7 +401,7 @@ void TemplateTable::ldc(bool wide) { __ b(fastCase, ne); // slow case - call runtime - __ mov(R1, wide); + __ mov(R1, is_ldc_wide(type) ? 1 : 0); call_VM(R0_tos, CAST_FROM_FN_PTR(address, InterpreterRuntime::ldc), R1); __ push(atos); __ b(Done); @@ -429,9 +429,9 @@ void TemplateTable::ldc(bool wide) { } // Fast path for caching oop constants. -void TemplateTable::fast_aldc(bool wide) { +void TemplateTable::fast_aldc(LdcType type) { transition(vtos, atos); - int index_size = wide ? sizeof(u2) : sizeof(u1); + int index_size = is_ldc_wide(type) ? sizeof(u2) : sizeof(u1); Label resolved; // We are resolved if the resolved reference cache entry contains a diff --git a/src/hotspot/cpu/ppc/frame_ppc.cpp b/src/hotspot/cpu/ppc/frame_ppc.cpp index a4cd0c86b1809..859b0b29a4777 100644 --- a/src/hotspot/cpu/ppc/frame_ppc.cpp +++ b/src/hotspot/cpu/ppc/frame_ppc.cpp @@ -114,9 +114,9 @@ bool frame::safe_for_sender(JavaThread *thread) { return false; } - abi_minframe* sender_abi = (abi_minframe*) fp; + volatile abi_minframe* sender_abi = (abi_minframe*) fp; // May get updated concurrently by deoptimization! intptr_t* sender_sp = (intptr_t*) fp; - address sender_pc = (address) sender_abi->lr;; + address sender_pc = (address) sender_abi->lr; // We must always be able to find a recognizable pc. CodeBlob* sender_blob = CodeCache::find_blob_unsafe(sender_pc); @@ -129,9 +129,20 @@ bool frame::safe_for_sender(JavaThread *thread) { return false; } + intptr_t* unextended_sender_sp = is_interpreted_frame() ? (intptr_t*)get_ijava_state()->sender_sp : sender_sp; + // It should be safe to construct the sender though it might not be valid. - frame sender(sender_sp, sender_pc); + // JDK-8339386 is different than the upstream version: + // The frame constructor doesn't check sanity of a deopt pc, but determines it. + // Other accessors for reading it are not available in 17u. + frame sender(sender_sp, sender_pc, unextended_sender_sp); + // If the sender is a deoptimized nmethod we need to check if the original pc is valid. + nmethod* sender_nm = sender_blob->as_nmethod_or_null(); + if (sender_nm != nullptr && sender._deopt_state == is_deoptimized) { + address orig_pc = sender.pc(); + if (!sender_nm->insts_contains_inclusive(orig_pc)) return false; + } // Do we have a valid fp? address sender_fp = (address) sender.fp(); diff --git a/src/hotspot/cpu/ppc/interp_masm_ppc.hpp b/src/hotspot/cpu/ppc/interp_masm_ppc.hpp index f289b80815033..7def4f230ccb4 100644 --- a/src/hotspot/cpu/ppc/interp_masm_ppc.hpp +++ b/src/hotspot/cpu/ppc/interp_masm_ppc.hpp @@ -38,6 +38,7 @@ class InterpreterMacroAssembler: public MacroAssembler { InterpreterMacroAssembler(CodeBuffer* code) : MacroAssembler(code) {} void null_check_throw(Register a, int offset, Register temp_reg); + void load_klass_check_null_throw(Register dst, Register src, Register temp_reg); void jump_to_entry(address entry, Register Rscratch); @@ -125,7 +126,8 @@ class InterpreterMacroAssembler: public MacroAssembler { void get_cache_index_at_bcp(Register Rdst, int bcp_offset, size_t index_size); - void get_cache_and_index_at_bcp(Register cache, int bcp_offset, size_t index_size = sizeof(u2)); + void get_cache_and_index_at_bcp(Register cache, int bcp_offset, size_t index_size = sizeof(u2), + bool for_fast_bytecode = false); void get_u4(Register Rdst, Register Rsrc, int offset, signedOrNot is_signed); diff --git a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp index 6950b304d084c..2db717ffe9562 100644 --- a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp @@ -54,6 +54,11 @@ void InterpreterMacroAssembler::null_check_throw(Register a, int offset, Registe MacroAssembler::null_check_throw(a, offset, temp_reg, exception_entry); } +void InterpreterMacroAssembler::load_klass_check_null_throw(Register dst, Register src, Register temp_reg) { + null_check_throw(src, oopDesc::klass_offset_in_bytes(), temp_reg); + load_klass(dst, src); +} + void InterpreterMacroAssembler::jump_to_entry(address entry, Register Rscratch) { assert(entry, "Entry must have been generated by now"); if (is_within_range_of_b(entry, pc())) { @@ -445,10 +450,18 @@ void InterpreterMacroAssembler::get_cache_index_at_bcp(Register Rdst, int bcp_of } void InterpreterMacroAssembler::get_cache_and_index_at_bcp(Register cache, int bcp_offset, - size_t index_size) { + size_t index_size, bool for_fast_bytecode) { get_cache_index_at_bcp(cache, bcp_offset, index_size); sldi(cache, cache, exact_log2(in_words(ConstantPoolCacheEntry::size()) * BytesPerWord)); add(cache, R27_constPoolCache, cache); + + if (for_fast_bytecode) { + // Prevent speculative loading from ConstantPoolCacheEntry as it can miss the info written by another thread. + // TemplateTable::patch_bytecode uses release-store. + // We reached here via control dependency (Bytecode dispatch has used the rewritten Bytecode). + // So, we can use control-isync based ordering. + isync(); + } } // Load 4-byte signed or unsigned integer in Java format (that is, big-endian format) diff --git a/src/hotspot/cpu/ppc/methodHandles_ppc.cpp b/src/hotspot/cpu/ppc/methodHandles_ppc.cpp index ff8ba4fc84e96..e592257dfcde2 100644 --- a/src/hotspot/cpu/ppc/methodHandles_ppc.cpp +++ b/src/hotspot/cpu/ppc/methodHandles_ppc.cpp @@ -348,7 +348,9 @@ void MethodHandles::generate_method_handle_dispatch(MacroAssembler* _masm, ? -1 // enforce receiver null check : oopDesc::klass_offset_in_bytes(); // regular null-checking behavior - __ null_check_throw(receiver_reg, klass_offset, temp1, Interpreter::throw_NullPointerException_entry()); + address NullPointerException_entry = for_compiler_entry ? StubRoutines::throw_NullPointerException_at_call_entry() + : Interpreter::throw_NullPointerException_entry(); + __ null_check_throw(receiver_reg, klass_offset, temp1, NullPointerException_entry); if (iid != vmIntrinsics::_linkToSpecial || VerifyMethodHandles) { __ load_klass(temp1_recv_klass, receiver_reg); diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index fab403825e77d..95a2ea6689e4d 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -3507,6 +3507,7 @@ encode %{ call->_oop_map = _oop_map; call->_jvms = _jvms; call->_jvmadj = _jvmadj; + call->_has_ea_local_in_scope = _has_ea_local_in_scope; call->_in_rms = _in_rms; call->_nesting = _nesting; call->_override_symbolic_info = _override_symbolic_info; diff --git a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp index 5d33604489ec8..676f067d57660 100644 --- a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp @@ -146,7 +146,9 @@ void TemplateTable::patch_bytecode(Bytecodes::Code new_bc, Register Rnew_bc, Reg __ bind(L_fast_patch); } - // Patch bytecode. + // Patch bytecode with release store to coordinate with ConstantPoolCacheEntry + // loads in fast bytecode codelets. + __ release(); __ stb(Rnew_bc, 0, R14_bcp); __ bind(L_patch_done); @@ -237,7 +239,7 @@ void TemplateTable::sipush() { __ get_2_byte_integer_at_bcp(1, R17_tos, InterpreterMacroAssembler::Signed); } -void TemplateTable::ldc(bool wide) { +void TemplateTable::ldc(LdcType type) { Register Rscratch1 = R11_scratch1, Rscratch2 = R12_scratch2, Rcpool = R3_ARG1; @@ -246,7 +248,7 @@ void TemplateTable::ldc(bool wide) { Label notInt, notFloat, notClass, exit; __ get_cpool_and_tags(Rcpool, Rscratch2); // Set Rscratch2 = &tags. - if (wide) { // Read index. + if (is_ldc_wide(type)) { // Read index. __ get_2_byte_integer_at_bcp(1, Rscratch1, InterpreterMacroAssembler::Unsigned); } else { __ lbz(Rscratch1, 1, R14_bcp); @@ -268,7 +270,7 @@ void TemplateTable::ldc(bool wide) { __ crnor(CCR0, Assembler::equal, CCR1, Assembler::equal); // Neither resolved class nor unresolved case from above? __ beq(CCR0, notClass); - __ li(R4, wide ? 1 : 0); + __ li(R4, is_ldc_wide(type) ? 1 : 0); call_VM(R17_tos, CAST_FROM_FN_PTR(address, InterpreterRuntime::ldc), R4); __ push(atos); __ b(exit); @@ -301,15 +303,16 @@ void TemplateTable::ldc(bool wide) { } // Fast path for caching oop constants. -void TemplateTable::fast_aldc(bool wide) { +void TemplateTable::fast_aldc(LdcType type) { transition(vtos, atos); - int index_size = wide ? sizeof(u2) : sizeof(u1); + int index_size = is_ldc_wide(type) ? sizeof(u2) : sizeof(u1); Label is_null; // We are resolved if the resolved reference cache entry contains a // non-null object (CallSite, etc.) __ get_cache_index_at_bcp(R31, 1, index_size); // Load index. + // Only rewritten during link time. So, no need for memory barriers for accessing resolved info. __ load_resolved_reference_at_index(R17_tos, R31, R11_scratch1, R12_scratch2, &is_null); // Convert null sentinel to NULL @@ -1040,7 +1043,7 @@ void TemplateTable::bastore() { // Need to check whether array is boolean or byte // since both types share the bastore bytecode. - __ load_klass(Rscratch, Rarray); + __ load_klass_check_null_throw(Rscratch, Rarray, Rscratch); __ lwz(Rscratch, in_bytes(Klass::layout_helper_offset()), Rscratch); int diffbit = exact_log2(Klass::layout_helper_boolean_diffbit()); __ testbitdi(CCR0, R0, Rscratch, diffbit); @@ -2309,7 +2312,7 @@ void TemplateTable::load_invoke_cp_cache_entry(int byte_no, if (is_invokevfinal) { assert(Ritable_index == noreg, "register not used"); // Already resolved. - __ get_cache_and_index_at_bcp(Rcache, 1); + __ get_cache_and_index_at_bcp(Rcache, 1, sizeof(u2), /* for_fast_bytecode */ true); } else { resolve_cache_and_index(byte_no, Rcache, /* temp */ Rmethod, is_invokedynamic ? sizeof(u4) : sizeof(u2)); } @@ -3016,7 +3019,7 @@ void TemplateTable::fast_storefield(TosState state) { const ConditionRegister CR_is_vol = CCR2; // Non-volatile condition register (survives runtime call in do_oop_store). // Constant pool already resolved => Load flags and offset of field. - __ get_cache_and_index_at_bcp(Rcache, 1); + __ get_cache_and_index_at_bcp(Rcache, 1, sizeof(u2), /* for_fast_bytecode */ true); jvmti_post_field_mod(Rcache, Rscratch, false /* not static */); load_field_cp_cache_entry(noreg, Rcache, noreg, Roffset, Rflags, false); // Uses R11, R12 @@ -3097,7 +3100,7 @@ void TemplateTable::fast_accessfield(TosState state) { // R12_scratch2 used by load_field_cp_cache_entry // Constant pool already resolved. Get the field offset. - __ get_cache_and_index_at_bcp(Rcache, 1); + __ get_cache_and_index_at_bcp(Rcache, 1, sizeof(u2), /* for_fast_bytecode */ true); load_field_cp_cache_entry(noreg, Rcache, noreg, Roffset, Rflags, false); // Uses R11, R12 // JVMTI support @@ -3236,7 +3239,7 @@ void TemplateTable::fast_xaccess(TosState state) { __ ld(Rclass_or_obj, 0, R18_locals); // Constant pool already resolved. Get the field offset. - __ get_cache_and_index_at_bcp(Rcache, 2); + __ get_cache_and_index_at_bcp(Rcache, 2, sizeof(u2), /* for_fast_bytecode */ true); load_field_cp_cache_entry(noreg, Rcache, noreg, Roffset, Rflags, false); // Uses R11, R12 // JVMTI support not needed, since we switch back to single bytecode as soon as debugger attaches. @@ -3440,8 +3443,7 @@ void TemplateTable::invokevirtual(int byte_no) { __ load_dispatch_table(Rtable_addr, Interpreter::invoke_return_entry_table()); __ sldi(Rret_type, Rret_type, LogBytesPerWord); __ ldx(Rret_addr, Rret_type, Rtable_addr); - __ null_check_throw(Rrecv, oopDesc::klass_offset_in_bytes(), R11_scratch1); - __ load_klass(Rrecv_klass, Rrecv); + __ load_klass_check_null_throw(Rrecv_klass, Rrecv, R11_scratch1); __ verify_klass_ptr(Rrecv_klass); __ profile_virtual_call(Rrecv_klass, R11_scratch1, R12_scratch2, false); @@ -3578,8 +3580,7 @@ void TemplateTable::invokeinterface(int byte_no) { // then regular interface method. // Get receiver klass - this is also a null check - __ null_check_throw(Rreceiver, oopDesc::klass_offset_in_bytes(), Rscratch2); - __ load_klass(Rrecv_klass, Rreceiver); + __ load_klass_check_null_throw(Rrecv_klass, Rreceiver, Rscratch2); // Check corner case object method. // Special case of invokeinterface called for virtual method of diff --git a/src/hotspot/cpu/riscv/assembler_riscv.hpp b/src/hotspot/cpu/riscv/assembler_riscv.hpp index 98c51e9883d96..31a8f59f80277 100644 --- a/src/hotspot/cpu/riscv/assembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/assembler_riscv.hpp @@ -2779,7 +2779,7 @@ enum Nf { static const unsigned long branch_range = 1 * M; static bool reachable_from_branch_at(address branch, address target) { - return uabs(target - branch) < branch_range; + return g_uabs(target - branch) < branch_range; } // Decode the given instruction, checking if it's a 16-bit compressed diff --git a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp index 7fe0392ea5c63..a0ebb34a54860 100644 --- a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp @@ -925,7 +925,7 @@ class StubGenerator: public StubCodeGenerator { void copy_memory_v(Register s, Register d, Register count, Register tmp, int step) { bool is_backward = step < 0; - int granularity = uabs(step); + int granularity = g_uabs(step); const Register src = x30, dst = x31, vl = x14, cnt = x15, tmp1 = x16, tmp2 = x17; assert_different_registers(s, d, cnt, vl, tmp, tmp1, tmp2); @@ -974,7 +974,7 @@ class StubGenerator: public StubCodeGenerator { } bool is_backwards = step < 0; - int granularity = uabs(step); + int granularity = g_uabs(step); const Register src = x30, dst = x31, cnt = x15, tmp3 = x16, tmp4 = x17, tmp5 = x14, tmp6 = x13; diff --git a/src/hotspot/cpu/riscv/templateTable_riscv.cpp b/src/hotspot/cpu/riscv/templateTable_riscv.cpp index db847a0e7bc3b..7208f5391f740 100644 --- a/src/hotspot/cpu/riscv/templateTable_riscv.cpp +++ b/src/hotspot/cpu/riscv/templateTable_riscv.cpp @@ -300,12 +300,12 @@ void TemplateTable::sipush() } } -void TemplateTable::ldc(bool wide) +void TemplateTable::ldc(LdcType type) { transition(vtos, vtos); Label call_ldc, notFloat, notClass, notInt, Done; - if (wide) { + if (is_ldc_wide(type)) { __ get_unsigned_2_byte_index_at_bcp(x11, 1); } else { __ load_unsigned_byte(x11, at_bcp(1)); @@ -335,7 +335,7 @@ void TemplateTable::ldc(bool wide) __ bne(x13, t1, notClass); __ bind(call_ldc); - __ mv(c_rarg1, wide); + __ mv(c_rarg1, is_ldc_wide(type) ? 1 : 0); call_VM(x10, CAST_FROM_FN_PTR(address, InterpreterRuntime::ldc), c_rarg1); __ push_ptr(x10); __ verify_oop(x10); @@ -369,7 +369,7 @@ void TemplateTable::ldc(bool wide) } // Fast path for caching oop constants. -void TemplateTable::fast_aldc(bool wide) +void TemplateTable::fast_aldc(LdcType type) { transition(vtos, atos); @@ -377,7 +377,7 @@ void TemplateTable::fast_aldc(bool wide) const Register tmp = x11; const Register rarg = x12; - const int index_size = wide ? sizeof(u2) : sizeof(u1); + const int index_size = is_ldc_wide(type) ? sizeof(u2) : sizeof(u1); Label resolved; @@ -1129,6 +1129,7 @@ void TemplateTable::aastore() { // Get the value we will store __ ld(x10, at_tos()); // Now store using the appropriate barrier + // Clobbers: x11, x13, x29 do_oop_store(_masm, element_address, x10, IS_ARRAY); __ j(done); @@ -1137,6 +1138,7 @@ void TemplateTable::aastore() { __ profile_null_seen(x12); // Store a NULL + // Clobbers: x11, x13, x29 do_oop_store(_masm, element_address, noreg, IS_ARRAY); // Pop stack arguments @@ -2716,6 +2718,7 @@ void TemplateTable::putfield_or_static(int byte_no, bool is_static, RewriteContr __ add(off, obj, off); // if static, obj from cache, else obj from stack. const Address field(off, 0); // Store into the field + // Clobbers: x11, x13, x29 do_oop_store(_masm, field, x10, IN_HEAP); if (rc == may_rewrite) { patch_bytecode(Bytecodes::_fast_aputfield, bc, x11, true, byte_no); @@ -2950,8 +2953,8 @@ void TemplateTable::fast_storefield(TosState state) // Must prevent reordering of the following cp cache loads with bytecode load __ membar(MacroAssembler::LoadLoad); - // test for volatile with x13 - __ lwu(x13, Address(x12, in_bytes(base + + // test for volatile with x15 + __ lwu(x15, Address(x12, in_bytes(base + ConstantPoolCacheEntry::flags_offset()))); // replace index with field offset from cache entry @@ -2959,7 +2962,7 @@ void TemplateTable::fast_storefield(TosState state) { Label notVolatile; - __ test_bit(t0, x13, ConstantPoolCacheEntry::is_volatile_shift); + __ test_bit(t0, x15, ConstantPoolCacheEntry::is_volatile_shift); __ beqz(t0, notVolatile); __ membar(MacroAssembler::StoreStore | MacroAssembler::LoadStore); __ bind(notVolatile); @@ -2975,6 +2978,7 @@ void TemplateTable::fast_storefield(TosState state) // access field switch (bytecode()) { case Bytecodes::_fast_aputfield: + // Clobbers: x11, x13, x29 do_oop_store(_masm, field, x10, IN_HEAP); break; case Bytecodes::_fast_lputfield: @@ -3007,7 +3011,7 @@ void TemplateTable::fast_storefield(TosState state) { Label notVolatile; - __ test_bit(t0, x13, ConstantPoolCacheEntry::is_volatile_shift); + __ test_bit(t0, x15, ConstantPoolCacheEntry::is_volatile_shift); __ beqz(t0, notVolatile); __ membar(MacroAssembler::StoreLoad | MacroAssembler::StoreStore); __ bind(notVolatile); diff --git a/src/hotspot/cpu/s390/templateTable_s390.cpp b/src/hotspot/cpu/s390/templateTable_s390.cpp index d7ff098d3613d..38ccefe6aa021 100644 --- a/src/hotspot/cpu/s390/templateTable_s390.cpp +++ b/src/hotspot/cpu/s390/templateTable_s390.cpp @@ -377,13 +377,13 @@ void TemplateTable::sipush() { } -void TemplateTable::ldc(bool wide) { +void TemplateTable::ldc(LdcType type) { transition(vtos, vtos); Label call_ldc, notFloat, notClass, notInt, Done; const Register RcpIndex = Z_tmp_1; const Register Rtags = Z_ARG2; - if (wide) { + if (is_ldc_wide(type)) { __ get_2_byte_integer_at_bcp(RcpIndex, 1, InterpreterMacroAssembler::Unsigned); } else { __ z_llgc(RcpIndex, at_bcp(1)); @@ -411,7 +411,7 @@ void TemplateTable::ldc(bool wide) { // We deal with a class. Call vm to do the appropriate. __ bind(call_ldc); - __ load_const_optimized(Z_ARG2, wide); + __ load_const_optimized(Z_ARG2, is_ldc_wide(type) ? 1 : 0); call_VM(Z_RET, CAST_FROM_FN_PTR(address, InterpreterRuntime::ldc), Z_ARG2); __ push_ptr(Z_RET); __ z_bru(Done); @@ -447,11 +447,11 @@ void TemplateTable::ldc(bool wide) { // Fast path for caching oop constants. // %%% We should use this to handle Class and String constants also. // %%% It will simplify the ldc/primitive path considerably. -void TemplateTable::fast_aldc(bool wide) { +void TemplateTable::fast_aldc(LdcType type) { transition(vtos, atos); const Register index = Z_tmp_2; - int index_size = wide ? sizeof(u2) : sizeof(u1); + int index_size = is_ldc_wide(type) ? sizeof(u2) : sizeof(u1); Label L_do_resolve, L_resolved; // We are resolved if the resolved reference cache entry contains a diff --git a/src/hotspot/cpu/x86/assembler_x86.cpp b/src/hotspot/cpu/x86/assembler_x86.cpp index d3fad2b6d237f..6e509918faf8a 100644 --- a/src/hotspot/cpu/x86/assembler_x86.cpp +++ b/src/hotspot/cpu/x86/assembler_x86.cpp @@ -7546,6 +7546,10 @@ void Assembler::evprolq(XMMRegister dst, XMMRegister src, int shift, int vector_ emit_int24(0x72, (0xC0 | encode), shift & 0xFF); } +// Register is a class, but it would be assigned numerical value. +// "0" is assigned for xmm0. Thus we need to ignore -Wnonnull. +PRAGMA_DIAG_PUSH +PRAGMA_NONNULL_IGNORED void Assembler::evprord(XMMRegister dst, XMMRegister src, int shift, int vector_len) { assert(VM_Version::supports_evex(), "requires EVEX support"); assert(vector_len == Assembler::AVX_512bit || VM_Version::supports_avx512vl(), "requires VL support"); @@ -7563,6 +7567,7 @@ void Assembler::evprorq(XMMRegister dst, XMMRegister src, int shift, int vector_ int encode = vex_prefix_and_encode(xmm0->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int24(0x72, (0xC0 | encode), shift & 0xFF); } +PRAGMA_DIAG_POP void Assembler::evprolvd(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len) { assert(VM_Version::supports_evex(), "requires EVEX support"); diff --git a/src/hotspot/cpu/x86/c1_Runtime1_x86.cpp b/src/hotspot/cpu/x86/c1_Runtime1_x86.cpp index 42c46496d47b9..d0c356c462605 100644 --- a/src/hotspot/cpu/x86/c1_Runtime1_x86.cpp +++ b/src/hotspot/cpu/x86/c1_Runtime1_x86.cpp @@ -318,7 +318,11 @@ enum reg_save_layout { // expensive. The deopt blob is the only thing which needs to // describe FPU registers. In all other cases it should be sufficient // to simply save their current value. - +// +// Register is a class, but it would be assigned numerical value. +// "0" is assigned for rax. Thus we need to ignore -Wnonnull. +PRAGMA_DIAG_PUSH +PRAGMA_NONNULL_IGNORED static OopMap* generate_oop_map(StubAssembler* sasm, int num_rt_args, bool save_fpu_registers = true) { @@ -418,6 +422,7 @@ static OopMap* generate_oop_map(StubAssembler* sasm, int num_rt_args, return map; } +PRAGMA_DIAG_POP #define __ this-> diff --git a/src/hotspot/cpu/x86/frame_x86.inline.hpp b/src/hotspot/cpu/x86/frame_x86.inline.hpp index 589994d624926..23072238e16aa 100644 --- a/src/hotspot/cpu/x86/frame_x86.inline.hpp +++ b/src/hotspot/cpu/x86/frame_x86.inline.hpp @@ -230,6 +230,10 @@ inline JavaCallWrapper** frame::entry_frame_call_wrapper_addr() const { // Compiled frames +// Register is a class, but it would be assigned numerical value. +// "0" is assigned for rax. Thus we need to ignore -Wnonnull. +PRAGMA_DIAG_PUSH +PRAGMA_NONNULL_IGNORED inline oop frame::saved_oop_result(RegisterMap* map) const { oop* result_adr = (oop *)map->location(rax->as_VMReg()); guarantee(result_adr != NULL, "bad register save location"); @@ -243,5 +247,6 @@ inline void frame::set_saved_oop_result(RegisterMap* map, oop obj) { *result_adr = obj; } +PRAGMA_DIAG_POP #endif // CPU_X86_FRAME_X86_INLINE_HPP diff --git a/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp index 8316df3644bb5..2b06c3f365cab 100644 --- a/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp @@ -265,8 +265,6 @@ void G1BarrierSetAssembler::g1_write_barrier_post(MacroAssembler* masm, Register thread, Register tmp, Register tmp2) { - // Generated code assumes that buffer index is pointer sized. - STATIC_ASSERT(in_bytes(SATBMarkQueue::byte_width_of_index()) == sizeof(intptr_t)); #ifdef _LP64 assert(thread == r15_thread, "must be"); #endif // _LP64 @@ -317,6 +315,9 @@ void G1BarrierSetAssembler::g1_write_barrier_post(MacroAssembler* masm, __ movb(Address(card_addr, 0), (int)G1CardTable::dirty_card_val()); + // The code below assumes that buffer index is pointer sized. + STATIC_ASSERT(in_bytes(G1DirtyCardQueue::byte_width_of_index()) == sizeof(intptr_t)); + __ movptr(tmp2, queue_index); __ testptr(tmp2, tmp2); __ jcc(Assembler::zero, runtime); diff --git a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp index 6fc8833f7f174..8c74db952e8a7 100644 --- a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp @@ -465,6 +465,10 @@ class ZSaveLiveRegisters { _spill_offset += 8; } +// Register is a class, but it would be assigned numerical value. +// "0" is assigned for rax. Thus we need to ignore -Wnonnull. +PRAGMA_DIAG_PUSH +PRAGMA_NONNULL_IGNORED void initialize(ZLoadBarrierStubC2* stub) { // Create mask of caller saved registers that need to // be saved/restored if live @@ -540,6 +544,7 @@ class ZSaveLiveRegisters { // Stack pointer must be 16 bytes aligned for the call _spill_offset = _spill_size = align_up(xmm_spill_size + gp_spill_size + opmask_spill_size + arg_spill_size, 16); } +PRAGMA_DIAG_POP public: ZSaveLiveRegisters(MacroAssembler* masm, ZLoadBarrierStubC2* stub) : diff --git a/src/hotspot/cpu/x86/macroAssembler_x86_exp.cpp b/src/hotspot/cpu/x86/macroAssembler_x86_exp.cpp index 313c5b514b5c3..f258dc7b0ba3d 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86_exp.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86_exp.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2016, Intel Corporation. -* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +* Copyright (C) 2021, Tencent. All rights reserved. * Intel Math Library (LIBM) Source Code * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. diff --git a/src/hotspot/cpu/x86/macroAssembler_x86_log.cpp b/src/hotspot/cpu/x86/macroAssembler_x86_log.cpp index 9c28d9e510d47..6fa5c89dc63d7 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86_log.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86_log.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2016, Intel Corporation. -* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +* Copyright (C) 2021, Tencent. All rights reserved. * Intel Math Library (LIBM) Source Code * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. diff --git a/src/hotspot/cpu/x86/macroAssembler_x86_pow.cpp b/src/hotspot/cpu/x86/macroAssembler_x86_pow.cpp index d1c405ba3c8d9..849db6721e01b 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86_pow.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86_pow.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2016, Intel Corporation. -* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +* Copyright (C) 2021, Tencent. All rights reserved. * Intel Math Library (LIBM) Source Code * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp index f912b7650a170..24647a0acadfb 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp @@ -168,6 +168,10 @@ class RegisterSaver { static void restore_result_registers(MacroAssembler* masm); }; +// Register is a class, but it would be assigned numerical value. +// "0" is assigned for rax. Thus we need to ignore -Wnonnull. +PRAGMA_DIAG_PUSH +PRAGMA_NONNULL_IGNORED OopMap* RegisterSaver::save_live_registers(MacroAssembler* masm, int additional_frame_words, int* total_frame_words, bool save_wide_vectors) { int off = 0; int num_xmm_regs = XMMRegisterImpl::number_of_registers; @@ -361,6 +365,7 @@ OopMap* RegisterSaver::save_live_registers(MacroAssembler* masm, int additional_ return map; } +PRAGMA_DIAG_POP void RegisterSaver::restore_live_registers(MacroAssembler* masm, bool restore_wide_vectors) { int num_xmm_regs = XMMRegisterImpl::number_of_registers; diff --git a/src/hotspot/cpu/x86/templateTable_x86.cpp b/src/hotspot/cpu/x86/templateTable_x86.cpp index a82954580f059..dcbfc72058db2 100644 --- a/src/hotspot/cpu/x86/templateTable_x86.cpp +++ b/src/hotspot/cpu/x86/templateTable_x86.cpp @@ -350,12 +350,12 @@ void TemplateTable::sipush() { __ sarl(rax, 16); } -void TemplateTable::ldc(bool wide) { +void TemplateTable::ldc(LdcType type) { transition(vtos, vtos); Register rarg = NOT_LP64(rcx) LP64_ONLY(c_rarg1); Label call_ldc, notFloat, notClass, notInt, Done; - if (wide) { + if (is_ldc_wide(type)) { __ get_unsigned_2_byte_index_at_bcp(rbx, 1); } else { __ load_unsigned_byte(rbx, at_bcp(1)); @@ -383,7 +383,7 @@ void TemplateTable::ldc(bool wide) { __ bind(call_ldc); - __ movl(rarg, wide); + __ movl(rarg, is_ldc_wide(type) ? 1 : 0); call_VM(rax, CAST_FROM_FN_PTR(address, InterpreterRuntime::ldc), rarg); __ push(atos); @@ -415,13 +415,13 @@ void TemplateTable::ldc(bool wide) { } // Fast path for caching oop constants. -void TemplateTable::fast_aldc(bool wide) { +void TemplateTable::fast_aldc(LdcType type) { transition(vtos, atos); Register result = rax; Register tmp = rdx; Register rarg = NOT_LP64(rcx) LP64_ONLY(c_rarg1); - int index_size = wide ? sizeof(u2) : sizeof(u1); + int index_size = is_ldc_wide(type) ? sizeof(u2) : sizeof(u1); Label resolved; diff --git a/src/hotspot/cpu/x86/universalUpcallHandler_x86_64.cpp b/src/hotspot/cpu/x86/universalUpcallHandler_x86_64.cpp index 34771e9984ddd..c54b907f9b5c5 100644 --- a/src/hotspot/cpu/x86/universalUpcallHandler_x86_64.cpp +++ b/src/hotspot/cpu/x86/universalUpcallHandler_x86_64.cpp @@ -587,6 +587,10 @@ static void shuffle_arguments(MacroAssembler* _masm, const GrowableArraycode_begin(); } +PRAGMA_DIAG_POP bool ProgrammableUpcallHandler::supports_optimized_upcalls() { return true; diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index 073fde86d22c9..a216c31858615 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -212,11 +212,13 @@ static char cpu_arch[] = "ppc"; #error Add appropriate cpu_arch setting #endif -// Compiler variant -#ifdef COMPILER2 - #define COMPILER_VARIANT "server" +// JVM variant +#if defined(ZERO) + #define JVM_VARIANT "zero" +#elif defined(COMPILER2) + #define JVM_VARIANT "server" #else - #define COMPILER_VARIANT "client" + #define JVM_VARIANT "client" #endif @@ -1538,10 +1540,10 @@ void os::jvm_path(char *buf, jint buflen) { snprintf(jrelib_p, buflen-len, "/lib"); } - // Add the appropriate client or server subdir + // Add the appropriate JVM variant subdir len = strlen(buf); jrelib_p = buf + len; - snprintf(jrelib_p, buflen-len, "/%s", COMPILER_VARIANT); + snprintf(jrelib_p, buflen-len, "/%s", JVM_VARIANT); if (0 != access(buf, F_OK)) { snprintf(jrelib_p, buflen-len, "%s", ""); } diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index 81e5e332b859c..e15054ebbc9d4 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp @@ -35,7 +35,7 @@ #include "utilities/globalDefinitions.hpp" // controller names have to match the *_IDX indices -static const char* cg_controller_name[] = { "cpu", "cpuset", "cpuacct", "memory", "pids" }; +static const char* cg_controller_name[] = { "cpuset", "cpu", "cpuacct", "memory", "pids" }; CgroupSubsystem* CgroupSubsystemFactory::create() { CgroupV1MemoryController* memory = NULL; @@ -159,9 +159,10 @@ bool CgroupSubsystemFactory::determine_type(CgroupInfo* cg_infos, char buf[MAXPATHLEN+1]; char *p; bool is_cgroupsV2; - // true iff all required controllers, memory, cpu, cpuset, cpuacct are enabled + // true iff all required controllers, memory, cpu, cpuacct are enabled // at the kernel level. // pids might not be enabled on older Linux distros (SLES 12.1, RHEL 7.1) + // cpuset might not be enabled on newer Linux distros (Fedora 41) bool all_required_controllers_enabled; /* @@ -193,6 +194,7 @@ bool CgroupSubsystemFactory::determine_type(CgroupInfo* cg_infos, cg_infos[MEMORY_IDX]._hierarchy_id = hierarchy_id; cg_infos[MEMORY_IDX]._enabled = (enabled == 1); } else if (strcmp(name, "cpuset") == 0) { + log_debug(os, container)("Detected optional cpuset controller entry in %s", proc_cgroups); cg_infos[CPUSET_IDX]._name = os::strdup(name); cg_infos[CPUSET_IDX]._hierarchy_id = hierarchy_id; cg_infos[CPUSET_IDX]._enabled = (enabled == 1); @@ -216,8 +218,8 @@ bool CgroupSubsystemFactory::determine_type(CgroupInfo* cg_infos, is_cgroupsV2 = true; all_required_controllers_enabled = true; for (int i = 0; i < CG_INFO_LENGTH; i++) { - // pids controller is optional. All other controllers are required - if (i != PIDS_IDX) { + // pids and cpuset controllers are optional. All other controllers are required + if (i != PIDS_IDX && i != CPUSET_IDX) { is_cgroupsV2 = is_cgroupsV2 && cg_infos[i]._hierarchy_id == 0; all_required_controllers_enabled = all_required_controllers_enabled && cg_infos[i]._enabled; } diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp index d4efc0f3d83cc..c7f34fa65c0b0 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp @@ -78,77 +78,91 @@ class CgroupController: public CHeapObj { PRAGMA_DIAG_PUSH PRAGMA_FORMAT_NONLITERAL_IGNORED +// Parses a subsystem's file, looking for a matching line. +// If key is null, then the first line will be matched with scan_fmt. +// If key isn't null, then each line will be matched, looking for something that matches "$key $scan_fmt". +// The matching value will be assigned to returnval. +// scan_fmt uses scanf() syntax. +// Return value: 0 on match, OSCONTAINER_ERROR on error. template int subsystem_file_line_contents(CgroupController* c, const char *filename, - const char *matchline, + const char *key, const char *scan_fmt, T returnval) { - FILE *fp = NULL; - char *p; - char file[MAXPATHLEN+1]; - char buf[MAXPATHLEN+1]; - char discard[MAXPATHLEN+1]; - bool found_match = false; + if (c == nullptr) { + log_debug(os, container)("subsystem_file_line_contents: CgroupController* is nullptr"); + return OSCONTAINER_ERROR; + } + if (c->subsystem_path() == nullptr) { + log_debug(os, container)("subsystem_file_line_contents: subsystem path is nullptr"); + return OSCONTAINER_ERROR; + } - if (c == NULL) { - log_debug(os, container)("subsystem_file_line_contents: CgroupController* is NULL"); + stringStream file_path; + file_path.print_raw(c->subsystem_path()); + file_path.print_raw(filename); + + if (file_path.size() > (MAXPATHLEN-1)) { + log_debug(os, container)("File path too long %s, %s", file_path.base(), filename); return OSCONTAINER_ERROR; } - if (c->subsystem_path() == NULL) { - log_debug(os, container)("subsystem_file_line_contents: subsystem path is NULL"); + const char* absolute_path = file_path.freeze(); + log_trace(os, container)("Path to %s is %s", filename, absolute_path); + + FILE* fp = fopen(absolute_path, "r"); + if (fp == nullptr) { + log_debug(os, container)("Open of file %s failed, %s", absolute_path, os::strerror(errno)); return OSCONTAINER_ERROR; } - strncpy(file, c->subsystem_path(), MAXPATHLEN); - file[MAXPATHLEN-1] = '\0'; - int filelen = strlen(file); - if ((filelen + strlen(filename)) > (MAXPATHLEN-1)) { - log_debug(os, container)("File path too long %s, %s", file, filename); + const int buf_len = MAXPATHLEN+1; + char buf[buf_len]; + char* line = fgets(buf, buf_len, fp); + if (line == nullptr) { + log_debug(os, container)("Empty file %s", absolute_path); + fclose(fp); return OSCONTAINER_ERROR; } - strncat(file, filename, MAXPATHLEN-filelen); - log_trace(os, container)("Path to %s is %s", filename, file); - fp = fopen(file, "r"); - if (fp != NULL) { - int err = 0; - while ((p = fgets(buf, MAXPATHLEN, fp)) != NULL) { - found_match = false; - if (matchline == NULL) { - // single-line file case - int matched = sscanf(p, scan_fmt, returnval); - found_match = (matched == 1); - } else { - // multi-line file case - if (strstr(p, matchline) != NULL) { - // discard matchline string prefix - int matched = sscanf(p, scan_fmt, discard, returnval); - found_match = (matched == 2); - } else { - continue; // substring not found + + bool found_match = false; + if (key == nullptr) { + // File consists of a single line according to caller, with only a value + int matched = sscanf(line, scan_fmt, returnval); + found_match = matched == 1; + } else { + // File consists of multiple lines in a "key value" + // fashion, we have to find the key. + const int key_len = strlen(key); + for (; line != nullptr; line = fgets(buf, buf_len, fp)) { + char* key_substr = strstr(line, key); + char after_key = line[key_len]; + if (key_substr == line + && isspace(after_key) != 0 + && after_key != '\n') { + // Skip key, skip space + const char* value_substr = line + key_len + 1; + int matched = sscanf(value_substr, scan_fmt, returnval); + found_match = matched == 1; + if (found_match) { + break; } } - if (found_match) { - fclose(fp); - return 0; - } else { - err = 1; - log_debug(os, container)("Type %s not found in file %s", scan_fmt, file); - } - } - if (err == 0) { - log_debug(os, container)("Empty file %s", file); } - } else { - log_debug(os, container)("Open of file %s failed, %s", file, os::strerror(errno)); } - if (fp != NULL) - fclose(fp); + fclose(fp); + if (found_match) { + return 0; + } + log_debug(os, container)("Type %s (key == %s) not found in file %s", scan_fmt, + (key == nullptr ? "null" : key), absolute_path); return OSCONTAINER_ERROR; } PRAGMA_DIAG_POP +// log_fmt can be different than scan_fmt. For example +// cpu_period() for cgv2 uses log_fmt='%d' and scan_fmt='%*s %d' #define GET_CONTAINER_INFO(return_type, subsystem, filename, \ - logstring, scan_fmt, variable) \ + logstring, log_fmt, scan_fmt, variable) \ return_type variable; \ { \ int err; \ @@ -158,11 +172,11 @@ PRAGMA_DIAG_POP scan_fmt, \ &variable); \ if (err != 0) { \ - log_trace(os, container)(logstring, (return_type) OSCONTAINER_ERROR); \ + log_trace(os, container)(logstring "%d", OSCONTAINER_ERROR); \ return (return_type) OSCONTAINER_ERROR; \ } \ \ - log_trace(os, container)(logstring, variable); \ + log_trace(os, container)(logstring log_fmt, variable); \ } #define GET_CONTAINER_INFO_CPTR(return_type, subsystem, filename, \ @@ -252,6 +266,8 @@ class CgroupSubsystem: public CHeapObj { virtual jlong memory_and_swap_limit_in_bytes() = 0; virtual jlong memory_soft_limit_in_bytes() = 0; virtual jlong memory_max_usage_in_bytes() = 0; + virtual jlong rss_usage_in_bytes() = 0; + virtual jlong cache_usage_in_bytes() = 0; virtual char * cpu_cpuset_cpus() = 0; virtual char * cpu_cpuset_memory_nodes() = 0; diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index e380319e78980..bbe800c3a9e46 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp @@ -76,7 +76,7 @@ void CgroupV1Controller::set_subsystem_path(char *cgroup_path) { */ jlong CgroupV1MemoryController::uses_mem_hierarchy() { GET_CONTAINER_INFO(jlong, this, "/memory.use_hierarchy", - "Use Hierarchy is: " JLONG_FORMAT, JLONG_FORMAT, use_hierarchy); + "Use Hierarchy is: ", JLONG_FORMAT, JLONG_FORMAT, use_hierarchy); return use_hierarchy; } @@ -90,16 +90,14 @@ void CgroupV1MemoryController::set_subsystem_path(char *cgroup_path) { jlong CgroupV1Subsystem::read_memory_limit_in_bytes() { GET_CONTAINER_INFO(julong, _memory->controller(), "/memory.limit_in_bytes", - "Memory Limit is: " JULONG_FORMAT, JULONG_FORMAT, memlimit); + "Memory Limit is: ", JULONG_FORMAT, JULONG_FORMAT, memlimit); if (memlimit >= os::Linux::physical_memory()) { log_trace(os, container)("Non-Hierarchical Memory Limit is: Unlimited"); CgroupV1MemoryController* mem_controller = reinterpret_cast(_memory->controller()); if (mem_controller->is_hierarchical()) { - const char* matchline = "hierarchical_memory_limit"; - const char* format = "%s " JULONG_FORMAT; - GET_CONTAINER_INFO_LINE(julong, _memory->controller(), "/memory.stat", matchline, - "Hierarchical Memory Limit is: " JULONG_FORMAT, format, hier_memlimit) + GET_CONTAINER_INFO_LINE(julong, _memory->controller(), "/memory.stat", "hierarchical_memory_limit", + "Hierarchical Memory Limit is: " JULONG_FORMAT, JULONG_FORMAT, hier_memlimit) if (hier_memlimit >= os::Linux::physical_memory()) { log_trace(os, container)("Hierarchical Memory Limit is: Unlimited"); } else { @@ -113,54 +111,72 @@ jlong CgroupV1Subsystem::read_memory_limit_in_bytes() { } } -jlong CgroupV1Subsystem::memory_and_swap_limit_in_bytes() { +/* read_mem_swap + * + * Determine the memory and swap limit metric. Returns a positive limit value strictly + * lower than the physical memory and swap limit iff there is a limit. Otherwise a + * negative value is returned indicating the determined status. + * + * returns: + * * A number > 0 if the limit is available and lower than a physical upper bound. + * * OSCONTAINER_ERROR if the limit cannot be retrieved (i.e. not supported) or + * * -1 if there isn't any limit in place (note: includes values which exceed a physical + * upper bound) + */ +jlong CgroupV1Subsystem::read_mem_swap() { julong host_total_memsw; GET_CONTAINER_INFO(julong, _memory->controller(), "/memory.memsw.limit_in_bytes", - "Memory and Swap Limit is: " JULONG_FORMAT, JULONG_FORMAT, memswlimit); + "Memory and Swap Limit is: ", JULONG_FORMAT, JULONG_FORMAT, memswlimit); host_total_memsw = os::Linux::host_swap() + os::Linux::physical_memory(); if (memswlimit >= host_total_memsw) { log_trace(os, container)("Non-Hierarchical Memory and Swap Limit is: Unlimited"); CgroupV1MemoryController* mem_controller = reinterpret_cast(_memory->controller()); if (mem_controller->is_hierarchical()) { const char* matchline = "hierarchical_memsw_limit"; - const char* format = "%s " JULONG_FORMAT; GET_CONTAINER_INFO_LINE(julong, _memory->controller(), "/memory.stat", matchline, - "Hierarchical Memory and Swap Limit is : " JULONG_FORMAT, format, hier_memswlimit) + "Hierarchical Memory and Swap Limit is : " JULONG_FORMAT, JULONG_FORMAT, hier_memswlimit) if (hier_memswlimit >= host_total_memsw) { log_trace(os, container)("Hierarchical Memory and Swap Limit is: Unlimited"); } else { - jlong swappiness = read_mem_swappiness(); - if (swappiness == 0) { - const char* matchmemline = "hierarchical_memory_limit"; - GET_CONTAINER_INFO_LINE(julong, _memory->controller(), "/memory.stat", matchmemline, - "Hierarchical Memory Limit is : " JULONG_FORMAT, format, hier_memlimit) - log_trace(os, container)("Memory and Swap Limit has been reset to " JULONG_FORMAT " because swappiness is 0", hier_memlimit); - return (jlong)hier_memlimit; - } return (jlong)hier_memswlimit; } } return (jlong)-1; } else { - jlong swappiness = read_mem_swappiness(); - if (swappiness == 0) { - jlong memlimit = read_memory_limit_in_bytes(); - log_trace(os, container)("Memory and Swap Limit has been reset to " JULONG_FORMAT " because swappiness is 0", memlimit); - return memlimit; - } return (jlong)memswlimit; } } +jlong CgroupV1Subsystem::memory_and_swap_limit_in_bytes() { + jlong memory_swap = read_mem_swap(); + if (memory_swap == -1) { + return memory_swap; + } + // If there is a swap limit, but swappiness == 0, reset the limit + // to the memory limit. Do the same for cases where swap isn't + // supported. + jlong swappiness = read_mem_swappiness(); + if (swappiness == 0 || memory_swap == OSCONTAINER_ERROR) { + jlong memlimit = read_memory_limit_in_bytes(); + if (memory_swap == OSCONTAINER_ERROR) { + log_trace(os, container)("Memory and Swap Limit has been reset to " JLONG_FORMAT " because swap is not supported", memlimit); + } else { + log_trace(os, container)("Memory and Swap Limit has been reset to " JLONG_FORMAT " because swappiness is 0", memlimit); + } + return memlimit; + } + return memory_swap; +} + jlong CgroupV1Subsystem::read_mem_swappiness() { GET_CONTAINER_INFO(julong, _memory->controller(), "/memory.swappiness", - "Swappiness is: " JULONG_FORMAT, JULONG_FORMAT, swappiness); + "Swappiness is: ", JULONG_FORMAT, JULONG_FORMAT, swappiness); return swappiness; } jlong CgroupV1Subsystem::memory_soft_limit_in_bytes() { GET_CONTAINER_INFO(julong, _memory->controller(), "/memory.soft_limit_in_bytes", - "Memory Soft Limit is: " JULONG_FORMAT, JULONG_FORMAT, memsoftlimit); + "Memory Soft Limit is: ", JULONG_FORMAT, JULONG_FORMAT, memsoftlimit); if (memsoftlimit >= os::Linux::physical_memory()) { log_trace(os, container)("Memory Soft Limit is: Unlimited"); return (jlong)-1; @@ -180,7 +196,7 @@ jlong CgroupV1Subsystem::memory_soft_limit_in_bytes() { */ jlong CgroupV1Subsystem::memory_usage_in_bytes() { GET_CONTAINER_INFO(jlong, _memory->controller(), "/memory.usage_in_bytes", - "Memory Usage is: " JLONG_FORMAT, JLONG_FORMAT, memusage); + "Memory Usage is: ", JLONG_FORMAT, JLONG_FORMAT, memusage); return memusage; } @@ -194,20 +210,31 @@ jlong CgroupV1Subsystem::memory_usage_in_bytes() { */ jlong CgroupV1Subsystem::memory_max_usage_in_bytes() { GET_CONTAINER_INFO(jlong, _memory->controller(), "/memory.max_usage_in_bytes", - "Maximum Memory Usage is: " JLONG_FORMAT, JLONG_FORMAT, memmaxusage); + "Maximum Memory Usage is: ", JLONG_FORMAT, JLONG_FORMAT, memmaxusage); return memmaxusage; } +jlong CgroupV1Subsystem::rss_usage_in_bytes() { + GET_CONTAINER_INFO_LINE(julong, _memory->controller(), "/memory.stat", + "rss", JULONG_FORMAT, JULONG_FORMAT, rss); + return rss; +} + +jlong CgroupV1Subsystem::cache_usage_in_bytes() { + GET_CONTAINER_INFO_LINE(julong, _memory->controller(), "/memory.stat", + "cache", JULONG_FORMAT, JULONG_FORMAT, cache); + return cache; +} jlong CgroupV1Subsystem::kernel_memory_usage_in_bytes() { GET_CONTAINER_INFO(jlong, _memory->controller(), "/memory.kmem.usage_in_bytes", - "Kernel Memory Usage is: " JLONG_FORMAT, JLONG_FORMAT, kmem_usage); + "Kernel Memory Usage is: ", JLONG_FORMAT, JLONG_FORMAT, kmem_usage); return kmem_usage; } jlong CgroupV1Subsystem::kernel_memory_limit_in_bytes() { GET_CONTAINER_INFO(julong, _memory->controller(), "/memory.kmem.limit_in_bytes", - "Kernel Memory Limit is: " JULONG_FORMAT, JULONG_FORMAT, kmem_limit); + "Kernel Memory Limit is: ", JULONG_FORMAT, JULONG_FORMAT, kmem_limit); if (kmem_limit >= os::Linux::physical_memory()) { return (jlong)-1; } @@ -216,7 +243,7 @@ jlong CgroupV1Subsystem::kernel_memory_limit_in_bytes() { jlong CgroupV1Subsystem::kernel_memory_max_usage_in_bytes() { GET_CONTAINER_INFO(jlong, _memory->controller(), "/memory.kmem.max_usage_in_bytes", - "Maximum Kernel Memory Usage is: " JLONG_FORMAT, JLONG_FORMAT, kmem_max_usage); + "Maximum Kernel Memory Usage is: ", JLONG_FORMAT, JLONG_FORMAT, kmem_max_usage); return kmem_max_usage; } @@ -254,13 +281,13 @@ char * CgroupV1Subsystem::cpu_cpuset_memory_nodes() { */ int CgroupV1Subsystem::cpu_quota() { GET_CONTAINER_INFO(int, _cpu->controller(), "/cpu.cfs_quota_us", - "CPU Quota is: %d", "%d", quota); + "CPU Quota is: ", "%d", "%d", quota); return quota; } int CgroupV1Subsystem::cpu_period() { GET_CONTAINER_INFO(int, _cpu->controller(), "/cpu.cfs_period_us", - "CPU Period is: %d", "%d", period); + "CPU Period is: ", "%d", "%d", period); return period; } @@ -276,7 +303,7 @@ int CgroupV1Subsystem::cpu_period() { */ int CgroupV1Subsystem::cpu_shares() { GET_CONTAINER_INFO(int, _cpu->controller(), "/cpu.shares", - "CPU Shares is: %d", "%d", shares); + "CPU Shares is: ", "%d", "%d", shares); // Convert 1024 to no shares setup if (shares == 1024) return -1; @@ -286,10 +313,7 @@ int CgroupV1Subsystem::cpu_shares() { char* CgroupV1Subsystem::pids_max_val() { GET_CONTAINER_INFO_CPTR(cptr, _pids, "/pids.max", - "Maximum number of tasks is: %s", "%s %*d", pidsmax, 1024); - if (pidsmax == NULL) { - return NULL; - } + "Maximum number of tasks is: %s", "%1023s", pidsmax, 1024); return os::strdup(pidsmax); } @@ -319,6 +343,6 @@ jlong CgroupV1Subsystem::pids_max() { jlong CgroupV1Subsystem::pids_current() { if (_pids == NULL) return OSCONTAINER_ERROR; GET_CONTAINER_INFO(jlong, _pids, "/pids.current", - "Current number of tasks is: " JLONG_FORMAT, JLONG_FORMAT, pids_current); + "Current number of tasks is: ", JLONG_FORMAT, JLONG_FORMAT, pids_current); return pids_current; } diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp index 07fac4a9461cd..9eb1dd2594e6f 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp @@ -79,6 +79,8 @@ class CgroupV1Subsystem: public CgroupSubsystem { jlong memory_soft_limit_in_bytes(); jlong memory_usage_in_bytes(); jlong memory_max_usage_in_bytes(); + jlong rss_usage_in_bytes(); + jlong cache_usage_in_bytes(); jlong kernel_memory_usage_in_bytes(); jlong kernel_memory_limit_in_bytes(); @@ -114,6 +116,7 @@ class CgroupV1Subsystem: public CgroupSubsystem { char * pids_max_val(); jlong read_mem_swappiness(); + jlong read_mem_swap(); public: CgroupV1Subsystem(CgroupV1Controller* cpuset, diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index 2456385af7abd..e8928a579d38c 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -36,7 +36,7 @@ */ int CgroupV2Subsystem::cpu_shares() { GET_CONTAINER_INFO(int, _unified, "/cpu.weight", - "Raw value for CPU Shares is: %d", "%d", shares); + "Raw value for CPU Shares is: ", "%d", "%d", shares); // Convert default value of 100 to no shares setup if (shares == 100) { log_debug(os, container)("CPU Shares is: %d", -1); @@ -92,33 +92,24 @@ int CgroupV2Subsystem::cpu_quota() { char * CgroupV2Subsystem::cpu_cpuset_cpus() { GET_CONTAINER_INFO_CPTR(cptr, _unified, "/cpuset.cpus", "cpuset.cpus is: %s", "%1023s", cpus, 1024); - if (cpus == NULL) { - return NULL; - } return os::strdup(cpus); } char* CgroupV2Subsystem::cpu_quota_val() { GET_CONTAINER_INFO_CPTR(cptr, _unified, "/cpu.max", - "Raw value for CPU quota is: %s", "%s %*d", quota, 1024); - if (quota == NULL) { - return NULL; - } + "Raw value for CPU quota is: %s", "%1023s %*d", quota, 1024); return os::strdup(quota); } char * CgroupV2Subsystem::cpu_cpuset_memory_nodes() { GET_CONTAINER_INFO_CPTR(cptr, _unified, "/cpuset.mems", "cpuset.mems is: %s", "%1023s", mems, 1024); - if (mems == NULL) { - return NULL; - } return os::strdup(mems); } int CgroupV2Subsystem::cpu_period() { GET_CONTAINER_INFO(int, _unified, "/cpu.max", - "CPU Period is: %d", "%*s %d", period); + "CPU Period is: ", "%d", "%*s %d", period); return period; } @@ -133,7 +124,7 @@ int CgroupV2Subsystem::cpu_period() { */ jlong CgroupV2Subsystem::memory_usage_in_bytes() { GET_CONTAINER_INFO(jlong, _unified, "/memory.current", - "Memory Usage is: " JLONG_FORMAT, JLONG_FORMAT, memusage); + "Memory Usage is: ", JLONG_FORMAT, JLONG_FORMAT, memusage); return memusage; } @@ -148,12 +139,21 @@ jlong CgroupV2Subsystem::memory_max_usage_in_bytes() { return OSCONTAINER_ERROR; // not supported } +jlong CgroupV2Subsystem::rss_usage_in_bytes() { + GET_CONTAINER_INFO_LINE(julong, _memory->controller(), "/memory.stat", + "anon", JULONG_FORMAT, JULONG_FORMAT, rss); + return rss; +} + +jlong CgroupV2Subsystem::cache_usage_in_bytes() { + GET_CONTAINER_INFO_LINE(julong, _memory->controller(), "/memory.stat", + "file", JULONG_FORMAT, JULONG_FORMAT, cache); + return cache; +} + char* CgroupV2Subsystem::mem_soft_limit_val() { GET_CONTAINER_INFO_CPTR(cptr, _unified, "/memory.low", - "Memory Soft Limit is: %s", "%s", mem_soft_limit_str, 1024); - if (mem_soft_limit_str == NULL) { - return NULL; - } + "Memory Soft Limit is: %s", "%1023s", mem_soft_limit_str, 1024); return os::strdup(mem_soft_limit_str); } @@ -164,31 +164,32 @@ char* CgroupV2Subsystem::mem_soft_limit_val() { // without also setting a memory limit is not allowed. jlong CgroupV2Subsystem::memory_and_swap_limit_in_bytes() { char* mem_swp_limit_str = mem_swp_limit_val(); + if (mem_swp_limit_str == nullptr) { + // Some container tests rely on this trace logging to happen. + log_trace(os, container)("Memory and Swap Limit is: %d", OSCONTAINER_ERROR); + // swap disabled at kernel level, treat it as no swap + return read_memory_limit_in_bytes(); + } jlong swap_limit = limit_from_str(mem_swp_limit_str); if (swap_limit >= 0) { jlong memory_limit = read_memory_limit_in_bytes(); assert(memory_limit >= 0, "swap limit without memory limit?"); return memory_limit + swap_limit; } + log_trace(os, container)("Memory and Swap Limit is: " JLONG_FORMAT, swap_limit); return swap_limit; } char* CgroupV2Subsystem::mem_swp_limit_val() { GET_CONTAINER_INFO_CPTR(cptr, _unified, "/memory.swap.max", - "Memory and Swap Limit is: %s", "%s", mem_swp_limit_str, 1024); - if (mem_swp_limit_str == NULL) { - return NULL; - } + "Memory and Swap Limit is: %s", "%1023s", mem_swp_limit_str, 1024); return os::strdup(mem_swp_limit_str); } // memory.swap.current : total amount of swap currently used by the cgroup and its descendants char* CgroupV2Subsystem::mem_swp_current_val() { GET_CONTAINER_INFO_CPTR(cptr, _unified, "/memory.swap.current", - "Swap currently used is: %s", "%s", mem_swp_current_str, 1024); - if (mem_swp_current_str == NULL) { - return NULL; - } + "Swap currently used is: %s", "%1023s", mem_swp_current_str, 1024); return os::strdup(mem_swp_current_str); } @@ -215,10 +216,7 @@ jlong CgroupV2Subsystem::read_memory_limit_in_bytes() { char* CgroupV2Subsystem::mem_limit_val() { GET_CONTAINER_INFO_CPTR(cptr, _unified, "/memory.max", - "Raw value for memory limit is: %s", "%s", mem_limit_str, 1024); - if (mem_limit_str == NULL) { - return NULL; - } + "Raw value for memory limit is: %s", "%1023s", mem_limit_str, 1024); return os::strdup(mem_limit_str); } @@ -244,10 +242,7 @@ char* CgroupV2Controller::construct_path(char* mount_path, char *cgroup_path) { char* CgroupV2Subsystem::pids_max_val() { GET_CONTAINER_INFO_CPTR(cptr, _unified, "/pids.max", - "Maximum number of tasks is: %s", "%s %*d", pidsmax, 1024); - if (pidsmax == NULL) { - return NULL; - } + "Maximum number of tasks is: %s", "%1023s", pidsmax, 1024); return os::strdup(pidsmax); } @@ -275,6 +270,6 @@ jlong CgroupV2Subsystem::pids_max() { */ jlong CgroupV2Subsystem::pids_current() { GET_CONTAINER_INFO(jlong, _unified, "/pids.current", - "Current number of tasks is: " JLONG_FORMAT, JLONG_FORMAT, pids_current); + "Current number of tasks is: ", JLONG_FORMAT, JLONG_FORMAT, pids_current); return pids_current; } diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp index bda5872bbbcaa..fe794b2fb9b34 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp @@ -78,6 +78,8 @@ class CgroupV2Subsystem: public CgroupSubsystem { jlong memory_soft_limit_in_bytes(); jlong memory_usage_in_bytes(); jlong memory_max_usage_in_bytes(); + jlong rss_usage_in_bytes(); + jlong cache_usage_in_bytes(); char * cpu_cpuset_cpus(); char * cpu_cpuset_memory_nodes(); diff --git a/src/hotspot/os/linux/osContainer_linux.cpp b/src/hotspot/os/linux/osContainer_linux.cpp index 52e6ab86c716c..ceca8038fc4b3 100644 --- a/src/hotspot/os/linux/osContainer_linux.cpp +++ b/src/hotspot/os/linux/osContainer_linux.cpp @@ -91,6 +91,16 @@ jlong OSContainer::memory_max_usage_in_bytes() { return cgroup_subsystem->memory_max_usage_in_bytes(); } +jlong OSContainer::rss_usage_in_bytes() { + assert(cgroup_subsystem != nullptr, "cgroup subsystem not available"); + return cgroup_subsystem->rss_usage_in_bytes(); +} + +jlong OSContainer::cache_usage_in_bytes() { + assert(cgroup_subsystem != nullptr, "cgroup subsystem not available"); + return cgroup_subsystem->cache_usage_in_bytes(); +} + void OSContainer::print_version_specific_info(outputStream* st) { assert(cgroup_subsystem != NULL, "cgroup subsystem not available"); cgroup_subsystem->print_version_specific_info(st); @@ -138,7 +148,7 @@ jlong OSContainer::pids_current() { void OSContainer::print_container_helper(outputStream* st, jlong j, const char* metrics) { st->print("%s: ", metrics); - if (j > 0) { + if (j >= 0) { if (j >= 1024) { st->print_cr(UINT64_FORMAT " k", uint64_t(j) / 1024); } else { diff --git a/src/hotspot/os/linux/osContainer_linux.hpp b/src/hotspot/os/linux/osContainer_linux.hpp index ecf9dc5459a0f..35575135fc5bd 100644 --- a/src/hotspot/os/linux/osContainer_linux.hpp +++ b/src/hotspot/os/linux/osContainer_linux.hpp @@ -55,6 +55,8 @@ class OSContainer: AllStatic { static jlong memory_soft_limit_in_bytes(); static jlong memory_usage_in_bytes(); static jlong memory_max_usage_in_bytes(); + static jlong rss_usage_in_bytes(); + static jlong cache_usage_in_bytes(); static int active_processor_count(); diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 7c951cee51cf7..3d242348168a2 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -2387,6 +2387,8 @@ bool os::Linux::print_container_info(outputStream* st) { OSContainer::print_container_helper(st, OSContainer::memory_soft_limit_in_bytes(), "memory_soft_limit_in_bytes"); OSContainer::print_container_helper(st, OSContainer::memory_usage_in_bytes(), "memory_usage_in_bytes"); OSContainer::print_container_helper(st, OSContainer::memory_max_usage_in_bytes(), "memory_max_usage_in_bytes"); + OSContainer::print_container_helper(st, OSContainer::rss_usage_in_bytes(), "rss_usage_in_bytes"); + OSContainer::print_container_helper(st, OSContainer::cache_usage_in_bytes(), "cache_usage_in_bytes"); OSContainer::print_version_specific_info(st); diff --git a/src/hotspot/os/linux/os_linux.hpp b/src/hotspot/os/linux/os_linux.hpp index dc83208f60387..e225d521f3230 100644 --- a/src/hotspot/os/linux/os_linux.hpp +++ b/src/hotspot/os/linux/os_linux.hpp @@ -31,9 +31,7 @@ static bool zero_page_read_protected() { return true; } class Linux { - friend class CgroupSubsystem; friend class os; - friend class OSContainer; friend class TestReserveMemorySpecial; static int (*_pthread_getcpuclockid)(pthread_t, clockid_t *); @@ -57,7 +55,6 @@ class Linux { static int _page_size; static julong available_memory(); - static int active_processor_count(); static void initialize_system_info(); @@ -107,6 +104,7 @@ class Linux { bool has_steal_ticks; }; + static int active_processor_count(); // which_logical_cpu=-1 returns accumulated ticks for all cpus. static bool get_tick_information(CPUPerfTicks* pticks, int which_logical_cpu); static bool _stack_is_executable; diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp index 4307a189edf12..1561f50fba79a 100644 --- a/src/hotspot/os/posix/os_posix.cpp +++ b/src/hotspot/os/posix/os_posix.cpp @@ -698,9 +698,20 @@ void* os::dll_lookup(void* handle, const char* name) { } void os::dll_unload(void *lib) { - const char* l_path = LINUX_ONLY(os::Linux::dll_path(lib)) - NOT_LINUX(""); - if (l_path == NULL) l_path = ""; + // os::Linux::dll_path returns a pointer to a string that is owned by the dynamic loader. Upon + // calling dlclose the dynamic loader may free the memory containing the string, thus we need to + // copy the string to be able to reference it after dlclose. + const char* l_path = NULL; +#ifdef LINUX + char* l_pathdup = NULL; + l_path = os::Linux::dll_path(lib); + if (l_path != NULL) { + l_path = l_pathdup = os::strdup(l_path); + } +#endif // LINUX + if (l_path == NULL) { + l_path = ""; + } int res = ::dlclose(lib); if (res == 0) { @@ -718,6 +729,7 @@ void os::dll_unload(void *lib) { log_info(os)("Attempt to unload shared library \"%s\" [" INTPTR_FORMAT "] failed, %s", l_path, p2i(lib), error_report); } + LINUX_ONLY(os::free(l_pathdup)); } jlong os::lseek(int fd, jlong offset, int whence) { diff --git a/src/hotspot/os/posix/signals_posix.cpp b/src/hotspot/os/posix/signals_posix.cpp index d800fa3381a86..c1c700d1996a4 100644 --- a/src/hotspot/os/posix/signals_posix.cpp +++ b/src/hotspot/os/posix/signals_posix.cpp @@ -42,6 +42,13 @@ #include +#define SEGV_BNDERR_value 3 + +#if defined(SEGV_BNDERR) +STATIC_ASSERT(SEGV_BNDERR == SEGV_BNDERR_value); +#else +#define SEGV_BNDERR SEGV_BNDERR_value +#endif static const char* get_signal_name(int sig, char* out, size_t outlen); @@ -763,7 +770,7 @@ static address get_signal_handler(const struct sigaction* action) { typedef int (*os_sigaction_t)(int, const struct sigaction *, struct sigaction *); -static void SR_handler(int sig, siginfo_t* siginfo, ucontext_t* context); +static void SR_handler(int sig, siginfo_t* siginfo, void* ucVoid); // Semantically compare two sigaction structures. Return true if they are referring to // the same handler, using the same flags. @@ -914,6 +921,9 @@ static bool get_signal_code_description(const siginfo_t* si, enum_sigcode_desc_t { SIGFPE, FPE_FLTSUB, "FPE_FLTSUB", "Subscript out of range." }, { SIGSEGV, SEGV_MAPERR, "SEGV_MAPERR", "Address not mapped to object." }, { SIGSEGV, SEGV_ACCERR, "SEGV_ACCERR", "Invalid permissions for mapped object." }, +#if defined(LINUX) + { SIGSEGV, SEGV_BNDERR, "SEGV_BNDERR", "Failed address bound checks." }, +#endif #if defined(AIX) // no explanation found what keyerr would be { SIGSEGV, SEGV_KEYERR, "SEGV_KEYERR", "key error" }, @@ -1555,8 +1565,8 @@ static void resume_clear_context(OSThread *osthread) { osthread->set_siginfo(NULL); } -static void suspend_save_context(OSThread *osthread, siginfo_t* siginfo, ucontext_t* context) { - osthread->set_ucontext(context); +static void suspend_save_context(OSThread *osthread, siginfo_t* siginfo, void* ucVoid) { + osthread->set_ucontext((ucontext_t*)ucVoid); osthread->set_siginfo(siginfo); } @@ -1573,7 +1583,7 @@ static void suspend_save_context(OSThread *osthread, siginfo_t* siginfo, ucontex // // Currently only ever called on the VMThread and JavaThreads (PC sampling) // -static void SR_handler(int sig, siginfo_t* siginfo, ucontext_t* context) { +static void SR_handler(int sig, siginfo_t* siginfo, void* ucVoid) { // Save and restore errno to avoid confusing native code with EINTR // after sigsuspend. @@ -1601,7 +1611,7 @@ static void SR_handler(int sig, siginfo_t* siginfo, ucontext_t* context) { os::SuspendResume::State current = osthread->sr.state(); if (current == os::SuspendResume::SR_SUSPEND_REQUEST) { - suspend_save_context(osthread, siginfo, context); + suspend_save_context(osthread, siginfo, ucVoid); // attempt to switch the state, we assume we had a SUSPEND_REQUEST os::SuspendResume::State state = osthread->sr.suspended(); @@ -1670,7 +1680,7 @@ int SR_initialize() { // Set up signal handler for suspend/resume act.sa_flags = SA_RESTART|SA_SIGINFO; - act.sa_handler = (void (*)(int)) SR_handler; + act.sa_sigaction = SR_handler; // SR_signum is blocked by default. pthread_sigmask(SIG_BLOCK, NULL, &act.sa_mask); diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index dd318b0653256..c1a8b07b8fd4d 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -4547,7 +4547,7 @@ static errno_t get_full_path(LPCWSTR unicode_path, LPWSTR* full_path) { return ERROR_SUCCESS; } -static void set_path_prefix(char* buf, LPWSTR* prefix, int* prefix_off, bool* needs_fullpath) { +static void set_path_prefix(char* buf, LPCWSTR* prefix, int* prefix_off, bool* needs_fullpath) { *prefix_off = 0; *needs_fullpath = true; @@ -4583,7 +4583,7 @@ static wchar_t* wide_abs_unc_path(char const* path, errno_t & err, int additiona strncpy(buf, path, buf_len); os::native_path(buf); - LPWSTR prefix = NULL; + LPCWSTR prefix = NULL; int prefix_off = 0; bool needs_fullpath = true; set_path_prefix(buf, &prefix, &prefix_off, &needs_fullpath); diff --git a/src/hotspot/os_cpu/aix_ppc/thread_aix_ppc.cpp b/src/hotspot/os_cpu/aix_ppc/thread_aix_ppc.cpp index 3bfee91458817..69cd56e4f1575 100644 --- a/src/hotspot/os_cpu/aix_ppc/thread_aix_ppc.cpp +++ b/src/hotspot/os_cpu/aix_ppc/thread_aix_ppc.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2014 SAP SE. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2025 SAP SE. All rights reserved. * Copyright (c) 2022, IBM Corp. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -27,6 +27,7 @@ #include "precompiled.hpp" #include "memory/metaspace.hpp" #include "runtime/frame.inline.hpp" +#include "runtime/os.inline.hpp" #include "runtime/thread.hpp" frame JavaThread::pd_last_frame() { @@ -54,9 +55,19 @@ bool JavaThread::pd_get_top_frame_for_profiling(frame* fr_addr, void* ucontext, if (has_last_Java_frame() && frame_anchor()->walkable()) { intptr_t* sp = last_Java_sp(); address pc = _anchor.last_Java_pc(); - // pc can be seen as null because not all writers use store pc + release store sp. - // Simply discard the sample in this very rare case. - if (pc == nullptr) return false; + if (pc == nullptr) { + // This is not uncommon. Many c1/c2 runtime stubs do not set the pc in the anchor. + intptr_t* top_sp = os::Aix::ucontext_get_sp((const ucontext_t*)ucontext); + if ((uint64_t)sp <= ((frame::abi_minframe*)top_sp)->callers_sp) { + // The interrupt occurred either in the last java frame or in its direct callee. + // We cannot be sure that the link register LR was already saved to the + // java frame. Therefore we discard this sample. + return false; + } + // The last java pc will be found in the abi part of the last java frame. + *fr_addr = frame(sp); + return true; + } *fr_addr = frame(sp, pc); return true; } diff --git a/src/hotspot/os_cpu/linux_ppc/thread_linux_ppc.cpp b/src/hotspot/os_cpu/linux_ppc/thread_linux_ppc.cpp index bf15786ae1f73..152c39485e20b 100644 --- a/src/hotspot/os_cpu/linux_ppc/thread_linux_ppc.cpp +++ b/src/hotspot/os_cpu/linux_ppc/thread_linux_ppc.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2022 SAP SE. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2025 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ #include "precompiled.hpp" #include "memory/metaspace.hpp" #include "runtime/frame.inline.hpp" +#include "runtime/os.hpp" #include "runtime/thread.hpp" frame JavaThread::pd_last_frame() { @@ -53,9 +54,19 @@ bool JavaThread::pd_get_top_frame_for_profiling(frame* fr_addr, void* ucontext, if (has_last_Java_frame() && frame_anchor()->walkable()) { intptr_t* sp = last_Java_sp(); address pc = _anchor.last_Java_pc(); - // pc can be seen as null because not all writers use store pc + release store sp. - // Simply discard the sample in this very rare case. - if (pc == nullptr) return false; + if (pc == nullptr) { + // This is not uncommon. Many c1/c2 runtime stubs do not set the pc in the anchor. + intptr_t* top_sp = os::Linux::ucontext_get_sp((const ucontext_t*)ucontext); + if ((uint64_t)sp <= ((frame::abi_minframe*)top_sp)->callers_sp) { + // The interrupt occurred either in the last java frame or in its direct callee. + // We cannot be sure that the link register LR was already saved to the + // java frame. Therefore we discard this sample. + return false; + } + // The last java pc will be found in the abi part of the last java frame. + *fr_addr = frame(sp); + return true; + } *fr_addr = frame(sp, pc); return true; } diff --git a/src/hotspot/share/asm/codeBuffer.cpp b/src/hotspot/share/asm/codeBuffer.cpp index 2d219121cc7dd..48ae3a73523a5 100644 --- a/src/hotspot/share/asm/codeBuffer.cpp +++ b/src/hotspot/share/asm/codeBuffer.cpp @@ -88,7 +88,7 @@ typedef CodeBuffer::csize_t csize_t; // file-local definition // External buffer, in a predefined CodeBlob. // Important: The code_start must be taken exactly, and not realigned. -CodeBuffer::CodeBuffer(CodeBlob* blob) { +CodeBuffer::CodeBuffer(CodeBlob* blob) DEBUG_ONLY(: Scrubber(this, sizeof(*this))) { // Provide code buffer with meaningful name initialize_misc(blob->name()); initialize(blob->content_begin(), blob->content_size()); @@ -126,11 +126,9 @@ void CodeBuffer::initialize(csize_t code_size, csize_t locs_size) { CodeBuffer::~CodeBuffer() { verify_section_allocation(); - // If we allocate our code buffer from the CodeCache - // via a BufferBlob, and it's not permanent, then - // free the BufferBlob. - // The rest of the memory will be freed when the ResourceObj - // is released. + // If we allocated our code buffer from the CodeCache via a BufferBlob, and + // it's not permanent, then free the BufferBlob. The rest of the memory + // will be freed when the ResourceObj is released. for (CodeBuffer* cb = this; cb != NULL; cb = cb->before_expand()) { // Previous incarnations of this buffer are held live, so that internal // addresses constructed before expansions will not be confused. @@ -140,18 +138,9 @@ CodeBuffer::~CodeBuffer() { // free any overflow storage delete _overflow_arena; } - // Claim is that stack allocation ensures resources are cleaned up. - // This is resource clean up, let's hope that all were properly copied out. - NOT_PRODUCT(free_strings();) + NOT_PRODUCT(clear_strings()); -#ifdef ASSERT - // Save allocation type to execute assert in ~ResourceObj() - // which is called after this destructor. assert(_default_oop_recorder.allocated_on_stack(), "should be embedded object"); - ResourceObj::allocation_type at = _default_oop_recorder.get_allocation_type(); - Copy::fill_to_bytes(this, sizeof(*this), badResourceValue); - ResourceObj::set_allocation_type((address)(&_default_oop_recorder), at); -#endif } void CodeBuffer::initialize_oop_recorder(OopRecorder* r) { @@ -715,8 +704,9 @@ void CodeBuffer::copy_code_to(CodeBlob* dest_blob) { relocate_code_to(&dest); - // transfer strings and comments from buffer to blob - NOT_PRODUCT(dest_blob->set_strings(_code_strings);) + // Share assembly remarks and debug strings with the blob. + NOT_PRODUCT(dest_blob->use_remarks(_asm_remarks)); + NOT_PRODUCT(dest_blob->use_strings(_dbg_strings)); // Done moving code bytes; were they the right size? assert((int)align_up(dest.total_content_size(), oopSize) == dest_blob->content_size(), "sanity"); @@ -989,221 +979,342 @@ void CodeBuffer::log_section_sizes(const char* name) { } #ifndef PRODUCT - -void CodeBuffer::block_comment(intptr_t offset, const char * comment) { +void CodeBuffer::block_comment(ptrdiff_t offset, const char* comment) { if (_collect_comments) { - _code_strings.add_comment(offset, comment); + const char* str = _asm_remarks.insert(offset, comment); + postcond(str != comment); } } const char* CodeBuffer::code_string(const char* str) { - return _code_strings.add_string(str); + const char* tmp = _dbg_strings.insert(str); + postcond(tmp != str); + return tmp; } -class CodeString: public CHeapObj { - private: - friend class CodeStrings; - const char * _string; - CodeString* _next; - CodeString* _prev; - intptr_t _offset; - - static long allocated_code_strings; - - ~CodeString() { - assert(_next == NULL && _prev == NULL, "wrong interface for freeing list"); - allocated_code_strings--; - log_trace(codestrings)("Freeing CodeString [%s] (%p)", _string, (void*)_string); - os::free((void*)_string); +void CodeBuffer::decode() { + ttyLocker ttyl; + Disassembler::decode(decode_begin(), insts_end(), tty NOT_PRODUCT(COMMA &asm_remarks())); + _decode_begin = insts_end(); +} + +void CodeSection::print(const char* name) { + csize_t locs_size = locs_end() - locs_start(); + tty->print_cr(" %7s.code = " PTR_FORMAT " : " PTR_FORMAT " : " PTR_FORMAT " (%d of %d)", + name, p2i(start()), p2i(end()), p2i(limit()), size(), capacity()); + tty->print_cr(" %7s.locs = " PTR_FORMAT " : " PTR_FORMAT " : " PTR_FORMAT " (%d of %d) point=%d", + name, p2i(locs_start()), p2i(locs_end()), p2i(locs_limit()), locs_size, locs_capacity(), locs_point_off()); + if (PrintRelocations) { + RelocIterator iter(this); + iter.print(); } +} - bool is_comment() const { return _offset >= 0; } +void CodeBuffer::print() { + if (this == NULL) { + tty->print_cr("NULL CodeBuffer pointer"); + return; + } + tty->print_cr("CodeBuffer:"); + for (int n = 0; n < (int)SECT_LIMIT; n++) { + // print each section + CodeSection* cs = code_section(n); + cs->print(code_section_name(n)); + } +} + +// ----- CHeapString ----------------------------------------------------------- + +class CHeapString : public CHeapObj { public: - CodeString(const char * string, intptr_t offset = -1) - : _next(NULL), _prev(NULL), _offset(offset) { - allocated_code_strings++; - _string = os::strdup(string, mtCode); - log_trace(codestrings)("Created CodeString [%s] (%p)", _string, (void*)_string); + CHeapString(const char* str) : _string(os::strdup(str)) {} + ~CHeapString() { + os::free((void*)_string); + _string = nullptr; } + const char* string() const { return _string; } + + private: + const char* _string; +}; - const char * string() const { return _string; } - intptr_t offset() const { assert(_offset >= 0, "offset for non comment?"); return _offset; } - CodeString* next() const { return _next; } +// ----- AsmRemarkCollection --------------------------------------------------- - void set_next(CodeString* next) { - _next = next; - if (next != NULL) { - next->_prev = this; - } +class AsmRemarkCollection : public CHeapObj { + public: + AsmRemarkCollection() : _ref_cnt(1), _remarks(nullptr), _next(nullptr) {} + ~AsmRemarkCollection() { + assert(is_empty(), "Must 'clear()' before deleting!"); + assert(_ref_cnt == 0, "No uses must remain when deleting!"); + } + AsmRemarkCollection* reuse() { + precond(_ref_cnt > 0); + return _ref_cnt++, this; } - CodeString* first_comment() { - if (is_comment()) { - return this; - } else { - return next_comment(); + const char* insert(uint offset, const char* remark); + const char* lookup(uint offset) const; + const char* next(uint offset) const; + + bool is_empty() const { return _remarks == nullptr; } + uint clear(); + + private: + struct Cell : CHeapString { + Cell(const char* remark, uint offset) : + CHeapString(remark), offset(offset), prev(nullptr), next(nullptr) {} + void push_back(Cell* cell) { + Cell* head = this; + Cell* tail = prev; + tail->next = cell; + cell->next = head; + cell->prev = tail; + prev = cell; } + uint offset; + Cell* prev; + Cell* next; + }; + uint _ref_cnt; + Cell* _remarks; + // Using a 'mutable' iteration pointer to allow 'const' on lookup/next (that + // does not change the state of the list per se), supportig a simplistic + // iteration scheme. + mutable Cell* _next; +}; + +// ----- DbgStringCollection --------------------------------------------------- + +class DbgStringCollection : public CHeapObj { + public: + DbgStringCollection() : _ref_cnt(1), _strings(nullptr) {} + ~DbgStringCollection() { + assert(is_empty(), "Must 'clear()' before deleting!"); + assert(_ref_cnt == 0, "No uses must remain when deleting!"); } - CodeString* next_comment() const { - CodeString* s = _next; - while (s != NULL && !s->is_comment()) { - s = s->_next; - } - return s; + DbgStringCollection* reuse() { + precond(_ref_cnt > 0); + return _ref_cnt++, this; } + + const char* insert(const char* str); + const char* lookup(const char* str) const; + + bool is_empty() const { return _strings == nullptr; } + uint clear(); + + private: + struct Cell : CHeapString { + Cell(const char* dbgstr) : + CHeapString(dbgstr), prev(nullptr), next(nullptr) {} + void push_back(Cell* cell) { + Cell* head = this; + Cell* tail = prev; + tail->next = cell; + cell->next = head; + cell->prev = tail; + prev = cell; + } + Cell* prev; + Cell* next; + }; + uint _ref_cnt; + Cell* _strings; }; -// For tracing statistics. Will use raw increment/decrement, so it might not be -// exact -long CodeString::allocated_code_strings = 0; +// ----- AsmRemarks ------------------------------------------------------------ +// +// Acting as interface to reference counted mapping [offset -> remark], where +// offset is a byte offset into an instruction stream (CodeBuffer, CodeBlob or +// other memory buffer) and remark is a string (comment). +// +AsmRemarks::AsmRemarks() : _remarks(new AsmRemarkCollection()) { + assert(_remarks != nullptr, "Allocation failure!"); +} -CodeString* CodeStrings::find(intptr_t offset) const { - CodeString* a = _strings->first_comment(); - while (a != NULL && a->offset() != offset) { - a = a->next_comment(); - } - return a; +AsmRemarks::~AsmRemarks() { + assert(_remarks == nullptr, "Must 'clear()' before deleting!"); } -// Convenience for add_comment. -CodeString* CodeStrings::find_last(intptr_t offset) const { - CodeString* a = _strings_last; - while (a != NULL && !(a->is_comment() && a->offset() == offset)) { - a = a->_prev; - } - return a; +const char* AsmRemarks::insert(uint offset, const char* remstr) { + precond(remstr != nullptr); + return _remarks->insert(offset, remstr); } -void CodeStrings::add_comment(intptr_t offset, const char * comment) { - check_valid(); - CodeString* c = new CodeString(comment, offset); - CodeString* inspos = (_strings == NULL) ? NULL : find_last(offset); +bool AsmRemarks::is_empty() const { + return _remarks->is_empty(); +} - if (inspos != NULL) { - // insert after already existing comments with same offset - c->set_next(inspos->next()); - inspos->set_next(c); - } else { - // no comments with such offset, yet. Insert before anything else. - c->set_next(_strings); - _strings = c; - } - if (c->next() == NULL) { - _strings_last = c; - } +void AsmRemarks::share(const AsmRemarks &src) { + precond(is_empty()); + clear(); + _remarks = src._remarks->reuse(); } -// Deep copy of CodeStrings for consistent memory management. -void CodeStrings::copy(CodeStrings& other) { - log_debug(codestrings)("Copying %d Codestring(s)", other.count()); - - other.check_valid(); - check_valid(); - assert(is_null(), "Cannot copy onto non-empty CodeStrings"); - CodeString* n = other._strings; - CodeString** ps = &_strings; - CodeString* prev = NULL; - while (n != NULL) { - if (n->is_comment()) { - *ps = new CodeString(n->string(), n->offset()); - } else { - *ps = new CodeString(n->string()); - } - (*ps)->_prev = prev; - prev = *ps; - ps = &((*ps)->_next); - n = n->next(); +void AsmRemarks::clear() { + if (_remarks->clear() == 0) { + delete _remarks; } + _remarks = nullptr; } -const char* CodeStrings::_prefix = " ;; "; // default: can be changed via set_prefix - -void CodeStrings::print_block_comment(outputStream* stream, intptr_t offset) const { - check_valid(); - if (_strings != NULL) { - CodeString* c = find(offset); - while (c && c->offset() == offset) { - stream->bol(); - stream->print("%s", _prefix); - // Don't interpret as format strings since it could contain % - stream->print_raw(c->string()); - stream->bol(); // advance to next line only if string didn't contain a cr() at the end. - c = c->next_comment(); - } +uint AsmRemarks::print(uint offset, outputStream* strm) const { + uint count = 0; + const char* prefix = " ;; "; + const char* remstr = _remarks->lookup(offset); + while (remstr != nullptr) { + strm->bol(); + strm->print("%s", prefix); + // Don't interpret as format strings since it could contain '%'. + strm->print_raw(remstr); + // Advance to next line iff string didn't contain a cr() at the end. + strm->bol(); + remstr = _remarks->next(offset); + count++; } + return count; +} + +// ----- DbgStrings ------------------------------------------------------------ +// +// Acting as interface to reference counted collection of (debug) strings used +// in the code generated, and thus requiring a fixed address. +// +DbgStrings::DbgStrings() : _strings(new DbgStringCollection()) { + assert(_strings != nullptr, "Allocation failure!"); +} + +DbgStrings::~DbgStrings() { + assert(_strings == nullptr, "Must 'clear()' before deleting!"); +} + +const char* DbgStrings::insert(const char* dbgstr) { + const char* str = _strings->lookup(dbgstr); + return str != nullptr ? str : _strings->insert(dbgstr); +} + +bool DbgStrings::is_empty() const { + return _strings->is_empty(); +} + +void DbgStrings::share(const DbgStrings &src) { + precond(is_empty()); + _strings = src._strings->reuse(); } -int CodeStrings::count() const { - int i = 0; - CodeString* s = _strings; - while (s != NULL) { - i++; - s = s->_next; +void DbgStrings::clear() { + if (_strings->clear() == 0) { + delete _strings; } - return i; + _strings = nullptr; } -// Also sets is_null() -void CodeStrings::free() { - log_debug(codestrings)("Freeing %d out of approx. %ld CodeString(s), ", count(), CodeString::allocated_code_strings); - CodeString* n = _strings; - while (n) { - // unlink the node from the list saving a pointer to the next - CodeString* p = n->next(); - n->set_next(NULL); - if (p != NULL) { - assert(p->_prev == n, "missing prev link"); - p->_prev = NULL; - } - delete n; - n = p; +// ----- AsmRemarkCollection --------------------------------------------------- + +const char* AsmRemarkCollection::insert(uint offset, const char* remstr) { + precond(remstr != nullptr); + Cell* cell = new Cell { remstr, offset }; + if (is_empty()) { + cell->prev = cell; + cell->next = cell; + _remarks = cell; + } else { + _remarks->push_back(cell); } - set_null_and_invalidate(); + return cell->string(); } -const char* CodeStrings::add_string(const char * string) { - check_valid(); - CodeString* s = new CodeString(string); - s->set_next(_strings); - if (_strings == NULL) { - _strings_last = s; +const char* AsmRemarkCollection::lookup(uint offset) const { + _next = _remarks; + return next(offset); +} + +const char* AsmRemarkCollection::next(uint offset) const { + if (_next != nullptr) { + Cell* i = _next; + do { + if (i->offset == offset) { + _next = i->next == _remarks ? nullptr : i->next; + return i->string(); + } + i = i->next; + } while (i != _remarks); + _next = nullptr; } - _strings = s; - assert(s->string() != NULL, "should have a string"); - return s->string(); + return nullptr; } -void CodeBuffer::decode() { - ttyLocker ttyl; - Disassembler::decode(decode_begin(), insts_end(), tty NOT_PRODUCT(COMMA &strings())); - _decode_begin = insts_end(); +uint AsmRemarkCollection::clear() { + precond(_ref_cnt > 0); + if (--_ref_cnt > 0) { + return _ref_cnt; + } + if (!is_empty()) { + uint count = 0; + Cell* i = _remarks; + do { + Cell* next = i->next; + delete i; + i = next; + count++; + } while (i != _remarks); + + log_debug(codestrings)("Clear %u asm-remark%s.", count, count == 1 ? "" : "s"); + _remarks = nullptr; + } + return 0; // i.e. _ref_cnt == 0 } -void CodeSection::print(const char* name) { - csize_t locs_size = locs_end() - locs_start(); - tty->print_cr(" %7s.code = " PTR_FORMAT " : " PTR_FORMAT " : " PTR_FORMAT " (%d of %d)", - name, p2i(start()), p2i(end()), p2i(limit()), size(), capacity()); - tty->print_cr(" %7s.locs = " PTR_FORMAT " : " PTR_FORMAT " : " PTR_FORMAT " (%d of %d) point=%d", - name, p2i(locs_start()), p2i(locs_end()), p2i(locs_limit()), locs_size, locs_capacity(), locs_point_off()); - if (PrintRelocations) { - RelocIterator iter(this); - iter.print(); +// ----- DbgStringCollection --------------------------------------------------- + +const char* DbgStringCollection::insert(const char* dbgstr) { + precond(dbgstr != nullptr); + Cell* cell = new Cell { dbgstr }; + + if (is_empty()) { + cell->prev = cell; + cell->next = cell; + _strings = cell; + } else { + _strings->push_back(cell); } + return cell->string(); } -void CodeBuffer::print() { - if (this == NULL) { - tty->print_cr("NULL CodeBuffer pointer"); - return; +const char* DbgStringCollection::lookup(const char* dbgstr) const { + precond(dbgstr != nullptr); + if (_strings != nullptr) { + Cell* i = _strings; + do { + if (strcmp(i->string(), dbgstr) == 0) { + return i->string(); + } + i = i->next; + } while (i != _strings); } + return nullptr; +} - tty->print_cr("CodeBuffer:"); - for (int n = 0; n < (int)SECT_LIMIT; n++) { - // print each section - CodeSection* cs = code_section(n); - cs->print(code_section_name(n)); +uint DbgStringCollection::clear() { + precond(_ref_cnt > 0); + if (--_ref_cnt > 0) { + return _ref_cnt; + } + if (!is_empty()) { + uint count = 0; + Cell* i = _strings; + do { + Cell* next = i->next; + delete i; + i = next; + count++; + } while (i != _strings); + + log_debug(codestrings)("Clear %u dbg-string%s.", count, count == 1 ? "" : "s"); + _strings = nullptr; } + return 0; // i.e. _ref_cnt == 0 } -#endif // PRODUCT +#endif // not PRODUCT diff --git a/src/hotspot/share/asm/codeBuffer.hpp b/src/hotspot/share/asm/codeBuffer.hpp index be1e14ad86cf6..773994a01af9e 100644 --- a/src/hotspot/share/asm/codeBuffer.hpp +++ b/src/hotspot/share/asm/codeBuffer.hpp @@ -31,7 +31,6 @@ #include "utilities/debug.hpp" #include "utilities/macros.hpp" -class CodeStrings; class PhaseCFG; class Compile; class BufferBlob; @@ -222,7 +221,11 @@ class CodeSection { set_end(curr); } - void emit_int32(int32_t x) { *((int32_t*) end()) = x; set_end(end() + sizeof(int32_t)); } + void emit_int32(int32_t x) { + address curr = end(); + *((int32_t*) curr) = x; + set_end(curr + sizeof(int32_t)); + } void emit_int32(int8_t x1, int8_t x2, int8_t x3, int8_t x4) { address curr = end(); *((int8_t*) curr++) = x1; @@ -269,70 +272,75 @@ class CodeSection { #endif //PRODUCT }; -class CodeString; -class CodeStrings { -private: + #ifndef PRODUCT - CodeString* _strings; - CodeString* _strings_last; -#ifdef ASSERT - // Becomes true after copy-out, forbids further use. - bool _defunct; // Zero bit pattern is "valid", see memset call in decode_env::decode_env -#endif - static const char* _prefix; // defaults to " ;; " - CodeString* find(intptr_t offset) const; - CodeString* find_last(intptr_t offset) const; +class AsmRemarkCollection; +class DbgStringCollection; - void set_null_and_invalidate() { - _strings = NULL; - _strings_last = NULL; -#ifdef ASSERT - _defunct = true; -#endif - } -#endif +// The assumption made here is that most code remarks (or comments) added to +// the generated assembly code are unique, i.e. there is very little gain in +// trying to share the strings between the different offsets tracked in a +// buffer (or blob). -public: - CodeStrings() { -#ifndef PRODUCT - _strings = NULL; - _strings_last = NULL; -#ifdef ASSERT - _defunct = false; -#endif -#endif - } +class AsmRemarks { + public: + AsmRemarks(); + ~AsmRemarks(); -#ifndef PRODUCT - bool is_null() { -#ifdef ASSERT - return _strings == NULL; -#else - return true; -#endif - } + const char* insert(uint offset, const char* remstr); - const char* add_string(const char * string); + bool is_empty() const; - void add_comment(intptr_t offset, const char * comment); - void print_block_comment(outputStream* stream, intptr_t offset) const; - int count() const; - // COPY strings from other to this; leave other valid. - void copy(CodeStrings& other); - // FREE strings; invalidate this. - void free(); + void share(const AsmRemarks &src); + void clear(); + uint print(uint offset, outputStream* strm = tty) const; - // Guarantee that _strings are used at most once; assign and free invalidate a buffer. - inline void check_valid() const { - assert(!_defunct, "Use of invalid CodeStrings"); - } + // For testing purposes only. + const AsmRemarkCollection* ref() const { return _remarks; } + +private: + AsmRemarkCollection* _remarks; +}; + +// The assumption made here is that the number of debug strings (with a fixed +// address requirement) is a rather small set per compilation unit. + +class DbgStrings { + public: + DbgStrings(); + ~DbgStrings(); + + const char* insert(const char* dbgstr); + + bool is_empty() const; + + void share(const DbgStrings &src); + void clear(); - static void set_prefix(const char *prefix) { - _prefix = prefix; + // For testing purposes only. + const DbgStringCollection* ref() const { return _strings; } + +private: + DbgStringCollection* _strings; +}; +#endif // not PRODUCT + + +#ifdef ASSERT +#include "utilities/copy.hpp" + +class Scrubber { + public: + Scrubber(void* addr, size_t size) : _addr(addr), _size(size) {} + ~Scrubber() { + Copy::fill_to_bytes(_addr, _size, badResourceValue); } -#endif // !PRODUCT + private: + void* _addr; + size_t _size; }; +#endif // ASSERT // A CodeBuffer describes a memory space into which assembly // code is generated. This memory space usually occupies the @@ -358,7 +366,7 @@ class CodeStrings { // Instructions and data in one section can contain relocatable references to // addresses in a sibling section. -class CodeBuffer: public StackObj { +class CodeBuffer: public StackObj DEBUG_ONLY(COMMA private Scrubber) { friend class CodeSection; friend class StubCodeGenerator; @@ -407,7 +415,8 @@ class CodeBuffer: public StackObj { address _last_insn; // used to merge consecutive memory barriers, loads or stores. #ifndef PRODUCT - CodeStrings _code_strings; + AsmRemarks _asm_remarks; + DbgStrings _dbg_strings; bool _collect_comments; // Indicate if we need to collect block comments at all. address _decode_begin; // start address for decode address decode_begin(); @@ -425,7 +434,6 @@ class CodeBuffer: public StackObj { #ifndef PRODUCT _decode_begin = NULL; - _code_strings = CodeStrings(); // Collect block comments, but restrict collection to cases where a disassembly is output. _collect_comments = ( PrintAssembly || PrintStubCode @@ -480,7 +488,9 @@ class CodeBuffer: public StackObj { public: // (1) code buffer referring to pre-allocated instruction memory - CodeBuffer(address code_start, csize_t code_size) { + CodeBuffer(address code_start, csize_t code_size) + DEBUG_ONLY(: Scrubber(this, sizeof(*this))) + { assert(code_start != NULL, "sanity"); initialize_misc("static buffer"); initialize(code_start, code_size); @@ -493,14 +503,18 @@ class CodeBuffer: public StackObj { // (3) code buffer allocating codeBlob memory for code & relocation // info but with lazy initialization. The name must be something // informative. - CodeBuffer(const char* name) { + CodeBuffer(const char* name) + DEBUG_ONLY(: Scrubber(this, sizeof(*this))) + { initialize_misc(name); } // (4) code buffer allocating codeBlob memory for code & relocation // info. The name must be something informative and code_size must // include both code and stubs sizes. - CodeBuffer(const char* name, csize_t code_size, csize_t locs_size) { + CodeBuffer(const char* name, csize_t code_size, csize_t locs_size) + DEBUG_ONLY(: Scrubber(this, sizeof(*this))) + { initialize_misc(name); initialize(code_size, locs_size); } @@ -630,12 +644,12 @@ class CodeBuffer: public StackObj { void clear_last_insn() { set_last_insn(NULL); } #ifndef PRODUCT - CodeStrings& strings() { return _code_strings; } + AsmRemarks &asm_remarks() { return _asm_remarks; } + DbgStrings &dbg_strings() { return _dbg_strings; } - void free_strings() { - if (!_code_strings.is_null()) { - _code_strings.free(); // sets _strings Null as a side-effect. - } + void clear_strings() { + _asm_remarks.clear(); + _dbg_strings.clear(); } #endif @@ -662,7 +676,7 @@ class CodeBuffer: public StackObj { } } - void block_comment(intptr_t offset, const char * comment) PRODUCT_RETURN; + void block_comment(ptrdiff_t offset, const char* comment) PRODUCT_RETURN; const char* code_string(const char* str) PRODUCT_RETURN_(return NULL;); // Log a little info about section usage in the CodeBuffer diff --git a/src/hotspot/share/c1/c1_Canonicalizer.hpp b/src/hotspot/share/c1/c1_Canonicalizer.hpp index fe6095d5bdc29..884e9c8ee99cc 100644 --- a/src/hotspot/share/c1/c1_Canonicalizer.hpp +++ b/src/hotspot/share/c1/c1_Canonicalizer.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,10 +40,6 @@ class Canonicalizer: InstructionVisitor { void set_constant(jlong x) { set_canonical(new Constant(new LongConstant(x))); } void set_constant(jfloat x) { set_canonical(new Constant(new FloatConstant(x))); } void set_constant(jdouble x) { set_canonical(new Constant(new DoubleConstant(x))); } -#ifdef _WINDOWS - // jint is defined as long in jni_md.h, so convert from int to jint - void set_constant(int x) { set_constant((jint)x); } -#endif void move_const_to_right(Op2* x); void do_Op2(Op2* x); void do_UnsafeRawOp(UnsafeRawOp* x); diff --git a/src/hotspot/share/c1/c1_globals.hpp b/src/hotspot/share/c1/c1_globals.hpp index 7564b2b8aae0d..41d4607f8e32e 100644 --- a/src/hotspot/share/c1/c1_globals.hpp +++ b/src/hotspot/share/c1/c1_globals.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -287,9 +287,11 @@ develop(bool, InstallMethods, true, \ "Install methods at the end of successful compilations") \ \ + /* The compiler assumes, in many places, that methods are at most 1MB. */ \ + /* Therefore, we restrict this flag to at most 1MB. */ \ develop(intx, NMethodSizeLimit, (64*K)*wordSize, \ "Maximum size of a compiled method.") \ - range(0, max_jint) \ + range(0, 1*M) \ \ develop(bool, TraceFPUStack, false, \ "Trace emulation of the FPU stack (intel only)") \ diff --git a/src/hotspot/share/classfile/classLoaderData.cpp b/src/hotspot/share/classfile/classLoaderData.cpp index 006e9c9602f20..e0dcf69baa73d 100644 --- a/src/hotspot/share/classfile/classLoaderData.cpp +++ b/src/hotspot/share/classfile/classLoaderData.cpp @@ -985,14 +985,23 @@ void ClassLoaderData::print_on(outputStream* out) const { } out->print_cr(" - handles %d", _handles.count()); out->print_cr(" - dependency count %d", _dependency_count); - out->print (" - klasses {"); - PrintKlassClosure closure(out); - ((ClassLoaderData*)this)->classes_do(&closure); + out->print (" - klasses { "); + if (Verbose) { + PrintKlassClosure closure(out); + ((ClassLoaderData*)this)->classes_do(&closure); + } else { + out->print("..."); + } out->print_cr(" }"); out->print_cr(" - packages " INTPTR_FORMAT, p2i(_packages)); out->print_cr(" - module " INTPTR_FORMAT, p2i(_modules)); out->print_cr(" - unnamed module " INTPTR_FORMAT, p2i(_unnamed_module)); - out->print_cr(" - dictionary " INTPTR_FORMAT, p2i(_dictionary)); + if (_dictionary != nullptr) { + out->print (" - dictionary " INTPTR_FORMAT " ", p2i(_dictionary)); + _dictionary->print_size(out); + } else { + out->print_cr(" - dictionary " INTPTR_FORMAT, p2i(_dictionary)); + } if (_jmethod_ids != NULL) { out->print (" - jmethod count "); Method::print_jmethod_ids_count(this, out); diff --git a/src/hotspot/share/classfile/dictionary.cpp b/src/hotspot/share/classfile/dictionary.cpp index 9905eb4fc70a8..ce4f8b6f5c147 100644 --- a/src/hotspot/share/classfile/dictionary.cpp +++ b/src/hotspot/share/classfile/dictionary.cpp @@ -586,13 +586,17 @@ void DictionaryEntry::print_count(outputStream *st) { // ---------------------------------------------------------------------------- +void Dictionary::print_size(outputStream* st) const { + st->print_cr("Java dictionary (table_size=%d, classes=%d, resizable=%s)", + table_size(), number_of_entries(), BOOL_TO_STR(_resizable)); +} + void Dictionary::print_on(outputStream* st) const { ResourceMark rm; assert(loader_data() != NULL, "loader data should not be null"); assert(!loader_data()->has_class_mirror_holder(), "cld should have a ClassLoader holder not a Class holder"); - st->print_cr("Java dictionary (table_size=%d, classes=%d, resizable=%s)", - table_size(), number_of_entries(), BOOL_TO_STR(_resizable)); + print_size(st); st->print_cr("^ indicates that initiating loader is different from defining loader"); for (int index = 0; index < table_size(); index++) { diff --git a/src/hotspot/share/classfile/dictionary.hpp b/src/hotspot/share/classfile/dictionary.hpp index e42b60cf02622..322244e5d17d6 100644 --- a/src/hotspot/share/classfile/dictionary.hpp +++ b/src/hotspot/share/classfile/dictionary.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -79,6 +79,7 @@ class Dictionary : public Hashtable { TRAPS); void print_on(outputStream* st) const; + void print_size(outputStream* st) const; void verify(); private: diff --git a/src/hotspot/share/classfile/placeholders.cpp b/src/hotspot/share/classfile/placeholders.cpp index 88b8d0fcc208d..18707c04b4605 100644 --- a/src/hotspot/share/classfile/placeholders.cpp +++ b/src/hotspot/share/classfile/placeholders.cpp @@ -326,7 +326,12 @@ void PlaceholderTable::find_and_remove(unsigned int hash, PlaceholderEntry *probe = get_entry(hash, name, loader_data); if (probe != NULL) { log(probe, "find_and_remove", action); - probe->remove_seen_thread(thread, action); + + bool empty = probe->remove_seen_thread(thread, action); + if (empty && action == LOAD_SUPER) { + probe->set_supername(NULL); + } + // If no other threads using this entry, and this thread is not using this entry for other states if ((probe->superThreadQ() == NULL) && (probe->loadInstanceThreadQ() == NULL) && (probe->defineThreadQ() == NULL) && (probe->definer() == NULL)) { diff --git a/src/hotspot/share/classfile/placeholders.hpp b/src/hotspot/share/classfile/placeholders.hpp index d85ac9adfdc84..0474632e69070 100644 --- a/src/hotspot/share/classfile/placeholders.hpp +++ b/src/hotspot/share/classfile/placeholders.hpp @@ -142,8 +142,12 @@ class PlaceholderEntry : public HashtableEntry { Symbol* supername() const { return _supername; } void set_supername(Symbol* supername) { - _supername = supername; - if (_supername != NULL) _supername->increment_refcount(); + if (supername != _supername) { + _supername = supername; + if (_supername != NULL) { + _supername->increment_refcount(); + } + } } Thread* definer() const {return _definer; } diff --git a/src/hotspot/share/classfile/stackMapTable.cpp b/src/hotspot/share/classfile/stackMapTable.cpp index 59f1b077cdbf2..5adaa877d8548 100644 --- a/src/hotspot/share/classfile/stackMapTable.cpp +++ b/src/hotspot/share/classfile/stackMapTable.cpp @@ -122,8 +122,16 @@ bool StackMapTable::match_stackmap( } void StackMapTable::check_jump_target( - StackMapFrame* frame, int32_t target, TRAPS) const { + StackMapFrame* frame, int bci, int offset, TRAPS) const { ErrorContext ctx; + // Jump targets must be within the method and the method size is limited. See JVMS 4.11 + int min_offset = -1 * max_method_code_size; + if (offset < min_offset || offset > max_method_code_size) { + frame->verifier()->verify_error(ErrorContext::bad_stackmap(bci, frame), + "Illegal target of jump or branch (bci %d + offset %d)", bci, offset); + return; + } + int target = bci + offset; bool match = match_stackmap( frame, target, true, false, &ctx, CHECK_VERIFY(frame->verifier())); if (!match || (target < 0 || target >= _code_length)) { diff --git a/src/hotspot/share/classfile/stackMapTable.hpp b/src/hotspot/share/classfile/stackMapTable.hpp index c0e33ae176c0e..4dfd14e2b0e7f 100644 --- a/src/hotspot/share/classfile/stackMapTable.hpp +++ b/src/hotspot/share/classfile/stackMapTable.hpp @@ -69,7 +69,7 @@ class StackMapTable : public StackObj { // Check jump instructions. Make sure there are no uninitialized // instances on backward branch. - void check_jump_target(StackMapFrame* frame, int32_t target, TRAPS) const; + void check_jump_target(StackMapFrame* frame, int bci, int offset, TRAPS) const; // The following methods are only used inside this class. diff --git a/src/hotspot/share/classfile/verifier.cpp b/src/hotspot/share/classfile/verifier.cpp index 5ad53e9eca198..bf39b30a2426b 100644 --- a/src/hotspot/share/classfile/verifier.cpp +++ b/src/hotspot/share/classfile/verifier.cpp @@ -792,7 +792,6 @@ void ClassVerifier::verify_method(const methodHandle& m, TRAPS) { // Merge with the next instruction { u2 index; - int target; VerificationType type, type2; VerificationType atype; @@ -1608,9 +1607,8 @@ void ClassVerifier::verify_method(const methodHandle& m, TRAPS) { case Bytecodes::_ifle: current_frame.pop_stack( VerificationType::integer_type(), CHECK_VERIFY(this)); - target = bcs.dest(); stackmap_table.check_jump_target( - ¤t_frame, target, CHECK_VERIFY(this)); + ¤t_frame, bcs.bci(), bcs.get_offset_s2(), CHECK_VERIFY(this)); no_control_flow = false; break; case Bytecodes::_if_acmpeq : case Bytecodes::_if_acmpne : @@ -1621,19 +1619,16 @@ void ClassVerifier::verify_method(const methodHandle& m, TRAPS) { case Bytecodes::_ifnonnull : current_frame.pop_stack( VerificationType::reference_check(), CHECK_VERIFY(this)); - target = bcs.dest(); stackmap_table.check_jump_target - (¤t_frame, target, CHECK_VERIFY(this)); + (¤t_frame, bcs.bci(), bcs.get_offset_s2(), CHECK_VERIFY(this)); no_control_flow = false; break; case Bytecodes::_goto : - target = bcs.dest(); stackmap_table.check_jump_target( - ¤t_frame, target, CHECK_VERIFY(this)); + ¤t_frame, bcs.bci(), bcs.get_offset_s2(), CHECK_VERIFY(this)); no_control_flow = true; break; case Bytecodes::_goto_w : - target = bcs.dest_w(); stackmap_table.check_jump_target( - ¤t_frame, target, CHECK_VERIFY(this)); + ¤t_frame, bcs.bci(), bcs.get_offset_s4(), CHECK_VERIFY(this)); no_control_flow = true; break; case Bytecodes::_tableswitch : case Bytecodes::_lookupswitch : @@ -2283,15 +2278,14 @@ void ClassVerifier::verify_switch( } } } - int target = bci + default_offset; - stackmap_table->check_jump_target(current_frame, target, CHECK_VERIFY(this)); + stackmap_table->check_jump_target(current_frame, bci, default_offset, CHECK_VERIFY(this)); for (int i = 0; i < keys; i++) { // Because check_jump_target() may safepoint, the bytecode could have // moved, which means 'aligned_bcp' is no good and needs to be recalculated. aligned_bcp = align_up(bcs->bcp() + 1, jintSize); - target = bci + (jint)Bytes::get_Java_u4(aligned_bcp+(3+i*delta)*jintSize); + int offset = (jint)Bytes::get_Java_u4(aligned_bcp+(3+i*delta)*jintSize); stackmap_table->check_jump_target( - current_frame, target, CHECK_VERIFY(this)); + current_frame, bci, offset, CHECK_VERIFY(this)); } NOT_PRODUCT(aligned_bcp = NULL); // no longer valid at this point } @@ -2445,207 +2439,6 @@ void ClassVerifier::verify_field_instructions(RawBytecodeStream* bcs, } } -// Look at the method's handlers. If the bci is in the handler's try block -// then check if the handler_pc is already on the stack. If not, push it -// unless the handler has already been scanned. -void ClassVerifier::push_handlers(ExceptionTable* exhandlers, - GrowableArray* handler_list, - GrowableArray* handler_stack, - u4 bci) { - int exlength = exhandlers->length(); - for(int x = 0; x < exlength; x++) { - if (bci >= exhandlers->start_pc(x) && bci < exhandlers->end_pc(x)) { - u4 exhandler_pc = exhandlers->handler_pc(x); - if (!handler_list->contains(exhandler_pc)) { - handler_stack->append_if_missing(exhandler_pc); - handler_list->append(exhandler_pc); - } - } - } -} - -// Return TRUE if all code paths starting with start_bc_offset end in -// bytecode athrow or loop. -bool ClassVerifier::ends_in_athrow(u4 start_bc_offset) { - ResourceMark rm; - // Create bytecode stream. - RawBytecodeStream bcs(method()); - u4 code_length = method()->code_size(); - bcs.set_start(start_bc_offset); - u4 target; - // Create stack for storing bytecode start offsets for if* and *switch. - GrowableArray* bci_stack = new GrowableArray(30); - // Create stack for handlers for try blocks containing this handler. - GrowableArray* handler_stack = new GrowableArray(30); - // Create list of handlers that have been pushed onto the handler_stack - // so that handlers embedded inside of their own TRY blocks only get - // scanned once. - GrowableArray* handler_list = new GrowableArray(30); - // Create list of visited branch opcodes (goto* and if*). - GrowableArray* visited_branches = new GrowableArray(30); - ExceptionTable exhandlers(_method()); - - while (true) { - if (bcs.is_last_bytecode()) { - // if no more starting offsets to parse or if at the end of the - // method then return false. - if ((bci_stack->is_empty()) || ((u4)bcs.end_bci() == code_length)) - return false; - // Pop a bytecode starting offset and scan from there. - bcs.set_start(bci_stack->pop()); - } - Bytecodes::Code opcode = bcs.raw_next(); - u4 bci = bcs.bci(); - - // If the bytecode is in a TRY block, push its handlers so they - // will get parsed. - push_handlers(&exhandlers, handler_list, handler_stack, bci); - - switch (opcode) { - case Bytecodes::_if_icmpeq: - case Bytecodes::_if_icmpne: - case Bytecodes::_if_icmplt: - case Bytecodes::_if_icmpge: - case Bytecodes::_if_icmpgt: - case Bytecodes::_if_icmple: - case Bytecodes::_ifeq: - case Bytecodes::_ifne: - case Bytecodes::_iflt: - case Bytecodes::_ifge: - case Bytecodes::_ifgt: - case Bytecodes::_ifle: - case Bytecodes::_if_acmpeq: - case Bytecodes::_if_acmpne: - case Bytecodes::_ifnull: - case Bytecodes::_ifnonnull: - target = bcs.dest(); - if (visited_branches->contains(bci)) { - if (bci_stack->is_empty()) { - if (handler_stack->is_empty()) { - return true; - } else { - // Parse the catch handlers for try blocks containing athrow. - bcs.set_start(handler_stack->pop()); - } - } else { - // Pop a bytecode starting offset and scan from there. - bcs.set_start(bci_stack->pop()); - } - } else { - if (target > bci) { // forward branch - if (target >= code_length) return false; - // Push the branch target onto the stack. - bci_stack->push(target); - // then, scan bytecodes starting with next. - bcs.set_start(bcs.next_bci()); - } else { // backward branch - // Push bytecode offset following backward branch onto the stack. - bci_stack->push(bcs.next_bci()); - // Check bytecodes starting with branch target. - bcs.set_start(target); - } - // Record target so we don't branch here again. - visited_branches->append(bci); - } - break; - - case Bytecodes::_goto: - case Bytecodes::_goto_w: - target = (opcode == Bytecodes::_goto ? bcs.dest() : bcs.dest_w()); - if (visited_branches->contains(bci)) { - if (bci_stack->is_empty()) { - if (handler_stack->is_empty()) { - return true; - } else { - // Parse the catch handlers for try blocks containing athrow. - bcs.set_start(handler_stack->pop()); - } - } else { - // Been here before, pop new starting offset from stack. - bcs.set_start(bci_stack->pop()); - } - } else { - if (target >= code_length) return false; - // Continue scanning from the target onward. - bcs.set_start(target); - // Record target so we don't branch here again. - visited_branches->append(bci); - } - break; - - // Check that all switch alternatives end in 'athrow' bytecodes. Since it - // is difficult to determine where each switch alternative ends, parse - // each switch alternative until either hit a 'return', 'athrow', or reach - // the end of the method's bytecodes. This is gross but should be okay - // because: - // 1. tableswitch and lookupswitch byte codes in handlers for ctor explicit - // constructor invocations should be rare. - // 2. if each switch alternative ends in an athrow then the parsing should be - // short. If there is no athrow then it is bogus code, anyway. - case Bytecodes::_lookupswitch: - case Bytecodes::_tableswitch: - { - address aligned_bcp = align_up(bcs.bcp() + 1, jintSize); - u4 default_offset = Bytes::get_Java_u4(aligned_bcp) + bci; - int keys, delta; - if (opcode == Bytecodes::_tableswitch) { - jint low = (jint)Bytes::get_Java_u4(aligned_bcp + jintSize); - jint high = (jint)Bytes::get_Java_u4(aligned_bcp + 2*jintSize); - // This is invalid, but let the regular bytecode verifier - // report this because the user will get a better error message. - if (low > high) return true; - keys = high - low + 1; - delta = 1; - } else { - keys = (int)Bytes::get_Java_u4(aligned_bcp + jintSize); - delta = 2; - } - // Invalid, let the regular bytecode verifier deal with it. - if (keys < 0) return true; - - // Push the offset of the next bytecode onto the stack. - bci_stack->push(bcs.next_bci()); - - // Push the switch alternatives onto the stack. - for (int i = 0; i < keys; i++) { - u4 target = bci + (jint)Bytes::get_Java_u4(aligned_bcp+(3+i*delta)*jintSize); - if (target > code_length) return false; - bci_stack->push(target); - } - - // Start bytecode parsing for the switch at the default alternative. - if (default_offset > code_length) return false; - bcs.set_start(default_offset); - break; - } - - case Bytecodes::_return: - return false; - - case Bytecodes::_athrow: - { - if (bci_stack->is_empty()) { - if (handler_stack->is_empty()) { - return true; - } else { - // Parse the catch handlers for try blocks containing athrow. - bcs.set_start(handler_stack->pop()); - } - } else { - // Pop a bytecode offset and starting scanning from there. - bcs.set_start(bci_stack->pop()); - } - } - break; - - default: - ; - } // end switch - } // end while loop - - return false; -} - void ClassVerifier::verify_invoke_init( RawBytecodeStream* bcs, u2 ref_class_index, VerificationType ref_class_type, StackMapFrame* current_frame, u4 code_length, bool in_try_block, @@ -2670,25 +2463,6 @@ void ClassVerifier::verify_invoke_init( // sure that all catch clause paths end in a throw. Otherwise, this can // result in returning an incomplete object. if (in_try_block) { - ExceptionTable exhandlers(_method()); - int exlength = exhandlers.length(); - for(int i = 0; i < exlength; i++) { - u2 start_pc = exhandlers.start_pc(i); - u2 end_pc = exhandlers.end_pc(i); - - if (bci >= start_pc && bci < end_pc) { - if (!ends_in_athrow(exhandlers.handler_pc(i))) { - verify_error(ErrorContext::bad_code(bci), - "Bad method call from after the start of a try block"); - return; - } else if (log_is_enabled(Debug, verification)) { - ResourceMark rm(THREAD); - log_debug(verification)("Survived call to ends_in_athrow(): %s", - current_class()->name()->as_C_string()); - } - } - } - // Check the exception handler target stackmaps with the locals from the // incoming stackmap (before initialize_object() changes them to outgoing // state). diff --git a/src/hotspot/share/classfile/verifier.hpp b/src/hotspot/share/classfile/verifier.hpp index 999391ae696ff..b02da700df03f 100644 --- a/src/hotspot/share/classfile/verifier.hpp +++ b/src/hotspot/share/classfile/verifier.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -336,17 +336,6 @@ class ClassVerifier : public StackObj { bool* this_uninit, const constantPoolHandle& cp, StackMapTable* stackmap_table, TRAPS); - // Used by ends_in_athrow() to push all handlers that contain bci onto the - // handler_stack, if the handler has not already been pushed on the stack. - void push_handlers(ExceptionTable* exhandlers, - GrowableArray* handler_list, - GrowableArray* handler_stack, - u4 bci); - - // Returns true if all paths starting with start_bc_offset end in athrow - // bytecode or loop. - bool ends_in_athrow(u4 start_bc_offset); - void verify_invoke_instructions( RawBytecodeStream* bcs, u4 code_length, StackMapFrame* current_frame, bool in_try_block, bool* this_uninit, VerificationType return_type, diff --git a/src/hotspot/share/code/codeBlob.cpp b/src/hotspot/share/code/codeBlob.cpp index 41a582f43ec1a..57990cdc53253 100644 --- a/src/hotspot/share/code/codeBlob.cpp +++ b/src/hotspot/share/code/codeBlob.cpp @@ -94,7 +94,6 @@ CodeBlob::CodeBlob(const char* name, CompilerType type, const CodeBlobLayout& la _oop_maps(oop_maps), _caller_must_gc_arguments(caller_must_gc_arguments), _name(name) - NOT_PRODUCT(COMMA _strings(CodeStrings())) { assert(is_aligned(layout.size(), oopSize), "unaligned size"); assert(is_aligned(layout.header_size(), oopSize), "unaligned size"); @@ -107,7 +106,7 @@ CodeBlob::CodeBlob(const char* name, CompilerType type, const CodeBlobLayout& la S390_ONLY(_ctable_offset = 0;) // avoid uninitialized fields } -CodeBlob::CodeBlob(const char* name, CompilerType type, const CodeBlobLayout& layout, CodeBuffer* cb, int frame_complete_offset, int frame_size, OopMapSet* oop_maps, bool caller_must_gc_arguments) : +CodeBlob::CodeBlob(const char* name, CompilerType type, const CodeBlobLayout& layout, CodeBuffer* cb /*UNUSED*/, int frame_complete_offset, int frame_size, OopMapSet* oop_maps, bool caller_must_gc_arguments) : _type(type), _size(layout.size()), _header_size(layout.header_size()), @@ -122,7 +121,6 @@ CodeBlob::CodeBlob(const char* name, CompilerType type, const CodeBlobLayout& la _relocation_end(layout.relocation_end()), _caller_must_gc_arguments(caller_must_gc_arguments), _name(name) - NOT_PRODUCT(COMMA _strings(CodeStrings())) { assert(is_aligned(_size, oopSize), "unaligned size"); assert(is_aligned(_header_size, oopSize), "unaligned size"); @@ -164,7 +162,8 @@ RuntimeBlob::RuntimeBlob( void CodeBlob::flush() { FREE_C_HEAP_ARRAY(unsigned char, _oop_maps); _oop_maps = NULL; - NOT_PRODUCT(_strings.free();) + NOT_PRODUCT(_asm_remarks.clear()); + NOT_PRODUCT(_dbg_strings.clear()); } void CodeBlob::set_oop_maps(OopMapSet* p) { @@ -191,7 +190,8 @@ void RuntimeBlob::trace_new_stub(RuntimeBlob* stub, const char* name1, const cha tty->print_cr("- - - [BEGIN] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); tty->print_cr("Decoding %s " PTR_FORMAT " [" PTR_FORMAT ", " PTR_FORMAT "] (%d bytes)", stub_id, p2i(stub), p2i(stub->code_begin()), p2i(stub->code_end()), stub->code_size()); - Disassembler::decode(stub->code_begin(), stub->code_end(), tty); + Disassembler::decode(stub->code_begin(), stub->code_end(), tty + NOT_PRODUCT(COMMA &stub->asm_remarks())); if ((stub->oop_maps() != NULL) && AbstractDisassembler::show_structs()) { tty->print_cr("- - - [OOP MAPS]- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); stub->oop_maps()->print(); diff --git a/src/hotspot/share/code/codeBlob.hpp b/src/hotspot/share/code/codeBlob.hpp index 104e04fba5339..3994fbfd42c74 100644 --- a/src/hotspot/share/code/codeBlob.hpp +++ b/src/hotspot/share/code/codeBlob.hpp @@ -112,15 +112,22 @@ class CodeBlob { const char* _name; S390_ONLY(int _ctable_offset;) - NOT_PRODUCT(CodeStrings _strings;) +#ifndef PRODUCT + AsmRemarks _asm_remarks; + DbgStrings _dbg_strings; + + ~CodeBlob() { + _asm_remarks.clear(); + _dbg_strings.clear(); + } +#endif // not PRODUCT CodeBlob(const char* name, CompilerType type, const CodeBlobLayout& layout, int frame_complete_offset, int frame_size, ImmutableOopMapSet* oop_maps, bool caller_must_gc_arguments); CodeBlob(const char* name, CompilerType type, const CodeBlobLayout& layout, CodeBuffer* cb, int frame_complete_offset, int frame_size, OopMapSet* oop_maps, bool caller_must_gc_arguments); public: // Only used by unit test. - CodeBlob() - : _type(compiler_none) {} + CodeBlob() : _type(compiler_none) {} // Returns the space needed for CodeBlob static unsigned int allocation_size(CodeBuffer* cb, int header_size); @@ -232,18 +239,21 @@ class CodeBlob { void dump_for_addr(address addr, outputStream* st, bool verbose) const; void print_code(); - // Print the comment associated with offset on stream, if there is one + // Print to stream, any comments associated with offset. virtual void print_block_comment(outputStream* stream, address block_begin) const { - #ifndef PRODUCT - intptr_t offset = (intptr_t)(block_begin - code_begin()); - _strings.print_block_comment(stream, offset); - #endif +#ifndef PRODUCT + ptrdiff_t offset = block_begin - code_begin(); + assert(offset >= 0, "Expecting non-negative offset!"); + _asm_remarks.print(uint(offset), stream); +#endif } #ifndef PRODUCT - void set_strings(CodeStrings& strings) { - _strings.copy(strings); - } + AsmRemarks &asm_remarks() { return _asm_remarks; } + DbgStrings &dbg_strings() { return _dbg_strings; } + + void use_remarks(AsmRemarks &remarks) { _asm_remarks.share(remarks); } + void use_strings(DbgStrings &strings) { _dbg_strings.share(strings); } #endif }; diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp index f4753fd6cfffa..0c53a0a2ec001 100644 --- a/src/hotspot/share/code/codeCache.cpp +++ b/src/hotspot/share/code/codeCache.cpp @@ -653,11 +653,6 @@ bool CodeCache::contains(nmethod *nm) { return contains((void *)nm); } -static bool is_in_asgct() { - Thread* current_thread = Thread::current_or_null_safe(); - return current_thread != NULL && current_thread->is_Java_thread() && current_thread->as_Java_thread()->in_asgct(); -} - // This method is safe to call without holding the CodeCache_lock, as long as a dead CodeBlob is not // looked up (i.e., one that has been marked for deletion). It only depends on the _segmap to contain // valid indices, which it will always do, as long as the CodeBlob is not in the process of being recycled. @@ -666,7 +661,7 @@ CodeBlob* CodeCache::find_blob(void* start) { // We could potentially look up non_entrant methods bool is_zombie = result != NULL && result->is_zombie(); bool is_result_safe = !is_zombie || result->is_locked_by_vm() || VMError::is_error_reported(); - guarantee(is_result_safe || is_in_asgct(), "unsafe access to zombie method"); + guarantee(is_result_safe || Thread::current_in_asgct(), "unsafe access to zombie method"); // When in ASGCT the previous gurantee will pass for a zombie method but we still don't want that code blob returned in order // to minimize the chance of accessing dead memory return is_result_safe ? result : NULL; diff --git a/src/hotspot/share/code/icBuffer.hpp b/src/hotspot/share/code/icBuffer.hpp index eb45e043bf19e..cda634b42fe95 100644 --- a/src/hotspot/share/code/icBuffer.hpp +++ b/src/hotspot/share/code/icBuffer.hpp @@ -56,8 +56,7 @@ class ICStub: public Stub { protected: friend class ICStubInterface; // This will be called only by ICStubInterface - void initialize(int size, - CodeStrings strings) { _size = size; _ic_site = NULL; } + void initialize(int size) { _size = size; _ic_site = NULL; } void finalize(); // called when a method is removed // General info diff --git a/src/hotspot/share/code/nmethod.hpp b/src/hotspot/share/code/nmethod.hpp index ea5e92845fff3..21f20b44d2469 100644 --- a/src/hotspot/share/code/nmethod.hpp +++ b/src/hotspot/share/code/nmethod.hpp @@ -256,7 +256,7 @@ class nmethod : public CompiledMethod { // stack. An not_entrant method can be removed when there are no // more activations, i.e., when the _stack_traversal_mark is less than // current sweep traversal index. - volatile long _stack_traversal_mark; + volatile int64_t _stack_traversal_mark; // The _hotness_counter indicates the hotness of a method. The higher // the value the hotter the method. The hotness counter of a nmethod is @@ -546,8 +546,8 @@ class nmethod : public CompiledMethod { void fix_oop_relocations() { fix_oop_relocations(NULL, NULL, false); } // Sweeper support - long stack_traversal_mark() { return _stack_traversal_mark; } - void set_stack_traversal_mark(long l) { _stack_traversal_mark = l; } + int64_t stack_traversal_mark() { return _stack_traversal_mark; } + void set_stack_traversal_mark(int64_t l) { _stack_traversal_mark = l; } // On-stack replacement support int osr_entry_bci() const { assert(is_osr_method(), "wrong kind of nmethod"); return _entry_bci; } diff --git a/src/hotspot/share/code/relocInfo.cpp b/src/hotspot/share/code/relocInfo.cpp index 47769c53a5b70..986f40c9f2f8c 100644 --- a/src/hotspot/share/code/relocInfo.cpp +++ b/src/hotspot/share/code/relocInfo.cpp @@ -359,6 +359,14 @@ void CallRelocation::fix_relocation_after_move(const CodeBuffer* src, CodeBuffer } +#ifdef USE_TRAMPOLINE_STUB_FIX_OWNER +void trampoline_stub_Relocation::fix_relocation_after_move(const CodeBuffer* src, CodeBuffer* dest) { + // Finalize owner destination only for nmethods + if (dest->blob() != nullptr) return; + pd_fix_owner_after_move(); +} +#endif + //// pack/unpack methods void oop_Relocation::pack_data_to(CodeSection* dest) { diff --git a/src/hotspot/share/code/relocInfo.hpp b/src/hotspot/share/code/relocInfo.hpp index 55d4ac7c62dbb..2c6b570e01be0 100644 --- a/src/hotspot/share/code/relocInfo.hpp +++ b/src/hotspot/share/code/relocInfo.hpp @@ -1183,6 +1183,11 @@ class runtime_call_w_cp_Relocation : public CallRelocation { // in the code, it can patch it to jump to the trampoline where is // sufficient space for a far branch. Needed on PPC. class trampoline_stub_Relocation : public Relocation { +#ifdef USE_TRAMPOLINE_STUB_FIX_OWNER + void pd_fix_owner_after_move(); + void fix_relocation_after_move(const CodeBuffer* src, CodeBuffer* dest) override; +#endif + public: static RelocationHolder spec(address static_call) { RelocationHolder rh = newHolder(); @@ -1204,8 +1209,8 @@ class trampoline_stub_Relocation : public Relocation { // Return the address of the NativeCall that owns the trampoline. address owner() { return _owner; } - void pack_data_to(CodeSection * dest); - void unpack_data(); + void pack_data_to(CodeSection * dest) override; + void unpack_data() override; // Find the trampoline stub for a call. static address get_trampoline_for(address call, nmethod* code); diff --git a/src/hotspot/share/code/stubs.cpp b/src/hotspot/share/code/stubs.cpp index 2c48ae424a355..d941f9c2073b4 100644 --- a/src/hotspot/share/code/stubs.cpp +++ b/src/hotspot/share/code/stubs.cpp @@ -109,8 +109,7 @@ Stub* StubQueue::stub_containing(address pc) const { Stub* StubQueue::request_committed(int code_size) { Stub* s = request(code_size); - CodeStrings strings; - if (s != NULL) commit(code_size, strings); + if (s != NULL) commit(code_size); return s; } @@ -127,8 +126,7 @@ Stub* StubQueue::request(int requested_code_size) { assert(_buffer_limit == _buffer_size, "buffer must be fully usable"); if (_queue_end + requested_size <= _buffer_size) { // code fits in at the end => nothing to do - CodeStrings strings; - stub_initialize(s, requested_size, strings); + stub_initialize(s, requested_size); return s; } else { // stub doesn't fit in at the queue end @@ -145,8 +143,7 @@ Stub* StubQueue::request(int requested_code_size) { // Queue: |XXX|.......|XXXXXXX|.......| // ^0 ^end ^begin ^limit ^size s = current_stub(); - CodeStrings strings; - stub_initialize(s, requested_size, strings); + stub_initialize(s, requested_size); return s; } // Not enough space left @@ -155,12 +152,12 @@ Stub* StubQueue::request(int requested_code_size) { } -void StubQueue::commit(int committed_code_size, CodeStrings& strings) { +void StubQueue::commit(int committed_code_size) { assert(committed_code_size > 0, "committed_code_size must be > 0"); int committed_size = align_up(stub_code_size_to_size(committed_code_size), CodeEntryAlignment); Stub* s = current_stub(); assert(committed_size <= stub_size(s), "committed size must not exceed requested size"); - stub_initialize(s, committed_size, strings); + stub_initialize(s, committed_size); _queue_end += committed_size; _number_of_stubs++; if (_mutex != NULL) _mutex->unlock(); diff --git a/src/hotspot/share/code/stubs.hpp b/src/hotspot/share/code/stubs.hpp index 60ca20679cc60..411695ef0ef0e 100644 --- a/src/hotspot/share/code/stubs.hpp +++ b/src/hotspot/share/code/stubs.hpp @@ -60,8 +60,7 @@ class Stub { public: // Initialization/finalization - void initialize(int size, - CodeStrings& strings) { ShouldNotCallThis(); } // called to initialize/specify the stub's size + void initialize(int size) { ShouldNotCallThis(); } // called to initialize/specify the stub's size void finalize() { ShouldNotCallThis(); } // called before the stub is deallocated // General info/converters @@ -94,8 +93,7 @@ class Stub { class StubInterface: public CHeapObj { public: // Initialization/finalization - virtual void initialize(Stub* self, int size, - CodeStrings& strings) = 0; // called after creation (called twice if allocated via (request, commit)) + virtual void initialize(Stub* self, int size) = 0; // called after creation (called twice if allocated via (request, commit)) virtual void finalize(Stub* self) = 0; // called before deallocation // General info/converters @@ -123,8 +121,7 @@ class StubInterface: public CHeapObj { \ public: \ /* Initialization/finalization */ \ - virtual void initialize(Stub* self, int size, \ - CodeStrings& strings) { cast(self)->initialize(size, strings); } \ + virtual void initialize(Stub* self, int size) { cast(self)->initialize(size); } \ virtual void finalize(Stub* self) { cast(self)->finalize(); } \ \ /* General info */ \ @@ -163,8 +160,7 @@ class StubQueue: public CHeapObj { Stub* current_stub() const { return stub_at(_queue_end); } // Stub functionality accessed via interface - void stub_initialize(Stub* s, int size, - CodeStrings& strings) { assert(size % CodeEntryAlignment == 0, "size not aligned"); _stub_interface->initialize(s, size, strings); } + void stub_initialize(Stub* s, int size) { assert(size % CodeEntryAlignment == 0, "size not aligned"); _stub_interface->initialize(s, size); } void stub_finalize(Stub* s) { _stub_interface->finalize(s); } int stub_size(Stub* s) const { return _stub_interface->size(s); } bool stub_contains(Stub* s, address pc) const { return _stub_interface->code_begin(s) <= pc && pc < _stub_interface->code_end(s); } @@ -191,8 +187,7 @@ class StubQueue: public CHeapObj { // Stub allocation (atomic transactions) Stub* request_committed(int code_size); // request a stub that provides exactly code_size space for code Stub* request(int requested_code_size); // request a stub with a (maximum) code space - locks the queue - void commit (int committed_code_size, - CodeStrings& strings); // commit the previously requested stub - unlocks the queue + void commit (int committed_code_size); // commit the previously requested stub - unlocks the queue // Stub deallocation void remove_first(); // remove the first stub in the queue diff --git a/src/hotspot/share/compiler/compileBroker.cpp b/src/hotspot/share/compiler/compileBroker.cpp index f54308944e281..1d7c6c6b55434 100644 --- a/src/hotspot/share/compiler/compileBroker.cpp +++ b/src/hotspot/share/compiler/compileBroker.cpp @@ -185,7 +185,7 @@ int CompileBroker::_sum_standard_bytes_compiled = 0; int CompileBroker::_sum_nmethod_size = 0; int CompileBroker::_sum_nmethod_code_size = 0; -long CompileBroker::_peak_compilation_time = 0; +jlong CompileBroker::_peak_compilation_time = 0; CompilerStatistics CompileBroker::_stats_per_level[CompLevel_full_optimization]; @@ -1069,18 +1069,34 @@ void CompileBroker::init_compiler_sweeper_threads() { void CompileBroker::possibly_add_compiler_threads(JavaThread* THREAD) { + int old_c2_count = 0, new_c2_count = 0, old_c1_count = 0, new_c1_count = 0; + const int c2_tasks_per_thread = 2, c1_tasks_per_thread = 4; + + // Quick check if we already have enough compiler threads without taking the lock. + // Numbers may change concurrently, so we read them again after we have the lock. + if (_c2_compile_queue != nullptr) { + old_c2_count = get_c2_thread_count(); + new_c2_count = MIN2(_c2_count, _c2_compile_queue->size() / c2_tasks_per_thread); + } + if (_c1_compile_queue != nullptr) { + old_c1_count = get_c1_thread_count(); + new_c1_count = MIN2(_c1_count, _c1_compile_queue->size() / c1_tasks_per_thread); + } + if (new_c2_count <= old_c2_count && new_c1_count <= old_c1_count) return; + + // Now, we do the more expensive operations. julong available_memory = os::available_memory(); // If SegmentedCodeCache is off, both values refer to the single heap (with type CodeBlobType::All). - size_t available_cc_np = CodeCache::unallocated_capacity(CodeBlobType::MethodNonProfiled), - available_cc_p = CodeCache::unallocated_capacity(CodeBlobType::MethodProfiled); + size_t available_cc_np = CodeCache::unallocated_capacity(CodeBlobType::MethodNonProfiled), + available_cc_p = CodeCache::unallocated_capacity(CodeBlobType::MethodProfiled); - // Only do attempt to start additional threads if the lock is free. + // Only attempt to start additional threads if the lock is free. if (!CompileThread_lock->try_lock()) return; if (_c2_compile_queue != NULL) { - int old_c2_count = _compilers[1]->num_compiler_threads(); - int new_c2_count = MIN4(_c2_count, - _c2_compile_queue->size() / 2, + old_c2_count = get_c2_thread_count(); + new_c2_count = MIN4(_c2_count, + _c2_compile_queue->size() / c2_tasks_per_thread, (int)(available_memory / (200*M)), (int)(available_cc_np / (128*K))); @@ -1114,7 +1130,7 @@ void CompileBroker::possibly_add_compiler_threads(JavaThread* THREAD) { break; } // Check if another thread has beaten us during the Java calls. - if (_compilers[1]->num_compiler_threads() != i) break; + if (get_c2_thread_count() != i) break; jobject thread_handle = JNIHandles::make_global(thread_oop); assert(compiler2_object(i) == NULL, "Old one must be released!"); _compiler2_objects[i] = thread_handle; @@ -1136,9 +1152,9 @@ void CompileBroker::possibly_add_compiler_threads(JavaThread* THREAD) { } if (_c1_compile_queue != NULL) { - int old_c1_count = _compilers[0]->num_compiler_threads(); - int new_c1_count = MIN4(_c1_count, - _c1_compile_queue->size() / 4, + old_c1_count = get_c1_thread_count(); + new_c1_count = MIN4(_c1_count, + _c1_compile_queue->size() / c1_tasks_per_thread, (int)(available_memory / (100*M)), (int)(available_cc_p / (128*K))); diff --git a/src/hotspot/share/compiler/compileBroker.hpp b/src/hotspot/share/compiler/compileBroker.hpp index 0b721f83121f4..91e48217cf132 100644 --- a/src/hotspot/share/compiler/compileBroker.hpp +++ b/src/hotspot/share/compiler/compileBroker.hpp @@ -88,7 +88,7 @@ class CompileQueue : public CHeapObj { CompileTask* _first_stale; - int _size; + volatile int _size; void purge_stale_tasks(); public: @@ -223,7 +223,7 @@ class CompileBroker: AllStatic { static int _sum_standard_bytes_compiled; static int _sum_nmethod_size; static int _sum_nmethod_code_size; - static long _peak_compilation_time; + static jlong _peak_compilation_time; static CompilerStatistics _stats_per_level[]; @@ -402,6 +402,8 @@ class CompileBroker: AllStatic { static CompileLog* get_log(CompilerThread* ct); + static int get_c1_thread_count() { return _compilers[0]->num_compiler_threads(); } + static int get_c2_thread_count() { return _compilers[1]->num_compiler_threads(); } static int get_total_compile_count() { return _total_compile_count; } static int get_total_bailout_count() { return _total_bailout_count; } static int get_total_invalidated_count() { return _total_invalidated_count; } @@ -414,8 +416,8 @@ class CompileBroker: AllStatic { static int get_sum_standard_bytes_compiled() { return _sum_standard_bytes_compiled; } static int get_sum_nmethod_size() { return _sum_nmethod_size;} static int get_sum_nmethod_code_size() { return _sum_nmethod_code_size; } - static long get_peak_compilation_time() { return _peak_compilation_time; } - static long get_total_compilation_time() { return _t_total_compilation.milliseconds(); } + static jlong get_peak_compilation_time() { return _peak_compilation_time; } + static jlong get_total_compilation_time() { return _t_total_compilation.milliseconds(); } // Log that compilation profiling is skipped because metaspace is full. static void log_metaspace_failure(); diff --git a/src/hotspot/share/compiler/compileTask.cpp b/src/hotspot/share/compiler/compileTask.cpp index d610d8bdcf814..689e529fa93dd 100644 --- a/src/hotspot/share/compiler/compileTask.cpp +++ b/src/hotspot/share/compiler/compileTask.cpp @@ -240,13 +240,13 @@ void CompileTask::print_impl(outputStream* st, Method* method, int compile_id, i jlong time_queued, jlong time_started) { if (!short_form) { // Print current time - st->print("%7d ", (int)tty->time_stamp().milliseconds()); + st->print(UINT64_FORMAT " ", (uint64_t) tty->time_stamp().milliseconds()); if (Verbose && time_queued != 0) { // Print time in queue and time being processed by compiler thread jlong now = os::elapsed_counter(); - st->print("%d ", (int)TimeHelper::counter_to_millis(now-time_queued)); + st->print("%.0f ", TimeHelper::counter_to_millis(now-time_queued)); if (time_started != 0) { - st->print("%d ", (int)TimeHelper::counter_to_millis(now-time_started)); + st->print("%.0f ", TimeHelper::counter_to_millis(now-time_started)); } } } diff --git a/src/hotspot/share/compiler/disassembler.cpp b/src/hotspot/share/compiler/disassembler.cpp index 4cc2f8781c336..c7ff3877ba87e 100644 --- a/src/hotspot/share/compiler/disassembler.cpp +++ b/src/hotspot/share/compiler/disassembler.cpp @@ -72,7 +72,10 @@ class decode_env { bool _print_help; bool _helpPrinted; static bool _optionsParsed; - NOT_PRODUCT(const CodeStrings* _strings;) +#ifndef PRODUCT + const AsmRemarks* _remarks; // Used with start/end range to provide code remarks. + ptrdiff_t _disp; // Adjustment to offset -> remark mapping. +#endif enum { tabspacing = 8 @@ -214,7 +217,8 @@ class decode_env { decode_env(nmethod* code, outputStream* output); // Constructor for a 'decode_env' to decode an arbitrary // piece of memory, hopefully containing code. - decode_env(address start, address end, outputStream* output, const CodeStrings* strings = NULL); + decode_env(address start, address end, outputStream* output + NOT_PRODUCT(COMMA const AsmRemarks* remarks = NULL COMMA ptrdiff_t disp = 0)); // Add 'original_start' argument which is the the original address // the instructions were located at (if this is not equal to 'start'). @@ -332,11 +336,11 @@ decode_env::decode_env(CodeBlob* code, outputStream* output) : _print_file_name(false), _print_help(false), _helpPrinted(false) - NOT_PRODUCT(COMMA _strings(NULL)) { - + NOT_PRODUCT(COMMA _remarks(nullptr)) + NOT_PRODUCT(COMMA _disp(0)) +{ memset(_option_buf, 0, sizeof(_option_buf)); process_options(_output); - } decode_env::decode_env(nmethod* code, outputStream* output) : @@ -354,15 +358,17 @@ decode_env::decode_env(nmethod* code, outputStream* output) : _print_file_name(false), _print_help(false), _helpPrinted(false) - NOT_PRODUCT(COMMA _strings(NULL)) { - + NOT_PRODUCT(COMMA _remarks(nullptr)) + NOT_PRODUCT(COMMA _disp(0)) +{ memset(_option_buf, 0, sizeof(_option_buf)); process_options(_output); } // Constructor for a 'decode_env' to decode a memory range [start, end) // of unknown origin, assuming it contains code. -decode_env::decode_env(address start, address end, outputStream* output, const CodeStrings* c) : +decode_env::decode_env(address start, address end, outputStream* output + NOT_PRODUCT(COMMA const AsmRemarks* remarks COMMA ptrdiff_t disp)) : _output(output ? output : tty), _codeBlob(NULL), _nm(NULL), @@ -377,8 +383,9 @@ decode_env::decode_env(address start, address end, outputStream* output, const C _print_file_name(false), _print_help(false), _helpPrinted(false) - NOT_PRODUCT(COMMA _strings(c)) { - + NOT_PRODUCT(COMMA _remarks(remarks)) + NOT_PRODUCT(COMMA _disp(disp)) +{ assert(start < end, "Range must have a positive size, [" PTR_FORMAT ".." PTR_FORMAT ").", p2i(start), p2i(end)); memset(_option_buf, 0, sizeof(_option_buf)); process_options(_output); @@ -645,15 +652,15 @@ void decode_env::print_insn_labels() { //---< Block comments for nmethod >--- // Outputs a bol() before and a cr() after, but only if a comment is printed. // Prints nmethod_section_label as well. - if (_nm != NULL) { + if (_nm != nullptr) { _nm->print_block_comment(st, p); } - if (_codeBlob != NULL) { + else if (_codeBlob != nullptr) { _codeBlob->print_block_comment(st, p); } #ifndef PRODUCT - if (_strings != NULL) { - _strings->print_block_comment(st, (intptr_t)(p - _start)); + else if (_remarks != nullptr) { + _remarks->print((p - _start) + _disp, st); } #endif } @@ -930,7 +937,8 @@ void Disassembler::decode(nmethod* nm, outputStream* st) { } // Decode a range, given as [start address, end address) -void Disassembler::decode(address start, address end, outputStream* st, const CodeStrings* c) { +void Disassembler::decode(address start, address end, outputStream* st + NOT_PRODUCT(COMMA const AsmRemarks* remarks COMMA ptrdiff_t disp)) { #if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) //---< Test memory before decoding >--- if (!os::is_readable_range(start, end)) { @@ -943,22 +951,9 @@ void Disassembler::decode(address start, address end, outputStream* st, const Co if (is_abstract()) { AbstractDisassembler::decode_abstract(start, end, st, Assembler::instr_maxlen()); - return; - } - -// Don't do that fancy stuff. If we just have two addresses, live with it -// and treat the memory contents as "amorphic" piece of code. -#if 0 - CodeBlob* cb = CodeCache::find_blob_unsafe(start); - if (cb != NULL) { - // If we have an CodeBlob at hand, - // call the specialized decoder directly. - decode(cb, st, c); - } else -#endif - { + } else { // This seems to be just a chunk of memory. - decode_env env(start, end, st, c); + decode_env env(start, end, st NOT_PRODUCT(COMMA remarks COMMA disp)); env.output()->print_cr("--------------------------------------------------------------------------------"); env.decode_instructions(start, end); env.output()->print_cr("--------------------------------------------------------------------------------"); diff --git a/src/hotspot/share/compiler/disassembler.hpp b/src/hotspot/share/compiler/disassembler.hpp index 93228a565b4bf..57fc376b21853 100644 --- a/src/hotspot/share/compiler/disassembler.hpp +++ b/src/hotspot/share/compiler/disassembler.hpp @@ -99,11 +99,12 @@ class Disassembler : public AbstractDisassembler { } // Directly disassemble code blob. - static void decode(CodeBlob *cb, outputStream* st = NULL); + static void decode(CodeBlob* cb, outputStream* st = NULL); // Directly disassemble nmethod. static void decode(nmethod* nm, outputStream* st = NULL); // Disassemble an arbitrary memory range. - static void decode(address start, address end, outputStream* st = NULL, const CodeStrings* = NULL); + static void decode(address start, address end, outputStream* st = NULL + NOT_PRODUCT(COMMA const AsmRemarks* remarks = NULL COMMA ptrdiff_t disp = 0)); static void _hook(const char* file, int line, class MacroAssembler* masm); diff --git a/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp b/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp index f6fd08bdcc744..b8954a97df777 100644 --- a/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp +++ b/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp @@ -340,7 +340,7 @@ void G1GCPhaseTimes::trace_phase(WorkerDataArray* phase, bool print_sum, } } -#define TIME_FORMAT "%.1lfms" +#define TIME_FORMAT "%.2lfms" void G1GCPhaseTimes::info_time(const char* name, double value) const { log_info(gc, phases)(" %s: " TIME_FORMAT, name, value); diff --git a/src/hotspot/share/gc/parallel/psCompactionManager.inline.hpp b/src/hotspot/share/gc/parallel/psCompactionManager.inline.hpp index e40e3689da291..1b1e396bef0f6 100644 --- a/src/hotspot/share/gc/parallel/psCompactionManager.inline.hpp +++ b/src/hotspot/share/gc/parallel/psCompactionManager.inline.hpp @@ -168,10 +168,12 @@ inline void ParCompactionManager::follow_class_loader(ClassLoaderData* cld) { inline void ParCompactionManager::follow_contents(oop obj) { assert(PSParallelCompact::mark_bitmap()->is_marked(obj), "should be marked"); + PCIterateMarkAndPushClosure cl(this, PSParallelCompact::ref_processor()); + if (obj->is_objArray()) { + cl.do_klass(obj->klass()); follow_array(objArrayOop(obj), 0); } else { - PCIterateMarkAndPushClosure cl(this, PSParallelCompact::ref_processor()); obj->oop_iterate(&cl); } } diff --git a/src/hotspot/share/gc/shared/locationPrinter.inline.hpp b/src/hotspot/share/gc/shared/locationPrinter.inline.hpp index f57a030deb764..72bb7b53e504f 100644 --- a/src/hotspot/share/gc/shared/locationPrinter.inline.hpp +++ b/src/hotspot/share/gc/shared/locationPrinter.inline.hpp @@ -27,6 +27,7 @@ #include "gc/shared/locationPrinter.hpp" +#include "memory/resourceArea.inline.hpp" #include "oops/compressedOops.inline.hpp" #include "oops/oopsHierarchy.hpp" @@ -51,6 +52,7 @@ oop BlockLocationPrinter::base_oop_or_null(void* addr) { template bool BlockLocationPrinter::print_location(outputStream* st, void* addr) { + ResourceMark rm; // Check if addr points into Java heap. if (CollectedHeapT::heap()->is_in(addr)) { oop o = base_oop_or_null(addr); diff --git a/src/hotspot/share/gc/shared/weakProcessorTimes.cpp b/src/hotspot/share/gc/shared/weakProcessorTimes.cpp index ee9db87f2e6b0..ec979e328991a 100644 --- a/src/hotspot/share/gc/shared/weakProcessorTimes.cpp +++ b/src/hotspot/share/gc/shared/weakProcessorTimes.cpp @@ -173,7 +173,7 @@ static const char* indent_str(size_t i) { return indents[MIN2(i, max_indents_index)]; } -#define TIME_FORMAT "%.1lfms" +#define TIME_FORMAT "%.2lfms" void WeakProcessorTimes::log_summary(OopStorageSet::WeakId id, uint indent) const { LogTarget(Debug, gc, phases) lt; diff --git a/src/hotspot/share/gc/shared/workerDataArray.cpp b/src/hotspot/share/gc/shared/workerDataArray.cpp index b71406622109f..b296683790270 100644 --- a/src/hotspot/share/gc/shared/workerDataArray.cpp +++ b/src/hotspot/share/gc/shared/workerDataArray.cpp @@ -36,6 +36,8 @@ double WorkerDataArray::uninitialized() { return -1.0; } +#define WDA_TIME_FORMAT "%4.2lf" + template <> void WorkerDataArray::WDAPrinter::summary(outputStream* out, double time) { out->print_cr(" %.1lfms", time * MILLIUNITS); @@ -48,9 +50,10 @@ void WorkerDataArray::WDAPrinter::summary(outputStream* out, size_t valu template <> void WorkerDataArray::WDAPrinter::summary(outputStream* out, double min, double avg, double max, double diff, double sum, bool print_sum) { - out->print(" Min: %4.1lf, Avg: %4.1lf, Max: %4.1lf, Diff: %4.1lf", min * MILLIUNITS, avg * MILLIUNITS, max * MILLIUNITS, diff* MILLIUNITS); + out->print(" Min: " WDA_TIME_FORMAT ", Avg: " WDA_TIME_FORMAT ", Max: " WDA_TIME_FORMAT ", Diff: " WDA_TIME_FORMAT, + min * MILLIUNITS, avg * MILLIUNITS, max * MILLIUNITS, diff* MILLIUNITS); if (print_sum) { - out->print(", Sum: %4.1lf", sum * MILLIUNITS); + out->print(", Sum: " WDA_TIME_FORMAT, sum * MILLIUNITS); } } @@ -68,7 +71,7 @@ void WorkerDataArray::WDAPrinter::details(const WorkerDataArray* for (uint i = 0; i < phase->_length; ++i) { double value = phase->get(i); if (value != phase->uninitialized()) { - out->print(" %4.1lf", phase->get(i) * 1000.0); + out->print(" " WDA_TIME_FORMAT, phase->get(i) * 1000.0); } else { out->print(" -"); } diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp index caf642d0847b9..5ce390459ba44 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp @@ -639,6 +639,34 @@ void ShenandoahBarrierC2Support::verify(RootNode* root) { } #endif +bool ShenandoahBarrierC2Support::is_anti_dependent_load_at_control(PhaseIdealLoop* phase, Node* maybe_load, Node* store, + Node* control) { + return maybe_load->is_Load() && phase->C->can_alias(store->adr_type(), phase->C->get_alias_index(maybe_load->adr_type())) && + phase->ctrl_or_self(maybe_load) == control; +} + +void ShenandoahBarrierC2Support::maybe_push_anti_dependent_loads(PhaseIdealLoop* phase, Node* maybe_store, Node* control, Unique_Node_List &wq) { + if (!maybe_store->is_Store() && !maybe_store->is_LoadStore()) { + return; + } + Node* mem = maybe_store->in(MemNode::Memory); + for (DUIterator_Fast imax, i = mem->fast_outs(imax); i < imax; i++) { + Node* u = mem->fast_out(i); + if (is_anti_dependent_load_at_control(phase, u, maybe_store, control)) { + wq.push(u); + } + } +} + +void ShenandoahBarrierC2Support::push_data_inputs_at_control(PhaseIdealLoop* phase, Node* n, Node* ctrl, Unique_Node_List &wq) { + for (uint i = 0; i < n->req(); i++) { + Node* in = n->in(i); + if (in != nullptr && (ShenandoahIUBarrier ? (phase->ctrl_or_self(in) == ctrl) : (phase->has_ctrl(in) && phase->get_ctrl(in) == ctrl))) { + wq.push(in); + } + } +} + bool ShenandoahBarrierC2Support::is_dominator_same_ctrl(Node* c, Node* d, Node* n, PhaseIdealLoop* phase) { // That both nodes have the same control is not sufficient to prove // domination, verify that there's no path from d to n @@ -653,22 +681,9 @@ bool ShenandoahBarrierC2Support::is_dominator_same_ctrl(Node* c, Node* d, Node* if (m->is_Phi() && m->in(0)->is_Loop()) { assert(phase->ctrl_or_self(m->in(LoopNode::EntryControl)) != c, "following loop entry should lead to new control"); } else { - if (m->is_Store() || m->is_LoadStore()) { - // Take anti-dependencies into account - Node* mem = m->in(MemNode::Memory); - for (DUIterator_Fast imax, i = mem->fast_outs(imax); i < imax; i++) { - Node* u = mem->fast_out(i); - if (u->is_Load() && phase->C->can_alias(m->adr_type(), phase->C->get_alias_index(u->adr_type())) && - phase->ctrl_or_self(u) == c) { - wq.push(u); - } - } - } - for (uint i = 0; i < m->req(); i++) { - if (m->in(i) != NULL && phase->ctrl_or_self(m->in(i)) == c) { - wq.push(m->in(i)); - } - } + // Take anti-dependencies into account + maybe_push_anti_dependent_loads(phase, m, c, wq); + push_data_inputs_at_control(phase, m, c, wq); } } return true; @@ -1020,7 +1035,20 @@ void ShenandoahBarrierC2Support::call_lrb_stub(Node*& ctrl, Node*& val, Node* lo phase->register_new_node(val, ctrl); } -void ShenandoahBarrierC2Support::fix_ctrl(Node* barrier, Node* region, const MemoryGraphFixer& fixer, Unique_Node_List& uses, Unique_Node_List& uses_to_ignore, uint last, PhaseIdealLoop* phase) { +void ShenandoahBarrierC2Support::collect_nodes_above_barrier(Unique_Node_List &nodes_above_barrier, PhaseIdealLoop* phase, Node* ctrl, Node* init_raw_mem) { + nodes_above_barrier.clear(); + if (phase->has_ctrl(init_raw_mem) && phase->get_ctrl(init_raw_mem) == ctrl && !init_raw_mem->is_Phi()) { + nodes_above_barrier.push(init_raw_mem); + } + for (uint next = 0; next < nodes_above_barrier.size(); next++) { + Node* n = nodes_above_barrier.at(next); + // Take anti-dependencies into account + maybe_push_anti_dependent_loads(phase, n, ctrl, nodes_above_barrier); + push_data_inputs_at_control(phase, n, ctrl, nodes_above_barrier); + } +} + +void ShenandoahBarrierC2Support::fix_ctrl(Node* barrier, Node* region, const MemoryGraphFixer& fixer, Unique_Node_List& uses, Unique_Node_List& nodes_above_barrier, uint last, PhaseIdealLoop* phase) { Node* ctrl = phase->get_ctrl(barrier); Node* init_raw_mem = fixer.find_mem(ctrl, barrier); @@ -1031,30 +1059,17 @@ void ShenandoahBarrierC2Support::fix_ctrl(Node* barrier, Node* region, const Mem // control will be after the expanded barrier. The raw memory (if // its memory is control dependent on the barrier's input control) // must stay above the barrier. - uses_to_ignore.clear(); - if (phase->has_ctrl(init_raw_mem) && phase->get_ctrl(init_raw_mem) == ctrl && !init_raw_mem->is_Phi()) { - uses_to_ignore.push(init_raw_mem); - } - for (uint next = 0; next < uses_to_ignore.size(); next++) { - Node *n = uses_to_ignore.at(next); - for (uint i = 0; i < n->req(); i++) { - Node* in = n->in(i); - if (in != NULL && phase->has_ctrl(in) && phase->get_ctrl(in) == ctrl) { - uses_to_ignore.push(in); - } - } - } + collect_nodes_above_barrier(nodes_above_barrier, phase, ctrl, init_raw_mem); for (DUIterator_Fast imax, i = ctrl->fast_outs(imax); i < imax; i++) { Node* u = ctrl->fast_out(i); if (u->_idx < last && u != barrier && !u->depends_only_on_test() && // preserve dependency on test - !uses_to_ignore.member(u) && + !nodes_above_barrier.member(u) && (u->in(0) != ctrl || (!u->is_Region() && !u->is_Phi())) && (ctrl->Opcode() != Op_CatchProj || u->Opcode() != Op_CreateEx)) { Node* old_c = phase->ctrl_or_self(u); - Node* c = old_c; - if (c != ctrl || + if (old_c != ctrl || is_dominator_same_ctrl(old_c, barrier, u, phase) || ShenandoahBarrierSetC2::is_shenandoah_state_load(u)) { phase->igvn().rehash_node_delayed(u); @@ -1326,7 +1341,7 @@ void ShenandoahBarrierC2Support::pin_and_expand(PhaseIdealLoop* phase) { // Expand load-reference-barriers MemoryGraphFixer fixer(Compile::AliasIdxRaw, true, phase); - Unique_Node_List uses_to_ignore; + Unique_Node_List nodes_above_barriers; for (int i = state->load_reference_barriers_count() - 1; i >= 0; i--) { ShenandoahLoadReferenceBarrierNode* lrb = state->load_reference_barrier(i); uint last = phase->C->unique(); @@ -1423,7 +1438,7 @@ void ShenandoahBarrierC2Support::pin_and_expand(PhaseIdealLoop* phase) { Node* out_val = val_phi; phase->register_new_node(val_phi, region); - fix_ctrl(lrb, region, fixer, uses, uses_to_ignore, last, phase); + fix_ctrl(lrb, region, fixer, uses, nodes_above_barriers, last, phase); ctrl = orig_ctrl; @@ -1579,7 +1594,7 @@ void ShenandoahBarrierC2Support::pin_and_expand(PhaseIdealLoop* phase) { phase->register_control(region, loop, heap_stable_ctrl->in(0)); phase->register_new_node(phi, region); - fix_ctrl(barrier, region, fixer, uses, uses_to_ignore, last, phase); + fix_ctrl(barrier, region, fixer, uses, nodes_above_barriers, last, phase); for(uint next = 0; next < uses.size(); next++ ) { Node *n = uses.at(next); assert(phase->get_ctrl(n) == init_ctrl, "bad control"); diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.hpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.hpp index df5fa55e58fca..ab60d8b366f1f 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.hpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.hpp @@ -62,12 +62,16 @@ class ShenandoahBarrierC2Support : public AllStatic { PhaseIdealLoop* phase, int flags); static void call_lrb_stub(Node*& ctrl, Node*& val, Node* load_addr, DecoratorSet decorators, PhaseIdealLoop* phase); + + static void collect_nodes_above_barrier(Unique_Node_List &nodes_above_barrier, PhaseIdealLoop* phase, Node* ctrl, + Node* init_raw_mem); + static void test_in_cset(Node*& ctrl, Node*& not_cset_ctrl, Node* val, Node* raw_mem, PhaseIdealLoop* phase); static void move_gc_state_test_out_of_loop(IfNode* iff, PhaseIdealLoop* phase); static void merge_back_to_back_tests(Node* n, PhaseIdealLoop* phase); static bool merge_point_safe(Node* region); static bool identical_backtoback_ifs(Node *n, PhaseIdealLoop* phase); - static void fix_ctrl(Node* barrier, Node* region, const MemoryGraphFixer& fixer, Unique_Node_List& uses, Unique_Node_List& uses_to_ignore, uint last, PhaseIdealLoop* phase); + static void fix_ctrl(Node* barrier, Node* region, const MemoryGraphFixer& fixer, Unique_Node_List& uses, Unique_Node_List& nodes_above_barrier, uint last, PhaseIdealLoop* phase); static IfNode* find_unswitching_candidate(const IdealLoopTree *loop, PhaseIdealLoop* phase); static Node* get_load_addr(PhaseIdealLoop* phase, VectorSet& visited, Node* lrb); @@ -82,6 +86,11 @@ class ShenandoahBarrierC2Support : public AllStatic { static void pin_and_expand(PhaseIdealLoop* phase); static void optimize_after_expansion(VectorSet& visited, Node_Stack& nstack, Node_List& old_new, PhaseIdealLoop* phase); + static void push_data_inputs_at_control(PhaseIdealLoop* phase, Node* n, Node* ctrl, + Unique_Node_List &wq); + static bool is_anti_dependent_load_at_control(PhaseIdealLoop* phase, Node* maybe_load, Node* store, Node* control); + + static void maybe_push_anti_dependent_loads(PhaseIdealLoop* phase, Node* maybe_store, Node* control, Unique_Node_List &wq); #ifdef ASSERT static void verify(RootNode* root); #endif diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index ceb067389a2a4..b07a085796edd 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -1039,26 +1039,23 @@ void ShenandoahHeap::print_heap_regions_on(outputStream* st) const { } } -void ShenandoahHeap::trash_humongous_region_at(ShenandoahHeapRegion* start) { +size_t ShenandoahHeap::trash_humongous_region_at(ShenandoahHeapRegion* start) const { assert(start->is_humongous_start(), "reclaim regions starting with the first one"); - - oop humongous_obj = cast_to_oop(start->bottom()); - size_t size = humongous_obj->size(); - size_t required_regions = ShenandoahHeapRegion::required_regions(size * HeapWordSize); - size_t index = start->index() + required_regions - 1; - assert(!start->has_live(), "liveness must be zero"); - for(size_t i = 0; i < required_regions; i++) { - // Reclaim from tail. Otherwise, assertion fails when printing region to trace log, - // as it expects that every region belongs to a humongous region starting with a humongous start region. - ShenandoahHeapRegion* region = get_region(index --); - - assert(region->is_humongous(), "expect correct humongous start or continuation"); + // Do not try to get the size of this humongous object. STW collections will + // have already unloaded classes, so an unmarked object may have a bad klass pointer. + ShenandoahHeapRegion* region = start; + size_t index = region->index(); + do { + assert(region->is_humongous(), "Expect correct humongous start or continuation"); assert(!region->is_cset(), "Humongous region should not be in collection set"); - region->make_trash_immediate(); - } + region = get_region(++index); + } while (region != nullptr && region->is_humongous_continuation()); + + // Return number of regions trashed + return index - start->index(); } class ShenandoahCheckCleanGCLABClosure : public ThreadClosure { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp index c7ba21e6e9f0b..a26f837548989 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp @@ -651,7 +651,7 @@ class ShenandoahHeap : public CollectedHeap { static inline void atomic_clear_oop(narrowOop* addr, oop compare); static inline void atomic_clear_oop(narrowOop* addr, narrowOop compare); - void trash_humongous_region_at(ShenandoahHeapRegion *r); + size_t trash_humongous_region_at(ShenandoahHeapRegion *r) const; private: void trash_cset_regions(); diff --git a/src/hotspot/share/interpreter/bytecodeStream.hpp b/src/hotspot/share/interpreter/bytecodeStream.hpp index 4c41faa538013..b2457d12ce89f 100644 --- a/src/hotspot/share/interpreter/bytecodeStream.hpp +++ b/src/hotspot/share/interpreter/bytecodeStream.hpp @@ -100,8 +100,23 @@ class BaseBytecodeStream: StackObj { void set_next_bci(int bci) { assert(0 <= bci && bci <= method()->code_size(), "illegal bci"); _next_bci = bci; } // Bytecode-specific attributes - int dest() const { return bci() + bytecode().get_offset_s2(raw_code()); } - int dest_w() const { return bci() + bytecode().get_offset_s4(raw_code()); } + int get_offset_s2() const { return bytecode().get_offset_s2(raw_code()); } + int get_offset_s4() const { return bytecode().get_offset_s4(raw_code()); } + + // These methods are not safe to use before or during verification as they may + // have large offsets and cause overflows + int dest() const { + int min_offset = -1 * max_method_code_size; + int offset = bytecode().get_offset_s2(raw_code()); + guarantee(offset >= min_offset && offset <= max_method_code_size, "must be"); + return bci() + offset; + } + int dest_w() const { + int min_offset = -1 * max_method_code_size; + int offset = bytecode().get_offset_s4(raw_code()); + guarantee(offset >= min_offset && offset <= max_method_code_size, "must be"); + return bci() + offset; + } // One-byte indices. int get_index_u1() const { assert_raw_index_size(1); return *(jubyte*)(bcp()+1); } diff --git a/src/hotspot/share/interpreter/interpreter.cpp b/src/hotspot/share/interpreter/interpreter.cpp index 7805f39e18f07..1c8570516c4ed 100644 --- a/src/hotspot/share/interpreter/interpreter.cpp +++ b/src/hotspot/share/interpreter/interpreter.cpp @@ -47,18 +47,21 @@ # define __ _masm-> -//------------------------------------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // Implementation of InterpreterCodelet void InterpreterCodelet::initialize(const char* description, Bytecodes::Code bytecode) { - _description = description; - _bytecode = bytecode; -} - - -void InterpreterCodelet::verify() { + _description = description; + _bytecode = bytecode; +#ifndef PRODUCT + AsmRemarks* arp = new(&_asm_remarks) AsmRemarks(); + DbgStrings* dsp = new(&_dbg_strings) DbgStrings(); + postcond(arp == &_asm_remarks); + postcond(dsp == &_dbg_strings); +#endif } +void InterpreterCodelet::verify() {} void InterpreterCodelet::print_on(outputStream* st) const { ttyLocker ttyl; @@ -75,7 +78,7 @@ void InterpreterCodelet::print_on(outputStream* st) const { if (PrintInterpreter) { st->cr(); - Disassembler::decode(code_begin(), code_end(), st DEBUG_ONLY(COMMA &_strings)); + Disassembler::decode(code_begin(), code_end(), st NOT_PRODUCT(COMMA &_asm_remarks)); } } @@ -104,9 +107,13 @@ CodeletMark::~CodeletMark() { // Commit Codelet. int committed_code_size = (*_masm)->code()->pure_insts_size(); - if (committed_code_size) { - CodeStrings cs NOT_PRODUCT(= (*_masm)->code()->strings()); - AbstractInterpreter::code()->commit(committed_code_size, cs); + if (committed_code_size > 0) { + // This is the ONE place where we pickup any assembly remarks and debug + // strings, and propagate these to the codelet. + NOT_PRODUCT(_clet->use_remarks((*_masm)->code()->asm_remarks())); + NOT_PRODUCT(_clet->use_strings((*_masm)->code()->dbg_strings())); + + AbstractInterpreter::code()->commit(committed_code_size); } // Make sure nobody can use _masm outside a CodeletMark lifespan. *_masm = NULL; diff --git a/src/hotspot/share/interpreter/interpreter.hpp b/src/hotspot/share/interpreter/interpreter.hpp index 2e1333b08b570..b639aa9759e3e 100644 --- a/src/hotspot/share/interpreter/interpreter.hpp +++ b/src/hotspot/share/interpreter/interpreter.hpp @@ -46,17 +46,15 @@ class InterpreterCodelet: public Stub { friend class VMStructs; friend class CodeCacheDumper; // possible extension [do not remove] private: - NOT_PRODUCT(CodeStrings _strings;) // Comments for annotating assembler output. const char* _description; // A description of the codelet, for debugging & printing int _size; // The codelet size in bytes Bytecodes::Code _bytecode; // Associated bytecode, if any + NOT_PRODUCT(AsmRemarks _asm_remarks;) // Comments for annotating assembler output. + NOT_PRODUCT(DbgStrings _dbg_strings;) // Debug strings used in generated code. public: // Initialization/finalization - void initialize(int size, - CodeStrings& strings) { _size = size; - NOT_PRODUCT(_strings = CodeStrings();) - NOT_PRODUCT(_strings.copy(strings);) } + void initialize(int size) { _size = size; } void finalize() { ShouldNotCallThis(); } // General info/converters @@ -79,6 +77,15 @@ class InterpreterCodelet: public Stub { int code_size() const { return code_end() - code_begin(); } const char* description() const { return _description; } Bytecodes::Code bytecode() const { return _bytecode; } +#ifndef PRODUCT + ~InterpreterCodelet() { + // InterpreterCodelets reside in the StubQueue and should not be deleted, + // nor are they ever finalized (see above). + ShouldNotCallThis(); + } + void use_remarks(AsmRemarks &remarks) { _asm_remarks.share(remarks); } + void use_strings(DbgStrings &strings) { _dbg_strings.share(strings); } +#endif }; // Define a prototype interface diff --git a/src/hotspot/share/interpreter/interpreterRuntime.cpp b/src/hotspot/share/interpreter/interpreterRuntime.cpp index d66ed24d86257..611ed66862264 100644 --- a/src/hotspot/share/interpreter/interpreterRuntime.cpp +++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp @@ -1345,7 +1345,8 @@ void SignatureHandlerLibrary::add(const methodHandle& method) { fingerprint, buffer.insts_size()); if (buffer.insts_size() > 0) { - Disassembler::decode(handler, handler + buffer.insts_size()); + Disassembler::decode(handler, handler + buffer.insts_size(), tty + NOT_PRODUCT(COMMA &buffer.asm_remarks())); } #ifndef PRODUCT address rh_begin = Interpreter::result_handler(method()->result_type()); diff --git a/src/hotspot/share/interpreter/templateTable.cpp b/src/hotspot/share/interpreter/templateTable.cpp index e60d6a8362be2..827d89a858ef5 100644 --- a/src/hotspot/share/interpreter/templateTable.cpp +++ b/src/hotspot/share/interpreter/templateTable.cpp @@ -202,8 +202,8 @@ void TemplateTable::def(Bytecodes::Code code, int flags, TosState in, TosState o } -void TemplateTable::def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)(bool arg ), bool arg) { - def(code, flags, in, out, (Template::generator)gen, (int)arg); +void TemplateTable::def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)(LdcType ldct), LdcType ldct) { + def(code, flags, in, out, (Template::generator)gen, (int)ldct); } @@ -250,8 +250,8 @@ void TemplateTable::initialize() { def(Bytecodes::_dconst_1 , ____|____|____|____, vtos, dtos, dconst , 1 ); def(Bytecodes::_bipush , ubcp|____|____|____, vtos, itos, bipush , _ ); def(Bytecodes::_sipush , ubcp|____|____|____, vtos, itos, sipush , _ ); - def(Bytecodes::_ldc , ubcp|____|clvm|____, vtos, vtos, ldc , false ); - def(Bytecodes::_ldc_w , ubcp|____|clvm|____, vtos, vtos, ldc , true ); + def(Bytecodes::_ldc , ubcp|____|clvm|____, vtos, vtos, ldc , ldc_normal ); + def(Bytecodes::_ldc_w , ubcp|____|clvm|____, vtos, vtos, ldc , ldc_wide ); def(Bytecodes::_ldc2_w , ubcp|____|clvm|____, vtos, vtos, ldc2_w , _ ); def(Bytecodes::_iload , ubcp|____|clvm|____, vtos, itos, iload , _ ); def(Bytecodes::_lload , ubcp|____|____|____, vtos, ltos, lload , _ ); @@ -484,8 +484,8 @@ void TemplateTable::initialize() { def(Bytecodes::_fast_linearswitch , ubcp|disp|____|____, itos, vtos, fast_linearswitch , _ ); def(Bytecodes::_fast_binaryswitch , ubcp|disp|____|____, itos, vtos, fast_binaryswitch , _ ); - def(Bytecodes::_fast_aldc , ubcp|____|clvm|____, vtos, atos, fast_aldc , false ); - def(Bytecodes::_fast_aldc_w , ubcp|____|clvm|____, vtos, atos, fast_aldc , true ); + def(Bytecodes::_fast_aldc , ubcp|____|clvm|____, vtos, atos, fast_aldc , ldc_normal ); + def(Bytecodes::_fast_aldc_w , ubcp|____|clvm|____, vtos, atos, fast_aldc , ldc_wide ); def(Bytecodes::_return_register_finalizer , ____|disp|clvm|____, vtos, vtos, _return , vtos ); diff --git a/src/hotspot/share/interpreter/templateTable.hpp b/src/hotspot/share/interpreter/templateTable.hpp index ff5d2c338803d..ceb5607ddbb09 100644 --- a/src/hotspot/share/interpreter/templateTable.hpp +++ b/src/hotspot/share/interpreter/templateTable.hpp @@ -83,6 +83,7 @@ class TemplateTable: AllStatic { enum Operation { add, sub, mul, div, rem, _and, _or, _xor, shl, shr, ushr }; enum Condition { equal, not_equal, less, less_equal, greater, greater_equal }; enum CacheByte { f1_byte = 1, f2_byte = 2 }; // byte_no codes + enum LdcType { ldc_normal = 0, ldc_wide = 1 }; // LDC type enum RewriteControl { may_rewrite, may_not_rewrite }; // control for fast code under CDS private: @@ -105,6 +106,11 @@ class TemplateTable: AllStatic { static void patch_bytecode(Bytecodes::Code bc, Register bc_reg, Register temp_reg, bool load_bc_into_bc_reg = true, int byte_no = -1); + static bool is_ldc_wide(LdcType type) { + assert(type == ldc_wide || type == ldc_normal, "sanity"); + return (type == ldc_wide); + } + // C calls static void call_VM(Register oop_result, address entry_point); static void call_VM(Register oop_result, address entry_point, Register arg_1); @@ -128,9 +134,9 @@ class TemplateTable: AllStatic { static void bipush(); static void sipush(); - static void ldc(bool wide); + static void ldc(LdcType type); static void ldc2_w(); - static void fast_aldc(bool wide); + static void fast_aldc(LdcType type); static void locals_index(Register reg, int offset = 1); static void iload(); @@ -327,7 +333,7 @@ class TemplateTable: AllStatic { // initialization helpers static void def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)( ), char filler ); static void def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)(int arg ), int arg ); - static void def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)(bool arg ), bool arg ); + static void def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)(LdcType ldct), LdcType ldct); static void def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)(TosState tos), TosState tos); static void def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)(Operation op), Operation op); static void def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)(Condition cc), Condition cc); diff --git a/src/hotspot/share/jfr/support/jfrJdkJfrEvent.cpp b/src/hotspot/share/jfr/support/jfrJdkJfrEvent.cpp index c591fcef13057..ed62a59c7d4cf 100644 --- a/src/hotspot/share/jfr/support/jfrJdkJfrEvent.cpp +++ b/src/hotspot/share/jfr/support/jfrJdkJfrEvent.cpp @@ -132,7 +132,9 @@ jobject JdkJfrEvent::get_all_klasses(TRAPS) { transform_klasses_to_local_jni_handles(event_subklasses, THREAD); Handle h_array_list(THREAD, new_java_util_arraylist(THREAD)); - assert(h_array_list.not_null(), "invariant"); + if (h_array_list.is_null()) { + return empty_java_util_arraylist; + } static const char add_method_name[] = "add"; static const char add_method_signature[] = "(Ljava/lang/Object;)Z"; diff --git a/src/hotspot/share/jvmci/jvmciEnv.cpp b/src/hotspot/share/jvmci/jvmciEnv.cpp index b3397bdd3fb75..80a9a7363fa47 100644 --- a/src/hotspot/share/jvmci/jvmciEnv.cpp +++ b/src/hotspot/share/jvmci/jvmciEnv.cpp @@ -569,7 +569,7 @@ void JVMCIEnv::put_int_at(JVMCIPrimitiveArray array, int index, jint value) { } } -long JVMCIEnv::get_long_at(JVMCIPrimitiveArray array, int index) { +jlong JVMCIEnv::get_long_at(JVMCIPrimitiveArray array, int index) { if (is_hotspot()) { return HotSpotJVMCI::resolve(array)->long_at(index); } else { diff --git a/src/hotspot/share/jvmci/jvmciEnv.hpp b/src/hotspot/share/jvmci/jvmciEnv.hpp index 18a80accd8bc4..61b7cd4e3312b 100644 --- a/src/hotspot/share/jvmci/jvmciEnv.hpp +++ b/src/hotspot/share/jvmci/jvmciEnv.hpp @@ -251,7 +251,7 @@ class JVMCIEnv : public ResourceObj { jint get_int_at(JVMCIPrimitiveArray array, int index); void put_int_at(JVMCIPrimitiveArray array, int index, jint value); - long get_long_at(JVMCIPrimitiveArray array, int index); + jlong get_long_at(JVMCIPrimitiveArray array, int index); void put_long_at(JVMCIPrimitiveArray array, int index, jlong value); void copy_bytes_to(JVMCIPrimitiveArray src, jbyte* dest, int offset, jsize length); diff --git a/src/hotspot/share/memory/guardedMemory.cpp b/src/hotspot/share/memory/guardedMemory.cpp index 6cd141ce4ff0d..26cba4718827c 100644 --- a/src/hotspot/share/memory/guardedMemory.cpp +++ b/src/hotspot/share/memory/guardedMemory.cpp @@ -27,11 +27,12 @@ #include "memory/guardedMemory.hpp" #include "runtime/os.hpp" -void* GuardedMemory::wrap_copy(const void* ptr, const size_t len, const void* tag) { +void* GuardedMemory::wrap_copy(const void* ptr, const size_t len, + const void* tag, const void* tag2) { size_t total_sz = GuardedMemory::get_total_size(len); void* outerp = os::malloc(total_sz, mtInternal); if (outerp != NULL) { - GuardedMemory guarded(outerp, len, tag); + GuardedMemory guarded(outerp, len, tag, tag2); void* innerp = guarded.get_user_ptr(); if (ptr != nullptr) { memcpy(innerp, ptr, len); @@ -60,8 +61,8 @@ void GuardedMemory::print_on(outputStream* st) const { return; } st->print_cr("GuardedMemory(" PTR_FORMAT ") base_addr=" PTR_FORMAT - " tag=" PTR_FORMAT " user_size=" SIZE_FORMAT " user_data=" PTR_FORMAT, - p2i(this), p2i(_base_addr), p2i(get_tag()), get_user_size(), p2i(get_user_ptr())); + " tag=" PTR_FORMAT "tag2=" PTR_FORMAT " user_size=" SIZE_FORMAT " user_data=" PTR_FORMAT, + p2i(this), p2i(_base_addr), p2i(get_tag()), p2i(get_tag2()), get_user_size(), p2i(get_user_ptr())); Guard* guard = get_head_guard(); st->print_cr(" Header guard @" PTR_FORMAT " is %s", p2i(guard), (guard->verify() ? "OK" : "BROKEN")); diff --git a/src/hotspot/share/memory/guardedMemory.hpp b/src/hotspot/share/memory/guardedMemory.hpp index 917a873223322..7044935f872f7 100644 --- a/src/hotspot/share/memory/guardedMemory.hpp +++ b/src/hotspot/share/memory/guardedMemory.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ #define SHARE_MEMORY_GUARDEDMEMORY_HPP #include "memory/allocation.hpp" +#include "runtime/os.hpp" #include "utilities/globalDefinitions.hpp" /** @@ -41,15 +42,19 @@ * |Offset | Content | Description | * |------------------------------------------------------------ * |base_addr | 0xABABABABABABABAB | Head guard | - * |+16 | | User data size | - * |+sizeof(uintptr_t) | | Tag word | + * |+GUARD_SIZE | | User data size | + * |+sizeof(size_t) | | Tag word | + * |+sizeof(void*) | | Tag word | + * |+sizeof(void*) | | Padding | * |+sizeof(void*) | 0xF1 ( | User data | * |+user_size | 0xABABABABABABABAB | Tail guard | * ------------------------------------------------------------- * * Where: * - guard padding uses "badResourceValue" (0xAB) - * - tag word is general purpose + * - tag word and tag2 word are general purpose + * - padding is inserted as-needed by the compiler to ensure + * the user data is aligned on a 16-byte boundary * - user data * -- initially padded with "uninitBlockPad" (0xF1), * -- to "freeBlockPad" (0xBA), when freed @@ -111,6 +116,10 @@ class GuardedMemory : StackObj { // Wrapper on stack } bool verify() const { + // We may not be able to dereference directly. + if (!os::is_readable_range((const void*) _guard, (const void*) (_guard + GUARD_SIZE))) { + return false; + } u_char* c = (u_char*) _guard; u_char* end = c + GUARD_SIZE; while (c < end) { @@ -126,17 +135,21 @@ class GuardedMemory : StackObj { // Wrapper on stack /** * Header guard and size + * + * NB: the size and placement of the GuardHeader must be such that the + * user-ptr is maximally aligned i.e. 16-byte alignment for x86 ABI for + * stack alignment and use of vector (xmm) instructions. We use alignas + * to achieve this. */ - class GuardHeader : Guard { + class alignas(16) GuardHeader : Guard { friend class GuardedMemory; protected: - // Take care in modifying fields here, will effect alignment - // e.g. x86 ABI 16 byte stack alignment union { uintptr_t __unused_full_word1; size_t _user_size; }; void* _tag; + void* _tag2; public: void set_user_size(const size_t usz) { _user_size = usz; } size_t get_user_size() const { return _user_size; } @@ -144,6 +157,8 @@ class GuardedMemory : StackObj { // Wrapper on stack void set_tag(const void* tag) { _tag = (void*) tag; } void* get_tag() const { return _tag; } + void set_tag2(const void* tag2) { _tag2 = (void*) tag2; } + void* get_tag2() const { return _tag2; } }; // GuardedMemory::GuardHeader // Guarded Memory... @@ -162,9 +177,11 @@ class GuardedMemory : StackObj { // Wrapper on stack * @param base_ptr allocation wishing to be wrapped, must be at least "GuardedMemory::get_total_size()" bytes. * @param user_size the size of the user data to be wrapped. * @param tag optional general purpose tag. + * @param tag2 optional second general purpose tag. */ - GuardedMemory(void* base_ptr, const size_t user_size, const void* tag = NULL) { - wrap_with_guards(base_ptr, user_size, tag); + GuardedMemory(void* base_ptr, const size_t user_size, + const void* tag = nullptr, const void* tag2 = nullptr) { + wrap_with_guards(base_ptr, user_size, tag, tag2); } /** @@ -189,16 +206,19 @@ class GuardedMemory : StackObj { // Wrapper on stack * @param base_ptr allocation wishing to be wrapped, must be at least "GuardedMemory::get_total_size()" bytes. * @param user_size the size of the user data to be wrapped. * @param tag optional general purpose tag. + * @param tag2 optional second general purpose tag. * * @return user data pointer (inner pointer to supplied "base_ptr"). */ - void* wrap_with_guards(void* base_ptr, size_t user_size, const void* tag = NULL) { + void* wrap_with_guards(void* base_ptr, size_t user_size, + const void* tag = nullptr, const void* tag2 = nullptr) { assert(base_ptr != NULL, "Attempt to wrap NULL with memory guard"); _base_addr = (u_char*)base_ptr; get_head_guard()->build(); get_head_guard()->set_user_size(user_size); get_tail_guard()->build(); set_tag(tag); + set_tag2(tag2); set_user_bytes(uninitBlockPad); assert(verify_guards(), "Expected valid memory guards"); return get_user_ptr(); @@ -230,6 +250,20 @@ class GuardedMemory : StackObj { // Wrapper on stack */ void* get_tag() const { return get_head_guard()->get_tag(); } + /** + * Set the second general purpose tag. + * + * @param tag general purpose tag. + */ + void set_tag2(const void* tag) { get_head_guard()->set_tag2(tag); } + + /** + * Return the second general purpose tag. + * + * @return the second general purpose tag, defaults to null. + */ + void* get_tag2() const { return get_head_guard()->get_tag2(); } + /** * Return the size of the user data. * @@ -302,10 +336,12 @@ class GuardedMemory : StackObj { // Wrapper on stack * @param ptr the memory to be copied * @param len the length of the copy * @param tag optional general purpose tag (see GuardedMemory::get_tag()) + * @param tag2 optional general purpose tag (see GuardedMemory::get_tag2()) * * @return guarded wrapped memory pointer to the user area, or NULL if OOM. */ - static void* wrap_copy(const void* p, const size_t len, const void* tag = NULL); + static void* wrap_copy(const void* p, const size_t len, + const void* tag = nullptr, const void* tag2 = nullptr); /** * Free wrapped copy. diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 35e8e2bdc1ce1..ed83e224b418c 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1164,6 +1164,14 @@ static bool can_overflow(const TypeInt* t, jint c) { (c > 0 && (java_add(t_hi, c) < t_hi))); } +// Check if addition of a long with type 't' and a constant 'c' can overflow. +static bool can_overflow(const TypeLong* t, jlong c) { + jlong t_lo = t->_lo; + jlong t_hi = t->_hi; + return ((c < 0 && (java_add(t_lo, c) > t_lo)) || + (c > 0 && (java_add(t_hi, c) < t_hi))); +} + //============================================================================= //------------------------------Idealize--------------------------------------- // MINs show up in range-check loop limit calculations. Look for @@ -1285,6 +1293,31 @@ const Type *MinINode::add_ring( const Type *t0, const Type *t1 ) const { // // Note: we assume that SubL was already replaced by an AddL, and that the stride // has its sign flipped: SubL(limit, stride) -> AddL(limit, -stride). +// +// Proof MaxL collapsed version equivalent to original (MinL version similar): +// is_sub_con ensures that con1, con2 ∈ [min_int, 0[ +// +// Original: +// - AddL2 underflow => x + con2 ∈ ]max_long - min_int, max_long], ALWAYS BAILOUT as x + con1 + con2 surely fails can_overflow (*) +// - AddL2 no underflow => x + con2 ∈ [min_long, max_long] +// - MaxL2 clamp => min_int +// - AddL1 underflow: NOT POSSIBLE: cannot underflow since min_int + con1 ∈ [2 * min_int, min_int] always > min_long +// - AddL1 no underflow => min_int + con1 ∈ [2 * min_int, min_int] +// - MaxL1 clamp => min_int (RESULT 1) +// - MaxL1 no clamp: NOT POSSIBLE: min_int + con1 ∈ [2 * min_int, min_int] always <= min_int, so clamp always taken +// - MaxL2 no clamp => x + con2 ∈ [min_int, max_long] +// - AddL1 underflow: NOT POSSIBLE: cannot underflow since x + con2 + con1 ∈ [2 * min_int, max_long] always > min_long +// - AddL1 no underflow => x + con2 + con1 ∈ [2 * min_int, max_long] +// - MaxL1 clamp => min_int (RESULT 2) +// - MaxL1 no clamp => x + con2 + con1 ∈ ]min_int, max_long] (RESULT 3) +// +// Collapsed: +// - AddL2 (cannot underflow) => con2 + con1 ∈ [2 * min_int, 0] +// - AddL1 underflow: NOT POSSIBLE: would have bailed out at can_overflow (*) +// - AddL1 no underflow => x + con2 + con1 ∈ [min_long, max_long] +// - MaxL clamp => min_int (RESULT 1 and RESULT 2) +// - MaxL no clamp => x + con2 + con1 ∈ ]min_int, max_long] (RESULT 3) +// Node* fold_subI_no_underflow_pattern(Node* n, PhaseGVN* phase) { assert(n->Opcode() == Op_MaxL || n->Opcode() == Op_MinL, "sanity"); // Check that the two clamps have the correct values. @@ -1314,6 +1347,10 @@ Node* fold_subI_no_underflow_pattern(Node* n, PhaseGVN* phase) { Node* x = add2->in(1); Node* con2 = add2->in(2); if (is_sub_con(con2)) { + // Collapsed graph not equivalent if potential over/underflow -> bailing out (*) + if (can_overflow(phase->type(x)->is_long(), con1->get_long() + con2->get_long())) { + return nullptr; + } Node* new_con = phase->transform(new AddLNode(con1, con2)); Node* new_sub = phase->transform(new AddLNode(x, new_con)); n->set_req_X(1, new_sub, phase); diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index c69208089f835..74074063badd1 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -775,7 +775,7 @@ Compile::Compile( ciEnv* ci_env, ciMethod* target, int osr_bci, // If any phase is randomized for stress testing, seed random number // generation and log the seed for repeatability. if (StressLCM || StressGCM || StressIGVN || StressCCP) { - if (FLAG_IS_DEFAULT(StressSeed) || (FLAG_IS_ERGO(StressSeed) && RepeatCompilation)) { + if (FLAG_IS_DEFAULT(StressSeed) || (FLAG_IS_ERGO(StressSeed) && directive->RepeatCompilationOption)) { _stress_seed = static_cast(Ticks::now().nanoseconds()); FLAG_SET_ERGO(StressSeed, _stress_seed); } else { @@ -4610,7 +4610,7 @@ void Compile::remove_speculative_types(PhaseIterGVN &igvn) { const Type* t_no_spec = t->remove_speculative(); if (t_no_spec != t) { bool in_hash = igvn.hash_delete(n); - assert(in_hash, "node should be in igvn hash table"); + assert(in_hash || n->hash() == Node::NO_HASH, "node should be in igvn hash table"); tn->set_type(t_no_spec); igvn.hash_insert(n); igvn._worklist.push(n); // give it a chance to go away diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index 880dbff8307b6..e57f87b843750 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -1717,10 +1717,11 @@ bool PhaseIdealLoop::ctrl_of_use_out_of_loop(const Node* n, Node* n_ctrl, IdealL // Sinking a node from a pre loop to its main loop pins the node between the pre and main loops. If that node is input // to a check that's eliminated by range check elimination, it becomes input to an expression that feeds into the exit // test of the pre loop above the point in the graph where it's pinned. - if (n_loop->_head->is_CountedLoop() && n_loop->_head->as_CountedLoop()->is_pre_loop() && - u_loop->_head->is_CountedLoop() && u_loop->_head->as_CountedLoop()->is_main_loop() && - n_loop->_next == get_loop(u_loop->_head->as_CountedLoop()->skip_strip_mined())) { - return false; + if (n_loop->_head->is_CountedLoop() && n_loop->_head->as_CountedLoop()->is_pre_loop()) { + CountedLoopNode* pre_loop = n_loop->_head->as_CountedLoop(); + if (is_dominator(pre_loop->loopexit(), ctrl)) { + return false; + } } return true; } diff --git a/src/hotspot/share/opto/mulnode.cpp b/src/hotspot/share/opto/mulnode.cpp index 6d35fd8bf7b02..6eb47eb38bb92 100644 --- a/src/hotspot/share/opto/mulnode.cpp +++ b/src/hotspot/share/opto/mulnode.cpp @@ -242,7 +242,7 @@ Node *MulINode::Ideal(PhaseGVN *phase, bool can_reshape) { // Check for negative constant; if so negate the final result bool sign_flip = false; - unsigned int abs_con = uabs(con); + unsigned int abs_con = g_uabs(con); if (abs_con != (unsigned int)con) { sign_flip = true; } @@ -336,7 +336,7 @@ Node *MulLNode::Ideal(PhaseGVN *phase, bool can_reshape) { // Check for negative constant; if so negate the final result bool sign_flip = false; - julong abs_con = uabs(con); + julong abs_con = g_uabs(con); if (abs_con != (julong)con) { sign_flip = true; } diff --git a/src/hotspot/share/opto/parse2.cpp b/src/hotspot/share/opto/parse2.cpp index aedf9485bae8b..57addc1bbd281 100644 --- a/src/hotspot/share/opto/parse2.cpp +++ b/src/hotspot/share/opto/parse2.cpp @@ -434,7 +434,7 @@ void Parse::do_tableswitch() { // generate decision tree, using trichotomy when possible int rnum = len+2; - bool makes_backward_branch = false; + bool makes_backward_branch = (default_dest <= bci()); SwitchRange* ranges = NEW_RESOURCE_ARRAY(SwitchRange, rnum); int rp = -1; if (lo_index != min_jint) { @@ -536,7 +536,7 @@ void Parse::do_lookupswitch() { } int rnum = len*2+1; - bool makes_backward_branch = false; + bool makes_backward_branch = (default_dest <= bci()); SwitchRange* ranges = NEW_RESOURCE_ARRAY(SwitchRange, rnum); int rp = -1; for (int j = 0; j < len; j++) { diff --git a/src/hotspot/share/opto/stringopts.cpp b/src/hotspot/share/opto/stringopts.cpp index 200e071db923e..f5cf5bcc413f9 100644 --- a/src/hotspot/share/opto/stringopts.cpp +++ b/src/hotspot/share/opto/stringopts.cpp @@ -1004,12 +1004,21 @@ bool StringConcat::validate_control_flow() { continue; } - // A test which leads to an uncommon trap which should be safe. - // Later this trap will be converted into a trap that restarts + // A test which leads to an uncommon trap. It is safe to convert the trap + // into a trap that restarts at the beginning as long as its test does not + // depend on intermediate results of the candidate chain. // at the beginning. if (otherproj->outcnt() == 1) { CallStaticJavaNode* call = otherproj->unique_out()->isa_CallStaticJava(); if (call != nullptr && call->_name != nullptr && strcmp(call->_name, "uncommon_trap") == 0) { + // First check for dependency on a toString that is going away during stacked concats. + if (_multiple && + ((v1->is_Proj() && is_SB_toString(v1->in(0)) && ctrl_path.member(v1->in(0))) || + (v2->is_Proj() && is_SB_toString(v2->in(0)) && ctrl_path.member(v2->in(0))))) { + // iftrue -> if -> bool -> cmpp -> resproj -> tostring + fail = true; + break; + } // control flow leads to uct so should be ok _uncommon_traps.push(call); ctrl_path.push(call); diff --git a/src/hotspot/share/opto/vector.cpp b/src/hotspot/share/opto/vector.cpp index 6f0adce1ae82f..6c3a2d39919d9 100644 --- a/src/hotspot/share/opto/vector.cpp +++ b/src/hotspot/share/opto/vector.cpp @@ -163,6 +163,17 @@ static JVMState* clone_jvms(Compile* C, SafePointNode* sfpt) { for (uint i = 0; i < size; i++) { map->init_req(i, sfpt->in(i)); } + Node* mem = map->memory(); + if (!mem->is_MergeMem()) { + // Since we are not in parsing, the SafePointNode does not guarantee that the memory + // input is necessarily a MergeMemNode. But we need to ensure that there is that + // MergeMemNode, since the GraphKit assumes the memory input of the map to be a + // MergeMemNode, so that it can directly access the memory slices. + PhaseGVN& gvn = *C->initial_gvn(); + Node* mergemem = MergeMemNode::make(mem); + gvn.set_type_bottom(mergemem); + map->set_memory(mergemem); + } new_jvms->set_map(map); return new_jvms; } diff --git a/src/hotspot/share/opto/vectorIntrinsics.cpp b/src/hotspot/share/opto/vectorIntrinsics.cpp index c4a99ed8f6d5f..7e52e1ebc510c 100644 --- a/src/hotspot/share/opto/vectorIntrinsics.cpp +++ b/src/hotspot/share/opto/vectorIntrinsics.cpp @@ -1813,7 +1813,7 @@ bool LibraryCallKit::inline_vector_insert() { default: fatal("%s", type2name(elem_bt)); break; } - Node* operation = gvn().transform(VectorInsertNode::make(opd, insert_val, idx->get_con())); + Node* operation = gvn().transform(VectorInsertNode::make(opd, insert_val, idx->get_con(), gvn())); Node* vbox = box_vector(operation, vbox_type, elem_bt, num_elem); set_result(vbox); diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp index b9ef5abd75a67..bd4b2b10fe2a7 100644 --- a/src/hotspot/share/opto/vectornode.cpp +++ b/src/hotspot/share/opto/vectornode.cpp @@ -1252,9 +1252,9 @@ Node* VectorReinterpretNode::Identity(PhaseGVN *phase) { return this; } -Node* VectorInsertNode::make(Node* vec, Node* new_val, int position) { +Node* VectorInsertNode::make(Node* vec, Node* new_val, int position, PhaseGVN& gvn) { assert(position < (int)vec->bottom_type()->is_vect()->length(), "pos in range"); - ConINode* pos = ConINode::make(position); + ConINode* pos = gvn.intcon(position); return new VectorInsertNode(vec, new_val, pos, vec->bottom_type()->is_vect()); } diff --git a/src/hotspot/share/opto/vectornode.hpp b/src/hotspot/share/opto/vectornode.hpp index ccb5fd88bf6a1..d1d30f36ac2a6 100644 --- a/src/hotspot/share/opto/vectornode.hpp +++ b/src/hotspot/share/opto/vectornode.hpp @@ -1398,7 +1398,7 @@ class VectorInsertNode : public VectorNode { virtual int Opcode() const; uint pos() const { return in(3)->get_int(); } - static Node* make(Node* vec, Node* new_val, int position); + static Node* make(Node* vec, Node* new_val, int position, PhaseGVN& gvn); }; class VectorBoxNode : public Node { diff --git a/src/hotspot/share/prims/jniCheck.cpp b/src/hotspot/share/prims/jniCheck.cpp index 490edb45318b5..f75ca64192c50 100644 --- a/src/hotspot/share/prims/jniCheck.cpp +++ b/src/hotspot/share/prims/jniCheck.cpp @@ -46,9 +46,6 @@ #include "utilities/formatBuffer.hpp" #include "utilities/utf8.hpp" -// Complain every extra number of unplanned local refs -#define CHECK_JNI_LOCAL_REF_CAP_WARN_THRESHOLD 32 - // Heap objects are allowed to be directly referenced only in VM code, // not in native code. @@ -202,17 +199,6 @@ check_pending_exception(JavaThread* thr) { } } -/** - * Add to the planned number of handles. I.e. plus current live & warning threshold - */ -static inline void -add_planned_handle_capacity(JNIHandleBlock* handles, size_t capacity) { - handles->set_planned_capacity(capacity + - handles->get_number_of_live_handles() + - CHECK_JNI_LOCAL_REF_CAP_WARN_THRESHOLD); -} - - static inline void functionEnterCritical(JavaThread* thr) { @@ -244,18 +230,7 @@ functionEnterExceptionAllowed(JavaThread* thr) static inline void functionExit(JavaThread* thr) { - JNIHandleBlock* handles = thr->active_handles(); - size_t planned_capacity = handles->get_planned_capacity(); - size_t live_handles = handles->get_number_of_live_handles(); - if (live_handles > planned_capacity) { - IN_VM( - tty->print_cr("WARNING: JNI local refs: " SIZE_FORMAT ", exceeds capacity: " SIZE_FORMAT, - live_handles, planned_capacity); - thr->print_stack(); - ) - // Complain just the once, reset to current + warn threshold - add_planned_handle_capacity(handles, 0); - } + // No checks at this time } static inline void @@ -376,24 +351,33 @@ check_is_obj_array(JavaThread* thr, jarray jArray) { } } +// Arbitrary (but well-known) tag for GetStringChars +const void* STRING_TAG = (void*)0x47114711; + +// Arbitrary (but well-known) tag for GetStringUTFChars +const void* STRING_UTF_TAG = (void*) 0x48124812; + +// Arbitrary (but well-known) tag for GetPrimitiveArrayCritical +const void* CRITICAL_TAG = (void*)0x49134913; + /* * Copy and wrap array elements for bounds checking. * Remember the original elements (GuardedMemory::get_tag()) */ static void* check_jni_wrap_copy_array(JavaThread* thr, jarray array, - void* orig_elements) { + void* orig_elements, jboolean is_critical = JNI_FALSE) { void* result; IN_VM( oop a = JNIHandles::resolve_non_null(array); size_t len = arrayOop(a)->length() << TypeArrayKlass::cast(a->klass())->log2_element_size(); - result = GuardedMemory::wrap_copy(orig_elements, len, orig_elements); + result = GuardedMemory::wrap_copy(orig_elements, len, orig_elements, is_critical ? CRITICAL_TAG : nullptr); ) return result; } static void* check_wrapped_array(JavaThread* thr, const char* fn_name, - void* obj, void* carray, size_t* rsz) { + void* obj, void* carray, size_t* rsz, jboolean is_critical) { if (carray == NULL) { tty->print_cr("%s: elements vector NULL" PTR_FORMAT, fn_name, p2i(obj)); NativeReportJNIFatalError(thr, "Elements vector NULL"); @@ -412,6 +396,29 @@ static void* check_wrapped_array(JavaThread* thr, const char* fn_name, DEBUG_ONLY(guarded.print_on(tty);) // This may crash. NativeReportJNIFatalError(thr, err_msg("%s: unrecognized elements", fn_name)); } + if (orig_result == STRING_TAG || orig_result == STRING_UTF_TAG) { + bool was_utf = orig_result == STRING_UTF_TAG; + tty->print_cr("%s: called on something allocated by %s", + fn_name, was_utf ? "GetStringUTFChars" : "GetStringChars"); + DEBUG_ONLY(guarded.print_on(tty);) // This may crash. + NativeReportJNIFatalError(thr, err_msg("%s called on something allocated by %s", + fn_name, was_utf ? "GetStringUTFChars" : "GetStringChars")); + } + + if (is_critical && (guarded.get_tag2() != CRITICAL_TAG)) { + tty->print_cr("%s: called on something not allocated by GetPrimitiveArrayCritical", fn_name); + DEBUG_ONLY(guarded.print_on(tty);) // This may crash. + NativeReportJNIFatalError(thr, err_msg("%s called on something not allocated by GetPrimitiveArrayCritical", + fn_name)); + } + + if (!is_critical && (guarded.get_tag2() == CRITICAL_TAG)) { + tty->print_cr("%s: called on something allocated by GetPrimitiveArrayCritical", fn_name); + DEBUG_ONLY(guarded.print_on(tty);) // This may crash. + NativeReportJNIFatalError(thr, err_msg("%s called on something allocated by GetPrimitiveArrayCritical", + fn_name)); + } + if (rsz != NULL) { *rsz = guarded.get_user_size(); } @@ -421,7 +428,7 @@ static void* check_wrapped_array(JavaThread* thr, const char* fn_name, static void* check_wrapped_array_release(JavaThread* thr, const char* fn_name, void* obj, void* carray, jint mode, jboolean is_critical) { size_t sz; - void* orig_result = check_wrapped_array(thr, fn_name, obj, carray, &sz); + void* orig_result = check_wrapped_array(thr, fn_name, obj, carray, &sz, is_critical); switch (mode) { case 0: memcpy(orig_result, carray, sz); @@ -746,9 +753,6 @@ JNI_ENTRY_CHECKED(jint, if (capacity < 0) NativeReportJNIFatalError(thr, "negative capacity"); jint result = UNCHECKED()->PushLocalFrame(env, capacity); - if (result == JNI_OK) { - add_planned_handle_capacity(thr->active_handles(), capacity); - } functionExit(thr); return result; JNI_END @@ -850,12 +854,6 @@ JNI_ENTRY_CHECKED(jint, NativeReportJNIFatalError(thr, "negative capacity"); } jint result = UNCHECKED()->EnsureLocalCapacity(env, capacity); - if (result == JNI_OK) { - // increase local ref capacity if needed - if ((size_t)capacity > thr->active_handles()->get_planned_capacity()) { - add_planned_handle_capacity(thr->active_handles(), capacity); - } - } functionExit(thr); return result; JNI_END @@ -1465,9 +1463,6 @@ JNI_ENTRY_CHECKED(jsize, return result; JNI_END -// Arbitrary (but well-known) tag -const void* STRING_TAG = (void*)0x47114711; - JNI_ENTRY_CHECKED(const jchar *, checked_jni_GetStringChars(JNIEnv *env, jstring str, @@ -1549,9 +1544,6 @@ JNI_ENTRY_CHECKED(jsize, return result; JNI_END -// Arbitrary (but well-known) tag - different than GetStringChars -const void* STRING_UTF_TAG = (void*) 0x48124812; - JNI_ENTRY_CHECKED(const char *, checked_jni_GetStringUTFChars(JNIEnv *env, jstring str, @@ -1873,7 +1865,7 @@ JNI_ENTRY_CHECKED(void *, ) void *result = UNCHECKED()->GetPrimitiveArrayCritical(env, array, isCopy); if (result != NULL) { - result = check_jni_wrap_copy_array(thr, array, result); + result = check_jni_wrap_copy_array(thr, array, result, JNI_TRUE); } functionExit(thr); return result; diff --git a/src/hotspot/share/prims/jvmtiEnv.cpp b/src/hotspot/share/prims/jvmtiEnv.cpp index f04dc7dd27ca2..ccb35889f5d70 100644 --- a/src/hotspot/share/prims/jvmtiEnv.cpp +++ b/src/hotspot/share/prims/jvmtiEnv.cpp @@ -2747,7 +2747,9 @@ JvmtiEnv::GetFieldName(fieldDescriptor* fdesc_ptr, char** name_ptr, char** signa // declaring_class_ptr - pre-checked for NULL jvmtiError JvmtiEnv::GetFieldDeclaringClass(fieldDescriptor* fdesc_ptr, jclass* declaring_class_ptr) { - + // As for the GetFieldDeclaringClass method, the XSL generated C++ code that calls it has + // a jclass of the relevant class or a subclass of it, which is fine in terms of ensuring + // the holder is kept alive. *declaring_class_ptr = get_jni_class_non_null(fdesc_ptr->field_holder()); return JVMTI_ERROR_NONE; } /* end GetFieldDeclaringClass */ @@ -2825,7 +2827,9 @@ JvmtiEnv::GetMethodName(Method* method, char** name_ptr, char** signature_ptr, c jvmtiError JvmtiEnv::GetMethodDeclaringClass(Method* method, jclass* declaring_class_ptr) { NULL_CHECK(method, JVMTI_ERROR_INVALID_METHODID); - (*declaring_class_ptr) = get_jni_class_non_null(method->method_holder()); + Klass* k = method->method_holder(); + Handle holder(Thread::current(), k->klass_holder()); // keep the klass alive + (*declaring_class_ptr) = get_jni_class_non_null(k); return JVMTI_ERROR_NONE; } /* end GetMethodDeclaringClass */ diff --git a/src/hotspot/share/prims/jvmtiEnvBase.cpp b/src/hotspot/share/prims/jvmtiEnvBase.cpp index 943aedd2e5ef6..353112079818a 100644 --- a/src/hotspot/share/prims/jvmtiEnvBase.cpp +++ b/src/hotspot/share/prims/jvmtiEnvBase.cpp @@ -584,6 +584,7 @@ JvmtiEnvBase::vframeForNoProcess(JavaThread* java_thread, jint depth) { jclass JvmtiEnvBase::get_jni_class_non_null(Klass* k) { assert(k != NULL, "k != NULL"); + assert(k->is_loader_alive(), "Must be alive"); Thread *thread = Thread::current(); return (jclass)jni_reference(Handle(thread, k->java_mirror())); } diff --git a/src/hotspot/share/prims/universalUpcallHandler.cpp b/src/hotspot/share/prims/universalUpcallHandler.cpp index 44b64504d1c75..b34704ea7b52a 100644 --- a/src/hotspot/share/prims/universalUpcallHandler.cpp +++ b/src/hotspot/share/prims/universalUpcallHandler.cpp @@ -131,21 +131,17 @@ void ProgrammableUpcallHandler::on_exit(OptimizedEntryBlob::FrameData* context) // restore previous handle block thread->set_active_handles(context->old_handles); - thread->frame_anchor()->zap(); - debug_only(thread->dec_java_call_counter()); + thread->frame_anchor()->copy(&context->jfa); + // Old thread-local info. has been restored. We are now back in native code. ThreadStateTransition::transition_from_java(thread, _thread_in_native); - thread->frame_anchor()->copy(&context->jfa); - // Release handles after we are marked as being in native code again, since this // operation might block JNIHandleBlock::release_block(context->new_handles, thread); - assert(!thread->has_pending_exception(), "Upcall can not throw an exception"); - if (context->should_detach) { detach_current_thread(); } diff --git a/src/hotspot/share/runtime/abstract_vm_version.cpp b/src/hotspot/share/runtime/abstract_vm_version.cpp index cd8e239f02326..b4cd937a49b02 100644 --- a/src/hotspot/share/runtime/abstract_vm_version.cpp +++ b/src/hotspot/share/runtime/abstract_vm_version.cpp @@ -253,6 +253,18 @@ const char* Abstract_VM_Version::internal_vm_info_string() { #define HOTSPOT_BUILD_COMPILER "MS VC++ 17.7 (VS2022)" #elif _MSC_VER == 1938 #define HOTSPOT_BUILD_COMPILER "MS VC++ 17.8 (VS2022)" + #elif _MSC_VER == 1939 + #define HOTSPOT_BUILD_COMPILER "MS VC++ 17.9 (VS2022)" + #elif _MSC_VER == 1940 + #define HOTSPOT_BUILD_COMPILER "MS VC++ 17.10 (VS2022)" + #elif _MSC_VER == 1941 + #define HOTSPOT_BUILD_COMPILER "MS VC++ 17.11 (VS2022)" + #elif _MSC_VER == 1942 + #define HOTSPOT_BUILD_COMPILER "MS VC++ 17.12 (VS2022)" + #elif _MSC_VER == 1943 + #define HOTSPOT_BUILD_COMPILER "MS VC++ 17.13 (VS2022)" + #elif _MSC_VER == 1944 + #define HOTSPOT_BUILD_COMPILER "MS VC++ 17.14 (VS2022)" #else #define HOTSPOT_BUILD_COMPILER "unknown MS VC++:" XSTR(_MSC_VER) #endif diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index c6acbbc39f57d..02e31a2454d93 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -339,7 +339,6 @@ static bool matches_property_suffix(const char* option, const char* property, si // any of the reserved module properties. // property should be passed without the leading "-D". bool Arguments::is_internal_module_property(const char* property) { - assert((strncmp(property, "-D", 2) != 0), "Unexpected leading -D"); if (strncmp(property, MODULE_PROPERTY_PREFIX, MODULE_PROPERTY_PREFIX_LEN) == 0) { const char* property_suffix = property + MODULE_PROPERTY_PREFIX_LEN; if (matches_property_suffix(property_suffix, ADDEXPORTS, ADDEXPORTS_LEN) || diff --git a/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.cpp b/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.cpp index 5b09758e08951..f42cc0011d602 100644 --- a/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.cpp +++ b/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.cpp @@ -161,3 +161,13 @@ JVMFlag::Error NUMAInterleaveGranularityConstraintFunc(size_t value, bool verbos return JVMFlag::SUCCESS; } + +JVMFlag::Error LargePageSizeInBytesConstraintFunc(size_t value, bool verbose) { + if (!is_power_of_2(value)) { + JVMFlag::printError(verbose, "LargePageSizeInBytes ( %zu ) must be " + "a power of 2\n", + value); + return JVMFlag::VIOLATES_CONSTRAINT; + } + return JVMFlag::SUCCESS; +} diff --git a/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.hpp b/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.hpp index 8bc3a9a154843..f22f421e80c53 100644 --- a/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.hpp +++ b/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.hpp @@ -42,7 +42,8 @@ f(intx, BiasedLockingDecayTimeFunc) \ f(intx, PerfDataSamplingIntervalFunc) \ f(uintx, VMPageSizeConstraintFunc) \ - f(size_t, NUMAInterleaveGranularityConstraintFunc) + f(size_t, NUMAInterleaveGranularityConstraintFunc) \ + f(size_t, LargePageSizeInBytesConstraintFunc) RUNTIME_CONSTRAINTS(DECLARE_CONSTRAINT) diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index aac0dc88482bf..a3d32f594042e 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -241,8 +241,10 @@ const intx ObjectAlignmentInBytes = 8; \ product(size_t, LargePageSizeInBytes, 0, \ "Maximum large page size used (0 will use the default large " \ - "page size for the environment as the maximum)") \ + "page size for the environment as the maximum) " \ + "(must be a power of 2)") \ range(0, max_uintx) \ + constraint(LargePageSizeInBytesConstraintFunc, AtParse) \ \ product(size_t, LargePageHeapSizeThreshold, 128*M, \ "Use large pages if maximum heap is at least this big") \ diff --git a/src/hotspot/share/runtime/interfaceSupport.cpp b/src/hotspot/share/runtime/interfaceSupport.cpp index cfdc6e2a717e7..db02febef4870 100644 --- a/src/hotspot/share/runtime/interfaceSupport.cpp +++ b/src/hotspot/share/runtime/interfaceSupport.cpp @@ -78,7 +78,7 @@ VMNativeEntryWrapper::~VMNativeEntryWrapper() { unsigned int InterfaceSupport::_scavenge_alot_counter = 1; unsigned int InterfaceSupport::_fullgc_alot_counter = 1; -int InterfaceSupport::_fullgc_alot_invocation = 0; +intx InterfaceSupport::_fullgc_alot_invocation = 0; void InterfaceSupport::gc_alot() { Thread *thread = Thread::current(); diff --git a/src/hotspot/share/runtime/interfaceSupport.inline.hpp b/src/hotspot/share/runtime/interfaceSupport.inline.hpp index 3cae644d63a56..116eb6c3a154e 100644 --- a/src/hotspot/share/runtime/interfaceSupport.inline.hpp +++ b/src/hotspot/share/runtime/interfaceSupport.inline.hpp @@ -54,7 +54,7 @@ class InterfaceSupport: AllStatic { public: static unsigned int _scavenge_alot_counter; static unsigned int _fullgc_alot_counter; - static int _fullgc_alot_invocation; + static intx _fullgc_alot_invocation; // Helper methods used to implement +ScavengeALot and +FullGCALot static void check_gc_alot() { if (ScavengeALot || FullGCALot) gc_alot(); } diff --git a/src/hotspot/share/runtime/jniHandles.cpp b/src/hotspot/share/runtime/jniHandles.cpp index 183dbbe756099..f6f85e8b8318b 100644 --- a/src/hotspot/share/runtime/jniHandles.cpp +++ b/src/hotspot/share/runtime/jniHandles.cpp @@ -385,7 +385,6 @@ JNIHandleBlock* JNIHandleBlock::allocate_block(Thread* thread, AllocFailType all block->_top = 0; block->_next = NULL; block->_pop_frame_link = NULL; - block->_planned_capacity = block_size_in_oops; // _last, _free_list & _allocate_before_rebuild initialized in allocate_handle debug_only(block->_last = NULL); debug_only(block->_free_list = NULL); @@ -591,22 +590,6 @@ size_t JNIHandleBlock::length() const { return result; } -class CountJNIHandleClosure: public OopClosure { -private: - int _count; -public: - CountJNIHandleClosure(): _count(0) {} - virtual void do_oop(oop* ooph) { _count++; } - virtual void do_oop(narrowOop* unused) { ShouldNotReachHere(); } - int count() { return _count; } -}; - -const size_t JNIHandleBlock::get_number_of_live_handles() { - CountJNIHandleClosure counter; - oops_do(&counter); - return counter.count(); -} - // This method is not thread-safe, i.e., must be called while holding a lock on the // structure. size_t JNIHandleBlock::memory_usage() const { diff --git a/src/hotspot/share/runtime/jniHandles.hpp b/src/hotspot/share/runtime/jniHandles.hpp index d64caa58c6055..880ca545e25bc 100644 --- a/src/hotspot/share/runtime/jniHandles.hpp +++ b/src/hotspot/share/runtime/jniHandles.hpp @@ -154,9 +154,6 @@ class JNIHandleBlock : public CHeapObj { uintptr_t* _free_list; // Handle free list int _allocate_before_rebuild; // Number of blocks to allocate before rebuilding free list - // Check JNI, "planned capacity" for current frame (or push/ensure) - size_t _planned_capacity; - #ifndef PRODUCT JNIHandleBlock* _block_list_link; // Link for list below static JNIHandleBlock* _block_list; // List of all allocated blocks (for debugging only) @@ -193,11 +190,6 @@ class JNIHandleBlock : public CHeapObj { // Traversal of handles void oops_do(OopClosure* f); - // Checked JNI support - void set_planned_capacity(size_t planned_capacity) { _planned_capacity = planned_capacity; } - const size_t get_planned_capacity() { return _planned_capacity; } - const size_t get_number_of_live_handles(); - // Debugging bool chain_contains(jobject handle) const; // Does this block or following blocks contain handle bool contains(jobject handle) const; // Does this block contain handle diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index 9af4b513a9994..e4ff6fa9df425 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -2945,7 +2945,8 @@ AdapterHandlerEntry* AdapterHandlerLibrary::create_adapter(AdapterBlob*& new_ada if (Verbose || PrintStubCode) { address first_pc = entry->base_address(); if (first_pc != NULL) { - Disassembler::decode(first_pc, first_pc + insts_size); + Disassembler::decode(first_pc, first_pc + insts_size, tty + NOT_PRODUCT(COMMA &new_adapter->asm_remarks())); tty->cr(); } } diff --git a/src/hotspot/share/runtime/stubCodeGenerator.cpp b/src/hotspot/share/runtime/stubCodeGenerator.cpp index 965c8a9e575ec..fb546bc8ebee7 100644 --- a/src/hotspot/share/runtime/stubCodeGenerator.cpp +++ b/src/hotspot/share/runtime/stubCodeGenerator.cpp @@ -78,7 +78,8 @@ StubCodeGenerator::~StubCodeGenerator() { CodeBuffer* cbuf = _masm->code(); CodeBlob* blob = CodeCache::find_blob_unsafe(cbuf->insts()->start()); if (blob != NULL) { - blob->set_strings(cbuf->strings()); + blob->use_remarks(cbuf->asm_remarks()); + blob->use_strings(cbuf->dbg_strings()); } #endif } @@ -90,15 +91,15 @@ void StubCodeGenerator::stub_prolog(StubCodeDesc* cdesc) { void StubCodeGenerator::stub_epilog(StubCodeDesc* cdesc) { if (_print_code) { #ifndef PRODUCT - // Find the code strings in the outer CodeBuffer. - CodeBuffer *outer_cbuf = _masm->code_section()->outer(); - CodeStrings* cs = &outer_cbuf->strings(); + // Find the assembly code remarks in the outer CodeBuffer. + AsmRemarks* remarks = &_masm->code_section()->outer()->asm_remarks(); #endif ttyLocker ttyl; tty->print_cr("- - - [BEGIN] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); cdesc->print_on(tty); tty->cr(); - Disassembler::decode(cdesc->begin(), cdesc->end(), tty NOT_PRODUCT(COMMA cs)); + Disassembler::decode(cdesc->begin(), cdesc->end(), tty + NOT_PRODUCT(COMMA remarks COMMA cdesc->disp())); tty->print_cr("- - - [END] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); tty->cr(); } @@ -119,6 +120,11 @@ StubCodeMark::~StubCodeMark() { _cgen->assembler()->flush(); _cdesc->set_end(_cgen->assembler()->pc()); assert(StubCodeDesc::_list == _cdesc, "expected order on list"); +#ifndef PRODUCT + address base = _cgen->assembler()->code_section()->outer()->insts_begin(); + address head = _cdesc->begin(); + _cdesc->set_disp(uint(head - base)); +#endif _cgen->stub_epilog(_cdesc); Forte::register_stub(_cdesc->name(), _cdesc->begin(), _cdesc->end()); diff --git a/src/hotspot/share/runtime/stubCodeGenerator.hpp b/src/hotspot/share/runtime/stubCodeGenerator.hpp index dae1eccc6f9ac..061e2d0b71df6 100644 --- a/src/hotspot/share/runtime/stubCodeGenerator.hpp +++ b/src/hotspot/share/runtime/stubCodeGenerator.hpp @@ -38,19 +38,18 @@ class StubCodeDesc: public CHeapObj { private: - static StubCodeDesc* _list; // the list of all descriptors - static bool _frozen; // determines whether _list modifications are allowed + static StubCodeDesc* _list; // the list of all descriptors + static bool _frozen; // determines whether _list modifications are allowed - StubCodeDesc* _next; // the next element in the linked list - const char* _group; // the group to which the stub code belongs - const char* _name; // the name assigned to the stub code - address _begin; // points to the first byte of the stub code (included) - address _end; // points to the first byte after the stub code (excluded) + StubCodeDesc* _next; // the next element in the linked list + const char* _group; // the group to which the stub code belongs + const char* _name; // the name assigned to the stub code + address _begin; // points to the first byte of the stub code (included) + address _end; // points to the first byte after the stub code (excluded) + uint _disp; // Displacement relative base address in buffer. - void set_end(address end) { - assert(_begin <= end, "begin & end not properly ordered"); - _end = end; - } + friend class StubCodeMark; + friend class StubCodeGenerator; void set_begin(address begin) { assert(begin >= _begin, "begin may not decrease"); @@ -58,8 +57,12 @@ class StubCodeDesc: public CHeapObj { _begin = begin; } - friend class StubCodeMark; - friend class StubCodeGenerator; + void set_end(address end) { + assert(_begin <= end, "begin & end not properly ordered"); + _end = end; + } + + void set_disp(uint disp) { _disp = disp; } public: static StubCodeDesc* first() { return _list; } @@ -76,6 +79,7 @@ class StubCodeDesc: public CHeapObj { _name = name; _begin = begin; _end = end; + _disp = 0; _list = this; }; @@ -85,6 +89,7 @@ class StubCodeDesc: public CHeapObj { const char* name() const { return _name; } address begin() const { return _begin; } address end() const { return _end; } + uint disp() const { return _disp; } int size_in_bytes() const { return _end - _begin; } bool contains(address pc) const { return _begin <= pc && pc < _end; } void print_on(outputStream* st) const; diff --git a/src/hotspot/share/runtime/sweeper.cpp b/src/hotspot/share/runtime/sweeper.cpp index 93fac19effee3..e0674de5f7add 100644 --- a/src/hotspot/share/runtime/sweeper.cpp +++ b/src/hotspot/share/runtime/sweeper.cpp @@ -55,9 +55,9 @@ // Sweeper logging code class SweeperRecord { public: - int traversal; + int64_t traversal; int compile_id; - long traversal_mark; + int64_t traversal_mark; int state; const char* kind; address vep; @@ -65,8 +65,8 @@ class SweeperRecord { int line; void print() { - tty->print_cr("traversal = %d compile_id = %d %s uep = " PTR_FORMAT " vep = " - PTR_FORMAT " state = %d traversal_mark %ld line = %d", + tty->print_cr("traversal = " INT64_FORMAT " compile_id = %d %s uep = " PTR_FORMAT " vep = " + PTR_FORMAT " state = %d traversal_mark " INT64_FORMAT " line = %d", traversal, compile_id, kind == NULL ? "" : kind, @@ -107,8 +107,8 @@ void NMethodSweeper::init_sweeper_log() { #endif CompiledMethodIterator NMethodSweeper::_current(CompiledMethodIterator::all_blobs); // Current compiled method -long NMethodSweeper::_traversals = 0; // Stack scan count, also sweep ID. -long NMethodSweeper::_total_nof_code_cache_sweeps = 0; // Total number of full sweeps of the code cache +int64_t NMethodSweeper::_traversals = 0; // Stack scan count, also sweep ID. +int64_t NMethodSweeper::_total_nof_code_cache_sweeps = 0; // Total number of full sweeps of the code cache int NMethodSweeper::_seen = 0; // Nof. nmethod we have currently processed in current pass of CodeCache size_t NMethodSweeper::_sweep_threshold_bytes = 0; // Threshold for when to sweep. Updated after ergonomics @@ -119,8 +119,8 @@ volatile size_t NMethodSweeper::_bytes_changed = 0; // Counts the tot // 2) not_entrant -> zombie int NMethodSweeper::_hotness_counter_reset_val = 0; -long NMethodSweeper::_total_nof_methods_reclaimed = 0; // Accumulated nof methods flushed -long NMethodSweeper::_total_nof_c2_methods_reclaimed = 0; // Accumulated nof methods flushed +int64_t NMethodSweeper::_total_nof_methods_reclaimed = 0; // Accumulated nof methods flushed +int64_t NMethodSweeper::_total_nof_c2_methods_reclaimed = 0; // Accumulated nof methods flushed size_t NMethodSweeper::_total_flushed_size = 0; // Total number of bytes flushed from the code cache Tickspan NMethodSweeper::_total_time_sweeping; // Accumulated time sweeping Tickspan NMethodSweeper::_total_time_this_sweep; // Total time this sweep @@ -187,7 +187,7 @@ CodeBlobClosure* NMethodSweeper::prepare_mark_active_nmethods() { _total_time_this_sweep = Tickspan(); if (PrintMethodFlushing) { - tty->print_cr("### Sweep: stack traversal %ld", _traversals); + tty->print_cr("### Sweep: stack traversal " INT64_FORMAT, _traversals); } return &mark_activation_closure; } @@ -217,7 +217,7 @@ void NMethodSweeper::sweeper_loop() { { ThreadBlockInVM tbivm(JavaThread::current()); MonitorLocker waiter(CodeSweeper_lock, Mutex::_no_safepoint_check_flag); - const long wait_time = 60*60*24 * 1000; + const int64_t wait_time = 60*60*24 * 1000; timeout = waiter.wait(wait_time); } if (!timeout && (_should_sweep || _force_sweep)) { @@ -649,7 +649,7 @@ void NMethodSweeper::log_sweep(const char* msg, const char* format, ...) { CodeCache::log_state(&s); ttyLocker ttyl; - xtty->begin_elem("sweeper state='%s' traversals='" INTX_FORMAT "' ", msg, (intx)traversal_count()); + xtty->begin_elem("sweeper state='%s' traversals='" INT64_FORMAT "' ", msg, traversal_count()); if (format != NULL) { va_list ap; va_start(ap, format); @@ -667,8 +667,9 @@ void NMethodSweeper::print(outputStream* out) { out = (out == NULL) ? tty : out; out->print_cr("Code cache sweeper statistics:"); out->print_cr(" Total sweep time: %1.0lf ms", (double)_total_time_sweeping.value()/1000000); - out->print_cr(" Total number of full sweeps: %ld", _total_nof_code_cache_sweeps); - out->print_cr(" Total number of flushed methods: %ld (thereof %ld C2 methods)", _total_nof_methods_reclaimed, + out->print_cr(" Total number of full sweeps: " INT64_FORMAT, _total_nof_code_cache_sweeps); + out->print_cr(" Total number of flushed methods: " INT64_FORMAT " (thereof " INT64_FORMAT " C2 methods)", + _total_nof_methods_reclaimed, _total_nof_c2_methods_reclaimed); out->print_cr(" Total size of flushed methods: " SIZE_FORMAT " kB", _total_flushed_size/K); } diff --git a/src/hotspot/share/runtime/sweeper.hpp b/src/hotspot/share/runtime/sweeper.hpp index 5f4e0ed7796d7..06daf37ee3a7e 100644 --- a/src/hotspot/share/runtime/sweeper.hpp +++ b/src/hotspot/share/runtime/sweeper.hpp @@ -66,8 +66,8 @@ class NMethodSweeper : public AllStatic { MadeZombie, Flushed }; - static long _traversals; // Stack scan count, also sweep ID. - static long _total_nof_code_cache_sweeps; // Total number of full sweeps of the code cache + static int64_t _traversals; // Stack scan count, also sweep ID. + static int64_t _total_nof_code_cache_sweeps; // Total number of full sweeps of the code cache static CompiledMethodIterator _current; // Current compiled method static int _seen; // Nof. nmethod we have currently processed in current pass of CodeCache static size_t _sweep_threshold_bytes; // The threshold for when to invoke sweeps @@ -78,8 +78,8 @@ class NMethodSweeper : public AllStatic { // 1) alive -> not_entrant // 2) not_entrant -> zombie // Stat counters - static long _total_nof_methods_reclaimed; // Accumulated nof methods flushed - static long _total_nof_c2_methods_reclaimed; // Accumulated nof C2-compiled methods flushed + static int64_t _total_nof_methods_reclaimed; // Accumulated nof methods flushed + static int64_t _total_nof_c2_methods_reclaimed; // Accumulated nof C2-compiled methods flushed static size_t _total_flushed_size; // Total size of flushed methods static int _hotness_counter_reset_val; @@ -97,10 +97,10 @@ class NMethodSweeper : public AllStatic { static void do_stack_scanning(); static void sweep(); public: - static long traversal_count() { return _traversals; } + static int64_t traversal_count() { return _traversals; } static size_t sweep_threshold_bytes() { return _sweep_threshold_bytes; } static void set_sweep_threshold_bytes(size_t threshold) { _sweep_threshold_bytes = threshold; } - static int total_nof_methods_reclaimed() { return _total_nof_methods_reclaimed; } + static int64_t total_nof_methods_reclaimed() { return _total_nof_methods_reclaimed; } static const Tickspan total_time_sweeping() { return _total_time_sweeping; } static const Tickspan peak_sweep_time() { return _peak_sweep_time; } static const Tickspan peak_sweep_fraction_time() { return _peak_sweep_fraction_time; } diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp index 3905336d40f57..8e89abcd3440c 100644 --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.cpp @@ -1008,7 +1008,6 @@ void JavaThread::check_for_valid_safepoint_state() { JavaThread::JavaThread() : // Initialize fields - _in_asgct(false), _on_thread_list(false), DEBUG_ONLY(_java_call_counter(0) COMMA) _entry_point(nullptr), diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp index 9c949cb3b4157..65c634dd11407 100644 --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -715,7 +715,6 @@ class JavaThread: public Thread { friend class ThreadsSMRSupport; // to access _threadObj for exiting_threads_oops_do friend class HandshakeState; private: - bool _in_asgct; // Is set when this JavaThread is handling ASGCT call bool _on_thread_list; // Is set when this JavaThread is added to the Threads list OopHandle _threadObj; // The Java level thread object @@ -1640,10 +1639,6 @@ class JavaThread: public Thread { // Helper function to do vm_exit_on_initialization for osthread // resource allocation failure. static void vm_exit_on_osthread_failure(JavaThread* thread); - - // AsyncGetCallTrace support - inline bool in_asgct(void) {return _in_asgct;} - inline void set_in_asgct(bool value) {_in_asgct = value;} }; // Inline implementation of JavaThread::current diff --git a/src/hotspot/share/runtime/threadSMR.cpp b/src/hotspot/share/runtime/threadSMR.cpp index fc90da14ddcbd..ecf4cbaeec538 100644 --- a/src/hotspot/share/runtime/threadSMR.cpp +++ b/src/hotspot/share/runtime/threadSMR.cpp @@ -367,6 +367,7 @@ class ScanHazardPtrPrintMatchingThreadsClosure : public ThreadClosure { } }; +#ifdef ASSERT // Closure to validate hazard ptrs. // class ValidateHazardPtrsClosure : public ThreadClosure { @@ -387,6 +388,7 @@ class ValidateHazardPtrsClosure : public ThreadClosure { p2i(thread)); } }; +#endif // Closure to determine if the specified JavaThread is found by // threads_do(). @@ -928,8 +930,10 @@ void ThreadsSMRSupport::free_list(ThreadsList* threads) { log_debug(thread, smr)("tid=" UINTX_FORMAT ": ThreadsSMRSupport::free_list: threads=" INTPTR_FORMAT " is not freed.", os::current_thread_id(), p2i(threads)); } +#ifdef ASSERT ValidateHazardPtrsClosure validate_cl; threads_do(&validate_cl); +#endif delete scan_table; } diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 33de84a68c155..28dbd337eecba 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -687,7 +687,7 @@ typedef HashtableEntry KlassHashtableEntry; nonstatic_field(nmethod, _verified_entry_point, address) \ nonstatic_field(nmethod, _osr_entry_point, address) \ volatile_nonstatic_field(nmethod, _lock_count, jint) \ - volatile_nonstatic_field(nmethod, _stack_traversal_mark, long) \ + volatile_nonstatic_field(nmethod, _stack_traversal_mark, int64_t) \ nonstatic_field(nmethod, _compile_id, int) \ nonstatic_field(nmethod, _comp_level, int) \ \ @@ -1224,6 +1224,7 @@ typedef HashtableEntry KlassHashtableEntry; declare_integer_type(ssize_t) \ declare_integer_type(intx) \ declare_integer_type(intptr_t) \ + declare_integer_type(int64_t) \ declare_unsigned_integer_type(uintx) \ declare_unsigned_integer_type(uintptr_t) \ declare_unsigned_integer_type(uint8_t) \ diff --git a/src/hotspot/share/utilities/compilerWarnings.hpp b/src/hotspot/share/utilities/compilerWarnings.hpp index fb796859b96ef..acc3513aa4962 100644 --- a/src/hotspot/share/utilities/compilerWarnings.hpp +++ b/src/hotspot/share/utilities/compilerWarnings.hpp @@ -74,4 +74,8 @@ #define PRAGMA_INFINITE_RECURSION_IGNORED #endif +#ifndef PRAGMA_NONNULL_IGNORED +#define PRAGMA_NONNULL_IGNORED +#endif + #endif // SHARE_UTILITIES_COMPILERWARNINGS_HPP diff --git a/src/hotspot/share/utilities/compilerWarnings_gcc.hpp b/src/hotspot/share/utilities/compilerWarnings_gcc.hpp index ecc13dc2b0739..5e0f264bfd2ec 100644 --- a/src/hotspot/share/utilities/compilerWarnings_gcc.hpp +++ b/src/hotspot/share/utilities/compilerWarnings_gcc.hpp @@ -58,6 +58,9 @@ #define PRAGMA_STRINGOP_TRUNCATION_IGNORED PRAGMA_DISABLE_GCC_WARNING("-Wstringop-truncation") #endif +#define PRAGMA_NONNULL_IGNORED \ + PRAGMA_DISABLE_GCC_WARNING("-Wnonnull") + #if defined(__clang_major__) && \ (__clang_major__ >= 4 || \ (__clang_major__ >= 3 && __clang_minor__ >= 1)) || \ diff --git a/src/hotspot/share/utilities/exceptions.cpp b/src/hotspot/share/utilities/exceptions.cpp index cf9291f6dcf52..499542235b9e0 100644 --- a/src/hotspot/share/utilities/exceptions.cpp +++ b/src/hotspot/share/utilities/exceptions.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -440,6 +440,7 @@ void Exceptions::wrap_dynamic_exception(bool is_indy, JavaThread* THREAD) { // Pass through an Error, including BootstrapMethodError, any other form // of linkage error, or say ThreadDeath/OutOfMemoryError if (ls != NULL) { + ResourceMark rm(THREAD); ls->print_cr("bootstrap method invocation wraps BSME around " INTPTR_FORMAT, p2i((void *)exception)); exception->print_on(ls); } @@ -448,6 +449,7 @@ void Exceptions::wrap_dynamic_exception(bool is_indy, JavaThread* THREAD) { // Otherwise wrap the exception in a BootstrapMethodError if (ls != NULL) { + ResourceMark rm(THREAD); ls->print_cr("%s throws BSME for " INTPTR_FORMAT, is_indy ? "invokedynamic" : "dynamic constant", p2i((void *)exception)); exception->print_on(ls); } diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index 98d7fbc674c82..d5840b0c001ff 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -1064,7 +1064,7 @@ inline bool is_even(intx x) { return !is_odd(x); } // abs methods which cannot overflow and so are well-defined across // the entire domain of integer types. -static inline unsigned int uabs(unsigned int n) { +static inline unsigned int g_uabs(unsigned int n) { union { unsigned int result; int value; @@ -1073,7 +1073,7 @@ static inline unsigned int uabs(unsigned int n) { if (value < 0) result = 0-result; return result; } -static inline julong uabs(julong n) { +static inline julong g_uabs(julong n) { union { julong result; jlong value; @@ -1082,8 +1082,8 @@ static inline julong uabs(julong n) { if (value < 0) result = 0-result; return result; } -static inline julong uabs(jlong n) { return uabs((julong)n); } -static inline unsigned int uabs(int n) { return uabs((unsigned int)n); } +static inline julong g_uabs(jlong n) { return g_uabs((julong)n); } +static inline unsigned int g_uabs(int n) { return g_uabs((unsigned int)n); } // "to" should be greater than "from." inline intx byte_size(void* from, void* to) { diff --git a/src/hotspot/share/utilities/ostream.cpp b/src/hotspot/share/utilities/ostream.cpp index 4d7af94d0c369..4156bdc88fdc1 100644 --- a/src/hotspot/share/utilities/ostream.cpp +++ b/src/hotspot/share/utilities/ostream.cpp @@ -354,6 +354,7 @@ void stringStream::grow(size_t new_capacity) { } void stringStream::write(const char* s, size_t len) { + assert(_is_frozen == false, "Modification forbidden"); assert(_capacity >= _written + 1, "Sanity"); if (len == 0) { return; @@ -393,6 +394,7 @@ void stringStream::zero_terminate() { } void stringStream::reset() { + assert(_is_frozen == false, "Modification forbidden"); _written = 0; _precount = 0; _position = 0; _newlines = 0; zero_terminate(); diff --git a/src/hotspot/share/utilities/ostream.hpp b/src/hotspot/share/utilities/ostream.hpp index d37b1bc3e8408..7fcfb8fe2f56f 100644 --- a/src/hotspot/share/utilities/ostream.hpp +++ b/src/hotspot/share/utilities/ostream.hpp @@ -28,6 +28,7 @@ #include "memory/allocation.hpp" #include "runtime/timer.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/macros.hpp" DEBUG_ONLY(class ResourceMark;) @@ -191,6 +192,7 @@ class ttyUnlocker: StackObj { // for writing to strings; buffer will expand automatically. // Buffer will always be zero-terminated. class stringStream : public outputStream { + DEBUG_ONLY(bool _is_frozen = false); char* _buffer; size_t _written; // Number of characters written, excluding termin. zero size_t _capacity; @@ -215,9 +217,18 @@ class stringStream : public outputStream { // Return number of characters written into buffer, excluding terminating zero and // subject to truncation in static buffer mode. size_t size() const { return _written; } + // Returns internal buffer containing the accumulated string. + // Returned buffer is only guaranteed to be valid as long as stream is not modified const char* base() const { return _buffer; } + // Freezes stringStream (no further modifications possible) and returns pointer to it. + // No-op if stream is frozen already. + // Returns the internal buffer containing the accumulated string. + const char* freeze() NOT_DEBUG(const) { + DEBUG_ONLY(_is_frozen = true); + return _buffer; + }; void reset(); - // copy to a resource, or C-heap, array as requested + // Copy to a resource, or C-heap, array as requested char* as_string(bool c_heap = false) const; }; diff --git a/src/java.base/share/classes/com/sun/crypto/provider/RSACipher.java b/src/java.base/share/classes/com/sun/crypto/provider/RSACipher.java index b9438dfb30944..4c4f9a30254a3 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/RSACipher.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/RSACipher.java @@ -385,7 +385,7 @@ private byte[] doFinal() throws BadPaddingException, byte[] decryptBuffer = RSACore.convert(buffer, 0, bufOfs); paddingCopy = RSACore.rsa(decryptBuffer, privateKey, false); result = padding.unpad(paddingCopy); - if (result == null && !forTlsPremasterSecret) { + if (!forTlsPremasterSecret && result == null) { throw new BadPaddingException ("Padding error in decryption"); } @@ -405,6 +405,34 @@ private byte[] doFinal() throws BadPaddingException, } } + // TLS master secret decode version of the doFinal() method. + private byte[] doFinalForTls(int clientVersion, int serverVersion) + throws BadPaddingException, IllegalBlockSizeException { + if (bufOfs > buffer.length) { + throw new IllegalBlockSizeException("Data must not be longer " + + "than " + buffer.length + " bytes"); + } + byte[] paddingCopy = null; + byte[] result = null; + try { + byte[] decryptBuffer = RSACore.convert(buffer, 0, bufOfs); + + paddingCopy = RSACore.rsa(decryptBuffer, privateKey, false); + result = padding.unpadForTls(paddingCopy, clientVersion, + serverVersion); + + return result; + } finally { + Arrays.fill(buffer, 0, bufOfs, (byte)0); + bufOfs = 0; + if (paddingCopy != null + && paddingCopy != buffer // already cleaned + && paddingCopy != result) { // DO NOT CLEAN, THIS IS RESULT + Arrays.fill(paddingCopy, (byte)0); + } + } + } + // see JCE spec protected byte[] engineUpdate(byte[] in, int inOfs, int inLen) { update(in, inOfs, inLen); @@ -477,38 +505,34 @@ protected Key engineUnwrap(byte[] wrappedKey, String algorithm, byte[] encoded = null; update(wrappedKey, 0, wrappedKey.length); - try { - encoded = doFinal(); - } catch (BadPaddingException | IllegalBlockSizeException e) { - // BadPaddingException cannot happen for TLS RSA unwrap. - // In that case, padding error is indicated by returning null. - // IllegalBlockSizeException cannot happen in any case, - // because of the length check above. - throw new InvalidKeyException("Unwrapping failed", e); - } - try { if (isTlsRsaPremasterSecret) { if (!forTlsPremasterSecret) { throw new IllegalStateException( "No TlsRsaPremasterSecretParameterSpec specified"); } - - // polish the TLS premaster secret - encoded = KeyUtil.checkTlsPreMasterSecretKey( - ((TlsRsaPremasterSecretParameterSpec) spec).getClientVersion(), - ((TlsRsaPremasterSecretParameterSpec) spec).getServerVersion(), - random, encoded, encoded == null); + TlsRsaPremasterSecretParameterSpec parameterSpec = + (TlsRsaPremasterSecretParameterSpec) spec; + encoded = doFinalForTls(parameterSpec.getClientVersion(), + parameterSpec.getServerVersion()); + } else { + encoded = doFinal(); } - return ConstructKeys.constructKey(encoded, algorithm, type); + + } catch (BadPaddingException | IllegalBlockSizeException e) { + // BadPaddingException cannot happen for TLS RSA unwrap. + // Neither padding error nor server version error is indicated + // for TLS, but a fake unwrapped value is returned. + // IllegalBlockSizeException cannot happen in any case, + // because of the length check above. + throw new InvalidKeyException("Unwrapping failed", e); } finally { if (encoded != null) { Arrays.fill(encoded, (byte) 0); } } } - // see JCE spec protected int engineGetKeySize(Key key) throws InvalidKeyException { RSAKey rsaKey = RSAKeyFactory.toRSAKey(key); diff --git a/src/java.base/share/classes/java/io/ByteArrayInputStream.java b/src/java.base/share/classes/java/io/ByteArrayInputStream.java index 39401d6d45243..e9b1415205b08 100644 --- a/src/java.base/share/classes/java/io/ByteArrayInputStream.java +++ b/src/java.base/share/classes/java/io/ByteArrayInputStream.java @@ -44,6 +44,7 @@ * @since 1.0 */ public class ByteArrayInputStream extends InputStream { + private static final int MAX_TRANSFER_SIZE = 128*1024; /** * An array of bytes that was provided @@ -205,8 +206,16 @@ public int readNBytes(byte[] b, int off, int len) { public synchronized long transferTo(OutputStream out) throws IOException { int len = count - pos; - out.write(buf, pos, len); - pos = count; + if (len > 0) { + int nwritten = 0; + while (nwritten < len) { + int nbyte = Integer.min(len - nwritten, MAX_TRANSFER_SIZE); + out.write(buf, pos, nbyte); + pos += nbyte; + nwritten += nbyte; + } + assert pos == count; + } return len; } diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java index 61cbc42243c78..aceba5dd9f09a 100644 --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -53,7 +53,9 @@ import java.lang.constant.Constable; import java.net.URL; import java.security.AccessController; +import java.security.Permissions; import java.security.PrivilegedAction; +import java.security.ProtectionDomain; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -2966,10 +2968,6 @@ private boolean isOpenToCaller(String name, Class caller) { return true; } - - /** protection domain returned when the internal domain is null */ - private static java.security.ProtectionDomain allPermDomain; - /** * Returns the {@code ProtectionDomain} of this class. If there is a * security manager installed, this method first calls the security @@ -2990,7 +2988,7 @@ private boolean isOpenToCaller(String name, Class caller) { * @see java.lang.RuntimePermission * @since 1.2 */ - public java.security.ProtectionDomain getProtectionDomain() { + public ProtectionDomain getProtectionDomain() { @SuppressWarnings("removal") SecurityManager sm = System.getSecurityManager(); if (sm != null) { @@ -2999,26 +2997,30 @@ public java.security.ProtectionDomain getProtectionDomain() { return protectionDomain(); } + /** Holder for the protection domain returned when the internal domain is null */ + private static class Holder { + private static final ProtectionDomain allPermDomain; + static { + Permissions perms = new Permissions(); + perms.add(SecurityConstants.ALL_PERMISSION); + allPermDomain = new ProtectionDomain(null, perms); + } + } + // package-private - java.security.ProtectionDomain protectionDomain() { - java.security.ProtectionDomain pd = getProtectionDomain0(); + ProtectionDomain protectionDomain() { + ProtectionDomain pd = getProtectionDomain0(); if (pd == null) { - if (allPermDomain == null) { - java.security.Permissions perms = - new java.security.Permissions(); - perms.add(SecurityConstants.ALL_PERMISSION); - allPermDomain = - new java.security.ProtectionDomain(null, perms); - } - pd = allPermDomain; + return Holder.allPermDomain; + } else { + return pd; } - return pd; } /** * Returns the ProtectionDomain of this class. */ - private native java.security.ProtectionDomain getProtectionDomain0(); + private native ProtectionDomain getProtectionDomain0(); /* * Return the Virtual Machine's Class object for the named diff --git a/src/java.base/share/classes/java/math/BigDecimal.java b/src/java.base/share/classes/java/math/BigDecimal.java index 73138d5f3639b..22cfc937c711b 100644 --- a/src/java.base/share/classes/java/math/BigDecimal.java +++ b/src/java.base/share/classes/java/math/BigDecimal.java @@ -3473,21 +3473,21 @@ public String toPlainString() { return "0"; } int trailingZeros = checkScaleNonZero((-(long)scale)); - StringBuilder buf; - if(intCompact!=INFLATED) { - buf = new StringBuilder(20+trailingZeros); - buf.append(intCompact); - } else { - String str = intVal.toString(); - buf = new StringBuilder(str.length()+trailingZeros); - buf.append(str); + String str = intCompact != INFLATED + ? Long.toString(intCompact) + : intVal.toString(); + int len = str.length() + trailingZeros; + if (len < 0) { + throw new OutOfMemoryError("too large to fit in a String"); } - for (int i = 0; i < trailingZeros; i++) { + StringBuilder buf = new StringBuilder(len); + buf.append(str); + for (; trailingZeros>0; trailingZeros--) { buf.append('0'); } return buf.toString(); } - String str ; + String str; if(intCompact!=INFLATED) { str = Long.toString(Math.abs(intCompact)); } else { @@ -3497,11 +3497,11 @@ public String toPlainString() { } /* Returns a digit.digit string */ - private String getValueString(int signum, String intString, int scale) { + private static String getValueString(int signum, String intString, int scale) { /* Insert decimal point */ StringBuilder buf; int insertionPoint = intString.length() - scale; - if (insertionPoint == 0) { /* Point goes right before intVal */ + if (insertionPoint == 0) { /* Point goes just before intVal */ return (signum<0 ? "-0." : "0.") + intString; } else if (insertionPoint > 0) { /* Point goes inside intVal */ buf = new StringBuilder(intString); @@ -3509,9 +3509,13 @@ private String getValueString(int signum, String intString, int scale) { if (signum < 0) buf.insert(0, '-'); } else { /* We must insert zeros between point and intVal */ - buf = new StringBuilder(3-insertionPoint + intString.length()); + int len = (signum < 0 ? 3 : 2) + scale; + if (len < 0) { + throw new OutOfMemoryError("too large to fit in a String"); + } + buf = new StringBuilder(len); buf.append(signum<0 ? "-0." : "0."); - for (int i=0; i<-insertionPoint; i++) { + for (; insertionPoint<0; insertionPoint++) { // insertionPoint != MIN_VALUE buf.append('0'); } buf.append(intString); diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index 649aac444d66c..d42b822480636 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -312,12 +312,18 @@ public BigInteger(byte[] val, int off, int len) { throw new NumberFormatException("Zero length BigInteger"); } Objects.checkFromIndexSize(off, len, val.length); + if (len == 0) { + mag = ZERO.mag; + signum = ZERO.signum; + return; + } - if (val[off] < 0) { - mag = makePositive(val, off, len); + int b = val[off]; + if (b < 0) { + mag = makePositive(b, val, off, len); signum = -1; } else { - mag = stripLeadingZeroBytes(val, off, len); + mag = stripLeadingZeroBytes(b, val, off, len); signum = (mag.length == 0 ? 0 : 1); } if (mag.length >= MAX_MAG_LENGTH) { @@ -4438,77 +4444,168 @@ private static int[] trustedStripLeadingZeroInts(int val[]) { return keep == 0 ? val : java.util.Arrays.copyOfRange(val, keep, vlen); } - /** + private static int[] stripLeadingZeroBytes(byte[] a, int from, int len) { + return stripLeadingZeroBytes(Integer.MIN_VALUE, a, from, len); + } + + /* * Returns a copy of the input array stripped of any leading zero bytes. + * The returned array is either empty, or its 0-th element is non-zero, + * meeting the "minimal" requirement for field mag (see comment on mag). + * + * The range [from, from + len) must be well-formed w.r.t. array a. + * + * b < -128 means that a[from] has not yet been read. + * Otherwise, b must be a[from], have been read only once before invoking + * this method, and len > 0 must hold. */ - private static int[] stripLeadingZeroBytes(byte a[], int off, int len) { - int indexBound = off + len; - int keep; - // Find first nonzero byte - for (keep = off; keep < indexBound && a[keep] == 0; keep++) - ; - - // Allocate new array and copy relevant part of input array - int intLength = ((indexBound - keep) + 3) >>> 2; - int[] result = new int[intLength]; - int b = indexBound - 1; - for (int i = intLength-1; i >= 0; i--) { - result[i] = a[b--] & 0xff; - int bytesRemaining = b - keep + 1; - int bytesToTransfer = Math.min(3, bytesRemaining); - for (int j=8; j <= (bytesToTransfer << 3); j += 8) - result[i] |= ((a[b--] & 0xff) << j); + private static int[] stripLeadingZeroBytes(int b, byte[] a, int from, int len) { + /* + * Except for the first byte, each read access to the input array a + * is of the form a[from++]. + * The index from is never otherwise altered, except right below, + * and only increases in steps of 1, always up to index to. + * Hence, each byte in the array is read exactly once, from lower to + * higher indices (from most to least significant byte). + */ + if (len == 0) { + return ZERO.mag; } - return result; + int to = from + len; + if (b < -128) { + b = a[from]; + } + /* Either way, a[from] has now been read exactly once, skip to next. */ + ++from; + /* + * Set up the shortest int[] for the sequence of the bytes + * b, a[from+1], ..., a[to-1] (len > 0) + * Shortest means first skipping leading zeros. + */ + for (; b == 0 && from < to; b = a[from++]) + ; //empty body + if (b == 0) { + /* Here, from == to as well. All bytes are zeros. */ + return ZERO.mag; + } + /* + * Allocate just enough ints to hold (to - from + 1) bytes, that is + * ((to - from + 1) + 3) / 4 = (to - from) / 4 + 1 + */ + int[] res = new int[((to - from) >> 2) + 1]; + /* + * A "digit" is a group of 4 adjacent bytes aligned w.r.t. index to. + * (Implied 0 bytes are prepended as needed.) + * b is the most significant byte not 0. + * Digit d0 spans the range of indices that includes current (from - 1). + */ + int d0 = b & 0xFF; + while (((to - from) & 0x3) != 0) { + d0 = d0 << 8 | a[from++] & 0xFF; + } + res[0] = d0; + /* + * Prepare the remaining digits. + * (to - from) is a multiple of 4, so prepare an int for every 4 bytes. + * This is a candidate for Unsafe.copy[Swap]Memory(). + */ + int i = 1; + while (from < to) { + res[i++] = a[from++] << 24 | (a[from++] & 0xFF) << 16 + | (a[from++] & 0xFF) << 8 | (a[from++] & 0xFF); + } + return res; } - /** + /* * Takes an array a representing a negative 2's-complement number and * returns the minimal (no leading zero bytes) unsigned whose value is -a. + * + * len > 0 must hold. + * The range [from, from + len) must be well-formed w.r.t. array a. + * b is assumed to be the result of reading a[from] and to meet b < 0. */ - private static int[] makePositive(byte a[], int off, int len) { - int keep, k; - int indexBound = off + len; - - // Find first non-sign (0xff) byte of input - for (keep=off; keep < indexBound && a[keep] == -1; keep++) - ; - - - /* Allocate output array. If all non-sign bytes are 0x00, we must - * allocate space for one extra output byte. */ - for (k=keep; k < indexBound && a[k] == 0; k++) - ; - - int extraByte = (k == indexBound) ? 1 : 0; - int intLength = ((indexBound - keep + extraByte) + 3) >>> 2; - int result[] = new int[intLength]; - - /* Copy one's complement of input into output, leaving extra - * byte (if it exists) == 0x00 */ - int b = indexBound - 1; - for (int i = intLength-1; i >= 0; i--) { - result[i] = a[b--] & 0xff; - int numBytesToTransfer = Math.min(3, b-keep+1); - if (numBytesToTransfer < 0) - numBytesToTransfer = 0; - for (int j=8; j <= 8*numBytesToTransfer; j += 8) - result[i] |= ((a[b--] & 0xff) << j); - - // Mask indicates which bits must be complemented - int mask = -1 >>> (8*(3-numBytesToTransfer)); - result[i] = ~result[i] & mask; + private static int[] makePositive(int b, byte[] a, int from, int len) { + /* + * By assumption, b == a[from] < 0 and len > 0. + * + * First collect the bytes into the resulting array res. + * Then convert res to two's complement. + * + * Except for b == a[from], each read access to the input array a + * is of the form a[from++]. + * The index from is never otherwise altered, except right below, + * and only increases in steps of 1, always up to index to. + * Hence, each byte in the array is read exactly once, from lower to + * higher indices (from most to least significant byte). + */ + int to = from + len; + /* b == a[from] has been read exactly once, skip to next index. */ + ++from; + /* Skip leading -1 bytes. */ + for (; b == -1 && from < to; b = a[from++]) + ; //empty body + /* + * A "digit" is a group of 4 adjacent bytes aligned w.r.t. index to. + * b is the most significant byte not -1, or -1 only if from == to. + * Digit d0 spans the range of indices that includes current (from - 1). + * (Implied -1 bytes are prepended to array a as needed.) + * It usually corresponds to res[0], except for the special case below. + */ + int d0 = -1 << 8 | b & 0xFF; + while (((to - from) & 0x3) != 0) { + d0 = d0 << 8 | (b = a[from++]) & 0xFF; + } + int f = from; // keeps the current from for sizing purposes later + /* Skip zeros adjacent to d0, if at all. */ + for (; b == 0 && from < to; b = a[from++]) + ; //empty body + /* + * b is the most significant non-zero byte at or after (f - 1), + * or 0 only if from == to. + * Digit d spans the range of indices that includes (f - 1). + */ + int d = b & 0xFF; + while (((to - from) & 0x3) != 0) { + d = d << 8 | a[from++] & 0xFF; } - - // Add one to one's complement to generate two's complement - for (int i=result.length-1; i >= 0; i--) { - result[i] = (int)((result[i] & LONG_MASK) + 1); - if (result[i] != 0) - break; + /* + * If the situation here is like this: + * index: f to == from + * ..., -1,-1, 0,0,0,0, 0,0,0,0, ..., 0,0,0,0 + * digit: d0 d + * then, as shown, the number of zeros is a positive multiple of 4. + * The array res needs a minimal length of (1 + 1 + (to - f) / 4) + * to accommodate the two's complement, including a leading 1. + * In any other case, there is at least one byte that is non-zero. + * The array for the two's complement has length (0 + 1 + (to - f) / 4). + * c is 1, resp., 0 for the two situations. + */ + int c = (to - from | d0 | d) == 0 ? 1 : 0; + int[] res = new int[c + 1 + ((to - f) >> 2)]; + res[0] = c == 0 ? d0 : -1; + int i = res.length - ((to - from) >> 2); + if (i > 1) { + res[i - 1] = d; } - - return result; + /* + * Prepare the remaining digits. + * (to - from) is a multiple of 4, so prepare an int for every 4 bytes. + * This is a candidate for Unsafe.copy[Swap]Memory(). + */ + while (from < to) { + res[i++] = a[from++] << 24 | (a[from++] & 0xFF) << 16 + | (a[from++] & 0xFF) << 8 | (a[from++] & 0xFF); + } + /* Convert to two's complement. Here, i == res.length */ + while (--i >= 0 && res[i] == 0) + ; // empty body + res[i] = -res[i]; + while (--i >= 0) { + res[i] = ~res[i]; + } + return res; } /** diff --git a/src/java.base/share/classes/java/net/InMemoryCookieStore.java b/src/java.base/share/classes/java/net/InMemoryCookieStore.java index 0ce3dc5b68c4e..1c72549e37d5c 100644 --- a/src/java.base/share/classes/java/net/InMemoryCookieStore.java +++ b/src/java.base/share/classes/java/net/InMemoryCookieStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,10 +25,6 @@ package java.net; -import java.net.URI; -import java.net.CookieStore; -import java.net.HttpCookie; -import java.net.URISyntaxException; import java.util.List; import java.util.Map; import java.util.ArrayList; @@ -72,6 +68,7 @@ public InMemoryCookieStore() { /** * Add one cookie into cookie store. */ + @Override public void add(URI uri, HttpCookie cookie) { // pre-condition : argument can't be null if (cookie == null) { @@ -109,6 +106,7 @@ public void add(URI uri, HttpCookie cookie) { * 3) not expired. * See RFC 2965 sec. 3.3.4 for more detail. */ + @Override public List get(URI uri) { // argument can't be null if (uri == null) { @@ -127,12 +125,13 @@ public List get(URI uri) { lock.unlock(); } - return cookies; + return Collections.unmodifiableList(cookies); } /** * Get all cookies in cookie store, except those have expired */ + @Override public List getCookies() { List rt; @@ -156,6 +155,7 @@ public List getCookies() { * Get all URIs, which are associated with at least one cookie * of this cookie store. */ + @Override public List getURIs() { List uris = new ArrayList<>(); @@ -165,7 +165,7 @@ public List getURIs() { while (it.hasNext()) { URI uri = it.next(); List cookies = uriIndex.get(uri); - if (cookies == null || cookies.size() == 0) { + if (cookies == null || cookies.isEmpty()) { // no cookies list or an empty list associated with // this uri entry, delete it it.remove(); @@ -176,13 +176,14 @@ public List getURIs() { lock.unlock(); } - return uris; + return Collections.unmodifiableList(uris); } /** * Remove a cookie from store */ + @Override public boolean remove(URI uri, HttpCookie ck) { // argument can't be null if (ck == null) { @@ -204,6 +205,7 @@ public boolean remove(URI uri, HttpCookie ck) { /** * Remove all cookies in this cookie store. */ + @Override public boolean removeAll() { lock.lock(); try { diff --git a/src/java.base/share/classes/java/security/Provider.java b/src/java.base/share/classes/java/security/Provider.java index 72ddb41257896..65781da8c50e7 100644 --- a/src/java.base/share/classes/java/security/Provider.java +++ b/src/java.base/share/classes/java/security/Provider.java @@ -1288,6 +1288,7 @@ public Service getService(String type, String algorithm) { s = legacyMap.get(key); if (s != null && !s.isValid()) { legacyMap.remove(key, s); + return null; } } diff --git a/src/java.base/share/classes/java/text/Collator.java b/src/java.base/share/classes/java/text/Collator.java index cc0ad518045df..a64a97d9a392e 100644 --- a/src/java.base/share/classes/java/text/Collator.java +++ b/src/java.base/share/classes/java/text/Collator.java @@ -115,8 +115,13 @@ *
* Note: {@code CollationKey}s from different * {@code Collator}s can not be compared. See the class description - * for {@link CollationKey} - * for an example using {@code CollationKey}s. + * for {@link CollationKey} for an example using {@code CollationKey}s. + * + * @implNote Significant thread contention may occur during concurrent usage + * of the JDK Reference Implementation's {@link RuleBasedCollator}, which is the + * subtype returned by the default provider of the {@link #getInstance()} factory + * methods. As such, users should consider retrieving a separate instance for + * each thread when used in multithreaded environments. * * @see RuleBasedCollator * @see CollationKey diff --git a/src/java.base/share/classes/java/text/DateFormatSymbols.java b/src/java.base/share/classes/java/text/DateFormatSymbols.java index e7919e33e69c7..582fe64b9c190 100644 --- a/src/java.base/share/classes/java/text/DateFormatSymbols.java +++ b/src/java.base/share/classes/java/text/DateFormatSymbols.java @@ -605,7 +605,8 @@ public void setZoneStrings(String[][] newZoneStrings) { for (int i = 0; i < newZoneStrings.length; ++i) { int len = newZoneStrings[i].length; if (len < 5) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException(String.format( + "Row %s of the input array does not have a length of at least 5", i)); } aCopy[i] = Arrays.copyOf(newZoneStrings[i], len); } diff --git a/src/java.base/share/classes/java/text/MessageFormat.java b/src/java.base/share/classes/java/text/MessageFormat.java index 5ac88f0cfaa8a..ac1d94dab95fe 100644 --- a/src/java.base/share/classes/java/text/MessageFormat.java +++ b/src/java.base/share/classes/java/text/MessageFormat.java @@ -1637,7 +1637,7 @@ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundE // Check the correctness of arguments and offsets if (isValid) { - int lastOffset = patt.length() + 1; + int lastOffset = patt.length(); for (int i = maxOff; i >= 0; --i) { if (argNums[i] < 0 || argNums[i] >= MAX_ARGUMENT_INDEX || offs[i] < 0 || offs[i] > lastOffset) { diff --git a/src/java.base/share/classes/java/text/RuleBasedCollator.java b/src/java.base/share/classes/java/text/RuleBasedCollator.java index e779890b7a8fc..14a0a41c2cc09 100644 --- a/src/java.base/share/classes/java/text/RuleBasedCollator.java +++ b/src/java.base/share/classes/java/text/RuleBasedCollator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,10 +38,6 @@ package java.text; -import java.text.Normalizer; -import java.util.Vector; -import java.util.Locale; - /** * The {@code RuleBasedCollator} class is a concrete subclass of * {@code Collator} that provides a simple, data-driven, table @@ -239,6 +235,11 @@ * * * + * @implNote For this implementation, concurrent usage of this class may + * lead to significant thread contention since {@code synchronized} is employed + * to ensure thread-safety. As such, users of this class should consider creating + * a separate instance for each thread when used in multithreaded environments. + * * @see Collator * @see CollationElementIterator * @author Helena Shih, Laura Werner, Richard Gillam diff --git a/src/java.base/share/classes/java/util/Collections.java b/src/java.base/share/classes/java/util/Collections.java index 473de05f9e58f..31e83060e5532 100644 --- a/src/java.base/share/classes/java/util/Collections.java +++ b/src/java.base/share/classes/java/util/Collections.java @@ -792,15 +792,16 @@ private static void rotate1(List list, int distance) { if (distance == 0) return; - for (int cycleStart = 0, nMoved = 0; nMoved != size; cycleStart++) { + int bound = size - distance; + for (int cycleStart = 0, nMoved = 0; nMoved < size; cycleStart++) { T displaced = list.get(cycleStart); int i = cycleStart; do { - i += distance; - if (i >= size) + if (i >= bound) i -= size; + i += distance; displaced = list.set(i, displaced); - nMoved ++; + nMoved++; } while (i != cycleStart); } } diff --git a/src/java.base/share/classes/java/util/Currency.java b/src/java.base/share/classes/java/util/Currency.java index 502b597bbd876..d256433d8509d 100644 --- a/src/java.base/share/classes/java/util/Currency.java +++ b/src/java.base/share/classes/java/util/Currency.java @@ -314,7 +314,8 @@ private static Currency getInstance(String currencyCode, int defaultFractionDigi // or in the list of other currencies. boolean found = false; if (currencyCode.length() != 3) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException( + "The input currency code: \"%s\" must have a length of 3 characters".formatted(currencyCode)); } char char1 = currencyCode.charAt(0); char char2 = currencyCode.charAt(1); @@ -337,7 +338,8 @@ private static Currency getInstance(String currencyCode, int defaultFractionDigi if (!found) { OtherCurrencyEntry ocEntry = OtherCurrencyEntry.findEntry(currencyCode); if (ocEntry == null) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException( + "The input currency code: \"%s\" is not a valid ISO 4217 code".formatted(currencyCode)); } defaultFractionDigits = ocEntry.fraction; numericCode = ocEntry.numericCode; @@ -392,7 +394,8 @@ public static Currency getInstance(Locale locale) { String country = CalendarDataUtility.findRegionOverride(locale).getCountry(); if (country == null || !country.matches("^[a-zA-Z]{2}$")) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException( + "The country of the input locale: \"%s\" is not a valid ISO 3166 country code".formatted(locale)); } char char1 = country.charAt(0); @@ -409,7 +412,8 @@ public static Currency getInstance(Locale locale) { } else { // special cases if (tableEntry == INVALID_COUNTRY_ENTRY) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException( + "The country of the input locale: \"%s\" is not a valid ISO 3166 country code".formatted(locale)); } if (tableEntry == COUNTRY_WITHOUT_CURRENCY_ENTRY) { return null; @@ -674,7 +678,8 @@ private Object readResolve() { */ private static int getMainTableEntry(char char1, char char2) { if (char1 < 'A' || char1 > 'Z' || char2 < 'A' || char2 > 'Z') { - throw new IllegalArgumentException(); + throw new IllegalArgumentException( + "The country code: \"%c%c\" is not a valid ISO 3166 code".formatted(char1, char2)); } return mainTable[(char1 - 'A') * A_TO_Z + (char2 - 'A')]; } @@ -685,7 +690,8 @@ private static int getMainTableEntry(char char1, char char2) { */ private static void setMainTableEntry(char char1, char char2, int entry) { if (char1 < 'A' || char1 > 'Z' || char2 < 'A' || char2 > 'Z') { - throw new IllegalArgumentException(); + throw new IllegalArgumentException( + "The country code: \"%c%c\" is not a valid ISO 3166 code".formatted(char1, char2)); } mainTable[(char1 - 'A') * A_TO_Z + (char2 - 'A')] = entry; } diff --git a/src/java.base/share/classes/java/util/Formatter.java b/src/java.base/share/classes/java/util/Formatter.java index 0436b7faf89b4..498a470363c60 100644 --- a/src/java.base/share/classes/java/util/Formatter.java +++ b/src/java.base/share/classes/java/util/Formatter.java @@ -4721,9 +4721,9 @@ static boolean isValid(char c) { DECIMAL_FLOAT, HEXADECIMAL_FLOAT, HEXADECIMAL_FLOAT_UPPER, - LINE_SEPARATOR, - PERCENT_SIGN -> true; - default -> false; + LINE_SEPARATOR -> true; + // Don't put PERCENT_SIGN inside switch, as that will make the method size exceed 325 and cannot be inlined. + default -> c == PERCENT_SIGN; }; } diff --git a/src/java.base/share/classes/java/util/SimpleTimeZone.java b/src/java.base/share/classes/java/util/SimpleTimeZone.java index 72ce744e579d6..a368415245833 100644 --- a/src/java.base/share/classes/java/util/SimpleTimeZone.java +++ b/src/java.base/share/classes/java/util/SimpleTimeZone.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -112,15 +112,15 @@ *

  *      // Base GMT offset: -8:00
  *      // DST starts:      at 2:00am in standard time
- *      //                  on the first Sunday in April
+ *      //                  on the second Sunday in March
  *      // DST ends:        at 2:00am in daylight time
- *      //                  on the last Sunday in October
+ *      //                  on the first Sunday in November
  *      // Save:            1 hour
  *      SimpleTimeZone(-28800000,
  *                     "America/Los_Angeles",
- *                     Calendar.APRIL, 1, -Calendar.SUNDAY,
+ *                     Calendar.MARCH, 8, -Calendar.SUNDAY,
  *                     7200000,
- *                     Calendar.OCTOBER, -1, Calendar.SUNDAY,
+ *                     Calendar.NOVEMBER, 1, -Calendar.SUNDAY,
  *                     7200000,
  *                     3600000)
  *
@@ -857,13 +857,24 @@ public Object clone()
     }
 
     /**
-     * Generates the hash code for the SimpleDateFormat object.
+     * Generates the hash code for the SimpleTimeZone object.
      * @return the hash code for this object
      */
     public int hashCode()
     {
-        return startMonth ^ startDay ^ startDayOfWeek ^ startTime ^
-            endMonth ^ endDay ^ endDayOfWeek ^ endTime ^ rawOffset;
+        int hash = 31 * getID().hashCode() + rawOffset;
+        hash = 31 * hash + Boolean.hashCode(useDaylight);
+        if (useDaylight) {
+            hash = 31 * hash + startMonth;
+            hash = 31 * hash + startDay;
+            hash = 31 * hash + startDayOfWeek;
+            hash = 31 * hash + startTime;
+            hash = 31 * hash + endMonth;
+            hash = 31 * hash + endDay;
+            hash = 31 * hash + endDayOfWeek;
+            hash = 31 * hash + endTime;
+        }
+        return hash;
     }
 
     /**
diff --git a/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java b/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java
index 1f029bc022dd2..16d538364416a 100644
--- a/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java
+++ b/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java
@@ -1725,7 +1725,7 @@ else if (deadline == 0L)
             else if (deadline - System.currentTimeMillis() > TIMEOUT_SLOP)
                 LockSupport.parkUntil(deadline);
             else if (((int)c & SMASK) == (w.config & SMASK) &&
-                     compareAndSetCtl(c, ((UC_MASK & (c - TC_UNIT)) |
+                     compareAndSetCtl(c, ((c & RC_MASK) | ((c - TC_UNIT) & TC_MASK) |
                                           (prevCtl & SP_MASK)))) {
                 w.config |= QUIET;           // sentinel for deregisterWorker
                 return -1;                   // drop on timeout
diff --git a/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java b/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java
index e3c2a6098d800..e0a07c6263966 100644
--- a/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java
+++ b/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java
@@ -1059,7 +1059,7 @@ static final class BufferedSubscription
         final Subscriber subscriber;
         final BiConsumer, ? super Throwable> onNextHandler;
         Executor executor;                 // null on error
-        Thread waiter;                     // blocked producer thread
+        volatile Thread waiter;            // blocked producer thread
         Throwable pendingError;            // holds until onError issued
         BufferedSubscription next;      // used only by publisher
         BufferedSubscription nextRetry; // used only by publisher
diff --git a/src/java.base/share/classes/java/util/jar/JarFile.java b/src/java.base/share/classes/java/util/jar/JarFile.java
index 70cf99504e44a..ce4e01c7c372e 100644
--- a/src/java.base/share/classes/java/util/jar/JarFile.java
+++ b/src/java.base/share/classes/java/util/jar/JarFile.java
@@ -422,7 +422,8 @@ private Manifest getManifestFromReference() throws IOException {
                             jv = new JarVerifier(manEntry.getName(), b);
                         } else {
                             if (JarVerifier.debug != null) {
-                                JarVerifier.debug.println("Multiple MANIFEST.MF found. Treat JAR file as unsigned");
+                                JarVerifier.debug.println(
+                                        JarVerifier.MULTIPLE_MANIFEST_WARNING);
                             }
                         }
                     }
diff --git a/src/java.base/share/classes/java/util/jar/JarInputStream.java b/src/java.base/share/classes/java/util/jar/JarInputStream.java
index eb51942173230..5f90c1ab353a2 100644
--- a/src/java.base/share/classes/java/util/jar/JarInputStream.java
+++ b/src/java.base/share/classes/java/util/jar/JarInputStream.java
@@ -97,7 +97,17 @@ private JarEntry checkManifest(JarEntry e)
                 jv = new JarVerifier(e.getName(), bytes);
                 mev = new ManifestEntryVerifier(man, jv.manifestName);
             }
-            return (JarEntry)super.getNextEntry();
+            JarEntry nextEntry = (JarEntry)super.getNextEntry();
+            if (nextEntry != null &&
+                    JarFile.MANIFEST_NAME.equalsIgnoreCase(nextEntry.getName())) {
+                if (JarVerifier.debug != null) {
+                    JarVerifier.debug.println(JarVerifier.MULTIPLE_MANIFEST_WARNING);
+                }
+
+                jv = null;
+                mev = null;
+            }
+            return nextEntry;
         }
         return e;
     }
diff --git a/src/java.base/share/classes/java/util/jar/JarVerifier.java b/src/java.base/share/classes/java/util/jar/JarVerifier.java
index c9ab1ac06bfea..9185dc481fef3 100644
--- a/src/java.base/share/classes/java/util/jar/JarVerifier.java
+++ b/src/java.base/share/classes/java/util/jar/JarVerifier.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -44,6 +44,9 @@
  */
 class JarVerifier {
 
+    public static final String MULTIPLE_MANIFEST_WARNING =
+            "WARNING: Multiple MANIFEST.MF found. Treat JAR file as unsigned.";
+
     /* Are we debugging ? */
     static final Debug debug = Debug.getInstance("jar");
 
diff --git a/src/java.base/share/classes/java/util/regex/Pattern.java b/src/java.base/share/classes/java/util/regex/Pattern.java
index 1e03f56e38e1a..98ce8a5812c2b 100644
--- a/src/java.base/share/classes/java/util/regex/Pattern.java
+++ b/src/java.base/share/classes/java/util/regex/Pattern.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -1090,6 +1090,10 @@ public static Pattern compile(String regex) {
      *
      * @throws  PatternSyntaxException
      *          If the expression's syntax is invalid
+     *
+     * @implNote If {@link #CANON_EQ} is specified and the number of combining
+     * marks for any character is too large, an {@link java.lang.OutOfMemoryError}
+     * is thrown.
      */
     public static Pattern compile(String regex, int flags) {
         return new Pattern(regex, flags);
@@ -1123,6 +1127,13 @@ public String toString() {
      *         The character sequence to be matched
      *
      * @return  A new matcher for this pattern
+     *
+     * @implNote When a {@link Pattern} is deserialized, compilation is deferred
+     * until a direct or indirect invocation of this method. Thus, if a
+     * deserialized pattern has {@link #CANON_EQ} among its flags and the number
+     * of combining marks for any character is too large, an
+     * {@link java.lang.OutOfMemoryError} is thrown,
+     * as in {@link #compile(String, int)}.
      */
     public Matcher matcher(CharSequence input) {
         if (!compiled) {
@@ -1596,14 +1607,30 @@ private static String[] producePermutations(String input) {
             return result;
         }
 
-        int length = 1;
+        /*
+         * Since
+         *      12! =   479_001_600 < Integer.MAX_VALUE
+         *      13! = 6_227_020_800 > Integer.MAX_VALUE
+         * the computation of n! using int arithmetic will overflow iff
+         *      n < 0 or n > 12
+         *
+         * Here, nCodePoints! is computed in the next for-loop below.
+         * As nCodePoints >= 0, the computation overflows iff nCodePoints > 12.
+         * In that case, throw OOME to simulate length > Integer.MAX_VALUE.
+         */
         int nCodePoints = countCodePoints(input);
-        for(int x=1; x 12) {
+            throw new OutOfMemoryError("Pattern too complex");
+        }
 
+        /* Compute length = nCodePoints! */
+        int length = 1;
+        for (int x = 2; x <= nCodePoints; ++x) {
+            length *= x;
+        }
         String[] temp = new String[length];
 
-        int combClass[] = new int[nCodePoints];
+        int[] combClass = new int[nCodePoints];
         for(int x=0, i=0; x 0)
-            {
-                out.write(buf, 0, len);
-                if (len < buf.length)
+            // For SYNC_FLUSH, the Deflater.deflate() expects the callers
+            // to use a buffer whose length is greater than 6 to avoid
+            // flush marker (5 bytes) being repeatedly output to the output buffer
+            // every time it is invoked.
+            final byte[] flushBuf = buf.length < SYNC_FLUSH_MIN_BUF_SIZE
+                    ? new byte[DEFAULT_BUF_SIZE]
+                    : buf;
+            while ((len = def.deflate(flushBuf, 0, flushBuf.length, Deflater.SYNC_FLUSH)) > 0) {
+                out.write(flushBuf, 0, len);
+                if (len < flushBuf.length)
                     break;
             }
         }
diff --git a/src/java.base/share/classes/java/util/zip/GZIPOutputStream.java b/src/java.base/share/classes/java/util/zip/GZIPOutputStream.java
index cdfac329cfa83..7dad5fe0e879f 100644
--- a/src/java.base/share/classes/java/util/zip/GZIPOutputStream.java
+++ b/src/java.base/share/classes/java/util/zip/GZIPOutputStream.java
@@ -109,7 +109,7 @@ public GZIPOutputStream(OutputStream out, int size, boolean syncFlush)
      * @throws    IOException If an I/O error has occurred.
      */
     public GZIPOutputStream(OutputStream out) throws IOException {
-        this(out, 512, false);
+        this(out, DeflaterOutputStream.DEFAULT_BUF_SIZE, false);
     }
 
     /**
@@ -131,7 +131,7 @@ public GZIPOutputStream(OutputStream out) throws IOException {
     public GZIPOutputStream(OutputStream out, boolean syncFlush)
         throws IOException
     {
-        this(out, 512, syncFlush);
+        this(out, DeflaterOutputStream.DEFAULT_BUF_SIZE, syncFlush);
     }
 
     /**
diff --git a/src/java.base/share/classes/jdk/internal/util/random/RandomSupport.java b/src/java.base/share/classes/jdk/internal/util/random/RandomSupport.java
index 1fc1eeff46a03..6854124121ca5 100644
--- a/src/java.base/share/classes/jdk/internal/util/random/RandomSupport.java
+++ b/src/java.base/share/classes/jdk/internal/util/random/RandomSupport.java
@@ -265,7 +265,7 @@ public static long[] convertSeedBytesToLongs(byte[] seed, int n, int z) {
         final int m = Math.min(seed.length, n << 3);
         // Distribute seed bytes into the words to be formed.
         for (int j = 0; j < m; j++) {
-            result[j>>3] = (result[j>>3] << 8) | seed[j];
+            result[j>>3] = (result[j>>3] << 8) | (seed[j] & 0xFF);
         }
         // If there aren't enough seed bytes for all the words we need,
         // use a SplitMix-style PRNG to fill in the rest.
@@ -308,7 +308,7 @@ public static int[] convertSeedBytesToInts(byte[] seed, int n, int z) {
         final int m = Math.min(seed.length, n << 2);
         // Distribute seed bytes into the words to be formed.
         for (int j = 0; j < m; j++) {
-            result[j>>2] = (result[j>>2] << 8) | seed[j];
+            result[j>>2] = (result[j>>2] << 8) | (seed[j] & 0xFF);
         }
         // If there aren't enough seed bytes for all the words we need,
         // use a SplitMix-style PRNG to fill in the rest.
diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java
index 29a813a485fca..044056c7bc8fd 100644
--- a/src/java.base/share/classes/module-info.java
+++ b/src/java.base/share/classes/module-info.java
@@ -228,6 +228,7 @@
         jdk.jfr;
     exports jdk.internal.ref to
         java.desktop,
+        java.net.http,
         jdk.incubator.foreign;
     exports jdk.internal.reflect to
         java.logging,
diff --git a/src/java.base/share/classes/sun/launcher/LauncherHelper.java b/src/java.base/share/classes/sun/launcher/LauncherHelper.java
index 74c0cef881e96..59adbec3d64bd 100644
--- a/src/java.base/share/classes/sun/launcher/LauncherHelper.java
+++ b/src/java.base/share/classes/sun/launcher/LauncherHelper.java
@@ -75,6 +75,7 @@
 import java.util.Properties;
 import java.util.ResourceBundle;
 import java.util.Set;
+import java.util.TimeZone;
 import java.util.TreeSet;
 import java.util.jar.Attributes;
 import java.util.jar.JarFile;
@@ -292,6 +293,8 @@ private static void printLocale(boolean summaryMode) {
                 Locale.getDefault(Category.DISPLAY).getDisplayName());
         ostream.println(INDENT + "default format locale = " +
                 Locale.getDefault(Category.FORMAT).getDisplayName());
+        ostream.println(INDENT + "default timezone = " +
+                TimeZone.getDefault().getID());
         ostream.println(INDENT + "tzdata version = " +
                 ZoneInfoFile.getVersion());
         if (!summaryMode) {
diff --git a/src/java.base/share/classes/sun/net/ftp/impl/FtpClient.java b/src/java.base/share/classes/sun/net/ftp/impl/FtpClient.java
index 560fbfbea945c..0ec730072b497 100644
--- a/src/java.base/share/classes/sun/net/ftp/impl/FtpClient.java
+++ b/src/java.base/share/classes/sun/net/ftp/impl/FtpClient.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -24,8 +24,6 @@
  */
 package sun.net.ftp.impl;
 
-
-
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.BufferedReader;
@@ -64,6 +62,7 @@
 import java.util.regex.Pattern;
 import javax.net.ssl.SSLSocket;
 import javax.net.ssl.SSLSocketFactory;
+
 import sun.net.ftp.FtpDirEntry;
 import sun.net.ftp.FtpDirParser;
 import sun.net.ftp.FtpProtocolException;
@@ -71,6 +70,7 @@
 import sun.net.util.IPAddressUtil;
 import sun.util.logging.PlatformLogger;
 
+import static sun.net.util.ProxyUtil.copyProxy;
 
 public class FtpClient extends sun.net.ftp.FtpClient {
 
@@ -982,7 +982,7 @@ public int getReadTimeout() {
     }
 
     public sun.net.ftp.FtpClient setProxy(Proxy p) {
-        proxy = p;
+        proxy = copyProxy(p);
         return this;
     }
 
diff --git a/src/java.base/share/classes/sun/net/util/ProxyUtil.java b/src/java.base/share/classes/sun/net/util/ProxyUtil.java
new file mode 100644
index 0000000000000..dfb60671e3796
--- /dev/null
+++ b/src/java.base/share/classes/sun/net/util/ProxyUtil.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.net.util;
+
+import sun.net.ApplicationProxy;
+
+import java.net.Proxy;
+
+public final class ProxyUtil {
+
+    private ProxyUtil() {}
+
+    /**
+     * Creates a new {@link Proxy} instance for the given proxy iff it is
+     * neither null, {@link Proxy#NO_PROXY Proxy.NO_PROXY}, an
+     * {@link ApplicationProxy} instance, nor already a {@code Proxy} instance.
+     */
+    public static Proxy copyProxy(Proxy proxy) {
+        return proxy == null
+                || proxy.getClass() == Proxy.class
+                || proxy instanceof ApplicationProxy
+                ? proxy
+                : new Proxy(proxy.type(), proxy.address());
+    }
+
+}
diff --git a/src/java.base/share/classes/sun/net/www/http/HttpClient.java b/src/java.base/share/classes/sun/net/www/http/HttpClient.java
index ba5e38827c73a..4057dc43334b1 100644
--- a/src/java.base/share/classes/sun/net/www/http/HttpClient.java
+++ b/src/java.base/share/classes/sun/net/www/http/HttpClient.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1994, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2025, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -42,6 +42,8 @@
 import sun.net.www.protocol.http.AuthenticatorKeys;
 import sun.net.www.protocol.http.HttpURLConnection;
 import sun.util.logging.PlatformLogger;
+
+import static sun.net.util.ProxyUtil.copyProxy;
 import static sun.net.www.protocol.http.HttpURLConnection.TunnelState.*;
 import sun.security.action.GetPropertyAction;
 
@@ -268,7 +270,7 @@ public HttpClient(URL url, String proxyHost, int proxyPort)
     }
 
     protected HttpClient(URL url, Proxy p, int to) throws IOException {
-        proxy = (p == null) ? Proxy.NO_PROXY : p;
+        proxy = p == null ? Proxy.NO_PROXY : copyProxy(p);
         this.host = url.getHost();
         this.url = url;
         port = url.getPort();
@@ -333,9 +335,7 @@ public static HttpClient New(URL url, boolean useCache)
     public static HttpClient New(URL url, Proxy p, int to, boolean useCache,
         HttpURLConnection httpuc) throws IOException
     {
-        if (p == null) {
-            p = Proxy.NO_PROXY;
-        }
+        p = p == null ? Proxy.NO_PROXY : copyProxy(p);
         HttpClient ret = null;
         /* see if one's already around */
         if (useCache) {
diff --git a/src/java.base/share/classes/sun/net/www/protocol/file/FileURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/file/FileURLConnection.java
index c6878a6369945..dd34c339ebc48 100644
--- a/src/java.base/share/classes/sun/net/www/protocol/file/FileURLConnection.java
+++ b/src/java.base/share/classes/sun/net/www/protocol/file/FileURLConnection.java
@@ -99,6 +99,12 @@ public void connect() throws IOException {
         }
     }
 
+    public synchronized void closeInputStream() throws IOException {
+        if (is != null) {
+            is.close();
+        }
+    }
+
     private boolean initializedHeaders = false;
 
     private void initializeHeaders() {
diff --git a/src/java.base/share/classes/sun/net/www/protocol/ftp/FtpURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/ftp/FtpURLConnection.java
index 42555f01fc7fe..d5bd4e27b365f 100644
--- a/src/java.base/share/classes/sun/net/www/protocol/ftp/FtpURLConnection.java
+++ b/src/java.base/share/classes/sun/net/www/protocol/ftp/FtpURLConnection.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1994, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2025, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -49,6 +49,7 @@
 import java.util.Iterator;
 import java.security.Permission;
 import java.util.Properties;
+
 import sun.net.NetworkClient;
 import sun.net.util.IPAddressUtil;
 import sun.net.www.MessageHeader;
@@ -62,6 +63,7 @@
 import sun.net.www.ParseUtil;
 import sun.security.action.GetPropertyAction;
 
+import static sun.net.util.ProxyUtil.copyProxy;
 
 /**
  * This class Opens an FTP input (or output) stream given a URL.
@@ -252,7 +254,7 @@ public ProxySelector run() {
                 }
                 final Iterator it = proxies.iterator();
                 while (it.hasNext()) {
-                    p = it.next();
+                    p = copyProxy(it.next());
                     if (p == null || p == Proxy.NO_PROXY ||
                         p.type() == Proxy.Type.SOCKS) {
                         break;
diff --git a/src/java.base/share/classes/sun/net/www/protocol/ftp/Handler.java b/src/java.base/share/classes/sun/net/www/protocol/ftp/Handler.java
index 80c85ea642b4c..4610c43a23d65 100644
--- a/src/java.base/share/classes/sun/net/www/protocol/ftp/Handler.java
+++ b/src/java.base/share/classes/sun/net/www/protocol/ftp/Handler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2025, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -32,11 +32,9 @@
 import java.io.IOException;
 import java.net.URL;
 import java.net.Proxy;
-import java.util.Map;
-import java.util.HashMap;
 import java.util.Objects;
-import sun.net.ftp.FtpClient;
-import sun.net.www.protocol.http.HttpURLConnection;
+
+import static sun.net.util.ProxyUtil.copyProxy;
 
 /** open an ftp connection given a URL */
 public class Handler extends java.net.URLStreamHandler {
@@ -56,8 +54,8 @@ protected java.net.URLConnection openConnection(URL u)
         return openConnection(u, null);
     }
 
-    protected java.net.URLConnection openConnection(URL u, Proxy p)
+    protected java.net.URLConnection openConnection(URL u, Proxy proxy)
         throws IOException {
-        return new FtpURLConnection(u, p);
+        return new FtpURLConnection(u, copyProxy(proxy));
     }
 }
diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java
index e3e72723428f4..0f92a366e2429 100644
--- a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java
+++ b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1995, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -83,6 +83,7 @@
 import java.util.Properties;
 import java.util.concurrent.locks.ReentrantLock;
 
+import static sun.net.util.ProxyUtil.copyProxy;
 import static sun.net.www.protocol.http.AuthScheme.BASIC;
 import static sun.net.www.protocol.http.AuthScheme.DIGEST;
 import static sun.net.www.protocol.http.AuthScheme.NTLM;
@@ -420,6 +421,10 @@ private static Set schemesListToSet(String list) {
        calls getInputStream after disconnect */
     private Exception rememberedException = null;
 
+    /* Remembered Exception, we will throw it again if somebody
+       calls getOutputStream after disconnect  or error */
+    private Exception rememberedExceptionOut = null;
+
     /* If we decide we want to reuse a client, we put it here */
     private HttpClient reuseClient = null;
 
@@ -936,7 +941,7 @@ protected HttpURLConnection(URL u, Proxy p, Handler handler)
         responses = new MessageHeader(maxHeaderSize);
         userHeaders = new MessageHeader();
         this.handler = handler;
-        instProxy = p;
+        instProxy = copyProxy(p);
         if (instProxy instanceof sun.net.ApplicationProxy) {
             /* Application set Proxies should not have access to cookies
              * in a secure environment unless explicitly allowed. */
@@ -1251,7 +1256,7 @@ public ProxySelector run() {
                     final Iterator it = proxies.iterator();
                     Proxy p;
                     while (it.hasNext()) {
-                        p = it.next();
+                        p = copyProxy(it.next());
                         try {
                             if (!failedOnce) {
                                 http = getNewHttpClient(url, p, connectTimeout);
@@ -1449,6 +1454,14 @@ private OutputStream getOutputStream0() throws IOException {
                                + " if doOutput=false - call setDoOutput(true)");
             }
 
+            if (rememberedExceptionOut != null) {
+                if (rememberedExceptionOut instanceof RuntimeException) {
+                    throw new RuntimeException(rememberedExceptionOut);
+                } else {
+                    throw getChainedException((IOException) rememberedExceptionOut);
+                }
+            }
+
             if (method.equals("GET")) {
                 method = "POST"; // Backward compatibility
             }
@@ -1511,9 +1524,11 @@ private OutputStream getOutputStream0() throws IOException {
             int i = responseCode;
             disconnectInternal();
             responseCode = i;
+            rememberedExceptionOut = e;
             throw e;
         } catch (IOException e) {
             disconnectInternal();
+            rememberedExceptionOut = e;
             throw e;
         }
     }
diff --git a/src/java.base/share/classes/sun/net/www/protocol/jar/JarURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/jar/JarURLConnection.java
index 4ccf781991440..725b4cb95a084 100644
--- a/src/java.base/share/classes/sun/net/www/protocol/jar/JarURLConnection.java
+++ b/src/java.base/share/classes/sun/net/www/protocol/jar/JarURLConnection.java
@@ -25,21 +25,18 @@
 
 package sun.net.www.protocol.jar;
 
-import java.io.InputStream;
-import java.io.IOException;
-import java.io.FileNotFoundException;
+import sun.net.www.protocol.file.FileURLConnection;
 import java.io.BufferedInputStream;
-import java.net.URL;
-import java.net.URLConnection;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
 import java.net.MalformedURLException;
-import java.net.UnknownServiceException;
-import java.util.Enumeration;
-import java.util.Map;
+import java.net.URL;
+import java.security.Permission;
 import java.util.List;
+import java.util.Map;
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
-import java.util.jar.Manifest;
-import java.security.Permission;
 
 /**
  * @author Benjamin Renaud
@@ -47,26 +44,10 @@
  */
 public class JarURLConnection extends java.net.JarURLConnection {
 
-    private static final boolean debug = false;
-
     /* the Jar file factory. It handles both retrieval and caching.
      */
     private static final JarFileFactory factory = JarFileFactory.getInstance();
 
-    /* the url for the Jar file */
-    private URL jarFileURL;
-
-    /* the permission to get this JAR file. This is the actual, ultimate,
-     * permission, returned by the jar file factory.
-     */
-    private Permission permission;
-
-    /* the url connection for the JAR file */
-    private URLConnection jarFileURLConnection;
-
-    /* the entry name, if any */
-    private String entryName;
-
     /* the JarEntry */
     private JarEntry jarEntry;
 
@@ -80,12 +61,10 @@ public JarURLConnection(URL url, Handler handler)
     throws MalformedURLException, IOException {
         super(url);
 
-        jarFileURL = getJarFileURL();
-        jarFileURLConnection = jarFileURL.openConnection();
+        jarFileURLConnection = getJarFileURL().openConnection();
         // whether, or not, the embedded URL should use the cache will depend
         // on this instance's cache value
         jarFileURLConnection.setUseCaches(useCaches);
-        entryName = getEntryName();
     }
 
     public JarFile getJarFile() throws IOException {
@@ -110,8 +89,14 @@ public void close () throws IOException {
             try {
                 super.close();
             } finally {
-                if (!getUseCaches()) {
-                    jarFile.close();
+                try {
+                    if (!getUseCaches()) {
+                        jarFile.close();
+                    }
+                } finally {
+                    if (jarFileURLConnection instanceof FileURLConnection fileURLConnection) {
+                        fileURLConnection.closeInputStream();
+                    }
                 }
             }
         }
@@ -120,7 +105,7 @@ public void close () throws IOException {
     public void connect() throws IOException {
         if (!connected) {
             boolean useCaches = getUseCaches();
-            String entryName = this.entryName;
+            String entryName = getEntryName();
 
             /* the factory call will do the security checks */
             URL url = getJarFileURL();
@@ -176,6 +161,7 @@ public InputStream getInputStream() throws IOException {
 
         InputStream result = null;
 
+        String entryName = getEntryName();
         if (entryName == null) {
             throw new IOException("no entry name specified");
         } else {
@@ -216,7 +202,7 @@ public Object getContent() throws IOException {
         Object result = null;
 
         connect();
-        if (entryName == null) {
+        if (getEntryName() == null) {
             result = jarFile;
         } else {
             result = super.getContent();
@@ -226,6 +212,7 @@ public Object getContent() throws IOException {
 
     public String getContentType() {
         if (contentType == null) {
+            String entryName = getEntryName();
             if (entryName == null) {
                 contentType = "x-java/jar";
             } else {
diff --git a/src/java.base/share/classes/sun/nio/ch/Net.java b/src/java.base/share/classes/sun/nio/ch/Net.java
index 80a9aed6c0940..eb9ebacb76c29 100644
--- a/src/java.base/share/classes/sun/nio/ch/Net.java
+++ b/src/java.base/share/classes/sun/nio/ch/Net.java
@@ -70,6 +70,9 @@ public String name() {
     // set to true if the fast tcp loopback should be enabled on Windows
     private static final boolean fastLoopback;
 
+    // set to true if shut down before close should be enabled on Windows
+    private static final boolean SHUTDOWN_WRITE_BEFORE_CLOSE;
+
     // -- Miscellaneous utilities --
 
     private static volatile boolean checkedIPv6;
@@ -106,6 +109,13 @@ static boolean useExclusiveBind() {
         return exclusiveBind;
     }
 
+    /**
+     * Tells whether a TCP connection should be shutdown for writing before closing.
+     */
+    static boolean shouldShutdownWriteBeforeClose() {
+        return SHUTDOWN_WRITE_BEFORE_CLOSE;
+    }
+
     /**
      * Tells whether both IPV6_XXX and IP_XXX socket options should be set on
      * IPv6 sockets. On some kernels, both IPV6_XXX and IP_XXX socket options
@@ -506,6 +516,8 @@ public static boolean isFastTcpLoopbackRequested() {
      */
     private static native int isExclusiveBindAvailable();
 
+    private static native boolean shouldShutdownWriteBeforeClose0();
+
     private static native boolean shouldSetBothIPv4AndIPv6Options0();
 
     private static native boolean canIPv6SocketJoinIPv4Group0();
@@ -832,5 +844,6 @@ static native int blockOrUnblock6(boolean block, FileDescriptor fd, byte[] group
         }
 
         fastLoopback = isFastTcpLoopbackRequested();
+        SHUTDOWN_WRITE_BEFORE_CLOSE = shouldShutdownWriteBeforeClose0();
     }
 }
diff --git a/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java b/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java
index 477fa1afca238..42e623c478fd6 100644
--- a/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java
+++ b/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java
@@ -743,7 +743,7 @@ public boolean isConnectionPending() {
     /**
      * Marks the beginning of a connect operation that might block.
      * @param blocking true if configured blocking
-     * @param isa the remote address
+     * @param sa the remote socket address
      * @throws ClosedChannelException if the channel is closed
      * @throws AlreadyConnectedException if already connected
      * @throws ConnectionPendingException is a connection is pending
@@ -970,8 +970,8 @@ public boolean finishConnect() throws IOException {
     }
 
     /**
-     * Closes the socket if there are no I/O operations in progress and the
-     * channel is not registered with a Selector.
+     * Closes the socket if there are no I/O operations in progress (or no I/O
+     * operations tracked), and the channel is not registered with a Selector.
      */
     private boolean tryClose() throws IOException {
         assert Thread.holdsLock(stateLock) && state == ST_CLOSING;
@@ -996,11 +996,16 @@ private void tryFinishClose() {
     }
 
     /**
-     * Closes this channel when configured in blocking mode.
+     * Closes this channel when configured in blocking mode. If there are no I/O
+     * operations in progress (or tracked), then the channel's socket is closed. If
+     * there are I/O operations in progress then the behavior is platform specific.
      *
-     * If there is an I/O operation in progress then the socket is pre-closed
-     * and the I/O threads signalled, in which case the final close is deferred
-     * until all I/O operations complete.
+     * On Unix systems, the channel's socket is pre-closed. The socket is dup'ed
+     * and the threads signalled. The final close is deferred until all I/O
+     * operations complete.
+     *
+     * On Windows, the channel's socket is pre-closed. The channel's
+     * socket is closed.
      *
      * Note that a channel configured blocking may be registered with a Selector
      * This arises when a key is canceled and the channel configured to blocking
@@ -1009,7 +1014,19 @@ private void tryFinishClose() {
     private void implCloseBlockingMode() throws IOException {
         synchronized (stateLock) {
             assert state < ST_CLOSING;
+            boolean connected = (state == ST_CONNECTED);
             state = ST_CLOSING;
+
+            if (connected && Net.shouldShutdownWriteBeforeClose()) {
+                // shutdown output when linger interval not set to 0
+                try {
+                    var SO_LINGER = StandardSocketOptions.SO_LINGER;
+                    if ((int) Net.getSocketOption(fd, SO_LINGER) != 0) {
+                        Net.shutdown(fd, Net.SHUT_WR);
+                    }
+                } catch (IOException ignore) { }
+            }
+
             if (!tryClose()) {
                 long reader = readerThread;
                 long writer = writerThread;
diff --git a/src/java.base/share/classes/sun/nio/cs/StreamDecoder.java b/src/java.base/share/classes/sun/nio/cs/StreamDecoder.java
index c8a6a476e66f3..675b48b63993a 100644
--- a/src/java.base/share/classes/sun/nio/cs/StreamDecoder.java
+++ b/src/java.base/share/classes/sun/nio/cs/StreamDecoder.java
@@ -332,7 +332,6 @@ int implRead(char[] cbuf, int off, int end) throws IOException {
                     eof = true;
                     if ((cb.position() == 0) && (!bb.hasRemaining()))
                         break;
-                    decoder.reset();
                 }
                 continue;
             }
diff --git a/src/java.base/share/classes/sun/nio/cs/StreamEncoder.java b/src/java.base/share/classes/sun/nio/cs/StreamEncoder.java
index 65cb25bb06d12..03ddeaa166dca 100644
--- a/src/java.base/share/classes/sun/nio/cs/StreamEncoder.java
+++ b/src/java.base/share/classes/sun/nio/cs/StreamEncoder.java
@@ -322,8 +322,8 @@ void implFlush() throws IOException {
     }
 
     void implClose() throws IOException {
-        flushLeftoverChar(null, true);
-        try {
+        try (ch; out) {
+            flushLeftoverChar(null, true);
             for (;;) {
                 CoderResult cr = encoder.flush(bb);
                 if (cr.isUnderflow())
@@ -338,15 +338,8 @@ void implClose() throws IOException {
 
             if (bb.position() > 0)
                 writeBytes();
-            if (ch != null)
-                ch.close();
-            else {
-                try {
-                    out.flush();
-                } finally {
-                    out.close();
-                }
-            }
+            if (out != null)
+                out.flush();
         } catch (IOException x) {
             encoder.reset();
             throw x;
diff --git a/src/java.base/share/classes/sun/nio/cs/Unicode.java b/src/java.base/share/classes/sun/nio/cs/Unicode.java
index fa97b6f36f5f1..aac77a13ffb98 100644
--- a/src/java.base/share/classes/sun/nio/cs/Unicode.java
+++ b/src/java.base/share/classes/sun/nio/cs/Unicode.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -45,6 +45,12 @@ public boolean contains(Charset cs) {
                 || (cs instanceof UTF_16BE)
                 || (cs instanceof UTF_16LE)
                 || (cs instanceof UTF_16LE_BOM)
+                || (cs instanceof CESU_8)
+                || (cs instanceof UTF_32)
+                || (cs instanceof UTF_32BE)
+                || (cs instanceof UTF_32BE_BOM)
+                || (cs instanceof UTF_32LE)
+                || (cs instanceof UTF_32LE_BOM)
                 || (cs.name().equals("GBK"))
                 || (cs.name().equals("GB18030"))
                 || (cs.name().equals("ISO-8859-2"))
diff --git a/src/java.base/share/classes/sun/security/pkcs/PKCS9Attribute.java b/src/java.base/share/classes/sun/security/pkcs/PKCS9Attribute.java
index 79eaee18862c6..f7a36b1571ad4 100644
--- a/src/java.base/share/classes/sun/security/pkcs/PKCS9Attribute.java
+++ b/src/java.base/share/classes/sun/security/pkcs/PKCS9Attribute.java
@@ -26,7 +26,6 @@
 package sun.security.pkcs;
 
 import java.io.IOException;
-import java.io.OutputStream;
 import java.security.cert.CertificateException;
 import java.util.Date;
 import sun.security.x509.CertificateExtensions;
@@ -527,7 +526,7 @@ public PKCS9Attribute(DerValue derVal) throws IOException {
      * should be encoded as T61Strings.
      */
     @Override
-    public void derEncode(OutputStream out) throws IOException {
+    public void derEncode(DerOutputStream out) throws IOException {
         DerOutputStream temp = new DerOutputStream();
         temp.putOID(oid);
         switch (index) {
@@ -648,11 +647,7 @@ public void derEncode(OutputStream out) throws IOException {
         default: // can't happen
         }
 
-        DerOutputStream derOut = new DerOutputStream();
-        derOut.write(DerValue.tag_Sequence, temp.toByteArray());
-
-        out.write(derOut.toByteArray());
-
+        out.write(DerValue.tag_Sequence, temp.toByteArray());
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/pkcs/SignerInfo.java b/src/java.base/share/classes/sun/security/pkcs/SignerInfo.java
index 52b916b5bd639..4e965820fd47c 100644
--- a/src/java.base/share/classes/sun/security/pkcs/SignerInfo.java
+++ b/src/java.base/share/classes/sun/security/pkcs/SignerInfo.java
@@ -25,7 +25,6 @@
 
 package sun.security.pkcs;
 
-import java.io.OutputStream;
 import java.io.IOException;
 import java.math.BigInteger;
 import java.security.cert.CertPathValidatorException;
@@ -236,7 +235,7 @@ public void encode(DerOutputStream out) throws IOException {
      *
      * @exception IOException on encoding error.
      */
-    public void derEncode(OutputStream out) throws IOException {
+    public void derEncode(DerOutputStream out) throws IOException {
         DerOutputStream seq = new DerOutputStream();
         seq.putInteger(version);
         DerOutputStream issuerAndSerialNumber = new DerOutputStream();
@@ -258,10 +257,7 @@ public void derEncode(OutputStream out) throws IOException {
         if (unauthenticatedAttributes != null)
             unauthenticatedAttributes.encode((byte)0xA1, seq);
 
-        DerOutputStream tmp = new DerOutputStream();
-        tmp.write(DerValue.tag_Sequence, seq);
-
-        out.write(tmp.toByteArray());
+        out.write(DerValue.tag_Sequence, seq);
     }
 
     /*
diff --git a/src/java.base/share/classes/sun/security/pkcs10/PKCS10Attribute.java b/src/java.base/share/classes/sun/security/pkcs10/PKCS10Attribute.java
index c7cac3baa259e..b80457f8eb6d4 100644
--- a/src/java.base/share/classes/sun/security/pkcs10/PKCS10Attribute.java
+++ b/src/java.base/share/classes/sun/security/pkcs10/PKCS10Attribute.java
@@ -25,7 +25,6 @@
 
 package sun.security.pkcs10;
 
-import java.io.OutputStream;
 import java.io.IOException;
 
 import sun.security.pkcs.PKCS9Attribute;
@@ -108,7 +107,7 @@ public PKCS10Attribute(PKCS9Attribute attr) {
      *
      * @exception IOException on encoding errors.
      */
-    public void derEncode(OutputStream out) throws IOException {
+    public void derEncode(DerOutputStream out) throws IOException {
         PKCS9Attribute attr = new PKCS9Attribute(attributeId, attributeValue);
         attr.derEncode(out);
     }
diff --git a/src/java.base/share/classes/sun/security/pkcs10/PKCS10Attributes.java b/src/java.base/share/classes/sun/security/pkcs10/PKCS10Attributes.java
index fefffaf67f7af..c4d3fea6b8a97 100644
--- a/src/java.base/share/classes/sun/security/pkcs10/PKCS10Attributes.java
+++ b/src/java.base/share/classes/sun/security/pkcs10/PKCS10Attributes.java
@@ -26,7 +26,6 @@
 package sun.security.pkcs10;
 
 import java.io.IOException;
-import java.io.OutputStream;
 import java.security.cert.CertificateException;
 import java.util.Collection;
 import java.util.Collections;
@@ -92,7 +91,7 @@ public PKCS10Attributes(DerInputStream in) throws IOException {
      * @param out the OutputStream to marshal the contents to.
      * @exception IOException on encoding errors.
      */
-    public void encode(OutputStream out) throws IOException {
+    public void encode(DerOutputStream out) throws IOException {
         derEncode(out);
     }
 
@@ -103,17 +102,14 @@ public void encode(OutputStream out) throws IOException {
      * @param out the OutputStream to marshal the contents to.
      * @exception IOException on encoding errors.
      */
-    public void derEncode(OutputStream out) throws IOException {
+    public void derEncode(DerOutputStream out) throws IOException {
         // first copy the elements into an array
         Collection allAttrs = map.values();
         PKCS10Attribute[] attribs =
                 allAttrs.toArray(new PKCS10Attribute[map.size()]);
 
-        DerOutputStream attrOut = new DerOutputStream();
-        attrOut.putOrderedSetOf(DerValue.createTag(DerValue.TAG_CONTEXT,
-                                                   true, (byte)0),
-                                attribs);
-        out.write(attrOut.toByteArray());
+        out.putOrderedSetOf(
+                DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0), attribs);
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/rsa/RSAPadding.java b/src/java.base/share/classes/sun/security/rsa/RSAPadding.java
index 6954c2ad7eaeb..686073ac6862f 100644
--- a/src/java.base/share/classes/sun/security/rsa/RSAPadding.java
+++ b/src/java.base/share/classes/sun/security/rsa/RSAPadding.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -322,48 +322,103 @@ private byte[] padV15(byte[] data, int ofs, int len) {
      * Note that we want to make it a constant-time operation
      */
     private byte[] unpadV15(byte[] padded) {
-        int k = 0;
-        boolean bp = false;
+        int paddedLength = padded.length;
 
-        if (padded[k++] != 0) {
-            bp = true;
-        }
-        if (padded[k++] != type) {
-            bp = true;
+        if (paddedLength < 2) {
+            return null;
         }
-        int p = 0;
-        while (k < padded.length) {
+
+        // The following check ensures that the lead byte is zero and
+        // the second byte is equivalent to the padding type.  The
+        // bp (bad padding) variable throughout this unpadding process will
+        // be updated and remain 0 if good padding, 1 if bad.
+        int p0 = padded[0];
+        int p1 = padded[1];
+        int bp = (-(p0 & 0xff) | ((p1 - type) | (type - p1))) >>> 31;
+
+        int padLen = 0;
+        int k = 2;
+        // Walk through the random, nonzero padding bytes.  For each padding
+        // byte bp and padLen will remain zero.  When the end-of-padding
+        // byte (0x00) is reached then padLen will be set to the index of the
+        // first byte of the message content.
+        while (k < paddedLength) {
             int b = padded[k++] & 0xff;
-            if ((b == 0) && (p == 0)) {
-                p = k;
-            }
-            if ((k == padded.length) && (p == 0)) {
-                bp = true;
-            }
-            if ((type == PAD_BLOCKTYPE_1) && (b != 0xff) &&
-                    (p == 0)) {
-                bp = true;
+            padLen += (k * (1 - ((-(b | padLen)) >>> 31)));
+            if (k == paddedLength) {
+                bp = bp | (1 - ((-padLen) >>> 31));
             }
+            bp = bp | (1 - (-(((type - PAD_BLOCKTYPE_1) & 0xff) |
+                    padLen | (1 - ((b - 0xff) >>> 31))) >>> 31));
         }
-        int n = padded.length - p;
-        if (n > maxDataSize) {
-            bp = true;
-        }
+        int n = paddedLength - padLen;
+        // So long as n <= maxDataSize, bp will remain zero
+        bp = bp | ((maxDataSize - n) >>> 31);
 
         // copy useless padding array for a constant-time method
-        byte[] padding = new byte[p];
-        System.arraycopy(padded, 0, padding, 0, p);
+        byte[] padding = new byte[padLen + 2];
+        for (int i = 0; i < padLen; i++) {
+            padding[i] = padded[i];
+        }
 
         byte[] data = new byte[n];
-        System.arraycopy(padded, p, data, 0, n);
+        for (int i = 0; i < n; i++) {
+            data[i] = padded[padLen + i];
+        }
 
-        if (bp) {
+        if ((bp | padding[bp]) != 0) {
+            // using the array padding here hoping that this way
+            // the compiler does not eliminate the above useless copy
             return null;
         } else {
             return data;
         }
     }
 
+    public byte[] unpadForTls(byte[] padded, int clientVersion,
+            int serverVersion) {
+        int paddedLength = padded.length;
+
+        // bp is positive if the padding is bad and 0 if it is good so far
+        int bp = (((int) padded[0] | ((int)padded[1] - PAD_BLOCKTYPE_2)) &
+                0xFFF);
+
+        int k = 2;
+        while (k < paddedLength - 49) {
+            int b = padded[k++] & 0xFF;
+            bp = bp | (1 - (-b >>> 31)); // if (padded[k] == 0) bp |= 1;
+        }
+        bp |= ((int)padded[k++] & 0xFF);
+        int encodedVersion = ((padded[k] & 0xFF) << 8) | (padded[k + 1] & 0xFF);
+
+        int bv1 = clientVersion - encodedVersion;
+        bv1 |= -bv1;
+        int bv3 = serverVersion - encodedVersion;
+        bv3 |= -bv3;
+        int bv2 = (0x301 - clientVersion);
+
+        bp |= ((bv1 & (bv2 | bv3)) >>> 28);
+
+        byte[] data = Arrays.copyOfRange(padded, paddedLength - 48,
+                paddedLength);
+        if (random == null) {
+            random = JCAUtil.getSecureRandom();
+        }
+
+        byte[] fake = new byte[48];
+        random.nextBytes(fake);
+
+        bp = (-bp >> 24);
+
+        // Now bp is 0 if the padding and version number were good and
+        // -1 otherwise.
+        for (int i = 0; i < 48; i++) {
+            data[i] = (byte)((~bp & data[i]) | (bp & fake[i]));
+        }
+
+        return data;
+    }
+
     /**
      * PKCS#1 v2.0 OAEP padding (MGF1).
      * Paragraph references refer to PKCS#1 v2.1 (June 14, 2002)
diff --git a/src/java.base/share/classes/sun/security/ssl/CertSignAlgsExtension.java b/src/java.base/share/classes/sun/security/ssl/CertSignAlgsExtension.java
index ee20f8f0faae5..b975290d09d85 100644
--- a/src/java.base/share/classes/sun/security/ssl/CertSignAlgsExtension.java
+++ b/src/java.base/share/classes/sun/security/ssl/CertSignAlgsExtension.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,8 @@
 
 package sun.security.ssl;
 
+import static sun.security.ssl.SignatureScheme.CERTIFICATE_SCOPE;
+
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.List;
@@ -97,26 +99,21 @@ public byte[] produce(ConnectionContext context,
             }
 
             // Produce the extension.
-            if (chc.localSupportedSignAlgs == null) {
-                chc.localSupportedSignAlgs =
-                    SignatureScheme.getSupportedAlgorithms(
-                            chc.sslConfig,
-                            chc.algorithmConstraints, chc.activeProtocols);
-            }
+            SignatureScheme.updateHandshakeLocalSupportedAlgs(chc);
 
             int vectorLen = SignatureScheme.sizeInRecord() *
-                    chc.localSupportedSignAlgs.size();
+                    chc.localSupportedCertSignAlgs.size();
             byte[] extData = new byte[vectorLen + 2];
             ByteBuffer m = ByteBuffer.wrap(extData);
             Record.putInt16(m, vectorLen);
-            for (SignatureScheme ss : chc.localSupportedSignAlgs) {
+            for (SignatureScheme ss : chc.localSupportedCertSignAlgs) {
                 Record.putInt16(m, ss.id);
             }
 
             // Update the context.
             chc.handshakeExtensions.put(
                     SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT,
-                    new SignatureSchemesSpec(chc.localSupportedSignAlgs));
+                    new SignatureSchemesSpec(chc.localSupportedCertSignAlgs));
 
             return extData;
         }
@@ -191,7 +188,9 @@ public void consume(ConnectionContext context,
                     SignatureScheme.getSupportedAlgorithms(
                             shc.sslConfig,
                             shc.algorithmConstraints, shc.negotiatedProtocol,
-                            spec.signatureSchemes);
+                            spec.signatureSchemes,
+                            CERTIFICATE_SCOPE);
+
             shc.peerRequestedCertSignSchemes = schemes;
             shc.handshakeSession.setPeerSupportedSignatureAlgorithms(schemes);
 
@@ -240,24 +239,21 @@ public byte[] produce(ConnectionContext context,
             }
 
             // Produce the extension.
-            List sigAlgs =
-                    SignatureScheme.getSupportedAlgorithms(
-                            shc.sslConfig,
-                            shc.algorithmConstraints,
-                            List.of(shc.negotiatedProtocol));
-
-            int vectorLen = SignatureScheme.sizeInRecord() * sigAlgs.size();
+            // localSupportedCertSignAlgs has been already updated when we set
+            // the negotiated protocol.
+            int vectorLen = SignatureScheme.sizeInRecord()
+                    * shc.localSupportedCertSignAlgs.size();
             byte[] extData = new byte[vectorLen + 2];
             ByteBuffer m = ByteBuffer.wrap(extData);
             Record.putInt16(m, vectorLen);
-            for (SignatureScheme ss : sigAlgs) {
+            for (SignatureScheme ss : shc.localSupportedCertSignAlgs) {
                 Record.putInt16(m, ss.id);
             }
 
             // Update the context.
             shc.handshakeExtensions.put(
                     SSLExtension.CR_SIGNATURE_ALGORITHMS_CERT,
-                    new SignatureSchemesSpec(shc.localSupportedSignAlgs));
+                    new SignatureSchemesSpec(shc.localSupportedCertSignAlgs));
 
             return extData;
         }
@@ -331,7 +327,9 @@ public void consume(ConnectionContext context,
                     SignatureScheme.getSupportedAlgorithms(
                             chc.sslConfig,
                             chc.algorithmConstraints, chc.negotiatedProtocol,
-                            spec.signatureSchemes);
+                            spec.signatureSchemes,
+                            CERTIFICATE_SCOPE);
+
             chc.peerRequestedCertSignSchemes = schemes;
             chc.handshakeSession.setPeerSupportedSignatureAlgorithms(schemes);
         }
diff --git a/src/java.base/share/classes/sun/security/ssl/CertificateAuthoritiesExtension.java b/src/java.base/share/classes/sun/security/ssl/CertificateAuthoritiesExtension.java
index 760daf4b3a152..1032685f9138a 100644
--- a/src/java.base/share/classes/sun/security/ssl/CertificateAuthoritiesExtension.java
+++ b/src/java.base/share/classes/sun/security/ssl/CertificateAuthoritiesExtension.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -122,8 +122,11 @@ private static List getEncodedAuthorities(
             return authorities;
         }
 
+        // This method will throw IllegalArgumentException if the
+        // X500Principal cannot be parsed.
         X500Principal[] getAuthorities() {
             X500Principal[] principals = new X500Principal[authorities.size()];
+
             int i = 0;
             for (byte[] encoded : authorities) {
                 principals[i++] = new X500Principal(encoded);
@@ -138,8 +141,12 @@ public String toString() {
                 "\"certificate authorities\": '['\n{0}']'", Locale.ENGLISH);
             StringBuilder builder = new StringBuilder(512);
             for (byte[] encoded : authorities) {
-                X500Principal principal = new X500Principal(encoded);
-                builder.append(principal.toString());
+                try {
+                    X500Principal principal = new X500Principal(encoded);
+                    builder.append(principal.toString());
+                } catch (IllegalArgumentException iae) {
+                    builder.append("unparseable distinguished name: " + iae);
+                }
                 builder.append("\n");
             }
             Object[] messageFields = {
@@ -277,7 +284,13 @@ public void consume(ConnectionContext context,
                     new CertificateAuthoritiesSpec(shc, buffer);
 
             // Update the context.
-            shc.peerSupportedAuthorities = spec.getAuthorities();
+            try {
+                shc.peerSupportedAuthorities = spec.getAuthorities();
+            } catch (IllegalArgumentException iae) {
+                shc.conContext.fatal(Alert.DECODE_ERROR, "The distinguished " +
+                        "names of the peer's certificate authorities could " +
+                        "not be parsed", iae);
+            }
             shc.handshakeExtensions.put(
                     SSLExtension.CH_CERTIFICATE_AUTHORITIES, spec);
 
@@ -398,7 +411,13 @@ public void consume(ConnectionContext context,
                     new CertificateAuthoritiesSpec(chc, buffer);
 
             // Update the context.
-            chc.peerSupportedAuthorities = spec.getAuthorities();
+            try {
+                chc.peerSupportedAuthorities = spec.getAuthorities();
+            } catch (IllegalArgumentException iae) {
+                chc.conContext.fatal(Alert.DECODE_ERROR, "The distinguished " +
+                        "names of the peer's certificate authorities could " +
+                        "not be parsed", iae);
+            }
             chc.handshakeExtensions.put(
                     SSLExtension.CR_CERTIFICATE_AUTHORITIES, spec);
 
diff --git a/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java b/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java
index 840c0055b9acb..99f6be3582670 100644
--- a/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java
+++ b/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -701,48 +701,6 @@ private static void checkClientCerts(ServerHandshakeContext shc,
             }
         }
 
-        /**
-         * When a failure happens during certificate checking from an
-         * {@link X509TrustManager}, determine what TLS alert description
-         * to use.
-         *
-         * @param cexc The exception thrown by the {@link X509TrustManager}
-         *
-         * @return A byte value corresponding to a TLS alert description number.
-         */
-        private static Alert getCertificateAlert(
-                ClientHandshakeContext chc, CertificateException cexc) {
-            // The specific reason for the failure will determine how to
-            // set the alert description value
-            Alert alert = Alert.CERTIFICATE_UNKNOWN;
-
-            Throwable baseCause = cexc.getCause();
-            if (baseCause instanceof CertPathValidatorException) {
-                CertPathValidatorException cpve =
-                        (CertPathValidatorException)baseCause;
-                Reason reason = cpve.getReason();
-                if (reason == BasicReason.REVOKED) {
-                    alert = chc.staplingActive ?
-                            Alert.BAD_CERT_STATUS_RESPONSE :
-                            Alert.CERTIFICATE_REVOKED;
-                } else if (
-                        reason == BasicReason.UNDETERMINED_REVOCATION_STATUS) {
-                    alert = chc.staplingActive ?
-                            Alert.BAD_CERT_STATUS_RESPONSE :
-                            Alert.CERTIFICATE_UNKNOWN;
-                } else if (reason == BasicReason.ALGORITHM_CONSTRAINED) {
-                    alert = Alert.UNSUPPORTED_CERTIFICATE;
-                } else if (reason == BasicReason.EXPIRED) {
-                    alert = Alert.CERTIFICATE_EXPIRED;
-                } else if (reason == BasicReason.INVALID_SIGNATURE ||
-                        reason == BasicReason.NOT_YET_VALID) {
-                    alert = Alert.BAD_CERTIFICATE;
-                }
-            }
-
-            return alert;
-        }
-
     }
 
     /**
@@ -1042,46 +1000,22 @@ private static SSLPossession choosePossession(
                 return null;
             }
 
-            Collection checkedKeyTypes = new HashSet<>();
-            List supportedKeyTypes = new ArrayList<>();
-            for (SignatureScheme ss : hc.peerRequestedCertSignSchemes) {
-                if (checkedKeyTypes.contains(ss.keyAlgorithm)) {
-                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
-                        SSLLogger.warning(
-                            "Unsupported authentication scheme: " + ss.name);
-                    }
-                    continue;
-                }
-                checkedKeyTypes.add(ss.keyAlgorithm);
-
-                // Don't select a signature scheme unless we will be able to
-                // produce a CertificateVerify message later
-                if (SignatureScheme.getPreferableAlgorithm(
-                        hc.algorithmConstraints,
-                        hc.peerRequestedSignatureSchemes,
-                        ss, hc.negotiatedProtocol) == null) {
-
-                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
-                        SSLLogger.warning(
-                            "Unable to produce CertificateVerify for " +
-                            "signature scheme: " + ss.name);
-                    }
-                    continue;
-                }
-
-                X509Authentication ka = X509Authentication.valueOf(ss);
-                if (ka == null) {
-                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
-                        SSLLogger.warning(
-                            "Unsupported authentication scheme: " + ss.name);
-                    }
-                    continue;
-                }
-                supportedKeyTypes.add(ss.keyAlgorithm);
-            }
+            String[] supportedKeyTypes = hc.peerRequestedCertSignSchemes
+                    .stream()
+                    .map(ss -> ss.keyAlgorithm)
+                    .distinct()
+                    .filter(ka -> SignatureScheme.getPreferableAlgorithm(   // Don't select a signature scheme unless
+                            hc.algorithmConstraints,                        //  we will be able to produce
+                            hc.peerRequestedSignatureSchemes,               //  a CertificateVerify message later
+                            ka, hc.negotiatedProtocol) != null
+                            || SSLLogger.logWarning("ssl,handshake",
+                                    "Unable to produce CertificateVerify for key algorithm: " + ka))
+                    .filter(ka -> X509Authentication.valueOfKeyAlgorithm(ka) != null
+                            || SSLLogger.logWarning("ssl,handshake", "Unsupported key algorithm: " + ka))
+                    .toArray(String[]::new);
 
             SSLPossession pos = X509Authentication
-                    .createPossession(hc, supportedKeyTypes.toArray(String[]::new));
+                    .createPossession(hc, supportedKeyTypes);
             if (pos == null) {
                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
                     SSLLogger.warning("No available authentication scheme");
@@ -1160,6 +1094,15 @@ public void consume(ConnectionContext context,
 
             // clean up this consumer
             hc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE.id);
+
+            // Ensure that the Certificate message has not been sent w/o
+            // an EncryptedExtensions preceding
+            if (hc.handshakeConsumers.containsKey(
+                    SSLHandshake.ENCRYPTED_EXTENSIONS.id)) {
+                throw hc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                                           "Unexpected Certificate handshake message");
+            }
+
             T13CertificateMessage cm = new T13CertificateMessage(hc, message);
             if (hc.sslConfig.isClientMode) {
                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
@@ -1354,39 +1297,59 @@ private static X509Certificate[] checkServerCerts(
             return certs;
         }
 
-        /**
-         * When a failure happens during certificate checking from an
-         * {@link X509TrustManager}, determine what TLS alert description
-         * to use.
-         *
-         * @param cexc The exception thrown by the {@link X509TrustManager}
-         *
-         * @return A byte value corresponding to a TLS alert description number.
-         */
-        private static Alert getCertificateAlert(
-                ClientHandshakeContext chc, CertificateException cexc) {
-            // The specific reason for the failure will determine how to
-            // set the alert description value
-            Alert alert = Alert.CERTIFICATE_UNKNOWN;
-
-            Throwable baseCause = cexc.getCause();
-            if (baseCause instanceof CertPathValidatorException) {
-                CertPathValidatorException cpve =
-                        (CertPathValidatorException)baseCause;
-                Reason reason = cpve.getReason();
-                if (reason == BasicReason.REVOKED) {
-                    alert = chc.staplingActive ?
-                            Alert.BAD_CERT_STATUS_RESPONSE :
-                            Alert.CERTIFICATE_REVOKED;
-                } else if (
-                        reason == BasicReason.UNDETERMINED_REVOCATION_STATUS) {
-                    alert = chc.staplingActive ?
-                            Alert.BAD_CERT_STATUS_RESPONSE :
-                            Alert.CERTIFICATE_UNKNOWN;
+    }
+
+    /**
+     * When a failure happens during certificate checking from an
+     * {@link X509TrustManager}, determine what TLS alert description
+     * to use.
+     *
+     * @param cexc The exception thrown by the {@link X509TrustManager}
+     * @return A byte value corresponding to a TLS alert description number.
+     */
+    private static Alert getCertificateAlert(
+            ClientHandshakeContext chc, CertificateException cexc) {
+        // The specific reason for the failure will determine how to
+        // set the alert description value
+        Alert alert = Alert.CERTIFICATE_UNKNOWN;
+
+        Throwable baseCause = cexc.getCause();
+        if (baseCause instanceof CertPathValidatorException) {
+            CertPathValidatorException cpve =
+                (CertPathValidatorException)baseCause;
+            Reason reason = cpve.getReason();
+            if (reason == BasicReason.REVOKED) {
+                alert = chc.staplingActive ?
+                        Alert.BAD_CERT_STATUS_RESPONSE :
+                        Alert.CERTIFICATE_REVOKED;
+            } else if (reason == BasicReason.UNDETERMINED_REVOCATION_STATUS) {
+                alert = chc.staplingActive ?
+                        Alert.BAD_CERT_STATUS_RESPONSE :
+                        Alert.CERTIFICATE_UNKNOWN;
+            } else if (reason == BasicReason.EXPIRED) {
+                alert = Alert.CERTIFICATE_EXPIRED;
+            } else if (reason == BasicReason.INVALID_SIGNATURE
+                    || reason == BasicReason.NOT_YET_VALID) {
+                alert = Alert.BAD_CERTIFICATE;
+            } else if (reason == BasicReason.ALGORITHM_CONSTRAINED) {
+                alert = Alert.UNSUPPORTED_CERTIFICATE;
+
+                // Per TLSv1.3 RFC we MUST abort the handshake with a
+                // "bad_certificate" alert if we reject certificate
+                // because of the signature using MD5 or SHA1 algorithm.
+                if (chc.negotiatedProtocol != null
+                        && chc.negotiatedProtocol.useTLS13PlusSpec()) {
+                    final String exMsg = cexc.getMessage().toUpperCase();
+
+                    if (exMsg.contains("MD5WITH")
+                            || exMsg.contains("SHA1WITH")) {
+                        alert = Alert.BAD_CERTIFICATE;
+                    }
                 }
             }
-
-            return alert;
         }
+
+        return alert;
     }
+
 }
diff --git a/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java b/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java
index 2134506f8ea99..c2c31b3380386 100644
--- a/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java
+++ b/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,9 @@
 
 package sun.security.ssl;
 
+import static sun.security.ssl.SignatureScheme.CERTIFICATE_SCOPE;
+import static sun.security.ssl.SignatureScheme.HANDSHAKE_SCOPE;
+
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.security.PrivateKey;
@@ -32,9 +35,7 @@
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
@@ -205,9 +206,12 @@ String[] getKeyTypes() {
             return  ClientCertificateType.getKeyTypes(types);
         }
 
+        // This method will throw IllegalArgumentException if the
+        // X500Principal cannot be parsed.
         X500Principal[] getAuthorities() {
             X500Principal[] principals = new X500Principal[authorities.size()];
             int i = 0;
+
             for (byte[] encoded : authorities) {
                 principals[i++] = new X500Principal(encoded);
             }
@@ -260,8 +264,12 @@ public String toString() {
 
             List authorityNames = new ArrayList<>(authorities.size());
             for (byte[] encoded : authorities) {
-                X500Principal principal = new X500Principal(encoded);
-                authorityNames.add(principal.toString());
+                try {
+                    X500Principal principal = new X500Principal(encoded);
+                    authorityNames.add(principal.toString());
+                } catch (IllegalArgumentException iae) {
+                    authorityNames.add("unparseable distinguished name: " + iae);
+                }
             }
             Object[] messageFields = {
                 typeNames,
@@ -376,14 +384,24 @@ public void consume(ConnectionContext context,
 
             X509ExtendedKeyManager km = chc.sslContext.getX509KeyManager();
             String clientAlias = null;
-            if (chc.conContext.transport instanceof SSLSocketImpl) {
-                clientAlias = km.chooseClientAlias(crm.getKeyTypes(),
-                    crm.getAuthorities(), (SSLSocket)chc.conContext.transport);
-            } else if (chc.conContext.transport instanceof SSLEngineImpl) {
-                clientAlias = km.chooseEngineClientAlias(crm.getKeyTypes(),
-                    crm.getAuthorities(), (SSLEngine)chc.conContext.transport);
-            }
 
+            try {
+                if (chc.conContext.transport instanceof SSLSocketImpl) {
+                    clientAlias = km.chooseClientAlias(crm.getKeyTypes(),
+                        crm.getAuthorities(),
+                        (SSLSocket) chc.conContext.transport);
+                } else if (chc.conContext.transport instanceof SSLEngineImpl) {
+                    clientAlias =
+                        km.chooseEngineClientAlias(crm.getKeyTypes(),
+                            crm.getAuthorities(),
+                            (SSLEngine) chc.conContext.transport);
+                }
+            } catch (IllegalArgumentException iae) {
+                chc.conContext.fatal(Alert.DECODE_ERROR,
+                    "The distinguished names of the peer's "
+                    + "certificate authorities could not be parsed",
+                        iae);
+            }
 
             if (clientAlias == null) {
                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
@@ -518,9 +536,12 @@ String[] getKeyTypes() {
             return ClientCertificateType.getKeyTypes(types);
         }
 
+        // This method will throw IllegalArgumentException if the
+        // X500Principal cannot be parsed.
         X500Principal[] getAuthorities() {
             X500Principal[] principals = new X500Principal[authorities.size()];
             int i = 0;
+
             for (byte[] encoded : authorities) {
                 principals[i++] = new X500Principal(encoded);
             }
@@ -584,8 +605,13 @@ public String toString() {
 
             List authorityNames = new ArrayList<>(authorities.size());
             for (byte[] encoded : authorities) {
-                X500Principal principal = new X500Principal(encoded);
-                authorityNames.add(principal.toString());
+                try {
+                    X500Principal principal = new X500Principal(encoded);
+                    authorityNames.add(principal.toString());
+                } catch (IllegalArgumentException iae) {
+                    authorityNames.add("unparseable distinguished name: " +
+                        iae);
+                }
             }
             Object[] messageFields = {
                 typeNames,
@@ -611,16 +637,19 @@ private T12CertificateRequestProducer() {
         public byte[] produce(ConnectionContext context,
                 HandshakeMessage message) throws IOException {
             // The producing happens in server side only.
-            ServerHandshakeContext shc = (ServerHandshakeContext)context;
-            if (shc.localSupportedSignAlgs == null) {
-                shc.localSupportedSignAlgs =
-                    SignatureScheme.getSupportedAlgorithms(
-                            shc.sslConfig,
-                            shc.algorithmConstraints, shc.activeProtocols);
-            }
-
-            if (shc.localSupportedSignAlgs == null ||
-                    shc.localSupportedSignAlgs.isEmpty()) {
+            ServerHandshakeContext shc = (ServerHandshakeContext) context;
+
+            // According to TLSv1.2 RFC, CertificateRequest message must
+            // contain signature schemes supported for both:
+            // handshake signatures and certificate signatures.
+            // localSupportedSignAlgs and localSupportedCertSignAlgs have been
+            // already updated when we set the negotiated protocol.
+            List certReqSignAlgs =
+                    new ArrayList<>(shc.localSupportedSignAlgs);
+            certReqSignAlgs.retainAll(shc.localSupportedCertSignAlgs);
+
+            if (certReqSignAlgs == null ||
+                    certReqSignAlgs.isEmpty()) {
                 throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
                     "No supported signature algorithm");
             }
@@ -629,7 +658,7 @@ public byte[] produce(ConnectionContext context,
                     shc.sslContext.getX509TrustManager().getAcceptedIssuers();
             T12CertificateRequestMessage crm = new T12CertificateRequestMessage(
                     shc, caCerts, shc.negotiatedCipherSuite.keyExchange,
-                    shc.localSupportedSignAlgs);
+                    certReqSignAlgs);
             if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
                 SSLLogger.fine(
                     "Produced CertificateRequest handshake message", crm);
@@ -710,21 +739,36 @@ public void consume(ConnectionContext context,
             chc.handshakeProducers.put(SSLHandshake.CERTIFICATE.id,
                     SSLHandshake.CERTIFICATE);
 
-            List sss =
+            List signAlgs =
                     SignatureScheme.getSupportedAlgorithms(
                             chc.sslConfig,
                             chc.algorithmConstraints, chc.negotiatedProtocol,
-                            crm.algorithmIds);
-            if (sss == null || sss.isEmpty()) {
+                            crm.algorithmIds,
+                            HANDSHAKE_SCOPE);
+
+            List signCertAlgs =
+                    SignatureScheme.getSupportedAlgorithms(
+                            chc.sslConfig,
+                            chc.algorithmConstraints, chc.negotiatedProtocol,
+                            crm.algorithmIds,
+                            CERTIFICATE_SCOPE);
+
+            if (signAlgs == null || signAlgs.isEmpty() || signCertAlgs.isEmpty()) {
                 throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
                         "No supported signature algorithm");
             }
 
-            chc.peerRequestedSignatureSchemes = sss;
-            chc.peerRequestedCertSignSchemes = sss;     // use the same schemes
-            chc.handshakeSession.setPeerSupportedSignatureAlgorithms(sss);
-            chc.peerSupportedAuthorities = crm.getAuthorities();
+            chc.peerRequestedSignatureSchemes = signAlgs;
+            chc.peerRequestedCertSignSchemes = signCertAlgs;
+            chc.handshakeSession.setPeerSupportedSignatureAlgorithms(signCertAlgs);
 
+            try {
+                chc.peerSupportedAuthorities = crm.getAuthorities();
+            } catch (IllegalArgumentException iae) {
+                chc.conContext.fatal(Alert.DECODE_ERROR, "The "
+                    + "distinguished names of the peer's certificate "
+                    + "authorities could not be parsed", iae);
+            }
             // For TLS 1.2, we no longer use the certificate_types field
             // from the CertificateRequest message to directly determine
             // the SSLPossession.  Instead, the choosePossession method
@@ -760,59 +804,28 @@ private static SSLPossession choosePossession(HandshakeContext hc,
                 crKeyTypes.add("RSASSA-PSS");
             }
 
-            Collection checkedKeyTypes = new HashSet<>();
-            List supportedKeyTypes = new ArrayList<>();
-            for (SignatureScheme ss : hc.peerRequestedCertSignSchemes) {
-                if (checkedKeyTypes.contains(ss.keyAlgorithm)) {
-                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
-                        SSLLogger.warning(
-                            "Unsupported authentication scheme: " + ss.name);
-                    }
-                    continue;
-                }
-                checkedKeyTypes.add(ss.keyAlgorithm);
-
-                // Don't select a signature scheme unless we will be able to
-                // produce a CertificateVerify message later
-                if (SignatureScheme.getPreferableAlgorithm(
-                        hc.algorithmConstraints,
-                        hc.peerRequestedSignatureSchemes,
-                        ss, hc.negotiatedProtocol) == null) {
-
-                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
-                        SSLLogger.warning(
-                            "Unable to produce CertificateVerify for " +
-                            "signature scheme: " + ss.name);
-                    }
-                    continue;
-                }
-
-                X509Authentication ka = X509Authentication.valueOf(ss);
-                if (ka == null) {
-                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
-                        SSLLogger.warning(
-                            "Unsupported authentication scheme: " + ss.name);
-                    }
-                    continue;
-                } else {
-                    // Any auth object will have a set of allowed key types.
-                    // This set should share at least one common algorithm with
-                    // the CR's allowed key types.
-                    if (Collections.disjoint(crKeyTypes,
-                            Arrays.asList(ka.keyTypes))) {
-                        if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
-                            SSLLogger.warning(
-                                    "Unsupported authentication scheme: " +
-                                            ss.name);
-                        }
-                        continue;
-                    }
-                }
-                supportedKeyTypes.add(ss.keyAlgorithm);
-            }
+            String[] supportedKeyTypes = hc.peerRequestedCertSignSchemes
+                    .stream()
+                    .map(ss -> ss.keyAlgorithm)
+                    .distinct()
+                    .filter(ka -> SignatureScheme.getPreferableAlgorithm(   // Don't select a signature scheme unless
+                            hc.algorithmConstraints,                        //  we will be able to produce
+                            hc.peerRequestedSignatureSchemes,               //  a CertificateVerify message later
+                            ka, hc.negotiatedProtocol) != null
+                            || SSLLogger.logWarning("ssl,handshake",
+                                    "Unable to produce CertificateVerify for key algorithm: " + ka))
+                    .filter(ka -> {
+                        var xa = X509Authentication.valueOfKeyAlgorithm(ka);
+                        // Any auth object will have a set of allowed key types.
+                        // This set should share at least one common algorithm with
+                        // the CR's allowed key types.
+                        return xa != null && !Collections.disjoint(crKeyTypes, Arrays.asList(xa.keyTypes))
+                                || SSLLogger.logWarning("ssl,handshake", "Unsupported key algorithm: " + ka);
+                    })
+                    .toArray(String[]::new);
 
             SSLPossession pos = X509Authentication
-                    .createPossession(hc, supportedKeyTypes.toArray(String[]::new));
+                    .createPossession(hc, supportedKeyTypes);
             if (pos == null) {
                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
                     SSLLogger.warning("No available authentication scheme");
diff --git a/src/java.base/share/classes/sun/security/ssl/CertificateVerify.java b/src/java.base/share/classes/sun/security/ssl/CertificateVerify.java
index 66f651e28aecf..6041ddf6873f9 100644
--- a/src/java.base/share/classes/sun/security/ssl/CertificateVerify.java
+++ b/src/java.base/share/classes/sun/security/ssl/CertificateVerify.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -1160,6 +1160,14 @@ public void consume(ConnectionContext context,
             // Clean up this consumer
             hc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE_VERIFY.id);
 
+            // Ensure that the Certificate Verify message has not been sent w/o
+            // a Certificate message preceding
+            if (hc.handshakeConsumers.containsKey(
+                    SSLHandshake.CERTIFICATE.id)) {
+                throw hc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                                  "Unexpected Certificate Verify handshake message");
+            }
+
             T13CertificateVerifyMessage cvm =
                     new T13CertificateVerifyMessage(hc, message);
             if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
diff --git a/src/java.base/share/classes/sun/security/ssl/ClientHello.java b/src/java.base/share/classes/sun/security/ssl/ClientHello.java
index 3a2641b6ec6c8..dcd866bb7da1a 100644
--- a/src/java.base/share/classes/sun/security/ssl/ClientHello.java
+++ b/src/java.base/share/classes/sun/security/ssl/ClientHello.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -826,6 +826,10 @@ private void onClientHello(ServerHandshakeContext context,
                     "Negotiated protocol version: " + negotiatedProtocol.name);
             }
 
+            // Protocol version is negotiated, update locally supported
+            // signature schemes according to the protocol being used.
+            SignatureScheme.updateHandshakeLocalSupportedAlgs(context);
+
             // Consume the handshake message for the specific protocol version.
             if (negotiatedProtocol.isDTLS) {
                 if (negotiatedProtocol.useTLS13PlusSpec()) {
diff --git a/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java b/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java
index 123b1667dfd12..7747cbfa87c97 100644
--- a/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java
+++ b/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -809,8 +809,11 @@ void queueUpHandshake(HandshakeFragment hsf) throws SSLProtocolException {
 
             // buffer this fragment
             if (hsf.handshakeType == SSLHandshake.FINISHED.id) {
-                // Need no status update.
-                bufferedFragments.add(hsf);
+                // Make sure it's not a retransmitted message
+                if (hsf.recordEpoch > handshakeEpoch) {
+                    bufferedFragments.add(hsf);
+                    flightIsReady = holes.isEmpty();
+                }
             } else {
                 bufferFragment(hsf);
             }
diff --git a/src/java.base/share/classes/sun/security/ssl/Finished.java b/src/java.base/share/classes/sun/security/ssl/Finished.java
index a1ecca519204d..64949ae62511f 100644
--- a/src/java.base/share/classes/sun/security/ssl/Finished.java
+++ b/src/java.base/share/classes/sun/security/ssl/Finished.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -900,6 +900,14 @@ public void consume(ConnectionContext context,
 
         private void onConsumeFinished(ClientHandshakeContext chc,
                 ByteBuffer message) throws IOException {
+            // Ensure that the Finished message has not been sent w/o
+            // an EncryptedExtensions preceding
+            if (chc.handshakeConsumers.containsKey(
+                    SSLHandshake.ENCRYPTED_EXTENSIONS.id)) {
+                throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                                           "Unexpected Finished handshake message");
+            }
+
             // Make sure that any expected CertificateVerify message
             // has been received and processed.
             if (!chc.isResumption) {
diff --git a/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java b/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java
index c8437cd72bd34..40b86ae1e4919 100644
--- a/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java
+++ b/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -82,7 +82,7 @@ abstract class HandshakeContext implements ConnectionContext {
     // consolidated parameters
     final List             activeProtocols;
     final List                 activeCipherSuites;
-    final AlgorithmConstraints              algorithmConstraints;
+    final SSLAlgorithmConstraints           algorithmConstraints;
     final ProtocolVersion                   maximumActiveProtocol;
 
     // output stream
@@ -137,6 +137,7 @@ abstract class HandshakeContext implements ConnectionContext {
 
     // SignatureScheme
     List                   localSupportedSignAlgs;
+    List                   localSupportedCertSignAlgs;
     List                   peerRequestedSignatureSchemes;
     List                   peerRequestedCertSignSchemes;
 
diff --git a/src/java.base/share/classes/sun/security/ssl/PostHandshakeContext.java b/src/java.base/share/classes/sun/security/ssl/PostHandshakeContext.java
index e0af48719d256..b06549b40e3e9 100644
--- a/src/java.base/share/classes/sun/security/ssl/PostHandshakeContext.java
+++ b/src/java.base/share/classes/sun/security/ssl/PostHandshakeContext.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -43,7 +43,7 @@ final class PostHandshakeContext extends HandshakeContext {
                 "Post-handshake not supported in " + negotiatedProtocol.name);
         }
 
-        this.localSupportedSignAlgs = new ArrayList<>(
+        this.localSupportedCertSignAlgs = new ArrayList<>(
             context.conSession.getLocalSupportedSignatureSchemes());
 
         // Add the potential post-handshake consumers.
diff --git a/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java b/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java
index 0b3481fbb19a3..73af49b95aae1 100644
--- a/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java
+++ b/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -442,16 +442,11 @@ private static boolean canRejoin(ClientHelloMessage clientHello,
             result = false;
         }
 
-        // Make sure that the server handshake context's localSupportedSignAlgs
-        // field is populated.  This is particularly important when
-        // client authentication was used in an initial session and it is
-        // now being resumed.
-        if (shc.localSupportedSignAlgs == null) {
-            shc.localSupportedSignAlgs =
-                    SignatureScheme.getSupportedAlgorithms(
-                            shc.sslConfig,
-                            shc.algorithmConstraints, shc.activeProtocols);
-        }
+        // Make sure that the server handshake context's
+        // localSupportedCertSignAlgs field is populated.  This is particularly
+        // important when client authentication was used in an initial session,
+        // and it is now being resumed.
+        SignatureScheme.updateHandshakeLocalSupportedAlgs(shc);
 
         // Validate the required client authentication.
         if (result &&
@@ -472,7 +467,7 @@ private static boolean canRejoin(ClientHelloMessage clientHello,
             Collection sessionSigAlgs =
                 s.getLocalSupportedSignatureSchemes();
             if (result &&
-                !shc.localSupportedSignAlgs.containsAll(sessionSigAlgs)) {
+                !shc.localSupportedCertSignAlgs.containsAll(sessionSigAlgs)) {
 
                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
                     SSLLogger.fine("Can't resume. Session uses different " +
@@ -666,7 +661,7 @@ public byte[] produce(ConnectionContext context,
             // Make sure the list of supported signature algorithms matches
             Collection sessionSigAlgs =
                 chc.resumingSession.getLocalSupportedSignatureSchemes();
-            if (!chc.localSupportedSignAlgs.containsAll(sessionSigAlgs)) {
+            if (!chc.localSupportedCertSignAlgs.containsAll(sessionSigAlgs)) {
                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
                     SSLLogger.fine("Existing session uses different " +
                         "signature algorithms");
diff --git a/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java b/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java
index c226e3dee8d32..88cdfbca5ff25 100644
--- a/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java
+++ b/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -42,11 +42,11 @@
  */
 final class SSLAlgorithmConstraints implements AlgorithmConstraints {
 
-    private static final AlgorithmConstraints tlsDisabledAlgConstraints =
+    private static final DisabledAlgorithmConstraints tlsDisabledAlgConstraints =
             new DisabledAlgorithmConstraints(PROPERTY_TLS_DISABLED_ALGS,
                     new SSLAlgorithmDecomposer());
 
-    private static final AlgorithmConstraints x509DisabledAlgConstraints =
+    private static final DisabledAlgorithmConstraints x509DisabledAlgConstraints =
             new DisabledAlgorithmConstraints(PROPERTY_CERTPATH_DISABLED_ALGS,
                     new SSLAlgorithmDecomposer(true));
 
@@ -56,11 +56,11 @@ final class SSLAlgorithmConstraints implements AlgorithmConstraints {
     private final boolean enabledX509DisabledAlgConstraints;
 
     // the default algorithm constraints
-    static final AlgorithmConstraints DEFAULT =
+    static final SSLAlgorithmConstraints DEFAULT =
                         new SSLAlgorithmConstraints(null, true);
 
     // the default SSL only algorithm constraints
-    static final AlgorithmConstraints DEFAULT_SSL_ONLY =
+    static final SSLAlgorithmConstraints DEFAULT_SSL_ONLY =
                         new SSLAlgorithmConstraints(null, false);
 
     private SSLAlgorithmConstraints(AlgorithmConstraints userSpecifiedConstraints,
@@ -84,11 +84,11 @@ private SSLAlgorithmConstraints(
      * @param userSpecifiedConstraints additional constraints to check
      * @return a SSLAlgorithmConstraints instance
      */
-    static AlgorithmConstraints wrap(AlgorithmConstraints userSpecifiedConstraints) {
+    static SSLAlgorithmConstraints wrap(AlgorithmConstraints userSpecifiedConstraints) {
         return wrap(userSpecifiedConstraints, true);
     }
 
-    private static AlgorithmConstraints wrap(
+    private static SSLAlgorithmConstraints wrap(
             AlgorithmConstraints userSpecifiedConstraints,
             boolean withDefaultCertPathConstraints) {
         if (nullIfDefault(userSpecifiedConstraints) == null) {
@@ -199,22 +199,22 @@ public boolean permits(Set primitives,
 
         if (peerSpecifiedConstraints != null) {
             permitted = peerSpecifiedConstraints.permits(
-                                    primitives, algorithm, parameters);
+                    primitives, algorithm, parameters);
         }
 
         if (permitted && userSpecifiedConstraints != null) {
             permitted = userSpecifiedConstraints.permits(
-                                    primitives, algorithm, parameters);
+                    primitives, algorithm, parameters);
         }
 
         if (permitted) {
             permitted = tlsDisabledAlgConstraints.permits(
-                                    primitives, algorithm, parameters);
+                    primitives, algorithm, parameters);
         }
 
         if (permitted && enabledX509DisabledAlgConstraints) {
             permitted = x509DisabledAlgConstraints.permits(
-                                    primitives, algorithm, parameters);
+                    primitives, algorithm, parameters);
         }
 
         return permitted;
@@ -252,27 +252,31 @@ public boolean permits(Set primitives,
 
         if (peerSpecifiedConstraints != null) {
             permitted = peerSpecifiedConstraints.permits(
-                                    primitives, algorithm, key, parameters);
+                    primitives, algorithm, key, parameters);
         }
 
         if (permitted && userSpecifiedConstraints != null) {
             permitted = userSpecifiedConstraints.permits(
-                                    primitives, algorithm, key, parameters);
+                    primitives, algorithm, key, parameters);
         }
 
         if (permitted) {
             permitted = tlsDisabledAlgConstraints.permits(
-                                    primitives, algorithm, key, parameters);
+                    primitives, algorithm, key, parameters);
         }
 
         if (permitted && enabledX509DisabledAlgConstraints) {
             permitted = x509DisabledAlgConstraints.permits(
-                                    primitives, algorithm, key, parameters);
+                    primitives, algorithm, key, parameters);
         }
 
         return permitted;
     }
 
+    // Checks if algorithm is disabled for the given TLS scopes.
+    boolean permits(String algorithm, Set scopes) {
+        return tlsDisabledAlgConstraints.permits(algorithm, scopes);
+    }
 
     private static class SupportedSignatureAlgorithmConstraints
                                     implements AlgorithmConstraints {
diff --git a/src/java.base/share/classes/sun/security/ssl/SSLLogger.java b/src/java.base/share/classes/sun/security/ssl/SSLLogger.java
index a97eb68dfa6ac..25b30af9631dc 100644
--- a/src/java.base/share/classes/sun/security/ssl/SSLLogger.java
+++ b/src/java.base/share/classes/sun/security/ssl/SSLLogger.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -146,8 +146,10 @@ private static boolean hasOption(String option) {
         if (property.contains("all")) {
             return true;
         } else {
-            int offset = property.indexOf("ssl");
-            if (offset != -1 && property.indexOf("sslctx", offset) != -1) {
+            // remove first occurrence of "sslctx" since
+            // it interferes with search for "ssl"
+            String modified = property.replaceFirst("sslctx", "");
+            if (modified.contains("ssl")) {
                 // don't enable data and plaintext options by default
                 if (!(option.equals("data")
                         || option.equals("packet")
@@ -208,6 +210,15 @@ static String toString(Object... params) {
         }
     }
 
+    // Logs a warning message and always returns false. This method
+    // can be used as an OR Predicate to add a log in a stream filter.
+    public static boolean logWarning(String option, String s) {
+        if (SSLLogger.isOn && SSLLogger.isOn(option)) {
+            SSLLogger.warning(s);
+        }
+        return false;
+    }
+
     private static class SSLConsoleLogger implements Logger {
         private final String loggerName;
         private final boolean useCompactFormat;
diff --git a/src/java.base/share/classes/sun/security/ssl/SSLScope.java b/src/java.base/share/classes/sun/security/ssl/SSLScope.java
new file mode 100644
index 0000000000000..d2f4dfc14a3a6
--- /dev/null
+++ b/src/java.base/share/classes/sun/security/ssl/SSLScope.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+/*
+ * Scopes defining different parts of TLS protocol.
+ */
+
+public enum SSLScope {
+    // Handshake signature scope as in signature_algorithms extension.
+    HANDSHAKE_SIGNATURE("HandshakeSignature"),
+
+    // Certificate signature scope as in signature_algorithms_cert extension.
+    CERTIFICATE_SIGNATURE("CertificateSignature");
+
+    private final String name;
+
+    SSLScope(String name) {
+        this.name = name;
+    }
+
+    // Note: the SSLScope name is case-insensitive.
+    public static SSLScope nameOf(String scopeName) {
+        for (SSLScope scope : SSLScope.values()) {
+            if (scope.name.equalsIgnoreCase(scopeName)) {
+                return scope;
+            }
+        }
+
+        return null;
+    }
+}
diff --git a/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java b/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java
index 74d1d3a038111..3ae35645ae47a 100644
--- a/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java
+++ b/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -30,6 +30,7 @@
 import java.math.BigInteger;
 import java.net.InetAddress;
 import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
 import java.security.Principal;
 import java.security.PrivateKey;
 import java.security.cert.X509Certificate;
@@ -193,10 +194,10 @@ final class SSLSessionImpl extends ExtendedSSLSession {
         this.sessionId = id;
         this.host = hc.conContext.transport.getPeerHost();
         this.port = hc.conContext.transport.getPeerPort();
-        this.localSupportedSignAlgs = hc.localSupportedSignAlgs == null ?
+        this.localSupportedSignAlgs = hc.localSupportedCertSignAlgs == null ?
                 Collections.emptySet() :
                 Collections.unmodifiableCollection(
-                        new ArrayList<>(hc.localSupportedSignAlgs));
+                        new ArrayList<>(hc.localSupportedCertSignAlgs));
         this.serverNameIndication = hc.negotiatedServerName;
         this.requestedServerNames = List.copyOf(hc.getRequestedServerNames());
         if (hc.sslConfig.isClientMode) {
@@ -309,108 +310,85 @@ final class SSLSessionImpl extends ExtendedSSLSession {
     SSLSessionImpl(HandshakeContext hc, ByteBuffer buf) throws IOException {
         boundValues = new ConcurrentHashMap<>();
         this.protocolVersion =
-                ProtocolVersion.valueOf(Short.toUnsignedInt(buf.getShort()));
+                ProtocolVersion.valueOf(Record.getInt16(buf));
 
         // The CH session id may reset this if it's provided
         this.sessionId = new SessionId(true,
                 hc.sslContext.getSecureRandom());
 
         this.cipherSuite =
-                CipherSuite.valueOf(Short.toUnsignedInt(buf.getShort()));
+                CipherSuite.valueOf(Record.getInt16(buf));
 
         // Local Supported signature algorithms
         ArrayList list = new ArrayList<>();
-        int i = Byte.toUnsignedInt(buf.get());
+        int i = Record.getInt8(buf);
         while (i-- > 0) {
             list.add(SignatureScheme.valueOf(
-                    Short.toUnsignedInt(buf.getShort())));
+                    Record.getInt16(buf)));
         }
         this.localSupportedSignAlgs = Collections.unmodifiableCollection(list);
 
         // Peer Supported signature algorithms
-        i = Byte.toUnsignedInt(buf.get());
+        i = Record.getInt8(buf);
         list.clear();
         while (i-- > 0) {
             list.add(SignatureScheme.valueOf(
-                    Short.toUnsignedInt(buf.getShort())));
+                    Record.getInt16(buf)));
         }
         this.peerSupportedSignAlgs = Collections.unmodifiableCollection(list);
 
         // PSK
-        byte[] b;
-        i = Short.toUnsignedInt(buf.getShort());
-        if (i > 0) {
-            b = new byte[i];
-            // Get algorithm string
-            buf.get(b, 0, i);
-            // Encoded length
-            i = Short.toUnsignedInt(buf.getShort());
-            // Encoded SecretKey
-            b = new byte[i];
-            buf.get(b);
+        byte[] b = Record.getBytes16(buf);
+        if (b.length > 0) {
+            b = Record.getBytes16(buf);
             this.preSharedKey = new SecretKeySpec(b, "TlsMasterSecret");
         } else {
             this.preSharedKey = null;
         }
 
         // PSK identity
-        i = buf.get();
-        if (i > 0) {
-            b = new byte[i];
-            buf.get(b);
+        b = Record.getBytes8(buf);
+        if (b.length > 0) {
             this.pskIdentity = b;
         } else {
             this.pskIdentity = null;
         }
 
         // Master secret length of secret key algorithm  (one byte)
-        i = buf.get();
-        if (i > 0) {
-            b = new byte[i];
-            // Get algorithm string
-            buf.get(b, 0, i);
-            // Encoded length
-            i = Short.toUnsignedInt(buf.getShort());
-            // Encoded SecretKey
-            b = new byte[i];
-            buf.get(b);
+        b = Record.getBytes8(buf);
+        if (b.length > 0) {
+            b = Record.getBytes16(buf);
             this.masterSecret = new SecretKeySpec(b, "TlsMasterSecret");
         } else {
             this.masterSecret = null;
         }
         // Use extended master secret
-        this.useExtendedMasterSecret = (buf.get() != 0);
+        this.useExtendedMasterSecret = (Record.getInt8(buf) != 0);
 
         // Identification Protocol
-        i = buf.get();
-        if (i == 0) {
+        b = Record.getBytes8(buf);
+        if (b.length == 0) {
             identificationProtocol = null;
         } else {
-            b = new byte[i];
-            buf.get(b);
             identificationProtocol = new String(b);
         }
 
         // SNI
-        i = buf.get();  // length
-        if (i == 0) {
+        b = Record.getBytes8(buf);
+        if (b.length == 0) {
             serverNameIndication = null;
         } else {
-            b = new byte[i];
-            buf.get(b, 0, b.length);
             serverNameIndication = new SNIHostName(b);
         }
 
         // List of SNIServerName
-        int len = Short.toUnsignedInt(buf.getShort());
+        int len = Record.getInt16(buf);
         if (len == 0) {
             this.requestedServerNames = Collections.emptyList();
         } else {
             requestedServerNames = new ArrayList<>();
             while (len > 0) {
-                int l = buf.get();
-                b = new byte[l];
-                buf.get(b, 0, l);
+                b = Record.getBytes8(buf);
                 requestedServerNames.add(new SNIHostName(new String(b)));
                 len--;
             }
@@ -425,31 +403,28 @@ final class SSLSessionImpl extends ExtendedSSLSession {
         // Get Buffer sizes
 
         // Status Response
-        len = Short.toUnsignedInt(buf.getShort());
+        len = Record.getInt16(buf);
         if (len == 0) {
             statusResponses = Collections.emptyList();
         } else {
             statusResponses = new ArrayList<>();
         }
         while (len-- > 0) {
-            b = new byte[Short.toUnsignedInt(buf.getShort())];
-            buf.get(b);
+            b = Record.getBytes16(buf);
             statusResponses.add(b);
         }
 
         // Get Peer host & port
-        i = Byte.toUnsignedInt(buf.get());
-        if (i == 0) {
+        b = Record.getBytes8(buf);
+        if (b.length == 0) {
             this.host = new String();
         } else {
-            b = new byte[i];
-            buf.get(b, 0, i);
             this.host = new String(b);
         }
-        this.port = Short.toUnsignedInt(buf.getShort());
+        this.port = Record.getInt16(buf);
 
         // Peer certs
-        i = buf.get();
+        i = Record.getInt8(buf);
         if (i == 0) {
             this.peerCerts = null;
         } else {
@@ -468,7 +443,7 @@ final class SSLSessionImpl extends ExtendedSSLSession {
         }
 
         // Get local certs of PSK
-        switch (buf.get()) {
+        switch (Record.getInt8(buf)) {
             case 0:
                 break;
             case 1:
@@ -490,19 +465,13 @@ final class SSLSessionImpl extends ExtendedSSLSession {
             case 2:
                 // pre-shared key
                 // Length of pre-shared key algorithm  (one byte)
-                i = buf.get();
-                b = new byte[i];
-                buf.get(b, 0 , i);
+                b = Record.getBytes8(buf);
                 String alg = new String(b);
-                // Get length of encoding
-                i = Short.toUnsignedInt(buf.getShort());
                 // Get encoding
-                b = new byte[i];
-                buf.get(b);
+                b = Record.getBytes16(buf);
                 this.preSharedKey = new SecretKeySpec(b, alg);
                 // Get identity len
-                this.pskIdentity = new byte[buf.get()];
-                buf.get(pskIdentity);
+                this.pskIdentity = Record.getBytes8(buf);
                 break;
             default:
                 throw new SSLException("Failed local certs of session.");
@@ -513,6 +482,7 @@ final class SSLSessionImpl extends ExtendedSSLSession {
         this.lastUsedTime = System.currentTimeMillis();
     }
 
+
     // Some situations we cannot provide a stateless ticket, but after it
     // has been negotiated
     boolean isStatelessable() {
diff --git a/src/java.base/share/classes/sun/security/ssl/ServerHello.java b/src/java.base/share/classes/sun/security/ssl/ServerHello.java
index 2905cbf619214..9c755807347dc 100644
--- a/src/java.base/share/classes/sun/security/ssl/ServerHello.java
+++ b/src/java.base/share/classes/sun/security/ssl/ServerHello.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -274,13 +274,6 @@ public byte[] produce(ConnectionContext context,
                         "Not resumption, and no new session is allowed");
                 }
 
-                if (shc.localSupportedSignAlgs == null) {
-                    shc.localSupportedSignAlgs =
-                        SignatureScheme.getSupportedAlgorithms(
-                                shc.sslConfig,
-                                shc.algorithmConstraints, shc.activeProtocols);
-                }
-
                 SSLSessionImpl session =
                         new SSLSessionImpl(shc, CipherSuite.C_NULL);
                 session.setMaximumPacketSize(shc.sslConfig.maximumPacketSize);
@@ -515,13 +508,6 @@ public byte[] produce(ConnectionContext context,
                         "Not resumption, and no new session is allowed");
                 }
 
-                if (shc.localSupportedSignAlgs == null) {
-                    shc.localSupportedSignAlgs =
-                        SignatureScheme.getSupportedAlgorithms(
-                                shc.sslConfig,
-                                shc.algorithmConstraints, shc.activeProtocols);
-                }
-
                 SSLSessionImpl session =
                         new SSLSessionImpl(shc, CipherSuite.C_NULL);
                 session.setMaximumPacketSize(shc.sslConfig.maximumPacketSize);
@@ -943,6 +929,10 @@ private void onHelloRetryRequest(ClientHandshakeContext chc,
                     "Negotiated protocol version: " + serverVersion.name);
             }
 
+            // Protocol version is negotiated, update locally supported
+            // signature schemes according to the protocol being used.
+            SignatureScheme.updateHandshakeLocalSupportedAlgs(chc);
+
             // TLS 1.3 key share extension may have produced client
             // possessions for TLS 1.3 key exchanges.
             //
@@ -994,6 +984,10 @@ private void onServerHello(ClientHandshakeContext chc,
                     "Negotiated protocol version: " + serverVersion.name);
             }
 
+            // Protocol version is negotiated, update locally supported
+            // signature schemes according to the protocol being used.
+            SignatureScheme.updateHandshakeLocalSupportedAlgs(chc);
+
             if (serverHello.serverRandom.isVersionDowngrade(chc)) {
                 throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
                     "A potential protocol version downgrade attack");
diff --git a/src/java.base/share/classes/sun/security/ssl/SessionTicketExtension.java b/src/java.base/share/classes/sun/security/ssl/SessionTicketExtension.java
index 1132ec120da50..18885ffa1491f 100644
--- a/src/java.base/share/classes/sun/security/ssl/SessionTicketExtension.java
+++ b/src/java.base/share/classes/sun/security/ssl/SessionTicketExtension.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -355,12 +355,7 @@ public byte[] produce(ConnectionContext context,
                 return new byte[0];
             }
 
-            if (chc.localSupportedSignAlgs == null) {
-                chc.localSupportedSignAlgs =
-                        SignatureScheme.getSupportedAlgorithms(
-                                chc.sslConfig,
-                                chc.algorithmConstraints, chc.activeProtocols);
-            }
+            SignatureScheme.updateHandshakeLocalSupportedAlgs(chc);
 
             return chc.resumingSession.getPskIdentity();
         }
diff --git a/src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java b/src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java
index ecadab7011cdf..d69fad3a16eca 100644
--- a/src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java
+++ b/src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,12 +25,16 @@
 
 package sun.security.ssl;
 
+import static sun.security.ssl.SignatureScheme.CERTIFICATE_SCOPE;
+import static sun.security.ssl.SignatureScheme.HANDSHAKE_SCOPE;
+
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.text.MessageFormat;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
+import javax.net.ssl.SSLException;
 import javax.net.ssl.SSLProtocolException;
 import sun.security.ssl.SSLExtension.ExtensionConsumer;
 import sun.security.ssl.SSLExtension.SSLExtensionSpec;
@@ -185,12 +189,7 @@ public byte[] produce(ConnectionContext context,
             }
 
             // Produce the extension.
-            if (chc.localSupportedSignAlgs == null) {
-                chc.localSupportedSignAlgs =
-                    SignatureScheme.getSupportedAlgorithms(
-                            chc.sslConfig,
-                            chc.algorithmConstraints, chc.activeProtocols);
-            }
+            SignatureScheme.updateHandshakeLocalSupportedAlgs(chc);
 
             int vectorLen = SignatureScheme.sizeInRecord() *
                     chc.localSupportedSignAlgs.size();
@@ -273,28 +272,8 @@ public void consume(ConnectionContext context,
                 return;
             }
 
-            // update the context
-            List sss =
-                    SignatureScheme.getSupportedAlgorithms(
-                            shc.sslConfig,
-                            shc.algorithmConstraints, shc.negotiatedProtocol,
-                            spec.signatureSchemes);
-            if (sss == null || sss.isEmpty()) {
-                throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
-                        "No supported signature algorithm");
-            }
-            shc.peerRequestedSignatureSchemes = sss;
-
-            // If no "signature_algorithms_cert" extension is present, then
-            // the "signature_algorithms" extension also applies to
-            // signatures appearing in certificates.
-            SignatureSchemesSpec certSpec =
-                    (SignatureSchemesSpec)shc.handshakeExtensions.get(
-                            SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT);
-            if (certSpec == null) {
-                shc.peerRequestedCertSignSchemes = sss;
-                shc.handshakeSession.setPeerSupportedSignatureAlgorithms(sss);
-            }
+            updateHandshakeContext(shc, spec.signatureSchemes,
+                    SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT);
 
             if (!shc.isResumption &&
                     shc.negotiatedProtocol.useTLS13PlusSpec()) {
@@ -412,17 +391,14 @@ public byte[] produce(ConnectionContext context,
             }
 
             // Produce the extension.
-            List sigAlgs =
-                    SignatureScheme.getSupportedAlgorithms(
-                            shc.sslConfig,
-                            shc.algorithmConstraints,
-                            List.of(shc.negotiatedProtocol));
-
-            int vectorLen = SignatureScheme.sizeInRecord() * sigAlgs.size();
+            // localSupportedSignAlgs has been already updated when we
+            // set the negotiated protocol.
+            int vectorLen = SignatureScheme.sizeInRecord()
+                    * shc.localSupportedSignAlgs.size();
             byte[] extData = new byte[vectorLen + 2];
             ByteBuffer m = ByteBuffer.wrap(extData);
             Record.putInt16(m, vectorLen);
-            for (SignatureScheme ss : sigAlgs) {
+            for (SignatureScheme ss : shc.localSupportedSignAlgs) {
                 Record.putInt16(m, ss.id);
             }
 
@@ -501,28 +477,8 @@ public void consume(ConnectionContext context,
                 return;
             }
 
-            // update the context
-            List sss =
-                    SignatureScheme.getSupportedAlgorithms(
-                            chc.sslConfig,
-                            chc.algorithmConstraints, chc.negotiatedProtocol,
-                            spec.signatureSchemes);
-            if (sss == null || sss.isEmpty()) {
-                throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
-                        "No supported signature algorithm");
-            }
-            chc.peerRequestedSignatureSchemes = sss;
-
-            // If no "signature_algorithms_cert" extension is present, then
-            // the "signature_algorithms" extension also applies to
-            // signatures appearing in certificates.
-            SignatureSchemesSpec certSpec =
-                    (SignatureSchemesSpec)chc.handshakeExtensions.get(
-                            SSLExtension.CR_SIGNATURE_ALGORITHMS_CERT);
-            if (certSpec == null) {
-                chc.peerRequestedCertSignSchemes = sss;
-                chc.handshakeSession.setPeerSupportedSignatureAlgorithms(sss);
-            }
+            updateHandshakeContext(chc, spec.signatureSchemes,
+                    SSLExtension.CR_SIGNATURE_ALGORITHMS_CERT);
         }
     }
 
@@ -545,4 +501,49 @@ public void absent(ConnectionContext context,
                     "received CertificateRequest handshake message");
         }
     }
+
+    // Updates given HandshakeContext with peer signature schemes.
+    private static void updateHandshakeContext(HandshakeContext hc,
+            int[] signatureSchemes, SSLExtension signatureAlgorithmsCertExt)
+            throws SSLException {
+        List handshakeSS =
+                SignatureScheme.getSupportedAlgorithms(
+                        hc.sslConfig,
+                        hc.algorithmConstraints,
+                        hc.negotiatedProtocol,
+                        signatureSchemes,
+                        HANDSHAKE_SCOPE);
+
+        if (handshakeSS.isEmpty()) {
+            throw hc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                    "No supported signature algorithm");
+        }
+
+        hc.peerRequestedSignatureSchemes = handshakeSS;
+
+        // If no "signature_algorithms_cert" extension is present, then
+        // the "signature_algorithms" extension also applies to
+        // signatures appearing in certificates.
+        SignatureSchemesSpec certSpec =
+                (SignatureSchemesSpec) hc.handshakeExtensions.get(
+                        signatureAlgorithmsCertExt);
+
+        if (certSpec == null) {
+            List certSS =
+                    SignatureScheme.getSupportedAlgorithms(
+                            hc.sslConfig,
+                            hc.algorithmConstraints,
+                            hc.negotiatedProtocol,
+                            signatureSchemes,
+                            CERTIFICATE_SCOPE);
+
+            if (certSS.isEmpty()) {
+                throw hc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "No supported signature algorithm");
+            }
+
+            hc.peerRequestedCertSignSchemes = certSS;
+            hc.handshakeSession.setPeerSupportedSignatureAlgorithms(certSS);
+        }
+    }
 }
diff --git a/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java b/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java
index 706cb4371d3da..60cebb8e6289f 100644
--- a/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java
+++ b/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -35,7 +35,6 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.EnumSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -134,8 +133,9 @@ enum SignatureScheme {
                                     "DSA",
                                     ProtocolVersion.PROTOCOLS_TO_12),
     ECDSA_SHA1              (0x0203, "ecdsa_sha1", "SHA1withECDSA",
-                                    "EC",
-                                    ProtocolVersion.PROTOCOLS_TO_13),
+                                    "EC", null, null, -1,
+                                    ProtocolVersion.PROTOCOLS_TO_13,
+                                    ProtocolVersion.PROTOCOLS_TO_12),
     RSA_PKCS1_SHA1          (0x0201, "rsa_pkcs1_sha1", "SHA1withRSA",
                                     "RSA", null, null, 511,
                                     ProtocolVersion.PROTOCOLS_TO_13,
@@ -219,10 +219,17 @@ static enum SigAlgParamSpec {   // support RSASSA-PSS only now
         }
     }
 
-    // performance optimization
-    private static final Set SIGNATURE_PRIMITIVE_SET =
-        Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));
+    // Handshake signature scope.
+    static final Set HANDSHAKE_SCOPE =
+            Set.of(SSLScope.HANDSHAKE_SIGNATURE);
 
+    // Certificate signature scope.
+    static final Set CERTIFICATE_SCOPE =
+            Set.of(SSLScope.CERTIFICATE_SIGNATURE);
+
+    // Non-TLS specific SIGNATURE CryptoPrimitive.
+    private static final Set SIGNATURE_PRIMITIVE_SET =
+            Set.of(CryptoPrimitive.SIGNATURE);
 
     private SignatureScheme(int id, String name,
             String algorithm, String keyAlgorithm,
@@ -356,24 +363,56 @@ static int sizeInRecord() {
         return 2;
     }
 
-    private boolean isPermitted(AlgorithmConstraints constraints) {
-        return constraints.permits(SIGNATURE_PRIMITIVE_SET,
-                        this.name, null) &&
-               constraints.permits(SIGNATURE_PRIMITIVE_SET,
-                        this.keyAlgorithm, null) &&
-               constraints.permits(SIGNATURE_PRIMITIVE_SET,
-                        this.algorithm, (signAlgParams != null ?
-                                signAlgParams.parameters : null)) &&
-                        (namedGroup == null ||
-                            namedGroup.isPermitted(constraints));
+    private boolean isPermitted(
+            SSLAlgorithmConstraints constraints, Set scopes) {
+        return constraints.permits(this.name, scopes)
+                && constraints.permits(this.keyAlgorithm, scopes)
+                && constraints.permits(this.algorithm, scopes)
+                && constraints.permits(SIGNATURE_PRIMITIVE_SET, this.name, null)
+                && constraints.permits(SIGNATURE_PRIMITIVE_SET, this.keyAlgorithm, null)
+                && constraints.permits(SIGNATURE_PRIMITIVE_SET, this.algorithm,
+                (signAlgParams != null ? signAlgParams.parameters : null))
+                && (namedGroup == null || namedGroup.isPermitted(constraints));
+    }
+
+    // Helper method to update all locally supported signature schemes for
+    // a given HandshakeContext.
+    static void updateHandshakeLocalSupportedAlgs(HandshakeContext hc) {
+        // To improve performance we only update when necessary.
+        // No need to do anything if we already computed the local supported
+        // algorithms and either there is no negotiated protocol yet or the
+        // only active protocol ends up to be the negotiated protocol.
+        if (hc.localSupportedSignAlgs != null
+                && hc.localSupportedCertSignAlgs != null
+                && (hc.negotiatedProtocol == null
+                || hc.activeProtocols.size() == 1)) {
+            return;
+        }
+
+        List protocols = hc.negotiatedProtocol != null ?
+                List.of(hc.negotiatedProtocol) :
+                hc.activeProtocols;
+
+        hc.localSupportedSignAlgs = getSupportedAlgorithms(
+                hc.sslConfig,
+                hc.algorithmConstraints,
+                protocols,
+                HANDSHAKE_SCOPE);
+
+        hc.localSupportedCertSignAlgs = getSupportedAlgorithms(
+                hc.sslConfig,
+                hc.algorithmConstraints,
+                protocols,
+                CERTIFICATE_SCOPE);
     }
 
     // Get local supported algorithm collection complying to algorithm
-    // constraints.
-    static List getSupportedAlgorithms(
+    // constraints and SSL scopes.
+    private static List getSupportedAlgorithms(
             SSLConfiguration config,
-            AlgorithmConstraints constraints,
-            List activeProtocols) {
+            SSLAlgorithmConstraints constraints,
+            List activeProtocols,
+            Set scopes) {
         List supported = new LinkedList<>();
 
         // If config.signatureSchemes is non-empty then it means that
@@ -398,14 +437,14 @@ static List getSupportedAlgorithms(
 
             boolean isMatch = false;
             for (ProtocolVersion pv : activeProtocols) {
-                if (ss.supportedProtocols.contains(pv)) {
+                if (ss.isSupportedProtocol(pv, scopes)) {
                     isMatch = true;
                     break;
                 }
             }
 
             if (isMatch) {
-                if (ss.isPermitted(constraints)) {
+                if (ss.isPermitted(constraints, scopes)) {
                     supported.add(ss);
                 } else if (SSLLogger.isOn &&
                         SSLLogger.isOn("ssl,handshake,verbose")) {
@@ -424,8 +463,10 @@ static List getSupportedAlgorithms(
 
     static List getSupportedAlgorithms(
             SSLConfiguration config,
-            AlgorithmConstraints constraints,
-            ProtocolVersion protocolVersion, int[] algorithmIds) {
+            SSLAlgorithmConstraints constraints,
+            ProtocolVersion protocolVersion,
+            int[] algorithmIds,
+            Set scopes) {
         List supported = new LinkedList<>();
         for (int ssid : algorithmIds) {
             SignatureScheme ss = SignatureScheme.valueOf(ssid);
@@ -435,11 +476,9 @@ static List getSupportedAlgorithms(
                             "Unsupported signature scheme: " +
                             SignatureScheme.nameOf(ssid));
                 }
-            } else if (ss.isAvailable &&
-                    ss.supportedProtocols.contains(protocolVersion) &&
-                    (config.signatureSchemes.isEmpty() ||
-                        config.signatureSchemes.contains(ss)) &&
-                    ss.isPermitted(constraints)) {
+            } else if ((config.signatureSchemes.isEmpty()
+                        || config.signatureSchemes.contains(ss))
+                    && ss.isAllowed(constraints, protocolVersion, scopes)) {
                 supported.add(ss);
             } else {
                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
@@ -453,16 +492,14 @@ static List getSupportedAlgorithms(
     }
 
     static SignatureScheme getPreferableAlgorithm(
-            AlgorithmConstraints constraints,
+            SSLAlgorithmConstraints constraints,
             List schemes,
-            SignatureScheme certScheme,
+            String keyAlgorithm,
             ProtocolVersion version) {
 
         for (SignatureScheme ss : schemes) {
-            if (ss.isAvailable &&
-                    ss.handshakeSupportedProtocols.contains(version) &&
-                    certScheme.keyAlgorithm.equalsIgnoreCase(ss.keyAlgorithm) &&
-                    ss.isPermitted(constraints)) {
+            if (keyAlgorithm.equalsIgnoreCase(ss.keyAlgorithm)
+                    && ss.isAllowed(constraints, version, HANDSHAKE_SCOPE)) {
                 return ss;
             }
         }
@@ -471,7 +508,7 @@ static SignatureScheme getPreferableAlgorithm(
     }
 
     static Map.Entry getSignerOfPreferableAlgorithm(
-            AlgorithmConstraints constraints,
+            SSLAlgorithmConstraints constraints,
             List schemes,
             X509Possession x509Possession,
             ProtocolVersion version) {
@@ -487,10 +524,9 @@ static Map.Entry getSignerOfPreferableAlgorithm(
             keySize = Integer.MAX_VALUE;
         }
         for (SignatureScheme ss : schemes) {
-            if (ss.isAvailable && (keySize >= ss.minimalKeySize) &&
-                    ss.handshakeSupportedProtocols.contains(version) &&
+            if (keySize >= ss.minimalKeySize &&
                     keyAlgorithm.equalsIgnoreCase(ss.keyAlgorithm) &&
-                    ss.isPermitted(constraints)) {
+                    ss.isAllowed(constraints, version, HANDSHAKE_SCOPE)) {
                 if ((ss.namedGroup != null) && (ss.namedGroup.spec ==
                         NamedGroupSpec.NAMED_GROUP_ECDHE)) {
                     ECParameterSpec params =
@@ -550,6 +586,26 @@ static Map.Entry getSignerOfPreferableAlgorithm(
         return null;
     }
 
+    // Returns true if this signature scheme is supported for the given
+    // protocol version and SSL scopes.
+    private boolean isSupportedProtocol(
+            ProtocolVersion version, Set scopes) {
+        if (scopes != null && scopes.equals(HANDSHAKE_SCOPE)) {
+            return this.handshakeSupportedProtocols.contains(version);
+        } else {
+            return this.supportedProtocols.contains(version);
+        }
+    }
+
+    // Returns true if this signature scheme is available, supported and
+    // permitted for the given constraints, protocol version and SSL scopes.
+    private boolean isAllowed(SSLAlgorithmConstraints constraints,
+            ProtocolVersion version, Set scopes) {
+        return isAvailable
+                && isSupportedProtocol(version, scopes)
+                && isPermitted(constraints, scopes);
+    }
+
     static String[] getAlgorithmNames(Collection schemes) {
         if (schemes != null) {
             ArrayList names = new ArrayList<>(schemes.size());
diff --git a/src/java.base/share/classes/sun/security/ssl/X509Authentication.java b/src/java.base/share/classes/sun/security/ssl/X509Authentication.java
index 366b27b4032f3..c35310bef5a1c 100644
--- a/src/java.base/share/classes/sun/security/ssl/X509Authentication.java
+++ b/src/java.base/share/classes/sun/security/ssl/X509Authentication.java
@@ -72,9 +72,9 @@ private X509Authentication(String keyAlgorithm,
         this.keyTypes = keyTypes;
     }
 
-    static X509Authentication valueOf(SignatureScheme signatureScheme) {
+    static X509Authentication valueOfKeyAlgorithm(String keyAlgorithm) {
         for (X509Authentication au : X509Authentication.values()) {
-            if (au.keyAlgorithm.equals(signatureScheme.keyAlgorithm)) {
+            if (au.keyAlgorithm.equals(keyAlgorithm)) {
                 return au;
             }
         }
diff --git a/src/java.base/share/classes/sun/security/util/Cache.java b/src/java.base/share/classes/sun/security/util/Cache.java
index 65bf6cc758112..ca8f731a6b640 100644
--- a/src/java.base/share/classes/sun/security/util/Cache.java
+++ b/src/java.base/share/classes/sun/security/util/Cache.java
@@ -55,7 +55,7 @@
  * However, note that because of the way SoftReferences are implemented in
  * HotSpot at the moment, this may not work perfectly as it clears them fairly
  * eagerly. Performance may be improved if the Java heap size is set to larger
- * value using e.g. java -ms64M -mx128M foo.Test
+ * value using e.g. java -Xms64M -Xmx128M foo.Test
  *
  * Cache sizing: the memory cache is implemented on top of a LinkedHashMap.
  * In its current implementation, the number of buckets (NOT entries) in
diff --git a/src/java.base/share/classes/sun/security/util/DerEncoder.java b/src/java.base/share/classes/sun/security/util/DerEncoder.java
index fadad5fff1998..be418c1d1a715 100644
--- a/src/java.base/share/classes/sun/security/util/DerEncoder.java
+++ b/src/java.base/share/classes/sun/security/util/DerEncoder.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 1999, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,6 @@
 package sun.security.util;
 
 import java.io.IOException;
-import java.io.OutputStream;
 
 /**
  * Interface to an object that knows how to write its own DER
@@ -41,7 +40,7 @@ public interface DerEncoder {
      *
      * @param out  the stream on which the DER encoding is written.
      */
-    public void derEncode(OutputStream out)
+    public void derEncode(DerOutputStream out)
         throws IOException;
 
 }
diff --git a/src/java.base/share/classes/sun/security/util/DerOutputStream.java b/src/java.base/share/classes/sun/security/util/DerOutputStream.java
index d7e54a8c5f236..d77e00fbadbe1 100644
--- a/src/java.base/share/classes/sun/security/util/DerOutputStream.java
+++ b/src/java.base/share/classes/sun/security/util/DerOutputStream.java
@@ -26,7 +26,6 @@
 package sun.security.util;
 
 import java.io.ByteArrayOutputStream;
-import java.io.OutputStream;
 import java.io.IOException;
 import java.math.BigInteger;
 import java.nio.charset.Charset;
@@ -583,7 +582,7 @@ public void putTag(byte tagClass, boolean form, byte val) {
      *
      *  @exception IOException on output error.
      */
-    public void derEncode(OutputStream out) throws IOException {
+    public void derEncode(DerOutputStream out) throws IOException {
         out.write(toByteArray());
     }
 
diff --git a/src/java.base/share/classes/sun/security/util/DerValue.java b/src/java.base/share/classes/sun/security/util/DerValue.java
index 14be5ad44cd58..8affd5e113e1f 100644
--- a/src/java.base/share/classes/sun/security/util/DerValue.java
+++ b/src/java.base/share/classes/sun/security/util/DerValue.java
@@ -852,6 +852,22 @@ public String getUniversalString() throws IOException {
         return readStringInternal(tag_UniversalString, new UTF_32BE());
     }
 
+    /**
+     * Checks that the BMPString does not contain any surrogate characters,
+     * which are outside the Basic Multilingual Plane.
+     *
+     * @throws IOException if illegal characters are detected
+     */
+    public void validateBMPString() throws IOException {
+        String bmpString = getBMPString();
+        for (int i = 0; i < bmpString.length(); i++) {
+            if (Character.isSurrogate(bmpString.charAt(i))) {
+                throw new IOException(
+                    "Illegal character in BMPString, index: " + i);
+            }
+         }
+     }
+
     /**
      * Reads the ASN.1 NULL value
      */
diff --git a/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java b/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java
index 41ea669613d0d..82900620c9e2a 100644
--- a/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java
+++ b/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,7 @@
 
 package sun.security.util;
 
+import sun.security.ssl.SSLScope;
 import sun.security.validator.Validator;
 
 import java.lang.ref.SoftReference;
@@ -42,10 +43,12 @@
 import java.security.spec.PSSParameterSpec;
 import java.time.DateTimeException;
 import java.time.Instant;
-import java.time.ZonedDateTime;
 import java.time.ZoneId;
+import java.time.ZonedDateTime;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -53,11 +56,10 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
-import java.util.Collection;
 import java.util.StringTokenizer;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.regex.Pattern;
 import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * Algorithm constraints for disabled algorithms property
@@ -102,6 +104,7 @@ private static class JarHolder {
     }
 
     private final Set disabledAlgorithms;
+    private final List disabledPatterns;
     private final Constraints algorithmConstraints;
     private volatile SoftReference> cacheRef =
             new SoftReference<>(null);
@@ -137,6 +140,13 @@ public DisabledAlgorithmConstraints(String propertyName,
         super(decomposer);
         disabledAlgorithms = getAlgorithms(propertyName);
 
+        // Support patterns only for jdk.tls.disabledAlgorithms
+        if (PROPERTY_TLS_DISABLED_ALGS.equals(propertyName)) {
+            disabledPatterns = getDisabledPatterns();
+        } else {
+            disabledPatterns = null;
+        }
+
         // Check for alias
         for (String s : disabledAlgorithms) {
             Matcher matcher = INCLUDE_PATTERN.matcher(s);
@@ -176,6 +186,12 @@ public final boolean permits(Set primitives,
         return true;
     }
 
+    // Checks if algorithm is disabled for the given TLS scopes.
+    public boolean permits(String algorithm, Set scopes) {
+        List list = algorithmConstraints.getConstraints(algorithm);
+        return list == null || list.stream().allMatch(c -> c.permits(scopes));
+    }
+
     /*
      * Checks if the key algorithm has been disabled or constraints have been
      * placed on the key.
@@ -432,7 +448,7 @@ public Constraints(String propertyName, Set constraintSet) {
                         denyAfterLimit = true;
                     } else if (entry.startsWith("usage")) {
                         String s[] = (entry.substring(5)).trim().split(" ");
-                        c = new UsageConstraint(algorithm, s);
+                        c = new UsageConstraint(algorithm, s, propertyName);
                         if (debug != null) {
                             debug.println("Constraints usage length is " + s.length);
                         }
@@ -603,6 +619,17 @@ public boolean permits(AlgorithmParameters parameters) {
             return true;
         }
 
+        /**
+         * Check if the algorithm constraint permits the given TLS scopes.
+         *
+         * @param scopes TLS scopes
+         * @return 'true' if TLS scopes are allowed,
+         *         'false' otherwise.
+         */
+        public boolean permits(Set scopes) {
+            return true;
+        }
+
         /**
          * Check if an algorithm constraint is permitted with a given
          * ConstraintsParameters.
@@ -780,14 +807,49 @@ public boolean permits(Key key) {
 
     /*
      * The usage constraint is for the "usage" keyword.  It checks against the
-     * variant value in ConstraintsParameters.
+     * variant value in ConstraintsParameters and against TLS scopes.
      */
     private static class UsageConstraint extends Constraint {
         String[] usages;
+        Set scopes;
 
-        UsageConstraint(String algorithm, String[] usages) {
+        UsageConstraint(
+                String algorithm, String[] usages, String propertyName) {
             this.algorithm = algorithm;
-            this.usages = usages;
+
+            // Support TLS scopes only for jdk.tls.disabledAlgorithms property.
+            if (PROPERTY_TLS_DISABLED_ALGS.equals(propertyName)) {
+                for (String usage : usages) {
+                    SSLScope scope = SSLScope.nameOf(usage);
+
+                    if (scope != null) {
+                        if (this.scopes == null) {
+                            this.scopes = new HashSet<>(usages.length);
+                        }
+                        this.scopes.add(scope);
+                    } else {
+                        this.usages = usages;
+                    }
+                }
+
+                if (this.scopes != null && this.usages != null) {
+                    throw new IllegalArgumentException(
+                            "Can't mix TLS protocol specific constraints"
+                            + " with other usage constraints");
+                }
+
+            } else {
+                this.usages = usages;
+            }
+        }
+
+        @Override
+        public boolean permits(Set scopes) {
+            if (this.scopes == null || scopes == null) {
+                return true;
+            }
+
+            return Collections.disjoint(this.scopes, scopes);
         }
 
         @Override
@@ -976,11 +1038,48 @@ private boolean cachedCheckAlgorithm(String algorithm) {
         if (result != null) {
             return result;
         }
-        result = checkAlgorithm(disabledAlgorithms, algorithm, decomposer);
+        // We won't check patterns if algorithm check fails.
+        result = checkAlgorithm(disabledAlgorithms, algorithm, decomposer)
+                && checkDisabledPatterns(algorithm);
         cache.put(algorithm, result);
         return result;
     }
 
+    private boolean checkDisabledPatterns(final String algorithm) {
+        return disabledPatterns == null || disabledPatterns.stream().noneMatch(
+                p -> p.matcher(algorithm).matches());
+    }
+
+    private List getDisabledPatterns() {
+        List ret = null;
+        List patternStrings = new ArrayList<>(4);
+
+        for (String p : disabledAlgorithms) {
+            if (p.contains("*")) {
+                if (!p.startsWith("TLS_")) {
+                    throw new IllegalArgumentException(
+                            "Wildcard pattern must start with \"TLS_\"");
+                }
+                patternStrings.add(p);
+            }
+        }
+
+        if (!patternStrings.isEmpty()) {
+            ret = new ArrayList<>(patternStrings.size());
+
+            for (String p : patternStrings) {
+                // Exclude patterns from algorithm code flow.
+                disabledAlgorithms.remove(p);
+
+                // Ignore all regex characters but asterisk.
+                ret.add(Pattern.compile(
+                        "^\\Q" + p.replace("*", "\\E.*\\Q") + "\\E$"));
+            }
+        }
+
+        return ret;
+    }
+
     /*
      * This constraint is used for the complete disabling of the algorithm.
      */
diff --git a/src/java.base/share/classes/sun/security/util/KeyUtil.java b/src/java.base/share/classes/sun/security/util/KeyUtil.java
index d5ab03e62f42f..a84aa1529a6b0 100644
--- a/src/java.base/share/classes/sun/security/util/KeyUtil.java
+++ b/src/java.base/share/classes/sun/security/util/KeyUtil.java
@@ -322,19 +322,31 @@ public static byte[] checkTlsPreMasterSecretKey(
             tmp = encoded;
         }
 
+        // At this point tmp.length is 48
         int encodedVersion =
                 ((tmp[0] & 0xFF) << 8) | (tmp[1] & 0xFF);
-        int check1 = 0;
-        int check2 = 0;
-        int check3 = 0;
-        if (clientVersion != encodedVersion) check1 = 1;
-        if (clientVersion > 0x0301) check2 = 1;
-        if (serverVersion != encodedVersion) check3 = 1;
-        if ((check1 & (check2 | check3)) == 1) {
-            return replacer;
-        } else {
-            return tmp;
+
+        // The following code is a time-constant version of
+        // if ((clientVersion != encodedVersion) ||
+        //    ((clientVersion > 0x301) && (serverVersion != encodedVersion))) {
+        //        return replacer;
+        // } else { return tmp; }
+        int check1 = (clientVersion - encodedVersion) |
+                (encodedVersion - clientVersion);
+        int check2 = 0x0301 - clientVersion;
+        int check3 = (serverVersion - encodedVersion) |
+                (encodedVersion - serverVersion);
+
+        check1 = (check1 & (check2 | check3)) >> 24;
+
+        // Now check1 is either 0 or -1
+        check2 = ~check1;
+
+        for (int i = 0; i < 48; i++) {
+            tmp[i] = (byte) ((tmp[i] & check2) | (replacer[i] & check1));
         }
+
+        return tmp;
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java b/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java
index 05acdcb94748b..05e992f2be94e 100644
--- a/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java
+++ b/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -525,6 +525,8 @@ private boolean verifyManifestMainAttrs(Manifest sf, ManifestDigester md)
         boolean attrsVerified = true;
         // If only weak algorithms are used.
         boolean weakAlgs = true;
+        // If only unsupported algorithms are used.
+        boolean unsupportedAlgs = true;
         // If a ATTR_DIGEST entry is found.
         boolean validEntry = false;
 
@@ -549,6 +551,7 @@ private boolean verifyManifestMainAttrs(Manifest sf, ManifestDigester md)
 
                 MessageDigest digest = getDigest(algorithm);
                 if (digest != null) {
+                    unsupportedAlgs = false;
                     ManifestDigester.Entry mde = md.getMainAttsEntry(false);
                     if (mde == null) {
                         throw new SignatureException("Manifest Main Attribute check " +
@@ -591,12 +594,22 @@ private boolean verifyManifestMainAttrs(Manifest sf, ManifestDigester md)
             }
         }
 
-        // If there were only weak algorithms entries used, throw an exception.
-        if (validEntry && weakAlgs) {
-            throw new SignatureException("Manifest Main Attribute check " +
-                    "failed (" + ATTR_DIGEST + ").  " +
-                    "Disabled algorithm(s) used: " +
-                    getWeakAlgorithms(ATTR_DIGEST));
+        if (validEntry) {
+            // If there were only weak algorithms entries used, throw an exception.
+            if (weakAlgs) {
+                throw new SignatureException(
+                        "Manifest Main Attribute check "
+                        + "failed (" + ATTR_DIGEST + ").  "
+                        + "Disabled algorithm(s) used: "
+                        + getWeakAlgorithms(ATTR_DIGEST));
+            }
+
+            // If there were only unsupported algorithms entries used, throw an exception.
+            if (unsupportedAlgs) {
+                throw new SignatureException(
+                        "Manifest Main Attribute check failed ("
+                        + ATTR_DIGEST + "). Unsupported algorithm(s) used");
+            }
         }
 
         // this method returns 'true' if either:
diff --git a/src/java.base/share/classes/sun/security/validator/CamerfirmaTLSPolicy.java b/src/java.base/share/classes/sun/security/validator/CamerfirmaTLSPolicy.java
index 591a7a26c6893..984f5a0ab47f9 100644
--- a/src/java.base/share/classes/sun/security/validator/CamerfirmaTLSPolicy.java
+++ b/src/java.base/share/classes/sun/security/validator/CamerfirmaTLSPolicy.java
@@ -43,26 +43,14 @@ final class CamerfirmaTLSPolicy {
 
     private static final Debug debug = Debug.getInstance("certpath");
 
-    // SHA-256 certificate fingerprints of distrusted roots
-    private static final Set FINGERPRINTS = Set.of(
-        // cacerts alias: camerfirmachamberscommerceca
-        // DN: CN=Chambers of Commerce Root,
-        //     OU=http://www.chambersign.org,
-        //     O=AC Camerfirma SA CIF A82743287, C=EU
-        "0C258A12A5674AEF25F28BA7DCFAECEEA348E541E6F5CC4EE63B71B361606AC3",
-        // cacerts alias: camerfirmachambersca
-        // DN: CN=Chambers of Commerce Root - 2008,
-        //     O=AC Camerfirma S.A., SERIALNUMBER=A82743287,
-        //     L=Madrid (see current address at www.camerfirma.com/address),
-        //     C=EU
-        "063E4AFAC491DFD332F3089B8542E94617D893D7FE944E10A7937EE29D9693C0",
-        // cacerts alias: camerfirmachambersignca
-        // DN: CN=Global Chambersign Root - 2008,
-        //     O=AC Camerfirma S.A., SERIALNUMBER=A82743287,
-        //     L=Madrid (see current address at www.camerfirma.com/address),
-        //     C=EU
-        "136335439334A7698016A0D324DE72284E079D7B5220BB8FBD747816EEBEBACA"
-    );
+    // SHA-256 certificate fingerprint of distrusted root for TLS
+    // cacerts alias: camerfirmachambersca
+    // DN: CN=Chambers of Commerce Root - 2008,
+    //     O=AC Camerfirma S.A., SERIALNUMBER=A82743287,
+    //     L=Madrid (see current address at www.camerfirma.com/address),
+    //     C=EU
+    private static final String FINGERPRINT =
+            "063E4AFAC491DFD332F3089B8542E94617D893D7FE944E10A7937EE29D9693C0";
 
     // Any TLS Server certificate that is anchored by one of the Camerfirma
     // roots above and is issued after this date will be distrusted.
@@ -85,7 +73,7 @@ static void checkDistrust(X509Certificate[] chain)
             throw new ValidatorException("Cannot generate fingerprint for "
                 + "trust anchor of TLS server certificate");
         }
-        if (FINGERPRINTS.contains(fp)) {
+        if (FINGERPRINT.equalsIgnoreCase(fp)) {
             Date notBefore = chain[0].getNotBefore();
             LocalDate ldNotBefore = LocalDate.ofInstant(notBefore.toInstant(),
                                                         ZoneOffset.UTC);
diff --git a/src/java.base/share/classes/sun/security/validator/EntrustTLSPolicy.java b/src/java.base/share/classes/sun/security/validator/EntrustTLSPolicy.java
index 4c4906d8eb398..f93e534f46ab4 100644
--- a/src/java.base/share/classes/sun/security/validator/EntrustTLSPolicy.java
+++ b/src/java.base/share/classes/sun/security/validator/EntrustTLSPolicy.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -71,19 +71,7 @@ final class EntrustTLSPolicy {
         //     OU=(c) 1999 Entrust.net Limited,
         //     OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.),
         //     O=Entrust.net
-        "6DC47172E01CBCB0BF62580D895FE2B8AC9AD4F873801E0C10B9C837D21EB177",
-        // cacerts alias: affirmtrustcommercialca
-        // DN: CN=AffirmTrust Commercial, O=AffirmTrust, C=US
-        "0376AB1D54C5F9803CE4B2E201A0EE7EEF7B57B636E8A93C9B8D4860C96F5FA7",
-        // cacerts alias: affirmtrustnetworkingca
-        // DN: CN=AffirmTrust Networking, O=AffirmTrust, C=US
-        "0A81EC5A929777F145904AF38D5D509F66B5E2C58FCDB531058B0E17F3F0B41B",
-        // cacerts alias: affirmtrustpremiumca
-        // DN: CN=AffirmTrust Premium, O=AffirmTrust, C=US
-        "70A73F7F376B60074248904534B11482D5BF0E698ECC498DF52577EBF2E93B9A",
-        // cacerts alias: affirmtrustpremiumeccca
-        // DN: CN=AffirmTrust Premium ECC, O=AffirmTrust, C=US
-        "BD71FDF6DA97E4CF62D1647ADD2581B07D79ADF8397EB4ECBA9C5E8488821423"
+        "6DC47172E01CBCB0BF62580D895FE2B8AC9AD4F873801E0C10B9C837D21EB177"
     );
 
     // Any TLS Server certificate that is anchored by one of the Entrust
diff --git a/src/java.base/share/classes/sun/security/x509/AVA.java b/src/java.base/share/classes/sun/security/x509/AVA.java
index 39d7469a38549..2cb43112a1ff1 100644
--- a/src/java.base/share/classes/sun/security/x509/AVA.java
+++ b/src/java.base/share/classes/sun/security/x509/AVA.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -27,12 +27,14 @@
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.io.OutputStream;
 import java.io.Reader;
+import java.nio.charset.Charset;
 import java.text.Normalizer;
 import java.util.*;
 
+import static java.nio.charset.StandardCharsets.ISO_8859_1;
 import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.nio.charset.StandardCharsets.UTF_16BE;
 
 import sun.security.action.GetBooleanAction;
 import sun.security.util.*;
@@ -595,6 +597,10 @@ private static boolean trailingSpace(Reader in) throws IOException {
             throw new IOException("AVA, extra bytes = "
                 + derval.data.available());
         }
+
+        if (value.tag == DerValue.tag_BMPString) {
+            value.validateBMPString();
+        }
     }
 
     AVA(DerInputStream in) throws IOException {
@@ -638,14 +644,12 @@ public void encode(DerOutputStream out) throws IOException {
      *
      * @exception IOException on encoding error.
      */
-    public void derEncode(OutputStream out) throws IOException {
+    public void derEncode(DerOutputStream out) throws IOException {
         DerOutputStream         tmp = new DerOutputStream();
-        DerOutputStream         tmp2 = new DerOutputStream();
 
         tmp.putOID(oid);
         value.encode(tmp);
-        tmp2.write(DerValue.tag_Sequence, tmp);
-        out.write(tmp2.toByteArray());
+        out.write(DerValue.tag_Sequence, tmp);
     }
 
     private String toKeyword(int format, Map oidMap) {
@@ -738,7 +742,7 @@ public String toRFC2253String(Map oidMap) {
              */
             String valStr = null;
             try {
-                valStr = new String(value.getDataBytes(), UTF_8);
+                valStr = new String(value.getDataBytes(), getCharset(value, false));
             } catch (IOException ie) {
                 throw new IllegalArgumentException("DER Value conversion");
             }
@@ -873,7 +877,7 @@ public String toRFC2253CanonicalString() {
              */
             String valStr = null;
             try {
-                valStr = new String(value.getDataBytes(), UTF_8);
+                valStr = new String(value.getDataBytes(), getCharset(value, true));
             } catch (IOException ie) {
                 throw new IllegalArgumentException("DER Value conversion");
             }
@@ -980,6 +984,39 @@ private static boolean isDerString(DerValue value, boolean canonical) {
         }
     }
 
+    /*
+     * Returns the charset that should be used to decode each DN string type.
+     *
+     * This method ensures that multi-byte (UTF8String and BMPString) types
+     * are decoded using the correct charset and the String forms represent
+     * the correct characters. For 8-bit ASCII-based types (PrintableString
+     * and IA5String), we return ISO_8859_1 rather than ASCII, so that the
+     * complete range of characters can be represented, as many certificates
+     * do not comply with the Internationalized Domain Name ACE format.
+     *
+     * NOTE: this method only supports DirectoryStrings of the types returned
+     * by isDerString().
+     */
+    private static Charset getCharset(DerValue value, boolean canonical) {
+        if (canonical) {
+            return switch (value.tag) {
+                case DerValue.tag_PrintableString -> ISO_8859_1;
+                case DerValue.tag_UTF8String -> UTF_8;
+                default -> throw new Error("unexpected tag: " + value.tag);
+            };
+        }
+
+        return switch (value.tag) {
+            case DerValue.tag_PrintableString,
+                 DerValue.tag_T61String,
+                 DerValue.tag_IA5String,
+                 DerValue.tag_GeneralString -> ISO_8859_1;
+            case DerValue.tag_BMPString -> UTF_16BE;
+            case DerValue.tag_UTF8String -> UTF_8;
+            default -> throw new Error("unexpected tag: " + value.tag);
+        };
+    }
+
     boolean hasRFC2253Keyword() {
         return AVAKeyword.hasKeyword(oid, RFC2253);
     }
diff --git a/src/java.base/share/classes/sun/security/x509/AlgorithmId.java b/src/java.base/share/classes/sun/security/x509/AlgorithmId.java
index 627fb503ba9c4..db35da16f900f 100644
--- a/src/java.base/share/classes/sun/security/x509/AlgorithmId.java
+++ b/src/java.base/share/classes/sun/security/x509/AlgorithmId.java
@@ -164,9 +164,8 @@ public final void encode(DerOutputStream out) throws IOException {
      * @exception IOException on encoding error.
      */
     @Override
-    public void derEncode (OutputStream out) throws IOException {
+    public void derEncode (DerOutputStream out) throws IOException {
         DerOutputStream bytes = new DerOutputStream();
-        DerOutputStream tmp = new DerOutputStream();
 
         bytes.putOID(algid);
 
@@ -230,8 +229,7 @@ public void derEncode (OutputStream out) throws IOException {
         } else {
             bytes.write(encodedParams);
         }
-        tmp.write(DerValue.tag_Sequence, bytes);
-        out.write(tmp.toByteArray());
+        out.write(DerValue.tag_Sequence, bytes);
     }
 
 
diff --git a/src/java.base/share/classes/sun/security/x509/AuthorityInfoAccessExtension.java b/src/java.base/share/classes/sun/security/x509/AuthorityInfoAccessExtension.java
index 69459fb6ef04b..259d0e3758512 100644
--- a/src/java.base/share/classes/sun/security/x509/AuthorityInfoAccessExtension.java
+++ b/src/java.base/share/classes/sun/security/x509/AuthorityInfoAccessExtension.java
@@ -26,7 +26,6 @@
 package sun.security.x509;
 
 import java.io.IOException;
-import java.io.OutputStream;
 
 import java.util.*;
 
@@ -149,15 +148,14 @@ public String getName() {
      * @param out the DerOutputStream to write the extension to.
      * @exception IOException on encoding errors.
      */
-    public void encode(OutputStream out) throws IOException {
-        DerOutputStream tmp = new DerOutputStream();
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
         if (this.extensionValue == null) {
             this.extensionId = PKIXExtensions.AuthInfoAccess_Id;
             this.critical = false;
             encodeThis();
         }
-        super.encode(tmp);
-        out.write(tmp.toByteArray());
+        super.encode(out);
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/AuthorityKeyIdentifierExtension.java b/src/java.base/share/classes/sun/security/x509/AuthorityKeyIdentifierExtension.java
index d3fb3cd5d9f68..0779cc2ffe452 100644
--- a/src/java.base/share/classes/sun/security/x509/AuthorityKeyIdentifierExtension.java
+++ b/src/java.base/share/classes/sun/security/x509/AuthorityKeyIdentifierExtension.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,6 @@
 package sun.security.x509;
 
 import java.io.IOException;
-import java.io.OutputStream;
 import java.util.Enumeration;
 
 import sun.security.util.*;
@@ -215,18 +214,17 @@ public String toString() {
     /**
      * Write the extension to the OutputStream.
      *
-     * @param out the OutputStream to write the extension to.
+     * @param out the DerOutputStream to write the extension to.
      * @exception IOException on error.
      */
-    public void encode(OutputStream out) throws IOException {
-        DerOutputStream tmp = new DerOutputStream();
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
         if (this.extensionValue == null) {
             extensionId = PKIXExtensions.AuthorityKey_Id;
             critical = false;
             encodeThis();
         }
-        super.encode(tmp);
-        out.write(tmp.toByteArray());
+        super.encode(out);
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/BasicConstraintsExtension.java b/src/java.base/share/classes/sun/security/x509/BasicConstraintsExtension.java
index 38abaf7cb39be..5d75334f738b1 100644
--- a/src/java.base/share/classes/sun/security/x509/BasicConstraintsExtension.java
+++ b/src/java.base/share/classes/sun/security/x509/BasicConstraintsExtension.java
@@ -26,7 +26,6 @@
 package sun.security.x509;
 
 import java.io.IOException;
-import java.io.OutputStream;
 import java.util.Enumeration;
 
 import sun.security.util.*;
@@ -190,7 +189,8 @@ public String toString() {
       *
       * @param out the DerOutputStream to encode the extension to.
       */
-     public void encode(OutputStream out) throws IOException {
+     @Override
+     public void encode(DerOutputStream out) throws IOException {
          DerOutputStream tmp = new DerOutputStream();
          if (extensionValue == null) {
              this.extensionId = PKIXExtensions.BasicConstraints_Id;
@@ -201,9 +201,7 @@ public void encode(OutputStream out) throws IOException {
              }
              encodeThis();
          }
-         super.encode(tmp);
-
-         out.write(tmp.toByteArray());
+         super.encode(out);
      }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/CRLDistributionPointsExtension.java b/src/java.base/share/classes/sun/security/x509/CRLDistributionPointsExtension.java
index cb7f106e68592..294039e704413 100644
--- a/src/java.base/share/classes/sun/security/x509/CRLDistributionPointsExtension.java
+++ b/src/java.base/share/classes/sun/security/x509/CRLDistributionPointsExtension.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2022, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,6 @@
 package sun.security.x509;
 
 import java.io.IOException;
-import java.io.OutputStream;
 
 import java.util.*;
 import java.util.Collections;
@@ -199,7 +198,8 @@ public String getName() {
      * @param out the DerOutputStream to write the extension to.
      * @exception IOException on encoding errors.
      */
-    public void encode(OutputStream out) throws IOException {
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
         encode(out, PKIXExtensions.CRLDistributionPoints_Id, false);
     }
 
@@ -207,17 +207,15 @@ public void encode(OutputStream out) throws IOException {
      * Write the extension to the DerOutputStream.
      * (Also called by the subclass)
      */
-    protected void encode(OutputStream out, ObjectIdentifier extensionId,
-        boolean isCritical) throws IOException {
+    protected void encode(DerOutputStream out, ObjectIdentifier extensionId,
+            boolean isCritical) throws IOException {
 
-        DerOutputStream tmp = new DerOutputStream();
         if (this.extensionValue == null) {
             this.extensionId = extensionId;
             this.critical = isCritical;
             encodeThis();
         }
-        super.encode(tmp);
-        out.write(tmp.toByteArray());
+        super.encode(out);
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/CRLExtensions.java b/src/java.base/share/classes/sun/security/x509/CRLExtensions.java
index 1f9c03a9f740c..228dd674bc837 100644
--- a/src/java.base/share/classes/sun/security/x509/CRLExtensions.java
+++ b/src/java.base/share/classes/sun/security/x509/CRLExtensions.java
@@ -30,7 +30,6 @@
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.security.cert.CRLException;
-import java.security.cert.CertificateException;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Enumeration;
@@ -145,16 +144,8 @@ public void encode(OutputStream out, boolean isExplicit)
     throws CRLException {
         try {
             DerOutputStream extOut = new DerOutputStream();
-            Collection allExts = map.values();
-            Object[] objs = allExts.toArray();
-
-            for (int i = 0; i < objs.length; i++) {
-                if (objs[i] instanceof CertAttrSet)
-                    ((CertAttrSet)objs[i]).encode(extOut);
-                else if (objs[i] instanceof Extension)
-                    ((Extension)objs[i]).encode(extOut);
-                else
-                    throw new CRLException("Illegal extension object");
+            for (Extension ext : map.values()) {
+                ext.encode(extOut);
             }
 
             DerOutputStream seq = new DerOutputStream();
@@ -170,8 +161,6 @@ else if (objs[i] instanceof Extension)
             out.write(tmp.toByteArray());
         } catch (IOException e) {
             throw new CRLException("Encoding error: " + e.toString());
-        } catch (CertificateException e) {
-            throw new CRLException("Encoding error: " + e.toString());
         }
     }
 
diff --git a/src/java.base/share/classes/sun/security/x509/CRLNumberExtension.java b/src/java.base/share/classes/sun/security/x509/CRLNumberExtension.java
index 9434e613441ac..6b3f0eadf490f 100644
--- a/src/java.base/share/classes/sun/security/x509/CRLNumberExtension.java
+++ b/src/java.base/share/classes/sun/security/x509/CRLNumberExtension.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,6 @@
 package sun.security.x509;
 
 import java.io.IOException;
-import java.io.OutputStream;
 import java.math.BigInteger;
 import java.util.Enumeration;
 
@@ -198,7 +197,8 @@ public String toString() {
      * @param out the DerOutputStream to write the extension to.
      * @exception IOException on encoding errors.
      */
-    public void encode(OutputStream out) throws IOException {
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
         DerOutputStream tmp = new DerOutputStream();
         encode(out, PKIXExtensions.CRLNumber_Id, true);
     }
@@ -207,18 +207,15 @@ public void encode(OutputStream out) throws IOException {
      * Write the extension to the DerOutputStream.
      * (Also called by the subclass)
      */
-    protected void encode(OutputStream out, ObjectIdentifier extensionId,
-        boolean isCritical) throws IOException {
-
-       DerOutputStream  tmp = new DerOutputStream();
+    protected void encode(DerOutputStream out, ObjectIdentifier extensionId,
+            boolean isCritical) throws IOException {
 
        if (this.extensionValue == null) {
            this.extensionId = extensionId;
            this.critical = isCritical;
            encodeThis();
        }
-       super.encode(tmp);
-       out.write(tmp.toByteArray());
+       super.encode(out);
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/CRLReasonCodeExtension.java b/src/java.base/share/classes/sun/security/x509/CRLReasonCodeExtension.java
index d54c35b31beb6..f1d35959f74a4 100644
--- a/src/java.base/share/classes/sun/security/x509/CRLReasonCodeExtension.java
+++ b/src/java.base/share/classes/sun/security/x509/CRLReasonCodeExtension.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,6 @@
 package sun.security.x509;
 
 import java.io.IOException;
-import java.io.OutputStream;
 import java.security.cert.CRLReason;
 import java.util.Enumeration;
 
@@ -158,16 +157,14 @@ public String toString() {
      * @param out the DerOutputStream to write the extension to.
      * @exception IOException on encoding errors.
      */
-    public void encode(OutputStream out) throws IOException {
-        DerOutputStream  tmp = new DerOutputStream();
-
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
         if (this.extensionValue == null) {
             this.extensionId = PKIXExtensions.ReasonCode_Id;
             this.critical = false;
             encodeThis();
         }
-        super.encode(tmp);
-        out.write(tmp.toByteArray());
+        super.encode(out);
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/CertAttrSet.java b/src/java.base/share/classes/sun/security/x509/CertAttrSet.java
index 8b5f5e6b6c757..2ee443735954b 100644
--- a/src/java.base/share/classes/sun/security/x509/CertAttrSet.java
+++ b/src/java.base/share/classes/sun/security/x509/CertAttrSet.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,8 +25,9 @@
 
 package sun.security.x509;
 
+import sun.security.util.DerOutputStream;
+
 import java.io.IOException;
-import java.io.OutputStream;
 import java.security.cert.CertificateException;
 import java.util.Enumeration;
 
@@ -58,12 +59,12 @@ public interface CertAttrSet {
      * Encodes the attribute to the output stream in a format
      * that can be parsed by the decode method.
      *
-     * @param out the OutputStream to encode the attribute to.
+     * @param out the DerOutputStream to encode the attribute to.
      *
      * @exception CertificateException on encoding or validity errors.
      * @exception IOException on other errors.
      */
-    void encode(OutputStream out)
+    void encode(DerOutputStream out)
         throws CertificateException, IOException;
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/CertificateAlgorithmId.java b/src/java.base/share/classes/sun/security/x509/CertificateAlgorithmId.java
index a7d6e2553e8a0..8b864c3d1bb13 100644
--- a/src/java.base/share/classes/sun/security/x509/CertificateAlgorithmId.java
+++ b/src/java.base/share/classes/sun/security/x509/CertificateAlgorithmId.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -27,7 +27,6 @@
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.OutputStream;
 import java.util.Enumeration;
 
 import sun.security.util.*;
@@ -105,11 +104,9 @@ public String toString() {
      * @param out the DerOutputStream to marshal the contents to.
      * @exception IOException on errors.
      */
-    public void encode(OutputStream out) throws IOException {
-        DerOutputStream tmp = new DerOutputStream();
-        algId.encode(tmp);
-
-        out.write(tmp.toByteArray());
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
+        algId.encode(out);
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/CertificateExtensions.java b/src/java.base/share/classes/sun/security/x509/CertificateExtensions.java
index 4f8022b406710..55f8fdd7ac270 100644
--- a/src/java.base/share/classes/sun/security/x509/CertificateExtensions.java
+++ b/src/java.base/share/classes/sun/security/x509/CertificateExtensions.java
@@ -26,7 +26,6 @@
 package sun.security.x509;
 
 import java.io.IOException;
-import java.io.OutputStream;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
@@ -152,8 +151,9 @@ private void parseExtension(Extension ext) throws IOException {
      * @exception CertificateException on encoding errors.
      * @exception IOException on errors.
      */
-    public void encode(OutputStream out)
-    throws CertificateException, IOException {
+    @Override
+    public void encode(DerOutputStream out)
+            throws CertificateException, IOException {
         encode(out, false);
     }
 
@@ -165,33 +165,21 @@ public void encode(OutputStream out)
      * @exception CertificateException on encoding errors.
      * @exception IOException on errors.
      */
-    public void encode(OutputStream out, boolean isCertReq)
+    public void encode(DerOutputStream out, boolean isCertReq)
     throws CertificateException, IOException {
         DerOutputStream extOut = new DerOutputStream();
-        Collection allExts = map.values();
-        Object[] objs = allExts.toArray();
-
-        for (int i = 0; i < objs.length; i++) {
-            if (objs[i] instanceof CertAttrSet)
-                ((CertAttrSet)objs[i]).encode(extOut);
-            else if (objs[i] instanceof Extension)
-                ((Extension)objs[i]).encode(extOut);
-            else
-                throw new CertificateException("Illegal extension object");
+        for (Extension ext : map.values()) {
+            ext.encode(extOut);
         }
 
-        DerOutputStream seq = new DerOutputStream();
-        seq.write(DerValue.tag_Sequence, extOut);
-
-        DerOutputStream tmp;
         if (!isCertReq) { // certificate
-            tmp = new DerOutputStream();
-            tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)3),
+            DerOutputStream seq = new DerOutputStream();
+            seq.write(DerValue.tag_Sequence, extOut);
+            out.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)3),
                     seq);
-        } else
-            tmp = seq; // pkcs#10 certificateRequest
-
-        out.write(tmp.toByteArray());
+        } else {
+            out.write(DerValue.tag_Sequence, extOut);
+        }
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/CertificateIssuerExtension.java b/src/java.base/share/classes/sun/security/x509/CertificateIssuerExtension.java
index 0dd8f39642a3b..31bd4506e1714 100644
--- a/src/java.base/share/classes/sun/security/x509/CertificateIssuerExtension.java
+++ b/src/java.base/share/classes/sun/security/x509/CertificateIssuerExtension.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,7 +25,6 @@
 package sun.security.x509;
 
 import java.io.IOException;
-import java.io.OutputStream;
 import java.util.Enumeration;
 
 import sun.security.util.DerValue;
@@ -176,18 +175,17 @@ public String toString() {
     /**
      * Write the extension to the OutputStream.
      *
-     * @param out the OutputStream to write the extension to
+     * @param out the DerOutputStream to write the extension to
      * @exception IOException on encoding errors
      */
-    public void encode(OutputStream out) throws IOException {
-        DerOutputStream  tmp = new DerOutputStream();
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
         if (extensionValue == null) {
             extensionId = PKIXExtensions.CertificateIssuer_Id;
             critical = true;
             encodeThis();
         }
-        super.encode(tmp);
-        out.write(tmp.toByteArray());
+        super.encode(out);
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/CertificateIssuerName.java b/src/java.base/share/classes/sun/security/x509/CertificateIssuerName.java
index 9c82d215e6f62..8d3e9a2370593 100644
--- a/src/java.base/share/classes/sun/security/x509/CertificateIssuerName.java
+++ b/src/java.base/share/classes/sun/security/x509/CertificateIssuerName.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -27,7 +27,6 @@
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.OutputStream;
 import java.util.Enumeration;
 
 import javax.security.auth.x500.X500Principal;
@@ -107,11 +106,9 @@ public String toString() {
      * @param out the DerOutputStream to marshal the contents to.
      * @exception IOException on errors.
      */
-    public void encode(OutputStream out) throws IOException {
-        DerOutputStream tmp = new DerOutputStream();
-        dnName.encode(tmp);
-
-        out.write(tmp.toByteArray());
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
+        dnName.encode(out);
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/CertificatePoliciesExtension.java b/src/java.base/share/classes/sun/security/x509/CertificatePoliciesExtension.java
index c2f71b3abc820..2ae2f8b5dd89e 100644
--- a/src/java.base/share/classes/sun/security/x509/CertificatePoliciesExtension.java
+++ b/src/java.base/share/classes/sun/security/x509/CertificatePoliciesExtension.java
@@ -26,7 +26,6 @@
 package sun.security.x509;
 
 import java.io.IOException;
-import java.io.OutputStream;
 import java.util.*;
 
 import sun.security.util.DerValue;
@@ -177,15 +176,14 @@ public String toString() {
      * @param out the DerOutputStream to write the extension to.
      * @exception IOException on encoding errors.
      */
-    public void encode(OutputStream out) throws IOException {
-        DerOutputStream tmp = new DerOutputStream();
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
         if (extensionValue == null) {
           extensionId = PKIXExtensions.CertificatePolicies_Id;
           critical = false;
           encodeThis();
         }
-        super.encode(tmp);
-        out.write(tmp.toByteArray());
+        super.encode(out);
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/CertificateSerialNumber.java b/src/java.base/share/classes/sun/security/x509/CertificateSerialNumber.java
index 18bfb090960f7..38d2bf2541e28 100644
--- a/src/java.base/share/classes/sun/security/x509/CertificateSerialNumber.java
+++ b/src/java.base/share/classes/sun/security/x509/CertificateSerialNumber.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,6 @@
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.OutputStream;
 import java.math.BigInteger;
 import java.util.Enumeration;
 import java.util.Random;
@@ -117,11 +116,9 @@ public String toString() {
      * @param out the DerOutputStream to marshal the contents to.
      * @exception IOException on errors.
      */
-    public void encode(OutputStream out) throws IOException {
-        DerOutputStream tmp = new DerOutputStream();
-        serial.encode(tmp);
-
-        out.write(tmp.toByteArray());
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
+        serial.encode(out);
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/CertificateSubjectName.java b/src/java.base/share/classes/sun/security/x509/CertificateSubjectName.java
index 81d344c7dcade..bb5ba5eb98c10 100644
--- a/src/java.base/share/classes/sun/security/x509/CertificateSubjectName.java
+++ b/src/java.base/share/classes/sun/security/x509/CertificateSubjectName.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -27,7 +27,6 @@
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.OutputStream;
 import java.util.Enumeration;
 
 import javax.security.auth.x500.X500Principal;
@@ -107,11 +106,9 @@ public String toString() {
      * @param out the DerOutputStream to marshal the contents to.
      * @exception IOException on errors.
      */
-    public void encode(OutputStream out) throws IOException {
-        DerOutputStream tmp = new DerOutputStream();
-        dnName.encode(tmp);
-
-        out.write(tmp.toByteArray());
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
+        dnName.encode(out);
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/CertificateValidity.java b/src/java.base/share/classes/sun/security/x509/CertificateValidity.java
index a5cff7d4bfa55..2b2b7021691c1 100644
--- a/src/java.base/share/classes/sun/security/x509/CertificateValidity.java
+++ b/src/java.base/share/classes/sun/security/x509/CertificateValidity.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,7 +25,6 @@
 package sun.security.x509;
 
 import java.io.IOException;
-import java.io.OutputStream;
 import java.security.cert.*;
 import java.util.Date;
 import java.util.Enumeration;
@@ -144,10 +143,11 @@ public String toString() {
     /**
      * Encode the CertificateValidity period in DER form to the stream.
      *
-     * @param out the OutputStream to marshal the contents to.
+     * @param out the DerOutputStream to marshal the contents to.
      * @exception IOException on errors.
      */
-    public void encode(OutputStream out) throws IOException {
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
 
         // in cases where default constructor is used check for
         // null values
@@ -167,10 +167,7 @@ public void encode(OutputStream out) throws IOException {
         } else {
             pair.putGeneralizedTime(notAfter);
         }
-        DerOutputStream seq = new DerOutputStream();
-        seq.write(DerValue.tag_Sequence, pair);
-
-        out.write(seq.toByteArray());
+        out.write(DerValue.tag_Sequence, pair);
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/CertificateVersion.java b/src/java.base/share/classes/sun/security/x509/CertificateVersion.java
index 7d3cb61e03725..e47dd5b20e5d1 100644
--- a/src/java.base/share/classes/sun/security/x509/CertificateVersion.java
+++ b/src/java.base/share/classes/sun/security/x509/CertificateVersion.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -27,7 +27,6 @@
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.OutputStream;
 import java.util.Enumeration;
 
 import sun.security.util.*;
@@ -155,10 +154,11 @@ public String toString() {
     /**
      * Encode the CertificateVersion period in DER form to the stream.
      *
-     * @param out the OutputStream to marshal the contents to.
+     * @param out the DerOutputStream to marshal the contents to.
      * @exception IOException on errors.
      */
-    public void encode(OutputStream out) throws IOException {
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
         // Nothing for default
         if (version == V1) {
             return;
@@ -166,11 +166,8 @@ public void encode(OutputStream out) throws IOException {
         DerOutputStream tmp = new DerOutputStream();
         tmp.putInteger(version);
 
-        DerOutputStream seq = new DerOutputStream();
-        seq.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0),
+        out.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0),
                   tmp);
-
-        out.write(seq.toByteArray());
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/CertificateX509Key.java b/src/java.base/share/classes/sun/security/x509/CertificateX509Key.java
index d78d618a5badb..cab22763dcf25 100644
--- a/src/java.base/share/classes/sun/security/x509/CertificateX509Key.java
+++ b/src/java.base/share/classes/sun/security/x509/CertificateX509Key.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -28,7 +28,6 @@
 import java.security.PublicKey;
 import java.io.InputStream;
 import java.io.IOException;
-import java.io.OutputStream;
 import java.util.Enumeration;
 
 import sun.security.util.*;
@@ -97,14 +96,12 @@ public String toString() {
     /**
      * Encode the key in DER form to the stream.
      *
-     * @param out the OutputStream to marshal the contents to.
+     * @param out the DerOutputStream to marshal the contents to.
      * @exception IOException on errors.
      */
-    public void encode(OutputStream out) throws IOException {
-        DerOutputStream tmp = new DerOutputStream();
-        tmp.write(key.getEncoded());
-
-        out.write(tmp.toByteArray());
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
+        out.write(key.getEncoded());
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/DeltaCRLIndicatorExtension.java b/src/java.base/share/classes/sun/security/x509/DeltaCRLIndicatorExtension.java
index 10b5a9dc09e95..d16ff142a3d53 100644
--- a/src/java.base/share/classes/sun/security/x509/DeltaCRLIndicatorExtension.java
+++ b/src/java.base/share/classes/sun/security/x509/DeltaCRLIndicatorExtension.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,8 +25,9 @@
 
 package sun.security.x509;
 
+import sun.security.util.DerOutputStream;
+
 import java.io.IOException;
-import java.io.OutputStream;
 import java.math.BigInteger;
 import java.util.Enumeration;
 
@@ -109,7 +110,8 @@ public DeltaCRLIndicatorExtension(Boolean critical, Object value)
      * @param out the DerOutputStream to write the extension to.
      * @exception IOException on encoding errors.
      */
-    public void encode(OutputStream out) throws IOException {
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
        DerOutputStream  tmp = new DerOutputStream();
         super.encode(out, PKIXExtensions.DeltaCRLIndicator_Id, true);
     }
diff --git a/src/java.base/share/classes/sun/security/x509/ExtendedKeyUsageExtension.java b/src/java.base/share/classes/sun/security/x509/ExtendedKeyUsageExtension.java
index 55842ed13f5c2..329bc80365674 100644
--- a/src/java.base/share/classes/sun/security/x509/ExtendedKeyUsageExtension.java
+++ b/src/java.base/share/classes/sun/security/x509/ExtendedKeyUsageExtension.java
@@ -26,7 +26,6 @@
 package sun.security.x509;
 
 import java.io.IOException;
-import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.HashMap;
@@ -197,15 +196,14 @@ public String toString() {
      * @param out the DerOutputStream to write the extension to.
      * @exception IOException on encoding errors.
      */
-    public void encode(OutputStream out) throws IOException {
-        DerOutputStream tmp = new DerOutputStream();
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
         if (extensionValue == null) {
           extensionId = PKIXExtensions.ExtendedKeyUsage_Id;
           critical = false;
           encodeThis();
         }
-        super.encode(tmp);
-        out.write(tmp.toByteArray());
+        super.encode(out);
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/Extension.java b/src/java.base/share/classes/sun/security/x509/Extension.java
index 5649d2a12066e..0dd040306af15 100644
--- a/src/java.base/share/classes/sun/security/x509/Extension.java
+++ b/src/java.base/share/classes/sun/security/x509/Extension.java
@@ -141,22 +141,28 @@ public static Extension newExtension(ObjectIdentifier extensionId,
         return ext;
     }
 
-    public void encode(OutputStream out) throws IOException {
+    /**
+     * Implementing {@link java.security.cert.Extension#encode(OutputStream)}.
+     * This implementation is made final to make sure all {@code encode()}
+     * methods in child classes are actually implementations of
+     * {@link #encode(DerOutputStream)} below.
+     *
+     * @param out the output stream
+     * @throws IOException
+     */
+    @Override
+    public final void encode(OutputStream out) throws IOException {
         if (out == null) {
             throw new NullPointerException();
         }
 
-        DerOutputStream dos1 = new DerOutputStream();
-        DerOutputStream dos2 = new DerOutputStream();
-
-        dos1.putOID(extensionId);
-        if (critical) {
-            dos1.putBoolean(critical);
+        if (out instanceof DerOutputStream dos) {
+            encode(dos);
+        } else {
+            DerOutputStream dos = new DerOutputStream();
+            encode(dos);
+            out.write(dos.toByteArray());
         }
-        dos1.putOctetString(extensionValue);
-
-        dos2.write(DerValue.tag_Sequence, dos1);
-        out.write(dos2.toByteArray());
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/FreshestCRLExtension.java b/src/java.base/share/classes/sun/security/x509/FreshestCRLExtension.java
index 84dcb284cab03..3d2be346fcd96 100644
--- a/src/java.base/share/classes/sun/security/x509/FreshestCRLExtension.java
+++ b/src/java.base/share/classes/sun/security/x509/FreshestCRLExtension.java
@@ -25,8 +25,9 @@
 
 package sun.security.x509;
 
+import sun.security.util.DerOutputStream;
+
 import java.io.IOException;
-import java.io.OutputStream;
 import java.math.BigInteger;
 import java.util.Enumeration;
 import java.util.List;
@@ -93,7 +94,8 @@ public FreshestCRLExtension(Boolean critical, Object value)
      * @param out the DerOutputStream to write the extension to.
      * @exception IOException on encoding errors.
      */
-    public void encode(OutputStream out) throws IOException {
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
         super.encode(out, PKIXExtensions.FreshestCRL_Id, false);
     }
 }
diff --git a/src/java.base/share/classes/sun/security/x509/InhibitAnyPolicyExtension.java b/src/java.base/share/classes/sun/security/x509/InhibitAnyPolicyExtension.java
index 6fa2ae16729fd..381c63702d94b 100644
--- a/src/java.base/share/classes/sun/security/x509/InhibitAnyPolicyExtension.java
+++ b/src/java.base/share/classes/sun/security/x509/InhibitAnyPolicyExtension.java
@@ -26,7 +26,6 @@
 package sun.security.x509;
 
 import java.io.IOException;
-import java.io.OutputStream;
 import java.util.Enumeration;
 
 import sun.security.util.*;
@@ -160,16 +159,14 @@ public String toString() {
       *
       * @param out the DerOutputStream to encode the extension to.
       */
-     public void encode(OutputStream out) throws IOException {
-         DerOutputStream tmp = new DerOutputStream();
+     @Override
+     public void encode(DerOutputStream out) throws IOException {
          if (extensionValue == null) {
              this.extensionId = PKIXExtensions.InhibitAnyPolicy_Id;
              critical = true;
              encodeThis();
          }
-         super.encode(tmp);
-
-         out.write(tmp.toByteArray());
+         super.encode(out);
      }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/InvalidityDateExtension.java b/src/java.base/share/classes/sun/security/x509/InvalidityDateExtension.java
index 7fac65f3ee592..e4e48563e5f68 100644
--- a/src/java.base/share/classes/sun/security/x509/InvalidityDateExtension.java
+++ b/src/java.base/share/classes/sun/security/x509/InvalidityDateExtension.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,6 @@
 package sun.security.x509;
 
 import java.io.IOException;
-import java.io.OutputStream;
 import java.util.Date;
 import java.util.Enumeration;
 
@@ -177,16 +176,14 @@ public String toString() {
      * @param out the DerOutputStream to write the extension to
      * @exception IOException on encoding errors
      */
-    public void encode(OutputStream out) throws IOException {
-        DerOutputStream  tmp = new DerOutputStream();
-
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
         if (this.extensionValue == null) {
             this.extensionId = PKIXExtensions.InvalidityDate_Id;
             this.critical = false;
             encodeThis();
         }
-        super.encode(tmp);
-        out.write(tmp.toByteArray());
+        super.encode(out);
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/IssuerAlternativeNameExtension.java b/src/java.base/share/classes/sun/security/x509/IssuerAlternativeNameExtension.java
index da7dde9a9b8dc..7ace854df0dd1 100644
--- a/src/java.base/share/classes/sun/security/x509/IssuerAlternativeNameExtension.java
+++ b/src/java.base/share/classes/sun/security/x509/IssuerAlternativeNameExtension.java
@@ -26,7 +26,6 @@
 package sun.security.x509;
 
 import java.io.IOException;
-import java.io.OutputStream;
 import java.util.Enumeration;
 
 import sun.security.util.*;
@@ -159,18 +158,17 @@ public String toString() {
     /**
      * Write the extension to the OutputStream.
      *
-     * @param out the OutputStream to write the extension to.
+     * @param out the DerOutputStream to write the extension to.
      * @exception IOException on encoding error.
      */
-    public void encode(OutputStream out) throws IOException {
-        DerOutputStream tmp = new DerOutputStream();
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
         if (extensionValue == null) {
             extensionId = PKIXExtensions.IssuerAlternativeName_Id;
             critical = false;
             encodeThis();
         }
-        super.encode(tmp);
-        out.write(tmp.toByteArray());
+        super.encode(out);
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/IssuingDistributionPointExtension.java b/src/java.base/share/classes/sun/security/x509/IssuingDistributionPointExtension.java
index bc0df11a8a231..9f8a6b1b5b86d 100644
--- a/src/java.base/share/classes/sun/security/x509/IssuingDistributionPointExtension.java
+++ b/src/java.base/share/classes/sun/security/x509/IssuingDistributionPointExtension.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,6 @@
 package sun.security.x509;
 
 import java.io.IOException;
-import java.io.OutputStream;
 
 import java.util.*;
 
@@ -234,15 +233,14 @@ public String getName() {
      * @param out the output stream.
      * @exception IOException on encoding error.
      */
-    public void encode(OutputStream out) throws IOException {
-        DerOutputStream tmp = new DerOutputStream();
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
         if (this.extensionValue == null) {
             this.extensionId = PKIXExtensions.IssuingDistributionPoint_Id;
             this.critical = false;
             encodeThis();
         }
-        super.encode(tmp);
-        out.write(tmp.toByteArray());
+        super.encode(out);
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/KeyUsageExtension.java b/src/java.base/share/classes/sun/security/x509/KeyUsageExtension.java
index f02d8eeb636cd..69235647e13d7 100644
--- a/src/java.base/share/classes/sun/security/x509/KeyUsageExtension.java
+++ b/src/java.base/share/classes/sun/security/x509/KeyUsageExtension.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,6 @@
 package sun.security.x509;
 
 import java.io.IOException;
-import java.io.OutputStream;
 import java.util.Enumeration;
 
 import sun.security.util.*;
@@ -318,16 +317,14 @@ public String toString() {
      * @param out the DerOutputStream to write the extension to.
      * @exception IOException on encoding errors.
      */
-    public void encode(OutputStream out) throws IOException {
-       DerOutputStream  tmp = new DerOutputStream();
-
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
        if (this.extensionValue == null) {
            this.extensionId = PKIXExtensions.KeyUsage_Id;
            this.critical = true;
            encodeThis();
        }
-       super.encode(tmp);
-       out.write(tmp.toByteArray());
+       super.encode(out);
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/NameConstraintsExtension.java b/src/java.base/share/classes/sun/security/x509/NameConstraintsExtension.java
index e595fbd68111b..ae4d0cc1565fe 100644
--- a/src/java.base/share/classes/sun/security/x509/NameConstraintsExtension.java
+++ b/src/java.base/share/classes/sun/security/x509/NameConstraintsExtension.java
@@ -26,7 +26,6 @@
 package sun.security.x509;
 
 import java.io.IOException;
-import java.io.OutputStream;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
 import java.util.*;
@@ -233,18 +232,17 @@ public String toString() {
     /**
      * Write the extension to the OutputStream.
      *
-     * @param out the OutputStream to write the extension to.
+     * @param out the DerOutputStream to write the extension to.
      * @exception IOException on encoding errors.
      */
-    public void encode(OutputStream out) throws IOException {
-        DerOutputStream tmp = new DerOutputStream();
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
         if (this.extensionValue == null) {
             this.extensionId = PKIXExtensions.NameConstraints_Id;
             this.critical = true;
             encodeThis();
         }
-        super.encode(tmp);
-        out.write(tmp.toByteArray());
+        super.encode(out);
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/NetscapeCertTypeExtension.java b/src/java.base/share/classes/sun/security/x509/NetscapeCertTypeExtension.java
index d455f00f8a8fd..2c297aee92721 100644
--- a/src/java.base/share/classes/sun/security/x509/NetscapeCertTypeExtension.java
+++ b/src/java.base/share/classes/sun/security/x509/NetscapeCertTypeExtension.java
@@ -26,7 +26,6 @@
 package sun.security.x509;
 
 import java.io.IOException;
-import java.io.OutputStream;
 import java.util.*;
 
 import sun.security.util.*;
@@ -264,16 +263,14 @@ public String toString() {
      * @param out the DerOutputStream to write the extension to.
      * @exception IOException on encoding errors.
      */
-    public void encode(OutputStream out) throws IOException {
-        DerOutputStream  tmp = new DerOutputStream();
-
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
         if (this.extensionValue == null) {
             this.extensionId = NetscapeCertType_Id;
             this.critical = true;
             encodeThis();
         }
-        super.encode(tmp);
-        out.write(tmp.toByteArray());
+        super.encode(out);
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/PolicyConstraintsExtension.java b/src/java.base/share/classes/sun/security/x509/PolicyConstraintsExtension.java
index eca45828aa638..a02c13581e314 100644
--- a/src/java.base/share/classes/sun/security/x509/PolicyConstraintsExtension.java
+++ b/src/java.base/share/classes/sun/security/x509/PolicyConstraintsExtension.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,6 @@
 package sun.security.x509;
 
 import java.io.IOException;
-import java.io.OutputStream;
 import java.util.Enumeration;
 
 import sun.security.util.*;
@@ -201,15 +200,14 @@ public String toString() {
      * @param out the DerOutputStream to write the extension to.
      * @exception IOException on encoding errors.
      */
-    public void encode(OutputStream out) throws IOException {
-        DerOutputStream tmp = new DerOutputStream();
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
         if (extensionValue == null) {
           extensionId = PKIXExtensions.PolicyConstraints_Id;
           critical = true;
           encodeThis();
         }
-        super.encode(tmp);
-        out.write(tmp.toByteArray());
+        super.encode(out);
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/PolicyMappingsExtension.java b/src/java.base/share/classes/sun/security/x509/PolicyMappingsExtension.java
index ed931a2119bc0..31b4ade1b63d1 100644
--- a/src/java.base/share/classes/sun/security/x509/PolicyMappingsExtension.java
+++ b/src/java.base/share/classes/sun/security/x509/PolicyMappingsExtension.java
@@ -26,7 +26,6 @@
 package sun.security.x509;
 
 import java.io.IOException;
-import java.io.OutputStream;
 import java.util.*;
 
 import sun.security.util.*;
@@ -146,18 +145,17 @@ public String toString() {
     /**
      * Write the extension to the OutputStream.
      *
-     * @param out the OutputStream to write the extension to.
+     * @param out the DerOutputStream to write the extension to.
      * @exception IOException on encoding errors.
      */
-    public void encode(OutputStream out) throws IOException {
-        DerOutputStream tmp = new DerOutputStream();
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
         if (extensionValue == null) {
             extensionId = PKIXExtensions.PolicyMappings_Id;
             critical = true;
             encodeThis();
         }
-        super.encode(tmp);
-        out.write(tmp.toByteArray());
+        super.encode(out);
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/PrivateKeyUsageExtension.java b/src/java.base/share/classes/sun/security/x509/PrivateKeyUsageExtension.java
index b4cc22e789ddf..cbfa5b8cb445f 100644
--- a/src/java.base/share/classes/sun/security/x509/PrivateKeyUsageExtension.java
+++ b/src/java.base/share/classes/sun/security/x509/PrivateKeyUsageExtension.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,6 @@
 package sun.security.x509;
 
 import java.io.IOException;
-import java.io.OutputStream;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateParsingException;
 import java.security.cert.CertificateExpiredException;
@@ -237,18 +236,17 @@ public void valid(Date now)
     /**
      * Write the extension to the OutputStream.
      *
-     * @param out the OutputStream to write the extension to.
+     * @param out the DerOutputStream to write the extension to.
      * @exception IOException on encoding errors.
      */
-    public void encode(OutputStream out) throws IOException {
-        DerOutputStream tmp = new DerOutputStream();
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
         if (extensionValue == null) {
             extensionId = PKIXExtensions.PrivateKeyUsage_Id;
             critical = false;
             encodeThis();
         }
-        super.encode(tmp);
-        out.write(tmp.toByteArray());
+        super.encode(out);
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/SubjectAlternativeNameExtension.java b/src/java.base/share/classes/sun/security/x509/SubjectAlternativeNameExtension.java
index 95eb67e0dd127..7e9487f372a36 100644
--- a/src/java.base/share/classes/sun/security/x509/SubjectAlternativeNameExtension.java
+++ b/src/java.base/share/classes/sun/security/x509/SubjectAlternativeNameExtension.java
@@ -26,7 +26,6 @@
 package sun.security.x509;
 
 import java.io.IOException;
-import java.io.OutputStream;
 import java.util.Enumeration;
 
 import sun.security.util.*;
@@ -161,18 +160,17 @@ public String toString() {
     /**
      * Write the extension to the OutputStream.
      *
-     * @param out the OutputStream to write the extension to.
+     * @param out the DerOutputStream to write the extension to.
      * @exception IOException on encoding errors.
      */
-    public void encode(OutputStream out) throws IOException {
-        DerOutputStream tmp = new DerOutputStream();
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
         if (extensionValue == null) {
             extensionId = PKIXExtensions.SubjectAlternativeName_Id;
             critical = false;
             encodeThis();
         }
-        super.encode(tmp);
-        out.write(tmp.toByteArray());
+        super.encode(out);
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/SubjectInfoAccessExtension.java b/src/java.base/share/classes/sun/security/x509/SubjectInfoAccessExtension.java
index 0acdf96057f84..f04fd9432f278 100644
--- a/src/java.base/share/classes/sun/security/x509/SubjectInfoAccessExtension.java
+++ b/src/java.base/share/classes/sun/security/x509/SubjectInfoAccessExtension.java
@@ -26,7 +26,6 @@
 package sun.security.x509;
 
 import java.io.IOException;
-import java.io.OutputStream;
 
 import java.util.Collections;
 import java.util.*;
@@ -154,15 +153,14 @@ public String getName() {
      * @param out the DerOutputStream to write the extension to.
      * @exception IOException on encoding errors.
      */
-    public void encode(OutputStream out) throws IOException {
-        DerOutputStream tmp = new DerOutputStream();
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
         if (this.extensionValue == null) {
             this.extensionId = PKIXExtensions.SubjectInfoAccess_Id;
             this.critical = false;
             encodeThis();
         }
-        super.encode(tmp);
-        out.write(tmp.toByteArray());
+        super.encode(out);
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/SubjectKeyIdentifierExtension.java b/src/java.base/share/classes/sun/security/x509/SubjectKeyIdentifierExtension.java
index b3dd2132407e9..cc9f1d121b118 100644
--- a/src/java.base/share/classes/sun/security/x509/SubjectKeyIdentifierExtension.java
+++ b/src/java.base/share/classes/sun/security/x509/SubjectKeyIdentifierExtension.java
@@ -26,7 +26,6 @@
 package sun.security.x509;
 
 import java.io.IOException;
-import java.io.OutputStream;
 import java.util.Enumeration;
 
 import sun.security.util.*;
@@ -122,18 +121,17 @@ public String toString() {
     /**
      * Write the extension to the OutputStream.
      *
-     * @param out the OutputStream to write the extension to.
+     * @param out the DerOutputStream to write the extension to.
      * @exception IOException on encoding errors.
      */
-    public void encode(OutputStream out) throws IOException {
-        DerOutputStream tmp = new DerOutputStream();
+    @Override
+    public void encode(DerOutputStream out) throws IOException {
         if (extensionValue == null) {
             extensionId = PKIXExtensions.SubjectKey_Id;
             critical = false;
             encodeThis();
         }
-        super.encode(tmp);
-        out.write(tmp.toByteArray());
+        super.encode(out);
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java b/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java
index 1523cde227d38..e9c0e434c97d6 100644
--- a/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java
+++ b/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java
@@ -1260,7 +1260,7 @@ private X500Principal getCertIssuer(X509CRLEntryImpl entry,
     }
 
     @Override
-    public void derEncode(OutputStream out) throws IOException {
+    public void derEncode(DerOutputStream out) throws IOException {
         if (signedCRL == null)
             throw new IOException("Null CRL to encode");
         out.write(signedCRL.clone());
diff --git a/src/java.base/share/classes/sun/security/x509/X509CertImpl.java b/src/java.base/share/classes/sun/security/x509/X509CertImpl.java
index 5a296bf9494f5..e6ab259d7ff32 100644
--- a/src/java.base/share/classes/sun/security/x509/X509CertImpl.java
+++ b/src/java.base/share/classes/sun/security/x509/X509CertImpl.java
@@ -332,7 +332,7 @@ public void encode(OutputStream out)
      *
      * @exception IOException on encoding error.
      */
-    public void derEncode(OutputStream out) throws IOException {
+    public void derEncode(DerOutputStream out) throws IOException {
         if (signedCert == null)
             throw new IOException("Null certificate to encode");
         out.write(signedCert.clone());
diff --git a/src/java.base/share/classes/sun/security/x509/X509CertInfo.java b/src/java.base/share/classes/sun/security/x509/X509CertInfo.java
index 5fbc9a08f54fe..d169617a2f401 100644
--- a/src/java.base/share/classes/sun/security/x509/X509CertInfo.java
+++ b/src/java.base/share/classes/sun/security/x509/X509CertInfo.java
@@ -26,7 +26,6 @@
 package sun.security.x509;
 
 import java.io.IOException;
-import java.io.OutputStream;
 
 import java.security.cert.*;
 import java.util.*;
@@ -179,14 +178,15 @@ public X509CertInfo(DerValue derVal) throws CertificateParsingException {
      * @exception CertificateException on encoding errors.
      * @exception IOException on other errors.
      */
-    public void encode(OutputStream out)
-    throws CertificateException, IOException {
+    @Override
+    public void encode(DerOutputStream out)
+            throws CertificateException, IOException {
         if (rawCertInfo == null) {
-            DerOutputStream tmp = new DerOutputStream();
-            emit(tmp);
-            rawCertInfo = tmp.toByteArray();
+            emit(out);
+            rawCertInfo = out.toByteArray();
+        } else {
+            out.write(rawCertInfo.clone());
         }
-        out.write(rawCertInfo.clone());
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/util/calendar/ZoneInfo.java b/src/java.base/share/classes/sun/util/calendar/ZoneInfo.java
index e04b769e91559..4e1cbc5631c12 100644
--- a/src/java.base/share/classes/sun/util/calendar/ZoneInfo.java
+++ b/src/java.base/share/classes/sun/util/calendar/ZoneInfo.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -45,14 +45,14 @@
  * for the {@link #getOffset(int,int,int,int,int,int) getOffset}
  * method that takes Gregorian calendar date fields.
  * 

- * This table covers transitions from 1900 until 2037 (as of version - * 1.4), Before 1900, it assumes that there was no daylight saving + * This table covers transitions from 1900 until 2100 (as of version + * 23), Before 1900, it assumes that there was no daylight saving * time and the getOffset methods always return the * {@link #getRawOffset} value. No Local Mean Time is supported. If a * specified date is beyond the transition table and this time zone is - * supposed to observe daylight saving time in 2037, it delegates + * supposed to observe daylight saving time in 2100, it delegates * operations to a {@link java.util.SimpleTimeZone SimpleTimeZone} - * object created using the daylight saving time schedule as of 2037. + * object created using the daylight saving time schedule as of 2100. *

* The date items, transitions, GMT offset(s), etc. are read from a database * file. See {@link ZoneInfoFile} for details. diff --git a/src/java.base/share/classes/sun/util/calendar/ZoneInfoFile.java b/src/java.base/share/classes/sun/util/calendar/ZoneInfoFile.java index 3762eb820bb98..12c098d283717 100644 --- a/src/java.base/share/classes/sun/util/calendar/ZoneInfoFile.java +++ b/src/java.base/share/classes/sun/util/calendar/ZoneInfoFile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -398,16 +398,16 @@ static long readEpochSec(DataInput in) throws IOException { // ZoneInfo starts with UTC1900 private static final long UTC1900 = -2208988800L; - // ZoneInfo ends with UTC2037 - // LocalDateTime.of(2038, 1, 1, 0, 0, 0).toEpochSecond(ZoneOffset.UTC) - 1; - private static final long UTC2037 = 2145916799L; + // ZoneInfo ends with UTC2100 + // LocalDateTime.of(2101, 1, 1, 0, 0, 0).toEpochSecond(ZoneOffset.UTC) - 1; + private static final long UTC2100 = 4133980799L; - // ZoneInfo has an ending entry for 2037, this need to be offset by + // ZoneInfo has an ending entry for 2100, this need to be offset by // a "rawOffset" - // LocalDateTime.of(2037, 1, 1, 0, 0, 0).toEpochSecond(ZoneOffset.UTC)); - private static final long LDT2037 = 2114380800L; + // LocalDateTime.of(2100, 1, 1, 0, 0, 0).toEpochSecond(ZoneOffset.UTC); + private static final long LDT2100 = 4102444800L; - //Current time. Used to determine future GMToffset transitions + //Current time. Used to determine future GMT offset transitions private static final long CURRT = System.currentTimeMillis()/1000; /* Get a ZoneInfo instance. @@ -474,7 +474,7 @@ private static ZoneInfo getZoneInfo(String zoneId, for (; i < savingsInstantTransitions.length; i++) { long trans = savingsInstantTransitions[i]; - if (trans > UTC2037) { + if (trans > UTC2100) { // no trans beyond LASTYEAR lastyear = LASTYEAR; break; @@ -621,11 +621,11 @@ private static ZoneInfo getZoneInfo(String zoneId, } } else if (nTrans > 0) { // only do this if there is something in table already if (lastyear < LASTYEAR) { - // ZoneInfo has an ending entry for 2037 + // ZoneInfo has an ending entry for 2100 //long trans = OffsetDateTime.of(LASTYEAR, 1, 1, 0, 0, 0, 0, // ZoneOffset.ofTotalSeconds(rawOffset/1000)) // .toEpochSecond(); - long trans = LDT2037 - rawOffset/1000; + long trans = LDT2100 - rawOffset/1000; int offsetIndex = indexOf(offsets, 0, nOffsets, rawOffset/1000); if (offsetIndex == nOffsets) @@ -810,7 +810,9 @@ private static int getYear(long epochSecond, int offset) { private static final long DST_MASK = 0xf0L; private static final int DST_NSHIFT = 4; private static final int TRANSITION_NSHIFT = 12; - private static final int LASTYEAR = 2037; + // The `last` year that transitions are accounted for. If there are + // rules that go beyond this LASTYEAR, the value needs to be expanded. + private static final int LASTYEAR = 2100; // from: 0 for offset lookup, 1 for dstsvings lookup private static int indexOf(int[] offsets, int from, int nOffsets, int offset) { diff --git a/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties b/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties index 3ed4f873a3c63..e543398fac1c1 100644 --- a/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties +++ b/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -258,6 +258,7 @@ VES=VES VND=VND VUV=VUV WST=WST +XAD=XAD XAF=XAF XAG=XAG XAU=XAU @@ -485,6 +486,7 @@ ves=Venezuelan Bol\u00edvar Soberano vnd=Vietnamese Dong vuv=Vanuatu Vatu wst=Samoan Tala +xad=Arab Accounting Dinar xaf=CFA Franc BEAC xag=Silver xau=Gold diff --git a/src/java.base/share/conf/security/java.security b/src/java.base/share/conf/security/java.security index 08effe23fce47..9391ad0d79843 100644 --- a/src/java.base/share/conf/security/java.security +++ b/src/java.base/share/conf/security/java.security @@ -786,6 +786,29 @@ jdk.jar.disabledAlgorithms=MD2, MD5, RSA keySize < 1024, \ # See the specification of "jdk.certpath.disabledAlgorithms" for the # syntax of the disabled algorithm string. # +# Additional TLS-specific syntax supported by this property: +# +# - TLS cipher suites can be disabled with this property using one or more +# "*" wildcard characters. For example, "TLS_RSA_*" disables all cipher +# suites that start with "TLS_RSA_". Only cipher suites starting with +# "TLS_" are allowed to have wildcard characters. +# +# - TLS protocol specific usage constraints are supported by this property: +# +# UsageConstraint: +# usage UsageType { UsageType } +# +# UsageType: +# HandshakeSignature | CertificateSignature +# +# HandshakeSignature restricts the use of the algorithm in TLS handshake +# signatures. CertificateSignature restricts the use of the algorithm in +# certificate signatures. An algorithm with this constraint cannot include +# other usage types defined in the jdk.certpath.disabledAlgorithms +# property. The usage type follows the keyword and more than one usage type +# can be specified with a whitespace delimiter. +# Example: "rsa_pkcs1_sha1 usage HandshakeSignature" +# # Note: The algorithm restrictions do not apply to trust anchors or # self-signed certificates. # @@ -794,10 +817,11 @@ jdk.jar.disabledAlgorithms=MD2, MD5, RSA keySize < 1024, \ # # Example: # jdk.tls.disabledAlgorithms=MD5, SSLv3, DSA, RSA keySize < 2048, \ -# rsa_pkcs1_sha1, secp224r1 +# rsa_pkcs1_sha1, secp224r1, TLS_RSA_* jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, DTLSv1.0, RC4, DES, \ MD5withRSA, DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL, \ - ECDH + ECDH, TLS_RSA_*, rsa_pkcs1_sha1 usage HandshakeSignature, \ + ecdsa_sha1 usage HandshakeSignature, dsa_sha1 usage HandshakeSignature # # Legacy algorithms for Secure Socket Layer/Transport Layer Security (SSL/TLS) @@ -1431,17 +1455,18 @@ jdk.io.permissionsUseCanonicalPath=false jdk.tls.alpnCharset=ISO_8859_1 # -# JNDI Object Factories Filter +# Global JNDI Object Factories Filter # # This filter is used by the JNDI runtime to control the set of object factory classes # which will be allowed to instantiate objects from object references returned by # naming/directory systems. The factory class named by the reference instance will be # matched against this filter. The filter property supports pattern-based filter syntax -# with the same format as jdk.serialFilter. +# with the same format as jdk.serialFilter. Limit patterns specified in the filter property +# are unused. # -# Each pattern is matched against the factory class name to allow or disallow it's -# instantiation. The access to a factory class is allowed unless the filter returns -# REJECTED. +# Each class name pattern is matched against the factory class name to allow or disallow its +# instantiation. The access to a factory class is allowed if the filter returns +# ALLOWED. # # Note: This property is currently used by the JDK Reference implementation. # It is not guaranteed to be examined and used by other implementations. @@ -1452,3 +1477,55 @@ jdk.tls.alpnCharset=ISO_8859_1 # The default pattern value allows any object factory class specified by the reference # instance to recreate the referenced object. #jdk.jndi.object.factoriesFilter=* + +# +# Protocol Specific JNDI/LDAP Object Factories Filter +# +# This filter is used by the JNDI/LDAP provider implementation in the JDK to further control the +# set of object factory classes which will be allowed to instantiate objects from object +# references bound to LDAP contexts. The factory class named by the reference instance will +# be matched against this filter. The filter property supports pattern-based filter syntax +# with the same format as jdk.serialFilter. Limit patterns specified in the filter property +# are unused. +# +# Each class name pattern is matched against the factory class name to allow or disallow its +# instantiation. The access to a factory class is allowed only when it is not rejected by this filter +# or by the global filter defined by "jdk.jndi.object.factoriesFilter", and at least one of these +# two filters returns ALLOWED. +# +# Note: This property is currently used by the JDK Reference implementation. +# It is not guaranteed to be examined and used by other implementations. +# +# If the system property jdk.jndi.ldap.object.factoriesFilter is also specified, it supersedes +# the security property value defined here. The default value of the property is +# "java.naming/com.sun.jndi.ldap.**;!*". +# +# The default pattern value allows any object factory class defined in the java.naming module +# to be specified by the reference instance, but rejects any other. +#jdk.jndi.ldap.object.factoriesFilter=java.naming/com.sun.jndi.ldap.**;!* + +# +# Protocol Specific JNDI/RMI Object Factories Filter +# +# This filter is used by the JNDI/RMI provider implementation in the JDK to further control the +# set of object factory classes which will be allowed to instantiate objects from object +# references bound to RMI names. The factory class named by the reference instance will +# be matched against this filter. The filter property supports pattern-based filter syntax +# with the same format as jdk.serialFilter. Limit patterns specified in the filter property +# are unused. +# +# Each class name pattern is matched against the factory class name to allow or disallow its +# instantiation. The access to a factory class is allowed only when it is not rejected by this filter +# or by the global filter defined by "jdk.jndi.object.factoriesFilter", and at least one of these +# two filters returns ALLOWED. +# +# Note: This property is currently used by the JDK Reference implementation. +# It is not guaranteed to be examined and used by other implementations. +# +# If the system property jdk.jndi.rmi.object.factoriesFilter is also specified, it supersedes +# the security property value defined here. The default value of the property is +# "jdk.naming.rmi/com.sun.jndi.rmi.**;!*". +# +# The default pattern value allows any object factory class defined in the jdk.naming.rmi module +# to be specified by the reference instance, but rejects any other. +#jdk.jndi.rmi.object.factoriesFilter=jdk.naming.rmi/com.sun.jndi.rmi.**;!* diff --git a/src/java.base/share/lib/security/default.policy b/src/java.base/share/lib/security/default.policy index c39faee2f43b8..d1eba14c252f2 100644 --- a/src/java.base/share/lib/security/default.policy +++ b/src/java.base/share/lib/security/default.policy @@ -19,6 +19,7 @@ grant codeBase "jrt:/java.net.http" { permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util"; permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www"; permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc"; + permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.ref"; permission java.lang.RuntimePermission "modifyThread"; permission java.net.SocketPermission "*","connect,resolve"; permission java.net.URLPermission "http:*","*:*"; diff --git a/src/java.base/share/man/java.1 b/src/java.base/share/man/java.1 index 44251290e05a5..fe462d1a474a0 100644 --- a/src/java.base/share/man/java.1 +++ b/src/java.base/share/man/java.1 @@ -843,10 +843,6 @@ A JNI call was made without checking for a pending exception from a previous JNI call, and the current call is not safe when an exception may be pending. .IP \[bu] 2 -The number of JNI local references existing when a JNI function -terminates exceeds the number guaranteed to be available. -See the \f[CB]EnsureLocalcapacity\f[R] function. -.IP \[bu] 2 A class descriptor is in decorated format (\f[CB]Lname;\f[R]) when it should not be. .IP \[bu] 2 diff --git a/src/java.base/share/native/launcher/main.c b/src/java.base/share/native/launcher/main.c index b734fe2ba7885..8e806e813fcaf 100644 --- a/src/java.base/share/native/launcher/main.c +++ b/src/java.base/share/native/launcher/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -150,7 +150,25 @@ main(int argc, char **argv) } } } - JLI_CmdToArgs(GetCommandLine()); + + // Obtain the command line in UTF-16, then convert it to ANSI code page + // without the "best-fit" option + LPWSTR wcCmdline = GetCommandLineW(); + int mbSize = WideCharToMultiByte(CP_ACP, + WC_NO_BEST_FIT_CHARS | WC_COMPOSITECHECK | WC_DEFAULTCHAR, + wcCmdline, -1, NULL, 0, NULL, NULL); + // If the call to WideCharToMultiByte() fails, it returns 0, which + // will then make the following JLI_MemAlloc() to issue exit(1) + LPSTR mbCmdline = JLI_MemAlloc(mbSize); + if (WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS | WC_COMPOSITECHECK | WC_DEFAULTCHAR, + wcCmdline, -1, mbCmdline, mbSize, NULL, NULL) == 0) { + perror("command line encoding conversion failure"); + exit(1); + } + + JLI_CmdToArgs(mbCmdline); + JLI_MemFree(mbCmdline); + margc = JLI_GetStdArgc(); // add one more to mark the end margv = (char **)JLI_MemAlloc((margc + 1) * (sizeof(char *))); diff --git a/src/java.base/share/native/libverify/check_code.c b/src/java.base/share/native/libverify/check_code.c index 6956be9b840aa..b3531f73f33b9 100644 --- a/src/java.base/share/native/libverify/check_code.c +++ b/src/java.base/share/native/libverify/check_code.c @@ -395,7 +395,8 @@ static jboolean is_superclass(context_type *, fullinfo_type); static void initialize_exception_table(context_type *); static int instruction_length(unsigned char *iptr, unsigned char *end); -static jboolean isLegalTarget(context_type *, int offset); +static jboolean isLegalOffset(context_type *, int bci, int offset); +static jboolean isLegalTarget(context_type *, int target); static void verify_constant_pool_type(context_type *, int, unsigned); static void initialize_dataflow(context_type *); @@ -1154,9 +1155,9 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) case JVM_OPC_goto: { /* Set the ->operand to be the instruction number of the target. */ int jump = (((signed char)(code[offset+1])) << 8) + code[offset+2]; - int target = offset + jump; - if (!isLegalTarget(context, target)) + if (!isLegalOffset(context, offset, jump)) CCerror(context, "Illegal target of jump or branch"); + int target = offset + jump; this_idata->operand.i = code_data[target]; break; } @@ -1170,9 +1171,9 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) int jump = (((signed char)(code[offset+1])) << 24) + (code[offset+2] << 16) + (code[offset+3] << 8) + (code[offset + 4]); - int target = offset + jump; - if (!isLegalTarget(context, target)) + if (!isLegalOffset(context, offset, jump)) CCerror(context, "Illegal target of jump or branch"); + int target = offset + jump; this_idata->operand.i = code_data[target]; break; } @@ -1211,13 +1212,16 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) } } saved_operand = NEW(int, keys + 2); - if (!isLegalTarget(context, offset + _ck_ntohl(lpc[0]))) + int jump = _ck_ntohl(lpc[0]); + if (!isLegalOffset(context, offset, jump)) CCerror(context, "Illegal default target in switch"); - saved_operand[keys + 1] = code_data[offset + _ck_ntohl(lpc[0])]; + int target = offset + jump; + saved_operand[keys + 1] = code_data[target]; for (k = keys, lptr = &lpc[3]; --k >= 0; lptr += delta) { - int target = offset + _ck_ntohl(lptr[0]); - if (!isLegalTarget(context, target)) + jump = _ck_ntohl(lptr[0]); + if (!isLegalOffset(context, offset, jump)) CCerror(context, "Illegal branch in tableswitch"); + target = offset + jump; saved_operand[k + 1] = code_data[target]; } saved_operand[0] = keys + 1; /* number of successors */ @@ -1746,11 +1750,24 @@ static int instruction_length(unsigned char *iptr, unsigned char *end) /* Given the target of a branch, make sure that it's a legal target. */ static jboolean -isLegalTarget(context_type *context, int offset) +isLegalTarget(context_type *context, int target) +{ + int code_length = context->code_length; + int *code_data = context->code_data; + return (target >= 0 && target < code_length && code_data[target] >= 0); +} + +/* Given a bci and offset, make sure the offset is valid and the target is legal */ +static jboolean +isLegalOffset(context_type *context, int bci, int offset) { int code_length = context->code_length; int *code_data = context->code_data; - return (offset >= 0 && offset < code_length && code_data[offset] >= 0); + int max_offset = 65535; // JVMS 4.11 + int min_offset = -65535; + if (offset < min_offset || offset > max_offset) return JNI_FALSE; + int target = bci + offset; + return (target >= 0 && target < code_length && code_data[target] >= 0); } diff --git a/src/java.base/unix/native/libjava/TimeZone_md.c b/src/java.base/unix/native/libjava/TimeZone_md.c index 5c40fc8b3206e..634c85abee48f 100644 --- a/src/java.base/unix/native/libjava/TimeZone_md.c +++ b/src/java.base/unix/native/libjava/TimeZone_md.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -60,7 +60,6 @@ static char *isFileIdentical(char* buf, size_t size, char *pathname); #endif #if defined(__linux__) || defined(_ALLBSD_SOURCE) -static const char *ETC_TIMEZONE_FILE = "/etc/timezone"; static const char *ZONEINFO_DIR = "/usr/share/zoneinfo"; static const char *DEFAULT_ZONEINFO_FILE = "/etc/localtime"; #else @@ -239,40 +238,13 @@ getPlatformTimeZoneID() { struct stat64 statbuf; char *tz = NULL; - FILE *fp; int fd; char *buf; size_t size; int res; -#if defined(__linux__) - /* - * Try reading the /etc/timezone file for Debian distros. There's - * no spec of the file format available. This parsing assumes that - * there's one line of an Olson tzid followed by a '\n', no - * leading or trailing spaces, no comments. - */ - if ((fp = fopen(ETC_TIMEZONE_FILE, "r")) != NULL) { - char line[256]; - - if (fgets(line, sizeof(line), fp) != NULL) { - char *p = strchr(line, '\n'); - if (p != NULL) { - *p = '\0'; - } - if (strlen(line) > 0) { - tz = strdup(line); - } - } - (void) fclose(fp); - if (tz != NULL) { - return tz; - } - } -#endif /* defined(__linux__) */ - /* - * Next, try /etc/localtime to find the zone ID. + * Try /etc/localtime to find the zone ID. */ RESTARTABLE(lstat64(DEFAULT_ZONEINFO_FILE, &statbuf), res); if (res == -1) { diff --git a/src/java.base/unix/native/libnio/ch/Net.c b/src/java.base/unix/native/libnio/ch/Net.c index d4a15b34ea6f3..563d337006b82 100644 --- a/src/java.base/unix/native/libnio/ch/Net.c +++ b/src/java.base/unix/native/libnio/ch/Net.c @@ -200,6 +200,11 @@ Java_sun_nio_ch_Net_isExclusiveBindAvailable(JNIEnv *env, jclass clazz) { return -1; } +JNIEXPORT jboolean JNICALL +Java_sun_nio_ch_Net_shouldShutdownWriteBeforeClose0(JNIEnv *env, jclass clazz) { + return JNI_FALSE; +} + JNIEXPORT jboolean JNICALL Java_sun_nio_ch_Net_shouldSetBothIPv4AndIPv6Options0(JNIEnv* env, jclass cl) { diff --git a/src/java.base/windows/native/include/jni_md.h b/src/java.base/windows/native/include/jni_md.h index b8c144fb047f5..f795421937e97 100644 --- a/src/java.base/windows/native/include/jni_md.h +++ b/src/java.base/windows/native/include/jni_md.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,9 +32,8 @@ #define JNIIMPORT __declspec(dllimport) #define JNICALL __stdcall -// 'long' is always 32 bit on windows so this matches what jdk expects -typedef long jint; -typedef __int64 jlong; +typedef int jint; +typedef long long jlong; typedef signed char jbyte; #endif /* !_JAVASOFT_JNI_MD_H_ */ diff --git a/src/java.base/windows/native/libnio/ch/Net.c b/src/java.base/windows/native/libnio/ch/Net.c index d4410c0ca2d34..81b46556f16e1 100644 --- a/src/java.base/windows/native/libnio/ch/Net.c +++ b/src/java.base/windows/native/libnio/ch/Net.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -117,6 +117,11 @@ Java_sun_nio_ch_Net_isExclusiveBindAvailable(JNIEnv *env, jclass clazz) { return 1; } +JNIEXPORT jboolean JNICALL +Java_sun_nio_ch_Net_shouldShutdownWriteBeforeClose0(JNIEnv *env, jclass clazz) { + return JNI_TRUE; +} + JNIEXPORT jboolean JNICALL Java_sun_nio_ch_Net_shouldSetBothIPv4AndIPv6Options0(JNIEnv* env, jclass cl) { diff --git a/src/java.desktop/macosx/classes/com/apple/laf/AquaFileChooserUI.java b/src/java.desktop/macosx/classes/com/apple/laf/AquaFileChooserUI.java index 67929fcf0db5a..d6e9cdf409ba0 100644 --- a/src/java.desktop/macosx/classes/com/apple/laf/AquaFileChooserUI.java +++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaFileChooserUI.java @@ -1181,6 +1181,9 @@ public Component getTableCellRendererComponent(final JTable list, setText(fc.getName(file)); setIcon(fc.getIcon(file)); setEnabled(isSelectableInList(file)); + + putClientProperty("html.disable", getFileChooser().getClientProperty("html.disable")); + return this; } } @@ -1246,6 +1249,9 @@ public Component getListCellRendererComponent(final JList list, final JFileChooser chooser = getFileChooser(); setText(chooser.getName(directory)); setIcon(chooser.getIcon(directory)); + + putClientProperty("html.disable", getFileChooser().getClientProperty("html.disable")); + return this; } }; diff --git a/src/java.desktop/macosx/classes/com/apple/laf/AquaKeyBindings.java b/src/java.desktop/macosx/classes/com/apple/laf/AquaKeyBindings.java index 81b87f54e10f8..6ebef93cdeee6 100644 --- a/src/java.desktop/macosx/classes/com/apple/laf/AquaKeyBindings.java +++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaKeyBindings.java @@ -155,6 +155,9 @@ LateBoundInputMap getPasswordFieldInputMap() { "shift alt KP_LEFT", null, "shift alt RIGHT", null, "shift alt KP_RIGHT", null, + "alt BACK_SPACE", null, + "ctrl W", null, + "alt DELETE", null, })); } @@ -383,7 +386,8 @@ LateBoundInputMap getTableInputMap() { "ENTER", "selectNextRowCell", "shift ENTER", "selectPreviousRowCell", "alt TAB", "focusHeader", - "alt shift TAB", "focusHeader" + "alt shift TAB", "focusHeader", + "F8", "focusHeader" })); } diff --git a/src/java.desktop/macosx/classes/com/apple/laf/AquaTabbedPaneCopyFromBasicUI.java b/src/java.desktop/macosx/classes/com/apple/laf/AquaTabbedPaneCopyFromBasicUI.java index 5722b28e576c6..5aeba8aa37f89 100644 --- a/src/java.desktop/macosx/classes/com/apple/laf/AquaTabbedPaneCopyFromBasicUI.java +++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaTabbedPaneCopyFromBasicUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -404,7 +404,11 @@ protected void installListeners() { } tabPane.addContainerListener(getHandler()); if (tabPane.getTabCount() > 0) { - htmlViews = createHTMLVector(); + Boolean htmlDisabled = (Boolean) + tabPane.getClientProperty("html.disable"); + if (!(Boolean.TRUE.equals(htmlDisabled))) { + htmlViews = createHTMLVector(); + } } } @@ -3446,8 +3450,10 @@ public void componentAdded(final ContainerEvent e) { private void updateHtmlViews(int index, boolean inserted) { final String title = tabPane.getTitleAt(index); + Boolean htmlDisabled = (Boolean) + tabPane.getClientProperty("html.disable"); final boolean isHTML = BasicHTML.isHTMLString(title); - if (isHTML) { + if (isHTML && !(Boolean.TRUE.equals(htmlDisabled))) { if (htmlViews == null) { // Initialize vector htmlViews = createHTMLVector(); } else { // Vector already exists diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java index 0ab38fc06b4ec..6e808db606c44 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -60,9 +60,11 @@ import javax.swing.JComponent; import javax.swing.JEditorPane; import javax.swing.JLabel; +import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JTextArea; import javax.swing.JList; +import javax.swing.JPopupMenu; import javax.swing.JTree; import javax.swing.KeyStroke; @@ -813,6 +815,34 @@ public Object[] call() throws Exception { }, c); } + private static Accessible getCurrentAccessiblePopupMenu(Accessible a, Component c) { + if (a == null) return null; + + return invokeAndWait(new Callable() { + @Override + public Accessible call() throws Exception { + return traversePopupMenu(a); + } + }, c); + } + + private static Accessible traversePopupMenu(Accessible a) { + // a is root level popupmenu + AccessibleContext ac = a.getAccessibleContext(); + if (ac != null) { + for (int i = 0; i < ac.getAccessibleChildrenCount(); i++) { + Accessible child = ac.getAccessibleChild(i); + if (child instanceof JMenu subMenu) { + JPopupMenu popup = subMenu.getPopupMenu(); + if (popup.isVisible()) { + return traversePopupMenu((Accessible) popup); + } + } + } + } + return a; + } + @Native private static final int JAVA_AX_ROWS = 1; @Native private static final int JAVA_AX_COLS = 2; diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java index 779423bc28fc0..babe899296f1d 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,9 +28,11 @@ import java.awt.Component; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.util.Objects; import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; +import javax.swing.JSpinner; import javax.swing.JTabbedPane; import static javax.accessibility.AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY; @@ -120,7 +122,11 @@ public void propertyChange(PropertyChangeEvent e) { if (name.equals(ACCESSIBLE_CARET_PROPERTY)) { selectedTextChanged(ptr); } else if (name.equals(ACCESSIBLE_TEXT_PROPERTY)) { - valueChanged(ptr); + AccessibleContext thisAC = accessible.getAccessibleContext(); + Accessible parentAccessible = thisAC.getAccessibleParent(); + if (!(parentAccessible instanceof JSpinner.NumberEditor)) { + valueChanged(ptr); + } } else if (name.equals(ACCESSIBLE_SELECTION_PROPERTY)) { selectionChanged(ptr); } else if (name.equals(ACCESSIBLE_TABLE_MODEL_CHANGED)) { @@ -182,7 +188,7 @@ public void propertyChange(PropertyChangeEvent e) { // Do send check box state changes to native side if (thisRole == AccessibleRole.CHECK_BOX) { - if (newValue != null && !newValue.equals(oldValue)) { + if (!Objects.equals(newValue, oldValue)) { valueChanged(ptr); } @@ -208,7 +214,7 @@ public void propertyChange(PropertyChangeEvent e) { // Do send toggle button state changes to native side if (thisRole == AccessibleRole.TOGGLE_BUTTON) { - if (newValue != null && !newValue.equals(oldValue)) { + if (!Objects.equals(newValue, oldValue)) { valueChanged(ptr); } } diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java index dd231e84f3a14..571cdc7e3d20c 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -64,6 +64,7 @@ import sun.awt.AWTAccessor.ComponentAccessor; import sun.awt.AWTAccessor.WindowAccessor; import sun.java2d.SurfaceData; +import sun.lwawt.LWKeyboardFocusManagerPeer; import sun.lwawt.LWLightweightFramePeer; import sun.lwawt.LWToolkit; import sun.lwawt.LWWindowPeer; @@ -1056,6 +1057,11 @@ public void setModalBlocked(boolean blocked) { } execute(ptr -> nativeSetEnabled(ptr, !blocked)); + + Window currFocus = LWKeyboardFocusManagerPeer.getInstance().getCurrentFocusedWindow(); + if (!blocked && (target == currFocus)) { + requestWindowFocus(); + } checkBlockingAndOrder(); } diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m index 79c5abdbc2c9e..30d41ec1cf25f 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m @@ -1022,7 +1022,11 @@ - (void)sendEvent:(NSEvent *)event { NSRect contentRect = [self.nsWindow contentRectForFrameRect:frame]; // Check if the click happened in the non-client area (title bar) - if (p.y >= (frame.origin.y + contentRect.size.height)) { + // Also, non-client area includes the edges at left, right and botton of frame + if ((p.y >= (frame.origin.y + contentRect.size.height)) || + (p.x >= (frame.origin.x + contentRect.size.width - 3)) || + (fabs(frame.origin.x - p.x) < 3) || + (fabs(frame.origin.y - p.y) < 3)) { JNIEnv *env = [ThreadUtilities getJNIEnvUncached]; jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow); if (platformWindow != NULL) { diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/MenuAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/MenuAccessibility.m index e8b28d9306854..90a147aa5a21d 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/MenuAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/MenuAccessibility.m @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,6 +24,10 @@ */ #import "MenuAccessibility.h" +#import "ThreadUtilities.h" +#import "sun_lwawt_macosx_CAccessibility.h" + +static jclass sjc_CAccessibility = NULL; /* * Implementing a protocol that represents menus both as submenu and as a @@ -51,4 +55,31 @@ - (id _Nullable)accessibilityValue return NULL; } +/* + * Return all non-ignored children. + */ +- (NSArray *)accessibilityChildren { + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBILITY_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(sjm_getCurrentAccessiblePopupMenu, sjc_CAccessibility, + "getCurrentAccessiblePopupMenu", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/Accessible;", nil); + jobject axComponent = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, + sjm_getCurrentAccessiblePopupMenu, + fAccessible, fComponent); + + CommonComponentAccessibility *currentElement = [CommonComponentAccessibility createWithAccessible:axComponent + withEnv:env withView:self->fView isCurrent:YES]; + + NSArray *children = [CommonComponentAccessibility childrenOfParent:currentElement + withEnv:env + withChildrenCode:sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN + allowIgnored:NO]; + + if ([children count] == 0) { + return nil; + } else { + return children; + } +} @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.m b/src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.m index fdd855b699417..4dac06a8a5f08 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.m @@ -237,6 +237,7 @@ @implementation CGGI_GlyphCanvas { UInt32 *src = (UInt32 *)canvas->image->data; size_t srcRowWidth = canvas->image->width; + size_t srcHeight = canvas->image->height; UInt8 *dest = (UInt8 *)info->image; size_t destRowWidth = info->width; @@ -246,12 +247,12 @@ @implementation CGGI_GlyphCanvas size_t y; // fill empty glyph image with black-on-white glyph - for (y = 0; y < height; y++) { + for (y = 0; y < height && y < srcHeight; y++) { size_t destRow = y * destRowWidth * 3; size_t srcRow = y * srcRowWidth; size_t x; - for (x = 0; x < destRowWidth; x++) { + for (x = 0; x < destRowWidth && x < srcRowWidth; x++) { CGGI_CopyARGBPixelToRGBPixel(src[srcRow + x], dest + destRow + x * 3); } @@ -289,6 +290,7 @@ @implementation CGGI_GlyphCanvas { UInt32 *src = (UInt32 *)canvas->image->data; size_t srcRowWidth = canvas->image->width; + size_t srcHeight = canvas->image->height; UInt8 *dest = (UInt8 *)info->image; size_t destRowWidth = info->width; @@ -298,11 +300,11 @@ @implementation CGGI_GlyphCanvas size_t y; // fill empty glyph image with black-on-white glyph - for (y = 0; y < height; y++) { + for (y = 0; y < height && y < srcHeight; y++) { size_t destRow = y * destRowWidth; size_t srcRow = y * srcRowWidth; size_t x; - for (x = 0; x < destRowWidth; x++) { + for (x = 0; x < destRowWidth && x < srcRowWidth; x++) { UInt32 p = src[srcRow + x]; dest[destRow + x] = CGGI_ConvertBWPixelToByteGray(p); } @@ -317,6 +319,7 @@ @implementation CGGI_GlyphCanvas UInt32 *src = (UInt32 *)canvas->image->data; size_t srcRowWidth = canvas->image->width; + size_t srcHeight = canvas->image->height; UInt8 *dest = (UInt8 *)info->image; size_t destRowWidth = info->width; @@ -325,15 +328,16 @@ @implementation CGGI_GlyphCanvas size_t y; - for (y = 0; y < height; y++) { + for (y = 0; y < height && y < srcHeight; y++) { size_t srcRow = y * srcRowWidth; if (littleEndian) { - UInt16 destRowBytes = info->rowBytes; + size_t srcRowBytes = canvas->image->rowBytes; + UInt16 destRowBytes = (info->rowBytes < srcRowBytes) ? info->rowBytes : srcRowBytes; memcpy(dest, src + srcRow, destRowBytes); dest += destRowBytes; } else { size_t x; - for (x = 0; x < destRowWidth; x++) { + for (x = 0; x < destRowWidth && x < srcRowWidth; x++) { UInt32 p = src[srcRow + x]; *dest++ = (p >> 24 & 0xFF); // blue (alpha-premultiplied) *dest++ = (p >> 16 & 0xFF); // green (alpha-premultiplied) @@ -426,8 +430,10 @@ @implementation CGGI_GlyphCanvas canvas->image->data = (void *)calloc(byteCount, sizeof(UInt8)); if (canvas->image->data == NULL) { - [[NSException exceptionWithName:NSMallocException - reason:@"Failed to allocate memory for the buffer which backs the CGContext for glyph strikes." userInfo:nil] raise]; + canvas->image->width = 0; + canvas->image->height = 0; + canvas->image->rowBytes = 0; + canvas->image->data = malloc(0); } uint32_t bmpInfo = kCGImageAlphaPremultipliedFirst; @@ -477,6 +483,10 @@ @implementation CGGI_GlyphCanvas /* * Quick and easy inline to check if this canvas is big enough. + * This function only increases the size. To get a smaller canvas, free it first. + * This function adds padding / slack multiplier to the requested size. + * So resizes must be based on the size you need, not the size of the canvas. + * The function will internally account for the multiplier it uses. */ static inline void CGGI_SizeCanvas(CGGI_GlyphCanvas *canvas, const vImagePixelCount width, @@ -484,18 +494,31 @@ @implementation CGGI_GlyphCanvas const CGGI_RenderingMode* mode) { if (canvas->image != NULL && - width < canvas->image->width && - height < canvas->image->height) + width * CGGI_GLYPH_CANVAS_SLACK <= canvas->image->width && + height * CGGI_GLYPH_CANVAS_SLACK <= canvas->image->height) { return; } + vImagePixelCount w = width * CGGI_GLYPH_CANVAS_SLACK; + vImagePixelCount h = height * CGGI_GLYPH_CANVAS_SLACK; + + // Do not allow the canvas to be resized smaller. + if (canvas->image != NULL) { + if (w < canvas->image->width) { + w = canvas->image->width; + } + if (h < canvas->image->height) { + h = canvas->image->height; + } + } + // if we don't have enough space to strike the largest glyph in the // run, resize the canvas CGGI_FreeCanvas(canvas); CGGI_InitCanvas(canvas, - width * CGGI_GLYPH_CANVAS_SLACK, - height * CGGI_GLYPH_CANVAS_SLACK, + w, + h, mode); JRSFontSetRenderingStyleOnContext(canvas->context, mode->cgFontMode); } @@ -511,6 +534,12 @@ @implementation CGGI_GlyphCanvas canvasRectToClear.data = canvas->image->data; canvasRectToClear.height = info->height; canvasRectToClear.width = info->width; + if (canvas->image->width < canvasRectToClear.width) { + canvasRectToClear.width = canvas->image->width; + } + if (canvas->image->height < canvasRectToClear.height) { + canvasRectToClear.height = canvas->image->height; + } // use the row stride of the canvas, not the info canvasRectToClear.rowBytes = canvas->image->rowBytes; @@ -870,7 +899,6 @@ @implementation CGGI_GlyphCanvas CGRect bbox = bboxes[i]; GlyphInfo *glyphInfo = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mainFontDescriptor); - if (maxWidth < glyphInfo->width) maxWidth = glyphInfo->width; if (maxHeight < glyphInfo->height) maxHeight = glyphInfo->height; @@ -946,26 +974,32 @@ @implementation CGGI_GlyphCanvas return; } - // just do one malloc, and carve it up for all the buffers - void *buffer = malloc(sizeof(CGRect) * sizeof(CGSize) * - sizeof(CGGlyph) * sizeof(UnicodeScalarValue) * len); - if (buffer == NULL) { + CGRect *bboxes = (CGRect*)calloc(len, sizeof(CGRect)); + CGSize *advances = (CGSize*)calloc(len, sizeof(CGSize)); + CGGlyph *glyphs = (CGGlyph*)calloc(len, sizeof(CGGlyph)); + UnicodeScalarValue *uniChars = (UnicodeScalarValue*)calloc(len, sizeof(UnicodeScalarValue)); + + if (bboxes == NULL || advances == NULL || glyphs == NULL || uniChars == NULL) { + free(bboxes); + free(advances); + free(glyphs); + free(uniChars); [[NSException exceptionWithName:NSMallocException reason:@"Failed to allocate memory for the temporary glyph strike and measurement buffers." userInfo:nil] raise]; } - CGRect *bboxes = (CGRect *)(buffer); - CGSize *advances = (CGSize *)(bboxes + sizeof(CGRect) * len); - CGGlyph *glyphs = (CGGlyph *)(advances + sizeof(CGGlyph) * len); - UnicodeScalarValue *uniChars = (UnicodeScalarValue *)(glyphs + sizeof(UnicodeScalarValue) * len); - CGGI_CreateGlyphsAndScanForComplexities(glyphInfos, strike, &mode, rawGlyphCodes, uniChars, glyphs, advances, bboxes, len); - free(buffer); + free(bboxes); + free(advances); + free(glyphs); + free(uniChars); } +#define TX_FIXED_UNSAFE(v) (isinf(v) || isnan(v) || fabs(v) >= (1<<30)) + /* * Calculates bounding boxes (for given transform) and advance (for untransformed 1pt-size font) for specified glyphs. */ @@ -977,6 +1011,27 @@ @implementation CGGI_GlyphCanvas size_t count, CGRect bboxes[], CGSize advances[]) { + + if (TX_FIXED_UNSAFE(tx->a) || TX_FIXED_UNSAFE(tx->b) || TX_FIXED_UNSAFE(tx->c) || + TX_FIXED_UNSAFE(tx->d) || TX_FIXED_UNSAFE(tx->tx) || TX_FIXED_UNSAFE(tx->tx)) { + + if (bboxes) { + for (int i = 0; i < count; i++) { + bboxes[i].origin.x = 0; + bboxes[i].origin.y = 0; + bboxes[i].size.width = 0; + bboxes[i].size.height = 0; + } + } + if (advances) { + for (int i = 0; i < count; i++) { + advances[i].width = 0; + advances[i].height = 0; + } + } + return; + } + if (IsEmojiFont(font)) { // Glyph metrics for emoji font are not strictly proportional to font size, // so we need to construct real-sized font object to calculate them. diff --git a/src/java.desktop/macosx/native/libjsound/PLATFORM_API_MacOSX_Ports.cpp b/src/java.desktop/macosx/native/libjsound/PLATFORM_API_MacOSX_Ports.cpp index 5f868f6e408e9..84aca8274f6fd 100644 --- a/src/java.desktop/macosx/native/libjsound/PLATFORM_API_MacOSX_Ports.cpp +++ b/src/java.desktop/macosx/native/libjsound/PLATFORM_API_MacOSX_Ports.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -124,9 +124,12 @@ OSStatus ChangeListenerProc(AudioObjectID inObjectID, UInt32 inNumberAddresses, kAudioHardwarePropertyDevices, &size); if (err == noErr) { int count = size/sizeof(AudioDeviceID); - AudioDeviceID devices[count]; + AudioDeviceID *devices = (AudioDeviceID *) malloc(size); + if (!devices) { + break; + } err = GetAudioObjectProperty(kAudioObjectSystemObject, kAudioObjectPropertyScopeGlobal, - kAudioHardwarePropertyDevices, count*sizeof(AudioDeviceID), devices, 1); + kAudioHardwarePropertyDevices, size, devices, 1); if (err == noErr) { bool found = false; for (int j = 0; j < count; j++) { @@ -139,6 +142,7 @@ OSStatus ChangeListenerProc(AudioObjectID inObjectID, UInt32 inNumberAddresses, invalid = true; } } + free(devices); } break; case kAudioObjectPropertyOwnedObjects: @@ -148,9 +152,12 @@ OSStatus ChangeListenerProc(AudioObjectID inObjectID, UInt32 inNumberAddresses, kAudioObjectPropertyOwnedObjects, &size); if (err == noErr) { int count = size / sizeof(AudioObjectID); - AudioObjectID controlIDs[count]; + AudioObjectID *controlIDs = (AudioObjectID *) malloc(size); + if (!controlIDs) { + break; + } err = GetAudioObjectProperty(mixer->deviceID, kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyOwnedObjects, count * sizeof(AudioObjectID), &controlIDs, 1); + kAudioObjectPropertyOwnedObjects, size, controlIDs, 1); if (err == noErr) { for (PortControl *ctrl = mixer->portControls; ctrl != NULL; ctrl = ctrl->next) { for (int i = 0; i < ctrl->controlCount; i++) { @@ -168,6 +175,7 @@ OSStatus ChangeListenerProc(AudioObjectID inObjectID, UInt32 inNumberAddresses, } } } + free(controlIDs); } } } @@ -229,6 +237,9 @@ INT32 PORT_GetPortMixerDescription(INT32 mixerIndex, PortMixerDescription* mixer void* PORT_Open(INT32 mixerIndex) { TRACE1("\n>>PORT_Open (mixerIndex=%d)\n", (int)mixerIndex); PortMixer *mixer = (PortMixer *)calloc(1, sizeof(PortMixer)); + if (!mixer) { + return nullptr; + } mixer->deviceID = deviceCache.GetDeviceID(mixerIndex); if (mixer->deviceID != 0) { @@ -480,16 +491,20 @@ void PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) { mixer->deviceControlCount = size / sizeof(AudioObjectID); TRACE1(" PORT_GetControls: detected %d owned objects\n", mixer->deviceControlCount); - AudioObjectID controlIDs[mixer->deviceControlCount]; + AudioObjectID *controlIDs = (AudioObjectID *) malloc(size); + if (!controlIDs) { + return; + } err = GetAudioObjectProperty(mixer->deviceID, kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyOwnedObjects, sizeof(controlIDs), controlIDs, 1); + kAudioObjectPropertyOwnedObjects, size, controlIDs, 1); if (err) { OS_ERROR1(err, "PORT_GetControls (portIndex = %d) get OwnedObject values", portIndex); } else { mixer->deviceControls = (AudioControl *)calloc(mixer->deviceControlCount, sizeof(AudioControl)); if (mixer->deviceControls == NULL) { + free(controlIDs); return; } @@ -513,6 +528,7 @@ void PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) { control->controlID, FourCC2Str(control->classID), FourCC2Str(control->scope), control->channel); } } + free(controlIDs); } } @@ -524,10 +540,14 @@ void PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) { int totalChannels = GetChannelCount(mixer->deviceID, port->scope == kAudioDevicePropertyScopeOutput ? 1 : 0); // collect volume and mute controls - AudioControl* volumeControls[totalChannels+1]; // 0 - for master channel - memset(&volumeControls, 0, sizeof(AudioControl *) * (totalChannels+1)); - AudioControl* muteControls[totalChannels+1]; // 0 - for master channel - memset(&muteControls, 0, sizeof(AudioControl *) * (totalChannels+1)); + size_t totalAndMaster = totalChannels + 1; // 0 - for master channel + AudioControl **volumeControls = (AudioControl **) calloc(totalAndMaster, sizeof(AudioControl *)); + AudioControl **muteControls = (AudioControl **) calloc(totalAndMaster, sizeof(AudioControl *)); + if (!volumeControls || !muteControls) { + free(muteControls); + free(volumeControls); + return; + } for (int i=0; ideviceControlCount; i++) { AudioControl *control = &mixer->deviceControls[i]; @@ -626,6 +646,8 @@ void PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) { CFIndex length = CFStringGetLength(cfname) + 1; channelName = (char *)malloc(length); if (channelName == NULL) { + free(muteControls); + free(volumeControls); return; } CFStringGetCString(cfname, channelName, length, kCFStringEncodingUTF8); @@ -633,6 +655,8 @@ void PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) { } else { channelName = (char *)malloc(16); if (channelName == NULL) { + free(muteControls); + free(volumeControls); return; } snprintf(channelName, 16, "Ch %d", ch); @@ -657,7 +681,8 @@ void PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) { } AddChangeListeners(mixer); - + free(muteControls); + free(volumeControls); TRACE1("<controlCount]; + Float32 *subVolumes = (Float32 *) malloc(control->controlCount * sizeof(Float32)); + if (!subVolumes) { + return DEFAULT_VOLUME_VALUE; + } Float32 maxVolume; switch (control->type) { case PortControl::Volume: if (!TestPortControlValidity(control)) { - return DEFAULT_VOLUME_VALUE; + result = DEFAULT_VOLUME_VALUE; + break; } if (!GetPortControlVolumes(control, subVolumes, &maxVolume)) { - return DEFAULT_VOLUME_VALUE; + result = DEFAULT_VOLUME_VALUE; + break; } result = maxVolume; break; case PortControl::Balance: if (!TestPortControlValidity(control)) { - return DEFAULT_BALANCE_VALUE; + result = DEFAULT_BALANCE_VALUE; + break; } // balance control always has 2 volume controls if (!GetPortControlVolumes(control, subVolumes, &maxVolume)) { - return DEFAULT_VOLUME_VALUE; + result = DEFAULT_VOLUME_VALUE; + break; } // calculate balance value if (subVolumes[0] > subVolumes[1]) { @@ -806,9 +838,10 @@ float PORT_GetFloatValue(void* controlIDV) { break; default: ERROR1("GetFloatValue requested for non-Float control (control-type == %d)\n", control->type); - return 0; + result = 0; + break; } - + free(subVolumes); TRACE1("<controlCount]; + Float32 *subVolumes = (Float32 *) malloc(sizeof(Float32) * control->controlCount); + if (!subVolumes) { + return; + } Float32 maxVolume; switch (control->type) { case PortControl::Volume: if (!GetPortControlVolumes(control, subVolumes, &maxVolume)) { - return; + break; } // update the values if (maxVolume > 0.001) { @@ -845,7 +881,7 @@ void PORT_SetFloatValue(void* controlIDV, float value) { case PortControl::Balance: // balance control always has 2 volume controls if (!GetPortControlVolumes(control, subVolumes, &maxVolume)) { - return; + break; } // calculate new values if (value < 0.0f) { @@ -861,8 +897,9 @@ void PORT_SetFloatValue(void* controlIDV, float value) { break; default: ERROR1("PORT_SetFloatValue requested for non-Float control (control-type == %d)\n", control->type); - return; + break; } + free(subVolumes); } #endif // USE_PORTS diff --git a/src/java.desktop/share/classes/com/sun/beans/introspect/MethodInfo.java b/src/java.desktop/share/classes/com/sun/beans/introspect/MethodInfo.java index 25c9598839357..496cbd3cd6b41 100644 --- a/src/java.desktop/share/classes/com/sun/beans/introspect/MethodInfo.java +++ b/src/java.desktop/share/classes/com/sun/beans/introspect/MethodInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,10 +31,11 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Type; +import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.Comparator; +import java.util.Deque; import java.util.List; import java.util.Set; @@ -105,13 +106,16 @@ static List get(Class type) { } } - // Add default methods inherited from interfaces - for (Class iface : type.getInterfaces()) { + // Add methods inherited from interfaces + Deque> ifaceDeque = new ArrayDeque<>(List.of(type.getInterfaces())); + while (!ifaceDeque.isEmpty()) { + Class iface = ifaceDeque.removeLast(); if (IGNORABLE_INTERFACES.contains(iface)) { continue; } + ifaceDeque.addAll(List.of(iface.getInterfaces())); for (Method method : iface.getMethods()) { - if (!Modifier.isAbstract(method.getModifiers())) { + if (!Modifier.isAbstract(method.getModifiers()) && !method.isBridge()) { (list = createIfNeeded(list)).add(method); } } diff --git a/src/java.desktop/share/classes/com/sun/beans/introspect/PropertyInfo.java b/src/java.desktop/share/classes/com/sun/beans/introspect/PropertyInfo.java index 934398ae7bd2b..89929cfab4ead 100644 --- a/src/java.desktop/share/classes/com/sun/beans/introspect/PropertyInfo.java +++ b/src/java.desktop/share/classes/com/sun/beans/introspect/PropertyInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -79,7 +79,8 @@ private boolean initialize() { } if (!isInitedToIsGetter && this.readList != null) { for (MethodInfo info : this.readList) { - if ((this.read == null) || this.read.type.isAssignableFrom(info.type)) { + if ((this.read == null) || (!info.method.isDefault() + && this.read.type.isAssignableFrom(info.type))) { this.read = info; this.type = info.type; } @@ -92,6 +93,9 @@ private boolean initialize() { if (writeType == null) { this.write = info; writeType = info.type; + } else if (isParentOfIncoming(this.write, info)) { + this.write = info; + writeType = info.type; } else if (writeType.isAssignableFrom(info.type)) { if ((this.write == null) || this.write.type.isAssignableFrom(info.type)) { this.write = info; @@ -313,4 +317,16 @@ public static Map get(Class type) { ? Collections.unmodifiableMap(map) : Collections.emptyMap(); } + + private static boolean isParentOfIncoming(MethodInfo current, MethodInfo incoming) { + if (null == current) { + return false; + } + Class currentClass = current.method.getDeclaringClass(); + Class incomingClass = incoming.method.getDeclaringClass(); + if (currentClass == incomingClass) { + return false; + } + return currentClass.isAssignableFrom(incomingClass); + } } diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java index 8cb93eaf727e6..b96a8216a5ca8 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -141,6 +141,7 @@ public class PNGImageReader extends ImageReader { static final int tRNS_TYPE = 0x74524e53; static final int zTXt_TYPE = 0x7a545874; + static final int MAX_INFLATED_TEXT_LENGTH = 262144; static final int PNG_COLOR_GRAY = 0; static final int PNG_COLOR_RGB = 2; static final int PNG_COLOR_PALETTE = 3; @@ -669,7 +670,7 @@ private void parse_tRNS_chunk(int chunkLength) throws IOException { private static byte[] inflate(byte[] b) throws IOException { InputStream bais = new ByteArrayInputStream(b); try (InputStream iis = new InflaterInputStream(bais)) { - return iis.readAllBytes(); + return iis.readNBytes(MAX_INFLATED_TEXT_LENGTH); } } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/SwingUtilities3.java b/src/java.desktop/share/classes/com/sun/java/swing/SwingUtilities3.java index 14423ec1567f6..55f8190cad04a 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/SwingUtilities3.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/SwingUtilities3.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,19 +25,34 @@ package com.sun.java.swing; -import sun.awt.AppContext; -import sun.awt.SunToolkit; - -import java.util.Collections; -import java.util.Map; -import java.util.WeakHashMap; import java.applet.Applet; +import java.awt.Color; import java.awt.Component; import java.awt.Container; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.Rectangle; +import java.awt.Stroke; import java.awt.Window; +import java.awt.geom.AffineTransform; +import java.util.Collections; +import java.util.Map; +import java.util.WeakHashMap; + +import javax.swing.ButtonModel; +import javax.swing.Icon; import javax.swing.JComponent; +import javax.swing.JMenu; import javax.swing.RepaintManager; +import sun.awt.AppContext; +import sun.awt.SunToolkit; +import sun.swing.MenuItemLayoutHelper; +import sun.swing.SwingUtilities2; + +import static sun.java2d.pipe.Region.clipRound; + /** * A collection of utility methods for Swing. *

@@ -135,4 +150,227 @@ public static RepaintManager getDelegateRepaintManager(Component } return delegate; } + + public static void applyInsets(Rectangle rect, Insets insets) { + if (insets != null) { + rect.x += insets.left; + rect.y += insets.top; + rect.width -= (insets.right + rect.x); + rect.height -= (insets.bottom + rect.y); + } + } + + public static void paintCheckIcon(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr, + Color holdc, Color foreground) { + if (lh.getCheckIcon() != null) { + ButtonModel model = lh.getMenuItem().getModel(); + if (model.isArmed() || (lh.getMenuItem() instanceof JMenu + && model.isSelected())) { + g.setColor(foreground); + } else { + g.setColor(holdc); + } + if (lh.useCheckAndArrow()) { + lh.getCheckIcon().paintIcon(lh.getMenuItem(), g, + lr.getCheckRect().x, lr.getCheckRect().y); + } + g.setColor(holdc); + } + } + + public static void paintIcon(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr, Color holdc) { + if (lh.getIcon() != null) { + Icon icon; + ButtonModel model = lh.getMenuItem().getModel(); + if (!model.isEnabled()) { + icon = lh.getMenuItem().getDisabledIcon(); + } else if (model.isPressed() && model.isArmed()) { + icon = lh.getMenuItem().getPressedIcon(); + if (icon == null) { + // Use default icon + icon = lh.getMenuItem().getIcon(); + } + } else { + icon = lh.getMenuItem().getIcon(); + } + + if (icon != null) { + icon.paintIcon(lh.getMenuItem(), g, lr.getIconRect().x, + lr.getIconRect().y); + g.setColor(holdc); + } + } + } + + + public static void paintAccText(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr, + Color disabledForeground, + Color acceleratorSelectionForeground, + Color acceleratorForeground) { + if (!lh.getAccText().isEmpty()) { + ButtonModel model = lh.getMenuItem().getModel(); + g.setFont(lh.getAccFontMetrics().getFont()); + if (!model.isEnabled()) { + + // paint the accText disabled + if (disabledForeground != null) { + g.setColor(disabledForeground); + SwingUtilities2.drawString(lh.getMenuItem(), g, + lh.getAccText(), lr.getAccRect().x, + lr.getAccRect().y + lh.getAccFontMetrics().getAscent()); + } else { + g.setColor(lh.getMenuItem().getBackground().brighter()); + SwingUtilities2.drawString(lh.getMenuItem(), g, + lh.getAccText(), lr.getAccRect().x, + lr.getAccRect().y + lh.getAccFontMetrics().getAscent()); + g.setColor(lh.getMenuItem().getBackground().darker()); + SwingUtilities2.drawString(lh.getMenuItem(), g, + lh.getAccText(), lr.getAccRect().x - 1, + lr.getAccRect().y + lh.getFontMetrics().getAscent() - 1); + } + } else { + + // paint the accText normally + if (model.isArmed() + || (lh.getMenuItem() instanceof JMenu + && model.isSelected())) { + g.setColor(acceleratorSelectionForeground); + } else { + g.setColor(acceleratorForeground); + } + SwingUtilities2.drawString(lh.getMenuItem(), g, lh.getAccText(), + lr.getAccRect().x, lr.getAccRect().y + + lh.getAccFontMetrics().getAscent()); + } + } + } + + public static void paintArrowIcon(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr, + Color foreground) { + if (lh.getArrowIcon() != null) { + ButtonModel model = lh.getMenuItem().getModel(); + if (model.isArmed() || (lh.getMenuItem() instanceof JMenu + && model.isSelected())) { + g.setColor(foreground); + } + if (lh.useCheckAndArrow()) { + lh.getArrowIcon().paintIcon(lh.getMenuItem(), g, + lr.getArrowRect().x, lr.getArrowRect().y); + } + } + } + + /** + * A task which paints an unscaled border after {@code Graphics} + * transforms are removed. It's used with the + * {@link #paintBorder(Component, Graphics, int, int, int, int, UnscaledBorderPainter) + * SwingUtilities3.paintBorder} which manages changing the transforms and calculating + * the coordinates and size of the border. + */ + @FunctionalInterface + public interface UnscaledBorderPainter { + /** + * Paints the border for the specified component after the + * {@code Graphics} transforms are removed. + * + *

+ * The x and y of the painted border are zero. + * + * @param c the component for which this border is being painted + * @param g the paint graphics + * @param w the width of the painted border, in physical pixels + * @param h the height of the painted border, in physical pixels + * @param scaleFactor the scale that was in the {@code Graphics} + * + * @see #paintBorder(Component, Graphics, int, int, int, int, UnscaledBorderPainter) + * SwingUtilities3.paintBorder + * @see javax.swing.border.Border#paintBorder(Component, Graphics, int, int, int, int) + * Border.paintBorder + */ + void paintUnscaledBorder(Component c, Graphics g, + int w, int h, + double scaleFactor); + } + + /** + * Paints the border for a component ensuring its sides have consistent + * thickness at different scales. + *

+ * It performs the following steps: + *

    + *
  1. Reset the scale transform on the {@code Graphics},
  2. + *
  3. Call {@code painter} to paint the border,
  4. + *
  5. Restores the transform.
  6. + *
+ * + * @param c the component for which this border is being painted + * @param g the paint graphics + * @param x the x position of the painted border + * @param y the y position of the painted border + * @param w the width of the painted border + * @param h the height of the painted border + * @param painter the painter object which paints the border after + * the transform on the {@code Graphics} is reset + */ + public static void paintBorder(Component c, Graphics g, + int x, int y, + int w, int h, + UnscaledBorderPainter painter) { + + // Step 1: Reset Transform + AffineTransform at = null; + Stroke oldStroke = null; + boolean resetTransform = false; + double scaleFactor = 1; + + int xtranslation = x; + int ytranslation = y; + int width = w; + int height = h; + + if (g instanceof Graphics2D) { + Graphics2D g2d = (Graphics2D) g; + at = g2d.getTransform(); + oldStroke = g2d.getStroke(); + scaleFactor = Math.min(at.getScaleX(), at.getScaleY()); + + // if m01 or m10 is non-zero, then there is a rotation or shear, + // or if scale=1, skip resetting the transform in these cases. + resetTransform = ((at.getShearX() == 0) && (at.getShearY() == 0)) + && ((at.getScaleX() > 1) || (at.getScaleY() > 1)); + + if (resetTransform) { + /* Deactivate the HiDPI scaling transform, + * so we can do paint operations in the device + * pixel coordinate system instead of the logical coordinate system. + */ + g2d.setTransform(new AffineTransform()); + double xx = at.getScaleX() * x + at.getTranslateX(); + double yy = at.getScaleY() * y + at.getTranslateY(); + xtranslation = clipRound(xx); + ytranslation = clipRound(yy); + width = clipRound(at.getScaleX() * w + xx) - xtranslation; + height = clipRound(at.getScaleY() * h + yy) - ytranslation; + } + } + + g.translate(xtranslation, ytranslation); + + // Step 2: Call respective paintBorder with transformed values + painter.paintUnscaledBorder(c, g, width, height, scaleFactor); + + // Step 3: Restore previous stroke & transform + g.translate(-xtranslation, -ytranslation); + if (g instanceof Graphics2D) { + Graphics2D g2d = (Graphics2D) g; + g2d.setStroke(oldStroke); + if (resetTransform) { + g2d.setTransform(at); + } + } + } } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKFileChooserUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKFileChooserUI.java index 4a0bbed71c677..148810a3a0836 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKFileChooserUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKFileChooserUI.java @@ -1082,6 +1082,9 @@ public Component getListCellRendererComponent(JList list, Object value, int i if (showFileIcons) { setIcon(getFileChooser().getIcon((File)value)); } + + putClientProperty("html.disable", getFileChooser().getClientProperty("html.disable")); + return this; } } @@ -1099,6 +1102,9 @@ public Component getListCellRendererComponent(JList list, Object value, int i } else { setText(getFileChooser().getName((File)value) + "/"); } + + putClientProperty("html.disable", getFileChooser().getClientProperty("html.disable")); + return this; } } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java index 40d713123c734..62c7a07d047b8 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java @@ -577,6 +577,7 @@ public Object createValue(UIDefaults table) { }), "ComboBox.font", new FontLazyValue(Region.COMBO_BOX), "ComboBox.isEnterSelectablePopup", Boolean.TRUE, + "ComboBox.squareButton", Boolean.FALSE, "EditorPane.caretForeground", caretColor, diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifFileChooserUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifFileChooserUI.java index fcb9e724febc6..5a0f44ffd4e9e 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifFileChooserUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifFileChooserUI.java @@ -664,6 +664,9 @@ public Component getListCellRendererComponent(JList list, Object value, int i super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); setText(getFileChooser().getName((File) value)); setInheritsPopupMenu(true); + + putClientProperty("html.disable", getFileChooser().getClientProperty("html.disable")); + return this; } } @@ -676,6 +679,9 @@ public Component getListCellRendererComponent(JList list, Object value, int i super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); setText(getFileChooser().getName((File) value)); setInheritsPopupMenu(true); + + putClientProperty("html.disable", getFileChooser().getClientProperty("html.disable")); + return this; } } diff --git a/src/java.desktop/share/classes/com/sun/media/sound/AudioFileSoundbankReader.java b/src/java.desktop/share/classes/com/sun/media/sound/AudioFileSoundbankReader.java index 1330e48aae51d..60d52d2b3052e 100644 --- a/src/java.desktop/share/classes/com/sun/media/sound/AudioFileSoundbankReader.java +++ b/src/java.desktop/share/classes/com/sun/media/sound/AudioFileSoundbankReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -93,6 +93,13 @@ public Soundbank getSoundbank(AudioInputStream ais) "Can not allocate enough memory to read audio data."); } + long maximumHeapSize = (long) ((Runtime.getRuntime().maxMemory() - + (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())) * 0.9); + if (totalSize > maximumHeapSize) { + throw new InvalidMidiDataException( + "Insufficient heap size to render audio data."); + } + if (ais.getFrameLength() == -1 || totalSize > MEGABYTE) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buff = new byte[DEFAULT_BUFFER_SIZE - (DEFAULT_BUFFER_SIZE % frameSize)]; diff --git a/src/java.desktop/share/classes/java/awt/AWTEventMulticaster.java b/src/java.desktop/share/classes/java/awt/AWTEventMulticaster.java index a9f102643f4b9..7887389f16809 100644 --- a/src/java.desktop/share/classes/java/awt/AWTEventMulticaster.java +++ b/src/java.desktop/share/classes/java/awt/AWTEventMulticaster.java @@ -110,6 +110,8 @@ public class AWTEventMulticaster implements TextListener, InputMethodListener, HierarchyListener, HierarchyBoundsListener, MouseWheelListener { + private static final int MAX_UNBALANCED_TOP_NODES = 100; + /** * A variable in the event chain (listener-a) */ @@ -954,6 +956,7 @@ public static MouseWheelListener remove(MouseWheelListener l, * If listener-b is null, it returns listener-a * If neither are null, then it creates and returns * a new AWTEventMulticaster instance which chains a with b. + * * @param a event listener-a * @param b event listener-b * @return the resulting listener @@ -961,7 +964,64 @@ public static MouseWheelListener remove(MouseWheelListener l, protected static EventListener addInternal(EventListener a, EventListener b) { if (a == null) return b; if (b == null) return a; - return new AWTEventMulticaster(a, b); + AWTEventMulticaster n = new AWTEventMulticaster(a, b); + if (!needsRebalance(n)) { + return n; + } + + EventListener[] array = getListeners(n, EventListener.class); + return rebalance(array, 0, array.length - 1); + } + + /** + * Return true if the argument represents a binary tree that needs to be rebalanced. + */ + private static boolean needsRebalance(AWTEventMulticaster l) { + int level = 0; + while (true) { + // The criteria for when we need a rebalance is subjective. This method checks + // up to a given threshold of the topmost nodes of a AWTEventMulticaster. If + // they all include one leaf node, then this method returns true. This criteria + // will be met after several consecutive iterations of `addInternal(a, b)` + if (++level > MAX_UNBALANCED_TOP_NODES) { + return true; + } + if (l.a instanceof AWTEventMulticaster aMulti) { + if (l.b instanceof AWTEventMulticaster) { + // we reached a node where both children are AWTEventMulticaster: let's assume + // the current node marks the start of a well-balanced subtree + return false; + } + l = aMulti; + } else if (l.b instanceof AWTEventMulticaster bMulti) { + l = bMulti; + } else { + return false; + } + } + } + + /** + * Recursively create a balanced tree that includes a given range of EventListeners. + * + * @param array the array of the EventListeners to consult + * @param index0 the lowest index (inclusive) that the return value must include + * @param index1 the highest index (inclusive) that the return value must include. + * + * @return a balanced tree. If index0 equals index1 then this returns an EventListener from + * the array provided. Otherwise this returns an AWTEventMulticaster. + */ + private static EventListener rebalance(EventListener[] array, int index0, int index1) { + if (index0 == index1) { + return array[index0]; + } + if (index0 == index1 - 1) { + return new AWTEventMulticaster(array[index0], array[index1]); + } + int mid = (index0 + index1) / 2; + return new AWTEventMulticaster( + rebalance(array, index0, mid), + rebalance(array, mid + 1, index1)); } /** diff --git a/src/java.desktop/share/classes/java/awt/color/ICC_ColorSpace.java b/src/java.desktop/share/classes/java/awt/color/ICC_ColorSpace.java index 633ebabd018c0..3bc138d2e7556 100644 --- a/src/java.desktop/share/classes/java/awt/color/ICC_ColorSpace.java +++ b/src/java.desktop/share/classes/java/awt/color/ICC_ColorSpace.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -140,10 +140,11 @@ public ICC_ColorSpace(ICC_Profile profile) { if (profileClass != ICC_Profile.CLASS_INPUT && profileClass != ICC_Profile.CLASS_DISPLAY && profileClass != ICC_Profile.CLASS_OUTPUT + && profileClass != ICC_Profile.CLASS_DEVICELINK && profileClass != ICC_Profile.CLASS_COLORSPACECONVERSION && profileClass != ICC_Profile.CLASS_NAMEDCOLOR && profileClass != ICC_Profile.CLASS_ABSTRACT) { - throw new IllegalArgumentException("Invalid profile type"); + throw new IllegalArgumentException("Invalid profile class"); } thisProfile = profile; diff --git a/src/java.desktop/share/classes/java/awt/color/ICC_Profile.java b/src/java.desktop/share/classes/java/awt/color/ICC_Profile.java index da14d24892680..dac498cb0be5e 100644 --- a/src/java.desktop/share/classes/java/awt/color/ICC_Profile.java +++ b/src/java.desktop/share/classes/java/awt/color/ICC_Profile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -751,6 +751,7 @@ private interface BuiltInProfile { */ public static final int icXYZNumberX = 8; + private static final int HEADER_SIZE = 128; /** * Constructs an {@code ICC_Profile} object with a given ID. @@ -799,10 +800,15 @@ public static ICC_Profile getInstance(byte[] data) { ProfileDataVerifier.verify(data); Profile p; try { + byte[] theHeader = new byte[HEADER_SIZE]; + System.arraycopy(data, 0, theHeader, 0, HEADER_SIZE); + verifyHeader(theHeader); + p = CMSManager.getModule().loadProfile(data); } catch (CMMException c) { throw new IllegalArgumentException("Invalid ICC Profile Data"); } + try { if (getColorSpaceType(p) == ColorSpace.TYPE_GRAY && getData(p, icSigMediaWhitePointTag) != null @@ -991,6 +997,10 @@ public int getProfileClass() { return info.profileClass; } byte[] theHeader = getData(icSigHead); + return getProfileClass(theHeader); + } + + private static int getProfileClass(byte[] theHeader) { int theClassSig = intFromBigEndian(theHeader, icHdrDeviceClass); return switch (theClassSig) { case icSigInputClass -> CLASS_INPUT; @@ -1032,6 +1042,11 @@ private static int getColorSpaceType(Profile p) { return iccCStoJCS(theColorSpaceSig); } + private static int getColorSpaceType(byte[] theHeader) { + int theColorSpaceSig = intFromBigEndian(theHeader, icHdrColorSpace); + return iccCStoJCS(theColorSpaceSig); + } + /** * Returns the color space type of the Profile Connection Space (PCS). * Returns one of the color space type constants defined by the ColorSpace @@ -1051,6 +1066,21 @@ public int getPCSType() { return iccCStoJCS(thePCSSig); } + private static int getPCSType(byte[] theHeader) { + int thePCSSig = intFromBigEndian(theHeader, icHdrPcs); + int theDeviceClass = intFromBigEndian(theHeader, icHdrDeviceClass); + + if (theDeviceClass == icSigLinkClass) { + return iccCStoJCS(thePCSSig); + } else { + return switch (thePCSSig) { + case icSigXYZData -> ColorSpace.TYPE_XYZ; + case icSigLabData -> ColorSpace.TYPE_Lab; + default -> throw new IllegalArgumentException("Unexpected PCS type"); + }; + } + } + /** * Write this {@code ICC_Profile} to a file. * @@ -1129,9 +1159,42 @@ private static byte[] getData(Profile p, int tagSignature) { * @see #getData */ public void setData(int tagSignature, byte[] tagData) { + if (tagSignature == ICC_Profile.icSigHead) { + verifyHeader(tagData); + } CMSManager.getModule().setTagData(cmmProfile(), tagSignature, tagData); } + private static void verifyHeader(byte[] data) { + if (data == null || data.length < HEADER_SIZE) { + throw new IllegalArgumentException("Invalid header data"); + } + getProfileClass(data); + getColorSpaceType(data); + getPCSType(data); + checkRenderingIntent(data); + } + + private static boolean checkRenderingIntent(byte[] header) { + int index = ICC_Profile.icHdrRenderingIntent; + + /* According to ICC spec, only the least-significant 16 bits shall be + * used to encode the rendering intent. The most significant 16 bits + * shall be set to zero. Thus, we are ignoring two most significant + * bytes here. Please refer ICC Spec Document for more details. + */ + int renderingIntent = ((header[index+2] & 0xff) << 8) | + (header[index+3] & 0xff); + + switch (renderingIntent) { + case icPerceptual, icMediaRelativeColorimetric, + icSaturation, icAbsoluteColorimetric -> { + return true; + } + default -> throw new IllegalArgumentException("Unknown Rendering Intent"); + } + } + /** * Returns the number of color components in the "input" color space of this * profile. For example if the color space type of this profile is diff --git a/src/java.desktop/share/classes/java/awt/image/ColorConvertOp.java b/src/java.desktop/share/classes/java/awt/image/ColorConvertOp.java index 023b376d73d80..34ec923102983 100644 --- a/src/java.desktop/share/classes/java/awt/image/ColorConvertOp.java +++ b/src/java.desktop/share/classes/java/awt/image/ColorConvertOp.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -622,7 +622,7 @@ public BufferedImage createCompatibleDestImage (BufferedImage src, "Destination ColorSpace is undefined"); } ICC_Profile destProfile = profileList[nProfiles - 1]; - cs = new ICC_ColorSpace(destProfile); + cs = createCompatibleColorSpace(destProfile); } else { /* non-ICC case */ int nSpaces = CSList.length; @@ -632,6 +632,25 @@ public BufferedImage createCompatibleDestImage (BufferedImage src, return createCompatibleDestImage(src, destCM, cs); } + private static ColorSpace createCompatibleColorSpace(ICC_Profile profile) { + if (profile == ICC_Profile.getInstance(ColorSpace.CS_sRGB)) { + return ColorSpace.getInstance(ColorSpace.CS_sRGB); + } + if (profile == ICC_Profile.getInstance(ColorSpace.CS_LINEAR_RGB)) { + return ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB); + } + if (profile == ICC_Profile.getInstance(ColorSpace.CS_CIEXYZ)) { + return ColorSpace.getInstance(ColorSpace.CS_CIEXYZ); + } + if (profile == ICC_Profile.getInstance(ColorSpace.CS_PYCC)) { + return ColorSpace.getInstance(ColorSpace.CS_PYCC); + } + if (profile == ICC_Profile.getInstance(ColorSpace.CS_GRAY)) { + return ColorSpace.getInstance(ColorSpace.CS_GRAY); + } + return new ICC_ColorSpace(profile); + } + private BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel destCM, ColorSpace destCS) { @@ -811,7 +830,7 @@ private BufferedImage nonICCBIFilter(BufferedImage src, } float[] srcMinVal = new float[iccSrcNumComp]; float[] srcInvDiffMinMax = new float[iccSrcNumComp]; - for (int i = 0; i < srcNumComp; i++) { + for (int i = 0; i < iccSrcNumComp; i++) { srcMinVal[i] = cs.getMinValue(i); srcInvDiffMinMax[i] = maxNum / (cs.getMaxValue(i) - srcMinVal[i]); } @@ -825,7 +844,7 @@ private BufferedImage nonICCBIFilter(BufferedImage src, } float[] dstMinVal = new float[iccDstNumComp]; float[] dstDiffMinMax = new float[iccDstNumComp]; - for (int i = 0; i < dstNumComp; i++) { + for (int i = 0; i < iccDstNumComp; i++) { dstMinVal[i] = cs.getMinValue(i); dstDiffMinMax[i] = (cs.getMaxValue(i) - dstMinVal[i]) / maxNum; } @@ -878,7 +897,7 @@ private BufferedImage nonICCBIFilter(BufferedImage src, dstDiffMinMax[i] + dstMinVal[i]; } if (nonICCDst) { - color = srcColorSpace.fromCIEXYZ(dstColor); + color = dstColorSpace.fromCIEXYZ(dstColor); for (int i = 0; i < dstNumComp; i++) { dstColor[i] = color[i]; } diff --git a/src/java.desktop/share/classes/java/beans/Introspector.java b/src/java.desktop/share/classes/java/beans/Introspector.java index c3a0dd92e95d8..6f102f0bb613e 100644 --- a/src/java.desktop/share/classes/java/beans/Introspector.java +++ b/src/java.desktop/share/classes/java/beans/Introspector.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1068,8 +1068,12 @@ private void addMethod(MethodDescriptor md) { } } if (match) { - MethodDescriptor composite = new MethodDescriptor(old, md); - methods.put(name, composite); + Class oldClass = old.getMethod().getDeclaringClass(); + Class mdClass = md.getMethod().getDeclaringClass(); + if (oldClass == mdClass || oldClass.isAssignableFrom(mdClass) || !mdClass.isAssignableFrom(oldClass)) { + MethodDescriptor composite = new MethodDescriptor(old, md); + methods.put(name, composite); + } return; } diff --git a/src/java.desktop/share/classes/javax/swing/JColorChooser.java b/src/java.desktop/share/classes/javax/swing/JColorChooser.java index 29bf7009b4ffc..f1f753833f6c9 100644 --- a/src/java.desktop/share/classes/javax/swing/JColorChooser.java +++ b/src/java.desktop/share/classes/javax/swing/JColorChooser.java @@ -175,7 +175,9 @@ public static Color showDialog(Component component, * @return the selected color or null if the user opted out * @exception HeadlessException if GraphicsEnvironment.isHeadless() * returns true. + * * @see java.awt.GraphicsEnvironment#isHeadless + * @since 9 */ @SuppressWarnings("deprecation") public static Color showDialog(Component component, String title, diff --git a/src/java.desktop/share/classes/javax/swing/JFileChooser.java b/src/java.desktop/share/classes/javax/swing/JFileChooser.java index a0d2d882879a8..bf220463cbca0 100644 --- a/src/java.desktop/share/classes/javax/swing/JFileChooser.java +++ b/src/java.desktop/share/classes/javax/swing/JFileChooser.java @@ -398,6 +398,7 @@ protected void setup(FileSystemView view) { setFileFilter(getAcceptAllFileFilter()); } enableEvents(AWTEvent.MOUSE_EVENT_MASK); + putClientProperty("html.disable", true); } private void installHierarchyListener() { diff --git a/src/java.desktop/share/classes/javax/swing/JViewport.java b/src/java.desktop/share/classes/javax/swing/JViewport.java index e3974a84f2776..98be2876ac438 100644 --- a/src/java.desktop/share/classes/javax/swing/JViewport.java +++ b/src/java.desktop/share/classes/javax/swing/JViewport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/java.desktop/share/classes/javax/swing/SwingUtilities.java b/src/java.desktop/share/classes/javax/swing/SwingUtilities.java index 7a8aa2e316e6e..63d78a7d0b26c 100644 --- a/src/java.desktop/share/classes/javax/swing/SwingUtilities.java +++ b/src/java.desktop/share/classes/javax/swing/SwingUtilities.java @@ -2073,6 +2073,10 @@ static int findDisplayedMnemonicIndex(String text, int mnemonic) { return -1; } + if (mnemonic >= 'a' && mnemonic <= 'z') { + return -1; + } + char uc = Character.toUpperCase((char)mnemonic); char lc = Character.toLowerCase((char)mnemonic); diff --git a/src/java.desktop/share/classes/javax/swing/TablePrintable.java b/src/java.desktop/share/classes/javax/swing/TablePrintable.java index 4c8eb0529abd0..169e3355ae553 100644 --- a/src/java.desktop/share/classes/javax/swing/TablePrintable.java +++ b/src/java.desktop/share/classes/javax/swing/TablePrintable.java @@ -282,6 +282,9 @@ public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) // dictated by the previous two assertions assert sf > 0; + Rectangle bounds = table.getBounds(); + bounds.x = bounds.y = 0; + // This is in a loop for two reasons: // First, it allows us to catch up in case we're called starting // with a non-zero pageIndex. Second, we know that we can be called @@ -303,9 +306,10 @@ public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) // calculate the area of the table to be printed for this page findNextClip(scaledWidth, scaledHeight); - if (!((table.getBounds()).intersects(clip))) { + if (!(bounds.intersects(clip))) { return NO_SUCH_PAGE; } + last++; } @@ -395,11 +399,12 @@ public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) // draw a box around the table g2d.setColor(Color.BLACK); + // compute the visible portion of table and draw the rect around it - Rectangle visibleBounds = clip.intersection(table.getBounds()); + Rectangle visibleBounds = clip.intersection(bounds); Point upperLeft = visibleBounds.getLocation(); - Point lowerRight = new Point(visibleBounds.x + visibleBounds.width, - visibleBounds.y + visibleBounds.height); + Point lowerRight = new Point(visibleBounds.x + visibleBounds.width - 1, + visibleBounds.y + visibleBounds.height - 1); int rMin = table.rowAtPoint(upperLeft); int rMax = table.rowAtPoint(lowerRight); @@ -410,7 +415,7 @@ public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) rMax = table.getRowCount(); } int rowHeight = 0; - for(int visrow = rMin; visrow < rMax; visrow++) { + for(int visrow = rMin; visrow <= rMax; visrow++) { rowHeight += table.getRowHeight(visrow); } // If PrintMode is FIT_WIDTH, then draw rect for entire column width while diff --git a/src/java.desktop/share/classes/javax/swing/border/EtchedBorder.java b/src/java.desktop/share/classes/javax/swing/border/EtchedBorder.java index 8d83ac984de0b..4a9c1f260752e 100644 --- a/src/java.desktop/share/classes/javax/swing/border/EtchedBorder.java +++ b/src/java.desktop/share/classes/javax/swing/border/EtchedBorder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,10 +30,10 @@ import java.awt.Insets; import java.awt.Color; import java.awt.Component; -import java.awt.Stroke; -import java.awt.geom.AffineTransform; import java.beans.ConstructorProperties; +import com.sun.java.swing.SwingUtilities3; + /** * A class which implements a simple etched border which can * either be etched-in or etched-out. If no highlight/shadow @@ -150,59 +150,26 @@ private void paintBorderShadow(Graphics g, Color c, int w, int h, int stkWidth) * @param height the height of the painted border */ public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { - // We remove any initial transforms to prevent rounding errors - // when drawing in non-integer scales - AffineTransform at = null; - Stroke oldStk = null; - int stkWidth = 1; - boolean resetTransform = false; - if (g instanceof Graphics2D) { - Graphics2D g2d = (Graphics2D) g; - at = g2d.getTransform(); - oldStk = g2d.getStroke(); - // if m01 or m10 is non-zero, then there is a rotation or shear - // skip resetting the transform - resetTransform = (at.getShearX() == 0) && (at.getShearY() == 0); - if (resetTransform) { - g2d.setTransform(new AffineTransform()); - stkWidth = (int) Math.floor(Math.min(at.getScaleX(), at.getScaleY())); - g2d.setStroke(new BasicStroke((float) stkWidth)); - } - } + SwingUtilities3.paintBorder(c, g, + x, y, + width, height, + this::paintUnscaledBorder); + } - int w; - int h; - int xtranslation; - int ytranslation; - if (resetTransform) { - w = (int) Math.floor(at.getScaleX() * width - 1); - h = (int) Math.floor(at.getScaleY() * height - 1); - xtranslation = (int) Math.ceil(at.getScaleX()*x+at.getTranslateX()); - ytranslation = (int) Math.ceil(at.getScaleY()*y+at.getTranslateY()); - } else { - w = width; - h = height; - xtranslation = x; - ytranslation = y; + private void paintUnscaledBorder(Component c, Graphics g, + int w, int h, + double scaleFactor) { + int stkWidth = (int) Math.floor(scaleFactor); + if (g instanceof Graphics2D) { + ((Graphics2D) g).setStroke(new BasicStroke((float) stkWidth)); } - g.translate(xtranslation, ytranslation); - paintBorderShadow(g, (etchType == LOWERED) ? getHighlightColor(c) : getShadowColor(c), w, h, stkWidth); paintBorderHighlight(g, (etchType == LOWERED) ? getShadowColor(c) : getHighlightColor(c), w, h, stkWidth); - - g.translate(-xtranslation, -ytranslation); - - // Set the transform we removed earlier - if (resetTransform) { - Graphics2D g2d = (Graphics2D) g; - g2d.setTransform(at); - g2d.setStroke(oldStk); - } } /** diff --git a/src/java.desktop/share/classes/javax/swing/border/LineBorder.java b/src/java.desktop/share/classes/javax/swing/border/LineBorder.java index 8b4dee4eb8f99..058775130a00e 100644 --- a/src/java.desktop/share/classes/javax/swing/border/LineBorder.java +++ b/src/java.desktop/share/classes/javax/swing/border/LineBorder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,7 +34,8 @@ import java.awt.geom.Rectangle2D; import java.awt.geom.RoundRectangle2D; import java.beans.ConstructorProperties; -import java.awt.geom.AffineTransform; + +import com.sun.java.swing.SwingUtilities3; import static sun.java2d.pipe.Region.clipRound; @@ -144,73 +145,41 @@ public LineBorder(Color color, int thickness, boolean roundedCorners) { * @param height the height of the painted border */ public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { + SwingUtilities3.paintBorder(c, g, + x, y, + width, height, + this::paintUnscaledBorder); + } + + private void paintUnscaledBorder(Component c, Graphics g, + int w, int h, + double scaleFactor) { if ((this.thickness > 0) && (g instanceof Graphics2D)) { Graphics2D g2d = (Graphics2D) g; - AffineTransform at = g2d.getTransform(); - - // if m01 or m10 is non-zero, then there is a rotation or shear - // or if no Scaling enabled, - // skip resetting the transform - boolean resetTransform = ((at.getShearX() == 0) && (at.getShearY() == 0)) && - ((at.getScaleX() > 1) || (at.getScaleY() > 1)); - - int xtranslation; - int ytranslation; - int w; - int h; - int offs; - - if (resetTransform) { - /* Deactivate the HiDPI scaling transform, - * so we can do paint operations in the device - * pixel coordinate system instead of the logical coordinate system. - */ - g2d.setTransform(new AffineTransform()); - double xx = at.getScaleX() * x + at.getTranslateX(); - double yy = at.getScaleY() * y + at.getTranslateY(); - xtranslation = clipRound(xx); - ytranslation = clipRound(yy); - w = clipRound(at.getScaleX() * width + xx) - xtranslation; - h = clipRound(at.getScaleY() * height + yy) - ytranslation; - offs = this.thickness * (int) at.getScaleX(); - } else { - w = width; - h = height; - xtranslation = x; - ytranslation = y; - offs = this.thickness; - } - - g2d.translate(xtranslation, ytranslation); - Color oldColor = g2d.getColor(); g2d.setColor(this.lineColor); Shape outer; Shape inner; + int offs = clipRound(this.thickness * scaleFactor); int size = offs + offs; if (this.roundedCorners) { float arc = .2f * offs; outer = new RoundRectangle2D.Float(0, 0, w, h, offs, offs); inner = new RoundRectangle2D.Float(offs, offs, w - size, h - size, arc, arc); - } - else { + } else { outer = new Rectangle2D.Float(0, 0, w, h); inner = new Rectangle2D.Float(offs, offs, w - size, h - size); } + Path2D path = new Path2D.Float(Path2D.WIND_EVEN_ODD); path.append(outer, false); path.append(inner, false); g2d.fill(path); - g2d.setColor(oldColor); - - g2d.translate(-xtranslation, -ytranslation); - if (resetTransform) { - g2d.setTransform(at); - } + g2d.setColor(oldColor); } } diff --git a/src/java.desktop/share/classes/javax/swing/border/TitledBorder.java b/src/java.desktop/share/classes/javax/swing/border/TitledBorder.java index e1f94783e2ad7..b7a10d889dde0 100644 --- a/src/java.desktop/share/classes/javax/swing/border/TitledBorder.java +++ b/src/java.desktop/share/classes/javax/swing/border/TitledBorder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -736,6 +736,10 @@ private Color getColor(Component c) { } private JLabel getLabel(Component c) { + if (c instanceof JComponent comp) { + this.label.putClientProperty("html.disable", + comp.getClientProperty("html.disable")); + } this.label.setText(getTitle()); this.label.setFont(getFont(c)); this.label.setForeground(getColor(c)); diff --git a/src/java.desktop/share/classes/javax/swing/colorchooser/AbstractColorChooserPanel.java b/src/java.desktop/share/classes/javax/swing/colorchooser/AbstractColorChooserPanel.java index 2f2163c89397b..7fa56fb07f902 100644 --- a/src/java.desktop/share/classes/javax/swing/colorchooser/AbstractColorChooserPanel.java +++ b/src/java.desktop/share/classes/javax/swing/colorchooser/AbstractColorChooserPanel.java @@ -229,6 +229,7 @@ void setSelectedColor(Color color) { * * @param b true if the transparency of a color can be selected * @see #isColorTransparencySelectionEnabled() + * @since 9 */ @BeanProperty(description = "Sets the transparency of a color selection on or off.") @@ -241,6 +242,7 @@ public void setColorTransparencySelectionEnabled(boolean b){ * * @return true if the transparency of a color can be selected * @see #setColorTransparencySelectionEnabled(boolean) + * @since 9 */ public boolean isColorTransparencySelectionEnabled(){ return true; diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java index e2103468ab81a..231a8cbefee92 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,17 +25,52 @@ package javax.swing.plaf.basic; -import java.awt.*; -import java.awt.event.*; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import javax.swing.*; -import javax.swing.event.*; -import javax.swing.plaf.*; +import javax.swing.ButtonModel; +import javax.swing.Icon; +import javax.swing.InputMap; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JComponent; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JRadioButtonMenuItem; +import javax.swing.KeyStroke; +import javax.swing.LookAndFeel; +import javax.swing.MenuElement; +import javax.swing.MenuSelectionManager; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.event.MenuDragMouseEvent; +import javax.swing.event.MenuDragMouseListener; +import javax.swing.event.MenuKeyListener; + +import javax.swing.event.MouseInputListener; +import javax.swing.plaf.ComponentInputMapUIResource; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.MenuItemUI; +import javax.swing.plaf.UIResource; import javax.swing.text.View; -import sun.swing.*; +import com.sun.java.swing.SwingUtilities3; +import sun.swing.MenuItemCheckIconFactory; +import sun.swing.MenuItemLayoutHelper; +import sun.swing.SwingUtilities2; +import sun.swing.UIAction; + /** * BasicMenuItem implementation @@ -669,84 +704,21 @@ protected void paintMenuItem(Graphics g, JComponent c, private void paintIcon(Graphics g, MenuItemLayoutHelper lh, MenuItemLayoutHelper.LayoutResult lr, Color holdc) { - if (lh.getIcon() != null) { - Icon icon; - ButtonModel model = lh.getMenuItem().getModel(); - if (!model.isEnabled()) { - icon = lh.getMenuItem().getDisabledIcon(); - } else if (model.isPressed() && model.isArmed()) { - icon = lh.getMenuItem().getPressedIcon(); - if (icon == null) { - // Use default icon - icon = lh.getMenuItem().getIcon(); - } - } else { - icon = lh.getMenuItem().getIcon(); - } - - if (icon != null) { - icon.paintIcon(lh.getMenuItem(), g, lr.getIconRect().x, - lr.getIconRect().y); - g.setColor(holdc); - } - } + SwingUtilities3.paintIcon(g, lh, lr, holdc); } private void paintCheckIcon(Graphics g, MenuItemLayoutHelper lh, MenuItemLayoutHelper.LayoutResult lr, Color holdc, Color foreground) { - if (lh.getCheckIcon() != null) { - ButtonModel model = lh.getMenuItem().getModel(); - if (model.isArmed() || (lh.getMenuItem() instanceof JMenu - && model.isSelected())) { - g.setColor(foreground); - } else { - g.setColor(holdc); - } - if (lh.useCheckAndArrow()) { - lh.getCheckIcon().paintIcon(lh.getMenuItem(), g, - lr.getCheckRect().x, lr.getCheckRect().y); - } - g.setColor(holdc); - } + SwingUtilities3.paintCheckIcon(g, lh, lr, holdc, foreground); } private void paintAccText(Graphics g, MenuItemLayoutHelper lh, MenuItemLayoutHelper.LayoutResult lr) { - if (!lh.getAccText().isEmpty()) { - ButtonModel model = lh.getMenuItem().getModel(); - g.setFont(lh.getAccFontMetrics().getFont()); - if (!model.isEnabled()) { - // *** paint the accText disabled - if (disabledForeground != null) { - g.setColor(disabledForeground); - SwingUtilities2.drawString(lh.getMenuItem(), g, - lh.getAccText(), lr.getAccRect().x, - lr.getAccRect().y + lh.getAccFontMetrics().getAscent()); - } else { - g.setColor(lh.getMenuItem().getBackground().brighter()); - SwingUtilities2.drawString(lh.getMenuItem(), g, - lh.getAccText(), lr.getAccRect().x, - lr.getAccRect().y + lh.getAccFontMetrics().getAscent()); - g.setColor(lh.getMenuItem().getBackground().darker()); - SwingUtilities2.drawString(lh.getMenuItem(), g, - lh.getAccText(), lr.getAccRect().x - 1, - lr.getAccRect().y + lh.getFontMetrics().getAscent() - 1); - } - } else { - // *** paint the accText normally - if (model.isArmed() - || (lh.getMenuItem() instanceof JMenu - && model.isSelected())) { - g.setColor(acceleratorSelectionForeground); - } else { - g.setColor(acceleratorForeground); - } - SwingUtilities2.drawString(lh.getMenuItem(), g, lh.getAccText(), - lr.getAccRect().x, lr.getAccRect().y + - lh.getAccFontMetrics().getAscent()); - } - } + SwingUtilities3.paintAccText(g, lh, lr, + disabledForeground, + acceleratorSelectionForeground, + acceleratorForeground); } private void paintText(Graphics g, MenuItemLayoutHelper lh, @@ -765,26 +737,11 @@ private void paintText(Graphics g, MenuItemLayoutHelper lh, private void paintArrowIcon(Graphics g, MenuItemLayoutHelper lh, MenuItemLayoutHelper.LayoutResult lr, Color foreground) { - if (lh.getArrowIcon() != null) { - ButtonModel model = lh.getMenuItem().getModel(); - if (model.isArmed() || (lh.getMenuItem() instanceof JMenu - && model.isSelected())) { - g.setColor(foreground); - } - if (lh.useCheckAndArrow()) { - lh.getArrowIcon().paintIcon(lh.getMenuItem(), g, - lr.getArrowRect().x, lr.getArrowRect().y); - } - } + SwingUtilities3.paintArrowIcon(g, lh, lr, foreground); } private void applyInsets(Rectangle rect, Insets insets) { - if(insets != null) { - rect.x += insets.left; - rect.y += insets.top; - rect.width -= (insets.right + rect.x); - rect.height -= (insets.bottom + rect.y); - } + SwingUtilities3.applyInsets(rect, insets); } /** diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java index 427823573a6db..f75c59869ceaf 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -89,6 +89,7 @@ public class BasicOptionPaneUI extends OptionPaneUI { public static final int MinimumHeight = 90; private static String newline; + private static int recursionCount; /** * {@code JOptionPane} that the receiver is providing the @@ -373,6 +374,7 @@ protected Container createMessageArea() { "OptionPane.messageAnchor", GridBagConstraints.CENTER); cons.insets = new Insets(0,0,3,0); + recursionCount = 0; addMessageComponents(body, cons, getMessage(), getMaxCharactersPerLineCount(), false); top.add(realBody, BorderLayout.CENTER); @@ -454,42 +456,79 @@ protected void addMessageComponents(Container container, } else if ((nl = s.indexOf('\n')) >= 0) { nll = 1; } - if (nl >= 0) { - // break up newlines - if (nl == 0) { - @SuppressWarnings("serial") // anonymous class - JPanel breakPanel = new JPanel() { - public Dimension getPreferredSize() { - Font f = getFont(); - - if (f != null) { - return new Dimension(1, f.getSize() + 2); - } - return new Dimension(0, 0); - } - }; - breakPanel.setName("OptionPane.break"); - addMessageComponents(container, cons, breakPanel, maxll, - true); - } else { - addMessageComponents(container, cons, s.substring(0, nl), - maxll, false); + if (s.contains("")) { + /* line break in html text is done by
tag + * and not by /n so it's incorrect to address newline + * same as non-html text. + * Text between tags are extracted + * and rendered as JLabel text + */ + int index1 = s.indexOf(""); + int index2 = s.indexOf(""); + String str = ""; + if (index2 >= 0) { + str = s.substring(index2 + "".length()); + s = s.substring(index1, index2 + + "".length()); } - addMessageComponents(container, cons, s.substring(nl + nll), maxll, - false); - - } else if (len > maxll) { - Container c = Box.createVerticalBox(); - c.setName("OptionPane.verticalBox"); - burstStringInto(c, s, maxll); - addMessageComponents(container, cons, c, maxll, true ); - - } else { JLabel label; - label = new JLabel( s, JLabel.LEADING ); + label = new JLabel(s, JLabel.LEADING); + if (Boolean.TRUE.equals( + this.optionPane.getClientProperty("html.disable"))) { + label.putClientProperty("html.disable", true); + } label.setName("OptionPane.label"); configureMessageLabel(label); addMessageComponents(container, cons, label, maxll, true); + if (!str.isEmpty()) { + addMessageComponents(container, cons, str, maxll, false); + } + } else { + if (nl >= 0) { + // break up newlines + if (nl == 0) { + @SuppressWarnings("serial") // anonymous class + JPanel breakPanel = new JPanel() { + public Dimension getPreferredSize() { + Font f = getFont(); + + if (f != null) { + return new Dimension(1, f.getSize() + 2); + } + return new Dimension(0, 0); + } + }; + breakPanel.setName("OptionPane.break"); + addMessageComponents(container, cons, breakPanel, maxll, + true); + } else { + addMessageComponents(container, cons, s.substring(0, nl), + maxll, false); + } + // Prevent recursion of more than + // 200 successive newlines in a message + // and indicate message is truncated via ellipsis + if (recursionCount++ > 200) { + recursionCount = 0; + addMessageComponents(container, cons, new String("..."), + maxll, false); + return; + } + addMessageComponents(container, cons, s.substring(nl + nll), maxll, + false); + + } else if (len > maxll) { + Container c = Box.createVerticalBox(); + c.setName("OptionPane.verticalBox"); + burstStringInto(c, s, maxll); + addMessageComponents(container, cons, c, maxll, true); + + } else { + JLabel label; + label = new JLabel(s, JLabel.LEADING); + label.setName("OptionPane.label"); + configureMessageLabel(label); + addMessageComponents(container, cons, label, maxll, true); + } } } } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPasswordFieldUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPasswordFieldUI.java index 6a78e2d6871ea..ce3b699b8867e 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPasswordFieldUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPasswordFieldUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -91,23 +91,4 @@ protected void installDefaults() { public View create(Element elem) { return new PasswordView(elem); } - - /** - * Create the action map for Password Field. This map provides - * same actions for double mouse click and - * and for triple mouse click (see bug 4231444). - */ - - ActionMap createActionMap() { - ActionMap map = super.createActionMap(); - if (map.get(DefaultEditorKit.selectWordAction) != null) { - Action a = map.get(DefaultEditorKit.selectLineAction); - if (a != null) { - map.remove(DefaultEditorKit.selectWordAction); - map.put(DefaultEditorKit.selectWordAction, a); - } - } - return map; - } - } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTabbedPaneUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTabbedPaneUI.java index 8446354fe1426..662af3199ce5e 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTabbedPaneUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTabbedPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -456,7 +456,11 @@ protected void installListeners() { } tabPane.addContainerListener(getHandler()); if (tabPane.getTabCount()>0) { - htmlViews = createHTMLVector(); + Boolean htmlDisabled = (Boolean) + tabPane.getClientProperty("html.disable"); + if (!(Boolean.TRUE.equals(htmlDisabled))) { + htmlViews = createHTMLVector(); + } } } @@ -4026,8 +4030,10 @@ else if (name =="indexForTitle") { private void updateHtmlViews(int index, boolean inserted) { String title = tabPane.getTitleAt(index); + Boolean htmlDisabled = (Boolean) + tabPane.getClientProperty("html.disable"); boolean isHTML = BasicHTML.isHTMLString(title); - if (isHTML) { + if (isHTML && !(Boolean.TRUE.equals(htmlDisabled))) { if (htmlViews==null) { // Initialize vector htmlViews = createHTMLVector(); } else { // Vector already exists diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTableUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTableUI.java index 2ee4f1fa26d91..93d9bfe59234f 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTableUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTableUI.java @@ -1884,23 +1884,6 @@ public void paint(Graphics g, JComponent c) { comp = comp.getParent(); } - if (comp != null && !(comp instanceof JViewport) && !(comp instanceof JScrollPane)) { - // We did rMax-1 to paint the same number of rows that are drawn on console - // otherwise 1 extra row is printed per page than that are displayed - // when there is no scrollPane and we do printing of table - // but not when rmax is already pointing to index of last row - // and if there is any selected rows - if (rMax != (table.getRowCount() - 1) && - (table.getSelectedRow() == -1)) { - // Do not decrement rMax if rMax becomes - // less than or equal to rMin - // else cells will not be painted - if (rMax - rMin > 1) { - rMax = rMax - 1; - } - } - } - // Paint the grid. paintGrid(g, rMin, rMax, cMin, cMax); diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTextUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTextUI.java index b6d4bc6412bb7..5cc1306480c84 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTextUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTextUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -650,6 +650,22 @@ ActionMap createActionMap() { TransferHandler.getCopyAction()); map.put(TransferHandler.getPasteAction().getValue(Action.NAME), TransferHandler.getPasteAction()); + + if (getComponent() instanceof JPasswordField) { + // Edit the action map for Password Field. This map provides + // same actions for double mouse click and + // and for triple mouse click (see bugs 4231444, 8354646). + + if (map.get(DefaultEditorKit.selectWordAction) != null) { + map.remove(DefaultEditorKit.selectWordAction); + + Action a = map.get(DefaultEditorKit.selectLineAction); + if (a != null) { + map.put(DefaultEditorKit.selectWordAction, a); + } + } + } + return map; } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalBorders.java b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalBorders.java index 65905898d4c2d..8f032bfb2ece7 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalBorders.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalBorders.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,9 +33,7 @@ import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Insets; -import java.awt.Stroke; import java.awt.Window; -import java.awt.geom.AffineTransform; import javax.swing.AbstractButton; import javax.swing.ButtonModel; @@ -62,6 +60,7 @@ import javax.swing.plaf.basic.BasicBorders; import javax.swing.text.JTextComponent; +import com.sun.java.swing.SwingUtilities3; import sun.swing.StringUIClientPropertyKey; import sun.swing.SwingUtilities2; @@ -251,6 +250,14 @@ public InternalFrameBorder() {} public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) { + SwingUtilities3.paintBorder(c, g, + x, y, w, h, + this::paintUnscaledBorder); + } + + private void paintUnscaledBorder(Component c, Graphics g, + int width, int height, + double scaleFactor) { Color background; Color highlight; Color shadow; @@ -265,48 +272,6 @@ public void paintBorder(Component c, Graphics g, int x, int y, shadow = MetalLookAndFeel.getControlInfo(); } - AffineTransform at = null; - Stroke oldStk = null; - boolean resetTransform = false; - int stkWidth = 1; - double scaleFactor = 1; - - if (g instanceof Graphics2D g2d) { - at = g2d.getTransform(); - scaleFactor = at.getScaleX(); - oldStk = g2d.getStroke(); - - // if m01 or m10 is non-zero, then there is a rotation or shear - // skip resetting the transform - resetTransform = ((at.getShearX() == 0) && (at.getShearY() == 0)); - - if (resetTransform) { - g2d.setTransform(new AffineTransform()); - stkWidth = clipRound(Math.min(at.getScaleX(), at.getScaleY())); - g2d.setStroke(new BasicStroke((float) stkWidth)); - } - } - - int xtranslation; - int ytranslation; - int width; - int height; - - if (resetTransform) { - double xx = at.getScaleX() * x + at.getTranslateX(); - double yy = at.getScaleY() * y + at.getTranslateY(); - xtranslation = clipRound(xx); - ytranslation = clipRound(yy); - width = clipRound(at.getScaleX() * w + xx) - xtranslation; - height = clipRound(at.getScaleY() * h + yy) - ytranslation; - } else { - xtranslation = x; - ytranslation = y; - width = w; - height = h; - } - g.translate(xtranslation, ytranslation); - // scaled border int thickness = (int) Math.ceil(4 * scaleFactor); @@ -320,12 +285,17 @@ public void paintBorder(Component c, Graphics g, int x, int y, // midpoint at which highlight & shadow lines // are positioned on the border int midPoint = thickness / 2; + int stkWidth = clipRound(scaleFactor); int offset = (((scaleFactor - stkWidth) >= 0) && ((stkWidth % 2) != 0)) ? 1 : 0; int loc1 = thickness % 2 == 0 ? midPoint + stkWidth / 2 - stkWidth : midPoint; int loc2 = thickness % 2 == 0 ? midPoint + stkWidth / 2 : midPoint + stkWidth; // scaled corner int corner = (int) Math.round(CORNER * scaleFactor); + if (g instanceof Graphics2D) { + ((Graphics2D) g).setStroke(new BasicStroke((float) stkWidth)); + } + // Draw the Long highlight lines g.setColor(highlight); g.drawLine(corner + 1, loc2, width - corner, loc2); //top @@ -344,14 +314,6 @@ public void paintBorder(Component c, Graphics g, int x, int y, g.drawLine(corner, (height - offset) - loc2, width - corner - 1, (height - offset) - loc2); } - - // restore previous transform - g.translate(-xtranslation, -ytranslation); - if (resetTransform) { - Graphics2D g2d = (Graphics2D) g; - g2d.setTransform(at); - g2d.setStroke(oldStk); - } } public Insets getBorderInsets(Component c, Insets newInsets) { diff --git a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalFileChooserUI.java b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalFileChooserUI.java index 895379c18643e..52ed66c215d45 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalFileChooserUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalFileChooserUI.java @@ -953,6 +953,8 @@ public Component getListCellRendererComponent(JList list, Object value, ii.depth = directoryComboBoxModel.getDepth(index); setIcon(ii); + putClientProperty("html.disable", getFileChooser().getClientProperty("html.disable")); + return this; } } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java index a4b389d4b6a6a..3f6e091afd64c 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java @@ -357,6 +357,34 @@ public void update(Graphics g, JComponent c) { paint(context, g); } + /** + * The minimum size is the size of the display area plus insets plus the button. + */ + @Override + public Dimension getMinimumSize( JComponent c ) { + if ( !isMinimumSizeDirty ) { + return new Dimension(cachedMinimumSize); + } + Dimension size = getDisplaySize(); + Insets insets = getInsets(); + Insets arrowInsets = arrowButton.getInsets(); + //calculate the width and height of the button + int buttonHeight = size.height; + int buttonWidth = squareButton ? + buttonHeight : + arrowButton.getPreferredSize().width; + //adjust the size based on the button width + size.height += insets.top + insets.bottom + arrowInsets.top + + arrowInsets.bottom; + size.width += insets.left + insets.right + arrowInsets.left + + arrowInsets.right + buttonWidth; + + cachedMinimumSize.setSize( size.width, size.height ); + isMinimumSizeDirty = false; + + return new Dimension(size); + } + /** * Paints the specified component according to the Look and Feel. *

This method is not used by Synth Look and Feel. diff --git a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthPasswordFieldUI.java b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthPasswordFieldUI.java index 90b88376d595a..6aabdf4f5c780 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthPasswordFieldUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthPasswordFieldUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -95,19 +95,4 @@ public void paintBorder(SynthContext context, Graphics g, int x, int y, int w, int h) { context.getPainter().paintPasswordFieldBorder(context, g, x, y, w, h); } - - /** - * {@inheritDoc} - */ - @Override - protected void installKeyboardActions() { - super.installKeyboardActions(); - ActionMap map = SwingUtilities.getUIActionMap(getComponent()); - if (map != null && map.get(DefaultEditorKit.selectWordAction) != null) { - Action a = map.get(DefaultEditorKit.selectLineAction); - if (a != null) { - map.put(DefaultEditorKit.selectWordAction, a); - } - } - } } diff --git a/src/java.desktop/share/classes/javax/swing/text/Document.java b/src/java.desktop/share/classes/javax/swing/text/Document.java index e0f4ac791d5e8..e16d11acecb6f 100644 --- a/src/java.desktop/share/classes/javax/swing/text/Document.java +++ b/src/java.desktop/share/classes/javax/swing/text/Document.java @@ -277,7 +277,7 @@ *

Removing text from a DefaultStyledDocument is similar to removing text from * a PlainDocument. The only difference is the extra level of Elements. * Consider what would happen if you deleted two characters at Offset 1 - * from Figure 10, above. Since the the second Element of Paragraph 1 is + * from Figure 10, above. Since the second Element of Paragraph 1 is * completely contained in the deleted region, it would be removed. * Assuming the attributes of Paragraph 1's first child matched those of * Paragraph2's first child, the results would be those shown in Figure 11. diff --git a/src/java.desktop/share/classes/sun/awt/datatransfer/SunClipboard.java b/src/java.desktop/share/classes/sun/awt/datatransfer/SunClipboard.java index fe1baa3e631d6..1f2dcdfb65be9 100644 --- a/src/java.desktop/share/classes/sun/awt/datatransfer/SunClipboard.java +++ b/src/java.desktop/share/classes/sun/awt/datatransfer/SunClipboard.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -204,8 +204,9 @@ public Object getData(DataFlavor flavor) byte[] data = null; Transferable localeTransferable = null; + openClipboard(null); + try { - openClipboard(null); long[] formats = getClipboardFormats(); Long lFormat = DataTransferer.getInstance(). @@ -318,12 +319,7 @@ protected void lostOwnershipNow(final AppContext disposedContext) { * @since 1.5 */ protected long[] getClipboardFormatsOpenClose() { - try { - openClipboard(null); - return getClipboardFormats(); - } finally { - closeClipboard(); - } + return getClipboardFormats(); } /** @@ -356,15 +352,7 @@ public synchronized void addFlavorListener(FlavorListener listener) { flavorListeners.add(listener); if (numberOfFlavorListeners++ == 0) { - long[] currentFormats = null; - try { - openClipboard(null); - currentFormats = getClipboardFormats(); - } catch (final IllegalStateException ignored) { - } finally { - closeClipboard(); - } - this.currentFormats = currentFormats; + this.currentFormats = getClipboardFormats(); registerClipboardViewerChecked(); } diff --git a/src/java.desktop/share/classes/sun/awt/image/XbmImageDecoder.java b/src/java.desktop/share/classes/sun/awt/image/XbmImageDecoder.java index 03b36b3b819ac..cac9f8baab20e 100644 --- a/src/java.desktop/share/classes/sun/awt/image/XbmImageDecoder.java +++ b/src/java.desktop/share/classes/sun/awt/image/XbmImageDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,13 +23,22 @@ * questions. */ -/*- +/* * Reads xbitmap format images into a DIBitmap structure. */ package sun.awt.image; -import java.io.*; -import java.awt.image.*; +import java.awt.image.ImageConsumer; +import java.awt.image.IndexColorModel; +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static java.lang.Math.multiplyExact; /** * Parse files of the form: @@ -50,6 +59,8 @@ public class XbmImageDecoder extends ImageDecoder { ImageConsumer.COMPLETESCANLINES | ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME); + private static final int MAX_XBM_SIZE = 16384; + private static final int HEADER_SCAN_LIMIT = 100; public XbmImageDecoder(InputStreamImageSource src, InputStream is) { super(src, is); @@ -72,107 +83,125 @@ private static void error(String s1) throws ImageFormatException { * produce an image from the stream. */ public void produceImage() throws IOException, ImageFormatException { - char[] nm = new char[80]; - int c; - int i = 0; - int state = 0; int H = 0; int W = 0; int x = 0; int y = 0; - boolean start = true; + int n = 0; + int state = 0; byte[] raster = null; IndexColorModel model = null; - while (!aborted && (c = input.read()) != -1) { - if ('a' <= c && c <= 'z' || - 'A' <= c && c <= 'Z' || - '0' <= c && c <= '9' || c == '#' || c == '_') { - if (i < 78) - nm[i++] = (char) c; - } else if (i > 0) { - int nc = i; - i = 0; - if (start) { - if (nc != 7 || - nm[0] != '#' || - nm[1] != 'd' || - nm[2] != 'e' || - nm[3] != 'f' || - nm[4] != 'i' || - nm[5] != 'n' || - nm[6] != 'e') - { - error("Not an XBM file"); + + String matchRegex = "(0[xX])?[0-9a-fA-F]+[\\s+]?[,|};]"; + String replaceRegex = "(0[xX])|,|[\\s+]|[};]"; + + String line; + int lineNum = 0; + + try (BufferedReader br = new BufferedReader(new InputStreamReader(input))) { + // loop to process XBM header - width, height and create raster + while (!aborted && (line = br.readLine()) != null + && lineNum <= HEADER_SCAN_LIMIT) { + lineNum++; + // process #define stmts + if (line.trim().startsWith("#define")) { + String[] token = line.split("\\s+"); + if (token.length != 3) { + error("Error while parsing define statement"); + } + try { + if (!token[2].isBlank() && state == 0) { + W = Integer.parseInt(token[2]); + state = 1; // after width is set + } else if (!token[2].isBlank() && state == 1) { + H = Integer.parseInt(token[2]); + state = 2; // after height is set + } + } catch (NumberFormatException nfe) { + // parseInt() can throw NFE + error("Error while parsing width or height."); } - start = false; } - if (nm[nc - 1] == 'h') - state = 1; /* expecting width */ - else if (nm[nc - 1] == 't' && nc > 1 && nm[nc - 2] == 'h') - state = 2; /* expecting height */ - else if (nc > 2 && state < 0 && nm[0] == '0' && nm[1] == 'x') { - int n = 0; - for (int p = 2; p < nc; p++) { - c = nm[p]; - if ('0' <= c && c <= '9') - c = c - '0'; - else if ('A' <= c && c <= 'Z') - c = c - 'A' + 10; - else if ('a' <= c && c <= 'z') - c = c - 'a' + 10; - else - c = 0; - n = n * 16 + c; + + if (state == 2) { + if (W <= 0 || H <= 0) { + error("Invalid values for width or height."); } - for (int mask = 1; mask <= 0x80; mask <<= 1) { - if (x < W) { - if ((n & mask) != 0) - raster[x] = 1; - else - raster[x] = 0; - } - x++; + if (multiplyExact(W, H) > MAX_XBM_SIZE) { + error("Large XBM file size." + + " Maximum allowed size: " + MAX_XBM_SIZE); } - if (x >= W) { - if (setPixels(0, y, W, 1, model, raster, 0, W) <= 0) { - return; + model = new IndexColorModel(8, 2, XbmColormap, + 0, false, 0); + setDimensions(W, H); + setColorModel(model); + setHints(XbmHints); + headerComplete(); + raster = new byte[W]; + state = 3; + break; + } + } + + if (state != 3) { + error("Width or Height of XBM file not defined"); + } + + // loop to process image data + while (!aborted && (line = br.readLine()) != null) { + lineNum++; + + if (line.contains("[]")) { + Matcher matcher = Pattern.compile(matchRegex).matcher(line); + while (matcher.find()) { + if (y >= H) { + error("Scan size of XBM file exceeds" + + " the defined width x height"); + } + + int startIndex = matcher.start(); + int endIndex = matcher.end(); + String hexByte = line.substring(startIndex, endIndex); + + if (!(hexByte.startsWith("0x") + || hexByte.startsWith("0X"))) { + error("Invalid hexadecimal number at Ln#:" + lineNum + + " Col#:" + (startIndex + 1)); } - x = 0; - if (y++ >= H) { - break; + hexByte = hexByte.replaceAll(replaceRegex, ""); + if (hexByte.length() != 2) { + error("Invalid hexadecimal number at Ln#:" + lineNum + + " Col#:" + (startIndex + 1)); } - } - } else { - int n = 0; - for (int p = 0; p < nc; p++) - if ('0' <= (c = nm[p]) && c <= '9') - n = n * 10 + c - '0'; - else { - n = -1; - break; + + try { + n = Integer.parseInt(hexByte, 16); + } catch (NumberFormatException nfe) { + error("Error parsing hexadecimal at Ln#:" + lineNum + + " Col#:" + (startIndex + 1)); + } + for (int mask = 1; mask <= 0x80; mask <<= 1) { + if (x < W) { + if ((n & mask) != 0) + raster[x] = 1; + else + raster[x] = 0; + } + x++; } - if (n > 0 && state > 0) { - if (state == 1) - W = n; - else - H = n; - if (W == 0 || H == 0) - state = 0; - else { - model = new IndexColorModel(8, 2, XbmColormap, - 0, false, 0); - setDimensions(W, H); - setColorModel(model); - setHints(XbmHints); - headerComplete(); - raster = new byte[W]; - state = -1; + + if (x >= W) { + int result = setPixels(0, y, W, 1, model, raster, 0, W); + if (result <= 0) { + error("Unexpected error occurred during setPixel()"); + } + x = 0; + y++; } } } } + imageComplete(ImageConsumer.STATICIMAGEDONE, true); } - input.close(); - imageComplete(ImageConsumer.STATICIMAGEDONE, true); } } diff --git a/src/java.desktop/share/classes/sun/font/FileFontStrike.java b/src/java.desktop/share/classes/sun/font/FileFontStrike.java index bf98b8ca57863..aecfcc014f51a 100644 --- a/src/java.desktop/share/classes/sun/font/FileFontStrike.java +++ b/src/java.desktop/share/classes/sun/font/FileFontStrike.java @@ -202,7 +202,6 @@ public class FileFontStrike extends PhysicalStrike { this.disposer = new FontStrikeDisposer(fileFont, desc); initGlyphCache(); pScalerContext = NullFontScaler.getNullScalerContext(); - SunFontManager.getInstance().deRegisterBadFont(fileFont); return; } /* First, see if native code should be used to create the glyph. diff --git a/src/java.desktop/share/classes/sun/print/PathGraphics.java b/src/java.desktop/share/classes/sun/print/PathGraphics.java index 1fbd7fa68d6c2..c99a532ddf9dd 100644 --- a/src/java.desktop/share/classes/sun/print/PathGraphics.java +++ b/src/java.desktop/share/classes/sun/print/PathGraphics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -69,6 +69,7 @@ import java.awt.image.DataBufferInt; import java.awt.image.ImageObserver; import java.awt.image.IndexColorModel; +import java.awt.image.MultiResolutionImage; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; @@ -1138,6 +1139,9 @@ protected BufferedImage getBufferedImage(Image img) { // VI needs to make a new BI: this is unavoidable but // I don't expect VI's to be "huge" in any case. return ((VolatileImage)img).getSnapshot(); + } else if (img instanceof MultiResolutionImage) { + return convertToBufferedImage((MultiResolutionImage) img, + img.getWidth(null), img.getHeight(null)); } else { // may be null or may be some non-standard Image which // shouldn't happen as Image is implemented by the platform @@ -1148,6 +1152,18 @@ protected BufferedImage getBufferedImage(Image img) { } } + protected BufferedImage convertToBufferedImage(MultiResolutionImage multiResolutionImage, + double width, double height ) { + Image resolutionImage = multiResolutionImage.getResolutionVariant(width, height); + BufferedImage bufferedImage = new BufferedImage(resolutionImage.getWidth(null), + resolutionImage.getHeight(null), + BufferedImage.TYPE_INT_ARGB); + Graphics2D g2d = bufferedImage.createGraphics(); + g2d.drawImage(resolutionImage, 0, 0, (int) width, (int) height, null); + g2d.dispose(); + return bufferedImage; + } + /** * Return true if the BufferedImage argument has non-opaque * bits in it and therefore can not be directly rendered by diff --git a/src/java.desktop/share/classes/sun/swing/FilePane.java b/src/java.desktop/share/classes/sun/swing/FilePane.java index 5acf1380250b3..7b5f3c66287f3 100644 --- a/src/java.desktop/share/classes/sun/swing/FilePane.java +++ b/src/java.desktop/share/classes/sun/swing/FilePane.java @@ -1214,6 +1214,8 @@ public Component getTableCellRendererComponent(JTable table, Object value, setText(text); + putClientProperty("html.disable", getFileChooser().getClientProperty("html.disable")); + return this; } } @@ -1602,6 +1604,8 @@ public Component getListCellRendererComponent(JList list, Object value, } } + putClientProperty("html.disable", getFileChooser().getClientProperty("html.disable")); + return this; } } diff --git a/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java b/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java index e746dad6e7ee0..285ad524f139f 100644 --- a/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java +++ b/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/java.desktop/share/classes/sun/swing/plaf/synth/SynthFileChooserUIImpl.java b/src/java.desktop/share/classes/sun/swing/plaf/synth/SynthFileChooserUIImpl.java index ddab1198c3cb0..5a2c9ad5a41e4 100644 --- a/src/java.desktop/share/classes/sun/swing/plaf/synth/SynthFileChooserUIImpl.java +++ b/src/java.desktop/share/classes/sun/swing/plaf/synth/SynthFileChooserUIImpl.java @@ -692,6 +692,8 @@ public Component getListCellRendererComponent(JList list, File v ii.depth = directoryComboBoxModel.getDepth(index); label.setIcon(ii); + label.putClientProperty("html.disable", getFileChooser().getClientProperty("html.disable")); + return label; } } diff --git a/src/java.desktop/share/legal/freetype.md b/src/java.desktop/share/legal/freetype.md index 6bcb4976fd201..2c654e154ba4f 100644 --- a/src/java.desktop/share/legal/freetype.md +++ b/src/java.desktop/share/legal/freetype.md @@ -1,4 +1,4 @@ -## The FreeType Project: Freetype v2.13.2 +## The FreeType Project: Freetype v2.13.3 ### FreeType Notice @@ -21,23 +21,23 @@ which fits your needs best. ### FreeType License ``` -Copyright (C) 1996-2023 by David Turner, Robert Wilhelm, and Werner Lemberg. -Copyright (C) 2007-2023 by Dereg Clegg and Michael Toftdal. -Copyright (C) 1996-2023 by Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg. -Copyright (C) 2022-2023 by David Turner, Robert Wilhelm, Werner Lemberg, George Williams, and -Copyright (C) 2004-2023 by Masatake YAMATO and Redhat K.K. -Copyright (C) 2007-2023 by Derek Clegg and Michael Toftdal. -Copyright (C) 2003-2023 by Masatake YAMATO, Red Hat K.K., -Copyright (C) 1996-2023 by David Turner, Robert Wilhelm, Werner Lemberg, and Dominik Röttsches. -Copyright (C) 2007-2023 by David Turner. -Copyright (C) 2022-2023 by David Turner, Robert Wilhelm, Werner Lemberg, and Moazin Khatti. -Copyright (C) 2007-2023 by Rahul Bhalerao , . -Copyright (C) 2008-2023 by David Turner, Robert Wilhelm, Werner Lemberg, and suzuki toshiya. -Copyright (C) 2013-2023 by Google, Inc. -Copyright (C) 2019-2023 by Nikhil Ramakrishnan, David Turner, Robert Wilhelm, and Werner Lemberg. -Copyright (C) 2009-2023 by Oran Agra and Mickey Gabel. -Copyright (C) 2018-2023 by David Turner, Robert Wilhelm, Dominik Röttsches, and Werner Lemberg. -Copyright (C) 2004-2023 by David Turner, Robert Wilhelm, Werner Lemberg, and George Williams. +Copyright (C) 1996-2024 by David Turner, Robert Wilhelm, and Werner Lemberg. +Copyright (C) 2007-2024 by Dereg Clegg and Michael Toftdal. +Copyright (C) 1996-2024 by Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg. +Copyright (C) 2022-2024 by David Turner, Robert Wilhelm, Werner Lemberg, George Williams, and +Copyright (C) 2004-2024 by Masatake YAMATO and Redhat K.K. +Copyright (C) 2007-2024 by Derek Clegg and Michael Toftdal. +Copyright (C) 2003-2024 by Masatake YAMATO, Red Hat K.K., +Copyright (C) 1996-2024 by David Turner, Robert Wilhelm, Werner Lemberg, and Dominik Röttsches. +Copyright (C) 2007-2024 by David Turner. +Copyright (C) 2022-2024 by David Turner, Robert Wilhelm, Werner Lemberg, and Moazin Khatti. +Copyright (C) 2007-2024 by Rahul Bhalerao , . +Copyright (C) 2008-2024 by David Turner, Robert Wilhelm, Werner Lemberg, and suzuki toshiya. +Copyright (C) 2013-2024 by Google, Inc. +Copyright (C) 2019-2024 by Nikhil Ramakrishnan, David Turner, Robert Wilhelm, and Werner Lemberg. +Copyright (C) 2009-2024 by Oran Agra and Mickey Gabel. +Copyright (C) 2018-2024 by David Turner, Robert Wilhelm, Dominik Röttsches, and Werner Lemberg. +Copyright (C) 2004-2024 by David Turner, Robert Wilhelm, Werner Lemberg, and George Williams. The FreeType Project LICENSE @@ -559,7 +559,7 @@ Public License instead of this License. ``` --------------------------------- -The below license applies to the following files: +The below applies to the following file(s): libfreetype/src/psaux/psarrst.c libfreetype/src/psaux/psarrst.h libfreetype/src/psaux/psblues.c @@ -582,7 +582,7 @@ libfreetype/src/psaux/psstack.c libfreetype/src/psaux/psstack.h libfreetype/src/psaux/pstypes.h -Copyright 2006-2014 Adobe Systems Incorporated. +Copyright (C) 2006-2014 Adobe Systems Incorporated. This software, and all works of authorship, whether in source or object code form as indicated by the copyright notice(s) included @@ -618,12 +618,12 @@ and you accept them fully. ``` --------------------------------- -The below license applies to the following files: +The below applies to the following file(s): libfreetype/include/freetype/internal/fthash.h libfreetype/src/base/fthash.c -Copyright 2000 Computing Research Labs, New Mexico State University -Copyright 2001-2015 +Copyright (C) 2000 Computing Research Labs, New Mexico State University +Copyright (C) 2001-2015 Francesco Zappa Nardelli diff --git a/src/java.desktop/share/legal/harfbuzz.md b/src/java.desktop/share/legal/harfbuzz.md index 3ae73d215b046..e0c40705ed64d 100644 --- a/src/java.desktop/share/legal/harfbuzz.md +++ b/src/java.desktop/share/legal/harfbuzz.md @@ -1,4 +1,4 @@ -## Harfbuzz v8.2.2 +## Harfbuzz 11.2.0 ### Harfbuzz License @@ -8,14 +8,14 @@ HarfBuzz is licensed under the so-called "Old MIT" license. Details follow. For parts of HarfBuzz that are licensed under different licenses see individual files names COPYING in subdirectories where applicable. -Copyright © 2010-2023 Google, Inc. +Copyright © 2010-2024 Google, Inc. Copyright © 2018-2020 Ebrahim Byagowi Copyright © 2004-2013 Red Hat, Inc. Copyright © 2019 Facebook, Inc. Copyright (C) 2012 Zilong Tan (eric.zltan@gmail.com) Copyright © 2007 Chris Wilson Copyright © 2018-2019 Adobe Inc. -Copyright © 2006-2023 Behdad Esfahbod +Copyright © 2006-2025 Behdad Esfahbod Copyright © 1998-2004 David Turner and Werner Lemberg Copyright © 2009 Keith Stribley Copyright © 2018 Khaled Hosny @@ -54,7 +54,7 @@ exception is licensed with a slightly different MIT variant: The contents of this directory are licensed under the following terms: --------------------------------- -The below license applies to the following files: +The below applies to the following file(s): libharfbuzz/hb-ucd.cc Copyright (C) 2012 Grigori Goronzy @@ -72,13 +72,14 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. --------------------------------- -The below license applies to the following files: +The below applies to the following file(s): libharfbuzz/hb-unicode-emoji-table.hh -© 2023 Unicode®, Inc. +© 2024 Unicode®, Inc. + Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. -For terms of use, see https://www.unicode.org/terms_of_use.html +For terms of use and license, see https://www.unicode.org/terms_of_use.html

diff --git a/src/java.desktop/share/legal/lcms.md b/src/java.desktop/share/legal/lcms.md index 02af4fff0005e..83c47d3acdb47 100644 --- a/src/java.desktop/share/legal/lcms.md +++ b/src/java.desktop/share/legal/lcms.md @@ -1,11 +1,11 @@ -## Little Color Management System (LCMS) v2.16 +## Little Color Management System (LCMS) v2.17 ### LCMS License
 
 MIT License
 
-Copyright (C) 1998-2023 Marti Maria Saguer
+Copyright (C) 1998-2025 Marti Maria Saguer
 
 Permission is hereby granted, free of charge, to any person obtaining
 a copy of this software and associated documentation files (the "Software"),
@@ -26,10 +26,10 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 ---------------------------------
-The below license applies to the following files:
+The below applies to the following file(s):
 liblcms/cmssm.c
 
-Copyright 2001, softSurfer (www.softsurfer.com)
+Copyright (C) 2001, softSurfer (www.softsurfer.com)
 
 This code may be freely used and modified for any purpose
 providing that this copyright notice is included with it.
@@ -99,5 +99,5 @@ Christian Albrecht
 Dimitrios Anastassakis
 Lemke Software
 Tim Zaman
-
-```
+Amir Montazery and Open Source Technology Improvement Fund (ostif.org), Google, for fuzzer fundings.
+```
\ No newline at end of file
diff --git a/src/java.desktop/share/legal/libpng.md b/src/java.desktop/share/legal/libpng.md
index cbffed8133209..8899491c6c0a9 100644
--- a/src/java.desktop/share/legal/libpng.md
+++ b/src/java.desktop/share/legal/libpng.md
@@ -1,4 +1,4 @@
-## libpng v1.6.43
+## libpng v1.6.51
 
 ### libpng License
 
@@ -9,8 +9,8 @@ COPYRIGHT NOTICE, DISCLAIMER, and LICENSE
 PNG Reference Library License version 2
 ---------------------------------------
 
-Copyright (C) 1995-2024 The PNG Reference Library Authors.
-Copyright (C) 2018-2024 Cosmin Truta
+Copyright (C) 1995-2025 The PNG Reference Library Authors.
+Copyright (C) 2018-2025 Cosmin Truta
 Copyright (C) 1998-2018 Glenn Randers-Pehrson
 Copyright (C) 1996-1997 Andreas Dilger
 Copyright (C) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
@@ -170,8 +170,10 @@ Authors, for copyright and licensing purposes.
  * James Yu
  * John Bowler
  * Kevin Bracey
+ * Lucas Chollet
  * Magnus Holmgren
  * Mandar Sahastrabuddhe
+ * Manfred Schlaegl
  * Mans Rullgard
  * Matt Sarett
  * Mike Klein
@@ -183,6 +185,7 @@ Authors, for copyright and licensing purposes.
  * Samuel Williams
  * Simon-Pierre Cadieux
  * Tim Wegner
+ * Tobias Stoeckmann
  * Tom Lane
  * Tom Tanner
  * Vadim Barkov
@@ -192,8 +195,9 @@ Authors, for copyright and licensing purposes.
     - Zixu Wang (王子旭)
  * Arm Holdings
     - Richard Townsend
- * Google Inc.
+ * Google LLC
     - Dan Field
+    - Dragoș Tiselice
     - Leon Scroggins III
     - Matt Sarett
     - Mike Klein
@@ -203,6 +207,8 @@ Authors, for copyright and licensing purposes.
     - GuXiWei (顾希伟)
     - JinBo (金波)
     - ZhangLixia (张利霞)
+ * Samsung Group
+    - Filip Wasil
 
 The build projects, the build scripts, the test scripts, and other
 files in the "projects", "scripts" and "tests" directories, have
@@ -213,3 +219,4 @@ of the tools-generated files that are distributed with libpng, have
 other copyright owners, and are released under other open source
 licenses.
 ```
+
diff --git a/src/java.desktop/share/native/common/java2d/opengl/OGLContext.c b/src/java.desktop/share/native/common/java2d/opengl/OGLContext.c
index cac3895703ba6..3a8db7fde5a0f 100644
--- a/src/java.desktop/share/native/common/java2d/opengl/OGLContext.c
+++ b/src/java.desktop/share/native/common/java2d/opengl/OGLContext.c
@@ -484,6 +484,7 @@ OGLContext_SetTransform(OGLContext *oglc,
     if (oglc->xformMatrix == NULL) {
         size_t arrsize = 16 * sizeof(GLdouble);
         oglc->xformMatrix = (GLdouble *)malloc(arrsize);
+        RETURN_IF_NULL(oglc->xformMatrix);
         memset(oglc->xformMatrix, 0, arrsize);
         oglc->xformMatrix[10] = 1.0;
         oglc->xformMatrix[15] = 1.0;
diff --git a/src/java.desktop/share/native/libawt/java2d/loops/Blit.c b/src/java.desktop/share/native/libawt/java2d/loops/Blit.c
index fee108b833a4d..8a41584deefe8 100644
--- a/src/java.desktop/share/native/libawt/java2d/loops/Blit.c
+++ b/src/java.desktop/share/native/libawt/java2d/loops/Blit.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -68,14 +68,30 @@ Java_sun_java2d_loops_Blit_Blit
         return;
     }
 
+    if (width <= 0 || height <= 0) {
+        return;
+    }
+
     srcInfo.bounds.x1 = srcx;
     srcInfo.bounds.y1 = srcy;
+    if (UNSAFE_TO_ADD(srcx, width) ||
+        UNSAFE_TO_ADD(srcy, height) ||
+        UNSAFE_TO_ADD(dstx, width) ||
+        UNSAFE_TO_ADD(dsty, height)) {
+        return;
+    }
+
     srcInfo.bounds.x2 = srcx + width;
     srcInfo.bounds.y2 = srcy + height;
     dstInfo.bounds.x1 = dstx;
     dstInfo.bounds.y1 = dsty;
     dstInfo.bounds.x2 = dstx + width;
     dstInfo.bounds.y2 = dsty + height;
+    if (UNSAFE_TO_SUB(srcx, dstx) ||
+        UNSAFE_TO_SUB(srcy, dsty)) {
+        return;
+    }
+
     srcx -= dstx;
     srcy -= dsty;
     SurfaceData_IntersectBounds(&dstInfo.bounds, &clipInfo.bounds);
diff --git a/src/java.desktop/share/native/libfontmanager/freetypeScaler.c b/src/java.desktop/share/native/libfontmanager/freetypeScaler.c
index 7d5b1ed94fe04..8d864339c544c 100644
--- a/src/java.desktop/share/native/libfontmanager/freetypeScaler.c
+++ b/src/java.desktop/share/native/libfontmanager/freetypeScaler.c
@@ -500,7 +500,6 @@ Java_sun_font_FreetypeFontScaler_createScalerContextNative(
 
     if (context == NULL) {
         free(context);
-        invalidateJavaScaler(env, scaler, NULL);
         return (jlong) 0;
     }
     (*env)->GetDoubleArrayRegion(env, matrix, 0, 4, dmat);
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/config/ftconfig.h b/src/java.desktop/share/native/libfreetype/include/freetype/config/ftconfig.h
index a85151699d0e0..0667493fec64c 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/config/ftconfig.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/config/ftconfig.h
@@ -4,7 +4,7 @@
  *
  *   ANSI-specific configuration file (specification only).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/config/ftheader.h b/src/java.desktop/share/native/libfreetype/include/freetype/config/ftheader.h
index e607bce15c576..f6ef2618dedeb 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/config/ftheader.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/config/ftheader.h
@@ -4,7 +4,7 @@
  *
  *   Build macros of the FreeType 2 library.
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/config/ftoption.h b/src/java.desktop/share/native/libfreetype/include/freetype/config/ftoption.h
index 4375c7a6ff34f..d29a0a7cefbd0 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/config/ftoption.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/config/ftoption.h
@@ -4,7 +4,7 @@
  *
  *   User-selectable configuration macros (specification only).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -757,6 +757,22 @@ FT_BEGIN_HEADER
 #endif
 
 
+  /**************************************************************************
+   *
+   * Option `TT_CONFIG_OPTION_GPOS_KERNING` enables a basic GPOS kerning
+   * implementation (for TrueType fonts only).  With this defined, FreeType
+   * is able to get kerning pair data from the GPOS 'kern' feature as well as
+   * legacy 'kern' tables; without this defined, FreeType will only be able
+   * to use legacy 'kern' tables.
+   *
+   * Note that FreeType does not support more advanced GPOS layout features;
+   * even the 'kern' feature implemented here doesn't handle more
+   * sophisticated kerning variants.  Use a higher-level library like
+   * HarfBuzz instead for that.
+   */
+/* #define TT_CONFIG_OPTION_GPOS_KERNING */
+
+
   /*************************************************************************/
   /*************************************************************************/
   /****                                                                 ****/
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/config/ftstdlib.h b/src/java.desktop/share/native/libfreetype/include/freetype/config/ftstdlib.h
index f65148a902eac..e17aa7b89d572 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/config/ftstdlib.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/config/ftstdlib.h
@@ -5,7 +5,7 @@
  *   ANSI-specific library and header configuration file (specification
  *   only).
  *
- * Copyright (C) 2002-2023 by
+ * Copyright (C) 2002-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/config/integer-types.h b/src/java.desktop/share/native/libfreetype/include/freetype/config/integer-types.h
index 7258b50854150..c27505ffc4b6d 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/config/integer-types.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/config/integer-types.h
@@ -4,7 +4,7 @@
  *
  *   FreeType integer types definitions.
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/config/mac-support.h b/src/java.desktop/share/native/libfreetype/include/freetype/config/mac-support.h
index b77b96d5db835..07b6f915bd83b 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/config/mac-support.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/config/mac-support.h
@@ -4,7 +4,7 @@
  *
  *   Mac/OS X support configuration header.
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/config/public-macros.h b/src/java.desktop/share/native/libfreetype/include/freetype/config/public-macros.h
index 23d0fa6a329c6..f56581a6ee796 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/config/public-macros.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/config/public-macros.h
@@ -4,7 +4,7 @@
  *
  *   Define a set of compiler macros used in public FreeType headers.
  *
- * Copyright (C) 2020-2023 by
+ * Copyright (C) 2020-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/freetype.h b/src/java.desktop/share/native/libfreetype/include/freetype/freetype.h
index 92acf3794a7e8..58fc33dfe60ab 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/freetype.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/freetype.h
@@ -4,7 +4,7 @@
  *
  *   FreeType high-level API and common types (specification only).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -1322,9 +1322,13 @@ FT_BEGIN_HEADER
    *   FT_FACE_FLAG_KERNING ::
    *     The face contains kerning information.  If set, the kerning distance
    *     can be retrieved using the function @FT_Get_Kerning.  Otherwise the
-   *     function always returns the vector (0,0).  Note that FreeType
-   *     doesn't handle kerning data from the SFNT 'GPOS' table (as present
-   *     in many OpenType fonts).
+   *     function always returns the vector (0,0).
+   *
+   *     Note that for TrueType fonts only, FreeType supports both the 'kern'
+   *     table and the basic, pair-wise kerning feature from the 'GPOS' table
+   *     (with `TT_CONFIG_OPTION_GPOS_KERNING` enabled), though FreeType does
+   *     not support the more advanced GPOS layout features; use a library
+   *     like HarfBuzz for those instead.
    *
    *   FT_FACE_FLAG_FAST_GLYPHS ::
    *     THIS FLAG IS DEPRECATED.  DO NOT USE OR TEST IT.
@@ -3767,87 +3771,18 @@ FT_BEGIN_HEADER
    *     pixels and use the @FT_PIXEL_MODE_LCD_V mode.
    *
    *   FT_RENDER_MODE_SDF ::
-   *     This mode corresponds to 8-bit, single-channel signed distance field
-   *     (SDF) bitmaps.  Each pixel in the SDF grid is the value from the
-   *     pixel's position to the nearest glyph's outline.  The distances are
-   *     calculated from the center of the pixel and are positive if they are
-   *     filled by the outline (i.e., inside the outline) and negative
-   *     otherwise.  Check the note below on how to convert the output values
-   *     to usable data.
+   *     The positive (unsigned) 8-bit bitmap values can be converted to the
+   *     single-channel signed distance field (SDF) by subtracting 128, with
+   *     the positive and negative results corresponding to the inside and
+   *     the outside of a glyph contour, respectively.  The distance units are
+   *     arbitrarily determined by an adjustable @spread property.
    *
    * @note:
-   *   The selected render mode only affects vector glyphs of a font.
+   *   The selected render mode only affects scalable vector glyphs of a font.
    *   Embedded bitmaps often have a different pixel mode like
    *   @FT_PIXEL_MODE_MONO.  You can use @FT_Bitmap_Convert to transform them
    *   into 8-bit pixmaps.
    *
-   *   For @FT_RENDER_MODE_SDF the output bitmap buffer contains normalized
-   *   distances that are packed into unsigned 8-bit values.  To get pixel
-   *   values in floating point representation use the following pseudo-C
-   *   code for the conversion.
-   *
-   *   ```
-   *   // Load glyph and render using FT_RENDER_MODE_SDF,
-   *   // then use the output buffer as follows.
-   *
-   *   ...
-   *   FT_Byte  buffer = glyph->bitmap->buffer;
-   *
-   *
-   *   for pixel in buffer
-   *   {
-   *     // `sd` is the signed distance and `spread` is the current spread;
-   *     // the default spread is 2 and can be changed.
-   *
-   *     float  sd = (float)pixel - 128.0f;
-   *
-   *
-   *     // Convert to pixel values.
-   *     sd = ( sd / 128.0f ) * spread;
-   *
-   *     // Store `sd` in a buffer or use as required.
-   *   }
-   *
-   *   ```
-   *
-   *   FreeType has two rasterizers for generating SDF, namely:
-   *
-   *   1. `sdf` for generating SDF directly from glyph's outline, and
-   *
-   *   2. `bsdf` for generating SDF from rasterized bitmaps.
-   *
-   *   Depending on the glyph type (i.e., outline or bitmap), one of the two
-   *   rasterizers is chosen at runtime and used for generating SDFs.  To
-   *   force the use of `bsdf` you should render the glyph with any of the
-   *   FreeType's other rendering modes (e.g., `FT_RENDER_MODE_NORMAL`) and
-   *   then re-render with `FT_RENDER_MODE_SDF`.
-   *
-   *   There are some issues with stability and possible failures of the SDF
-   *   renderers (specifically `sdf`).
-   *
-   *   1. The `sdf` rasterizer is sensitive to really small features (e.g.,
-   *      sharp turns that are less than 1~pixel) and imperfections in the
-   *      glyph's outline, causing artifacts in the final output.
-   *
-   *   2. The `sdf` rasterizer has limited support for handling intersecting
-   *      contours and *cannot* handle self-intersecting contours whatsoever.
-   *      Self-intersection happens when a single connected contour
-   *      intersects itself at some point; having these in your font
-   *      definitely poses a problem to the rasterizer and cause artifacts,
-   *      too.
-   *
-   *   3. Generating SDF for really small glyphs may result in undesirable
-   *      output; the pixel grid (which stores distance information) becomes
-   *      too coarse.
-   *
-   *   4. Since the output buffer is normalized, precision at smaller spreads
-   *      is greater than precision at larger spread values because the
-   *      output range of [0..255] gets mapped to a smaller SDF range.  A
-   *      spread of~2 should be sufficient in most cases.
-   *
-   *   Points (1) and (2) can be avoided by using the `bsdf` rasterizer,
-   *   which is more stable than the `sdf` rasterizer in general.
-   *
    */
   typedef enum  FT_Render_Mode_
   {
@@ -4058,9 +3993,26 @@ FT_BEGIN_HEADER
    *   out of the scope of this API function -- they can be implemented
    *   through format-specific interfaces.
    *
-   *   Kerning for OpenType fonts implemented in a 'GPOS' table is not
-   *   supported; use @FT_HAS_KERNING to find out whether a font has data
-   *   that can be extracted with `FT_Get_Kerning`.
+   *   Note that, for TrueType fonts only, this can extract data from both
+   *   the 'kern' table and the basic, pair-wise kerning feature from the
+   *   GPOS table (with `TT_CONFIG_OPTION_GPOS_KERNING` enabled), though
+   *   FreeType does not support the more advanced GPOS layout features; use
+   *   a library like HarfBuzz for those instead.  If a font has both a
+   *   'kern' table and kern features of a GPOS table, the 'kern' table will
+   *   be used.
+   *
+   *   Also note for right-to-left scripts, the functionality may differ for
+   *   fonts with GPOS tables vs. 'kern' tables.  For GPOS, right-to-left
+   *   fonts typically use both a placement offset and an advance for pair
+   *   positioning, which this API does not support, so it would output
+   *   kerning values of zero; though if the right-to-left font used only
+   *   advances in GPOS pair positioning, then this API could output kerning
+   *   values for it, but it would use `left_glyph` to mean the first glyph
+   *   for that case.  Whereas 'kern' tables are always advance-only and
+   *   always store the left glyph first.
+   *
+   *   Use @FT_HAS_KERNING to find out whether a font has data that can be
+   *   extracted with `FT_Get_Kerning`.
    */
   FT_EXPORT( FT_Error )
   FT_Get_Kerning( FT_Face     face,
@@ -5222,7 +5174,7 @@ FT_BEGIN_HEADER
    */
 #define FREETYPE_MAJOR  2
 #define FREETYPE_MINOR  13
-#define FREETYPE_PATCH  2
+#define FREETYPE_PATCH  3
 
 
   /**************************************************************************
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftadvanc.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftadvanc.h
index 4560ded6dcbd3..85b8ba2554be7 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ftadvanc.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftadvanc.h
@@ -4,7 +4,7 @@
  *
  *   Quick computation of advance widths (specification only).
  *
- * Copyright (C) 2008-2023 by
+ * Copyright (C) 2008-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftbbox.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftbbox.h
index fc21740fc2bf8..12bbfa63a6284 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ftbbox.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftbbox.h
@@ -4,7 +4,7 @@
  *
  *   FreeType exact bbox computation (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftbdf.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftbdf.h
index e8ce6431285d2..6f63b0b1e7812 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ftbdf.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftbdf.h
@@ -4,7 +4,7 @@
  *
  *   FreeType API for accessing BDF-specific strings (specification).
  *
- * Copyright (C) 2002-2023 by
+ * Copyright (C) 2002-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftbitmap.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftbitmap.h
index eb6b4b1eebedf..df9d462652e75 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ftbitmap.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftbitmap.h
@@ -4,7 +4,7 @@
  *
  *   FreeType utility functions for bitmaps (specification).
  *
- * Copyright (C) 2004-2023 by
+ * Copyright (C) 2004-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftcid.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftcid.h
index ef22939022453..96b2a90fc59f3 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ftcid.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftcid.h
@@ -4,7 +4,7 @@
  *
  *   FreeType API for accessing CID font information (specification).
  *
- * Copyright (C) 2007-2023 by
+ * Copyright (C) 2007-2024 by
  * Dereg Clegg and Michael Toftdal.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftcolor.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftcolor.h
index eae200fdf1485..420720ddf225d 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ftcolor.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftcolor.h
@@ -4,7 +4,7 @@
  *
  *   FreeType's glyph color management (specification).
  *
- * Copyright (C) 2018-2023 by
+ * Copyright (C) 2018-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftdriver.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftdriver.h
index 7af7465bc768e..1b7f539f5e22d 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ftdriver.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftdriver.h
@@ -4,7 +4,7 @@
  *
  *   FreeType API for controlling driver modules (specification only).
  *
- * Copyright (C) 2017-2023 by
+ * Copyright (C) 2017-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -817,6 +817,80 @@ FT_BEGIN_HEADER
    *   2.5
    */
 
+
+  /**************************************************************************
+   *
+   * @property:
+   *   spread
+   *
+   * @description:
+   *   This property of the 'sdf' and 'bsdf' renderers defines how the signed
+   *   distance field (SDF) is represented in the output bitmap.  The output
+   *   values are calculated as follows, '128 * ( SDF / spread + 1 )', with
+   *   the result clamped to the 8-bit range [0..255].  Therefore, 'spread'
+   *   is also the maximum euclidean distance from the edge after which the
+   *   values are clamped.  The spread is specified in pixels with the
+   *   default value of 8.  For accurate SDF texture mapping (interpolation),
+   *   the spread should be large enough to accommodate the target grid unit.
+   *
+   * @example:
+   *   The following example code demonstrates how to set the SDF spread
+   *   (omitting the error handling).
+   *
+   *   ```
+   *     FT_Library  library;
+   *     FT_Int      spread = 2;
+   *
+   *
+   *     FT_Init_FreeType( &library );
+   *
+   *     FT_Property_Set( library, "sdf", "spread", &spread );
+   *   ```
+   *
+   * @note:
+   *   FreeType has two rasterizers for generating SDF, namely:
+   *
+   *   1. `sdf` for generating SDF directly from glyph's outline, and
+   *
+   *   2. `bsdf` for generating SDF from rasterized bitmaps.
+   *
+   *   Depending on the glyph type (i.e., outline or bitmap), one of the two
+   *   rasterizers is chosen at runtime and used for generating SDFs.  To
+   *   force the use of `bsdf` you should render the glyph with any of the
+   *   FreeType's other rendering modes (e.g., `FT_RENDER_MODE_NORMAL`) and
+   *   then re-render with `FT_RENDER_MODE_SDF`.
+   *
+   *   There are some issues with stability and possible failures of the SDF
+   *   renderers (specifically `sdf`).
+   *
+   *   1. The `sdf` rasterizer is sensitive to really small features (e.g.,
+   *      sharp turns that are less than 1~pixel) and imperfections in the
+   *      glyph's outline, causing artifacts in the final output.
+   *
+   *   2. The `sdf` rasterizer has limited support for handling intersecting
+   *      contours and *cannot* handle self-intersecting contours whatsoever.
+   *      Self-intersection happens when a single connected contour
+   *      intersects itself at some point; having these in your font
+   *      definitely poses a problem to the rasterizer and cause artifacts,
+   *      too.
+   *
+   *   3. Generating SDF for really small glyphs may result in undesirable
+   *      output; the pixel grid (which stores distance information) becomes
+   *      too coarse.
+   *
+   *   4. Since the output buffer is normalized, precision at smaller spreads
+   *      is greater than precision at larger spread values because the
+   *      output range of [0..255] gets mapped to a smaller SDF range.  A
+   *      spread of~2 should be sufficient in most cases.
+   *
+   *   Points (1) and (2) can be avoided by using the `bsdf` rasterizer,
+   *   which is more stable than the `sdf` rasterizer in general.
+   *
+   * @since:
+   *   2.11
+   */
+
+
   /**************************************************************************
    *
    * @property:
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/fterrdef.h b/src/java.desktop/share/native/libfreetype/include/freetype/fterrdef.h
index d59b3cc2da277..710ca91bbddb8 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/fterrdef.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/fterrdef.h
@@ -4,7 +4,7 @@
  *
  *   FreeType error codes (specification).
  *
- * Copyright (C) 2002-2023 by
+ * Copyright (C) 2002-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/fterrors.h b/src/java.desktop/share/native/libfreetype/include/freetype/fterrors.h
index 15ef3f76b59fc..27c0ece5c1ca6 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/fterrors.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/fterrors.h
@@ -4,7 +4,7 @@
  *
  *   FreeType error code handling (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftfntfmt.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftfntfmt.h
index c0018fc830c25..7c8b0874a8183 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ftfntfmt.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftfntfmt.h
@@ -4,7 +4,7 @@
  *
  *   Support functions for font formats.
  *
- * Copyright (C) 2002-2023 by
+ * Copyright (C) 2002-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftgasp.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftgasp.h
index d5f19add8f209..30e5a9bf82b00 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ftgasp.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftgasp.h
@@ -4,7 +4,7 @@
  *
  *   Access of TrueType's 'gasp' table (specification).
  *
- * Copyright (C) 2007-2023 by
+ * Copyright (C) 2007-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftglyph.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftglyph.h
index 4658895f7a962..dc1eb8873ae8c 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ftglyph.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftglyph.h
@@ -4,7 +4,7 @@
  *
  *   FreeType convenience functions to handle glyphs (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftgzip.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftgzip.h
index 443ec29db1bd3..9516dc030ac51 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ftgzip.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftgzip.h
@@ -4,7 +4,7 @@
  *
  *   Gzip-compressed stream support.
  *
- * Copyright (C) 2002-2023 by
+ * Copyright (C) 2002-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftimage.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftimage.h
index 6baa812560ead..2b4b4ac60ae2f 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ftimage.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftimage.h
@@ -5,7 +5,7 @@
  *   FreeType glyph image formats and default raster interface
  *   (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -21,6 +21,11 @@
    * Note: A 'raster' is simply a scan-line converter, used to render
    *       `FT_Outline`s into `FT_Bitmap`s.
    *
+   * Note: This file can be used for `STANDALONE_` compilation of raster
+   *       (B/W) and smooth (anti-aliased) renderers.  Therefore, it must
+   *       rely on standard variable types only instead of aliases in
+   *       `fttypes.h`.
+   *
    */
 
 
@@ -318,7 +323,7 @@ FT_BEGIN_HEADER
    *
    *     If bit~2 is set, bits 5-7 contain the drop-out mode (as defined in
    *     the OpenType specification; the value is the same as the argument to
-   *     the 'SCANMODE' instruction).
+   *     the 'SCANTYPE' instruction).
    *
    *     Bits 3 and~4 are reserved for internal purposes.
    *
@@ -341,14 +346,14 @@ FT_BEGIN_HEADER
    */
   typedef struct  FT_Outline_
   {
-    short       n_contours;      /* number of contours in glyph        */
-    short       n_points;        /* number of points in the glyph      */
+    unsigned short   n_contours;  /* number of contours in glyph        */
+    unsigned short   n_points;    /* number of points in the glyph      */
 
-    FT_Vector*  points;          /* the outline's points               */
-    char*       tags;            /* the points flags                   */
-    short*      contours;        /* the contour end points             */
+    FT_Vector*       points;      /* the outline's points               */
+    unsigned char*   tags;        /* the points flags                   */
+    unsigned short*  contours;    /* the contour end points             */
 
-    int         flags;           /* outline masks                      */
+    int              flags;       /* outline masks                      */
 
   } FT_Outline;
 
@@ -356,8 +361,8 @@ FT_BEGIN_HEADER
 
   /* Following limits must be consistent with */
   /* FT_Outline.{n_contours,n_points}         */
-#define FT_OUTLINE_CONTOURS_MAX  SHRT_MAX
-#define FT_OUTLINE_POINTS_MAX    SHRT_MAX
+#define FT_OUTLINE_CONTOURS_MAX  USHRT_MAX
+#define FT_OUTLINE_POINTS_MAX    USHRT_MAX
 
 
   /**************************************************************************
@@ -434,8 +439,8 @@ FT_BEGIN_HEADER
    *   rasterizer; see the `tags` field in @FT_Outline.
    *
    *   Please refer to the description of the 'SCANTYPE' instruction in the
-   *   OpenType specification (in file `ttinst1.doc`) how simple drop-outs,
-   *   smart drop-outs, and stubs are defined.
+   *   [OpenType specification](https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#scantype)
+   *   how simple drop-outs, smart drop-outs, and stubs are defined.
    */
 #define FT_OUTLINE_NONE             0x0
 #define FT_OUTLINE_OWNER            0x1
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftincrem.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftincrem.h
index 2d4f5def241b0..816581b78ebd5 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ftincrem.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftincrem.h
@@ -4,7 +4,7 @@
  *
  *   FreeType incremental loading (specification).
  *
- * Copyright (C) 2002-2023 by
+ * Copyright (C) 2002-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftlcdfil.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftlcdfil.h
index d3723e16f67dc..25274dc4ac223 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ftlcdfil.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftlcdfil.h
@@ -5,7 +5,7 @@
  *   FreeType API for color filtering of subpixel bitmap glyphs
  *   (specification).
  *
- * Copyright (C) 2006-2023 by
+ * Copyright (C) 2006-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftlist.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftlist.h
index b55313133593f..972fbfa2fe4e3 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ftlist.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftlist.h
@@ -4,7 +4,7 @@
  *
  *   Generic list support for FreeType (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftlogging.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftlogging.h
index 53b8b89642711..1813cfc2c27a2 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ftlogging.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftlogging.h
@@ -4,7 +4,7 @@
  *
  *   Additional debugging APIs.
  *
- * Copyright (C) 2020-2023 by
+ * Copyright (C) 2020-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftmac.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftmac.h
index a91e38f9ea752..e4efde33dd839 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ftmac.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftmac.h
@@ -4,7 +4,7 @@
  *
  *   Additional Mac-specific API.
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftmm.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftmm.h
index d145128a9bcdb..35ed039c89b8d 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ftmm.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftmm.h
@@ -4,7 +4,7 @@
  *
  *   FreeType Multiple Master font interface (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -19,8 +19,13 @@
 #ifndef FTMM_H_
 #define FTMM_H_
 
+#include 
 
-#include 
+#ifdef FREETYPE_H
+#error "freetype.h of FreeType 1 has been loaded!"
+#error "Please fix the directory search order for header files"
+#error "so that freetype.h of FreeType 2 is found first."
+#endif
 
 
 FT_BEGIN_HEADER
@@ -53,6 +58,30 @@ FT_BEGIN_HEADER
    */
 
 
+  /**************************************************************************
+   *
+   * @enum:
+   *   T1_MAX_MM_XXX
+   *
+   * @description:
+   *   Multiple Masters limits as defined in their specifications.
+   *
+   * @values:
+   *   T1_MAX_MM_AXIS ::
+   *     The maximum number of Multiple Masters axes.
+   *
+   *   T1_MAX_MM_DESIGNS ::
+   *     The maximum number of Multiple Masters designs.
+   *
+   *   T1_MAX_MM_MAP_POINTS ::
+   *     The maximum number of elements in a design map.
+   *
+   */
+#define T1_MAX_MM_AXIS         4
+#define T1_MAX_MM_DESIGNS     16
+#define T1_MAX_MM_MAP_POINTS  20
+
+
   /**************************************************************************
    *
    * @struct:
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftmodapi.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftmodapi.h
index c8f0c2c2a45ec..0ee715898f7b7 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ftmodapi.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftmodapi.h
@@ -4,7 +4,7 @@
  *
  *   FreeType modules public interface (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftmoderr.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftmoderr.h
index c8c892dcce8b2..6722fbf8b709c 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ftmoderr.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftmoderr.h
@@ -4,7 +4,7 @@
  *
  *   FreeType module error offsets (specification).
  *
- * Copyright (C) 2001-2023 by
+ * Copyright (C) 2001-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftoutln.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftoutln.h
index f9329ca40c98a..44e94b4f5bbbb 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ftoutln.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftoutln.h
@@ -5,7 +5,7 @@
  *   Support for the FT_Outline type used to store glyph shapes of
  *   most scalable font formats (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -112,7 +112,7 @@ FT_BEGIN_HEADER
    *   Degenerate contours, segments, and Bezier arcs may be reported.  In
    *   most cases, it is best to filter these out before using the outline
    *   for stroking or other path modification purposes (which may cause
-   *   degenerate segments to become non-degenrate and visible, like when
+   *   degenerate segments to become non-degenerate and visible, like when
    *   stroke caps are used or the path is otherwise outset).  Some glyph
    *   outlines may contain deliberate degenerate single points for mark
    *   attachement.
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftparams.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftparams.h
index 6a9f243bc904c..43bf69c202f76 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ftparams.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftparams.h
@@ -4,7 +4,7 @@
  *
  *   FreeType API for possible FT_Parameter tags (specification only).
  *
- * Copyright (C) 2017-2023 by
+ * Copyright (C) 2017-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftrender.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftrender.h
index 0b6fad32e8497..dc5018a1b54b6 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ftrender.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftrender.h
@@ -4,7 +4,7 @@
  *
  *   FreeType renderer modules public interface (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftsizes.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftsizes.h
index 7bfb1aed4c255..4ef5c7955dfde 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ftsizes.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftsizes.h
@@ -4,7 +4,7 @@
  *
  *   FreeType size objects management (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftsnames.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftsnames.h
index 9d5d22bb25543..d5d5cd9310329 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ftsnames.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftsnames.h
@@ -7,7 +7,7 @@
  *
  *   This is _not_ used to retrieve glyph names!
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftstroke.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftstroke.h
index b3d90802a56a1..41626dc9d7b38 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ftstroke.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftstroke.h
@@ -4,7 +4,7 @@
  *
  *   FreeType path stroker (specification).
  *
- * Copyright (C) 2002-2023 by
+ * Copyright (C) 2002-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftsynth.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftsynth.h
index af90967dda0de..43081b6c33074 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ftsynth.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftsynth.h
@@ -5,7 +5,7 @@
  *   FreeType synthesizing code for emboldening and slanting
  *   (specification).
  *
- * Copyright (C) 2000-2023 by
+ * Copyright (C) 2000-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftsystem.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftsystem.h
index 3a08f4912c982..1eacb3af398f3 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ftsystem.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftsystem.h
@@ -4,7 +4,7 @@
  *
  *   FreeType low-level system interface definition (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/fttrigon.h b/src/java.desktop/share/native/libfreetype/include/freetype/fttrigon.h
index 294981a6f3127..a5299e938d450 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/fttrigon.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/fttrigon.h
@@ -4,7 +4,7 @@
  *
  *   FreeType trigonometric functions (specification).
  *
- * Copyright (C) 2001-2023 by
+ * Copyright (C) 2001-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/fttypes.h b/src/java.desktop/share/native/libfreetype/include/freetype/fttypes.h
index 5b109f0c73c2f..27815143a6453 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/fttypes.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/fttypes.h
@@ -4,7 +4,7 @@
  *
  *   FreeType simple types definitions (specification only).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/autohint.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/autohint.h
index bf9c8b7cf2a50..8865d53b38945 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/autohint.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/autohint.h
@@ -4,7 +4,7 @@
  *
  *   High-level 'autohint' module-specific interface (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/cffotypes.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/cffotypes.h
index 50d535384989d..36b0390a5a565 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/cffotypes.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/cffotypes.h
@@ -4,7 +4,7 @@
  *
  *   Basic OpenType/CFF object type definitions (specification).
  *
- * Copyright (C) 2017-2023 by
+ * Copyright (C) 2017-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/cfftypes.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/cfftypes.h
index c2521764caa07..ef2e8e7569c34 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/cfftypes.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/cfftypes.h
@@ -5,7 +5,7 @@
  *   Basic OpenType/CFF type definitions and interface (specification
  *   only).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -248,10 +248,10 @@ FT_BEGIN_HEADER
     FT_Byte   num_family_blues;
     FT_Byte   num_family_other_blues;
 
-    FT_Pos    blue_values[14];
-    FT_Pos    other_blues[10];
-    FT_Pos    family_blues[14];
-    FT_Pos    family_other_blues[10];
+    FT_Fixed  blue_values[14];
+    FT_Fixed  other_blues[10];
+    FT_Fixed  family_blues[14];
+    FT_Fixed  family_other_blues[10];
 
     FT_Fixed  blue_scale;
     FT_Pos    blue_shift;
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/compiler-macros.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/compiler-macros.h
index 6f67650979e9e..876f66e256171 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/compiler-macros.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/compiler-macros.h
@@ -4,7 +4,7 @@
  *
  *   Compiler-specific macro definitions used internally by FreeType.
  *
- * Copyright (C) 2020-2023 by
+ * Copyright (C) 2020-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftcalc.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftcalc.h
index d9aea236024fb..71128a2df9090 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftcalc.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftcalc.h
@@ -4,7 +4,7 @@
  *
  *   Arithmetic computations (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -415,7 +415,7 @@ FT_BEGIN_HEADER
 
 #define FT_MSB( x )  ( 31 - _CountLeadingZeros( x ) )
 
-#elif defined( _M_ARM64 ) || defined( _M_ARM )
+#elif defined( _M_ARM64 ) || defined( _M_ARM ) || defined( _M_ARM64EC )
 
 #include 
 #pragma intrinsic( _CountLeadingZeros )
@@ -455,6 +455,12 @@ FT_BEGIN_HEADER
 
 #define FT_MSB( x )  FT_MSB_i386( x )
 
+#elif defined( __SunOS_5_11 )
+
+#include 
+
+#define FT_MSB( x )  ( fls( x ) - 1 )
+
 #elif defined( __DECC ) || defined( __DECCXX )
 
 #include 
@@ -489,8 +495,6 @@ FT_BEGIN_HEADER
             FT_Fixed  y );
 
 
-#if 0
-
   /**************************************************************************
    *
    * @function:
@@ -507,12 +511,11 @@ FT_BEGIN_HEADER
    *   The result of 'sqrt(x)'.
    *
    * @note:
-   *   This function is not very fast.
+   *   This function is slow and should be avoided.  Consider @FT_Hypot or
+   *   @FT_Vector_NormLen instead.
    */
-  FT_BASE( FT_Int32 )
-  FT_SqrtFixed( FT_Int32  x );
-
-#endif /* 0 */
+  FT_BASE( FT_UInt32 )
+  FT_SqrtFixed( FT_UInt32  x );
 
 
 #define INT_TO_F26DOT6( x )    ( (FT_Long)(x) * 64  )    /* << 6  */
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftdebug.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftdebug.h
index 4e013ba1e2673..d7fa8dc93cf7b 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftdebug.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftdebug.h
@@ -4,7 +4,7 @@
  *
  *   Debugging and logging component (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftdrv.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftdrv.h
index 9001c07ad0b4a..5609b3ef12bd2 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftdrv.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftdrv.h
@@ -4,7 +4,7 @@
  *
  *   FreeType internal font driver interface (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftgloadr.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftgloadr.h
index 36e5509f9eab6..f1c155b162caa 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftgloadr.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftgloadr.h
@@ -4,7 +4,7 @@
  *
  *   The FreeType glyph loader (specification).
  *
- * Copyright (C) 2002-2023 by
+ * Copyright (C) 2002-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftmemory.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftmemory.h
index 5eb1d21ff67c1..4e05a29f13a14 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftmemory.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftmemory.h
@@ -4,7 +4,7 @@
  *
  *   The FreeType memory management macros (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg
  *
  * This file is part of the FreeType project, and may only be used,
@@ -371,8 +371,11 @@ extern "C++"
 #define FT_STRDUP( dst, str )                           \
           FT_MEM_SET_ERROR( FT_MEM_STRDUP( dst, str ) )
 
-#define FT_MEM_DUP( dst, address, size )                                    \
-          (dst) = ft_mem_dup( memory, (address), (FT_ULong)(size), &error )
+#define FT_MEM_DUP( dst, address, size )                       \
+          FT_ASSIGNP_INNER( dst, ft_mem_dup( memory,           \
+                                             (address),        \
+                                             (FT_ULong)(size), \
+                                             &error ) )
 
 #define FT_DUP( dst, address, size )                           \
           FT_MEM_SET_ERROR( FT_MEM_DUP( dst, address, size ) )
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftmmtypes.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftmmtypes.h
index c4b21d6144ea0..8449e7a010d09 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftmmtypes.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftmmtypes.h
@@ -5,7 +5,7 @@
  *   OpenType Variations type definitions for internal use
  *   with the multi-masters service (specification).
  *
- * Copyright (C) 2022-2023 by
+ * Copyright (C) 2022-2024 by
  * David Turner, Robert Wilhelm, Werner Lemberg, George Williams, and
  * Dominik Röttsches.
  *
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftobjs.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftobjs.h
index 28bc9b65f0584..a1e93298fdbc4 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftobjs.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftobjs.h
@@ -4,7 +4,7 @@
  *
  *   The FreeType private base classes (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -604,12 +604,6 @@ FT_BEGIN_HEADER
 #define FT_FACE_MEMORY( x )   FT_FACE( x )->memory
 #define FT_FACE_STREAM( x )   FT_FACE( x )->stream
 
-#define FT_SIZE_FACE( x )     FT_SIZE( x )->face
-#define FT_SLOT_FACE( x )     FT_SLOT( x )->face
-
-#define FT_FACE_SLOT( x )     FT_FACE( x )->glyph
-#define FT_FACE_SIZE( x )     FT_FACE( x )->size
-
 
   /**************************************************************************
    *
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftpsprop.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftpsprop.h
index 1d5b287ad2081..4f11aa16ba111 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftpsprop.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftpsprop.h
@@ -4,7 +4,7 @@
  *
  *   Get and set properties of PostScript drivers (specification).
  *
- * Copyright (C) 2017-2023 by
+ * Copyright (C) 2017-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftrfork.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftrfork.h
index e96459921ef90..05c1d6c48b53e 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftrfork.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftrfork.h
@@ -4,7 +4,7 @@
  *
  *   Embedded resource forks accessor (specification).
  *
- * Copyright (C) 2004-2023 by
+ * Copyright (C) 2004-2024 by
  * Masatake YAMATO and Redhat K.K.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftserv.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftserv.h
index 1e85d6d3856a9..8c35dbd713950 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftserv.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftserv.h
@@ -4,7 +4,7 @@
  *
  *   The FreeType services (specification only).
  *
- * Copyright (C) 2003-2023 by
+ * Copyright (C) 2003-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftstream.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftstream.h
index 88e19287c8099..fd52f767ef774 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftstream.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftstream.h
@@ -4,7 +4,7 @@
  *
  *   Stream handling (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/fttrace.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/fttrace.h
index 319fe56fd2d37..42595a29ff33b 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/fttrace.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/fttrace.h
@@ -4,7 +4,7 @@
  *
  *   Tracing handling (specification only).
  *
- * Copyright (C) 2002-2023 by
+ * Copyright (C) 2002-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -64,6 +64,7 @@ FT_TRACE_DEF( ttbdf )     /* TrueType embedded BDF   (ttbdf.c)    */
 FT_TRACE_DEF( ttcmap )    /* charmap handler         (ttcmap.c)   */
 FT_TRACE_DEF( ttcolr )    /* glyph layer table       (ttcolr.c)   */
 FT_TRACE_DEF( ttcpal )    /* color palette table     (ttcpal.c)   */
+FT_TRACE_DEF( ttgpos )    /* GPOS handler            (ttgpos.c)   */
 FT_TRACE_DEF( ttsvg )     /* OpenType SVG table      (ttsvg.c)    */
 FT_TRACE_DEF( ttkern )    /* kerning handler         (ttkern.c)   */
 FT_TRACE_DEF( ttload )    /* basic TrueType tables   (ttload.c)   */
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftvalid.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftvalid.h
index e98ee4e473738..a1312f2aba6c1 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftvalid.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftvalid.h
@@ -4,7 +4,7 @@
  *
  *   FreeType validation support (specification).
  *
- * Copyright (C) 2004-2023 by
+ * Copyright (C) 2004-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/psaux.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/psaux.h
index dfb1987f86897..745d2cb56b77c 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/psaux.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/psaux.h
@@ -5,7 +5,7 @@
  *   Auxiliary functions and data structures related to PostScript fonts
  *   (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -225,6 +225,7 @@ FT_BEGIN_HEADER
 
   typedef enum  T1_FieldLocation_
   {
+    T1_FIELD_LOCATION_NONE = 0,
     T1_FIELD_LOCATION_CID_INFO,
     T1_FIELD_LOCATION_FONT_DICT,
     T1_FIELD_LOCATION_FONT_EXTRA,
@@ -249,6 +250,7 @@ FT_BEGIN_HEADER
   /* structure type used to model object fields */
   typedef struct  T1_FieldRec_
   {
+    FT_UInt             len;          /* field identifier length        */
     const char*         ident;        /* field identifier               */
     T1_FieldLocation    location;
     T1_FieldType        type;         /* type of field                  */
@@ -273,8 +275,9 @@ FT_BEGIN_HEADER
 
 #define T1_NEW_SIMPLE_FIELD( _ident, _type, _fname, _dict ) \
           {                                                 \
+            sizeof ( _ident ) - 1,                          \
             _ident, T1CODE, _type,                          \
-            0,                                              \
+            NULL,                                           \
             FT_FIELD_OFFSET( _fname ),                      \
             FT_FIELD_SIZE( _fname ),                        \
             0, 0,                                           \
@@ -283,6 +286,7 @@ FT_BEGIN_HEADER
 
 #define T1_NEW_CALLBACK_FIELD( _ident, _reader, _dict ) \
           {                                             \
+            sizeof ( _ident ) - 1,                      \
             _ident, T1CODE, T1_FIELD_TYPE_CALLBACK,     \
             (T1_Field_ParseFunc)_reader,                \
             0, 0,                                       \
@@ -292,8 +296,9 @@ FT_BEGIN_HEADER
 
 #define T1_NEW_TABLE_FIELD( _ident, _type, _fname, _max, _dict ) \
           {                                                      \
+            sizeof ( _ident ) - 1,                               \
             _ident, T1CODE, _type,                               \
-            0,                                                   \
+            NULL,                                                \
             FT_FIELD_OFFSET( _fname ),                           \
             FT_FIELD_SIZE_DELTA( _fname ),                       \
             _max,                                                \
@@ -303,8 +308,9 @@ FT_BEGIN_HEADER
 
 #define T1_NEW_TABLE_FIELD2( _ident, _type, _fname, _max, _dict ) \
           {                                                       \
+            sizeof ( _ident ) - 1,                                \
             _ident, T1CODE, _type,                                \
-            0,                                                    \
+            NULL,                                                 \
             FT_FIELD_OFFSET( _fname ),                            \
             FT_FIELD_SIZE_DELTA( _fname ),                        \
             _max, 0,                                              \
@@ -354,6 +360,13 @@ FT_BEGIN_HEADER
 #define T1_FIELD_CALLBACK( _ident, _name, _dict )       \
           T1_NEW_CALLBACK_FIELD( _ident, _name, _dict )
 
+#define T1_FIELD_ZERO                                         \
+          {                                                   \
+            0,                                                \
+            NULL, T1_FIELD_LOCATION_NONE, T1_FIELD_TYPE_NONE, \
+            NULL, 0, 0, 0, 0, 0                               \
+          }
+
 
   /*************************************************************************/
   /*************************************************************************/
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/pshints.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/pshints.h
index ededc4c72e7a1..dba6c7303fdcf 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/pshints.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/pshints.h
@@ -6,7 +6,7 @@
  *   recorders (specification only).  These are used to support native
  *   T1/T2 hints in the 'type1', 'cid', and 'cff' font drivers.
  *
- * Copyright (C) 2001-2023 by
+ * Copyright (C) 2001-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svbdf.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svbdf.h
index bf0c1dcc71457..89e9c2e5de80a 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svbdf.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svbdf.h
@@ -4,7 +4,7 @@
  *
  *   The FreeType BDF services (specification).
  *
- * Copyright (C) 2003-2023 by
+ * Copyright (C) 2003-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svcfftl.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svcfftl.h
index 4a20498ee0cfe..3cb483c344f87 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svcfftl.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svcfftl.h
@@ -4,7 +4,7 @@
  *
  *   The FreeType CFF tables loader service (specification).
  *
- * Copyright (C) 2017-2023 by
+ * Copyright (C) 2017-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svcid.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svcid.h
index 06d0cb8fd62ea..8362cb8724d86 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svcid.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svcid.h
@@ -4,7 +4,7 @@
  *
  *   The FreeType CID font services (specification).
  *
- * Copyright (C) 2007-2023 by
+ * Copyright (C) 2007-2024 by
  * Derek Clegg and Michael Toftdal.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svfntfmt.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svfntfmt.h
index bc45e80568fc7..6b837e79fcd43 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svfntfmt.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svfntfmt.h
@@ -4,7 +4,7 @@
  *
  *   The FreeType font format service (specification only).
  *
- * Copyright (C) 2003-2023 by
+ * Copyright (C) 2003-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svgldict.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svgldict.h
index 6437abfbf2e8b..6126ec9ada4ab 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svgldict.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svgldict.h
@@ -4,7 +4,7 @@
  *
  *   The FreeType glyph dictionary services (specification).
  *
- * Copyright (C) 2003-2023 by
+ * Copyright (C) 2003-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svgxval.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svgxval.h
index 31016afe0d040..29cf552818936 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svgxval.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svgxval.h
@@ -4,7 +4,7 @@
  *
  *   FreeType API for validating TrueTypeGX/AAT tables (specification).
  *
- * Copyright (C) 2004-2023 by
+ * Copyright (C) 2004-2024 by
  * Masatake YAMATO, Red Hat K.K.,
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svkern.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svkern.h
index bcabbc3e68fde..ac1bc30c412f0 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svkern.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svkern.h
@@ -4,7 +4,7 @@
  *
  *   The FreeType Kerning service (specification).
  *
- * Copyright (C) 2006-2023 by
+ * Copyright (C) 2006-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svmetric.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svmetric.h
index 167617ebb3d0e..8b3563b25ca4b 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svmetric.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svmetric.h
@@ -4,7 +4,7 @@
  *
  *   The FreeType services for metrics variations (specification).
  *
- * Copyright (C) 2016-2023 by
+ * Copyright (C) 2016-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svmm.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svmm.h
index 7e76ab8324e43..5288fadf3755f 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svmm.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svmm.h
@@ -4,7 +4,7 @@
  *
  *   The FreeType Multiple Masters and GX var services (specification).
  *
- * Copyright (C) 2003-2023 by
+ * Copyright (C) 2003-2024 by
  * David Turner, Robert Wilhelm, Werner Lemberg, and Dominik Röttsches.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svotval.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svotval.h
index a4683cd5fb648..7aea7ec11f02a 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svotval.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svotval.h
@@ -4,7 +4,7 @@
  *
  *   The FreeType OpenType validation service (specification).
  *
- * Copyright (C) 2004-2023 by
+ * Copyright (C) 2004-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpfr.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpfr.h
index fd189c7de773b..b2fac6d086b26 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpfr.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpfr.h
@@ -4,7 +4,7 @@
  *
  *   Internal PFR service functions (specification).
  *
- * Copyright (C) 2003-2023 by
+ * Copyright (C) 2003-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpostnm.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpostnm.h
index 2b8f6dfecfb02..d19f3adc6d5a4 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpostnm.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpostnm.h
@@ -4,7 +4,7 @@
  *
  *   The FreeType PostScript name services (specification).
  *
- * Copyright (C) 2003-2023 by
+ * Copyright (C) 2003-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svprop.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svprop.h
index 932ce32e03d78..ba39c0dd4da9e 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svprop.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svprop.h
@@ -4,7 +4,7 @@
  *
  *   The FreeType property service (specification).
  *
- * Copyright (C) 2012-2023 by
+ * Copyright (C) 2012-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpscmap.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpscmap.h
index 6e599f3aabe40..d4908ee41aa22 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpscmap.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpscmap.h
@@ -4,7 +4,7 @@
  *
  *   The FreeType PostScript charmap service (specification).
  *
- * Copyright (C) 2003-2023 by
+ * Copyright (C) 2003-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpsinfo.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpsinfo.h
index 09c4cdccc5342..2aadcdd02a1bc 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpsinfo.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpsinfo.h
@@ -4,7 +4,7 @@
  *
  *   The FreeType PostScript info service (specification).
  *
- * Copyright (C) 2003-2023 by
+ * Copyright (C) 2003-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svsfnt.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svsfnt.h
index f98df2ef5fe70..9e0f4ff202e99 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svsfnt.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svsfnt.h
@@ -4,7 +4,7 @@
  *
  *   The FreeType SFNT table loading service (specification).
  *
- * Copyright (C) 2003-2023 by
+ * Copyright (C) 2003-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svttcmap.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svttcmap.h
index 5f9eb02d66558..250886bcc5dba 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svttcmap.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svttcmap.h
@@ -4,7 +4,7 @@
  *
  *   The FreeType TrueType/sfnt cmap extra information service.
  *
- * Copyright (C) 2003-2023 by
+ * Copyright (C) 2003-2024 by
  * Masatake YAMATO, Redhat K.K.,
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svtteng.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svtteng.h
index ad577cb2904d5..14967529a9ac7 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svtteng.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svtteng.h
@@ -4,7 +4,7 @@
  *
  *   The FreeType TrueType engine query service (specification).
  *
- * Copyright (C) 2006-2023 by
+ * Copyright (C) 2006-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svttglyf.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svttglyf.h
index ca6fff74444b3..f190b3985d021 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svttglyf.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svttglyf.h
@@ -4,7 +4,7 @@
  *
  *   The FreeType TrueType glyph service.
  *
- * Copyright (C) 2007-2023 by
+ * Copyright (C) 2007-2024 by
  * David Turner.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svwinfnt.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svwinfnt.h
index 002923f8c914a..49f3fb7f775be 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svwinfnt.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svwinfnt.h
@@ -4,7 +4,7 @@
  *
  *   The FreeType Windows FNT/FONT service (specification).
  *
- * Copyright (C) 2003-2023 by
+ * Copyright (C) 2003-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/sfnt.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/sfnt.h
index a2d4e15baafde..35e4e73af02f8 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/sfnt.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/sfnt.h
@@ -4,7 +4,7 @@
  *
  *   High-level 'sfnt' driver interface (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -924,6 +924,7 @@ FT_BEGIN_HEADER
     /* this field was called `load_kerning' up to version 2.1.10 */
     TT_Load_Table_Func  load_kern;
 
+    TT_Load_Table_Func  load_gpos;
     TT_Load_Table_Func  load_gasp;
     TT_Load_Table_Func  load_pclt;
 
@@ -944,6 +945,8 @@ FT_BEGIN_HEADER
 
     /* new elements introduced after version 2.1.10 */
 
+    TT_Face_GetKerningFunc  get_gpos_kerning;
+
     /* load the font directory, i.e., the offset table and */
     /* the table directory                                 */
     TT_Load_Table_Func    load_font_dir;
@@ -1002,6 +1005,7 @@ FT_BEGIN_HEADER
           load_name_,                    \
           free_name_,                    \
           load_kern_,                    \
+          load_gpos_,                    \
           load_gasp_,                    \
           load_pclt_,                    \
           load_bhed_,                    \
@@ -1009,6 +1013,7 @@ FT_BEGIN_HEADER
           get_psname_,                   \
           free_psnames_,                 \
           get_kerning_,                  \
+          get_gpos_kerning_,             \
           load_font_dir_,                \
           load_hmtx_,                    \
           load_eblc_,                    \
@@ -1050,6 +1055,7 @@ FT_BEGIN_HEADER
     load_name_,                          \
     free_name_,                          \
     load_kern_,                          \
+    load_gpos_,                          \
     load_gasp_,                          \
     load_pclt_,                          \
     load_bhed_,                          \
@@ -1057,6 +1063,7 @@ FT_BEGIN_HEADER
     get_psname_,                         \
     free_psnames_,                       \
     get_kerning_,                        \
+    get_gpos_kerning_,                   \
     load_font_dir_,                      \
     load_hmtx_,                          \
     load_eblc_,                          \
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/svginterface.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/svginterface.h
index f464b2c0583a8..68c99efb10ab8 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/svginterface.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/svginterface.h
@@ -4,7 +4,7 @@
  *
  *   Interface of ot-svg module (specification only).
  *
- * Copyright (C) 2022-2023 by
+ * Copyright (C) 2022-2024 by
  * David Turner, Robert Wilhelm, Werner Lemberg, and Moazin Khatti.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/t1types.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/t1types.h
index b9c94398fd155..1821ae5cc8390 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/t1types.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/t1types.h
@@ -5,7 +5,7 @@
  *   Basic Type1/Type2 type definitions and interface (specification
  *   only).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -21,7 +21,7 @@
 #define T1TYPES_H_
 
 
-#include 
+#include 
 #include 
 #include 
 #include 
@@ -137,6 +137,54 @@ FT_BEGIN_HEADER
   } CID_SubrsRec, *CID_Subrs;
 
 
+  /* this structure is used to store the BlendDesignMap entry for an axis */
+  typedef struct  PS_DesignMap_
+  {
+    FT_Byte    num_points;
+    FT_Long*   design_points;
+    FT_Fixed*  blend_points;
+
+  } PS_DesignMapRec, *PS_DesignMap;
+
+  /* backward compatible definition */
+  typedef PS_DesignMapRec  T1_DesignMap;
+
+
+  typedef struct  PS_BlendRec_
+  {
+    FT_UInt          num_designs;
+    FT_UInt          num_axis;
+
+    FT_String*       axis_names[T1_MAX_MM_AXIS];
+    FT_Fixed*        design_pos[T1_MAX_MM_DESIGNS];
+    PS_DesignMapRec  design_map[T1_MAX_MM_AXIS];
+
+    FT_Fixed*        weight_vector;
+    FT_Fixed*        default_weight_vector;
+
+    PS_FontInfo      font_infos[T1_MAX_MM_DESIGNS + 1];
+    PS_Private       privates  [T1_MAX_MM_DESIGNS + 1];
+
+    FT_ULong         blend_bitflags;
+
+    FT_BBox*         bboxes    [T1_MAX_MM_DESIGNS + 1];
+
+    /* since 2.3.0 */
+
+    /* undocumented, optional: the default design instance;   */
+    /* corresponds to default_weight_vector --                */
+    /* num_default_design_vector == 0 means it is not present */
+    /* in the font and associated metrics files               */
+    FT_UInt          default_design_vector[T1_MAX_MM_DESIGNS];
+    FT_UInt          num_default_design_vector;
+
+  } PS_BlendRec, *PS_Blend;
+
+
+  /* backward compatible definition */
+  typedef PS_BlendRec  T1_Blend;
+
+
   /*************************************************************************/
   /*************************************************************************/
   /*************************************************************************/
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/tttypes.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/tttypes.h
index b9788c7831ec5..7053e656a7e5d 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/tttypes.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/tttypes.h
@@ -5,7 +5,7 @@
  *   Basic SFNT/TrueType type definitions and interface (specification
  *   only).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -24,6 +24,7 @@
 #include 
 #include 
 #include 
+#include "freetype/fttypes.h"
 
 #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
 #include 
@@ -1581,6 +1582,11 @@ FT_BEGIN_HEADER
     FT_UInt32             kern_avail_bits;
     FT_UInt32             kern_order_bits;
 
+#ifdef TT_CONFIG_OPTION_GPOS_KERNING
+    FT_Byte*              gpos_table;
+    FT_Bool               gpos_kerning_available;
+#endif
+
 #ifdef TT_CONFIG_OPTION_BDF
     TT_BDFRec             bdf;
 #endif /* TT_CONFIG_OPTION_BDF */
@@ -1649,9 +1655,9 @@ FT_BEGIN_HEADER
   {
     FT_Memory   memory;
     FT_UShort   max_points;
-    FT_Short    max_contours;
+    FT_UShort   max_contours;
     FT_UShort   n_points;    /* number of points in zone    */
-    FT_Short    n_contours;  /* number of contours          */
+    FT_UShort   n_contours;  /* number of contours          */
 
     FT_Vector*  org;         /* original point coordinates  */
     FT_Vector*  cur;         /* current point coordinates   */
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/wofftypes.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/wofftypes.h
index 0c1d8eeaf8c8b..4a169d12f57e5 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/wofftypes.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/wofftypes.h
@@ -5,7 +5,7 @@
  *   Basic WOFF/WOFF2 type definitions and interface (specification
  *   only).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/otsvg.h b/src/java.desktop/share/native/libfreetype/include/freetype/otsvg.h
index bfe9a6ab74e47..9d356938cc70e 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/otsvg.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/otsvg.h
@@ -4,7 +4,7 @@
  *
  *   Interface for OT-SVG support related things (specification).
  *
- * Copyright (C) 2022-2023 by
+ * Copyright (C) 2022-2024 by
  * David Turner, Robert Wilhelm, Werner Lemberg, and Moazin Khatti.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/t1tables.h b/src/java.desktop/share/native/libfreetype/include/freetype/t1tables.h
index 1aecfbbd902f2..fbd558aa34d05 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/t1tables.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/t1tables.h
@@ -5,7 +5,7 @@
  *   Basic Type 1/Type 2 tables definitions and interface (specification
  *   only).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -269,64 +269,6 @@ FT_BEGIN_HEADER
   /* */
 
 
-  /* maximum number of Multiple Masters designs, as defined in the spec */
-#define T1_MAX_MM_DESIGNS     16
-
-  /* maximum number of Multiple Masters axes, as defined in the spec */
-#define T1_MAX_MM_AXIS        4
-
-  /* maximum number of elements in a design map */
-#define T1_MAX_MM_MAP_POINTS  20
-
-
-  /* this structure is used to store the BlendDesignMap entry for an axis */
-  typedef struct  PS_DesignMap_
-  {
-    FT_Byte    num_points;
-    FT_Long*   design_points;
-    FT_Fixed*  blend_points;
-
-  } PS_DesignMapRec, *PS_DesignMap;
-
-  /* backward compatible definition */
-  typedef PS_DesignMapRec  T1_DesignMap;
-
-
-  typedef struct  PS_BlendRec_
-  {
-    FT_UInt          num_designs;
-    FT_UInt          num_axis;
-
-    FT_String*       axis_names[T1_MAX_MM_AXIS];
-    FT_Fixed*        design_pos[T1_MAX_MM_DESIGNS];
-    PS_DesignMapRec  design_map[T1_MAX_MM_AXIS];
-
-    FT_Fixed*        weight_vector;
-    FT_Fixed*        default_weight_vector;
-
-    PS_FontInfo      font_infos[T1_MAX_MM_DESIGNS + 1];
-    PS_Private       privates  [T1_MAX_MM_DESIGNS + 1];
-
-    FT_ULong         blend_bitflags;
-
-    FT_BBox*         bboxes    [T1_MAX_MM_DESIGNS + 1];
-
-    /* since 2.3.0 */
-
-    /* undocumented, optional: the default design instance;   */
-    /* corresponds to default_weight_vector --                */
-    /* num_default_design_vector == 0 means it is not present */
-    /* in the font and associated metrics files               */
-    FT_UInt          default_design_vector[T1_MAX_MM_DESIGNS];
-    FT_UInt          num_default_design_vector;
-
-  } PS_BlendRec, *PS_Blend;
-
-
-  /* backward compatible definition */
-  typedef PS_BlendRec  T1_Blend;
-
-
   /**************************************************************************
    *
    * @struct:
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ttnameid.h b/src/java.desktop/share/native/libfreetype/include/freetype/ttnameid.h
index e31c68b9baf34..d5d470e380f44 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/ttnameid.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/ttnameid.h
@@ -4,7 +4,7 @@
  *
  *   TrueType name ID definitions (specification only).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/tttables.h b/src/java.desktop/share/native/libfreetype/include/freetype/tttables.h
index a9f60e76201b1..2cf0ff1bc61e4 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/tttables.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/tttables.h
@@ -5,7 +5,7 @@
  *   Basic SFNT/TrueType tables definitions and interface
  *   (specification only).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -838,8 +838,9 @@ FT_BEGIN_HEADER
    *     The target charmap.
    *
    * @return:
-   *   The format of `charmap`.  If `charmap` doesn't belong to an SFNT face,
-   *   return -1.
+   *   The format of `charmap`.  If `charmap` doesn't belong to an SFNT face
+   *   (including the synthetic Unicode charmap sometimes created by
+   *   FreeType), return -1.
    */
   FT_EXPORT( FT_Long )
   FT_Get_CMap_Format( FT_CharMap  charmap );
diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/tttags.h b/src/java.desktop/share/native/libfreetype/include/freetype/tttags.h
index 9bf4fca23fb3a..da0af5d3f2353 100644
--- a/src/java.desktop/share/native/libfreetype/include/freetype/tttags.h
+++ b/src/java.desktop/share/native/libfreetype/include/freetype/tttags.h
@@ -4,7 +4,7 @@
  *
  *   Tags for TrueType and OpenType tables (specification only).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/include/ft2build.h b/src/java.desktop/share/native/libfreetype/include/ft2build.h
index 58491ceea1fb8..d3d7685039c4e 100644
--- a/src/java.desktop/share/native/libfreetype/include/ft2build.h
+++ b/src/java.desktop/share/native/libfreetype/include/ft2build.h
@@ -4,7 +4,7 @@
  *
  *   FreeType 2 build and setup macros.
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afblue.c b/src/java.desktop/share/native/libfreetype/src/autofit/afblue.c
index d7655b9b99e16..ea83969cdc973 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/afblue.c
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/afblue.c
@@ -7,7 +7,7 @@
  *
  *   Auto-fitter data for blue strings (body).
  *
- * Copyright (C) 2013-2023 by
+ * Copyright (C) 2013-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afblue.cin b/src/java.desktop/share/native/libfreetype/src/autofit/afblue.cin
index d561c5093b78b..d2270fac74438 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/afblue.cin
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/afblue.cin
@@ -4,7 +4,7 @@
  *
  *   Auto-fitter data for blue strings (body).
  *
- * Copyright (C) 2013-2023 by
+ * Copyright (C) 2013-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afblue.dat b/src/java.desktop/share/native/libfreetype/src/autofit/afblue.dat
index 8299baa2591d3..88bab2632abef 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/afblue.dat
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/afblue.dat
@@ -2,7 +2,7 @@
 //
 //   Auto-fitter data for blue strings.
 //
-// Copyright (C) 2013-2023 by
+// Copyright (C) 2013-2024 by
 // David Turner, Robert Wilhelm, and Werner Lemberg.
 //
 // This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afblue.h b/src/java.desktop/share/native/libfreetype/src/autofit/afblue.h
index 76f2f47cb0083..2aa9d0984ef75 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/afblue.h
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/afblue.h
@@ -7,7 +7,7 @@
  *
  *   Auto-fitter data for blue strings (specification).
  *
- * Copyright (C) 2013-2023 by
+ * Copyright (C) 2013-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afblue.hin b/src/java.desktop/share/native/libfreetype/src/autofit/afblue.hin
index 6a31298e65fde..38031505a85bf 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/afblue.hin
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/afblue.hin
@@ -4,7 +4,7 @@
  *
  *   Auto-fitter data for blue strings (specification).
  *
- * Copyright (C) 2013-2023 by
+ * Copyright (C) 2013-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afcjk.c b/src/java.desktop/share/native/libfreetype/src/autofit/afcjk.c
index f414289adcd25..869b60487c21c 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/afcjk.c
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/afcjk.c
@@ -4,7 +4,7 @@
  *
  *   Auto-fitter hinting routines for CJK writing system (body).
  *
- * Copyright (C) 2006-2023 by
+ * Copyright (C) 2006-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afcjk.h b/src/java.desktop/share/native/libfreetype/src/autofit/afcjk.h
index f380ef6e03211..bc5aaf12e6ee8 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/afcjk.h
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/afcjk.h
@@ -4,7 +4,7 @@
  *
  *   Auto-fitter hinting routines for CJK writing system (specification).
  *
- * Copyright (C) 2006-2023 by
+ * Copyright (C) 2006-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -84,7 +84,7 @@ FT_BEGIN_HEADER
     /* used for horizontal metrics too for CJK */
     FT_Bool        control_overshoot;
     FT_UInt        blue_count;
-    AF_CJKBlueRec  blues[AF_BLUE_STRINGSET_MAX];
+    AF_CJKBlueRec  blues[AF_BLUE_STRINGSET_MAX_LEN];
 
     FT_Fixed       org_scale;
     FT_Pos         org_delta;
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afcover.h b/src/java.desktop/share/native/libfreetype/src/autofit/afcover.h
index 102ed42782872..7980cf2e97969 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/afcover.h
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/afcover.h
@@ -4,7 +4,7 @@
  *
  *   Auto-fitter coverages (specification only).
  *
- * Copyright (C) 2013-2023 by
+ * Copyright (C) 2013-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afdummy.c b/src/java.desktop/share/native/libfreetype/src/autofit/afdummy.c
index a4629b528dc63..ad667d2edc796 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/afdummy.c
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/afdummy.c
@@ -5,7 +5,7 @@
  *   Auto-fitter dummy routines to be used if no hinting should be
  *   performed (body).
  *
- * Copyright (C) 2003-2023 by
+ * Copyright (C) 2003-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afdummy.h b/src/java.desktop/share/native/libfreetype/src/autofit/afdummy.h
index a7af3f62c9eca..613c2f88a3898 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/afdummy.h
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/afdummy.h
@@ -5,7 +5,7 @@
  *   Auto-fitter dummy routines to be used if no hinting should be
  *   performed (specification).
  *
- * Copyright (C) 2003-2023 by
+ * Copyright (C) 2003-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/aferrors.h b/src/java.desktop/share/native/libfreetype/src/autofit/aferrors.h
index 88faf05c95005..ae584ff06db62 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/aferrors.h
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/aferrors.h
@@ -4,7 +4,7 @@
  *
  *   Autofitter error codes (specification only).
  *
- * Copyright (C) 2005-2023 by
+ * Copyright (C) 2005-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afglobal.c b/src/java.desktop/share/native/libfreetype/src/autofit/afglobal.c
index b1957570f03bf..b7403fa65e1b5 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/afglobal.c
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/afglobal.c
@@ -4,7 +4,7 @@
  *
  *   Auto-fitter routines to compute global hinting values (body).
  *
- * Copyright (C) 2003-2023 by
+ * Copyright (C) 2003-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afglobal.h b/src/java.desktop/share/native/libfreetype/src/autofit/afglobal.h
index 66170e419ddc9..ddb54c89b2769 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/afglobal.h
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/afglobal.h
@@ -5,7 +5,7 @@
  *   Auto-fitter routines to compute global hinting values
  *   (specification).
  *
- * Copyright (C) 2003-2023 by
+ * Copyright (C) 2003-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afhints.c b/src/java.desktop/share/native/libfreetype/src/autofit/afhints.c
index e4a378fbf74ca..96ffe343aa441 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/afhints.c
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/afhints.c
@@ -4,7 +4,7 @@
  *
  *   Auto-fitter hinting routines (body).
  *
- * Copyright (C) 2003-2023 by
+ * Copyright (C) 2003-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -979,8 +979,8 @@
       /* compute coordinates & Bezier flags, next and prev */
       {
         FT_Vector*  vec           = outline->points;
-        char*       tag           = outline->tags;
-        FT_Short    endpoint      = outline->contours[0];
+        FT_Byte*    tag           = outline->tags;
+        FT_UShort   endpoint      = outline->contours[0];
         AF_Point    end           = points + endpoint;
         AF_Point    prev          = end;
         FT_Int      contour_index = 0;
@@ -1046,16 +1046,16 @@
 
       /* set up the contours array */
       {
-        AF_Point*  contour       = hints->contours;
-        AF_Point*  contour_limit = contour + hints->num_contours;
-        short*     end           = outline->contours;
-        short      idx           = 0;
+        AF_Point*   contour       = hints->contours;
+        AF_Point*   contour_limit = contour + hints->num_contours;
+        FT_UShort*  end           = outline->contours;
+        FT_Int      idx           = 0;
 
 
         for ( ; contour < contour_limit; contour++, end++ )
         {
           contour[0] = points + idx;
-          idx        = (short)( end[0] + 1 );
+          idx        = *end + 1;
         }
       }
 
@@ -1292,7 +1292,7 @@
     AF_Point    point = hints->points;
     AF_Point    limit = point + hints->num_points;
     FT_Vector*  vec   = outline->points;
-    char*       tag   = outline->tags;
+    FT_Byte*    tag   = outline->tags;
 
 
     for ( ; point < limit; point++, vec++, tag++ )
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afhints.h b/src/java.desktop/share/native/libfreetype/src/autofit/afhints.h
index d1cf9529bf12d..76fe83006a52a 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/afhints.h
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/afhints.h
@@ -4,7 +4,7 @@
  *
  *   Auto-fitter hinting routines (specification).
  *
- * Copyright (C) 2003-2023 by
+ * Copyright (C) 2003-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afindic.c b/src/java.desktop/share/native/libfreetype/src/autofit/afindic.c
index 7fb12c63d5a35..c6d23efd86f58 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/afindic.c
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/afindic.c
@@ -4,7 +4,7 @@
  *
  *   Auto-fitter hinting routines for Indic writing system (body).
  *
- * Copyright (C) 2007-2023 by
+ * Copyright (C) 2007-2024 by
  * Rahul Bhalerao , .
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afindic.h b/src/java.desktop/share/native/libfreetype/src/autofit/afindic.h
index 3eb67f63b0013..a7f73f25153a1 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/afindic.h
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/afindic.h
@@ -5,7 +5,7 @@
  *   Auto-fitter hinting routines for Indic writing system
  *   (specification).
  *
- * Copyright (C) 2007-2023 by
+ * Copyright (C) 2007-2024 by
  * Rahul Bhalerao , .
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/aflatin.c b/src/java.desktop/share/native/libfreetype/src/autofit/aflatin.c
index b86367aa94dc6..89287f7ea5a65 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/aflatin.c
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/aflatin.c
@@ -4,7 +4,7 @@
  *
  *   Auto-fitter hinting routines for latin writing system (body).
  *
- * Copyright (C) 2003-2023 by
+ * Copyright (C) 2003-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -981,7 +981,7 @@
       /* `ref' and `shoot' values of two blue zones must not overlap */
 
       FT_UInt       i;
-      AF_LatinBlue  blue_sorted[AF_BLUE_STRINGSET_MAX_LEN + 2];
+      AF_LatinBlue  blue_sorted[AF_BLUE_STRINGSET_MAX_LEN];
 
 
       for ( i = 0; i < axis->blue_count; i++ )
@@ -1263,10 +1263,9 @@
               max_height = FT_MAX( max_height, -Axis->blues[nn].descender );
             }
 
-            dist  = FT_ABS( FT_MulFix( max_height, new_scale - scale ) );
-            dist &= ~127;
+            dist  = FT_MulFix( max_height, new_scale - scale );
 
-            if ( dist == 0 )
+            if ( -128 < dist && dist < 128 )
             {
               FT_TRACE5(( "af_latin_metrics_scale_dim:"
                           " x height alignment (style `%s'):\n",
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/aflatin.h b/src/java.desktop/share/native/libfreetype/src/autofit/aflatin.h
index 31aa91d3bdb05..54e5061502147 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/aflatin.h
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/aflatin.h
@@ -5,7 +5,7 @@
  *   Auto-fitter hinting routines for latin writing system
  *   (specification).
  *
- * Copyright (C) 2003-2023 by
+ * Copyright (C) 2003-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -98,7 +98,7 @@ FT_BEGIN_HEADER
 
     /* ignored for horizontal metrics */
     FT_UInt          blue_count;
-    AF_LatinBlueRec  blues[AF_BLUE_STRINGSET_MAX];
+    AF_LatinBlueRec  blues[AF_BLUE_STRINGSET_MAX_LEN];
 
     FT_Fixed         org_scale;
     FT_Pos           org_delta;
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afloader.c b/src/java.desktop/share/native/libfreetype/src/autofit/afloader.c
index 7c47d562af66f..af1d59a689696 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/afloader.c
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/afloader.c
@@ -4,7 +4,7 @@
  *
  *   Auto-fitter glyph loading routines (body).
  *
- * Copyright (C) 2003-2023 by
+ * Copyright (C) 2003-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afloader.h b/src/java.desktop/share/native/libfreetype/src/autofit/afloader.h
index e4e197e374fd3..99f0e15f92b64 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/afloader.h
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/afloader.h
@@ -4,7 +4,7 @@
  *
  *   Auto-fitter glyph loading routines (specification).
  *
- * Copyright (C) 2003-2023 by
+ * Copyright (C) 2003-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afmodule.c b/src/java.desktop/share/native/libfreetype/src/autofit/afmodule.c
index 20a6b96bc4ff4..726f6ca2b78b4 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/afmodule.c
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/afmodule.c
@@ -4,7 +4,7 @@
  *
  *   Auto-fitter module implementation (body).
  *
- * Copyright (C) 2003-2023 by
+ * Copyright (C) 2003-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -438,7 +438,7 @@
     AF_Module  module = (AF_Module)module_;
 
     FT_Error   error  = FT_Err_Ok;
-    FT_Memory  memory = module->root.library->memory;
+    FT_Memory  memory = module->root.memory;
 
 #ifdef FT_DEBUG_AUTOFIT
 
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afmodule.h b/src/java.desktop/share/native/libfreetype/src/autofit/afmodule.h
index 4b8b4562c67c7..91a1abfef1fd7 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/afmodule.h
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/afmodule.h
@@ -4,7 +4,7 @@
  *
  *   Auto-fitter module implementation (specification).
  *
- * Copyright (C) 2003-2023 by
+ * Copyright (C) 2003-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afranges.c b/src/java.desktop/share/native/libfreetype/src/autofit/afranges.c
index cfcaf340a79e6..007b43281898a 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/afranges.c
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/afranges.c
@@ -4,7 +4,7 @@
  *
  *   Auto-fitter Unicode script ranges (body).
  *
- * Copyright (C) 2013-2023 by
+ * Copyright (C) 2013-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afranges.h b/src/java.desktop/share/native/libfreetype/src/autofit/afranges.h
index 5775738bc0bb0..813b3ee78ef66 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/afranges.h
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/afranges.h
@@ -4,7 +4,7 @@
  *
  *   Auto-fitter Unicode script ranges (specification).
  *
- * Copyright (C) 2013-2023 by
+ * Copyright (C) 2013-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afscript.h b/src/java.desktop/share/native/libfreetype/src/autofit/afscript.h
index 3a101937d7032..0a83d771501f5 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/afscript.h
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/afscript.h
@@ -4,7 +4,7 @@
  *
  *   Auto-fitter scripts (specification only).
  *
- * Copyright (C) 2013-2023 by
+ * Copyright (C) 2013-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afshaper.c b/src/java.desktop/share/native/libfreetype/src/autofit/afshaper.c
index abc6f1d292d42..df0f46ada898a 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/afshaper.c
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/afshaper.c
@@ -4,7 +4,7 @@
  *
  *   HarfBuzz interface for accessing OpenType features (body).
  *
- * Copyright (C) 2013-2023 by
+ * Copyright (C) 2013-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afshaper.h b/src/java.desktop/share/native/libfreetype/src/autofit/afshaper.h
index 054a18ffbc2d3..2eb03bb5d9883 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/afshaper.h
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/afshaper.h
@@ -4,7 +4,7 @@
  *
  *   HarfBuzz interface for accessing OpenType features (specification).
  *
- * Copyright (C) 2013-2023 by
+ * Copyright (C) 2013-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afstyles.h b/src/java.desktop/share/native/libfreetype/src/autofit/afstyles.h
index 73ebef01716c3..7a33f37a85684 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/afstyles.h
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/afstyles.h
@@ -4,7 +4,7 @@
  *
  *   Auto-fitter styles (specification only).
  *
- * Copyright (C) 2013-2023 by
+ * Copyright (C) 2013-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/aftypes.h b/src/java.desktop/share/native/libfreetype/src/autofit/aftypes.h
index 661519449653d..27e4185e9f857 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/aftypes.h
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/aftypes.h
@@ -4,7 +4,7 @@
  *
  *   Auto-fitter types (specification only).
  *
- * Copyright (C) 2003-2023 by
+ * Copyright (C) 2003-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afws-decl.h b/src/java.desktop/share/native/libfreetype/src/autofit/afws-decl.h
index 48c888afed88e..b78745af74e9e 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/afws-decl.h
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/afws-decl.h
@@ -4,7 +4,7 @@
  *
  *   Auto-fitter writing system declarations (specification only).
  *
- * Copyright (C) 2013-2023 by
+ * Copyright (C) 2013-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afws-iter.h b/src/java.desktop/share/native/libfreetype/src/autofit/afws-iter.h
index a0a686f8cee7f..c86d609a35231 100644
--- a/src/java.desktop/share/native/libfreetype/src/autofit/afws-iter.h
+++ b/src/java.desktop/share/native/libfreetype/src/autofit/afws-iter.h
@@ -4,7 +4,7 @@
  *
  *   Auto-fitter writing systems iterator (specification only).
  *
- * Copyright (C) 2013-2023 by
+ * Copyright (C) 2013-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftadvanc.c b/src/java.desktop/share/native/libfreetype/src/base/ftadvanc.c
index de25476fe9287..717f7d08b3549 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftadvanc.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftadvanc.c
@@ -4,7 +4,7 @@
  *
  *   Quick computation of advance widths (body).
  *
- * Copyright (C) 2008-2023 by
+ * Copyright (C) 2008-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftbase.h b/src/java.desktop/share/native/libfreetype/src/base/ftbase.h
index 00790d3b226f6..1d98b26dd5109 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftbase.h
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftbase.h
@@ -4,7 +4,7 @@
  *
  *   Private functions used in the `base' module (specification).
  *
- * Copyright (C) 2008-2023 by
+ * Copyright (C) 2008-2024 by
  * David Turner, Robert Wilhelm, Werner Lemberg, and suzuki toshiya.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftbbox.c b/src/java.desktop/share/native/libfreetype/src/base/ftbbox.c
index 385fea4040103..d6aa5d56df878 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftbbox.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftbbox.c
@@ -4,7 +4,7 @@
  *
  *   FreeType bbox computation (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used
@@ -489,7 +489,7 @@
       return FT_THROW( Invalid_Outline );
 
     /* if outline is empty, return (0,0,0,0) */
-    if ( outline->n_points == 0 || outline->n_contours <= 0 )
+    if ( outline->n_points == 0 || outline->n_contours == 0 )
     {
       abbox->xMin = abbox->xMax = 0;
       abbox->yMin = abbox->yMax = 0;
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftbitmap.c b/src/java.desktop/share/native/libfreetype/src/base/ftbitmap.c
index 1c93648dcbc88..4be145679fd50 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftbitmap.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftbitmap.c
@@ -4,7 +4,7 @@
  *
  *   FreeType utility functions for bitmaps (body).
  *
- * Copyright (C) 2004-2023 by
+ * Copyright (C) 2004-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftcalc.c b/src/java.desktop/share/native/libfreetype/src/base/ftcalc.c
index c5bc7e3b14eed..92de09ed8770c 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftcalc.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftcalc.c
@@ -4,7 +4,7 @@
  *
  *   Arithmetic computations (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -69,13 +69,15 @@
 
   /* transfer sign, leaving a positive number;                        */
   /* we need an unsigned value to safely negate INT_MIN (or LONG_MIN) */
-#define FT_MOVE_SIGN( x, x_unsigned, s ) \
-  FT_BEGIN_STMNT                         \
-    if ( x < 0 )                         \
-    {                                    \
-      x_unsigned = 0U - (x_unsigned);    \
-      s          = -s;                   \
-    }                                    \
+#define FT_MOVE_SIGN( utype, x, x_unsigned, s ) \
+  FT_BEGIN_STMNT                                \
+    if ( x < 0 )                                \
+    {                                           \
+      x_unsigned = 0U - (utype)x;               \
+      s          = -s;                          \
+    }                                           \
+    else                                        \
+      x_unsigned = (utype)x;                    \
   FT_END_STMNT
 
   /* The following three functions are available regardless of whether */
@@ -179,13 +181,9 @@
     FT_Long    d_;
 
 
-    a = (FT_UInt64)a_;
-    b = (FT_UInt64)b_;
-    c = (FT_UInt64)c_;
-
-    FT_MOVE_SIGN( a_, a, s );
-    FT_MOVE_SIGN( b_, b, s );
-    FT_MOVE_SIGN( c_, c, s );
+    FT_MOVE_SIGN( FT_UInt64, a_, a, s );
+    FT_MOVE_SIGN( FT_UInt64, b_, b, s );
+    FT_MOVE_SIGN( FT_UInt64, c_, c, s );
 
     d = c > 0 ? ( a * b + ( c >> 1 ) ) / c
               : 0x7FFFFFFFUL;
@@ -208,13 +206,9 @@
     FT_Long    d_;
 
 
-    a = (FT_UInt64)a_;
-    b = (FT_UInt64)b_;
-    c = (FT_UInt64)c_;
-
-    FT_MOVE_SIGN( a_, a, s );
-    FT_MOVE_SIGN( b_, b, s );
-    FT_MOVE_SIGN( c_, c, s );
+    FT_MOVE_SIGN( FT_UInt64, a_, a, s );
+    FT_MOVE_SIGN( FT_UInt64, b_, b, s );
+    FT_MOVE_SIGN( FT_UInt64, c_, c, s );
 
     d = c > 0 ? a * b / c
               : 0x7FFFFFFFUL;
@@ -257,11 +251,8 @@
     FT_Long    q_;
 
 
-    a = (FT_UInt64)a_;
-    b = (FT_UInt64)b_;
-
-    FT_MOVE_SIGN( a_, a, s );
-    FT_MOVE_SIGN( b_, b, s );
+    FT_MOVE_SIGN( FT_UInt64, a_, a, s );
+    FT_MOVE_SIGN( FT_UInt64, b_, b, s );
 
     q = b > 0 ? ( ( a << 16 ) + ( b >> 1 ) ) / b
               : 0x7FFFFFFFUL;
@@ -422,13 +413,9 @@
 
     /* XXX: this function does not allow 64-bit arguments */
 
-    a = (FT_UInt32)a_;
-    b = (FT_UInt32)b_;
-    c = (FT_UInt32)c_;
-
-    FT_MOVE_SIGN( a_, a, s );
-    FT_MOVE_SIGN( b_, b, s );
-    FT_MOVE_SIGN( c_, c, s );
+    FT_MOVE_SIGN( FT_UInt32, a_, a, s );
+    FT_MOVE_SIGN( FT_UInt32, b_, b, s );
+    FT_MOVE_SIGN( FT_UInt32, c_, c, s );
 
     if ( c == 0 )
       a = 0x7FFFFFFFUL;
@@ -470,13 +457,9 @@
 
     /* XXX: this function does not allow 64-bit arguments */
 
-    a = (FT_UInt32)a_;
-    b = (FT_UInt32)b_;
-    c = (FT_UInt32)c_;
-
-    FT_MOVE_SIGN( a_, a, s );
-    FT_MOVE_SIGN( b_, b, s );
-    FT_MOVE_SIGN( c_, c, s );
+    FT_MOVE_SIGN( FT_UInt32, a_, a, s );
+    FT_MOVE_SIGN( FT_UInt32, b_, b, s );
+    FT_MOVE_SIGN( FT_UInt32, c_, c, s );
 
     if ( c == 0 )
       a = 0x7FFFFFFFUL;
@@ -575,11 +558,8 @@
 
     /* XXX: this function does not allow 64-bit arguments */
 
-    a = (FT_UInt32)a_;
-    b = (FT_UInt32)b_;
-
-    FT_MOVE_SIGN( a_, a, s );
-    FT_MOVE_SIGN( b_, b, s );
+    FT_MOVE_SIGN( FT_UInt32, a_, a, s );
+    FT_MOVE_SIGN( FT_UInt32, b_, b, s );
 
     if ( a + ( b >> 8 ) <= 8190UL )
       a = ( a * b + 0x8000UL ) >> 16;
@@ -614,11 +594,8 @@
 
     /* XXX: this function does not allow 64-bit arguments */
 
-    a = (FT_UInt32)a_;
-    b = (FT_UInt32)b_;
-
-    FT_MOVE_SIGN( a_, a, s );
-    FT_MOVE_SIGN( b_, b, s );
+    FT_MOVE_SIGN( FT_UInt32, a_, a, s );
+    FT_MOVE_SIGN( FT_UInt32, b_, b, s );
 
     if ( b == 0 )
     {
@@ -829,11 +806,8 @@
     FT_Int     sx = 1, sy = 1, shift;
 
 
-    x = (FT_UInt32)x_;
-    y = (FT_UInt32)y_;
-
-    FT_MOVE_SIGN( x_, x, sx );
-    FT_MOVE_SIGN( y_, y, sy );
+    FT_MOVE_SIGN( FT_UInt32, x_, x, sx );
+    FT_MOVE_SIGN( FT_UInt32, y_, y, sy );
 
     /* trivial cases */
     if ( x == 0 )
@@ -913,43 +887,71 @@
   }
 
 
-#if 0
-
   /* documentation is in ftcalc.h */
 
-  FT_BASE_DEF( FT_Int32 )
-  FT_SqrtFixed( FT_Int32  x )
+  FT_BASE_DEF( FT_UInt32 )
+  FT_SqrtFixed( FT_UInt32  v )
   {
-    FT_UInt32  root, rem_hi, rem_lo, test_div;
-    FT_Int     count;
-
+    if ( v == 0 )
+      return 0;
 
-    root = 0;
+#ifndef FT_INT64
 
-    if ( x > 0 )
+    /* Algorithm by Christophe Meessen (1993) with overflow fixed and     */
+    /* rounding added.  Any unsigned fixed 16.16 argument is acceptable.  */
+    /* However, this algorithm is slower than the Babylonian method with  */
+    /* a good initial guess.  We only use it for large 32-bit values when */
+    /* 64-bit computations are not desirable.                             */
+    else if ( v > 0x10000U )
     {
-      rem_hi = 0;
-      rem_lo = (FT_UInt32)x;
-      count  = 24;
+      FT_UInt32  r = v >> 1;
+      FT_UInt32  q = ( v & 1 ) << 15;
+      FT_UInt32  b = 0x20000000;
+      FT_UInt32  t;
+
+
       do
       {
-        rem_hi   = ( rem_hi << 2 ) | ( rem_lo >> 30 );
-        rem_lo <<= 2;
-        root   <<= 1;
-        test_div = ( root << 1 ) + 1;
-
-        if ( rem_hi >= test_div )
+        t = q + b;
+        if ( r >= t )
         {
-          rem_hi -= test_div;
-          root   += 1;
+          r -= t;
+          q  = t + b;  /* equivalent to q += 2*b */
         }
-      } while ( --count );
+        r <<= 1;
+        b >>= 1;
+
+      } while ( b > 0x10 );  /* exactly 25 cycles */
+
+      return ( q + 0x40 ) >> 7;
     }
+    else
+    {
+      FT_UInt32  r = ( v << 16 ) - 1;
 
-    return (FT_Int32)root;
-  }
+#else /* FT_INT64 */
 
-#endif /* 0 */
+    else
+    {
+      FT_UInt64  r = ( (FT_UInt64)v << 16 ) - 1;
+
+#endif /* FT_INT64 */
+
+      FT_UInt32  q = 1 << ( ( 17 + FT_MSB( v ) ) >> 1 );
+      FT_UInt32  t;
+
+
+      /* Babylonian method with rounded-up division */
+      do
+      {
+        t = q;
+        q = ( t + (FT_UInt32)( r / t ) + 1 ) >> 1;
+
+      } while ( q != t );  /* less than 6 cycles */
+
+      return q;
+    }
+  }
 
 
   /* documentation is in ftcalc.h */
@@ -1094,11 +1096,8 @@
       FT_UInt32  factor;
 
 
-      scalar = (FT_UInt32)s[i];
-      factor = (FT_UInt32)f[i];
-
-      FT_MOVE_SIGN( s[i], scalar, sign );
-      FT_MOVE_SIGN( f[i], factor, sign );
+      FT_MOVE_SIGN( FT_UInt32, s[i], scalar, sign );
+      FT_MOVE_SIGN( FT_UInt32, f[i], factor, sign );
 
       ft_multo64( scalar, factor, &multResult );
 
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftcid.c b/src/java.desktop/share/native/libfreetype/src/base/ftcid.c
index 866cd23e91b59..4f2deb19a053f 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftcid.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftcid.c
@@ -4,7 +4,7 @@
  *
  *   FreeType API for accessing CID font information.
  *
- * Copyright (C) 2007-2023 by
+ * Copyright (C) 2007-2024 by
  * Derek Clegg and Michael Toftdal.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftcolor.c b/src/java.desktop/share/native/libfreetype/src/base/ftcolor.c
index bcd6e893d4a57..c6bf2a3cd1a44 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftcolor.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftcolor.c
@@ -4,7 +4,7 @@
  *
  *   FreeType's glyph color management (body).
  *
- * Copyright (C) 2018-2023 by
+ * Copyright (C) 2018-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftdbgmem.c b/src/java.desktop/share/native/libfreetype/src/base/ftdbgmem.c
index 8fab50dd017e6..902a5dc8bbce4 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftdbgmem.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftdbgmem.c
@@ -4,7 +4,7 @@
  *
  *   Memory debugger (body).
  *
- * Copyright (C) 2001-2023 by
+ * Copyright (C) 2001-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftdebug.c b/src/java.desktop/share/native/libfreetype/src/base/ftdebug.c
index 61c4563b0c433..11307eaace4e1 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftdebug.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftdebug.c
@@ -4,7 +4,7 @@
  *
  *   Debugging and logging component (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftfntfmt.c b/src/java.desktop/share/native/libfreetype/src/base/ftfntfmt.c
index 0b41f7cc83d00..77b4089e7e270 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftfntfmt.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftfntfmt.c
@@ -4,7 +4,7 @@
  *
  *   FreeType utility file for font formats (body).
  *
- * Copyright (C) 2002-2023 by
+ * Copyright (C) 2002-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftfstype.c b/src/java.desktop/share/native/libfreetype/src/base/ftfstype.c
index ea24e64c6ea92..1565c3b7e25ad 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftfstype.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftfstype.c
@@ -4,7 +4,7 @@
  *
  *   FreeType utility file to access FSType data (body).
  *
- * Copyright (C) 2008-2023 by
+ * Copyright (C) 2008-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftgasp.c b/src/java.desktop/share/native/libfreetype/src/base/ftgasp.c
index 29b7b08b787c3..c63d30e978c61 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftgasp.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftgasp.c
@@ -4,7 +4,7 @@
  *
  *   Access of TrueType's `gasp' table (body).
  *
- * Copyright (C) 2007-2023 by
+ * Copyright (C) 2007-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftgloadr.c b/src/java.desktop/share/native/libfreetype/src/base/ftgloadr.c
index 9823d09e41afa..484d98f1722a9 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftgloadr.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftgloadr.c
@@ -4,7 +4,7 @@
  *
  *   The FreeType glyph loader (body).
  *
- * Copyright (C) 2002-2023 by
+ * Copyright (C) 2002-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg
  *
  * This file is part of the FreeType project, and may only be used,
@@ -355,34 +355,25 @@
   FT_BASE_DEF( void )
   FT_GlyphLoader_Add( FT_GlyphLoader  loader )
   {
-    FT_GlyphLoad  base;
-    FT_GlyphLoad  current;
-
-    FT_Int        n_curr_contours;
-    FT_Int        n_base_points;
-    FT_Int        n;
+    FT_Outline*  base;
+    FT_Outline*  current;
+    FT_Int       n;
 
 
     if ( !loader )
       return;
 
-    base    = &loader->base;
-    current = &loader->current;
-
-    n_curr_contours = current->outline.n_contours;
-    n_base_points   = base->outline.n_points;
+    base    = &loader->base.outline;
+    current = &loader->current.outline;
 
-    base->outline.n_points =
-      (short)( base->outline.n_points + current->outline.n_points );
-    base->outline.n_contours =
-      (short)( base->outline.n_contours + current->outline.n_contours );
+    /* adjust contours count in newest outline */
+    for ( n = 0; n < current->n_contours; n++ )
+      current->contours[n] += base->n_points;
 
-    base->num_subglyphs += current->num_subglyphs;
+    base->n_points   += current->n_points;
+    base->n_contours += current->n_contours;
 
-    /* adjust contours count in newest outline */
-    for ( n = 0; n < n_curr_contours; n++ )
-      current->outline.contours[n] =
-        (short)( current->outline.contours[n] + n_base_points );
+    loader->base.num_subglyphs += loader->current.num_subglyphs;
 
     /* prepare for another new glyph image */
     FT_GlyphLoader_Prepare( loader );
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftglyph.c b/src/java.desktop/share/native/libfreetype/src/base/ftglyph.c
index 393d4949f849d..1b5849f99afa1 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftglyph.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftglyph.c
@@ -4,7 +4,7 @@
  *
  *   FreeType convenience functions to handle glyphs (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftinit.c b/src/java.desktop/share/native/libfreetype/src/base/ftinit.c
index c9c71d24bf961..9a6c00e13efaf 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftinit.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftinit.c
@@ -4,7 +4,7 @@
  *
  *   FreeType initialization layer (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftlcdfil.c b/src/java.desktop/share/native/libfreetype/src/base/ftlcdfil.c
index 6c3fd66e0bb15..1e69d4da70f51 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftlcdfil.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftlcdfil.c
@@ -4,7 +4,7 @@
  *
  *   FreeType API for color filtering of subpixel bitmap glyphs (body).
  *
- * Copyright (C) 2006-2023 by
+ * Copyright (C) 2006-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftmac.c b/src/java.desktop/share/native/libfreetype/src/base/ftmac.c
index 492d0553845dc..e8e35627b50c5 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftmac.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftmac.c
@@ -8,7 +8,7 @@
  * This file is for Mac OS X only; see builds/mac/ftoldmac.c for
  * classic platforms built by MPW.
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -812,6 +812,7 @@
     ResourceIndex  res_index;
     Handle         fond;
     short          num_faces_in_res;
+    FT_Long        count;
 
 
     if ( noErr != FT_FSPathMakeRes( pathname, &res_ref ) )
@@ -821,8 +822,10 @@
     if ( ResError() )
       return FT_THROW( Cannot_Open_Resource );
 
+    res_index        = 1;
     num_faces_in_res = 0;
-    for ( res_index = 1; ; res_index++ )
+    count            = face_index;
+    while ( count >= 0 )
     {
       short  num_faces_in_fond;
 
@@ -834,15 +837,21 @@
       num_faces_in_fond  = count_faces( fond, pathname );
       num_faces_in_res  += num_faces_in_fond;
 
-      if ( 0 <= face_index && face_index < num_faces_in_fond && error )
-        error = FT_New_Face_From_FOND( library, fond, face_index, aface );
+      if ( count < num_faces_in_fond )
+        error = FT_New_Face_From_FOND( library, fond, count, aface );
 
-      face_index -= num_faces_in_fond;
+      res_index++;
+      count -= num_faces_in_fond;
     }
 
     CloseResFile( res_ref );
+
     if ( !error && aface && *aface )
-      (*aface)->num_faces = num_faces_in_res;
+    {
+      (*aface)->num_faces  = num_faces_in_res;
+      (*aface)->face_index = face_index;
+    }
+
     return error;
   }
 
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftmm.c b/src/java.desktop/share/native/libfreetype/src/base/ftmm.c
index 9e2dd7ee79ddc..cc4ca22fba3ba 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftmm.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftmm.c
@@ -4,7 +4,7 @@
  *
  *   Multiple Master font support (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftobjs.c b/src/java.desktop/share/native/libfreetype/src/base/ftobjs.c
index 89a25bc732d6f..9b97820c379ec 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftobjs.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftobjs.c
@@ -4,7 +4,7 @@
  *
  *   The FreeType private base classes (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -2302,7 +2302,10 @@
                                       face_index_internal, aface );
       FT_FREE( data_offsets );
       if ( !error )
-        (*aface)->num_faces = count;
+      {
+        (*aface)->num_faces  = count;
+        (*aface)->face_index = face_index_internal;
+      }
     }
 
     return error;
@@ -5791,7 +5794,7 @@
     ttface = (TT_Face)face;
     sfnt   = (SFNT_Service)ttface->sfnt;
 
-    if ( sfnt->get_colr_layer )
+    if ( sfnt->get_colr_glyph_paint )
       return sfnt->get_colr_glyph_paint( ttface,
                                          base_glyph,
                                          root_transform,
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftoutln.c b/src/java.desktop/share/native/libfreetype/src/base/ftoutln.c
index 134f39d2b1fa0..ef699b3c7cd39 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftoutln.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftoutln.c
@@ -4,7 +4,7 @@
  *
  *   FreeType outline management (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -53,7 +53,7 @@
 
     FT_Vector*  point;
     FT_Vector*  limit;
-    char*       tags;
+    FT_Byte*    tags;
 
     FT_Error    error;
 
@@ -332,8 +332,8 @@
          FT_NEW_ARRAY( anoutline->contours, numContours ) )
       goto Fail;
 
-    anoutline->n_points    = (FT_Short)numPoints;
-    anoutline->n_contours  = (FT_Short)numContours;
+    anoutline->n_points    = (FT_UShort)numPoints;
+    anoutline->n_contours  = (FT_UShort)numContours;
     anoutline->flags      |= FT_OUTLINE_OWNER;
 
     return FT_Err_Ok;
@@ -359,12 +359,14 @@
       FT_Int  n;
 
 
+      FT_TRACE5(( "FT_Outline_Check: contours = %d, points = %d\n",
+                  n_contours, n_points ));
       /* empty glyph? */
       if ( n_points == 0 && n_contours == 0 )
         return FT_Err_Ok;
 
       /* check point and contour counts */
-      if ( n_points <= 0 || n_contours <= 0 )
+      if ( n_points == 0 || n_contours == 0 )
         goto Bad;
 
       end0 = -1;
@@ -576,13 +578,13 @@
 
       /* reverse tags table */
       {
-        char*  p = outline->tags + first;
-        char*  q = outline->tags + last;
+        FT_Byte*  p = outline->tags + first;
+        FT_Byte*  q = outline->tags + last;
 
 
         while ( p < q )
         {
-          char  swap;
+          FT_Byte  swap;
 
 
           swap = *p;
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftpatent.c b/src/java.desktop/share/native/libfreetype/src/base/ftpatent.c
index cb5efadffb1df..2055757e023cd 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftpatent.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftpatent.c
@@ -5,7 +5,7 @@
  *   FreeType API for checking patented TrueType bytecode instructions
  *   (body).  Obsolete, retained for backward compatibility.
  *
- * Copyright (C) 2007-2023 by
+ * Copyright (C) 2007-2024 by
  * David Turner.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftpsprop.c b/src/java.desktop/share/native/libfreetype/src/base/ftpsprop.c
index cefdf489d7f68..37a6cee6cc9ab 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftpsprop.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftpsprop.c
@@ -5,7 +5,7 @@
  *   Get and set properties of PostScript drivers (body).
  *   See `ftdriver.h' for available properties.
  *
- * Copyright (C) 2017-2023 by
+ * Copyright (C) 2017-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftrfork.c b/src/java.desktop/share/native/libfreetype/src/base/ftrfork.c
index 2ab430195f20b..dc9b043d8bb5b 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftrfork.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftrfork.c
@@ -4,7 +4,7 @@
  *
  *   Embedded resource forks accessor (body).
  *
- * Copyright (C) 2004-2023 by
+ * Copyright (C) 2004-2024 by
  * Masatake YAMATO and Redhat K.K.
  *
  * FT_Raccess_Get_HeaderInfo() and raccess_guess_darwin_hfsplus() are
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftsnames.c b/src/java.desktop/share/native/libfreetype/src/base/ftsnames.c
index 1917a3f1dffbd..f7231fd61ccde 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftsnames.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftsnames.c
@@ -7,7 +7,7 @@
  *
  *   This is _not_ used to retrieve glyph names!
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftstream.c b/src/java.desktop/share/native/libfreetype/src/base/ftstream.c
index 64826acebe52a..6672224612820 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftstream.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftstream.c
@@ -4,7 +4,7 @@
  *
  *   I/O stream support (body).
  *
- * Copyright (C) 2000-2023 by
+ * Copyright (C) 2000-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -763,10 +763,10 @@
       case ft_frame_bytes:  /* read a byte sequence */
       case ft_frame_skip:   /* skip some bytes      */
         {
-          FT_UInt  len = fields->size;
+          FT_Offset  len = fields->size;
 
 
-          if ( cursor + len > stream->limit )
+          if ( len > (FT_Offset)( stream->limit - cursor ) )
           {
             error = FT_THROW( Invalid_Stream_Operation );
             goto Exit;
@@ -830,7 +830,7 @@
         goto Exit;
       }
 
-      /* now, compute the signed value is necessary */
+      /* now, compute the signed value if necessary */
       if ( fields->value & FT_FRAME_OP_SIGNED )
         value = (FT_ULong)( (FT_Int32)( value << sign_shift ) >> sign_shift );
 
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftstroke.c b/src/java.desktop/share/native/libfreetype/src/base/ftstroke.c
index 92f1e43080fdc..64f46ce43e76c 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftstroke.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftstroke.c
@@ -4,7 +4,7 @@
  *
  *   FreeType path stroker (body).
  *
- * Copyright (C) 2002-2023 by
+ * Copyright (C) 2002-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -711,7 +711,7 @@
     {
       FT_UInt   count = border->num_points;
       FT_Byte*  read  = border->tags;
-      FT_Byte*  write = (FT_Byte*)outline->tags + outline->n_points;
+      FT_Byte*  write = outline->tags + outline->n_points;
 
 
       for ( ; count > 0; count--, read++, write++ )
@@ -727,10 +727,10 @@
 
     /* copy contours */
     {
-      FT_UInt    count = border->num_points;
-      FT_Byte*   tags  = border->tags;
-      FT_Short*  write = outline->contours + outline->n_contours;
-      FT_Short   idx   = (FT_Short)outline->n_points;
+      FT_UInt     count = border->num_points;
+      FT_Byte*    tags  = border->tags;
+      FT_UShort*  write = outline->contours + outline->n_contours;
+      FT_UShort   idx   = outline->n_points;
 
 
       for ( ; count > 0; count--, tags++, idx++ )
@@ -743,7 +743,7 @@
       }
     }
 
-    outline->n_points += (short)border->num_points;
+    outline->n_points += (FT_UShort)border->num_points;
 
     FT_ASSERT( FT_Outline_Check( outline ) == 0 );
   }
@@ -2050,7 +2050,7 @@
 
     FT_Vector*  point;
     FT_Vector*  limit;
-    char*       tags;
+    FT_Byte*    tags;
 
     FT_Error    error;
 
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftsynth.c b/src/java.desktop/share/native/libfreetype/src/base/ftsynth.c
index f32edd3388b0b..ec05bce33a9a6 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftsynth.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftsynth.c
@@ -4,7 +4,7 @@
  *
  *   FreeType synthesizing code for emboldening and slanting (body).
  *
- * Copyright (C) 2000-2023 by
+ * Copyright (C) 2000-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftsystem.c b/src/java.desktop/share/native/libfreetype/src/base/ftsystem.c
index 61c99e3635714..eee3642334f6e 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftsystem.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftsystem.c
@@ -4,7 +4,7 @@
  *
  *   ANSI-specific FreeType low-level system interface (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/base/fttrigon.c b/src/java.desktop/share/native/libfreetype/src/base/fttrigon.c
index 2dd2c3459e5fd..4b1aced1cbab0 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/fttrigon.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/fttrigon.c
@@ -4,7 +4,7 @@
  *
  *   FreeType trigonometric functions (body).
  *
- * Copyright (C) 2001-2023 by
+ * Copyright (C) 2001-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/base/fttype1.c b/src/java.desktop/share/native/libfreetype/src/base/fttype1.c
index 637c5cf775e6c..cedf7c405054d 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/fttype1.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/fttype1.c
@@ -4,7 +4,7 @@
  *
  *   FreeType utility file for PS names support (body).
  *
- * Copyright (C) 2002-2023 by
+ * Copyright (C) 2002-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftutil.c b/src/java.desktop/share/native/libfreetype/src/base/ftutil.c
index 6120846d2ca0c..b13512f870429 100644
--- a/src/java.desktop/share/native/libfreetype/src/base/ftutil.c
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftutil.c
@@ -4,7 +4,7 @@
  *
  *   FreeType utility file for memory and list management (body).
  *
- * Copyright (C) 2002-2023 by
+ * Copyright (C) 2002-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/cff/cffcmap.c b/src/java.desktop/share/native/libfreetype/src/cff/cffcmap.c
index 10d287bc81fbb..ea5f8ed288514 100644
--- a/src/java.desktop/share/native/libfreetype/src/cff/cffcmap.c
+++ b/src/java.desktop/share/native/libfreetype/src/cff/cffcmap.c
@@ -4,7 +4,7 @@
  *
  *   CFF character mapping table (cmap) support (body).
  *
- * Copyright (C) 2002-2023 by
+ * Copyright (C) 2002-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/cff/cffcmap.h b/src/java.desktop/share/native/libfreetype/src/cff/cffcmap.h
index b2afc2fab622c..1dd8700cd8baa 100644
--- a/src/java.desktop/share/native/libfreetype/src/cff/cffcmap.h
+++ b/src/java.desktop/share/native/libfreetype/src/cff/cffcmap.h
@@ -4,7 +4,7 @@
  *
  *   CFF character mapping table (cmap) support (specification).
  *
- * Copyright (C) 2002-2023 by
+ * Copyright (C) 2002-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/cff/cffdrivr.c b/src/java.desktop/share/native/libfreetype/src/cff/cffdrivr.c
index 9898d625ca4ed..f6ebdb3810a24 100644
--- a/src/java.desktop/share/native/libfreetype/src/cff/cffdrivr.c
+++ b/src/java.desktop/share/native/libfreetype/src/cff/cffdrivr.c
@@ -4,7 +4,7 @@
  *
  *   OpenType font driver implementation (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, Werner Lemberg, and Dominik Röttsches.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/cff/cffdrivr.h b/src/java.desktop/share/native/libfreetype/src/cff/cffdrivr.h
index ab1f147bb2a6a..fd5bc37ecd42a 100644
--- a/src/java.desktop/share/native/libfreetype/src/cff/cffdrivr.h
+++ b/src/java.desktop/share/native/libfreetype/src/cff/cffdrivr.h
@@ -4,7 +4,7 @@
  *
  *   High-level OpenType driver interface (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/cff/cfferrs.h b/src/java.desktop/share/native/libfreetype/src/cff/cfferrs.h
index bc9a3043fcff7..128adc3b716b8 100644
--- a/src/java.desktop/share/native/libfreetype/src/cff/cfferrs.h
+++ b/src/java.desktop/share/native/libfreetype/src/cff/cfferrs.h
@@ -4,7 +4,7 @@
  *
  *   CFF error codes (specification only).
  *
- * Copyright (C) 2001-2023 by
+ * Copyright (C) 2001-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/cff/cffgload.c b/src/java.desktop/share/native/libfreetype/src/cff/cffgload.c
index c483d1d1a5912..cbb071abdfeb4 100644
--- a/src/java.desktop/share/native/libfreetype/src/cff/cffgload.c
+++ b/src/java.desktop/share/native/libfreetype/src/cff/cffgload.c
@@ -4,7 +4,7 @@
  *
  *   OpenType Glyph Loader (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/cff/cffgload.h b/src/java.desktop/share/native/libfreetype/src/cff/cffgload.h
index 3b8cf236ddc21..346d4b11c31ac 100644
--- a/src/java.desktop/share/native/libfreetype/src/cff/cffgload.h
+++ b/src/java.desktop/share/native/libfreetype/src/cff/cffgload.h
@@ -4,7 +4,7 @@
  *
  *   OpenType Glyph Loader (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/cff/cffload.c b/src/java.desktop/share/native/libfreetype/src/cff/cffload.c
index af79082e98cb5..979fd45f6ca8d 100644
--- a/src/java.desktop/share/native/libfreetype/src/cff/cffload.c
+++ b/src/java.desktop/share/native/libfreetype/src/cff/cffload.c
@@ -4,7 +4,7 @@
  *
  *   OpenType and CFF data/program tables loader (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -1202,17 +1202,21 @@
         {
           CFF_AxisCoords*  axis = ®ion->axisList[j];
 
-          FT_Int16  start14, peak14, end14;
+          FT_Int  start, peak, end;
 
 
-          if ( FT_READ_SHORT( start14 ) ||
-               FT_READ_SHORT( peak14 )  ||
-               FT_READ_SHORT( end14 )   )
+          if ( FT_READ_SHORT( start ) ||
+               FT_READ_SHORT( peak )  ||
+               FT_READ_SHORT( end )   )
             goto Exit;
 
-          axis->startCoord = FT_fdot14ToFixed( start14 );
-          axis->peakCoord  = FT_fdot14ToFixed( peak14 );
-          axis->endCoord   = FT_fdot14ToFixed( end14 );
+          /* immediately tag invalid ranges with special peak = 0 */
+          if ( ( start < 0 && end > 0 ) || start > peak || peak > end )
+            peak = 0;
+
+          axis->startCoord = FT_fdot14ToFixed( start );
+          axis->peakCoord  = FT_fdot14ToFixed( peak );
+          axis->endCoord   = FT_fdot14ToFixed( end );
         }
       }
 
@@ -1379,10 +1383,10 @@
       /* opcode in both CFF and CFF2 DICTs.  See `cff_parse_num' for    */
       /* decode of this, which rounds to an integer.                    */
       *subFont->blend_top++ = 255;
-      *subFont->blend_top++ = (FT_Byte)( sum >> 24 );
-      *subFont->blend_top++ = (FT_Byte)( sum >> 16 );
-      *subFont->blend_top++ = (FT_Byte)( sum >>  8 );
-      *subFont->blend_top++ = (FT_Byte)sum;
+      *subFont->blend_top++ = (FT_Byte)( (FT_UInt32)sum >> 24 );
+      *subFont->blend_top++ = (FT_Byte)( (FT_UInt32)sum >> 16 );
+      *subFont->blend_top++ = (FT_Byte)( (FT_UInt32)sum >>  8 );
+      *subFont->blend_top++ = (FT_Byte)( (FT_UInt32)sum );
     }
 
     /* leave only numBlends results on parser stack */
@@ -1495,44 +1499,31 @@
       for ( j = 0; j < lenNDV; j++ )
       {
         CFF_AxisCoords*  axis = &varRegion->axisList[j];
-        FT_Fixed         axisScalar;
-
-
-        /* compute the scalar contribution of this axis; */
-        /* ignore invalid ranges                         */
-        if ( axis->startCoord > axis->peakCoord ||
-             axis->peakCoord > axis->endCoord   )
-          axisScalar = FT_FIXED_ONE;
 
-        else if ( axis->startCoord < 0 &&
-                  axis->endCoord > 0   &&
-                  axis->peakCoord != 0 )
-          axisScalar = FT_FIXED_ONE;
 
-        /* peak of 0 means ignore this axis */
-        else if ( axis->peakCoord == 0 )
-          axisScalar = FT_FIXED_ONE;
+        /* compute the scalar contribution of this axis */
+        /* with peak of 0 used for invalid axes         */
+        if ( axis->peakCoord == NDV[j] ||
+             axis->peakCoord == 0      )
+          continue;
 
         /* ignore this region if coords are out of range */
-        else if ( NDV[j] < axis->startCoord ||
-                  NDV[j] > axis->endCoord   )
-          axisScalar = 0;
-
-        /* calculate a proportional factor */
-        else
+        else if ( NDV[j] <= axis->startCoord ||
+                  NDV[j] >= axis->endCoord   )
         {
-          if ( NDV[j] == axis->peakCoord )
-            axisScalar = FT_FIXED_ONE;
-          else if ( NDV[j] < axis->peakCoord )
-            axisScalar = FT_DivFix( NDV[j] - axis->startCoord,
-                                    axis->peakCoord - axis->startCoord );
-          else
-            axisScalar = FT_DivFix( axis->endCoord - NDV[j],
-                                    axis->endCoord - axis->peakCoord );
+          blend->BV[master] = 0;
+          break;
         }
 
-        /* take product of all the axis scalars */
-        blend->BV[master] = FT_MulFix( blend->BV[master], axisScalar );
+        /* adjust proportionally */
+        else if ( NDV[j] < axis->peakCoord )
+          blend->BV[master] = FT_MulDiv( blend->BV[master],
+                                         NDV[j] - axis->startCoord,
+                                         axis->peakCoord - axis->startCoord );
+        else   /* NDV[j] > axis->peakCoord ) */
+          blend->BV[master] = FT_MulDiv( blend->BV[master],
+                                         axis->endCoord - NDV[j],
+                                         axis->endCoord - axis->peakCoord );
       }
 
       FT_TRACE4(( ", %f ",
diff --git a/src/java.desktop/share/native/libfreetype/src/cff/cffload.h b/src/java.desktop/share/native/libfreetype/src/cff/cffload.h
index b5286b0c8cb05..0220924542188 100644
--- a/src/java.desktop/share/native/libfreetype/src/cff/cffload.h
+++ b/src/java.desktop/share/native/libfreetype/src/cff/cffload.h
@@ -4,7 +4,7 @@
  *
  *   OpenType & CFF data/program tables loader (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/cff/cffobjs.c b/src/java.desktop/share/native/libfreetype/src/cff/cffobjs.c
index 6d08620c487c0..7c6713739a126 100644
--- a/src/java.desktop/share/native/libfreetype/src/cff/cffobjs.c
+++ b/src/java.desktop/share/native/libfreetype/src/cff/cffobjs.c
@@ -4,7 +4,7 @@
  *
  *   OpenType objects manager (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -42,6 +42,8 @@
 #include 
 #include 
 
+#define CFF_fixedToInt( x )                          \
+          ( (FT_Short)( ( (x) + 0x8000U ) >> 16 ) )
 
   /**************************************************************************
    *
@@ -124,19 +126,20 @@
 
     count = priv->num_blue_values = cpriv->num_blue_values;
     for ( n = 0; n < count; n++ )
-      priv->blue_values[n] = (FT_Short)cpriv->blue_values[n];
+      priv->blue_values[n] = CFF_fixedToInt( cpriv->blue_values[n] );
 
     count = priv->num_other_blues = cpriv->num_other_blues;
     for ( n = 0; n < count; n++ )
-      priv->other_blues[n] = (FT_Short)cpriv->other_blues[n];
+      priv->other_blues[n] = CFF_fixedToInt( cpriv->other_blues[n] );
 
     count = priv->num_family_blues = cpriv->num_family_blues;
     for ( n = 0; n < count; n++ )
-      priv->family_blues[n] = (FT_Short)cpriv->family_blues[n];
+      priv->family_blues[n] = CFF_fixedToInt( cpriv->family_blues[n] );
 
     count = priv->num_family_other_blues = cpriv->num_family_other_blues;
     for ( n = 0; n < count; n++ )
-      priv->family_other_blues[n] = (FT_Short)cpriv->family_other_blues[n];
+      priv->family_other_blues[n] =
+        CFF_fixedToInt( cpriv->family_other_blues[n] );
 
     priv->blue_scale = cpriv->blue_scale;
     priv->blue_shift = (FT_Int)cpriv->blue_shift;
@@ -421,32 +424,23 @@
   static void
   remove_subset_prefix( FT_String*  name )
   {
-    FT_Int32  idx             = 0;
-    FT_Int32  length          = (FT_Int32)ft_strlen( name ) + 1;
-    FT_Bool   continue_search = 1;
+    FT_UInt32  i = 0, idx = 0;
 
 
-    while ( continue_search )
+    /* six ASCII uppercase letters followed by a plus sign */
+    while ( 'A' <= name[i] && name[i++] <= 'Z' &&
+            'A' <= name[i] && name[i++] <= 'Z' &&
+            'A' <= name[i] && name[i++] <= 'Z' &&
+            'A' <= name[i] && name[i++] <= 'Z' &&
+            'A' <= name[i] && name[i++] <= 'Z' &&
+            'A' <= name[i] && name[i++] <= 'Z' &&
+                              name[i++] == '+' )
     {
-      if ( length >= 7 && name[6] == '+' )
-      {
-        for ( idx = 0; idx < 6; idx++ )
-        {
-          /* ASCII uppercase letters */
-          if ( !( 'A' <= name[idx] && name[idx] <= 'Z' ) )
-            continue_search = 0;
-        }
-
-        if ( continue_search )
-        {
-          for ( idx = 7; idx < length; idx++ )
-            name[idx - 7] = name[idx];
-          length -= 7;
-        }
-      }
-      else
-        continue_search = 0;
+      idx = i;
     }
+
+    if ( idx )
+      FT_MEM_MOVE( name, name + idx, ft_strlen( name + idx ) + 1 );
   }
 
 
@@ -456,42 +450,20 @@
   remove_style( FT_String*        family_name,
                 const FT_String*  style_name )
   {
-    FT_Int32  family_name_length, style_name_length;
+    FT_String*        f = family_name + ft_strlen( family_name );
+    const FT_String*  s =  style_name + ft_strlen(  style_name );
 
 
-    family_name_length = (FT_Int32)ft_strlen( family_name );
-    style_name_length  = (FT_Int32)ft_strlen( style_name );
+    /* compare strings moving backwards */
+    while ( s > style_name )
+      if ( f == family_name || *--s != *--f )
+        return;
 
-    if ( family_name_length > style_name_length )
-    {
-      FT_Int  idx;
-
-
-      for ( idx = 1; idx <= style_name_length; idx++ )
-      {
-        if ( family_name[family_name_length - idx] !=
-             style_name[style_name_length - idx] )
-          break;
-      }
-
-      if ( idx > style_name_length )
-      {
-        /* family_name ends with style_name; remove it */
-        idx = family_name_length - style_name_length - 1;
-
-        /* also remove special characters     */
-        /* between real family name and style */
-        while ( idx > 0                     &&
-                ( family_name[idx] == '-' ||
-                  family_name[idx] == ' ' ||
-                  family_name[idx] == '_' ||
-                  family_name[idx] == '+' ) )
-          idx--;
-
-        if ( idx > 0 )
-          family_name[idx + 1] = '\0';
-      }
-    }
+    /* terminate and remove special characters */
+    do
+      *f = '\0';
+    while ( f-- > family_name                                    &&
+            ( *f == '-' || *f == ' ' || *f == '_' || *f == '+' ) );
   }
 
 
@@ -722,8 +694,7 @@
         FT_UInt  instance_index = (FT_UInt)face_index >> 16;
 
 
-        if ( FT_HAS_MULTIPLE_MASTERS( cffface ) &&
-             instance_index > 0                 )
+        if ( FT_HAS_MULTIPLE_MASTERS( cffface ) )
         {
           error = FT_Set_Named_Instance( cffface, instance_index );
           if ( error )
diff --git a/src/java.desktop/share/native/libfreetype/src/cff/cffobjs.h b/src/java.desktop/share/native/libfreetype/src/cff/cffobjs.h
index 8f05f6132bc01..91ad83b1cd0c7 100644
--- a/src/java.desktop/share/native/libfreetype/src/cff/cffobjs.h
+++ b/src/java.desktop/share/native/libfreetype/src/cff/cffobjs.h
@@ -4,7 +4,7 @@
  *
  *   OpenType objects manager (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/cff/cffparse.c b/src/java.desktop/share/native/libfreetype/src/cff/cffparse.c
index 3b076704cf732..92a69c3b51659 100644
--- a/src/java.desktop/share/native/libfreetype/src/cff/cffparse.c
+++ b/src/java.desktop/share/native/libfreetype/src/cff/cffparse.c
@@ -4,7 +4,7 @@
  *
  *   CFF token stream parser (body)
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -501,10 +501,10 @@
       return cff_parse_real( *d, parser->limit, scaling, NULL );
     else if ( **d == 255 )
     {
-      FT_Fixed val = ( ( ( (FT_UInt32)*( d[0] + 1 ) << 24 ) |
-                         ( (FT_UInt32)*( d[0] + 2 ) << 16 ) |
-                         ( (FT_UInt32)*( d[0] + 3 ) <<  8 ) |
-                           (FT_UInt32)*( d[0] + 4 )         ) );
+      FT_Fixed val = (FT_Int32)( ( ( (FT_UInt32)*( d[0] + 1 ) << 24 ) |
+                                   ( (FT_UInt32)*( d[0] + 2 ) << 16 ) |
+                                   ( (FT_UInt32)*( d[0] + 3 ) <<  8 ) |
+                                     (FT_UInt32)*( d[0] + 4 )         ) );
 
       if ( scaling )
       {
@@ -1031,10 +1031,14 @@
           CFF_FIELD( code, name, id, cff_kind_string )
 #define CFF_FIELD_BOOL( code, name, id )             \
           CFF_FIELD( code, name, id, cff_kind_bool )
+#define CFF_FIELD_DELTA( code, name, max, id )                        \
+          CFF_FIELD_DELTA_KIND( code, name, max, id, cff_kind_delta )
+#define CFF_FIELD_DELTA_FIXED( code, name, max, id )                        \
+          CFF_FIELD_DELTA_KIND( code, name, max, id, cff_kind_delta_fixed )
 
 
 #undef  CFF_FIELD
-#undef  CFF_FIELD_DELTA
+#undef  CFF_FIELD_DELTA_KIND
 
 
 #ifndef FT_DEBUG_LEVEL_TRACE
@@ -1064,18 +1068,18 @@
             code | CFFCODE,               \
             FT_FIELD_OFFSET( name ),      \
             FT_FIELD_SIZE( name ),        \
-            0, 0, 0                       \
+            NULL, 0, 0                    \
           },
 
-#define CFF_FIELD_DELTA( code, name, max, id ) \
-          {                                    \
-            cff_kind_delta,                    \
-            code | CFFCODE,                    \
-            FT_FIELD_OFFSET( name ),           \
-            FT_FIELD_SIZE_DELTA( name ),       \
-            0,                                 \
-            max,                               \
-            FT_FIELD_OFFSET( num_ ## name )    \
+#define CFF_FIELD_DELTA_KIND( code, name, max, id, kind ) \
+          {                                               \
+            kind,                                         \
+            code | CFFCODE,                               \
+            FT_FIELD_OFFSET( name ),                      \
+            FT_FIELD_SIZE_DELTA( name ),                  \
+            NULL,                                         \
+            max,                                          \
+            FT_FIELD_OFFSET( num_ ## name )               \
           },
 
   static const CFF_Field_Handler  cff_field_handlers[] =
@@ -1083,7 +1087,7 @@
 
 #include "cfftoken.h"
 
-    { 0, 0, 0, 0, 0, 0, 0 }
+    { 0, 0, 0, 0, NULL, 0, 0 }
   };
 
 
@@ -1117,20 +1121,20 @@
             code | CFFCODE,               \
             FT_FIELD_OFFSET( name ),      \
             FT_FIELD_SIZE( name ),        \
-            0, 0, 0,                      \
+            NULL, 0, 0,                   \
             id                            \
           },
 
-#define CFF_FIELD_DELTA( code, name, max, id ) \
-          {                                    \
-            cff_kind_delta,                    \
-            code | CFFCODE,                    \
-            FT_FIELD_OFFSET( name ),           \
-            FT_FIELD_SIZE_DELTA( name ),       \
-            0,                                 \
-            max,                               \
-            FT_FIELD_OFFSET( num_ ## name ),   \
-            id                                 \
+#define CFF_FIELD_DELTA_KIND( code, name, max, id, kind ) \
+          {                                               \
+            kind,                                         \
+            code | CFFCODE,                               \
+            FT_FIELD_OFFSET( name ),                      \
+            FT_FIELD_SIZE_DELTA( name ),                  \
+            NULL,                                         \
+            max,                                          \
+            FT_FIELD_OFFSET( num_ ## name ),              \
+            id                                            \
           },
 
   static const CFF_Field_Handler  cff_field_handlers[] =
@@ -1138,7 +1142,7 @@
 
 #include "cfftoken.h"
 
-    { 0, 0, 0, 0, 0, 0, 0, 0 }
+    { 0, 0, 0, 0, NULL, 0, 0, NULL }
   };
 
 
@@ -1356,7 +1360,8 @@
 
             /* check that we have enough arguments -- except for */
             /* delta encoded arrays, which can be empty          */
-            if ( field->kind != cff_kind_delta && num_args < 1 )
+            if ( field->kind != cff_kind_delta                       &&
+                 field->kind != cff_kind_delta_fixed && num_args < 1 )
               goto Stack_Underflow;
 
             switch ( field->kind )
@@ -1471,6 +1476,38 @@
               }
               break;
 
+            case cff_kind_delta_fixed:
+              {
+                FT_Byte*   qcount = (FT_Byte*)parser->object +
+                                      field->count_offset;
+
+                FT_Byte**  data = parser->stack;
+
+
+                if ( num_args > field->array_max )
+                  num_args = field->array_max;
+
+                FT_TRACE4(( " [" ));
+
+                /* store count */
+                *qcount = (FT_Byte)num_args;
+
+                val = 0;
+                while ( num_args > 0 )
+                {
+                  val = ADD_LONG( val, cff_parse_fixed( parser, data++ ) );
+                  *(FT_Long*)q = val;
+
+                  FT_TRACE4(( " %f\n", (double)val / 65536 ));
+
+                  q += field->size;
+                  num_args--;
+                }
+
+                FT_TRACE4(( "]\n" ));
+              }
+              break;
+
             default:  /* callback or blend */
               error = field->reader( parser );
               if ( error )
diff --git a/src/java.desktop/share/native/libfreetype/src/cff/cffparse.h b/src/java.desktop/share/native/libfreetype/src/cff/cffparse.h
index 418caacc68c94..ca6b18af6aa32 100644
--- a/src/java.desktop/share/native/libfreetype/src/cff/cffparse.h
+++ b/src/java.desktop/share/native/libfreetype/src/cff/cffparse.h
@@ -4,7 +4,7 @@
  *
  *   CFF token stream parser (specification)
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -107,6 +107,7 @@ FT_BEGIN_HEADER
     cff_kind_string,
     cff_kind_bool,
     cff_kind_delta,
+    cff_kind_delta_fixed,
     cff_kind_callback,
     cff_kind_blend,
 
diff --git a/src/java.desktop/share/native/libfreetype/src/cff/cfftoken.h b/src/java.desktop/share/native/libfreetype/src/cff/cfftoken.h
index b61cb0e66e8d5..da45faa7f4ee3 100644
--- a/src/java.desktop/share/native/libfreetype/src/cff/cfftoken.h
+++ b/src/java.desktop/share/native/libfreetype/src/cff/cfftoken.h
@@ -4,7 +4,7 @@
  *
  *   CFF token definitions (specification only).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -80,26 +80,26 @@
 #undef  CFFCODE
 #define CFFCODE       CFF_CODE_PRIVATE
 
-  CFF_FIELD_DELTA     ( 6,     blue_values, 14,        "BlueValues" )
-  CFF_FIELD_DELTA     ( 7,     other_blues, 10,        "OtherBlues" )
-  CFF_FIELD_DELTA     ( 8,     family_blues, 14,       "FamilyBlues" )
-  CFF_FIELD_DELTA     ( 9,     family_other_blues, 10, "FamilyOtherBlues" )
-  CFF_FIELD_FIXED_1000( 0x109, blue_scale,             "BlueScale" )
-  CFF_FIELD_NUM       ( 0x10A, blue_shift,             "BlueShift" )
-  CFF_FIELD_NUM       ( 0x10B, blue_fuzz,              "BlueFuzz" )
-  CFF_FIELD_NUM       ( 10,    standard_width,         "StdHW" )
-  CFF_FIELD_NUM       ( 11,    standard_height,        "StdVW" )
-  CFF_FIELD_DELTA     ( 0x10C, snap_widths, 13,        "StemSnapH" )
-  CFF_FIELD_DELTA     ( 0x10D, snap_heights, 13,       "StemSnapV" )
-  CFF_FIELD_BOOL      ( 0x10E, force_bold,             "ForceBold" )
-  CFF_FIELD_FIXED     ( 0x10F, force_bold_threshold,   "ForceBoldThreshold" )
-  CFF_FIELD_NUM       ( 0x110, lenIV,                  "lenIV" )
-  CFF_FIELD_NUM       ( 0x111, language_group,         "LanguageGroup" )
-  CFF_FIELD_FIXED     ( 0x112, expansion_factor,       "ExpansionFactor" )
-  CFF_FIELD_NUM       ( 0x113, initial_random_seed,    "initialRandomSeed" )
-  CFF_FIELD_NUM       ( 19,    local_subrs_offset,     "Subrs" )
-  CFF_FIELD_NUM       ( 20,    default_width,          "defaultWidthX" )
-  CFF_FIELD_NUM       ( 21,    nominal_width,          "nominalWidthX" )
+  CFF_FIELD_DELTA_FIXED( 6,     blue_values, 14,        "BlueValues" )
+  CFF_FIELD_DELTA_FIXED( 7,     other_blues, 10,        "OtherBlues" )
+  CFF_FIELD_DELTA_FIXED( 8,     family_blues, 14,       "FamilyBlues" )
+  CFF_FIELD_DELTA_FIXED( 9,     family_other_blues, 10, "FamilyOtherBlues" )
+  CFF_FIELD_FIXED_1000 ( 0x109, blue_scale,             "BlueScale" )
+  CFF_FIELD_NUM        ( 0x10A, blue_shift,             "BlueShift" )
+  CFF_FIELD_NUM        ( 0x10B, blue_fuzz,              "BlueFuzz" )
+  CFF_FIELD_NUM        ( 10,    standard_width,         "StdHW" )
+  CFF_FIELD_NUM        ( 11,    standard_height,        "StdVW" )
+  CFF_FIELD_DELTA      ( 0x10C, snap_widths, 13,        "StemSnapH" )
+  CFF_FIELD_DELTA      ( 0x10D, snap_heights, 13,       "StemSnapV" )
+  CFF_FIELD_BOOL       ( 0x10E, force_bold,             "ForceBold" )
+  CFF_FIELD_FIXED      ( 0x10F, force_bold_threshold,   "ForceBoldThreshold" )
+  CFF_FIELD_NUM        ( 0x110, lenIV,                  "lenIV" )
+  CFF_FIELD_NUM        ( 0x111, language_group,         "LanguageGroup" )
+  CFF_FIELD_FIXED      ( 0x112, expansion_factor,       "ExpansionFactor" )
+  CFF_FIELD_NUM        ( 0x113, initial_random_seed,    "initialRandomSeed" )
+  CFF_FIELD_NUM        ( 19,    local_subrs_offset,     "Subrs" )
+  CFF_FIELD_NUM        ( 20,    default_width,          "defaultWidthX" )
+  CFF_FIELD_NUM        ( 21,    nominal_width,          "nominalWidthX" )
 
 
 #undef  FT_STRUCTURE
@@ -129,22 +129,22 @@
 #undef  CFFCODE
 #define CFFCODE       CFF2_CODE_PRIVATE
 
-  CFF_FIELD_DELTA     ( 6,     blue_values, 14,        "BlueValues" )
-  CFF_FIELD_DELTA     ( 7,     other_blues, 10,        "OtherBlues" )
-  CFF_FIELD_DELTA     ( 8,     family_blues, 14,       "FamilyBlues" )
-  CFF_FIELD_DELTA     ( 9,     family_other_blues, 10, "FamilyOtherBlues" )
-  CFF_FIELD_FIXED_1000( 0x109, blue_scale,             "BlueScale" )
-  CFF_FIELD_NUM       ( 0x10A, blue_shift,             "BlueShift" )
-  CFF_FIELD_NUM       ( 0x10B, blue_fuzz,              "BlueFuzz" )
-  CFF_FIELD_NUM       ( 10,    standard_width,         "StdHW" )
-  CFF_FIELD_NUM       ( 11,    standard_height,        "StdVW" )
-  CFF_FIELD_DELTA     ( 0x10C, snap_widths, 13,        "StemSnapH" )
-  CFF_FIELD_DELTA     ( 0x10D, snap_heights, 13,       "StemSnapV" )
-  CFF_FIELD_NUM       ( 0x111, language_group,         "LanguageGroup" )
-  CFF_FIELD_FIXED     ( 0x112, expansion_factor,       "ExpansionFactor" )
-  CFF_FIELD_CALLBACK  ( 22,    vsindex,                "vsindex" )
-  CFF_FIELD_BLEND     ( 23,                            "blend" )
-  CFF_FIELD_NUM       ( 19,    local_subrs_offset,     "Subrs" )
+  CFF_FIELD_DELTA_FIXED( 6,     blue_values, 14,        "BlueValues" )
+  CFF_FIELD_DELTA_FIXED( 7,     other_blues, 10,        "OtherBlues" )
+  CFF_FIELD_DELTA_FIXED( 8,     family_blues, 14,       "FamilyBlues" )
+  CFF_FIELD_DELTA_FIXED( 9,     family_other_blues, 10, "FamilyOtherBlues" )
+  CFF_FIELD_FIXED_1000 ( 0x109, blue_scale,             "BlueScale" )
+  CFF_FIELD_NUM        ( 0x10A, blue_shift,             "BlueShift" )
+  CFF_FIELD_NUM        ( 0x10B, blue_fuzz,              "BlueFuzz" )
+  CFF_FIELD_NUM        ( 10,    standard_width,         "StdHW" )
+  CFF_FIELD_NUM        ( 11,    standard_height,        "StdVW" )
+  CFF_FIELD_DELTA      ( 0x10C, snap_widths, 13,        "StemSnapH" )
+  CFF_FIELD_DELTA      ( 0x10D, snap_heights, 13,       "StemSnapV" )
+  CFF_FIELD_NUM        ( 0x111, language_group,         "LanguageGroup" )
+  CFF_FIELD_FIXED      ( 0x112, expansion_factor,       "ExpansionFactor" )
+  CFF_FIELD_CALLBACK   ( 22,    vsindex,                "vsindex" )
+  CFF_FIELD_BLEND      ( 23,                            "blend" )
+  CFF_FIELD_NUM        ( 19,    local_subrs_offset,     "Subrs" )
 
 
 /* END */
diff --git a/src/java.desktop/share/native/libfreetype/src/cid/ciderrs.h b/src/java.desktop/share/native/libfreetype/src/cid/ciderrs.h
index 40a1097d0ac7a..c439a8c4a0b30 100644
--- a/src/java.desktop/share/native/libfreetype/src/cid/ciderrs.h
+++ b/src/java.desktop/share/native/libfreetype/src/cid/ciderrs.h
@@ -4,7 +4,7 @@
  *
  *   CID error codes (specification only).
  *
- * Copyright (C) 2001-2023 by
+ * Copyright (C) 2001-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/cid/cidgload.c b/src/java.desktop/share/native/libfreetype/src/cid/cidgload.c
index eaca765ad06c8..7b571322d456b 100644
--- a/src/java.desktop/share/native/libfreetype/src/cid/cidgload.c
+++ b/src/java.desktop/share/native/libfreetype/src/cid/cidgload.c
@@ -4,7 +4,7 @@
  *
  *   CID-keyed Type1 Glyph Loader (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/cid/cidgload.h b/src/java.desktop/share/native/libfreetype/src/cid/cidgload.h
index edd6229234c41..9fdc9db589231 100644
--- a/src/java.desktop/share/native/libfreetype/src/cid/cidgload.h
+++ b/src/java.desktop/share/native/libfreetype/src/cid/cidgload.h
@@ -4,7 +4,7 @@
  *
  *   OpenType Glyph Loader (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/cid/cidload.c b/src/java.desktop/share/native/libfreetype/src/cid/cidload.c
index a7da8ea39d5d2..722f5a34ddf95 100644
--- a/src/java.desktop/share/native/libfreetype/src/cid/cidload.c
+++ b/src/java.desktop/share/native/libfreetype/src/cid/cidload.c
@@ -4,7 +4,7 @@
  *
  *   CID-keyed Type1 font loader (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -388,7 +388,7 @@
     T1_FIELD_CALLBACK( "ExpansionFactor", parse_expansion_factor, 0 )
     T1_FIELD_CALLBACK( "FontName",        parse_font_name, 0 )
 
-    { 0, T1_FIELD_LOCATION_CID_INFO, T1_FIELD_TYPE_NONE, 0, 0, 0, 0, 0, 0 }
+    T1_FIELD_ZERO
   };
 
 
@@ -469,36 +469,23 @@
             T1_Field  keyword = (T1_Field)cid_field_records;
 
 
-            for (;;)
+            while ( keyword->len )
             {
-              FT_Byte*  name;
+              FT_Byte*  name = (FT_Byte*)keyword->ident;
 
 
-              name = (FT_Byte*)keyword->ident;
-              if ( !name )
-                break;
-
-              if ( cur[0] == name[0]                     &&
-                   len == ft_strlen( (const char*)name ) )
+              if ( keyword->len == len              &&
+                   ft_memcmp( cur, name, len ) == 0 )
               {
-                FT_UInt  n;
-
-
-                for ( n = 1; n < len; n++ )
-                  if ( cur[n] != name[n] )
-                    break;
-
-                if ( n >= len )
-                {
-                  /* we found it - run the parsing callback */
-                  parser->root.error = cid_load_keyword( face,
-                                                         loader,
-                                                         keyword );
-                  if ( parser->root.error )
-                    return parser->root.error;
-                  break;
-                }
+                /* we found it - run the parsing callback */
+                parser->root.error = cid_load_keyword( face,
+                                                       loader,
+                                                       keyword );
+                if ( parser->root.error )
+                  return parser->root.error;
+                break;
               }
+
               keyword++;
             }
           }
diff --git a/src/java.desktop/share/native/libfreetype/src/cid/cidload.h b/src/java.desktop/share/native/libfreetype/src/cid/cidload.h
index d12d2962a6866..7f030b32df731 100644
--- a/src/java.desktop/share/native/libfreetype/src/cid/cidload.h
+++ b/src/java.desktop/share/native/libfreetype/src/cid/cidload.h
@@ -4,7 +4,7 @@
  *
  *   CID-keyed Type1 font loader (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/cid/cidobjs.c b/src/java.desktop/share/native/libfreetype/src/cid/cidobjs.c
index f698a419289e0..8d337c4112839 100644
--- a/src/java.desktop/share/native/libfreetype/src/cid/cidobjs.c
+++ b/src/java.desktop/share/native/libfreetype/src/cid/cidobjs.c
@@ -4,7 +4,7 @@
  *
  *   CID objects manager (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/cid/cidobjs.h b/src/java.desktop/share/native/libfreetype/src/cid/cidobjs.h
index 83c0c61c3ca63..d371cbe995423 100644
--- a/src/java.desktop/share/native/libfreetype/src/cid/cidobjs.h
+++ b/src/java.desktop/share/native/libfreetype/src/cid/cidobjs.h
@@ -4,7 +4,7 @@
  *
  *   CID objects manager (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/cid/cidparse.c b/src/java.desktop/share/native/libfreetype/src/cid/cidparse.c
index 171a886215ad2..73a3ade893be3 100644
--- a/src/java.desktop/share/native/libfreetype/src/cid/cidparse.c
+++ b/src/java.desktop/share/native/libfreetype/src/cid/cidparse.c
@@ -4,7 +4,7 @@
  *
  *   CID-keyed Type1 parser (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -90,10 +90,15 @@
     if ( error )
       goto Exit;
 
-  Again:
-    /* now, read the rest of the file until we find */
-    /* `StartData' or `/sfnts'                      */
+    if ( !stream->read ) {
+      /* just parse memory-based streams */
+      offset = stream->size;
+    }
+    else
     {
+      /* Find the last `StartData` or `/sfnts`.  The parser requires */
+      /* contiguous memory; attempt to pin as little as necessary.   */
+
       /*
        * The algorithm is as follows (omitting the case with less than 256
        * bytes to fill for simplicity).
@@ -119,7 +124,8 @@
       FT_Byte*  p           = buffer;
 
 
-      for ( offset = FT_STREAM_POS(); ; offset += 256 )
+      offset = 0;
+      while ( 1 )
       {
         FT_ULong  stream_len;
 
@@ -127,7 +133,7 @@
         stream_len = stream->size - FT_STREAM_POS();
 
         read_len = FT_MIN( read_len, stream_len );
-        if ( FT_STREAM_READ( p, read_len ) )
+        if ( read_len && FT_STREAM_READ( p, read_len ) )
           goto Exit;
 
         /* ensure that we do not compare with data beyond the buffer */
@@ -141,20 +147,23 @@
                ft_strncmp( (char*)p, STARTDATA, STARTDATA_LEN ) == 0 )
           {
             /* save offset of binary data after `StartData' */
-            offset += (FT_ULong)( p - buffer ) + STARTDATA_LEN + 1;
-            goto Found;
+            offset = FT_STREAM_POS() - read_len - read_offset
+                     + (FT_ULong)( p - buffer ) + STARTDATA_LEN + 1;
           }
           else if ( p[1] == 's'                                   &&
                     ft_strncmp( (char*)p, SFNTS, SFNTS_LEN ) == 0 )
           {
-            offset += (FT_ULong)( p - buffer ) + SFNTS_LEN + 1;
-            goto Found;
+            offset = FT_STREAM_POS() - read_len - read_offset
+                     + (FT_ULong)( p - buffer ) + SFNTS_LEN + 1;
           }
         }
 
-        if ( read_offset + read_len < STARTDATA_LEN )
+        if ( read_offset + read_len <= STARTDATA_LEN )
         {
-          FT_TRACE2(( "cid_parser_new: no `StartData' keyword found\n" ));
+          if ( offset )
+            goto Found;
+
+          FT_TRACE2(( "cid_parser_new: no `StartData` keyword found\n" ));
           error = FT_THROW( Invalid_File_Format );
           goto Exit;
         }
@@ -171,9 +180,9 @@
     }
 
   Found:
-    /* We have found the start of the binary data or the `/sfnts' token. */
-    /* Now rewind and extract the frame corresponding to this PostScript */
-    /* section.                                                          */
+    /* We have found an efficient range to look for the binary data or    */
+    /* `/sfnts' token.  Now rewind and extract the frame corresponding to */
+    /* this PostScript section.                                           */
 
     ps_len = offset - base_offset;
     if ( FT_STREAM_SEEK( base_offset )                  ||
@@ -187,8 +196,8 @@
     parser->root.limit     = parser->root.cursor + ps_len;
     parser->num_dict       = FT_UINT_MAX;
 
-    /* Finally, we check whether `StartData' or `/sfnts' was real --  */
-    /* it could be in a comment or string.  We also get the arguments */
+    /* Find the first real `StartData' or `/sfnts' -- the last one    */
+    /* could be in a comment or string.  We also get the arguments    */
     /* of `StartData' to find out whether the data is represented in  */
     /* binary or hex format.                                          */
 
@@ -216,6 +225,7 @@
       {
         T1_TokenRec  type_token;
         FT_Long      binary_length;
+        FT_ULong     found_offset;
 
 
         parser->root.cursor = arg1;
@@ -234,6 +244,24 @@
             parser->binary_length = (FT_ULong)binary_length;
         }
 
+        /* set the real values for the parser, if different */
+        found_offset = (FT_ULong)( cur - parser->postscript )
+                       + STARTDATA_LEN + 1;
+        if ( found_offset != offset )
+        {
+          FT_FRAME_RELEASE( parser->postscript );
+
+          ps_len = found_offset - base_offset;
+          if ( FT_STREAM_SEEK( base_offset )                  ||
+               FT_FRAME_EXTRACT( ps_len, parser->postscript ) )
+            goto Exit;
+
+          parser->data_offset    = found_offset;
+          parser->postscript_len = ps_len;
+          parser->root.base      = parser->postscript;
+          parser->root.cursor    = parser->postscript;
+          parser->root.limit     = parser->root.cursor + ps_len;
+        }
         goto Exit;
       }
       else if ( cur[1] == 's'                                   &&
@@ -251,11 +279,8 @@
       cur  = parser->root.cursor;
     }
 
-    /* we haven't found the correct `StartData'; go back and continue */
-    /* searching                                                      */
-    FT_FRAME_RELEASE( parser->postscript );
-    if ( !FT_STREAM_SEEK( offset ) )
-      goto Again;
+    FT_TRACE2(( "cid_parser_new: no `StartData` token found\n" ));
+    error = FT_THROW( Invalid_File_Format );
 
   Exit:
     return error;
diff --git a/src/java.desktop/share/native/libfreetype/src/cid/cidparse.h b/src/java.desktop/share/native/libfreetype/src/cid/cidparse.h
index 2fd4e7a9310b1..0f5baddcb926e 100644
--- a/src/java.desktop/share/native/libfreetype/src/cid/cidparse.h
+++ b/src/java.desktop/share/native/libfreetype/src/cid/cidparse.h
@@ -4,7 +4,7 @@
  *
  *   CID-keyed Type1 parser (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/cid/cidriver.c b/src/java.desktop/share/native/libfreetype/src/cid/cidriver.c
index 99e7b11839511..4be8a5c00d51a 100644
--- a/src/java.desktop/share/native/libfreetype/src/cid/cidriver.c
+++ b/src/java.desktop/share/native/libfreetype/src/cid/cidriver.c
@@ -4,7 +4,7 @@
  *
  *   CID driver interface (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/cid/cidriver.h b/src/java.desktop/share/native/libfreetype/src/cid/cidriver.h
index a6249385c8116..7ddce431c5b1c 100644
--- a/src/java.desktop/share/native/libfreetype/src/cid/cidriver.h
+++ b/src/java.desktop/share/native/libfreetype/src/cid/cidriver.h
@@ -4,7 +4,7 @@
  *
  *   High-level CID driver interface (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/cid/cidtoken.h b/src/java.desktop/share/native/libfreetype/src/cid/cidtoken.h
index 925951acdbd28..160897d144727 100644
--- a/src/java.desktop/share/native/libfreetype/src/cid/cidtoken.h
+++ b/src/java.desktop/share/native/libfreetype/src/cid/cidtoken.h
@@ -4,7 +4,7 @@
  *
  *   CID token definitions (specification only).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/afmparse.c b/src/java.desktop/share/native/libfreetype/src/psaux/afmparse.c
index db08941def744..e2f6a8e5adb9c 100644
--- a/src/java.desktop/share/native/libfreetype/src/psaux/afmparse.c
+++ b/src/java.desktop/share/native/libfreetype/src/psaux/afmparse.c
@@ -4,7 +4,7 @@
  *
  *   AFM parser (body).
  *
- * Copyright (C) 2006-2023 by
+ * Copyright (C) 2006-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/afmparse.h b/src/java.desktop/share/native/libfreetype/src/psaux/afmparse.h
index 2d3b6e6e1695f..b7766372821a9 100644
--- a/src/java.desktop/share/native/libfreetype/src/psaux/afmparse.h
+++ b/src/java.desktop/share/native/libfreetype/src/psaux/afmparse.h
@@ -4,7 +4,7 @@
  *
  *   AFM parser (specification).
  *
- * Copyright (C) 2006-2023 by
+ * Copyright (C) 2006-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/cffdecode.c b/src/java.desktop/share/native/libfreetype/src/psaux/cffdecode.c
index 562d17d221649..9556e11a5861b 100644
--- a/src/java.desktop/share/native/libfreetype/src/psaux/cffdecode.c
+++ b/src/java.desktop/share/native/libfreetype/src/psaux/cffdecode.c
@@ -4,7 +4,7 @@
  *
  *   PostScript CFF (Type 2) decoding routines (body).
  *
- * Copyright (C) 2017-2023 by
+ * Copyright (C) 2017-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -17,6 +17,7 @@
 
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -1752,22 +1753,9 @@
 
           /* without upper limit the loop below might not finish */
           if ( args[0] > 0x7FFFFFFFL )
-            args[0] = 46341;
+            args[0] = 0xB504F4L;    /* sqrt( 32768.0044 ) */
           else if ( args[0] > 0 )
-          {
-            FT_Fixed  root = args[0];
-            FT_Fixed  new_root;
-
-
-            for (;;)
-            {
-              new_root = ( root + FT_DivFix( args[0], root ) + 1 ) >> 1;
-              if ( new_root == root )
-                break;
-              root = new_root;
-            }
-            args[0] = new_root;
-          }
+            args[0] = (FT_Fixed)FT_SqrtFixed( args[0] );
           else
             args[0] = 0;
           args++;
diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/cffdecode.h b/src/java.desktop/share/native/libfreetype/src/psaux/cffdecode.h
index e8bb4001cba23..038f7235c3d64 100644
--- a/src/java.desktop/share/native/libfreetype/src/psaux/cffdecode.h
+++ b/src/java.desktop/share/native/libfreetype/src/psaux/cffdecode.h
@@ -4,7 +4,7 @@
  *
  *   PostScript CFF (Type 2) decoding routines (specification).
  *
- * Copyright (C) 2017-2023 by
+ * Copyright (C) 2017-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/psauxerr.h b/src/java.desktop/share/native/libfreetype/src/psaux/psauxerr.h
index 895ffa48c2c31..18428c40d5a45 100644
--- a/src/java.desktop/share/native/libfreetype/src/psaux/psauxerr.h
+++ b/src/java.desktop/share/native/libfreetype/src/psaux/psauxerr.h
@@ -4,7 +4,7 @@
  *
  *   PS auxiliary module error codes (specification only).
  *
- * Copyright (C) 2001-2023 by
+ * Copyright (C) 2001-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/psauxmod.c b/src/java.desktop/share/native/libfreetype/src/psaux/psauxmod.c
index 45e35aa53c4cb..6826f9d8d3ede 100644
--- a/src/java.desktop/share/native/libfreetype/src/psaux/psauxmod.c
+++ b/src/java.desktop/share/native/libfreetype/src/psaux/psauxmod.c
@@ -4,7 +4,7 @@
  *
  *   FreeType auxiliary PostScript module implementation (body).
  *
- * Copyright (C) 2000-2023 by
+ * Copyright (C) 2000-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/psauxmod.h b/src/java.desktop/share/native/libfreetype/src/psaux/psauxmod.h
index 94dbf48813c16..82d7e348af813 100644
--- a/src/java.desktop/share/native/libfreetype/src/psaux/psauxmod.h
+++ b/src/java.desktop/share/native/libfreetype/src/psaux/psauxmod.h
@@ -4,7 +4,7 @@
  *
  *   FreeType auxiliary PostScript module implementation (specification).
  *
- * Copyright (C) 2000-2023 by
+ * Copyright (C) 2000-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/psblues.c b/src/java.desktop/share/native/libfreetype/src/psaux/psblues.c
index f9c864fea9aeb..213b943b46576 100644
--- a/src/java.desktop/share/native/libfreetype/src/psaux/psblues.c
+++ b/src/java.desktop/share/native/libfreetype/src/psaux/psblues.c
@@ -54,14 +54,6 @@
 #define FT_COMPONENT  cf2blues
 
 
-  /*
-   * For blue values, the FreeType parser produces an array of integers,
-   * while the Adobe CFF engine produces an array of fixed.
-   * Define a macro to convert FreeType to fixed.
-   */
-#define cf2_blueToFixed( x )  cf2_intToFixed( x )
-
-
   FT_LOCAL_DEF( void )
   cf2_blues_init( CF2_Blues  blues,
                   CF2_Font   font )
@@ -78,10 +70,10 @@
     size_t  numFamilyBlues;
     size_t  numFamilyOtherBlues;
 
-    FT_Pos*  blueValues;
-    FT_Pos*  otherBlues;
-    FT_Pos*  familyBlues;
-    FT_Pos*  familyOtherBlues;
+    FT_Fixed*  blueValues;
+    FT_Fixed*  otherBlues;
+    FT_Fixed*  familyBlues;
+    FT_Fixed*  familyOtherBlues;
 
     size_t     i;
     CF2_Fixed  emBoxBottom, emBoxTop;
@@ -138,13 +130,13 @@
       emBoxTop    = CF2_ICF_Top;
     }
 
-    if ( cf2_getLanguageGroup( decoder ) == 1                   &&
-         ( numBlueValues == 0                                 ||
-           ( numBlueValues == 4                             &&
-             cf2_blueToFixed( blueValues[0] ) < emBoxBottom &&
-             cf2_blueToFixed( blueValues[1] ) < emBoxBottom &&
-             cf2_blueToFixed( blueValues[2] ) > emBoxTop    &&
-             cf2_blueToFixed( blueValues[3] ) > emBoxTop    ) ) )
+    if ( cf2_getLanguageGroup( decoder ) == 1     &&
+         ( numBlueValues == 0                   ||
+           ( numBlueValues == 4               &&
+             blueValues[0] < emBoxBottom      &&
+             blueValues[1] < emBoxBottom      &&
+             blueValues[2] > emBoxTop         &&
+             blueValues[3] > emBoxTop         ) ) )
     {
       /*
        * Construct hint edges suitable for synthetic ghost hints at top
@@ -189,10 +181,8 @@
     /* bottom zones                                                      */
     for ( i = 0; i < numBlueValues; i += 2 )
     {
-      blues->zone[blues->count].csBottomEdge =
-        cf2_blueToFixed( blueValues[i] );
-      blues->zone[blues->count].csTopEdge =
-        cf2_blueToFixed( blueValues[i + 1] );
+      blues->zone[blues->count].csBottomEdge = blueValues[i];
+      blues->zone[blues->count].csTopEdge    = blueValues[i + 1];
 
       zoneHeight = SUB_INT32( blues->zone[blues->count].csTopEdge,
                               blues->zone[blues->count].csBottomEdge );
@@ -238,10 +228,8 @@
 
     for ( i = 0; i < numOtherBlues; i += 2 )
     {
-      blues->zone[blues->count].csBottomEdge =
-        cf2_blueToFixed( otherBlues[i] );
-      blues->zone[blues->count].csTopEdge =
-        cf2_blueToFixed( otherBlues[i + 1] );
+      blues->zone[blues->count].csBottomEdge = otherBlues[i];
+      blues->zone[blues->count].csTopEdge    = otherBlues[i + 1];
 
       zoneHeight = SUB_INT32( blues->zone[blues->count].csTopEdge,
                               blues->zone[blues->count].csBottomEdge );
@@ -299,7 +287,7 @@
         for ( j = 0; j < numFamilyOtherBlues; j += 2 )
         {
           /* top edge */
-          flatFamilyEdge = cf2_blueToFixed( familyOtherBlues[j + 1] );
+          flatFamilyEdge = familyOtherBlues[j + 1];
 
           diff = cf2_fixedAbs( SUB_INT32( flatEdge, flatFamilyEdge ) );
 
@@ -317,7 +305,7 @@
         if ( numFamilyBlues >= 2 )
         {
           /* top edge */
-          flatFamilyEdge = cf2_blueToFixed( familyBlues[1] );
+          flatFamilyEdge = familyBlues[1];
 
           diff = cf2_fixedAbs( SUB_INT32( flatEdge, flatFamilyEdge ) );
 
@@ -337,7 +325,7 @@
         for ( j = 2; j < numFamilyBlues; j += 2 )
         {
           /* bottom edge */
-          flatFamilyEdge = cf2_blueToFixed( familyBlues[j] );
+          flatFamilyEdge = familyBlues[j];
 
           /* adjust edges of top zone upward by twice darkening amount */
           flatFamilyEdge += 2 * font->darkenY;      /* bottom edge */
diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/psconv.c b/src/java.desktop/share/native/libfreetype/src/psaux/psconv.c
index b9c7138d84637..56c0ecd1d7f21 100644
--- a/src/java.desktop/share/native/libfreetype/src/psaux/psconv.c
+++ b/src/java.desktop/share/native/libfreetype/src/psaux/psconv.c
@@ -4,7 +4,7 @@
  *
  *   Some convenience conversions (body).
  *
- * Copyright (C) 2006-2023 by
+ * Copyright (C) 2006-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/psconv.h b/src/java.desktop/share/native/libfreetype/src/psaux/psconv.h
index b7c3ee00be872..91fcd15a1c96b 100644
--- a/src/java.desktop/share/native/libfreetype/src/psaux/psconv.h
+++ b/src/java.desktop/share/native/libfreetype/src/psaux/psconv.h
@@ -4,7 +4,7 @@
  *
  *   Some convenience conversions (specification).
  *
- * Copyright (C) 2006-2023 by
+ * Copyright (C) 2006-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/psft.c b/src/java.desktop/share/native/libfreetype/src/psaux/psft.c
index 618864e6e0728..fd0abe17154c7 100644
--- a/src/java.desktop/share/native/libfreetype/src/psaux/psft.c
+++ b/src/java.desktop/share/native/libfreetype/src/psaux/psft.c
@@ -566,12 +566,12 @@
   FT_LOCAL_DEF( void )
   cf2_getBlueValues( PS_Decoder*  decoder,
                      size_t*      count,
-                     FT_Pos*     *data )
+                     FT_Fixed*   *data )
   {
     FT_ASSERT( decoder && decoder->current_subfont );
 
     *count = decoder->current_subfont->private_dict.num_blue_values;
-    *data  = (FT_Pos*)
+    *data  = (FT_Fixed*)
                &decoder->current_subfont->private_dict.blue_values;
   }
 
@@ -579,12 +579,12 @@
   FT_LOCAL_DEF( void )
   cf2_getOtherBlues( PS_Decoder*  decoder,
                      size_t*      count,
-                     FT_Pos*     *data )
+                     FT_Fixed*   *data )
   {
     FT_ASSERT( decoder && decoder->current_subfont );
 
     *count = decoder->current_subfont->private_dict.num_other_blues;
-    *data  = (FT_Pos*)
+    *data  = (FT_Fixed*)
                &decoder->current_subfont->private_dict.other_blues;
   }
 
@@ -592,12 +592,12 @@
   FT_LOCAL_DEF( void )
   cf2_getFamilyBlues( PS_Decoder*  decoder,
                       size_t*      count,
-                      FT_Pos*     *data )
+                      FT_Fixed*   *data )
   {
     FT_ASSERT( decoder && decoder->current_subfont );
 
     *count = decoder->current_subfont->private_dict.num_family_blues;
-    *data  = (FT_Pos*)
+    *data  = (FT_Fixed*)
                &decoder->current_subfont->private_dict.family_blues;
   }
 
@@ -605,12 +605,12 @@
   FT_LOCAL_DEF( void )
   cf2_getFamilyOtherBlues( PS_Decoder*  decoder,
                            size_t*      count,
-                           FT_Pos*     *data )
+                           FT_Fixed*   *data )
   {
     FT_ASSERT( decoder && decoder->current_subfont );
 
     *count = decoder->current_subfont->private_dict.num_family_other_blues;
-    *data  = (FT_Pos*)
+    *data  = (FT_Fixed*)
                &decoder->current_subfont->private_dict.family_other_blues;
   }
 
diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/psft.h b/src/java.desktop/share/native/libfreetype/src/psaux/psft.h
index 3da454e6012a5..d9082f3a2be12 100644
--- a/src/java.desktop/share/native/libfreetype/src/psaux/psft.h
+++ b/src/java.desktop/share/native/libfreetype/src/psaux/psft.h
@@ -92,19 +92,19 @@ FT_BEGIN_HEADER
   FT_LOCAL( void )
   cf2_getBlueValues( PS_Decoder*  decoder,
                      size_t*      count,
-                     FT_Pos*     *data );
+                     FT_Fixed*   *data );
   FT_LOCAL( void )
   cf2_getOtherBlues( PS_Decoder*  decoder,
                      size_t*      count,
-                     FT_Pos*     *data );
+                     FT_Fixed*   *data );
   FT_LOCAL( void )
   cf2_getFamilyBlues( PS_Decoder*  decoder,
                       size_t*      count,
-                      FT_Pos*     *data );
+                      FT_Fixed*   *data );
   FT_LOCAL( void )
   cf2_getFamilyOtherBlues( PS_Decoder*  decoder,
                            size_t*      count,
-                           FT_Pos*     *data );
+                           FT_Fixed*   *data );
 
   FT_LOCAL( CF2_Int )
   cf2_getLanguageGroup( PS_Decoder*  decoder );
diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/psintrp.c b/src/java.desktop/share/native/libfreetype/src/psaux/psintrp.c
index 6c640eebd5a50..7572e225e3777 100644
--- a/src/java.desktop/share/native/libfreetype/src/psaux/psintrp.c
+++ b/src/java.desktop/share/native/libfreetype/src/psaux/psintrp.c
@@ -37,6 +37,7 @@
 
 
 #include "psft.h"
+#include 
 #include 
 #include 
 
@@ -428,6 +429,8 @@
     base  = cf2_stack_count( opStack ) - numOperands;
     delta = base + numBlends;
 
+    FT_TRACE6(( " (" ));
+
     for ( i = 0; i < numBlends; i++ )
     {
       const CF2_Fixed*  weight = &blend->BV[1];
@@ -442,10 +445,14 @@
                                     cf2_stack_getReal( opStack,
                                                        delta++ ) ) );
 
+      FT_TRACE6(( "%f ", (double)sum / 65536 ));
+
       /* store blended result  */
       cf2_stack_setReal( opStack, i + base, sum );
     }
 
+    FT_TRACE6(( "blended)\n" ));
+
     /* leave only `numBlends' results on stack */
     cf2_stack_pop( opStack, numOperands - numBlends );
   }
@@ -734,7 +741,7 @@
           FT_UInt  numBlends;
 
 
-          FT_TRACE4(( " blend\n" ));
+          FT_TRACE4(( " blend" ));
 
           if ( !font->isCFF2 )
             break;    /* clear stack & ignore */
@@ -2275,23 +2282,7 @@
 
                     arg = cf2_stack_popFixed( opStack );
                     if ( arg > 0 )
-                    {
-                      /* use a start value that doesn't make */
-                      /* the algorithm's addition overflow   */
-                      FT_Fixed  root = arg < 10 ? arg : arg >> 1;
-                      FT_Fixed  new_root;
-
-
-                      /* Babylonian method */
-                      for (;;)
-                      {
-                        new_root = ( root + FT_DivFix( arg, root ) + 1 ) >> 1;
-                        if ( new_root == root )
-                          break;
-                        root = new_root;
-                      }
-                      arg = new_root;
-                    }
+                      arg = (CF2_F16Dot16)FT_SqrtFixed( (FT_UInt32)arg );
                     else
                       arg = 0;
 
diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/psobjs.c b/src/java.desktop/share/native/libfreetype/src/psaux/psobjs.c
index 8da755d0e5769..eca465f009e96 100644
--- a/src/java.desktop/share/native/libfreetype/src/psaux/psobjs.c
+++ b/src/java.desktop/share/native/libfreetype/src/psaux/psobjs.c
@@ -4,7 +4,7 @@
  *
  *   Auxiliary functions for PostScript fonts (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -23,6 +23,7 @@
 
 #include "psobjs.h"
 #include "psconv.h"
+#include "psft.h"
 
 #include "psauxerr.h"
 #include "psauxmod.h"
@@ -200,7 +201,9 @@
     /* add the object to the base block and adjust offset */
     table->elements[idx] = FT_OFFSET( table->block, table->cursor );
     table->lengths [idx] = length;
-    FT_MEM_COPY( table->block + table->cursor, object, length );
+    /* length == 0 also implies a NULL destination, so skip the copy call */
+    if ( length > 0 )
+      FT_MEM_COPY( table->block + table->cursor, object, length );
 
     table->cursor += length;
     return FT_Err_Ok;
@@ -1624,7 +1627,7 @@
     if ( builder->load_points )
     {
       FT_Vector*  point   = outline->points + outline->n_points;
-      FT_Byte*    control = (FT_Byte*)outline->tags + outline->n_points;
+      FT_Byte*    control = outline->tags   + outline->n_points;
 
 
       point->x = FIXED_TO_INT( x );
@@ -1677,8 +1680,7 @@
     if ( !error )
     {
       if ( outline->n_contours > 0 )
-        outline->contours[outline->n_contours - 1] =
-          (short)( outline->n_points - 1 );
+        outline->contours[outline->n_contours - 1] = outline->n_points - 1;
 
       outline->n_contours++;
     }
@@ -1740,7 +1742,7 @@
     {
       FT_Vector*  p1      = outline->points + first;
       FT_Vector*  p2      = outline->points + outline->n_points - 1;
-      FT_Byte*    control = (FT_Byte*)outline->tags + outline->n_points - 1;
+      FT_Byte*    control = outline->tags   + outline->n_points - 1;
 
 
       /* `delete' last point only if it coincides with the first */
@@ -1760,8 +1762,7 @@
         outline->n_points--;
       }
       else
-        outline->contours[outline->n_contours - 1] =
-          (short)( outline->n_points - 1 );
+        outline->contours[outline->n_contours - 1] = outline->n_points - 1;
     }
   }
 
@@ -1899,7 +1900,7 @@
     if ( builder->load_points )
     {
       FT_Vector*  point   = outline->points + outline->n_points;
-      FT_Byte*    control = (FT_Byte*)outline->tags + outline->n_points;
+      FT_Byte*    control = outline->tags   + outline->n_points;
 
 #ifdef CFF_CONFIG_OPTION_OLD_ENGINE
       PS_Driver  driver   = (PS_Driver)FT_FACE_DRIVER( builder->face );
@@ -1959,8 +1960,7 @@
     if ( !error )
     {
       if ( outline->n_contours > 0 )
-        outline->contours[outline->n_contours - 1] =
-          (short)( outline->n_points - 1 );
+        outline->contours[outline->n_contours - 1] = outline->n_points - 1;
 
       outline->n_contours++;
     }
@@ -2019,7 +2019,7 @@
     {
       FT_Vector*  p1      = outline->points + first;
       FT_Vector*  p2      = outline->points + outline->n_points - 1;
-      FT_Byte*    control = (FT_Byte*)outline->tags + outline->n_points - 1;
+      FT_Byte*    control = outline->tags   + outline->n_points - 1;
 
 
       /* `delete' last point only if it coincides with the first    */
@@ -2039,8 +2039,7 @@
         outline->n_points--;
       }
       else
-        outline->contours[outline->n_contours - 1] =
-          (short)( outline->n_points - 1 );
+        outline->contours[outline->n_contours - 1] = outline->n_points - 1;
     }
   }
 
@@ -2188,7 +2187,7 @@
     if ( builder->load_points )
     {
       FT_Vector*  point   = outline->points + outline->n_points;
-      FT_Byte*    control = (FT_Byte*)outline->tags + outline->n_points;
+      FT_Byte*    control = outline->tags   + outline->n_points;
 
 #ifdef CFF_CONFIG_OPTION_OLD_ENGINE
       PS_Driver  driver   = (PS_Driver)FT_FACE_DRIVER( builder->face );
@@ -2267,8 +2266,7 @@
     if ( !error )
     {
       if ( outline->n_contours > 0 )
-        outline->contours[outline->n_contours - 1] =
-          (short)( outline->n_points - 1 );
+        outline->contours[outline->n_contours - 1] = outline->n_points - 1;
 
       outline->n_contours++;
     }
@@ -2327,7 +2325,7 @@
     {
       FT_Vector*  p1      = outline->points + first;
       FT_Vector*  p2      = outline->points + outline->n_points - 1;
-      FT_Byte*    control = (FT_Byte*)outline->tags + outline->n_points - 1;
+      FT_Byte*    control = outline->tags   + outline->n_points - 1;
 
 
       /* `delete' last point only if it coincides with the first */
@@ -2347,8 +2345,7 @@
         outline->n_points--;
       }
       else
-        outline->contours[outline->n_contours - 1] =
-          (short)( outline->n_points - 1 );
+        outline->contours[outline->n_contours - 1] = outline->n_points - 1;
     }
   }
 
@@ -2463,19 +2460,20 @@
 
     count = cpriv->num_blue_values = priv->num_blue_values;
     for ( n = 0; n < count; n++ )
-      cpriv->blue_values[n] = (FT_Pos)priv->blue_values[n];
+      cpriv->blue_values[n] = cf2_intToFixed( priv->blue_values[n] );
 
     count = cpriv->num_other_blues = priv->num_other_blues;
     for ( n = 0; n < count; n++ )
-      cpriv->other_blues[n] = (FT_Pos)priv->other_blues[n];
+      cpriv->other_blues[n] = cf2_intToFixed( priv->other_blues[n] );
 
     count = cpriv->num_family_blues = priv->num_family_blues;
     for ( n = 0; n < count; n++ )
-      cpriv->family_blues[n] = (FT_Pos)priv->family_blues[n];
+      cpriv->family_blues[n] = cf2_intToFixed( priv->family_blues[n] );
 
     count = cpriv->num_family_other_blues = priv->num_family_other_blues;
     for ( n = 0; n < count; n++ )
-      cpriv->family_other_blues[n] = (FT_Pos)priv->family_other_blues[n];
+      cpriv->family_other_blues[n] =
+        cf2_intToFixed( priv->family_other_blues[n] );
 
     cpriv->blue_scale = priv->blue_scale;
     cpriv->blue_shift = (FT_Pos)priv->blue_shift;
diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/psobjs.h b/src/java.desktop/share/native/libfreetype/src/psaux/psobjs.h
index d5bce541082fd..345fc8a733561 100644
--- a/src/java.desktop/share/native/libfreetype/src/psaux/psobjs.h
+++ b/src/java.desktop/share/native/libfreetype/src/psaux/psobjs.h
@@ -4,7 +4,7 @@
  *
  *   Auxiliary functions for PostScript fonts (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/t1cmap.c b/src/java.desktop/share/native/libfreetype/src/psaux/t1cmap.c
index c4bcf599ea3ad..5681c3bd0fdbb 100644
--- a/src/java.desktop/share/native/libfreetype/src/psaux/t1cmap.c
+++ b/src/java.desktop/share/native/libfreetype/src/psaux/t1cmap.c
@@ -4,7 +4,7 @@
  *
  *   Type 1 character map support (body).
  *
- * Copyright (C) 2002-2023 by
+ * Copyright (C) 2002-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/t1cmap.h b/src/java.desktop/share/native/libfreetype/src/psaux/t1cmap.h
index b3702498a5537..445e6a2784f52 100644
--- a/src/java.desktop/share/native/libfreetype/src/psaux/t1cmap.h
+++ b/src/java.desktop/share/native/libfreetype/src/psaux/t1cmap.h
@@ -4,7 +4,7 @@
  *
  *   Type 1 character map support (specification).
  *
- * Copyright (C) 2002-2023 by
+ * Copyright (C) 2002-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/t1decode.c b/src/java.desktop/share/native/libfreetype/src/psaux/t1decode.c
index 4b6b969bcb99e..c74baa8038f3f 100644
--- a/src/java.desktop/share/native/libfreetype/src/psaux/t1decode.c
+++ b/src/java.desktop/share/native/libfreetype/src/psaux/t1decode.c
@@ -4,7 +4,7 @@
  *
  *   PostScript Type 1 decoding routines (body).
  *
- * Copyright (C) 2000-2023 by
+ * Copyright (C) 2000-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/t1decode.h b/src/java.desktop/share/native/libfreetype/src/psaux/t1decode.h
index 0970def960b22..16203b8f734f8 100644
--- a/src/java.desktop/share/native/libfreetype/src/psaux/t1decode.h
+++ b/src/java.desktop/share/native/libfreetype/src/psaux/t1decode.h
@@ -4,7 +4,7 @@
  *
  *   PostScript Type 1 decoding routines (specification).
  *
- * Copyright (C) 2000-2023 by
+ * Copyright (C) 2000-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/pshinter/pshalgo.c b/src/java.desktop/share/native/libfreetype/src/pshinter/pshalgo.c
index 4f622e1e440f0..967767b348573 100644
--- a/src/java.desktop/share/native/libfreetype/src/pshinter/pshalgo.c
+++ b/src/java.desktop/share/native/libfreetype/src/pshinter/pshalgo.c
@@ -4,7 +4,7 @@
  *
  *   PostScript hinting algorithm (body).
  *
- * Copyright (C) 2001-2023 by
+ * Copyright (C) 2001-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used
@@ -1118,7 +1118,7 @@
     FT_UInt     n;
     PSH_Point   point = glyph->points;
     FT_Vector*  vec   = glyph->outline->points;
-    char*       tags  = glyph->outline->tags;
+    FT_Byte*    tags  = glyph->outline->tags;
 
 
     for ( n = 0; n < glyph->num_points; n++ )
@@ -1171,8 +1171,8 @@
          FT_QNEW_ARRAY( glyph->contours, outline->n_contours ) )
       goto Exit;
 
-    glyph->num_points   = (FT_UInt)outline->n_points;
-    glyph->num_contours = (FT_UInt)outline->n_contours;
+    glyph->num_points   = outline->n_points;
+    glyph->num_contours = outline->n_contours;
 
     {
       FT_UInt      first = 0, next, n;
@@ -1186,7 +1186,7 @@
         PSH_Point  point;
 
 
-        next  = (FT_UInt)outline->contours[n] + 1;
+        next  = outline->contours[n] + 1;
         count = next - first;
 
         contour->start = points + first;
diff --git a/src/java.desktop/share/native/libfreetype/src/pshinter/pshalgo.h b/src/java.desktop/share/native/libfreetype/src/pshinter/pshalgo.h
index 3f0ba28a6930b..fb362f061b6e3 100644
--- a/src/java.desktop/share/native/libfreetype/src/pshinter/pshalgo.h
+++ b/src/java.desktop/share/native/libfreetype/src/pshinter/pshalgo.h
@@ -4,7 +4,7 @@
  *
  *   PostScript hinting algorithm (specification).
  *
- * Copyright (C) 2001-2023 by
+ * Copyright (C) 2001-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/pshinter/pshglob.c b/src/java.desktop/share/native/libfreetype/src/pshinter/pshglob.c
index d4c5eb32b1c57..435f45838ffd7 100644
--- a/src/java.desktop/share/native/libfreetype/src/pshinter/pshglob.c
+++ b/src/java.desktop/share/native/libfreetype/src/pshinter/pshglob.c
@@ -5,7 +5,7 @@
  *   PostScript hinter global hinting management (body).
  *   Inspired by the new auto-hinter module.
  *
- * Copyright (C) 2001-2023 by
+ * Copyright (C) 2001-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used
diff --git a/src/java.desktop/share/native/libfreetype/src/pshinter/pshglob.h b/src/java.desktop/share/native/libfreetype/src/pshinter/pshglob.h
index 579eb2148a572..c5a5c91316806 100644
--- a/src/java.desktop/share/native/libfreetype/src/pshinter/pshglob.h
+++ b/src/java.desktop/share/native/libfreetype/src/pshinter/pshglob.h
@@ -4,7 +4,7 @@
  *
  *   PostScript hinter global hinting management.
  *
- * Copyright (C) 2001-2023 by
+ * Copyright (C) 2001-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/pshinter/pshmod.c b/src/java.desktop/share/native/libfreetype/src/pshinter/pshmod.c
index 974a99e0186d3..9965d5b16bf16 100644
--- a/src/java.desktop/share/native/libfreetype/src/pshinter/pshmod.c
+++ b/src/java.desktop/share/native/libfreetype/src/pshinter/pshmod.c
@@ -4,7 +4,7 @@
  *
  *   FreeType PostScript hinter module implementation (body).
  *
- * Copyright (C) 2001-2023 by
+ * Copyright (C) 2001-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/pshinter/pshmod.h b/src/java.desktop/share/native/libfreetype/src/pshinter/pshmod.h
index 4bd781a35d76b..62ac0a60fdce5 100644
--- a/src/java.desktop/share/native/libfreetype/src/pshinter/pshmod.h
+++ b/src/java.desktop/share/native/libfreetype/src/pshinter/pshmod.h
@@ -4,7 +4,7 @@
  *
  *   PostScript hinter module interface (specification).
  *
- * Copyright (C) 2001-2023 by
+ * Copyright (C) 2001-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/pshinter/pshnterr.h b/src/java.desktop/share/native/libfreetype/src/pshinter/pshnterr.h
index 97624952d8caf..e9641340e5301 100644
--- a/src/java.desktop/share/native/libfreetype/src/pshinter/pshnterr.h
+++ b/src/java.desktop/share/native/libfreetype/src/pshinter/pshnterr.h
@@ -4,7 +4,7 @@
  *
  *   PS Hinter error codes (specification only).
  *
- * Copyright (C) 2003-2023 by
+ * Copyright (C) 2003-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/pshinter/pshrec.c b/src/java.desktop/share/native/libfreetype/src/pshinter/pshrec.c
index 680e6d013588f..0b2b549fc2969 100644
--- a/src/java.desktop/share/native/libfreetype/src/pshinter/pshrec.c
+++ b/src/java.desktop/share/native/libfreetype/src/pshinter/pshrec.c
@@ -4,7 +4,7 @@
  *
  *   FreeType PostScript hints recorder (body).
  *
- * Copyright (C) 2001-2023 by
+ * Copyright (C) 2001-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -806,7 +806,7 @@
   ps_hints_stem( PS_Hints  hints,
                  FT_UInt   dimension,
                  FT_Int    count,
-                 FT_Long*  stems )
+                 FT_Pos*   stems )
   {
     PS_Dimension  dim;
 
diff --git a/src/java.desktop/share/native/libfreetype/src/pshinter/pshrec.h b/src/java.desktop/share/native/libfreetype/src/pshinter/pshrec.h
index 0b2484af12100..7e375af7ba874 100644
--- a/src/java.desktop/share/native/libfreetype/src/pshinter/pshrec.h
+++ b/src/java.desktop/share/native/libfreetype/src/pshinter/pshrec.h
@@ -4,7 +4,7 @@
  *
  *   Postscript (Type1/Type2) hints recorder (specification).
  *
- * Copyright (C) 2001-2023 by
+ * Copyright (C) 2001-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/psnames/psmodule.c b/src/java.desktop/share/native/libfreetype/src/psnames/psmodule.c
index 8203a0465d25e..35d054d1cfb66 100644
--- a/src/java.desktop/share/native/libfreetype/src/psnames/psmodule.c
+++ b/src/java.desktop/share/native/libfreetype/src/psnames/psmodule.c
@@ -4,7 +4,7 @@
  *
  *   psnames module implementation (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/psnames/psmodule.h b/src/java.desktop/share/native/libfreetype/src/psnames/psmodule.h
index 0904700bfb8d1..770458316b1e0 100644
--- a/src/java.desktop/share/native/libfreetype/src/psnames/psmodule.h
+++ b/src/java.desktop/share/native/libfreetype/src/psnames/psmodule.h
@@ -4,7 +4,7 @@
  *
  *   High-level psnames module interface (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/psnames/psnamerr.h b/src/java.desktop/share/native/libfreetype/src/psnames/psnamerr.h
index 0073f8228486b..e123eb65e3930 100644
--- a/src/java.desktop/share/native/libfreetype/src/psnames/psnamerr.h
+++ b/src/java.desktop/share/native/libfreetype/src/psnames/psnamerr.h
@@ -4,7 +4,7 @@
  *
  *   PS names module error codes (specification only).
  *
- * Copyright (C) 2001-2023 by
+ * Copyright (C) 2001-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/psnames/pstables.h b/src/java.desktop/share/native/libfreetype/src/psnames/pstables.h
index 7f92cce603960..2a941b0460969 100644
--- a/src/java.desktop/share/native/libfreetype/src/psnames/pstables.h
+++ b/src/java.desktop/share/native/libfreetype/src/psnames/pstables.h
@@ -4,7 +4,7 @@
  *
  *   PostScript glyph names.
  *
- * Copyright (C) 2005-2023 by
+ * Copyright (C) 2005-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/raster/ftmisc.h b/src/java.desktop/share/native/libfreetype/src/raster/ftmisc.h
index 33dbfd631e92e..943f2aa0a50a0 100644
--- a/src/java.desktop/share/native/libfreetype/src/raster/ftmisc.h
+++ b/src/java.desktop/share/native/libfreetype/src/raster/ftmisc.h
@@ -5,7 +5,7 @@
  *   Miscellaneous macros for stand-alone rasterizer (specification
  *   only).
  *
- * Copyright (C) 2005-2023 by
+ * Copyright (C) 2005-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used
@@ -92,27 +92,6 @@
 #endif
 
 
-  static FT_Long
-  FT_MulDiv( FT_Long  a,
-             FT_Long  b,
-             FT_Long  c )
-  {
-    FT_Int   s;
-    FT_Long  d;
-
-
-    s = 1;
-    if ( a < 0 ) { a = -a; s = -1; }
-    if ( b < 0 ) { b = -b; s = -s; }
-    if ( c < 0 ) { c = -c; s = -s; }
-
-    d = (FT_Long)( c > 0 ? ( (FT_Int64)a * b + ( c >> 1 ) ) / c
-                         : 0x7FFFFFFFL );
-
-    return ( s > 0 ) ? d : -d;
-  }
-
-
   static FT_Long
   FT_MulDiv_No_Round( FT_Long  a,
                       FT_Long  b,
diff --git a/src/java.desktop/share/native/libfreetype/src/raster/ftraster.c b/src/java.desktop/share/native/libfreetype/src/raster/ftraster.c
index 192ca0701a242..e4b7b937d5a8d 100644
--- a/src/java.desktop/share/native/libfreetype/src/raster/ftraster.c
+++ b/src/java.desktop/share/native/libfreetype/src/raster/ftraster.c
@@ -4,7 +4,7 @@
  *
  *   The FreeType glyph rasterizer (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -63,8 +63,7 @@
 #else /* !STANDALONE_ */
 
 #include "ftraster.h"
-#include  /* for FT_MulDiv and FT_MulDiv_No_Round */
-#include          /* for FT_Outline_Get_CBox              */
+#include  /* for FT_MulDiv_No_Round */
 
 #endif /* !STANDALONE_ */
 
@@ -115,12 +114,12 @@
    *   a change of direction is detected in the outline, a new profile is
    *   generated until the end of the outline.
    *
-   *   Note that when all profiles have been generated, the function
-   *   Finalize_Profile_Table() is used to record, for each profile, its
-   *   bottom-most scanline as well as the scanline above its upmost
-   *   boundary.  These positions are called `y-turns' because they (sort
-   *   of) correspond to local extrema.  They are stored in a sorted list
-   *   built from the top of the render pool as a downwards stack:
+   *   Note that, for all generated profiles, the function End_Profile()
+   *   is used to record all their bottom-most scanlines as well as the
+   *   scanline above their upmost boundary.  These positions are called
+   *   `y-turns' because they (sort of) correspond to local extrema.
+   *   They are stored in a sorted list built from the top of the render
+   *   pool as a downwards stack:
    *
    *     _ _ _______________________________________
    *                           |                    |
@@ -136,7 +135,7 @@
    *   optimize performance (see technical note on the sweep below).
    *
    *   Of course, the raster detects whether the two stacks collide and
-   *   handles the situation properly.
+   *   handles the situation by bisecting the job and restarting.
    *
    */
 
@@ -252,7 +251,6 @@
   /* On the other hand, SMulDiv means `Slow MulDiv', and is used typically */
   /* for clipping computations.  It simply uses the FT_MulDiv() function   */
   /* defined in `ftcalc.h'.                                                */
-#define SMulDiv           FT_MulDiv
 #define SMulDiv_No_Round  FT_MulDiv_No_Round
 
   /* The rasterizer is a very general purpose component; please leave */
@@ -305,16 +303,6 @@
   typedef unsigned char   Byte, *PByte;
   typedef char            Bool;
 
-
-  typedef union  Alignment_
-  {
-    Long    l;
-    void*   p;
-    void  (*f)(void);
-
-  } Alignment, *PAlignment;
-
-
   typedef struct  TPoint_
   {
     Long  x;
@@ -327,6 +315,7 @@
 #define Flow_Up           0x08U
 #define Overshoot_Top     0x10U
 #define Overshoot_Bottom  0x20U
+#define Dropout           0x40U
 
 
   /* States of each line, arc, and profile */
@@ -345,31 +334,28 @@
 
   struct  TProfile_
   {
-    FT_F26Dot6  X;           /* current coordinate during sweep          */
     PProfile    link;        /* link to next profile (various purposes)  */
-    PLong       offset;      /* start of profile's data in render pool   */
+    PProfile    next;        /* next profile in same contour, used       */
+                             /* during drop-out control                  */
+    Int         offset;      /* bottom or currently scanned array index  */
+    Int         height;      /* profile's height in scanlines            */
+    Int         start;       /* profile's starting scanline, also use    */
+                             /* as activation counter                    */
     UShort      flags;       /* Bit 0-2: drop-out mode                   */
                              /* Bit 3: profile orientation (up/down)     */
                              /* Bit 4: is top profile?                   */
                              /* Bit 5: is bottom profile?                */
-    Long        height;      /* profile's height in scanlines            */
-    Long        start;       /* profile's starting scanline              */
+                             /* Bit 6: dropout detected                  */
 
-    Int         countL;      /* number of lines to step before this      */
-                             /* profile becomes drawable                 */
-
-    PProfile    next;        /* next profile in same contour, used       */
-                             /* during drop-out control                  */
+    FT_F26Dot6  X;           /* current coordinate during sweep          */
+    Long        x[1];        /* actually variable array of scanline      */
+                             /* intersections with `height` elements     */
   };
 
   typedef PProfile   TProfileList;
   typedef PProfile*  PProfileList;
 
 
-#define AlignProfileSize \
-  ( ( sizeof ( TProfile ) + sizeof ( Alignment ) - 1 ) / sizeof ( Long ) )
-
-
 #undef RAS_ARG
 #undef RAS_ARGS
 #undef RAS_VAR
@@ -407,15 +393,13 @@
 
   /* prototypes used for sweep function dispatch */
   typedef void
-  Function_Sweep_Init( RAS_ARGS Short  min,
-                                Short  max );
+  Function_Sweep_Init( RAS_ARGS Int  min,
+                                Int  max );
 
   typedef void
-  Function_Sweep_Span( RAS_ARGS Short       y,
+  Function_Sweep_Span( RAS_ARGS Int         y,
                                 FT_F26Dot6  x1,
-                                FT_F26Dot6  x2,
-                                PProfile    left,
-                                PProfile    right );
+                                FT_F26Dot6  x2 );
 
   typedef void
   Function_Sweep_Step( RAS_ARG );
@@ -441,8 +425,7 @@
           (Bool)( x - FLOOR( x ) >= ras.precision_half )
 
   /* Smart dropout rounding to find which pixel is closer to span ends. */
-  /* To mimick Windows, symmetric cases break down indepenently of the  */
-  /* precision.                                                         */
+  /* To mimic Windows, symmetric cases do not depend on the precision.  */
 #define SMART( p, q )  FLOOR( ( (p) + (q) + ras.precision * 63 / 64 ) >> 1 )
 
 #if FT_RENDER_POOL_SIZE > 2048
@@ -462,7 +445,6 @@
     Int         precision_half;
     Int         precision_scale;
     Int         precision_step;
-    Int         precision_jitter;
 
     PLong       buff;               /* The profiles buffer                 */
     PLong       sizeBuff;           /* Render pool size                    */
@@ -471,24 +453,14 @@
 
     FT_Error    error;
 
-    Int         numTurns;           /* number of Y-turns in outline        */
-
     Byte        dropOutControl;     /* current drop_out control method     */
 
-    UShort      bWidth;             /* target bitmap width                 */
-    PByte       bOrigin;            /* target bitmap bottom-left origin    */
-    PByte       bLine;              /* target bitmap current line          */
-
     Long        lastX, lastY;
     Long        minY, maxY;
 
     UShort      num_Profs;          /* current number of profiles          */
+    Int         numTurns;           /* number of Y-turns in outline        */
 
-    Bool        fresh;              /* signals a fresh new profile which   */
-                                    /* `start' field must be completed     */
-    Bool        joint;              /* signals that the last arc ended     */
-                                    /* exactly on a scanline.  Allows      */
-                                    /* removal of doublets                 */
     PProfile    cProfile;           /* current profile                     */
     PProfile    fProfile;           /* head of linked list of profiles     */
     PProfile    gProfile;           /* contour's first profile in case     */
@@ -496,9 +468,14 @@
 
     TStates     state;              /* rendering state                     */
 
-    FT_Bitmap   target;             /* description of target bit/pixmap    */
     FT_Outline  outline;
 
+    Int         bTop;               /* target bitmap max line  index       */
+    Int         bRight;             /* target bitmap rightmost index       */
+    Int         bPitch;             /* target bitmap pitch                 */
+    PByte       bOrigin;            /* target bitmap bottom-left origin    */
+    PByte       bLine;              /* target bitmap current line          */
+
     /* dispatch variables */
 
     Function_Sweep_Init*  Proc_Sweep_Init;
@@ -563,37 +540,82 @@
      *
      *   256 / (1 << 12) = 0.0625 pixels.
      *
-     * `precision_jitter' is an epsilon threshold used in
-     * `Vertical_Sweep_Span' to deal with small imperfections in the Bezier
-     * decomposition (after all, we are working with approximations only);
-     * it avoids switching on additional pixels which would cause artifacts
-     * otherwise.
-     *
-     * The value of `precision_jitter' has been determined heuristically.
-     *
      */
 
     if ( High )
     {
       ras.precision_bits   = 12;
       ras.precision_step   = 256;
-      ras.precision_jitter = 30;
     }
     else
     {
       ras.precision_bits   = 6;
       ras.precision_step   = 32;
-      ras.precision_jitter = 2;
     }
 
-    FT_TRACE6(( "Set_High_Precision(%s)\n", High ? "true" : "false" ));
-
     ras.precision       = 1 << ras.precision_bits;
     ras.precision_half  = ras.precision >> 1;
     ras.precision_scale = ras.precision >> Pixel_Bits;
   }
 
 
+  /**************************************************************************
+   *
+   * @Function:
+   *   Insert_Y_Turn
+   *
+   * @Description:
+   *   Insert a salient into the sorted list placed on top of the render
+   *   pool.
+   *
+   * @Input:
+   *   New y scanline position.
+   *
+   * @Return:
+   *   SUCCESS on success.  FAILURE in case of overflow.
+   */
+  static Bool
+  Insert_Y_Turns( RAS_ARGS Int  y,
+                           Int  top )
+  {
+    Int    n       = ras.numTurns;
+    PLong  y_turns = ras.maxBuff;
+
+
+    /* update top value */
+    if ( n == 0 || top > y_turns[n] )
+      y_turns[n] = top;
+
+    /* look for first y value that is <= */
+    while ( n-- && y < y_turns[n] )
+      ;
+
+    /* if it is <, simply insert it, ignore if == */
+    if ( n < 0 || y > y_turns[n] )
+    {
+      ras.maxBuff--;
+      if ( ras.maxBuff <= ras.top )
+      {
+        ras.error = FT_THROW( Raster_Overflow );
+        return FAILURE;
+      }
+
+      do
+      {
+        Int  y2 = (Int)y_turns[n];
+
+
+        y_turns[n] = y;
+        y = y2;
+      } while ( n-- >= 0 );
+
+      ras.numTurns++;
+    }
+
+    return SUCCESS;
+  }
+
+
   /**************************************************************************
    *
    * @Function:
@@ -606,52 +628,48 @@
    *   aState ::
    *     The state/orientation of the new profile.
    *
-   *   overshoot ::
-   *     Whether the profile's unrounded start position
-   *     differs by at least a half pixel.
-   *
    * @Return:
    *  SUCCESS on success.  FAILURE in case of overflow or of incoherent
    *  profile.
    */
   static Bool
-  New_Profile( RAS_ARGS TStates  aState,
-                        Bool     overshoot )
+  New_Profile( RAS_ARGS TStates  aState )
   {
-    if ( !ras.fProfile )
+    Long  e;
+
+
+    if ( !ras.cProfile || ras.cProfile->height )
     {
       ras.cProfile  = (PProfile)ras.top;
-      ras.fProfile  = ras.cProfile;
-      ras.top      += AlignProfileSize;
-    }
+      ras.top       = ras.cProfile->x;
 
-    if ( ras.top >= ras.maxBuff )
-    {
-      ras.error = FT_THROW( Raster_Overflow );
-      return FAILURE;
+      if ( ras.top >= ras.maxBuff )
+      {
+        FT_TRACE1(( "overflow in New_Profile\n" ));
+        ras.error = FT_THROW( Raster_Overflow );
+        return FAILURE;
+      }
+
+      ras.cProfile->height = 0;
     }
 
-    ras.cProfile->start  = 0;
-    ras.cProfile->height = 0;
-    ras.cProfile->offset = ras.top;
-    ras.cProfile->link   = (PProfile)0;
-    ras.cProfile->next   = (PProfile)0;
     ras.cProfile->flags  = ras.dropOutControl;
 
     switch ( aState )
     {
     case Ascending_State:
       ras.cProfile->flags |= Flow_Up;
-      if ( overshoot )
+      if ( IS_BOTTOM_OVERSHOOT( ras.lastY ) )
         ras.cProfile->flags |= Overshoot_Bottom;
 
-      FT_TRACE6(( "  new ascending profile = %p\n", (void *)ras.cProfile ));
+      e = CEILING( ras.lastY );
       break;
 
     case Descending_State:
-      if ( overshoot )
+      if ( IS_TOP_OVERSHOOT( ras.lastY ) )
         ras.cProfile->flags |= Overshoot_Top;
-      FT_TRACE6(( "  new descending profile = %p\n", (void *)ras.cProfile ));
+
+      e = FLOOR( ras.lastY );
       break;
 
     default:
@@ -660,12 +678,20 @@
       return FAILURE;
     }
 
-    if ( !ras.gProfile )
-      ras.gProfile = ras.cProfile;
+    if ( e > ras.maxY )
+      e = ras.maxY;
+    if ( e < ras.minY )
+      e = ras.minY;
+    ras.cProfile->start = (Int)TRUNC( e );
+
+    FT_TRACE7(( "  new %s profile = %p, start = %d\n",
+                aState == Ascending_State ? "ascending" : "descending",
+                (void *)ras.cProfile, ras.cProfile->start ));
+
+    if ( ras.lastY == e )
+      *ras.top++ = ras.lastX;
 
     ras.state = aState;
-    ras.fresh = TRUE;
-    ras.joint = FALSE;
 
     return SUCCESS;
   }
@@ -677,24 +703,19 @@
    *   End_Profile
    *
    * @Description:
-   *   Finalize the current profile.
-   *
-   * @Input:
-   *   overshoot ::
-   *     Whether the profile's unrounded end position differs
-   *     by at least a half pixel.
+   *   Finalize the current profile and record y-turns.
    *
    * @Return:
    *   SUCCESS on success.  FAILURE in case of overflow or incoherency.
    */
   static Bool
-  End_Profile( RAS_ARGS Bool  overshoot )
+  End_Profile( RAS_ARG )
   {
-    Long  h;
+    PProfile  p = ras.cProfile;
+    Int       h = (Int)( ras.top - p->x );
+    Int       bottom, top;
 
 
-    h = (Long)( ras.top - ras.cProfile->offset );
-
     if ( h < 0 )
     {
       FT_ERROR(( "End_Profile: negative height encountered\n" ));
@@ -704,98 +725,46 @@
 
     if ( h > 0 )
     {
-      PProfile  oldProfile;
+      FT_TRACE7(( "  ending profile %p, start = %2d, height = %+3d\n",
+                  (void *)p, p->start, p->flags & Flow_Up ? h : -h ));
 
+      p->height = h;
 
-      FT_TRACE6(( "  ending profile %p, start = %ld, height = %ld\n",
-                  (void *)ras.cProfile, ras.cProfile->start, h ));
+      if ( p->flags & Flow_Up )
+      {
+        if ( IS_TOP_OVERSHOOT( ras.lastY ) )
+          p->flags |= Overshoot_Top;
 
-      ras.cProfile->height = h;
-      if ( overshoot )
+        bottom    = p->start;
+        top       = bottom + h;
+        p->offset = 0;
+        p->X      = p->x[0];
+      }
+      else
       {
-        if ( ras.cProfile->flags & Flow_Up )
-          ras.cProfile->flags |= Overshoot_Top;
-        else
-          ras.cProfile->flags |= Overshoot_Bottom;
+        if ( IS_BOTTOM_OVERSHOOT( ras.lastY ) )
+          p->flags |= Overshoot_Bottom;
+
+        top       = p->start + 1;
+        bottom    = top - h;
+        p->start  = bottom;
+        p->offset = h - 1;
+        p->X      = p->x[h - 1];
       }
 
-      oldProfile   = ras.cProfile;
-      ras.cProfile = (PProfile)ras.top;
+      if ( Insert_Y_Turns( RAS_VARS bottom, top ) )
+        return FAILURE;
 
-      ras.top += AlignProfileSize;
+      if ( !ras.gProfile )
+        ras.gProfile = p;
 
-      ras.cProfile->height = 0;
-      ras.cProfile->offset = ras.top;
+      /* preliminary values to be finalized */
+      p->next = ras.gProfile;
+      p->link = (PProfile)ras.top;
 
-      oldProfile->next = ras.cProfile;
       ras.num_Profs++;
     }
 
-    if ( ras.top >= ras.maxBuff )
-    {
-      FT_TRACE1(( "overflow in End_Profile\n" ));
-      ras.error = FT_THROW( Raster_Overflow );
-      return FAILURE;
-    }
-
-    ras.joint = FALSE;
-
-    return SUCCESS;
-  }
-
-
-  /**************************************************************************
-   *
-   * @Function:
-   *   Insert_Y_Turn
-   *
-   * @Description:
-   *   Insert a salient into the sorted list placed on top of the render
-   *   pool.
-   *
-   * @Input:
-   *   New y scanline position.
-   *
-   * @Return:
-   *   SUCCESS on success.  FAILURE in case of overflow.
-   */
-  static Bool
-  Insert_Y_Turn( RAS_ARGS Int  y )
-  {
-    PLong  y_turns;
-    Int    n;
-
-
-    n       = ras.numTurns - 1;
-    y_turns = ras.sizeBuff - ras.numTurns;
-
-    /* look for first y value that is <= */
-    while ( n >= 0 && y < y_turns[n] )
-      n--;
-
-    /* if it is <, simply insert it, ignore if == */
-    if ( n >= 0 && y > y_turns[n] )
-      do
-      {
-        Int  y2 = (Int)y_turns[n];
-
-
-        y_turns[n] = y;
-        y = y2;
-      } while ( --n >= 0 );
-
-    if ( n < 0 )
-    {
-      ras.maxBuff--;
-      if ( ras.maxBuff <= ras.top )
-      {
-        ras.error = FT_THROW( Raster_Overflow );
-        return FAILURE;
-      }
-      ras.numTurns++;
-      ras.sizeBuff[-ras.numTurns] = y;
-    }
-
     return SUCCESS;
   }
 
@@ -807,56 +776,29 @@
    *
    * @Description:
    *   Adjust all links in the profiles list.
-   *
-   * @Return:
-   *   SUCCESS on success.  FAILURE in case of overflow.
    */
-  static Bool
+  static void
   Finalize_Profile_Table( RAS_ARG )
   {
-    UShort    n;
-    PProfile  p;
-
+    UShort    n = ras.num_Profs;
+    PProfile  p = ras.fProfile;
+    PProfile  q;
 
-    n = ras.num_Profs;
-    p = ras.fProfile;
 
-    if ( n > 1 && p )
+    /* there should be at least two profiles, up and down */
+    while ( --n )
     {
-      do
-      {
-        Int  bottom, top;
+      q = p->link;
 
+      /* fix the contour loop */
+      if ( q->next == p->next )
+        p->next = q;
 
-        if ( n > 1 )
-          p->link = (PProfile)( p->offset + p->height );
-        else
-          p->link = NULL;
-
-        if ( p->flags & Flow_Up )
-        {
-          bottom = (Int)p->start;
-          top    = (Int)( p->start + p->height - 1 );
-        }
-        else
-        {
-          bottom     = (Int)( p->start - p->height + 1 );
-          top        = (Int)p->start;
-          p->start   = bottom;
-          p->offset += p->height - 1;
-        }
-
-        if ( Insert_Y_Turn( RAS_VARS bottom )  ||
-             Insert_Y_Turn( RAS_VARS top + 1 ) )
-          return FAILURE;
-
-        p = p->link;
-      } while ( --n );
+      p = q;
     }
-    else
-      ras.fProfile = NULL;
 
-    return SUCCESS;
+    /* null-terminate */
+    p->link = NULL;
   }
 
 
@@ -986,107 +928,78 @@
                     Long  miny,
                     Long  maxy )
   {
-    Long   Dx, Dy;
-    Int    e1, e2, f1, f2, size;     /* XXX: is `Short' sufficient? */
-    Long   Ix, Rx, Ax;
+    Long  e, e2, Dx, Dy;
+    Long  Ix, Rx, Ax;
+    Int   size;
 
     PLong  top;
 
 
-    Dx = x2 - x1;
-    Dy = y2 - y1;
-
-    if ( Dy <= 0 || y2 < miny || y1 > maxy )
+    if ( y2 < miny || y1 > maxy )
       return SUCCESS;
 
-    if ( y1 < miny )
-    {
-      /* Take care: miny-y1 can be a very large value; we use     */
-      /*            a slow MulDiv function to avoid clipping bugs */
-      x1 += SMulDiv( Dx, miny - y1, Dy );
-      e1  = (Int)TRUNC( miny );
-      f1  = 0;
-    }
-    else
-    {
-      e1 = (Int)TRUNC( y1 );
-      f1 = (Int)FRAC( y1 );
-    }
+    e2 = y2 > maxy ? maxy : FLOOR( y2 );
+    e  = y1 < miny ? miny : CEILING( y1 );
 
-    if ( y2 > maxy )
-    {
-      /* x2 += FMulDiv( Dx, maxy - y2, Dy );  UNNECESSARY */
-      e2  = (Int)TRUNC( maxy );
-      f2  = 0;
-    }
-    else
-    {
-      e2 = (Int)TRUNC( y2 );
-      f2 = (Int)FRAC( y2 );
-    }
+    if ( y1 == e )
+      e += ras.precision;
 
-    if ( f1 > 0 )
-    {
-      if ( e1 == e2 )
-        return SUCCESS;
-      else
-      {
-        x1 += SMulDiv( Dx, ras.precision - f1, Dy );
-        e1 += 1;
-      }
-    }
-    else
-      if ( ras.joint )
-      {
-        ras.top--;
-        ras.joint = FALSE;
-      }
-
-    ras.joint = (char)( f2 == 0 );
+    if ( e2 < e )  /* nothing to do */
+      return SUCCESS;
 
-    if ( ras.fresh )
-    {
-      ras.cProfile->start = e1;
-      ras.fresh           = FALSE;
-    }
+    size = (Int)TRUNC( e2 - e ) + 1;
+    top  = ras.top;
 
-    size = e2 - e1 + 1;
-    if ( ras.top + size >= ras.maxBuff )
+    if ( top + size >= ras.maxBuff )
     {
       ras.error = FT_THROW( Raster_Overflow );
       return FAILURE;
     }
 
-    if ( Dx > 0 )
-    {
-      Ix = SMulDiv_No_Round( ras.precision, Dx, Dy );
-      Rx = ( ras.precision * Dx ) % Dy;
-      Dx = 1;
-    }
-    else
+    Dx = x2 - x1;
+    Dy = y2 - y1;
+
+    if ( Dx == 0 )  /* very easy */
     {
-      Ix = -SMulDiv_No_Round( ras.precision, -Dx, Dy );
-      Rx = ( ras.precision * -Dx ) % Dy;
-      Dx = -1;
+      do
+        *top++ = x1;
+      while ( --size );
+      goto Fin;
     }
 
-    Ax  = -Dy;
-    top = ras.top;
+    Ix     = SMulDiv_No_Round( e - y1, Dx, Dy );
+    x1    += Ix;
+    *top++ = x1;
 
-    while ( size > 0 )
+    if ( --size )
     {
-      *top++ = x1;
+      Ax = Dx * ( e - y1 )    - Dy * Ix;  /* remainder */
+      Ix = FMulDiv( ras.precision, Dx, Dy );
+      Rx = Dx * ras.precision - Dy * Ix;  /* remainder */
+      Dx = 1;
 
-      x1 += Ix;
-      Ax += Rx;
-      if ( Ax >= 0 )
+      if ( x2 < x1 )
+      {
+        Ax = -Ax;
+        Rx = -Rx;
+        Dx = -Dx;
+      }
+
+      do
       {
-        Ax -= Dy;
-        x1 += Dx;
+        x1 += Ix;
+        Ax += Rx;
+        if ( Ax >= Dy )
+        {
+          Ax -= Dy;
+          x1 += Dx;
+        }
+        *top++ = x1;
       }
-      size--;
+      while ( --size );
     }
 
+  Fin:
     ras.top = top;
     return SUCCESS;
   }
@@ -1131,17 +1044,7 @@
                       Long  miny,
                       Long  maxy )
   {
-    Bool  result, fresh;
-
-
-    fresh  = ras.fresh;
-
-    result = Line_Up( RAS_VARS x1, -y1, x2, -y2, -maxy, -miny );
-
-    if ( fresh && !ras.fresh )
-      ras.cProfile->start = -ras.cProfile->start;
-
-    return result;
+    return Line_Up( RAS_VARS x1, -y1, x2, -y2, -maxy, -miny );
   }
 
 
@@ -1181,105 +1084,73 @@
                       Long       miny,
                       Long       maxy )
   {
-    Long   y1, y2, e, e2, e0;
-    Short  f1;
+    Long  y1, y2, e, e2, dy;
+    Long  dx, x2;
 
-    TPoint*  start_arc;
-
-    PLong top;
+    PLong  top;
 
 
     y1  = arc[degree].y;
     y2  = arc[0].y;
-    top = ras.top;
 
     if ( y2 < miny || y1 > maxy )
-      goto Fin;
-
-    e2 = FLOOR( y2 );
-
-    if ( e2 > maxy )
-      e2 = maxy;
-
-    e0 = miny;
-
-    if ( y1 < miny )
-      e = miny;
-    else
-    {
-      e  = CEILING( y1 );
-      f1 = (Short)( FRAC( y1 ) );
-      e0 = e;
-
-      if ( f1 == 0 )
-      {
-        if ( ras.joint )
-        {
-          top--;
-          ras.joint = FALSE;
-        }
+      return SUCCESS;
 
-        *top++ = arc[degree].x;
+    e2 = y2 > maxy ? maxy : FLOOR( y2 );
+    e  = y1 < miny ? miny : CEILING( y1 );
 
-        e += ras.precision;
-      }
-    }
+    if ( y1 == e )
+      e += ras.precision;
 
-    if ( ras.fresh )
-    {
-      ras.cProfile->start = TRUNC( e0 );
-      ras.fresh = FALSE;
-    }
+    if ( e2 < e )  /* nothing to do */
+      return SUCCESS;
 
-    if ( e2 < e )
-      goto Fin;
+    top = ras.top;
 
     if ( ( top + TRUNC( e2 - e ) + 1 ) >= ras.maxBuff )
     {
-      ras.top   = top;
       ras.error = FT_THROW( Raster_Overflow );
       return FAILURE;
     }
 
-    start_arc = arc;
-
     do
     {
-      ras.joint = FALSE;
-
       y2 = arc[0].y;
+      x2 = arc[0].x;
 
       if ( y2 > e )
       {
-        y1 = arc[degree].y;
-        if ( y2 - y1 >= ras.precision_step )
+        dy = y2 - arc[degree].y;
+        dx = x2 - arc[degree].x;
+
+        /* split condition should be invariant of direction */
+        if (  dy > ras.precision_step ||
+              dx > ras.precision_step ||
+             -dx > ras.precision_step )
         {
           splitter( arc );
           arc += degree;
         }
         else
         {
-          *top++ = arc[degree].x + FMulDiv( arc[0].x - arc[degree].x,
-                                            e - y1, y2 - y1 );
+          *top++ = x2 - FMulDiv( y2 - e, dx, dy );
+          e     += ras.precision;
           arc -= degree;
-          e   += ras.precision;
         }
       }
       else
       {
         if ( y2 == e )
         {
-          ras.joint  = TRUE;
-          *top++     = arc[0].x;
-
-          e += ras.precision;
+          *top++ = x2;
+          e     += ras.precision;
         }
-        arc -= degree;
+        arc   -= degree;
       }
-    } while ( arc >= start_arc && e <= e2 );
+    }
+    while ( e <= e2 );
 
-  Fin:
-    ras.top  = top;
+    ras.top = top;
     return SUCCESS;
   }
 
@@ -1316,7 +1187,7 @@
                         Long       miny,
                         Long       maxy )
   {
-    Bool     result, fresh;
+    Bool  result;
 
 
     arc[0].y = -arc[0].y;
@@ -1325,13 +1196,8 @@
     if ( degree > 2 )
       arc[3].y = -arc[3].y;
 
-    fresh = ras.fresh;
-
     result = Bezier_Up( RAS_VARS degree, arc, splitter, -maxy, -miny );
 
-    if ( fresh && !ras.fresh )
-      ras.cProfile->start = -ras.cProfile->start;
-
     arc[0].y = -arc[0].y;
     return result;
   }
@@ -1362,74 +1228,50 @@
   Line_To( RAS_ARGS Long  x,
                     Long  y )
   {
-    /* First, detect a change of direction */
+    TStates  state;
 
-    switch ( ras.state )
-    {
-    case Unknown_State:
-      if ( y > ras.lastY )
-      {
-        if ( New_Profile( RAS_VARS Ascending_State,
-                                   IS_BOTTOM_OVERSHOOT( ras.lastY ) ) )
-          return FAILURE;
-      }
-      else
-      {
-        if ( y < ras.lastY )
-          if ( New_Profile( RAS_VARS Descending_State,
-                                     IS_TOP_OVERSHOOT( ras.lastY ) ) )
-            return FAILURE;
-      }
-      break;
 
-    case Ascending_State:
-      if ( y < ras.lastY )
-      {
-        if ( End_Profile( RAS_VARS IS_TOP_OVERSHOOT( ras.lastY ) ) ||
-             New_Profile( RAS_VARS Descending_State,
-                                   IS_TOP_OVERSHOOT( ras.lastY ) ) )
-          return FAILURE;
-      }
-      break;
+    if ( y == ras.lastY )
+      goto Fin;
 
-    case Descending_State:
-      if ( y > ras.lastY )
-      {
-        if ( End_Profile( RAS_VARS IS_BOTTOM_OVERSHOOT( ras.lastY ) ) ||
-             New_Profile( RAS_VARS Ascending_State,
-                                   IS_BOTTOM_OVERSHOOT( ras.lastY ) ) )
-          return FAILURE;
-      }
-      break;
+    /* First, detect a change of direction */
 
-    default:
-      ;
+    state = ras.lastY < y ? Ascending_State : Descending_State;
+
+    if ( ras.state != state )
+    {
+      /* finalize current profile if any */
+      if ( ras.state != Unknown_State &&
+           End_Profile( RAS_VAR )     )
+        goto Fail;
+
+      /* create a new profile */
+      if ( New_Profile( RAS_VARS state ) )
+        goto Fail;
     }
 
     /* Then compute the lines */
 
-    switch ( ras.state )
+    if ( state == Ascending_State )
     {
-    case Ascending_State:
       if ( Line_Up( RAS_VARS ras.lastX, ras.lastY,
                              x, y, ras.minY, ras.maxY ) )
-        return FAILURE;
-      break;
-
-    case Descending_State:
+        goto Fail;
+    }
+    else
+    {
       if ( Line_Down( RAS_VARS ras.lastX, ras.lastY,
                                x, y, ras.minY, ras.maxY ) )
-        return FAILURE;
-      break;
-
-    default:
-      ;
+        goto Fail;
     }
 
+  Fin:
     ras.lastX = x;
     ras.lastY = y;
-
     return SUCCESS;
+
+  Fail:
+    return FAILURE;
   }
 
 
@@ -1500,7 +1342,7 @@
         ymax = y1;
       }
 
-      if ( y2 < ymin || y2 > ymax )
+      if ( y2 < FLOOR( ymin ) || y2 > CEILING( ymax ) )
       {
         /* this arc has no given direction, split it! */
         Split_Conic( arc );
@@ -1508,8 +1350,12 @@
       }
       else if ( y1 == y3 )
       {
-        /* this arc is flat, ignore it and pop it from the Bezier stack */
+        /* this arc is flat, advance position */
+        /* and pop it from the Bezier stack   */
         arc -= 2;
+
+        ras.lastX = x3;
+        ras.lastY = y3;
       }
       else
       {
@@ -1518,18 +1364,13 @@
         state_bez = y1 < y3 ? Ascending_State : Descending_State;
         if ( ras.state != state_bez )
         {
-          Bool  o = ( state_bez == Ascending_State )
-                      ? IS_BOTTOM_OVERSHOOT( y1 )
-                      : IS_TOP_OVERSHOOT( y1 );
-
-
           /* finalize current profile if any */
           if ( ras.state != Unknown_State &&
-               End_Profile( RAS_VARS o )  )
+               End_Profile( RAS_VAR )     )
             goto Fail;
 
           /* create a new profile */
-          if ( New_Profile( RAS_VARS state_bez, o ) )
+          if ( New_Profile( RAS_VARS state_bez ) )
             goto Fail;
         }
 
@@ -1545,13 +1386,13 @@
                                      ras.minY, ras.maxY ) )
             goto Fail;
         arc -= 2;
+
+        ras.lastX = x3;
+        ras.lastY = y3;
       }
 
     } while ( arc >= arcs );
 
-    ras.lastX = x3;
-    ras.lastY = y3;
-
     return SUCCESS;
 
   Fail:
@@ -1648,7 +1489,7 @@
         ymax2 = y2;
       }
 
-      if ( ymin2 < ymin1 || ymax2 > ymax1 )
+      if ( ymin2 < FLOOR( ymin1 ) || ymax2 > CEILING( ymax1 ) )
       {
         /* this arc has no given direction, split it! */
         Split_Cubic( arc );
@@ -1656,27 +1497,26 @@
       }
       else if ( y1 == y4 )
       {
-        /* this arc is flat, ignore it and pop it from the Bezier stack */
+        /* this arc is flat, advance position */
+        /* and pop it from the Bezier stack   */
         arc -= 3;
+
+        ras.lastX = x4;
+        ras.lastY = y4;
       }
       else
       {
-        state_bez = ( y1 <= y4 ) ? Ascending_State : Descending_State;
+        state_bez = y1 < y4 ? Ascending_State : Descending_State;
 
         /* detect a change of direction */
         if ( ras.state != state_bez )
         {
-          Bool  o = ( state_bez == Ascending_State )
-                      ? IS_BOTTOM_OVERSHOOT( y1 )
-                      : IS_TOP_OVERSHOOT( y1 );
-
-
           /* finalize current profile if any */
           if ( ras.state != Unknown_State &&
-               End_Profile( RAS_VARS o )  )
+               End_Profile( RAS_VAR )     )
             goto Fail;
 
-          if ( New_Profile( RAS_VARS state_bez, o ) )
+          if ( New_Profile( RAS_VARS state_bez ) )
             goto Fail;
         }
 
@@ -1692,13 +1532,13 @@
                                      ras.minY, ras.maxY ) )
             goto Fail;
         arc -= 3;
+
+        ras.lastX = x4;
+        ras.lastY = y4;
       }
 
     } while ( arc >= arcs );
 
-    ras.lastX = x4;
-    ras.lastY = y4;
-
     return SUCCESS;
 
   Fail:
@@ -1740,6 +1580,11 @@
    *
    * @Return:
    *   SUCCESS on success, FAILURE on error.
+   *
+   * @Note:
+   *   Unlike FT_Outline_Decompose(), this function handles the scanmode
+   *   dropout tags in the individual contours.  Therefore, it cannot be
+   *   replaced.
    */
   static Bool
   Decompose_Curve( RAS_ARGS Int  first,
@@ -1753,7 +1598,7 @@
     FT_Vector*  points;
     FT_Vector*  point;
     FT_Vector*  limit;
-    char*       tags;
+    FT_Byte*    tags;
 
     UInt        tag;       /* current point's state           */
 
@@ -1974,24 +1819,17 @@
 
 
     ras.fProfile = NULL;
-    ras.joint    = FALSE;
-    ras.fresh    = FALSE;
-
-    ras.maxBuff  = ras.sizeBuff - AlignProfileSize;
+    ras.cProfile = NULL;
 
-    ras.numTurns = 0;
+    ras.top      = ras.buff;
+    ras.maxBuff  = ras.sizeBuff - 1;  /* top reserve */
 
-    ras.cProfile         = (PProfile)ras.top;
-    ras.cProfile->offset = ras.top;
-    ras.num_Profs        = 0;
+    ras.numTurns  = 0;
+    ras.num_Profs = 0;
 
     last = -1;
     for ( i = 0; i < ras.outline.n_contours; i++ )
     {
-      PProfile  lastProfile;
-      Bool      o;
-
-
       ras.state    = Unknown_State;
       ras.gProfile = NULL;
 
@@ -2001,35 +1839,30 @@
       if ( Decompose_Curve( RAS_VARS first, last, flipped ) )
         return FAILURE;
 
+      /* Note that ras.gProfile can stay nil if the contour was */
+      /* too small to be drawn or degenerate.                   */
+      if ( !ras.gProfile )
+        continue;
+
       /* we must now check whether the extreme arcs join or not */
       if ( FRAC( ras.lastY ) == 0 &&
            ras.lastY >= ras.minY  &&
            ras.lastY <= ras.maxY  )
-        if ( ras.gProfile                        &&
-             ( ras.gProfile->flags & Flow_Up ) ==
+        if ( ( ras.gProfile->flags & Flow_Up ) ==
                ( ras.cProfile->flags & Flow_Up ) )
           ras.top--;
-        /* Note that ras.gProfile can be nil if the contour was too small */
-        /* to be drawn.                                                   */
 
-      lastProfile = ras.cProfile;
-      if ( ras.top != ras.cProfile->offset &&
-           ( ras.cProfile->flags & Flow_Up ) )
-        o = IS_TOP_OVERSHOOT( ras.lastY );
-      else
-        o = IS_BOTTOM_OVERSHOOT( ras.lastY );
-      if ( End_Profile( RAS_VARS o ) )
+      if ( End_Profile( RAS_VAR ) )
         return FAILURE;
 
-      /* close the `next profile in contour' linked list */
-      if ( ras.gProfile )
-        lastProfile->next = ras.gProfile;
+      if ( !ras.fProfile )
+        ras.fProfile = ras.gProfile;
     }
 
-    if ( Finalize_Profile_Table( RAS_VAR ) )
-      return FAILURE;
+    if ( ras.fProfile )
+      Finalize_Profile_Table( RAS_VAR );
 
-    return (Bool)( ras.top < ras.maxBuff ? SUCCESS : FAILURE );
+    return SUCCESS;
   }
 
 
@@ -2042,24 +1875,11 @@
   /*************************************************************************/
 
 
-  /**************************************************************************
-   *
-   * Init_Linked
-   *
-   *   Initializes an empty linked list.
-   */
-  static void
-  Init_Linked( TProfileList*  l )
-  {
-    *l = NULL;
-  }
-
-
   /**************************************************************************
    *
    * InsNew
    *
-   *   Inserts a new profile in a linked list.
+   *   Inserts a new profile in a linked list, sorted by coordinate.
    */
   static void
   InsNew( PProfileList  list,
@@ -2073,10 +1893,8 @@
     current = *old;
     x       = profile->X;
 
-    while ( current )
+    while ( current && current->X < x )
     {
-      if ( x < current->X )
-        break;
       old     = ¤t->link;
       current = *old;
     }
@@ -2088,79 +1906,51 @@
 
   /**************************************************************************
    *
-   * DelOld
+   * Increment
    *
-   *   Removes an old profile from a linked list.
+   *   Advances all profile in the list to the next scanline.  It also
+   *   sorts the trace list in the unlikely case of profile crossing.
+   *   The profiles are inserted in sorted order.  We might need a single
+   *   swap to fix it when profiles (contours) cross.
+   *   Bubble sort with immediate restart is good enough and simple.
    */
   static void
-  DelOld( PProfileList    list,
-          const PProfile  profile )
+  Increment( PProfileList  list,
+             Int           flow )
   {
-    PProfile  *old, current;
-
+    PProfile  *old, current, next;
 
-    old     = list;
-    current = *old;
 
-    while ( current )
+    /* First, set the new X coordinates and remove exhausted profiles */
+    old = list;
+    while ( *old )
     {
-      if ( current == profile )
+      current = *old;
+      if ( --current->height )
       {
-        *old = current->link;
-        return;
+        current->offset += flow;
+        current->X       = current->x[current->offset];
+        old = ¤t->link;
       }
-
-      old     = ¤t->link;
-      current = *old;
-    }
-
-    /* we should never get there, unless the profile was not part of */
-    /* the list.                                                     */
-  }
-
-
-  /**************************************************************************
-   *
-   * Sort
-   *
-   *   Sorts a trace list.  In 95%, the list is already sorted.  We need
-   *   an algorithm which is fast in this case.  Bubble sort is enough
-   *   and simple.
-   */
-  static void
-  Sort( PProfileList  list )
-  {
-    PProfile  *old, current, next;
-
-
-    /* First, set the new X coordinate of each profile */
-    current = *list;
-    while ( current )
-    {
-      current->X       = *current->offset;
-      current->offset += ( current->flags & Flow_Up ) ? 1 : -1;
-      current->height--;
-      current = current->link;
+      else
+        *old = current->link;  /* remove */
     }
 
-    /* Then sort them */
+    /* Then make sure the list remains sorted */
     old     = list;
     current = *old;
 
     if ( !current )
       return;
 
-    next = current->link;
-
-    while ( next )
+    while ( current->link )
     {
+      next = current->link;
+
       if ( current->X <= next->X )
       {
         old     = ¤t->link;
-        current = *old;
-
-        if ( !current )
-          return;
+        current = next;
       }
       else
       {
@@ -2168,11 +1958,10 @@
         current->link = next->link;
         next->link    = current;
 
+        /* this is likely the only necessary swap -- restart */
         old     = list;
         current = *old;
       }
-
-      next = current->link;
     }
   }
 
@@ -2187,74 +1976,51 @@
    */
 
   static void
-  Vertical_Sweep_Init( RAS_ARGS Short  min,
-                                Short  max )
+  Vertical_Sweep_Init( RAS_ARGS Int  min,
+                                Int  max )
   {
     FT_UNUSED( max );
 
 
-    ras.bLine = ras.bOrigin - min * ras.target.pitch;
+    ras.bLine = ras.bOrigin - min * ras.bPitch;
   }
 
 
   static void
-  Vertical_Sweep_Span( RAS_ARGS Short       y,
+  Vertical_Sweep_Span( RAS_ARGS Int         y,
                                 FT_F26Dot6  x1,
-                                FT_F26Dot6  x2,
-                                PProfile    left,
-                                PProfile    right )
+                                FT_F26Dot6  x2 )
   {
-    Long  e1, e2;
-
-    Int  dropOutControl = left->flags & 7;
+    Int  e1 = (Int)TRUNC( CEILING( x1 ) );
+    Int  e2 = (Int)TRUNC(   FLOOR( x2 ) );
 
     FT_UNUSED( y );
-    FT_UNUSED( left );
-    FT_UNUSED( right );
 
 
-    /* in high-precision mode, we need 12 digits after the comma to */
-    /* represent multiples of 1/(1<<12) = 1/4096                    */
-    FT_TRACE7(( "  y=%d x=[% .12f;% .12f]",
+    FT_TRACE7(( "  y=%d x=[% .*f;% .*f]",
                 y,
-                (double)x1 / (double)ras.precision,
-                (double)x2 / (double)ras.precision ));
-
-    /* Drop-out control */
-
-    e1 = CEILING( x1 );
-    e2 = FLOOR( x2 );
-
-    /* take care of the special case where both the left */
-    /* and right contour lie exactly on pixel centers    */
-    if ( dropOutControl != 2                             &&
-         x2 - x1 - ras.precision <= ras.precision_jitter &&
-         e1 != x1 && e2 != x2                            )
-      e2 = e1;
+                ras.precision_bits, (double)x1 / (double)ras.precision,
+                ras.precision_bits, (double)x2 / (double)ras.precision ));
 
-    e1 = TRUNC( e1 );
-    e2 = TRUNC( e2 );
-
-    if ( e2 >= 0 && e1 < ras.bWidth )
+    if ( e2 >= 0 && e1 <= ras.bRight )
     {
-      Byte*  target;
+      PByte  target;
 
-      Int   c1, c2;
-      Byte  f1, f2;
+      Int   c1, f1, c2, f2;
 
 
       if ( e1 < 0 )
         e1 = 0;
-      if ( e2 >= ras.bWidth )
-        e2 = ras.bWidth - 1;
+      if ( e2 > ras.bRight )
+        e2 = ras.bRight;
 
-      FT_TRACE7(( " -> x=[%ld;%ld]", e1, e2 ));
+      FT_TRACE7(( " -> x=[%d;%d]", e1, e2 ));
 
-      c1 = (Short)( e1 >> 3 );
-      c2 = (Short)( e2 >> 3 );
+      c1 = e1 >> 3;
+      c2 = e2 >> 3;
 
-      f1 = (Byte)  ( 0xFF >> ( e1 & 7 ) );
-      f2 = (Byte) ~( 0x7F >> ( e2 & 7 ) );
+      f1 =  0xFF >> ( e1 & 7 );
+      f2 = ~0x7F >> ( e2 & 7 );
 
       target = ras.bLine + c1;
       c2 -= c1;
@@ -2280,163 +2046,50 @@
 
 
   static void
-  Vertical_Sweep_Drop( RAS_ARGS Short       y,
+  Vertical_Sweep_Drop( RAS_ARGS Int         y,
                                 FT_F26Dot6  x1,
-                                FT_F26Dot6  x2,
-                                PProfile    left,
-                                PProfile    right )
+                                FT_F26Dot6  x2 )
   {
-    Long   e1, e2, pxl;
-    Short  c1, f1;
-
-
-    FT_TRACE7(( "  y=%d x=[% .12f;% .12f]",
-                y,
-                (double)x1 / (double)ras.precision,
-                (double)x2 / (double)ras.precision ));
-
-    /* Drop-out control */
-
-    /*   e2            x2                    x1           e1   */
-    /*                                                         */
-    /*                 ^                     |                 */
-    /*                 |                     |                 */
-    /*   +-------------+---------------------+------------+    */
-    /*                 |                     |                 */
-    /*                 |                     v                 */
-    /*                                                         */
-    /* pixel         contour              contour       pixel  */
-    /* center                                           center */
-
-    /* drop-out mode    scan conversion rules (as defined in OpenType) */
-    /* --------------------------------------------------------------- */
-    /*  0                1, 2, 3                                       */
-    /*  1                1, 2, 4                                       */
-    /*  2                1, 2                                          */
-    /*  3                same as mode 2                                */
-    /*  4                1, 2, 5                                       */
-    /*  5                1, 2, 6                                       */
-    /*  6, 7             same as mode 2                                */
-
-    e1  = CEILING( x1 );
-    e2  = FLOOR  ( x2 );
-    pxl = e1;
-
-    if ( e1 > e2 )
-    {
-      Int  dropOutControl = left->flags & 7;
-
-
-      if ( e1 == e2 + ras.precision )
-      {
-        switch ( dropOutControl )
-        {
-        case 0: /* simple drop-outs including stubs */
-          pxl = e2;
-          break;
-
-        case 4: /* smart drop-outs including stubs */
-          pxl = SMART( x1, x2 );
-          break;
-
-        case 1: /* simple drop-outs excluding stubs */
-        case 5: /* smart drop-outs excluding stubs  */
-
-          /* Drop-out Control Rules #4 and #6 */
-
-          /* The specification neither provides an exact definition */
-          /* of a `stub' nor gives exact rules to exclude them.     */
-          /*                                                        */
-          /* Here the constraints we use to recognize a stub.       */
-          /*                                                        */
-          /*  upper stub:                                           */
-          /*                                                        */
-          /*   - P_Left and P_Right are in the same contour         */
-          /*   - P_Right is the successor of P_Left in that contour */
-          /*   - y is the top of P_Left and P_Right                 */
-          /*                                                        */
-          /*  lower stub:                                           */
-          /*                                                        */
-          /*   - P_Left and P_Right are in the same contour         */
-          /*   - P_Left is the successor of P_Right in that contour */
-          /*   - y is the bottom of P_Left                          */
-          /*                                                        */
-          /* We draw a stub if the following constraints are met.   */
-          /*                                                        */
-          /*   - for an upper or lower stub, there is top or bottom */
-          /*     overshoot, respectively                            */
-          /*   - the covered interval is greater or equal to a half */
-          /*     pixel                                              */
-
-          /* upper stub test */
-          if ( left->next == right                &&
-               left->height <= 0                  &&
-               !( left->flags & Overshoot_Top   &&
-                  x2 - x1 >= ras.precision_half ) )
-            goto Exit;
-
-          /* lower stub test */
-          if ( right->next == left                 &&
-               left->start == y                    &&
-               !( left->flags & Overshoot_Bottom &&
-                  x2 - x1 >= ras.precision_half  ) )
-            goto Exit;
-
-          if ( dropOutControl == 1 )
-            pxl = e2;
-          else
-            pxl = SMART( x1, x2 );
-          break;
-
-        default: /* modes 2, 3, 6, 7 */
-          goto Exit;  /* no drop-out control */
-        }
+    Int  e1 = (Int)TRUNC( x1 );
+    Int  e2 = (Int)TRUNC( x2 );
+    Int  c1, f1;
 
-        /* undocumented but confirmed: If the drop-out would result in a  */
-        /* pixel outside of the bounding box, use the pixel inside of the */
-        /* bounding box instead                                           */
-        if ( pxl < 0 )
-          pxl = e1;
-        else if ( TRUNC( pxl ) >= ras.bWidth )
-          pxl = e2;
+    FT_UNUSED( y );
 
-        /* check that the other pixel isn't set */
-        e1 = ( pxl == e1 ) ? e2 : e1;
 
-        e1 = TRUNC( e1 );
+    /* undocumented but confirmed: If the drop-out would result in a  */
+    /* pixel outside of the bounding box, use the pixel inside of the */
+    /* bounding box instead                                           */
+    if ( e1 < 0 || e1 > ras.bRight )
+      e1 = e2;
 
-        c1 = (Short)( e1 >> 3 );
-        f1 = (Short)( e1 &  7 );
+    /* otherwise check that the other pixel isn't set */
+    else if ( e2 >=0 && e2 <= ras.bRight )
+    {
+      c1 = e2 >> 3;
+      f1 = 0x80 >> ( e2 & 7 );
 
-        if ( e1 >= 0 && e1 < ras.bWidth     &&
-             ras.bLine[c1] & ( 0x80 >> f1 ) )
-          goto Exit;
-      }
-      else
-        goto Exit;
+      if ( ras.bLine[c1] & f1 )
+        return;
     }
 
-    e1 = TRUNC( pxl );
-
-    if ( e1 >= 0 && e1 < ras.bWidth )
+    if ( e1 >= 0 && e1 <= ras.bRight )
     {
-      FT_TRACE7(( " -> x=%ld", e1 ));
+      c1 = e1 >> 3;
+      f1 = 0x80 >> ( e1 & 7 );
 
-      c1 = (Short)( e1 >> 3 );
-      f1 = (Short)( e1 & 7 );
+      FT_TRACE7(( "  y=%d x=%d%s\n", y, e1,
+                  ras.bLine[c1] & f1 ? " redundant" : "" ));
 
-      ras.bLine[c1] |= (char)( 0x80 >> f1 );
+      ras.bLine[c1] |= f1;
     }
-
-  Exit:
-    FT_TRACE7(( " dropout=%d\n", left->flags & 7 ));
   }
 
 
   static void
   Vertical_Sweep_Step( RAS_ARG )
   {
-    ras.bLine -= ras.target.pitch;
+    ras.bLine -= ras.bPitch;
   }
 
 
@@ -2450,8 +2103,8 @@
    */
 
   static void
-  Horizontal_Sweep_Init( RAS_ARGS Short  min,
-                                  Short  max )
+  Horizontal_Sweep_Init( RAS_ARGS Int  min,
+                                  Int  max )
   {
     /* nothing, really */
     FT_UNUSED_RASTER;
@@ -2461,22 +2114,18 @@
 
 
   static void
-  Horizontal_Sweep_Span( RAS_ARGS Short       y,
+  Horizontal_Sweep_Span( RAS_ARGS Int         y,
                                   FT_F26Dot6  x1,
-                                  FT_F26Dot6  x2,
-                                  PProfile    left,
-                                  PProfile    right )
+                                  FT_F26Dot6  x2 )
   {
-    Long  e1, e2;
+    Long  e1 = CEILING( x1 );
+    Long  e2 =   FLOOR( x2 );
 
-    FT_UNUSED( left );
-    FT_UNUSED( right );
 
-
-    FT_TRACE7(( "  x=%d y=[% .12f;% .12f]",
+    FT_TRACE7(( "  x=%d y=[% .*f;% .*f]",
                 y,
-                (double)x1 / (double)ras.precision,
-                (double)x2 / (double)ras.precision ));
+                ras.precision_bits, (double)x1 / (double)ras.precision,
+                ras.precision_bits, (double)x2 / (double)ras.precision ));
 
     /* We should not need this procedure but the vertical sweep   */
     /* mishandles horizontal lines through pixel centers.  So we  */
@@ -2484,20 +2133,18 @@
     /*                                                            */
     /* XXX: Can we handle horizontal lines better and drop this?  */
 
-    e1 = CEILING( x1 );
-
     if ( x1 == e1 )
     {
       e1 = TRUNC( e1 );
 
-      if ( e1 >= 0 && (ULong)e1 < ras.target.rows )
+      if ( e1 >= 0 && e1 <= ras.bTop )
       {
-        Byte   f1;
+        Int    f1;
         PByte  bits;
 
 
-        bits = ras.bOrigin + ( y >> 3 ) - e1 * ras.target.pitch;
-        f1   = (Byte)( 0x80 >> ( y & 7 ) );
+        bits = ras.bOrigin + ( y >> 3 ) - e1 * ras.bPitch;
+        f1   = 0x80 >> ( y & 7 );
 
         FT_TRACE7(( bits[0] & f1 ? " redundant"
                                  : " -> y=%ld edge", e1 ));
@@ -2506,20 +2153,18 @@
       }
     }
 
-    e2 = FLOOR  ( x2 );
-
     if ( x2 == e2 )
     {
       e2 = TRUNC( e2 );
 
-      if ( e2 >= 0 && (ULong)e2 < ras.target.rows )
+      if ( e2 >= 0 && e2 <= ras.bTop )
       {
-        Byte   f1;
+        Int    f1;
         PByte  bits;
 
 
-        bits = ras.bOrigin + ( y >> 3 ) - e2 * ras.target.pitch;
-        f1   = (Byte)( 0x80 >> ( y & 7 ) );
+        bits = ras.bOrigin + ( y >> 3 ) - e2 * ras.bPitch;
+        f1   = 0x80 >> ( y & 7 );
 
         FT_TRACE7(( bits[0] & f1 ? " redundant"
                                  : " -> y=%ld edge", e2 ));
@@ -2533,122 +2178,42 @@
 
 
   static void
-  Horizontal_Sweep_Drop( RAS_ARGS Short       y,
+  Horizontal_Sweep_Drop( RAS_ARGS Int         y,
                                   FT_F26Dot6  x1,
-                                  FT_F26Dot6  x2,
-                                  PProfile    left,
-                                  PProfile    right )
+                                  FT_F26Dot6  x2 )
   {
-    Long   e1, e2, pxl;
+    Int    e1 = (Int)TRUNC( x1 );
+    Int    e2 = (Int)TRUNC( x2 );
     PByte  bits;
-    Byte   f1;
-
-
-    FT_TRACE7(( "  x=%d y=[% .12f;% .12f]",
-                y,
-                (double)x1 / (double)ras.precision,
-                (double)x2 / (double)ras.precision ));
-
-    /* During the horizontal sweep, we only take care of drop-outs */
-
-    /* e1     +       <-- pixel center */
-    /*        |                        */
-    /* x1  ---+-->    <-- contour      */
-    /*        |                        */
-    /*        |                        */
-    /* x2  <--+---    <-- contour      */
-    /*        |                        */
-    /*        |                        */
-    /* e2     +       <-- pixel center */
-
-    e1  = CEILING( x1 );
-    e2  = FLOOR  ( x2 );
-    pxl = e1;
-
-    if ( e1 > e2 )
-    {
-      Int  dropOutControl = left->flags & 7;
-
+    Int    f1;
 
-      if ( e1 == e2 + ras.precision )
-      {
-        switch ( dropOutControl )
-        {
-        case 0: /* simple drop-outs including stubs */
-          pxl = e2;
-          break;
-
-        case 4: /* smart drop-outs including stubs */
-          pxl = SMART( x1, x2 );
-          break;
-
-        case 1: /* simple drop-outs excluding stubs */
-        case 5: /* smart drop-outs excluding stubs  */
-          /* see Vertical_Sweep_Drop for details */
-
-          /* rightmost stub test */
-          if ( left->next == right                &&
-               left->height <= 0                  &&
-               !( left->flags & Overshoot_Top   &&
-                  x2 - x1 >= ras.precision_half ) )
-            goto Exit;
-
-          /* leftmost stub test */
-          if ( right->next == left                 &&
-               left->start == y                    &&
-               !( left->flags & Overshoot_Bottom &&
-                  x2 - x1 >= ras.precision_half  ) )
-            goto Exit;
-
-          if ( dropOutControl == 1 )
-            pxl = e2;
-          else
-            pxl = SMART( x1, x2 );
-          break;
 
-        default: /* modes 2, 3, 6, 7 */
-          goto Exit;  /* no drop-out control */
-        }
-
-        /* undocumented but confirmed: If the drop-out would result in a  */
-        /* pixel outside of the bounding box, use the pixel inside of the */
-        /* bounding box instead                                           */
-        if ( pxl < 0 )
-          pxl = e1;
-        else if ( (ULong)( TRUNC( pxl ) ) >= ras.target.rows )
-          pxl = e2;
-
-        /* check that the other pixel isn't set */
-        e1 = ( pxl == e1 ) ? e2 : e1;
-
-        e1 = TRUNC( e1 );
+    /* undocumented but confirmed: If the drop-out would result in a  */
+    /* pixel outside of the bounding box, use the pixel inside of the */
+    /* bounding box instead                                           */
+    if ( e1 < 0 || e1 > ras.bTop )
+      e1 = e2;
 
-        bits = ras.bOrigin + ( y >> 3 ) - e1 * ras.target.pitch;
-        f1   = (Byte)( 0x80 >> ( y & 7 ) );
+    /* otherwise check that the other pixel isn't set */
+    else if ( e2 >=0 && e2 <= ras.bTop )
+    {
+      bits = ras.bOrigin + ( y >> 3 ) - e2 * ras.bPitch;
+      f1   = 0x80 >> ( y & 7 );
 
-        if ( e1 >= 0                     &&
-             (ULong)e1 < ras.target.rows &&
-             *bits & f1                  )
-          goto Exit;
-      }
-      else
-        goto Exit;
+      if ( *bits & f1 )
+        return;
     }
 
-    e1 = TRUNC( pxl );
-
-    if ( e1 >= 0 && (ULong)e1 < ras.target.rows )
+    if ( e1 >= 0 && e1 <= ras.bTop )
     {
-      FT_TRACE7(( " -> y=%ld", e1 ));
+      bits  = ras.bOrigin + ( y >> 3 ) - e1 * ras.bPitch;
+      f1    = 0x80 >> ( y & 7 );
 
-      bits  = ras.bOrigin + ( y >> 3 ) - e1 * ras.target.pitch;
-      f1    = (Byte)( 0x80 >> ( y & 7 ) );
+      FT_TRACE7(( "  x=%d y=%d%s\n", y, e1,
+                  *bits & f1 ? " redundant" : "" ));
 
-      bits[0] |= f1;
+      *bits |= f1;
     }
-
-  Exit:
-    FT_TRACE7(( " dropout=%d\n", left->flags & 7 ));
   }
 
 
@@ -2664,116 +2229,61 @@
    *
    * Generic Sweep Drawing routine
    *
+   * Note that this routine is executed with the pool containing at least
+   * two valid profiles (up and down) and two y-turns (top and bottom).
+   *
    */
 
-  static Bool
+  static void
   Draw_Sweep( RAS_ARG )
   {
-    Short         y, y_change, y_height;
-
-    PProfile      P, Q, P_Left, P_Right;
-
-    Short         min_Y, max_Y, top, bottom, dropouts;
-
-    Long          x1, x2, xs, e1, e2;
-
-    TProfileList  waiting;
-    TProfileList  draw_left, draw_right;
-
-
-    /* initialize empty linked lists */
-
-    Init_Linked( &waiting );
-
-    Init_Linked( &draw_left  );
-    Init_Linked( &draw_right );
-
-    /* first, compute min and max Y */
-
-    P     = ras.fProfile;
-    max_Y = (Short)TRUNC( ras.minY );
-    min_Y = (Short)TRUNC( ras.maxY );
-
-    while ( P )
-    {
-      Q = P->link;
+    Int           min_Y, max_Y, dropouts;
+    Int           y, y_turn;
 
-      bottom = (Short)P->start;
-      top    = (Short)( P->start + P->height - 1 );
+    PProfile      *Q, P, P_Left, P_Right;
 
-      if ( min_Y > bottom )
-        min_Y = bottom;
-      if ( max_Y < top )
-        max_Y = top;
+    TProfileList  waiting    = ras.fProfile;
+    TProfileList  draw_left  = NULL;
+    TProfileList  draw_right = NULL;
 
-      P->X = 0;
-      InsNew( &waiting, P );
 
-      P = Q;
-    }
+    /* use y_turns to set the drawing range */
 
-    /* check the Y-turns */
-    if ( ras.numTurns == 0 )
-    {
-      ras.error = FT_THROW( Invalid_Outline );
-      return FAILURE;
-    }
+    min_Y = (Int)ras.maxBuff[0];
+    max_Y = (Int)ras.maxBuff[ras.numTurns] - 1;
 
     /* now initialize the sweep */
 
     ras.Proc_Sweep_Init( RAS_VARS min_Y, max_Y );
 
-    /* then compute the distance of each profile from min_Y */
-
-    P = waiting;
-
-    while ( P )
-    {
-      P->countL = P->start - min_Y;
-      P = P->link;
-    }
-
     /* let's go */
 
-    y        = min_Y;
-    y_height = 0;
-
-    if ( ras.numTurns > 0                     &&
-         ras.sizeBuff[-ras.numTurns] == min_Y )
-      ras.numTurns--;
-
-    while ( ras.numTurns > 0 )
+    for ( y = min_Y; y <= max_Y; )
     {
-      /* check waiting list for new activations */
-
-      P = waiting;
+      /* check waiting list for new profile activations */
 
-      while ( P )
+      Q = &waiting;
+      while ( *Q )
       {
-        Q = P->link;
-        P->countL -= y_height;
-        if ( P->countL == 0 )
+        P = *Q;
+        if ( P->start == y )
         {
-          DelOld( &waiting, P );
+          *Q = P->link;  /* remove */
 
+          /* each active list contains profiles with the same flow */
+          /* left and right are arbitrary, correspond to TrueType  */
           if ( P->flags & Flow_Up )
             InsNew( &draw_left,  P );
           else
             InsNew( &draw_right, P );
         }
-
-        P = Q;
+        else
+          Q = &P->link;
       }
 
-      /* sort the drawing lists */
+      y_turn = (Int)*++ras.maxBuff;
 
-      Sort( &draw_left );
-      Sort( &draw_right );
-
-      y_change = (Short)ras.sizeBuff[-ras.numTurns--];
-      y_height = (Short)( y_change - y );
-
-      while ( y < y_change )
+      do
       {
         /* let's trace */
 
@@ -2784,9 +2294,13 @@
 
         while ( P_Left && P_Right )
         {
-          x1 = P_Left ->X;
-          x2 = P_Right->X;
+          Long  x1 = P_Left ->X;
+          Long  x2 = P_Right->X;
+          Long  xs;
+
 
+          /* TrueType should have x2 > x1, but can be opposite */
+          /* by mistake or in CFF/Type1, fix it then           */
           if ( x1 > x2 )
           {
             xs = x1;
@@ -2794,205 +2308,130 @@
             x2 = xs;
           }
 
-          e1 = FLOOR( x1 );
-          e2 = CEILING( x2 );
+          if ( CEILING( x1 ) <= FLOOR( x2 ) )
+            ras.Proc_Sweep_Span( RAS_VARS y, x1, x2 );
 
-          if ( x2 - x1 <= ras.precision &&
-               e1 != x1 && e2 != x2     )
+          /* otherwise, bottom ceiling > top floor, it is a drop-out */
+          else
           {
-            if ( e1 > e2 || e2 == e1 + ras.precision )
+            Int  dropOutControl = P_Left->flags & 7;
+
+
+            /* Drop-out control */
+
+            /*   e2            x2                    x1           e1   */
+            /*                                                         */
+            /*                 ^                     |                 */
+            /*                 |                     |                 */
+            /*   +-------------+---------------------+------------+    */
+            /*                 |                     |                 */
+            /*                 |                     v                 */
+            /*                                                         */
+            /* pixel         contour              contour       pixel  */
+            /* center                                           center */
+
+            /* drop-out mode   scan conversion rules (OpenType specs)  */
+            /* ------------------------------------------------------- */
+            /*  bit 0          exclude stubs if set                    */
+            /*  bit 1          ignore drop-outs if set                 */
+            /*  bit 2          smart rounding if set                   */
+
+            if ( dropOutControl & 2 )
+              goto Next_Pair;
+
+            /* The specification neither provides an exact definition */
+            /* of a `stub' nor gives exact rules to exclude them.     */
+            /*                                                        */
+            /* Here the constraints we use to recognize a stub.       */
+            /*                                                        */
+            /*  upper stub:                                           */
+            /*                                                        */
+            /*   - P_Left and P_Right are in the same contour         */
+            /*   - P_Right is the successor of P_Left in that contour */
+            /*   - y is the top of P_Left and P_Right                 */
+            /*                                                        */
+            /*  lower stub:                                           */
+            /*                                                        */
+            /*   - P_Left and P_Right are in the same contour         */
+            /*   - P_Left is the successor of P_Right in that contour */
+            /*   - y is the bottom of P_Left                          */
+            /*                                                        */
+            /* We draw a stub if the following constraints are met.   */
+            /*                                                        */
+            /*   - for an upper or lower stub, there is top or bottom */
+            /*     overshoot, respectively                            */
+            /*   - the covered interval is greater or equal to a half */
+            /*     pixel                                              */
+
+            if ( dropOutControl & 1 )
             {
-              Int  dropOutControl = P_Left->flags & 7;
-
-
-              if ( dropOutControl != 2 )
-              {
-                /* a drop-out was detected */
-
-                P_Left ->X = x1;
-                P_Right->X = x2;
-
-                /* mark profile for drop-out processing */
-                P_Left->countL = 1;
-                dropouts++;
-              }
+              /* upper stub test */
+              if ( P_Left->height == 1                &&
+                   P_Left->next == P_Right            &&
+                   !( P_Left->flags & Overshoot_Top   &&
+                      x2 - x1 >= ras.precision_half   ) )
+                goto Next_Pair;
+
+              /* lower stub test */
+              if ( P_Left->offset == 0                 &&
+                   P_Right->next == P_Left             &&
+                   !( P_Left->flags & Overshoot_Bottom &&
+                      x2 - x1 >= ras.precision_half    ) )
+                goto Next_Pair;
+            }
 
-              goto Skip_To_Next;
+            /* select the pixel to set and the other pixel */
+            if ( dropOutControl & 4 )
+            {
+              x2 = SMART( x1, x2 );
+              x1 = x1 > x2 ? x2 + ras.precision : x2 - ras.precision;
+            }
+            else
+            {
+              x2 = FLOOR  ( x2 );
+              x1 = CEILING( x1 );
             }
-          }
 
-          ras.Proc_Sweep_Span( RAS_VARS y, x1, x2, P_Left, P_Right );
+            P_Left ->X = x2;
+            P_Right->X = x1;
 
-        Skip_To_Next:
+            /* mark profile for drop-out processing */
+            P_Left->flags |= Dropout;
+            dropouts++;
+          }
 
+        Next_Pair:
           P_Left  = P_Left->link;
           P_Right = P_Right->link;
         }
 
-        /* handle drop-outs _after_ the span drawing --       */
-        /* drop-out processing has been moved out of the loop */
-        /* for performance tuning                             */
-        if ( dropouts > 0 )
-          goto Scan_DropOuts;
-
-      Next_Line:
-
-        ras.Proc_Sweep_Step( RAS_VAR );
-
-        y++;
+        /* handle drop-outs _after_ the span drawing */
+        P_Left  = draw_left;
+        P_Right = draw_right;
 
-        if ( y < y_change )
+        while ( dropouts )
         {
-          Sort( &draw_left  );
-          Sort( &draw_right );
-        }
-      }
-
-      /* now finalize the profiles that need it */
-
-      P = draw_left;
-      while ( P )
-      {
-        Q = P->link;
-        if ( P->height == 0 )
-          DelOld( &draw_left, P );
-        P = Q;
-      }
-
-      P = draw_right;
-      while ( P )
-      {
-        Q = P->link;
-        if ( P->height == 0 )
-          DelOld( &draw_right, P );
-        P = Q;
-      }
-    }
-
-    /* for gray-scaling, flush the bitmap scanline cache */
-    while ( y <= max_Y )
-    {
-      ras.Proc_Sweep_Step( RAS_VAR );
-      y++;
-    }
-
-    return SUCCESS;
-
-  Scan_DropOuts:
-
-    P_Left  = draw_left;
-    P_Right = draw_right;
-
-    while ( P_Left && P_Right )
-    {
-      if ( P_Left->countL )
-      {
-        P_Left->countL = 0;
-#if 0
-        dropouts--;  /* -- this is useful when debugging only */
-#endif
-        ras.Proc_Sweep_Drop( RAS_VARS y,
-                                      P_Left->X,
-                                      P_Right->X,
-                                      P_Left,
-                                      P_Right );
-      }
-
-      P_Left  = P_Left->link;
-      P_Right = P_Right->link;
-    }
-
-    goto Next_Line;
-  }
-
-
-#ifdef STANDALONE_
-
-  /**************************************************************************
-   *
-   * The following functions should only compile in stand-alone mode,
-   * i.e., when building this component without the rest of FreeType.
-   *
-   */
-
-  /**************************************************************************
-   *
-   * @Function:
-   *   FT_Outline_Get_CBox
-   *
-   * @Description:
-   *   Return an outline's `control box'.  The control box encloses all
-   *   the outline's points, including Bézier control points.  Though it
-   *   coincides with the exact bounding box for most glyphs, it can be
-   *   slightly larger in some situations (like when rotating an outline
-   *   that contains Bézier outside arcs).
-   *
-   *   Computing the control box is very fast, while getting the bounding
-   *   box can take much more time as it needs to walk over all segments
-   *   and arcs in the outline.  To get the latter, you can use the
-   *   `ftbbox' component, which is dedicated to this single task.
-   *
-   * @Input:
-   *   outline ::
-   *     A pointer to the source outline descriptor.
-   *
-   * @Output:
-   *   acbox ::
-   *     The outline's control box.
-   *
-   * @Note:
-   *   See @FT_Glyph_Get_CBox for a discussion of tricky fonts.
-   */
-
-  static void
-  FT_Outline_Get_CBox( const FT_Outline*  outline,
-                       FT_BBox           *acbox )
-  {
-    if ( outline && acbox )
-    {
-      Long  xMin, yMin, xMax, yMax;
-
-
-      if ( outline->n_points == 0 )
-      {
-        xMin = 0;
-        yMin = 0;
-        xMax = 0;
-        yMax = 0;
-      }
-      else
-      {
-        FT_Vector*  vec   = outline->points;
-        FT_Vector*  limit = vec + outline->n_points;
-
-
-        xMin = xMax = vec->x;
-        yMin = yMax = vec->y;
-        vec++;
+          if ( P_Left->flags & Dropout )
+          {
+            ras.Proc_Sweep_Drop( RAS_VARS y, P_Left->X, P_Right->X );
 
-        for ( ; vec < limit; vec++ )
-        {
-          Long  x, y;
+            P_Left->flags &= ~Dropout;
+            dropouts--;
+          }
 
+          P_Left  = P_Left->link;
+          P_Right = P_Right->link;
+        }
 
-          x = vec->x;
-          if ( x < xMin ) xMin = x;
-          if ( x > xMax ) xMax = x;
+        ras.Proc_Sweep_Step( RAS_VAR );
 
-          y = vec->y;
-          if ( y < yMin ) yMin = y;
-          if ( y > yMax ) yMax = y;
-        }
+        Increment( &draw_left,   1 );
+        Increment( &draw_right, -1 );
       }
-      acbox->xMin = xMin;
-      acbox->xMax = xMax;
-      acbox->yMin = yMin;
-      acbox->yMax = yMax;
+      while ( ++y < y_turn );
     }
   }
 
-#endif /* STANDALONE_ */
-
 
   /**************************************************************************
    *
@@ -3019,13 +2458,15 @@
     Int  band_stack[32];  /* enough to bisect 32-bit int bands */
 
 
+    FT_TRACE6(( "%s pass [%d..%d]\n",
+                flipped ? "Horizontal" : "Vertical",
+                y_min, y_max ));
+
     while ( 1 )
     {
       ras.minY = (Long)y_min * ras.precision;
       ras.maxY = (Long)y_max * ras.precision;
 
-      ras.top = ras.buff;
-
       ras.error = Raster_Err_Ok;
 
       if ( Convert_Glyph( RAS_VARS flipped ) )
@@ -3038,6 +2479,9 @@
         if ( y_min == y_max )
           return ras.error;  /* still Raster_Overflow */
 
+        FT_TRACE6(( "band [%d..%d]: to be bisected\n",
+                    y_min, y_max ));
+
         y_mid = ( y_min + y_max ) >> 1;
 
         band_stack[band_top++] = y_min;
@@ -3045,9 +2489,12 @@
       }
       else
       {
+        FT_TRACE6(( "band [%d..%d]: %hd profiles; %td bytes remaining\n",
+                    y_min, y_max, ras.num_Profs,
+                    (char*)ras.maxBuff - (char*)ras.top ));
+
         if ( ras.fProfile )
-          if ( Draw_Sweep( RAS_VAR ) )
-             return ras.error;
+          Draw_Sweep( RAS_VAR );
 
         if ( --band_top < 0 )
           break;
@@ -3076,53 +2523,48 @@
   Render_Glyph( RAS_ARG )
   {
     FT_Error  error;
+    Long      buffer[FT_MAX_BLACK_POOL];
 
 
+    ras.buff     = buffer;
+    ras.sizeBuff = (&buffer)[1]; /* Points to right after buffer. */
+
     Set_High_Precision( RAS_VARS ras.outline.flags &
                                  FT_OUTLINE_HIGH_PRECISION );
 
+    ras.dropOutControl = 0;
+
     if ( ras.outline.flags & FT_OUTLINE_IGNORE_DROPOUTS )
-      ras.dropOutControl = 2;
-    else
-    {
-      if ( ras.outline.flags & FT_OUTLINE_SMART_DROPOUTS )
-        ras.dropOutControl = 4;
-      else
-        ras.dropOutControl = 0;
+      ras.dropOutControl |= 2;
 
-      if ( !( ras.outline.flags & FT_OUTLINE_INCLUDE_STUBS ) )
-        ras.dropOutControl += 1;
-    }
+    if ( ras.outline.flags & FT_OUTLINE_SMART_DROPOUTS )
+      ras.dropOutControl |= 4;
 
-    /* Vertical Sweep */
-    FT_TRACE7(( "Vertical pass (ftraster)\n" ));
+    if ( !( ras.outline.flags & FT_OUTLINE_INCLUDE_STUBS ) )
+      ras.dropOutControl |= 1;
+
+    FT_TRACE6(( "BW Raster: precision 1/%d, dropout mode %d\n",
+                ras.precision, ras.dropOutControl ));
 
+    /* Vertical Sweep */
     ras.Proc_Sweep_Init = Vertical_Sweep_Init;
     ras.Proc_Sweep_Span = Vertical_Sweep_Span;
     ras.Proc_Sweep_Drop = Vertical_Sweep_Drop;
     ras.Proc_Sweep_Step = Vertical_Sweep_Step;
 
-    ras.bWidth  = (UShort)ras.target.width;
-    ras.bOrigin = (Byte*)ras.target.buffer;
-
-    if ( ras.target.pitch > 0 )
-      ras.bOrigin += (Long)( ras.target.rows - 1 ) * ras.target.pitch;
-
-    error = Render_Single_Pass( RAS_VARS 0, 0, (Int)ras.target.rows - 1 );
+    error = Render_Single_Pass( RAS_VARS 0, 0, ras.bTop );
     if ( error )
       return error;
 
     /* Horizontal Sweep */
     if ( !( ras.outline.flags & FT_OUTLINE_SINGLE_PASS ) )
     {
-      FT_TRACE7(( "Horizontal pass (ftraster)\n" ));
-
       ras.Proc_Sweep_Init = Horizontal_Sweep_Init;
       ras.Proc_Sweep_Span = Horizontal_Sweep_Span;
       ras.Proc_Sweep_Drop = Horizontal_Sweep_Drop;
       ras.Proc_Sweep_Step = Horizontal_Sweep_Step;
 
-      error = Render_Single_Pass( RAS_VARS 1, 0, (Int)ras.target.width - 1 );
+      error = Render_Single_Pass( RAS_VARS 1, 0, ras.bRight );
       if ( error )
         return error;
     }
@@ -3233,8 +2675,6 @@
     black_TWorker  worker[1];
 #endif
 
-    Long  buffer[FT_MAX_BLACK_POOL];
-
 
     if ( !raster )
       return FT_THROW( Raster_Uninitialized );
@@ -3243,7 +2683,7 @@
       return FT_THROW( Invalid_Outline );
 
     /* return immediately if the outline is empty */
-    if ( outline->n_points == 0 || outline->n_contours <= 0 )
+    if ( outline->n_points == 0 || outline->n_contours == 0 )
       return Raster_Err_Ok;
 
     if ( !outline->contours || !outline->points )
@@ -3269,10 +2709,14 @@
       return FT_THROW( Invalid_Argument );
 
     ras.outline = *outline;
-    ras.target  = *target_map;
 
-    ras.buff     = buffer;
-    ras.sizeBuff = (&buffer)[1]; /* Points to right after buffer. */
+    ras.bTop    =   (Int)target_map->rows - 1;
+    ras.bRight  =   (Int)target_map->width - 1;
+    ras.bPitch  =   (Int)target_map->pitch;
+    ras.bOrigin = (PByte)target_map->buffer;
+
+    if ( ras.bPitch > 0 )
+      ras.bOrigin += ras.bTop * ras.bPitch;
 
     return Render_Glyph( RAS_VAR );
   }
diff --git a/src/java.desktop/share/native/libfreetype/src/raster/ftraster.h b/src/java.desktop/share/native/libfreetype/src/raster/ftraster.h
index b511b3a99e9c5..ad9cb1b9fe0dd 100644
--- a/src/java.desktop/share/native/libfreetype/src/raster/ftraster.h
+++ b/src/java.desktop/share/native/libfreetype/src/raster/ftraster.h
@@ -4,7 +4,7 @@
  *
  *   The FreeType glyph rasterizer (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used
diff --git a/src/java.desktop/share/native/libfreetype/src/raster/ftrend1.c b/src/java.desktop/share/native/libfreetype/src/raster/ftrend1.c
index 6d442b1ff8c52..fd9f174f2e1fa 100644
--- a/src/java.desktop/share/native/libfreetype/src/raster/ftrend1.c
+++ b/src/java.desktop/share/native/libfreetype/src/raster/ftrend1.c
@@ -4,7 +4,7 @@
  *
  *   The FreeType glyph rasterizer interface (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/raster/ftrend1.h b/src/java.desktop/share/native/libfreetype/src/raster/ftrend1.h
index cec35c8528ac3..cf3e73c0a248b 100644
--- a/src/java.desktop/share/native/libfreetype/src/raster/ftrend1.h
+++ b/src/java.desktop/share/native/libfreetype/src/raster/ftrend1.h
@@ -4,7 +4,7 @@
  *
  *   The FreeType glyph rasterizer interface (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/raster/rasterrs.h b/src/java.desktop/share/native/libfreetype/src/raster/rasterrs.h
index 989d8b44be157..326d42e0438e2 100644
--- a/src/java.desktop/share/native/libfreetype/src/raster/rasterrs.h
+++ b/src/java.desktop/share/native/libfreetype/src/raster/rasterrs.h
@@ -4,7 +4,7 @@
  *
  *   monochrome renderer error codes (specification only).
  *
- * Copyright (C) 2001-2023 by
+ * Copyright (C) 2001-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/pngshim.c b/src/java.desktop/share/native/libfreetype/src/sfnt/pngshim.c
index 33712162e00db..76181568af9f3 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/pngshim.c
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/pngshim.c
@@ -4,7 +4,7 @@
  *
  *   PNG Bitmap glyph support.
  *
- * Copyright (C) 2013-2023 by
+ * Copyright (C) 2013-2024 by
  * Google, Inc.
  * Written by Stuart Gill and Behdad Esfahbod.
  *
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/pngshim.h b/src/java.desktop/share/native/libfreetype/src/sfnt/pngshim.h
index 903bd2bc3482d..6e7a5c08e712a 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/pngshim.h
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/pngshim.h
@@ -4,7 +4,7 @@
  *
  *   PNG Bitmap glyph support.
  *
- * Copyright (C) 2013-2023 by
+ * Copyright (C) 2013-2024 by
  * Google, Inc.
  * Written by Stuart Gill and Behdad Esfahbod.
  *
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/sfdriver.c b/src/java.desktop/share/native/libfreetype/src/sfnt/sfdriver.c
index 0925940b03f22..81072207b4908 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/sfdriver.c
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/sfdriver.c
@@ -4,7 +4,7 @@
  *
  *   High-level SFNT driver interface (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -49,6 +49,10 @@
 #include 
 #endif
 
+#ifdef TT_CONFIG_OPTION_GPOS_KERNING
+#include "ttgpos.h"
+#endif
+
 #include "ttcmap.h"
 #include "ttkern.h"
 #include "ttmtx.h"
@@ -1249,6 +1253,12 @@
 #define PUT_PS_NAMES( a )  a
 #else
 #define PUT_PS_NAMES( a )  NULL
+#endif
+
+#ifdef TT_CONFIG_OPTION_GPOS_KERNING
+#define PUT_GPOS_KERNING( a )  a
+#else
+#define PUT_GPOS_KERNING( a )  NULL
 #endif
 
   FT_DEFINE_SFNT_INTERFACE(
@@ -1274,6 +1284,8 @@
     tt_face_free_name,      /* TT_Free_Table_Func      free_name       */
 
     tt_face_load_kern,      /* TT_Load_Table_Func      load_kern       */
+    PUT_GPOS_KERNING( tt_face_load_gpos ),
+                            /* TT_Load_Table_Func      load_gpos       */
     tt_face_load_gasp,      /* TT_Load_Table_Func      load_gasp       */
     tt_face_load_pclt,      /* TT_Load_Table_Func      load_init       */
 
@@ -1292,6 +1304,9 @@
     /* since version 2.1.8 */
     tt_face_get_kerning,    /* TT_Face_GetKerningFunc  get_kerning     */
 
+    PUT_GPOS_KERNING( tt_face_get_gpos_kerning ),
+                           /* TT_Face_GetKerningFunc  get_gpos_kerning */
+
     /* since version 2.2 */
     tt_face_load_font_dir,  /* TT_Load_Table_Func      load_font_dir   */
     tt_face_load_hmtx,      /* TT_Load_Metrics_Func    load_hmtx       */
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/sfdriver.h b/src/java.desktop/share/native/libfreetype/src/sfnt/sfdriver.h
index 2445958b69f42..6f71489fdc17f 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/sfdriver.h
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/sfdriver.h
@@ -4,7 +4,7 @@
  *
  *   High-level SFNT driver interface (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/sferrors.h b/src/java.desktop/share/native/libfreetype/src/sfnt/sferrors.h
index e7a8eb04bb883..d3ca1d9aa8b3b 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/sferrors.h
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/sferrors.h
@@ -4,7 +4,7 @@
  *
  *   SFNT error codes (specification only).
  *
- * Copyright (C) 2001-2023 by
+ * Copyright (C) 2001-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/sfobjs.c b/src/java.desktop/share/native/libfreetype/src/sfnt/sfobjs.c
index f5d66ef8403a1..6ee4e5e939b47 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/sfobjs.c
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/sfobjs.c
@@ -4,7 +4,7 @@
  *
  *   SFNT object management (base).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -40,6 +40,10 @@
 #include "ttbdf.h"
 #endif
 
+#ifdef TT_CONFIG_OPTION_GPOS_KERNING
+#include "ttgpos.h"
+#endif
+
 
   /**************************************************************************
    *
@@ -1026,6 +1030,10 @@
     LOAD_( gasp );
     LOAD_( kern );
 
+#ifdef TT_CONFIG_OPTION_GPOS_KERNING
+    LOAD_( gpos );
+#endif
+
     face->root.num_glyphs = face->max_profile.numGlyphs;
 
     /* Bit 8 of the `fsSelection' field in the `OS/2' table denotes  */
@@ -1119,7 +1127,11 @@
         flags |= FT_FACE_FLAG_VERTICAL;
 
       /* kerning available ? */
-      if ( TT_FACE_HAS_KERNING( face ) )
+      if ( TT_FACE_HAS_KERNING( face )
+#ifdef TT_CONFIG_OPTION_GPOS_KERNING
+           || face->gpos_kerning_available
+#endif
+         )
         flags |= FT_FACE_FLAG_KERNING;
 
 #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
@@ -1470,6 +1482,11 @@
     /* freeing the kerning table */
     tt_face_done_kern( face );
 
+#ifdef TT_CONFIG_OPTION_GPOS_KERNING
+    /* freeing the GPOS table */
+    tt_face_done_gpos( face );
+#endif
+
     /* freeing the collection table */
     FT_FREE( face->ttc_header.offsets );
     face->ttc_header.count = 0;
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/sfobjs.h b/src/java.desktop/share/native/libfreetype/src/sfnt/sfobjs.h
index 906aebbf904fe..90847d9573227 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/sfobjs.h
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/sfobjs.h
@@ -4,7 +4,7 @@
  *
  *   SFNT object management (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff.c b/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff.c
index 7c0ce2205e67b..14514bf9574cd 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff.c
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff.c
@@ -4,7 +4,7 @@
  *
  *   WOFF format management (base).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -18,6 +18,7 @@
 
 #include "sfwoff.h"
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -149,6 +150,7 @@
     /* Miscellaneous checks. */
     if ( woff.length != stream->size                              ||
          woff.num_tables == 0                                     ||
+         woff.num_tables >  0xFFFU                                ||
          44 + woff.num_tables * 20UL >= woff.length               ||
          12 + woff.num_tables * 16UL >= woff.totalSfntSize        ||
          ( woff.totalSfntSize & 3 ) != 0                          ||
@@ -169,21 +171,11 @@
 
     /* Write sfnt header. */
     {
-      FT_UInt  searchRange, entrySelector, rangeShift, x;
+      FT_Int  entrySelector = FT_MSB( woff.num_tables );
+      FT_Int  searchRange   = ( 1 << entrySelector ) * 16;
+      FT_Int  rangeShift    = woff.num_tables * 16 - searchRange;
 
 
-      x             = woff.num_tables;
-      entrySelector = 0;
-      while ( x )
-      {
-        x            >>= 1;
-        entrySelector += 1;
-      }
-      entrySelector--;
-
-      searchRange = ( 1 << entrySelector ) * 16;
-      rangeShift  = woff.num_tables * 16 - searchRange;
-
       WRITE_ULONG ( sfnt_header, woff.flavor );
       WRITE_USHORT( sfnt_header, woff.num_tables );
       WRITE_USHORT( sfnt_header, searchRange );
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff.h b/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff.h
index d4384227376b5..a04735ffe280f 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff.h
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff.h
@@ -4,7 +4,7 @@
  *
  *   WOFFF format management (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff2.c b/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff2.c
index 2be44a347ad95..589b3e0c6b77d 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff2.c
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff2.c
@@ -4,7 +4,7 @@
  *
  *   WOFF2 format management (base).
  *
- * Copyright (C) 2019-2023 by
+ * Copyright (C) 2019-2024 by
  * Nikhil Ramakrishnan, David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -18,6 +18,7 @@
 #include "sfwoff2.h"
 #include "woff2tags.h"
 #include 
+#include 
 #include 
 #include 
 
@@ -289,23 +290,15 @@
     FT_ULong  checksum     = 0;
     FT_ULong  aligned_size = size & ~3UL;
     FT_ULong  i;
-    FT_ULong  v;
+    FT_Int    shift;
 
 
     for ( i = 0; i < aligned_size; i += 4 )
-      checksum += ( (FT_ULong)buf[i    ] << 24 ) |
-                  ( (FT_ULong)buf[i + 1] << 16 ) |
-                  ( (FT_ULong)buf[i + 2] <<  8 ) |
-                  ( (FT_ULong)buf[i + 3] <<  0 );
+      checksum += FT_NEXT_ULONG( buf );
 
-    /* If size is not aligned to 4, treat as if it is padded with 0s. */
-    if ( size != aligned_size )
-    {
-      v = 0;
-      for ( i = aligned_size ; i < size; ++i )
-        v |= (FT_ULong)buf[i] << ( 24 - 8 * ( i & 3 ) );
-      checksum += v;
-    }
+    /* remaining bytes can be shifted and added one at a time */
+    for ( shift = 24; i < size; i++, shift -= 8 )
+      checksum += (FT_UInt32)FT_NEXT_BYTE( buf ) << shift;
 
     return checksum;
   }
@@ -1799,7 +1792,6 @@
 
     FT_Byte*   sfnt        = NULL;
     FT_Stream  sfnt_stream = NULL;
-    FT_Byte*   sfnt_header;
     FT_ULong   sfnt_size;
 
     FT_Byte*  uncompressed_buf = NULL;
@@ -1853,6 +1845,7 @@
     /* Miscellaneous checks. */
     if ( woff2.length != stream->size                               ||
          woff2.num_tables == 0                                      ||
+         woff2.num_tables >  0xFFFU                                 ||
          48 + woff2.num_tables * 20UL >= woff2.length               ||
          ( woff2.metaOffset == 0 && ( woff2.metaLength != 0     ||
                                       woff2.metaOrigLength != 0 ) ) ||
@@ -2143,6 +2136,13 @@
       WOFF2_TtcFont  ttc_font = woff2.ttc_fonts + face_index;
 
 
+      if ( ttc_font->num_tables == 0 || ttc_font->num_tables > 0xFFFU )
+      {
+        FT_ERROR(( "woff2_open_font: invalid WOFF2 CollectionFontEntry\n" ));
+        error = FT_THROW( Invalid_Table );
+        goto Exit;
+      }
+
       /* Create a temporary array. */
       if ( FT_QNEW_ARRAY( temp_indices,
                           ttc_font->num_tables ) )
@@ -2198,27 +2198,15 @@
          FT_NEW( sfnt_stream )        )
       goto Exit;
 
-    sfnt_header = sfnt;
-
-    WRITE_ULONG( sfnt_header, woff2.flavor );
-
-    if ( woff2.num_tables )
     {
-      FT_UInt  searchRange, entrySelector, rangeShift, x;
+      FT_Byte*  sfnt_header = sfnt;
 
+      FT_Int  entrySelector = FT_MSB( woff2.num_tables );
+      FT_Int  searchRange   = ( 1 << entrySelector ) * 16;
+      FT_Int  rangeShift    = woff2.num_tables * 16 - searchRange;
 
-      x             = woff2.num_tables;
-      entrySelector = 0;
-      while ( x )
-      {
-        x            >>= 1;
-        entrySelector += 1;
-      }
-      entrySelector--;
-
-      searchRange = ( 1 << entrySelector ) * 16;
-      rangeShift  = ( woff2.num_tables * 16 ) - searchRange;
 
+      WRITE_ULONG ( sfnt_header, woff2.flavor );
       WRITE_USHORT( sfnt_header, woff2.num_tables );
       WRITE_USHORT( sfnt_header, searchRange );
       WRITE_USHORT( sfnt_header, entrySelector );
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff2.h b/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff2.h
index 4901286ee085b..f41140648dc4e 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff2.h
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff2.h
@@ -4,7 +4,7 @@
  *
  *   WOFFF2 format management (specification).
  *
- * Copyright (C) 2019-2023 by
+ * Copyright (C) 2019-2024 by
  * Nikhil Ramakrishnan, David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttcmap.c b/src/java.desktop/share/native/libfreetype/src/sfnt/ttcmap.c
index 9ba25dcbc1385..28f4d1173c06f 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttcmap.c
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttcmap.c
@@ -4,7 +4,7 @@
  *
  *   TrueType character mapping table (cmap) support (body).
  *
- * Copyright (C) 2002-2023 by
+ * Copyright (C) 2002-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttcmap.h b/src/java.desktop/share/native/libfreetype/src/sfnt/ttcmap.h
index ff52917ed5bc0..e2c5e72bf0292 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttcmap.h
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttcmap.h
@@ -4,7 +4,7 @@
  *
  *   TrueType character mapping table (cmap) support (specification).
  *
- * Copyright (C) 2002-2023 by
+ * Copyright (C) 2002-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttcmapc.h b/src/java.desktop/share/native/libfreetype/src/sfnt/ttcmapc.h
index 0af48c2478af0..370898363f34c 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttcmapc.h
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttcmapc.h
@@ -4,7 +4,7 @@
  *
  *   TT CMAP classes definitions (specification only).
  *
- * Copyright (C) 2009-2023 by
+ * Copyright (C) 2009-2024 by
  * Oran Agra and Mickey Gabel.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttcolr.c b/src/java.desktop/share/native/libfreetype/src/sfnt/ttcolr.c
index 281e7135eea82..b37658dde9ec0 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttcolr.c
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttcolr.c
@@ -4,7 +4,7 @@
  *
  *   TrueType and OpenType colored glyph layer support (body).
  *
- * Copyright (C) 2018-2023 by
+ * Copyright (C) 2018-2024 by
  * David Turner, Robert Wilhelm, Dominik Röttsches, and Werner Lemberg.
  *
  * Originally written by Shao Yu Zhang .
@@ -208,18 +208,19 @@
     colr->num_base_glyphs = FT_NEXT_USHORT( p );
     base_glyph_offset     = FT_NEXT_ULONG( p );
 
-    if ( base_glyph_offset >= table_size )
+    if ( table_size <= base_glyph_offset )
       goto InvalidTable;
-    if ( colr->num_base_glyphs * BASE_GLYPH_SIZE >
-           table_size - base_glyph_offset )
+    if ( ( table_size - base_glyph_offset ) / BASE_GLYPH_SIZE
+             < colr->num_base_glyphs )
       goto InvalidTable;
 
     layer_offset     = FT_NEXT_ULONG( p );
     colr->num_layers = FT_NEXT_USHORT( p );
 
-    if ( layer_offset >= table_size )
+    if ( table_size <= layer_offset )
       goto InvalidTable;
-    if ( colr->num_layers * LAYER_SIZE > table_size - layer_offset )
+    if ( ( table_size - layer_offset ) / LAYER_SIZE
+             < colr->num_layers )
       goto InvalidTable;
 
     if ( colr->version == 1 )
@@ -229,14 +230,14 @@
 
       base_glyphs_offset_v1 = FT_NEXT_ULONG( p );
 
-      if ( base_glyphs_offset_v1 >= table_size - 4 )
+      if ( table_size - 4 <= base_glyphs_offset_v1 )
         goto InvalidTable;
 
       p1                 = (FT_Byte*)( table + base_glyphs_offset_v1 );
       num_base_glyphs_v1 = FT_PEEK_ULONG( p1 );
 
-      if ( num_base_glyphs_v1 * BASE_GLYPH_PAINT_RECORD_SIZE >
-             table_size - base_glyphs_offset_v1 )
+      if ( ( table_size - base_glyphs_offset_v1 ) / BASE_GLYPH_PAINT_RECORD_SIZE
+               < num_base_glyphs_v1 )
         goto InvalidTable;
 
       colr->num_base_glyphs_v1 = num_base_glyphs_v1;
@@ -244,19 +245,19 @@
 
       layer_offset_v1 = FT_NEXT_ULONG( p );
 
-      if ( layer_offset_v1 >= table_size )
+      if ( table_size <= layer_offset_v1 )
         goto InvalidTable;
 
       if ( layer_offset_v1 )
       {
-        if ( layer_offset_v1 >= table_size - 4 )
+        if ( table_size - 4 <= layer_offset_v1 )
           goto InvalidTable;
 
         p1            = (FT_Byte*)( table + layer_offset_v1 );
         num_layers_v1 = FT_PEEK_ULONG( p1 );
 
-        if ( num_layers_v1 * LAYER_V1_LIST_PAINT_OFFSET_SIZE >
-               table_size - layer_offset_v1 )
+        if ( ( table_size - layer_offset_v1 ) / LAYER_V1_LIST_PAINT_OFFSET_SIZE
+                < num_layers_v1 )
           goto InvalidTable;
 
         colr->num_layers_v1 = num_layers_v1;
@@ -279,7 +280,7 @@
 
       clip_list_offset = FT_NEXT_ULONG( p );
 
-      if ( clip_list_offset >= table_size )
+      if ( table_size <= clip_list_offset )
         goto InvalidTable;
 
       if ( clip_list_offset )
@@ -311,7 +312,7 @@
           goto InvalidTable;
 
         var_store_offset = FT_NEXT_ULONG( p );
-        if ( var_store_offset >= table_size )
+        if ( table_size <= var_store_offset )
           goto InvalidTable;
 
         if ( var_store_offset )
@@ -661,6 +662,7 @@
       FT_UInt32  first_layer_index;
 
 
+      ENSURE_READ_BYTES( 5 );
       num_layers = FT_NEXT_BYTE( p );
       if ( num_layers > colr->num_layers_v1 )
         return 0;
@@ -1278,7 +1280,8 @@
 
     while ( min < max )
     {
-      FT_UInt  mid = min + ( max - min ) / 2;
+      FT_UInt    mid = min + ( max - min ) / 2;
+      FT_UShort  gid;
 
       /*
        * `base_glyph_begin` is the beginning of `BaseGlyphV1List`;
@@ -1287,8 +1290,7 @@
        */
       FT_Byte  *p = base_glyph_begin + 4 + mid * BASE_GLYPH_PAINT_RECORD_SIZE;
 
-      FT_UShort  gid = FT_NEXT_USHORT( p );
-
+      gid = FT_NEXT_USHORT( p );
 
       if ( gid < glyph_id )
         min = mid + 1;
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttcolr.h b/src/java.desktop/share/native/libfreetype/src/sfnt/ttcolr.h
index 20c85f0359fe7..30031464c7371 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttcolr.h
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttcolr.h
@@ -4,7 +4,7 @@
  *
  *   TrueType and OpenType colored glyph layer support (specification).
  *
- * Copyright (C) 2018-2023 by
+ * Copyright (C) 2018-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * Originally written by Shao Yu Zhang .
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttcpal.c b/src/java.desktop/share/native/libfreetype/src/sfnt/ttcpal.c
index 46ae08596f39b..997eb869ffcaf 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttcpal.c
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttcpal.c
@@ -4,7 +4,7 @@
  *
  *   TrueType and OpenType color palette support (body).
  *
- * Copyright (C) 2018-2023 by
+ * Copyright (C) 2018-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * Originally written by Shao Yu Zhang .
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttcpal.h b/src/java.desktop/share/native/libfreetype/src/sfnt/ttcpal.h
index 8e9913f0ccd4a..bb301ae88b6a0 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttcpal.h
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttcpal.h
@@ -4,7 +4,7 @@
  *
  *   TrueType and OpenType color palette support (specification).
  *
- * Copyright (C) 2018-2023 by
+ * Copyright (C) 2018-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * Originally written by Shao Yu Zhang .
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttkern.c b/src/java.desktop/share/native/libfreetype/src/sfnt/ttkern.c
index a47d08bd6de66..f0411366af49d 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttkern.c
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttkern.c
@@ -5,7 +5,7 @@
  *   Load the basic TrueType kerning table.  This doesn't handle
  *   kerning data within the GPOS table at the moment.
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttkern.h b/src/java.desktop/share/native/libfreetype/src/sfnt/ttkern.h
index 960c7da4946d7..a54e51df12d96 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttkern.h
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttkern.h
@@ -5,7 +5,7 @@
  *   Load the basic TrueType kerning table.  This doesn't handle
  *   kerning data within the GPOS table at the moment.
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttload.c b/src/java.desktop/share/native/libfreetype/src/sfnt/ttload.c
index 7b44e9cd2e71c..c3a5fae2cb9b3 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttload.c
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttload.c
@@ -5,7 +5,7 @@
  *   Load the basic TrueType tables, i.e., tables that can be either in
  *   TTF or OTF fonts (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -1046,7 +1046,7 @@
   FT_LOCAL_DEF( void )
   tt_face_free_name( TT_Face  face )
   {
-    FT_Memory     memory = face->root.driver->root.memory;
+    FT_Memory     memory = face->root.memory;
     TT_NameTable  table  = &face->name_table;
 
 
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttload.h b/src/java.desktop/share/native/libfreetype/src/sfnt/ttload.h
index 1499dd5735f58..2b1d62d9bd9d5 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttload.h
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttload.h
@@ -5,7 +5,7 @@
  *   Load the basic TrueType tables, i.e., tables that can be either in
  *   TTF or OTF fonts (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttmtx.c b/src/java.desktop/share/native/libfreetype/src/sfnt/ttmtx.c
index 38ee9ae728a1d..2788411856360 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttmtx.c
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttmtx.c
@@ -4,7 +4,7 @@
  *
  *   Load the metrics tables common to TTF and OTF fonts (body).
  *
- * Copyright (C) 2006-2023 by
+ * Copyright (C) 2006-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttmtx.h b/src/java.desktop/share/native/libfreetype/src/sfnt/ttmtx.h
index 56d2b62766164..34b3c0e18f2d3 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttmtx.h
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttmtx.h
@@ -4,7 +4,7 @@
  *
  *   Load the metrics tables common to TTF and OTF fonts (specification).
  *
- * Copyright (C) 2006-2023 by
+ * Copyright (C) 2006-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttpost.c b/src/java.desktop/share/native/libfreetype/src/sfnt/ttpost.c
index 1dfad4298bd67..5698a62c8d1fb 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttpost.c
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttpost.c
@@ -5,7 +5,7 @@
  *   PostScript name table processing for TrueType and OpenType fonts
  *   (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -204,8 +204,8 @@
     /* now load the name strings */
     if ( num_names )
     {
-      FT_ULong   p;
-      FT_Byte*   strings;
+      FT_Byte*   p;
+      FT_Byte*   p_end;
 
 
       post_len -= (FT_ULong)num_glyphs * 2;
@@ -214,36 +214,36 @@
                                     post_len + 1 ) )
         goto Fail;
 
-      strings = (FT_Byte*)( name_strings + num_names );
-      if ( FT_STREAM_READ( strings, post_len ) )
+      p = (FT_Byte*)( name_strings + num_names );
+      if ( FT_STREAM_READ( p, post_len ) )
         goto Fail;
 
+      p_end = p + post_len;
+
       /* convert from Pascal- to C-strings and set pointers */
-      for ( p = 0, n = 0; p < post_len && n < num_names; n++ )
+      for ( n = 0; p < p_end && n < num_names; n++ )
       {
-        FT_UInt  len = strings[p];
+        FT_UInt  len = *p;
 
 
-        if ( len > 63U )
-        {
-          error = FT_THROW( Invalid_File_Format );
-          goto Fail;
-        }
+        /* names in the Adobe Glyph List are shorter than 40 characters */
+        if ( len >= 40U )
+          FT_TRACE4(( "load_format_20: unusual %u-char name found\n", len ));
 
-        strings[p]      = 0;
-        name_strings[n] = strings + p + 1;
-        p              += len + 1;
+        *p++            = 0;
+        name_strings[n] = p;
+        p              += len;
       }
-      strings[post_len] = 0;
+      *p_end = 0;
 
       /* deal with missing or insufficient string data */
       if ( n < num_names )
       {
         FT_TRACE4(( "load_format_20: %hu PostScript names are truncated\n",
-                    num_names - n ));
+                    (FT_UShort)( num_names - n ) ));
 
         for ( ; n < num_names; n++ )
-          name_strings[n] = strings + post_len;
+          name_strings[n] = p_end;
       }
     }
 
@@ -436,13 +436,8 @@
 
     format = face->postscript.FormatType;
 
-    if ( format == 0x00010000L )
-    {
-      if ( idx < 258 )                    /* paranoid checking */
-        *PSname = MAC_NAME( idx );
-    }
-    else if ( format == 0x00020000L ||
-              format == 0x00025000L )
+    if ( format == 0x00020000L ||
+         format == 0x00025000L )
     {
       TT_Post_Names  names = &face->postscript_names;
 
@@ -466,6 +461,11 @@
       }
     }
 
+    /* version 1.0 is only valid with 258 glyphs */
+    else if ( format == 0x00010000L              &&
+              face->max_profile.numGlyphs == 258 )
+      *PSname = MAC_NAME( idx );
+
     /* nothing to do for format == 0x00030000L */
 
   End:
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttpost.h b/src/java.desktop/share/native/libfreetype/src/sfnt/ttpost.h
index 528f1c5f2f207..150db6c3981eb 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttpost.h
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttpost.h
@@ -5,7 +5,7 @@
  *   PostScript name table processing for TrueType and OpenType fonts
  *   (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttsbit.c b/src/java.desktop/share/native/libfreetype/src/sfnt/ttsbit.c
index 03f90a628d6e6..cb3a8abf18214 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttsbit.c
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttsbit.c
@@ -4,7 +4,7 @@
  *
  *   TrueType and OpenType embedded bitmap support (body).
  *
- * Copyright (C) 2005-2023 by
+ * Copyright (C) 2005-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * Copyright 2013 by Google, Inc.
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttsbit.h b/src/java.desktop/share/native/libfreetype/src/sfnt/ttsbit.h
index 07e2db461a5c9..96f80a5842484 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttsbit.h
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttsbit.h
@@ -4,7 +4,7 @@
  *
  *   TrueType and OpenType embedded bitmap support (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/woff2tags.c b/src/java.desktop/share/native/libfreetype/src/sfnt/woff2tags.c
index eeedd9906be5a..532ccfa1737e8 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/woff2tags.c
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/woff2tags.c
@@ -4,7 +4,7 @@
  *
  *   WOFF2 Font table tags (base).
  *
- * Copyright (C) 2019-2023 by
+ * Copyright (C) 2019-2024 by
  * Nikhil Ramakrishnan, David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/woff2tags.h b/src/java.desktop/share/native/libfreetype/src/sfnt/woff2tags.h
index 1201848e5ecd9..d03b4b41bc9d8 100644
--- a/src/java.desktop/share/native/libfreetype/src/sfnt/woff2tags.h
+++ b/src/java.desktop/share/native/libfreetype/src/sfnt/woff2tags.h
@@ -4,7 +4,7 @@
  *
  *   WOFF2 Font table tags (specification).
  *
- * Copyright (C) 2019-2023 by
+ * Copyright (C) 2019-2024 by
  * Nikhil Ramakrishnan, David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/smooth/ftgrays.c b/src/java.desktop/share/native/libfreetype/src/smooth/ftgrays.c
index 0918272f87046..b7c0632a6fa9e 100644
--- a/src/java.desktop/share/native/libfreetype/src/smooth/ftgrays.c
+++ b/src/java.desktop/share/native/libfreetype/src/smooth/ftgrays.c
@@ -4,7 +4,7 @@
  *
  *   A new `perfect' anti-aliasing renderer (body).
  *
- * Copyright (C) 2000-2023 by
+ * Copyright (C) 2000-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -489,7 +489,7 @@ typedef ptrdiff_t  FT_PtrDist;
 
   typedef struct  gray_TWorker_
   {
-    ft_jmp_buf  jump_buffer;
+    FT_BBox     cbox;
 
     TCoord  min_ex, max_ex;  /* min and max integer pixel coordinates */
     TCoord  min_ey, max_ey;
@@ -510,6 +510,8 @@ typedef ptrdiff_t  FT_PtrDist;
     FT_Raster_Span_Func  render_span;
     void*                render_span_data;
 
+    ft_jmp_buf  jump_buffer;
+
   } gray_TWorker, *gray_PWorker;
 
 #if defined( _MSC_VER )
@@ -997,49 +999,12 @@ typedef ptrdiff_t  FT_PtrDist;
 #endif
 
   /*
-   * Benchmarking shows that using DDA to flatten the quadratic Bézier arcs
-   * is slightly faster in the following cases:
-   *
-   *   - When the host CPU is 64-bit.
-   *   - When SSE2 SIMD registers and instructions are available (even on
-   *     x86).
-   *
-   * For other cases, using binary splits is actually slightly faster.
-   */
-#if ( defined( __SSE2__ )                          ||   \
-      defined( __x86_64__ )                        ||   \
-      defined( _M_AMD64 )                          ||   \
-      ( defined( _M_IX86_FP ) && _M_IX86_FP >= 2 ) ) && \
-    !defined( __VMS )
-#  define FT_SSE2 1
-#else
-#  define FT_SSE2 0
-#endif
-
-#if FT_SSE2                || \
-    defined( __aarch64__ ) || \
-    defined( _M_ARM64 )
-#  define BEZIER_USE_DDA  1
-#else
-#  define BEZIER_USE_DDA  0
-#endif
-
-  /*
-   * For now, the code that depends on `BEZIER_USE_DDA` requires `FT_Int64`
-   * to be defined.  If `FT_INT64` is not defined, meaning there is no
-   * 64-bit type available, disable it to avoid compilation errors.  See for
-   * example https://gitlab.freedesktop.org/freetype/freetype/-/issues/1071.
+   * For now, the code that uses DDA to render conic curves requires
+   * `FT_Int64` to be defined.  See for example
+   *    https://gitlab.freedesktop.org/freetype/freetype/-/issues/1071.
    */
-#if !defined( FT_INT64 )
-#  undef BEZIER_USE_DDA
-#  define BEZIER_USE_DDA  0
-#endif
-
-#if BEZIER_USE_DDA
 
-#if FT_SSE2
-#  include 
-#endif
+#ifdef FT_INT64
 
 #define LEFT_SHIFT( a, b )  (FT_Int64)( (FT_UInt64)(a) << (b) )
 
@@ -1095,16 +1060,17 @@ typedef ptrdiff_t  FT_PtrDist;
       return;
     }
 
-    /* We can calculate the number of necessary bisections because  */
+    /* We can calculate the number of necessary segments because    */
     /* each bisection predictably reduces deviation exactly 4-fold. */
     /* Even 32-bit deviation would vanish after 16 bisections.      */
-    shift = 0;
+    shift = 16;
     do
     {
-      dx   >>= 2;
-      shift += 1;
+      dx >>= 2;
+      shift--;
 
     } while ( dx > ONE_PIXEL / 4 );
+    count = 0x10000U >> shift;
 
     /*
      * The (P0,P1,P2) arc equation, for t in [0,1] range:
@@ -1150,75 +1116,19 @@ typedef ptrdiff_t  FT_PtrDist;
      *             = (B << (33 - N)) + (A << (32 - 2*N))
      */
 
-#if FT_SSE2
-    /* Experience shows that for small shift values, */
-    /* SSE2 is actually slower.                      */
-    if ( shift > 2 )
-    {
-      union
-      {
-        struct { FT_Int64  ax, ay, bx, by; }  i;
-        struct { __m128i  a, b; }  vec;
-
-      } u;
-
-      union
-      {
-        struct { FT_Int32  px_lo, px_hi, py_lo, py_hi; }  i;
-        __m128i  vec;
-
-      } v;
-
-      __m128i  a, b;
-      __m128i  r, q, q2;
-      __m128i  p;
-
-
-      u.i.ax = ax;
-      u.i.ay = ay;
-      u.i.bx = bx;
-      u.i.by = by;
-
-      a = _mm_load_si128( &u.vec.a );
-      b = _mm_load_si128( &u.vec.b );
-
-      r  = _mm_slli_epi64( a, 33 - 2 * shift );
-      q  = _mm_slli_epi64( b, 33 - shift );
-      q2 = _mm_slli_epi64( a, 32 - 2 * shift );
-
-      q = _mm_add_epi64( q2, q );
-
-      v.i.px_lo = 0;
-      v.i.px_hi = p0.x;
-      v.i.py_lo = 0;
-      v.i.py_hi = p0.y;
-
-      p = _mm_load_si128( &v.vec );
-
-      for ( count = 1U << shift; count > 0; count-- )
-      {
-        p = _mm_add_epi64( p, q );
-        q = _mm_add_epi64( q, r );
-
-        _mm_store_si128( &v.vec, p );
-
-        gray_render_line( RAS_VAR_ v.i.px_hi, v.i.py_hi );
-      }
-
-      return;
-    }
-#endif  /* FT_SSE2 */
+    rx = LEFT_SHIFT( ax, shift + shift );
+    ry = LEFT_SHIFT( ay, shift + shift );
 
-    rx = LEFT_SHIFT( ax, 33 - 2 * shift );
-    ry = LEFT_SHIFT( ay, 33 - 2 * shift );
+    qx = LEFT_SHIFT( bx, shift + 17 ) + rx;
+    qy = LEFT_SHIFT( by, shift + 17 ) + ry;
 
-    qx = LEFT_SHIFT( bx, 33 - shift ) + LEFT_SHIFT( ax, 32 - 2 * shift );
-    qy = LEFT_SHIFT( by, 33 - shift ) + LEFT_SHIFT( ay, 32 - 2 * shift );
+    rx *= 2;
+    ry *= 2;
 
     px = LEFT_SHIFT( p0.x, 32 );
     py = LEFT_SHIFT( p0.y, 32 );
 
-    for ( count = 1U << shift; count > 0; count-- )
+    do
     {
       px += qx;
       py += qy;
@@ -1227,10 +1137,10 @@ typedef ptrdiff_t  FT_PtrDist;
 
       gray_render_line( RAS_VAR_ (FT_Pos)( px >> 32 ),
                                  (FT_Pos)( py >> 32 ) );
-    }
+    } while ( --count );
   }
 
-#else  /* !BEZIER_USE_DDA */
+#else  /* !FT_INT64 */
 
   /*
    * Note that multiple attempts to speed up the function below
@@ -1324,7 +1234,7 @@ typedef ptrdiff_t  FT_PtrDist;
     } while ( --draw );
   }
 
-#endif  /* !BEZIER_USE_DDA */
+#endif  /* !FT_INT64 */
 
 
   /*
@@ -1486,139 +1396,6 @@ typedef ptrdiff_t  FT_PtrDist;
   }
 
 
-  static void
-  gray_sweep( RAS_ARG )
-  {
-    int  fill = ( ras.outline.flags & FT_OUTLINE_EVEN_ODD_FILL ) ? 0x100
-                                                                 : INT_MIN;
-    int  coverage;
-    int  y;
-
-
-    for ( y = ras.min_ey; y < ras.max_ey; y++ )
-    {
-      PCell   cell  = ras.ycells[y - ras.min_ey];
-      TCoord  x     = ras.min_ex;
-      TArea   cover = 0;
-
-      unsigned char*  line = ras.target.origin - ras.target.pitch * y;
-
-
-      for ( ; cell != ras.cell_null; cell = cell->next )
-      {
-        TArea  area;
-
-
-        if ( cover != 0 && cell->x > x )
-        {
-          FT_FILL_RULE( coverage, cover, fill );
-          FT_GRAY_SET( line + x, coverage, cell->x - x );
-        }
-
-        cover += (TArea)cell->cover * ( ONE_PIXEL * 2 );
-        area   = cover - cell->area;
-
-        if ( area != 0 && cell->x >= ras.min_ex )
-        {
-          FT_FILL_RULE( coverage, area, fill );
-          line[cell->x] = (unsigned char)coverage;
-        }
-
-        x = cell->x + 1;
-      }
-
-      if ( cover != 0 )  /* only if cropped */
-      {
-        FT_FILL_RULE( coverage, cover, fill );
-        FT_GRAY_SET( line + x, coverage, ras.max_ex - x );
-      }
-    }
-  }
-
-
-  static void
-  gray_sweep_direct( RAS_ARG )
-  {
-    int  fill = ( ras.outline.flags & FT_OUTLINE_EVEN_ODD_FILL ) ? 0x100
-                                                                 : INT_MIN;
-    int  coverage;
-    int  y;
-
-    FT_Span  span[FT_MAX_GRAY_SPANS];
-    int      n = 0;
-
-
-    for ( y = ras.min_ey; y < ras.max_ey; y++ )
-    {
-      PCell   cell  = ras.ycells[y - ras.min_ey];
-      TCoord  x     = ras.min_ex;
-      TArea   cover = 0;
-
-
-      for ( ; cell != ras.cell_null; cell = cell->next )
-      {
-        TArea  area;
-
-
-        if ( cover != 0 && cell->x > x )
-        {
-          FT_FILL_RULE( coverage, cover, fill );
-
-          span[n].coverage = (unsigned char)coverage;
-          span[n].x        = (short)x;
-          span[n].len      = (unsigned short)( cell->x - x );
-
-          if ( ++n == FT_MAX_GRAY_SPANS )
-          {
-            /* flush the span buffer and reset the count */
-            ras.render_span( y, n, span, ras.render_span_data );
-            n = 0;
-          }
-        }
-
-        cover += (TArea)cell->cover * ( ONE_PIXEL * 2 );
-        area   = cover - cell->area;
-
-        if ( area != 0 && cell->x >= ras.min_ex )
-        {
-          FT_FILL_RULE( coverage, area, fill );
-
-          span[n].coverage = (unsigned char)coverage;
-          span[n].x        = (short)cell->x;
-          span[n].len      = 1;
-
-          if ( ++n == FT_MAX_GRAY_SPANS )
-          {
-            /* flush the span buffer and reset the count */
-            ras.render_span( y, n, span, ras.render_span_data );
-            n = 0;
-          }
-        }
-
-        x = cell->x + 1;
-      }
-
-      if ( cover != 0 )  /* only if cropped */
-      {
-        FT_FILL_RULE( coverage, cover, fill );
-
-        span[n].coverage = (unsigned char)coverage;
-        span[n].x        = (short)x;
-        span[n].len      = (unsigned short)( ras.max_ex - x );
-
-        ++n;
-      }
-
-      if ( n )
-      {
-        /* flush the span buffer and reset the count */
-        ras.render_span( y, n, span, ras.render_span_data );
-        n = 0;
-      }
-    }
-  }
-
-
 #ifdef STANDALONE_
 
   /**************************************************************************
@@ -1934,7 +1711,7 @@ typedef ptrdiff_t  FT_PtrDist;
       if ( continued )
         FT_Trace_Enable();
 
-      FT_TRACE7(( "band [%d..%d]: %td cell%s remaining/\n",
+      FT_TRACE7(( "band [%d..%d]: %td cell%s remaining\n",
                   ras.min_ey,
                   ras.max_ey,
                   ras.cell_null - ras.cell_free,
@@ -1952,14 +1729,144 @@ typedef ptrdiff_t  FT_PtrDist;
   }
 
 
+  static void
+  gray_sweep( RAS_ARG )
+  {
+    int  fill = ( ras.outline.flags & FT_OUTLINE_EVEN_ODD_FILL ) ? 0x100
+                                                                 : INT_MIN;
+    int  coverage;
+    int  y;
+
+
+    for ( y = ras.min_ey; y < ras.max_ey; y++ )
+    {
+      PCell   cell  = ras.ycells[y - ras.min_ey];
+      TCoord  x     = ras.min_ex;
+      TArea   cover = 0;
+
+      unsigned char*  line = ras.target.origin - ras.target.pitch * y;
+
+
+      for ( ; cell != ras.cell_null; cell = cell->next )
+      {
+        TArea  area;
+
+
+        if ( cover != 0 && cell->x > x )
+        {
+          FT_FILL_RULE( coverage, cover, fill );
+          FT_GRAY_SET( line + x, coverage, cell->x - x );
+        }
+
+        cover += (TArea)cell->cover * ( ONE_PIXEL * 2 );
+        area   = cover - cell->area;
+
+        if ( area != 0 && cell->x >= ras.min_ex )
+        {
+          FT_FILL_RULE( coverage, area, fill );
+          line[cell->x] = (unsigned char)coverage;
+        }
+
+        x = cell->x + 1;
+      }
+
+      if ( cover != 0 )  /* only if cropped */
+      {
+        FT_FILL_RULE( coverage, cover, fill );
+        FT_GRAY_SET( line + x, coverage, ras.max_ex - x );
+      }
+    }
+  }
+
+
+  static void
+  gray_sweep_direct( RAS_ARG )
+  {
+    int  fill = ( ras.outline.flags & FT_OUTLINE_EVEN_ODD_FILL ) ? 0x100
+                                                                 : INT_MIN;
+    int  coverage;
+    int  y;
+
+    FT_Span  span[FT_MAX_GRAY_SPANS];
+    int      n = 0;
+
+
+    for ( y = ras.min_ey; y < ras.max_ey; y++ )
+    {
+      PCell   cell  = ras.ycells[y - ras.min_ey];
+      TCoord  x     = ras.min_ex;
+      TArea   cover = 0;
+
+
+      for ( ; cell != ras.cell_null; cell = cell->next )
+      {
+        TArea  area;
+
+
+        if ( cover != 0 && cell->x > x )
+        {
+          FT_FILL_RULE( coverage, cover, fill );
+
+          span[n].coverage = (unsigned char)coverage;
+          span[n].x        = (short)x;
+          span[n].len      = (unsigned short)( cell->x - x );
+
+          if ( ++n == FT_MAX_GRAY_SPANS )
+          {
+            /* flush the span buffer and reset the count */
+            ras.render_span( y, n, span, ras.render_span_data );
+            n = 0;
+          }
+        }
+
+        cover += (TArea)cell->cover * ( ONE_PIXEL * 2 );
+        area   = cover - cell->area;
+
+        if ( area != 0 && cell->x >= ras.min_ex )
+        {
+          FT_FILL_RULE( coverage, area, fill );
+
+          span[n].coverage = (unsigned char)coverage;
+          span[n].x        = (short)cell->x;
+          span[n].len      = 1;
+
+          if ( ++n == FT_MAX_GRAY_SPANS )
+          {
+            /* flush the span buffer and reset the count */
+            ras.render_span( y, n, span, ras.render_span_data );
+            n = 0;
+          }
+        }
+
+        x = cell->x + 1;
+      }
+
+      if ( cover != 0 )  /* only if cropped */
+      {
+        FT_FILL_RULE( coverage, cover, fill );
+
+        span[n].coverage = (unsigned char)coverage;
+        span[n].x        = (short)x;
+        span[n].len      = (unsigned short)( ras.max_ex - x );
+
+        ++n;
+      }
+
+      if ( n )
+      {
+        /* flush the span buffer and reset the count */
+        ras.render_span( y, n, span, ras.render_span_data );
+        n = 0;
+      }
+    }
+  }
+
+
   static int
   gray_convert_glyph( RAS_ARG )
   {
-    const TCoord  yMin = ras.min_ey;
-    const TCoord  yMax = ras.max_ey;
-
     TCell    buffer[FT_MAX_GRAY_POOL];
-    size_t   height = (size_t)( yMax - yMin );
+    size_t   height = (size_t)( ras.cbox.yMax - ras.cbox.yMin );
     size_t   n = FT_MAX_GRAY_POOL / 8;
     TCoord   y;
     TCoord   bands[32];  /* enough to accommodate bisections */
@@ -1985,35 +1892,36 @@ typedef ptrdiff_t  FT_PtrDist;
       height  = ( height + n - 1 ) / n;
     }
 
-    for ( y = yMin; y < yMax; )
+    for ( y = ras.cbox.yMin; y < ras.cbox.yMax; )
     {
       ras.min_ey = y;
       y         += height;
-      ras.max_ey = FT_MIN( y, yMax );
+      ras.max_ey = FT_MIN( y, ras.cbox.yMax );
+
+      ras.count_ey = ras.max_ey - ras.min_ey;
 
       band    = bands;
-      band[1] = ras.min_ey;
-      band[0] = ras.max_ey;
+      band[1] = ras.cbox.xMin;
+      band[0] = ras.cbox.xMax;
 
       do
       {
-        TCoord  width = band[0] - band[1];
-        TCoord  w;
+        TCoord  i;
         int     error;
 
 
-        for ( w = 0; w < width; ++w )
-          ras.ycells[w] = ras.cell_null;
+        ras.min_ex = band[1];
+        ras.max_ex = band[0];
+
+        /* memory management: zero out and skip ycells */
+        for ( i = 0; i < ras.count_ey; ++i )
+          ras.ycells[i] = ras.cell_null;
 
-        /* memory management: skip ycells */
-        n = ( (size_t)width * sizeof ( PCell ) + sizeof ( TCell ) - 1 ) /
-              sizeof ( TCell );
+        n = ( (size_t)ras.count_ey * sizeof ( PCell ) + sizeof ( TCell ) - 1 )
+              / sizeof ( TCell );
 
         ras.cell_free = buffer + n;
         ras.cell      = ras.cell_null;
-        ras.min_ey    = band[1];
-        ras.max_ey    = band[0];
-        ras.count_ey  = width;
 
         error     = gray_convert_glyph_inner( RAS_VAR_ continued );
         continued = 1;
@@ -2031,10 +1939,10 @@ typedef ptrdiff_t  FT_PtrDist;
           return error;
 
         /* render pool overflow; we will reduce the render band by half */
-        width >>= 1;
+        i = ( band[0] - band[1] ) >> 1;
 
         /* this should never happen even with tiny rendering pool */
-        if ( width == 0 )
+        if ( i == 0 )
         {
           FT_TRACE7(( "gray_convert_glyph: rotten glyph\n" ));
           return FT_THROW( Raster_Overflow );
@@ -2042,7 +1950,7 @@ typedef ptrdiff_t  FT_PtrDist;
 
         band++;
         band[1]  = band[0];
-        band[0] += width;
+        band[0] += i;
       } while ( band >= bands );
     }
 
@@ -2073,7 +1981,7 @@ typedef ptrdiff_t  FT_PtrDist;
       return FT_THROW( Invalid_Outline );
 
     /* return immediately if the outline is empty */
-    if ( outline->n_points == 0 || outline->n_contours <= 0 )
+    if ( outline->n_points == 0 || outline->n_contours == 0 )
       return Smooth_Err_Ok;
 
     if ( !outline->contours || !outline->points )
@@ -2093,10 +2001,7 @@ typedef ptrdiff_t  FT_PtrDist;
       ras.render_span      = (FT_Raster_Span_Func)params->gray_spans;
       ras.render_span_data = params->user;
 
-      ras.min_ex = params->clip_box.xMin;
-      ras.min_ey = params->clip_box.yMin;
-      ras.max_ex = params->clip_box.xMax;
-      ras.max_ey = params->clip_box.yMax;
+      ras.cbox = params->clip_box;
     }
     else
     {
@@ -2122,14 +2027,14 @@ typedef ptrdiff_t  FT_PtrDist;
       ras.render_span      = (FT_Raster_Span_Func)NULL;
       ras.render_span_data = NULL;
 
-      ras.min_ex = 0;
-      ras.min_ey = 0;
-      ras.max_ex = (FT_Pos)target_map->width;
-      ras.max_ey = (FT_Pos)target_map->rows;
+      ras.cbox.xMin = 0;
+      ras.cbox.yMin = 0;
+      ras.cbox.xMax = (FT_Pos)target_map->width;
+      ras.cbox.yMax = (FT_Pos)target_map->rows;
     }
 
     /* exit if nothing to do */
-    if ( ras.max_ex <= ras.min_ex || ras.max_ey <= ras.min_ey )
+    if ( ras.cbox.xMin >= ras.cbox.xMax || ras.cbox.yMin >= ras.cbox.yMax )
       return Smooth_Err_Ok;
 
     return gray_convert_glyph( RAS_VAR );
diff --git a/src/java.desktop/share/native/libfreetype/src/smooth/ftgrays.h b/src/java.desktop/share/native/libfreetype/src/smooth/ftgrays.h
index a5001bf40d325..940fbe8c79bdd 100644
--- a/src/java.desktop/share/native/libfreetype/src/smooth/ftgrays.h
+++ b/src/java.desktop/share/native/libfreetype/src/smooth/ftgrays.h
@@ -4,7 +4,7 @@
  *
  *   FreeType smooth renderer declaration
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/smooth/ftsmerrs.h b/src/java.desktop/share/native/libfreetype/src/smooth/ftsmerrs.h
index f4ac93dc410f3..6d41fb8e0fd96 100644
--- a/src/java.desktop/share/native/libfreetype/src/smooth/ftsmerrs.h
+++ b/src/java.desktop/share/native/libfreetype/src/smooth/ftsmerrs.h
@@ -4,7 +4,7 @@
  *
  *   smooth renderer error codes (specification only).
  *
- * Copyright (C) 2001-2023 by
+ * Copyright (C) 2001-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/smooth/ftsmooth.c b/src/java.desktop/share/native/libfreetype/src/smooth/ftsmooth.c
index 9b0e8886cb332..f0acc1ea4a6e2 100644
--- a/src/java.desktop/share/native/libfreetype/src/smooth/ftsmooth.c
+++ b/src/java.desktop/share/native/libfreetype/src/smooth/ftsmooth.c
@@ -4,7 +4,7 @@
  *
  *   Anti-aliasing renderer interface (body).
  *
- * Copyright (C) 2000-2023 by
+ * Copyright (C) 2000-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/smooth/ftsmooth.h b/src/java.desktop/share/native/libfreetype/src/smooth/ftsmooth.h
index f8bdc9938b32c..d7b61a9e60ec3 100644
--- a/src/java.desktop/share/native/libfreetype/src/smooth/ftsmooth.h
+++ b/src/java.desktop/share/native/libfreetype/src/smooth/ftsmooth.h
@@ -4,7 +4,7 @@
  *
  *   Anti-aliasing renderer interface (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/truetype/ttdriver.c b/src/java.desktop/share/native/libfreetype/src/truetype/ttdriver.c
index d1496fec7fad7..4ab68eb9a12d5 100644
--- a/src/java.desktop/share/native/libfreetype/src/truetype/ttdriver.c
+++ b/src/java.desktop/share/native/libfreetype/src/truetype/ttdriver.c
@@ -4,7 +4,7 @@
  *
  *   TrueType font driver implementation (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -217,7 +217,20 @@
     kerning->y = 0;
 
     if ( sfnt )
-      kerning->x = sfnt->get_kerning( ttface, left_glyph, right_glyph );
+    {
+      /* Use 'kern' table if available since that can be faster; otherwise */
+      /* use GPOS kerning pairs if available.                              */
+      if ( ttface->kern_avail_bits != 0 )
+        kerning->x = sfnt->get_kerning( ttface,
+                                        left_glyph,
+                                        right_glyph );
+#ifdef TT_CONFIG_OPTION_GPOS_KERNING
+      else if ( ttface->gpos_kerning_available )
+        kerning->x = sfnt->get_gpos_kerning( ttface,
+                                             left_glyph,
+                                             right_glyph );
+#endif
+    }
 
     return 0;
   }
diff --git a/src/java.desktop/share/native/libfreetype/src/truetype/ttdriver.h b/src/java.desktop/share/native/libfreetype/src/truetype/ttdriver.h
index 757a66f425d5b..3e1cf234fcfd4 100644
--- a/src/java.desktop/share/native/libfreetype/src/truetype/ttdriver.h
+++ b/src/java.desktop/share/native/libfreetype/src/truetype/ttdriver.h
@@ -4,7 +4,7 @@
  *
  *   High-level TrueType driver interface (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/truetype/tterrors.h b/src/java.desktop/share/native/libfreetype/src/truetype/tterrors.h
index 008ee99853cbb..7ad937bd04d35 100644
--- a/src/java.desktop/share/native/libfreetype/src/truetype/tterrors.h
+++ b/src/java.desktop/share/native/libfreetype/src/truetype/tterrors.h
@@ -4,7 +4,7 @@
  *
  *   TrueType error codes (specification only).
  *
- * Copyright (C) 2001-2023 by
+ * Copyright (C) 2001-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/truetype/ttgload.c b/src/java.desktop/share/native/libfreetype/src/truetype/ttgload.c
index dc427e8a1160a..b656ccf04e3c1 100644
--- a/src/java.desktop/share/native/libfreetype/src/truetype/ttgload.c
+++ b/src/java.desktop/share/native/libfreetype/src/truetype/ttgload.c
@@ -4,7 +4,7 @@
  *
  *   TrueType Glyph Loader (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -353,7 +353,8 @@
     FT_Byte         c, count;
     FT_Vector       *vec, *vec_limit;
     FT_Pos          x, y;
-    FT_Short        *cont, *cont_limit, last;
+    FT_UShort       *cont, *cont_limit;
+    FT_Int          last;
 
 
     /* check that we can add the contours to the glyph */
@@ -372,7 +373,7 @@
     last = -1;
     for ( ; cont < cont_limit; cont++ )
     {
-      *cont = FT_NEXT_SHORT( p );
+      *cont = FT_NEXT_USHORT( p );
 
       if ( *cont <= last )
         goto Invalid_Outline;
@@ -418,11 +419,9 @@
       /* and thus allocate the bytecode array size by ourselves     */
       if ( n_ins )
       {
-        if ( FT_QNEW_ARRAY( exec->glyphIns, n_ins ) )
+        if ( FT_DUP( exec->glyphIns, p, n_ins ) )
           return error;
 
-        FT_MEM_COPY( exec->glyphIns, p, (FT_Long)n_ins );
-
         exec->glyphSize  = n_ins;
       }
     }
@@ -432,7 +431,7 @@
     p += n_ins;
 
     /* reading the point tags */
-    flag       = (FT_Byte*)outline->tags;
+    flag       = outline->tags;
     flag_limit = flag + n_points;
 
     FT_ASSERT( flag );
@@ -465,7 +464,7 @@
 
     vec       = outline->points;
     vec_limit = vec + n_points;
-    flag      = (FT_Byte*)outline->tags;
+    flag      = outline->tags;
     x         = 0;
 
     for ( ; vec < vec_limit; vec++, flag++ )
@@ -499,7 +498,7 @@
 
     vec       = outline->points;
     vec_limit = vec + n_points;
-    flag      = (FT_Byte*)outline->tags;
+    flag      = outline->tags;
     y         = 0;
 
     for ( ; vec < vec_limit; vec++, flag++ )
@@ -532,8 +531,8 @@
       *flag  = (FT_Byte)( f & ON_CURVE_POINT );
     }
 
-    outline->n_points   = (FT_Short)n_points;
-    outline->n_contours = (FT_Short)n_contours;
+    outline->n_points   = (FT_UShort)n_points;
+    outline->n_contours = (FT_UShort)n_contours;
 
     load->cursor = p;
 
@@ -754,15 +753,13 @@
                    FT_UInt       start_point,
                    FT_UInt       start_contour )
   {
-    zone->n_points    = (FT_UShort)load->outline.n_points + 4 -
-                          (FT_UShort)start_point;
-    zone->n_contours  = load->outline.n_contours -
-                          (FT_Short)start_contour;
+    zone->n_points    = load->outline.n_points + 4 - (FT_UShort)start_point;
+    zone->n_contours  = load->outline.n_contours - (FT_UShort)start_contour;
     zone->org         = load->extra_points + start_point;
     zone->cur         = load->outline.points + start_point;
     zone->orus        = load->extra_points2 + start_point;
-    zone->tags        = (FT_Byte*)load->outline.tags + start_point;
-    zone->contours    = (FT_UShort*)load->outline.contours + start_contour;
+    zone->tags        = load->outline.tags + start_point;
+    zone->contours    = load->outline.contours + start_contour;
     zone->first_point = (FT_UShort)start_point;
   }
 
@@ -1046,7 +1043,7 @@
     current.points   = gloader->base.outline.points +
                          num_base_points;
     current.n_points = gloader->base.outline.n_points -
-                         (short)num_base_points;
+                         (FT_UShort)num_base_points;
 
     have_scale = FT_BOOL( subglyph->flags & ( WE_HAVE_A_SCALE     |
                                               WE_HAVE_AN_XY_SCALE |
@@ -1059,7 +1056,7 @@
     /* get offset */
     if ( !( subglyph->flags & ARGS_ARE_XY_VALUES ) )
     {
-      FT_UInt     num_points = (FT_UInt)gloader->base.outline.n_points;
+      FT_UInt     num_points = gloader->base.outline.n_points;
       FT_UInt     k = (FT_UInt)subglyph->arg1;
       FT_UInt     l = (FT_UInt)subglyph->arg2;
       FT_Vector*  p1;
@@ -1721,8 +1718,8 @@
         FT_List_Add( &loader->composites, node );
       }
 
-      start_point   = (FT_UInt)gloader->base.outline.n_points;
-      start_contour = (FT_UInt)gloader->base.outline.n_contours;
+      start_point   = gloader->base.outline.n_points;
+      start_contour = gloader->base.outline.n_contours;
 
       /* for each subglyph, read composite header */
       error = face->read_composite_glyph( loader );
@@ -1741,14 +1738,14 @@
       if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) ||
            FT_IS_VARIATION( FT_FACE( face ) )      )
       {
-        short        i, limit;
+        FT_UShort    i, limit;
         FT_SubGlyph  subglyph;
 
         FT_Outline  outline = { 0, 0, NULL, NULL, NULL, 0 };
         FT_Vector*  unrounded = NULL;
 
 
-        limit = (short)gloader->current.num_subglyphs;
+        limit = (FT_UShort)gloader->current.num_subglyphs;
 
         /* construct an outline structure for              */
         /* communication with `TT_Vary_Apply_Glyph_Deltas' */
@@ -1874,7 +1871,7 @@
           linear_hadvance = loader->linear;
           linear_vadvance = loader->vadvance;
 
-          num_base_points = (FT_UInt)gloader->base.outline.n_points;
+          num_base_points = gloader->base.outline.n_points;
 
           error = load_truetype_glyph( loader,
                                        (FT_UInt)subglyph->index,
@@ -1898,7 +1895,7 @@
             loader->vadvance = linear_vadvance;
           }
 
-          num_points = (FT_UInt)gloader->base.outline.n_points;
+          num_points = gloader->base.outline.n_points;
 
           if ( num_points == num_base_points )
             continue;
@@ -2313,7 +2310,7 @@
        *
        * 1) we have a `tricky' font that heavily relies on the interpreter to
        *    render glyphs correctly, for example DFKai-SB, or
-       * 2) FT_RENDER_MODE_MONO (i.e, monochome rendering) is requested.
+       * 2) FT_RENDER_MODE_MONO (i.e, monochrome rendering) is requested.
        *
        * In those cases, backward compatibility needs to be turned off to get
        * correct rendering.  The rendering is then completely up to the
@@ -2719,7 +2716,7 @@
          size->metrics->y_ppem < 24         )
       glyph->outline.flags |= FT_OUTLINE_HIGH_PRECISION;
 
-    FT_TRACE1(( "  subglyphs = %u, contours = %hd, points = %hd,"
+    FT_TRACE1(( "  subglyphs = %u, contours = %hu, points = %hu,"
                 " flags = 0x%.3x\n",
                 loader.gloader->base.num_subglyphs,
                 glyph->outline.n_contours,
diff --git a/src/java.desktop/share/native/libfreetype/src/truetype/ttgload.h b/src/java.desktop/share/native/libfreetype/src/truetype/ttgload.h
index f18637dce330b..22ea967f30133 100644
--- a/src/java.desktop/share/native/libfreetype/src/truetype/ttgload.h
+++ b/src/java.desktop/share/native/libfreetype/src/truetype/ttgload.h
@@ -4,7 +4,7 @@
  *
  *   TrueType Glyph Loader (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/truetype/ttgxvar.c b/src/java.desktop/share/native/libfreetype/src/truetype/ttgxvar.c
index 9d149ea365cf1..4f0083c96b7c4 100644
--- a/src/java.desktop/share/native/libfreetype/src/truetype/ttgxvar.c
+++ b/src/java.desktop/share/native/libfreetype/src/truetype/ttgxvar.c
@@ -4,7 +4,7 @@
  *
  *   TrueType GX Font Variation loader
  *
- * Copyright (C) 2004-2023 by
+ * Copyright (C) 2004-2024 by
  * David Turner, Robert Wilhelm, Werner Lemberg, and George Williams.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -129,9 +129,6 @@
    *   stream ::
    *     The data stream.
    *
-   *   size ::
-   *     The size of the table holding the data.
-   *
    * @Output:
    *   point_cnt ::
    *     The number of points read.  A zero value means that
@@ -144,14 +141,14 @@
    */
   static FT_UShort*
   ft_var_readpackedpoints( FT_Stream  stream,
-                           FT_ULong   size,
                            FT_UInt   *point_cnt )
   {
     FT_UShort *points = NULL;
     FT_UInt    n;
-    FT_UInt    runcnt;
+    FT_UInt    runcnt, cnt;
     FT_UInt    i, j;
     FT_UShort  first;
+    FT_Byte*   p;
     FT_Memory  memory = stream->memory;
     FT_Error   error;
 
@@ -169,56 +166,60 @@
       n  |= FT_GET_BYTE();
     }
 
-    if ( n > size )
-    {
-      FT_TRACE1(( "ft_var_readpackedpoints: number of points too large\n" ));
+    if ( FT_QNEW_ARRAY( points, n ) )
       return NULL;
-    }
-
-    /* in the nested loops below we increase `i' twice; */
-    /* it is faster to simply allocate one more slot    */
-    /* than to add another test within the loop         */
-    if ( FT_QNEW_ARRAY( points, n + 1 ) )
-      return NULL;
-
-    *point_cnt = n;
 
+    p     = stream->cursor;
     first = 0;
     i     = 0;
     while ( i < n )
     {
-      runcnt = FT_GET_BYTE();
+      if ( p >= stream->limit )
+        goto Fail;
+
+      runcnt = FT_NEXT_BYTE( p );
+      cnt    = runcnt & GX_PT_POINT_RUN_COUNT_MASK;
+
+      /* first point not included in run count */
+      cnt++;
+      if ( cnt > n - i )
+        cnt = n - i;
+
       if ( runcnt & GX_PT_POINTS_ARE_WORDS )
       {
-        runcnt     &= GX_PT_POINT_RUN_COUNT_MASK;
-        first      += FT_GET_USHORT();
-        points[i++] = first;
+        if ( 2 * cnt > (FT_UInt)( stream->limit - p ) )
+          goto Fail;
 
-        /* first point not included in run count */
-        for ( j = 0; j < runcnt; j++ )
+        for ( j = 0; j < cnt; j++ )
         {
-          first      += FT_GET_USHORT();
+          first      += FT_NEXT_USHORT( p );
           points[i++] = first;
-          if ( i >= n )
-            break;
         }
       }
       else
       {
-        first      += FT_GET_BYTE();
-        points[i++] = first;
+        if ( cnt > (FT_UInt)( stream->limit - p ) )
+          goto Fail;
 
-        for ( j = 0; j < runcnt; j++ )
+        for ( j = 0; j < cnt; j++ )
         {
-          first      += FT_GET_BYTE();
+          first      += FT_NEXT_BYTE( p );
           points[i++] = first;
-          if ( i >= n )
-            break;
         }
       }
     }
 
+    stream->cursor = p;
+
+    *point_cnt = n;
+
     return points;
+
+  Fail:
+    FT_TRACE1(( "ft_var_readpackedpoints: invalid table\n" ));
+
+    FT_FREE( points );
+    return NULL;
   }
 
 
@@ -240,9 +241,6 @@
    *   stream ::
    *     The data stream.
    *
-   *   size ::
-   *     The size of the table holding the data.
-   *
    *   delta_cnt ::
    *     The number of deltas to be read.
    *
@@ -258,13 +256,12 @@
    */
   static FT_Fixed*
   ft_var_readpackeddeltas( FT_Stream  stream,
-                           FT_ULong   size,
                            FT_UInt    delta_cnt )
   {
     FT_Fixed  *deltas = NULL;
     FT_UInt    runcnt, cnt;
     FT_UInt    i, j;
-    FT_UInt    bytes_used;
+    FT_Byte*   p;
     FT_Memory  memory = stream->memory;
     FT_Error   error;
 
@@ -272,68 +269,51 @@
     if ( FT_QNEW_ARRAY( deltas, delta_cnt ) )
       return NULL;
 
-    i          = 0;
-    bytes_used = 0;
-
-    while ( i < delta_cnt && bytes_used < size )
+    p = stream->cursor;
+    i = 0;
+    while ( i < delta_cnt )
     {
-      runcnt = FT_GET_BYTE();
+      if ( p >= stream->limit )
+        goto Fail;
+
+      runcnt = FT_NEXT_BYTE( p );
       cnt    = runcnt & GX_DT_DELTA_RUN_COUNT_MASK;
 
-      bytes_used++;
+      /* first point not included in run count */
+      cnt++;
+      if ( cnt > delta_cnt - i )
+        cnt = delta_cnt - i;
 
       if ( runcnt & GX_DT_DELTAS_ARE_ZERO )
       {
-        /* `cnt` + 1 zeroes get added */
-        for ( j = 0; j <= cnt && i < delta_cnt; j++ )
+        for ( j = 0; j < cnt; j++ )
           deltas[i++] = 0;
       }
       else if ( runcnt & GX_DT_DELTAS_ARE_WORDS )
       {
-        /* `cnt` + 1 shorts from the stack */
-        bytes_used += 2 * ( cnt + 1 );
-        if ( bytes_used > size )
-        {
-          FT_TRACE1(( "ft_var_readpackeddeltas:"
-                      " number of short deltas too large\n" ));
+        if ( 2 * cnt > (FT_UInt)( stream->limit - p ) )
           goto Fail;
-        }
 
-        for ( j = 0; j <= cnt && i < delta_cnt; j++ )
-          deltas[i++] = FT_intToFixed( FT_GET_SHORT() );
+        for ( j = 0; j < cnt; j++ )
+          deltas[i++] = FT_intToFixed( FT_NEXT_SHORT( p ) );
       }
       else
       {
-        /* `cnt` + 1 signed bytes from the stack */
-        bytes_used += cnt + 1;
-        if ( bytes_used > size )
-        {
-          FT_TRACE1(( "ft_var_readpackeddeltas:"
-                      " number of byte deltas too large\n" ));
+        if ( cnt > (FT_UInt)( stream->limit - p ) )
           goto Fail;
-        }
 
-        for ( j = 0; j <= cnt && i < delta_cnt; j++ )
-          deltas[i++] = FT_intToFixed( FT_GET_CHAR() );
-      }
-
-      if ( j <= cnt )
-      {
-        FT_TRACE1(( "ft_var_readpackeddeltas:"
-                    " number of deltas too large\n" ));
-        goto Fail;
+        for ( j = 0; j < cnt; j++ )
+          deltas[i++] = FT_intToFixed( FT_NEXT_CHAR( p ) );
       }
     }
 
-    if ( i < delta_cnt )
-    {
-      FT_TRACE1(( "ft_var_readpackeddeltas: not enough deltas\n" ));
-      goto Fail;
-    }
+    stream->cursor = p;
 
     return deltas;
 
   Fail:
+    FT_TRACE1(( "ft_var_readpackeddeltas: invalid table\n" ));
+
     FT_FREE( deltas );
     return NULL;
   }
@@ -596,7 +576,7 @@
 
       for ( j = 0; j < itemStore->axisCount; j++ )
       {
-        FT_Short  start, peak, end;
+        FT_Int  start, peak, end;
 
 
         if ( FT_READ_SHORT( start ) ||
@@ -604,6 +584,10 @@
              FT_READ_SHORT( end )   )
           goto Exit;
 
+        /* immediately tag invalid ranges with special peak = 0 */
+        if ( ( start < 0 && end > 0 ) || start > peak || peak > end )
+          peak = 0;
+
         axisCoords[j].startCoord = FT_fdot14ToFixed( start );
         axisCoords[j].peakCoord  = FT_fdot14ToFixed( peak );
         axisCoords[j].endCoord   = FT_fdot14ToFixed( end );
@@ -1024,6 +1008,9 @@
     if ( innerIndex >= varData->itemCount )
       return 0; /* Out of range. */
 
+    if ( varData->regionIdxCount == 0 )
+      return 0; /* Avoid "applying zero offset to null pointer". */
+
     if ( varData->regionIdxCount < 16 )
     {
       deltaSet = deltaSetStack;
@@ -1074,43 +1061,32 @@
       /* inner loop steps through axes in this region */
       for ( j = 0; j < itemStore->axisCount; j++, axis++ )
       {
-        /* compute the scalar contribution of this axis; */
-        /* ignore invalid ranges                         */
-        if ( axis->startCoord > axis->peakCoord ||
-             axis->peakCoord > axis->endCoord   )
-          continue;
+        FT_Fixed  ncv = ttface->blend->normalizedcoords[j];
 
-        else if ( axis->startCoord < 0 &&
-                  axis->endCoord > 0   &&
-                  axis->peakCoord != 0 )
-          continue;
 
-        /* peak of 0 means ignore this axis */
-        else if ( axis->peakCoord == 0 )
-          continue;
-
-        else if ( ttface->blend->normalizedcoords[j] == axis->peakCoord )
+        /* compute the scalar contribution of this axis */
+        /* with peak of 0 used for invalid axes         */
+        if ( axis->peakCoord == ncv ||
+             axis->peakCoord == 0   )
           continue;
 
         /* ignore this region if coords are out of range */
-        else if ( ttface->blend->normalizedcoords[j] <= axis->startCoord ||
-                  ttface->blend->normalizedcoords[j] >= axis->endCoord   )
+        else if ( ncv <= axis->startCoord ||
+                  ncv >= axis->endCoord   )
         {
           scalar = 0;
           break;
         }
 
         /* cumulative product of all the axis scalars */
-        else if ( ttface->blend->normalizedcoords[j] < axis->peakCoord )
-          scalar =
-            FT_MulDiv( scalar,
-                       ttface->blend->normalizedcoords[j] - axis->startCoord,
-                       axis->peakCoord - axis->startCoord );
-        else
-          scalar =
-            FT_MulDiv( scalar,
-                       axis->endCoord - ttface->blend->normalizedcoords[j],
-                       axis->endCoord - axis->peakCoord );
+        else if ( ncv < axis->peakCoord )
+          scalar = FT_MulDiv( scalar,
+                              ncv - axis->startCoord,
+                              axis->peakCoord - axis->startCoord );
+        else   /* ncv > axis->peakCoord */
+          scalar = FT_MulDiv( scalar,
+                              axis->endCoord - ncv,
+                              axis->endCoord - axis->peakCoord );
 
       } /* per-axis loop */
 
@@ -1920,32 +1896,27 @@
 
     for ( i = 0; i < blend->num_axis; i++ )
     {
-      FT_TRACE6(( "    axis %d coordinate %.5f:\n",
-                  i, (double)blend->normalizedcoords[i] / 65536 ));
+      FT_Fixed  ncv = blend->normalizedcoords[i];
+
+
+      FT_TRACE6(( "    axis %d coordinate %.5f:\n", i, (double)ncv / 65536 ));
 
       /* It's not clear why (for intermediate tuples) we don't need     */
       /* to check against start/end -- the documentation says we don't. */
       /* Similarly, it's unclear why we don't need to scale along the   */
       /* axis.                                                          */
 
-      if ( tuple_coords[i] == 0 )
+      if ( tuple_coords[i] == ncv )
       {
-        FT_TRACE6(( "      tuple coordinate is zero, ignore\n" ));
+        FT_TRACE6(( "      tuple coordinate %.5f fits perfectly\n",
+                    (double)tuple_coords[i] / 65536 ));
+        /* `apply' does not change */
         continue;
       }
 
-      if ( blend->normalizedcoords[i] == 0 )
-      {
-        FT_TRACE6(( "      axis coordinate is zero, stop\n" ));
-        apply = 0;
-        break;
-      }
-
-      if ( blend->normalizedcoords[i] == tuple_coords[i] )
+      if ( tuple_coords[i] == 0 )
       {
-        FT_TRACE6(( "      tuple coordinate %.5f fits perfectly\n",
-                    (double)tuple_coords[i] / 65536 ));
-        /* `apply' does not change */
+        FT_TRACE6(( "      tuple coordinate is zero, ignore\n" ));
         continue;
       }
 
@@ -1953,27 +1924,27 @@
       {
         /* not an intermediate tuple */
 
-        if ( blend->normalizedcoords[i] < FT_MIN( 0, tuple_coords[i] ) ||
-             blend->normalizedcoords[i] > FT_MAX( 0, tuple_coords[i] ) )
+        if ( ( tuple_coords[i] > ncv && ncv > 0 ) ||
+             ( tuple_coords[i] < ncv && ncv < 0 ) )
+        {
+          FT_TRACE6(( "      tuple coordinate %.5f fits\n",
+                      (double)tuple_coords[i] / 65536 ));
+          apply = FT_MulDiv( apply, ncv, tuple_coords[i] );
+        }
+        else
         {
           FT_TRACE6(( "      tuple coordinate %.5f is exceeded, stop\n",
                       (double)tuple_coords[i] / 65536 ));
           apply = 0;
           break;
         }
-
-        FT_TRACE6(( "      tuple coordinate %.5f fits\n",
-                    (double)tuple_coords[i] / 65536 ));
-        apply = FT_MulDiv( apply,
-                           blend->normalizedcoords[i],
-                           tuple_coords[i] );
       }
       else
       {
         /* intermediate tuple */
 
-        if ( blend->normalizedcoords[i] <= im_start_coords[i] ||
-             blend->normalizedcoords[i] >= im_end_coords[i]   )
+        if ( ncv <= im_start_coords[i] ||
+             ncv >= im_end_coords[i]   )
         {
           FT_TRACE6(( "      intermediate tuple range ]%.5f;%.5f[ is exceeded,"
                       " stop\n",
@@ -1986,13 +1957,13 @@
         FT_TRACE6(( "      intermediate tuple range ]%.5f;%.5f[ fits\n",
                     (double)im_start_coords[i] / 65536,
                     (double)im_end_coords[i] / 65536 ));
-        if ( blend->normalizedcoords[i] < tuple_coords[i] )
+        if ( ncv < tuple_coords[i] )
           apply = FT_MulDiv( apply,
-                             blend->normalizedcoords[i] - im_start_coords[i],
+                             ncv - im_start_coords[i],
                              tuple_coords[i] - im_start_coords[i] );
-        else
+        else /* ncv > tuple_coords[i] */
           apply = FT_MulDiv( apply,
-                             im_end_coords[i] - blend->normalizedcoords[i],
+                             im_end_coords[i] - ncv,
                              im_end_coords[i] - tuple_coords[i] );
       }
     }
@@ -2141,11 +2112,12 @@
                                          outerIndex,
                                          innerIndex );
 
-      v += delta << 2;
+          /* Convert delta in F2DOT14 to 16.16 before adding. */
+          v += MUL_INT( delta, 4 );
 
-      /* Clamp value range. */
-      v = v >=  0x10000L ?  0x10000 : v;
-      v = v <= -0x10000L ? -0x10000 : v;
+          /* Clamp value to range [-1, 1]. */
+          v = v >=  0x10000L ?  0x10000 : v;
+          v = v <= -0x10000L ? -0x10000 : v;
 
           new_normalized[i] = v;
         }
@@ -2721,9 +2693,8 @@
       FT_UInt  n;
 
 
-      if ( FT_ALLOC( mmvar, ttface->blend->mmvar_len ) )
+      if ( FT_DUP( mmvar, ttface->blend->mmvar, ttface->blend->mmvar_len ) )
         goto Exit;
-      FT_MEM_COPY( mmvar, ttface->blend->mmvar, ttface->blend->mmvar_len );
 
       axis_flags =
         (FT_UShort*)( (char*)mmvar + mmvar_size );
@@ -3533,9 +3504,10 @@
     FT_ULong  here;
     FT_UInt   i, j;
 
-    FT_Fixed*  tuple_coords    = NULL;
-    FT_Fixed*  im_start_coords = NULL;
-    FT_Fixed*  im_end_coords   = NULL;
+    FT_Fixed*  peak_coords = NULL;
+    FT_Fixed*  tuple_coords;
+    FT_Fixed*  im_start_coords;
+    FT_Fixed*  im_end_coords;
 
     GX_Blend  blend = face->blend;
 
@@ -3556,16 +3528,16 @@
     {
       FT_TRACE2(( "\n" ));
       FT_TRACE2(( "tt_face_vary_cvt: no blend specified\n" ));
-      error = FT_Err_Ok;
-      goto Exit;
+
+      return FT_Err_Ok;
     }
 
     if ( !face->cvt )
     {
       FT_TRACE2(( "\n" ));
       FT_TRACE2(( "tt_face_vary_cvt: no `cvt ' table\n" ));
-      error = FT_Err_Ok;
-      goto Exit;
+
+      return FT_Err_Ok;
     }
 
     error = face->goto_table( face, TTAG_cvar, stream, &table_len );
@@ -3573,15 +3545,11 @@
     {
       FT_TRACE2(( "is missing\n" ));
 
-      error = FT_Err_Ok;
-      goto Exit;
+      return FT_Err_Ok;
     }
 
     if ( FT_FRAME_ENTER( table_len ) )
-    {
-      error = FT_Err_Ok;
-      goto Exit;
-    }
+      return FT_Err_Ok;
 
     table_start = FT_Stream_FTell( stream );
     if ( FT_GET_LONG() != 0x00010000L )
@@ -3594,11 +3562,6 @@
 
     FT_TRACE2(( "loaded\n" ));
 
-    if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis )    ||
-         FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
-         FT_NEW_ARRAY( im_end_coords, blend->num_axis )   )
-      goto FExit;
-
     tupleCount   = FT_GET_USHORT();
     offsetToData = FT_GET_USHORT();
 
@@ -3621,9 +3584,8 @@
 
       FT_Stream_SeekSet( stream, offsetToData );
 
-      sharedpoints = ft_var_readpackedpoints( stream,
-                                              table_len,
-                                              &spoint_count );
+      sharedpoints = ft_var_readpackedpoints( stream, &spoint_count );
+
       offsetToData = FT_Stream_FTell( stream );
 
       FT_Stream_SeekSet( stream, here );
@@ -3634,8 +3596,12 @@
                 tupleCount & GX_TC_TUPLE_COUNT_MASK,
                 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "" : "s" ));
 
-    if ( FT_NEW_ARRAY( cvt_deltas, face->cvt_size ) )
-      goto FExit;
+    if ( FT_QNEW_ARRAY( peak_coords, 3 * blend->num_axis ) ||
+         FT_NEW_ARRAY( cvt_deltas, face->cvt_size )        )
+      goto Exit;
+
+    im_start_coords = peak_coords + blend->num_axis;
+    im_end_coords = im_start_coords + blend->num_axis;
 
     for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); i++ )
     {
@@ -3652,32 +3618,19 @@
       if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
       {
         for ( j = 0; j < blend->num_axis; j++ )
-          tuple_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() );
+          peak_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() );
+        tuple_coords = peak_coords;
       }
-      else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
+      else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) < blend->tuplecount )
+        tuple_coords = blend->tuplecoords +
+            ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) * blend->num_axis;
+      else
       {
         FT_TRACE2(( "tt_face_vary_cvt:"
                     " invalid tuple index\n" ));
 
         error = FT_THROW( Invalid_Table );
-        goto FExit;
-      }
-      else
-      {
-        if ( !blend->tuplecoords )
-        {
-          FT_TRACE2(( "tt_face_vary_cvt:"
-                      " no valid tuple coordinates available\n" ));
-
-          error = FT_THROW( Invalid_Table );
-          goto FExit;
-        }
-
-        FT_MEM_COPY(
-          tuple_coords,
-          blend->tuplecoords +
-            ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) * blend->num_axis,
-          blend->num_axis * sizeof ( FT_Fixed ) );
+        goto Exit;
       }
 
       if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
@@ -3706,9 +3659,7 @@
 
       if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
       {
-        localpoints = ft_var_readpackedpoints( stream,
-                                               table_len,
-                                               &point_count );
+        localpoints = ft_var_readpackedpoints( stream, &point_count );
         points      = localpoints;
       }
       else
@@ -3719,7 +3670,6 @@
       }
 
       deltas = ft_var_readpackeddeltas( stream,
-                                        table_len,
                                         point_count == 0 ? face->cvt_size
                                                          : point_count );
 
@@ -3820,22 +3770,20 @@
     for ( i = 0; i < face->cvt_size; i++ )
       face->cvt[i] += FT_fixedToFdot6( cvt_deltas[i] );
 
-  FExit:
-    FT_FRAME_EXIT();
+    /* Iterate over all `FT_Size` objects and set `cvt_ready` to -1 */
+    /* to trigger rescaling of all CVT values.                      */
+    FT_List_Iterate( &root->sizes_list,
+                     tt_cvt_ready_iterator,
+                     NULL );
 
   Exit:
     if ( sharedpoints != ALL_POINTS )
       FT_FREE( sharedpoints );
-    FT_FREE( tuple_coords );
-    FT_FREE( im_start_coords );
-    FT_FREE( im_end_coords );
     FT_FREE( cvt_deltas );
+    FT_FREE( peak_coords );
 
-    /* iterate over all FT_Size objects and set `cvt_ready' to -1 */
-    /* to trigger rescaling of all CVT values                     */
-    FT_List_Iterate( &root->sizes_list,
-                     tt_cvt_ready_iterator,
-                     NULL );
+  FExit:
+    FT_FRAME_EXIT();
 
     return error;
 
@@ -4099,9 +4047,10 @@
     FT_ULong  here;
     FT_UInt   i, j;
 
-    FT_Fixed*  tuple_coords    = NULL;
-    FT_Fixed*  im_start_coords = NULL;
-    FT_Fixed*  im_end_coords   = NULL;
+    FT_Fixed*  peak_coords = NULL;
+    FT_Fixed*  tuple_coords;
+    FT_Fixed*  im_start_coords;
+    FT_Fixed*  im_end_coords;
 
     GX_Blend  blend = face->blend;
 
@@ -4136,27 +4085,17 @@
       return FT_Err_Ok;
     }
 
-    if ( FT_NEW_ARRAY( points_org, n_points ) ||
-         FT_NEW_ARRAY( points_out, n_points ) ||
-         FT_NEW_ARRAY( has_delta, n_points )  )
-      goto Fail1;
-
     dataSize = blend->glyphoffsets[glyph_index + 1] -
                  blend->glyphoffsets[glyph_index];
 
     if ( FT_STREAM_SEEK( blend->glyphoffsets[glyph_index] ) ||
          FT_FRAME_ENTER( dataSize )                         )
-      goto Fail1;
+      return error;
 
     glyph_start = FT_Stream_FTell( stream );
 
     /* each set of glyph variation data is formatted similarly to `cvar' */
 
-    if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis )    ||
-         FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
-         FT_NEW_ARRAY( im_end_coords, blend->num_axis )   )
-      goto Fail2;
-
     tupleCount   = FT_GET_USHORT();
     offsetToData = FT_GET_USHORT();
 
@@ -4168,7 +4107,7 @@
                   " invalid glyph variation array header\n" ));
 
       error = FT_THROW( Invalid_Table );
-      goto Fail2;
+      goto FExit;
     }
 
     offsetToData += glyph_start;
@@ -4179,9 +4118,8 @@
 
       FT_Stream_SeekSet( stream, offsetToData );
 
-      sharedpoints = ft_var_readpackedpoints( stream,
-                                              blend->gvar_size,
-                                              &spoint_count );
+      sharedpoints = ft_var_readpackedpoints( stream, &spoint_count );
+
       offsetToData = FT_Stream_FTell( stream );
 
       FT_Stream_SeekSet( stream, here );
@@ -4192,9 +4130,16 @@
                 tupleCount & GX_TC_TUPLE_COUNT_MASK,
                 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "" : "s" ));
 
-    if ( FT_NEW_ARRAY( point_deltas_x, n_points ) ||
-         FT_NEW_ARRAY( point_deltas_y, n_points ) )
-      goto Fail3;
+    if ( FT_QNEW_ARRAY( peak_coords, 3 * blend->num_axis ) ||
+         FT_NEW_ARRAY( point_deltas_x, 2 * n_points )      ||
+         FT_QNEW_ARRAY( points_org, n_points )             ||
+         FT_QNEW_ARRAY( points_out, n_points )             ||
+         FT_QNEW_ARRAY( has_delta, n_points )              )
+      goto Exit;
+
+    im_start_coords = peak_coords + blend->num_axis;
+    im_end_coords   = im_start_coords + blend->num_axis;
+    point_deltas_y  = point_deltas_x + n_points;
 
     for ( j = 0; j < n_points; j++ )
     {
@@ -4217,22 +4162,20 @@
       if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
       {
         for ( j = 0; j < blend->num_axis; j++ )
-          tuple_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() );
+          peak_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() );
+        tuple_coords = peak_coords;
       }
-      else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
+      else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) < blend->tuplecount )
+        tuple_coords = blend->tuplecoords +
+            ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) * blend->num_axis;
+      else
       {
         FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
                     " invalid tuple index\n" ));
 
         error = FT_THROW( Invalid_Table );
-        goto Fail3;
+        goto Exit;
       }
-      else
-        FT_MEM_COPY(
-          tuple_coords,
-          blend->tuplecoords +
-            ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) * blend->num_axis,
-          blend->num_axis * sizeof ( FT_Fixed ) );
 
       if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
       {
@@ -4260,9 +4203,7 @@
 
       if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
       {
-        localpoints = ft_var_readpackedpoints( stream,
-                                               blend->gvar_size,
-                                               &point_count );
+        localpoints = ft_var_readpackedpoints( stream, &point_count );
         points      = localpoints;
       }
       else
@@ -4272,11 +4213,9 @@
       }
 
       deltas_x = ft_var_readpackeddeltas( stream,
-                                          blend->gvar_size,
                                           point_count == 0 ? n_points
                                                            : point_count );
       deltas_y = ft_var_readpackeddeltas( stream,
-                                          blend->gvar_size,
                                           point_count == 0 ? n_points
                                                            : point_count );
 
@@ -4460,23 +4399,17 @@
                                        unrounded[n_points - 2].y ) / 64;
     }
 
-  Fail3:
-    FT_FREE( point_deltas_x );
-    FT_FREE( point_deltas_y );
-
-  Fail2:
+  Exit:
     if ( sharedpoints != ALL_POINTS )
       FT_FREE( sharedpoints );
-    FT_FREE( tuple_coords );
-    FT_FREE( im_start_coords );
-    FT_FREE( im_end_coords );
-
-    FT_FRAME_EXIT();
-
-  Fail1:
     FT_FREE( points_org );
     FT_FREE( points_out );
     FT_FREE( has_delta );
+    FT_FREE( peak_coords );
+    FT_FREE( point_deltas_x );
+
+  FExit:
+    FT_FRAME_EXIT();
 
     return error;
   }
diff --git a/src/java.desktop/share/native/libfreetype/src/truetype/ttgxvar.h b/src/java.desktop/share/native/libfreetype/src/truetype/ttgxvar.h
index e3da6d1705cc7..9326011e3a243 100644
--- a/src/java.desktop/share/native/libfreetype/src/truetype/ttgxvar.h
+++ b/src/java.desktop/share/native/libfreetype/src/truetype/ttgxvar.h
@@ -4,7 +4,7 @@
  *
  *   TrueType GX Font Variation loader (specification)
  *
- * Copyright (C) 2004-2023 by
+ * Copyright (C) 2004-2024 by
  * David Turner, Robert Wilhelm, Werner Lemberg and George Williams.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/truetype/ttinterp.c b/src/java.desktop/share/native/libfreetype/src/truetype/ttinterp.c
index 79df4555d9431..951891dbf51c4 100644
--- a/src/java.desktop/share/native/libfreetype/src/truetype/ttinterp.c
+++ b/src/java.desktop/share/native/libfreetype/src/truetype/ttinterp.c
@@ -4,7 +4,7 @@
  *
  *   TrueType bytecode interpreter (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -5270,11 +5270,11 @@
     FT_UShort        refp;
     FT_F26Dot6       dx, dy;
 
-    FT_Short         contour, bounds;
+    FT_UShort        contour, bounds;
     FT_UShort        start, limit, i;
 
 
-    contour = (FT_Short)args[0];
+    contour = (FT_UShort)args[0];
     bounds  = ( exc->GS.gep2 == 0 ) ? 1 : exc->zp2.n_contours;
 
     if ( BOUNDS( contour, bounds ) )
@@ -5290,15 +5290,13 @@
     if ( contour == 0 )
       start = 0;
     else
-      start = (FT_UShort)( exc->zp2.contours[contour - 1] + 1 -
-                           exc->zp2.first_point );
+      start = exc->zp2.contours[contour - 1] + 1 - exc->zp2.first_point;
 
     /* we use the number of points if in the twilight zone */
     if ( exc->GS.gep2 == 0 )
       limit = exc->zp2.n_points;
     else
-      limit = (FT_UShort)( exc->zp2.contours[contour] -
-                           exc->zp2.first_point + 1 );
+      limit = exc->zp2.contours[contour] + 1 - exc->zp2.first_point;
 
     for ( i = start; i < limit; i++ )
     {
@@ -5341,9 +5339,9 @@
     /*      Normal zone's `n_points' includes phantoms, so must    */
     /*      use end of last contour.                               */
     if ( exc->GS.gep2 == 0 )
-      limit = (FT_UShort)exc->zp2.n_points;
+      limit = exc->zp2.n_points;
     else if ( exc->GS.gep2 == 1 && exc->zp2.n_contours > 0 )
-      limit = (FT_UShort)( exc->zp2.contours[exc->zp2.n_contours - 1] + 1 );
+      limit = exc->zp2.contours[exc->zp2.n_contours - 1] + 1;
     else
       limit = 0;
 
diff --git a/src/java.desktop/share/native/libfreetype/src/truetype/ttinterp.h b/src/java.desktop/share/native/libfreetype/src/truetype/ttinterp.h
index e98e258fe7edb..4f1a9bbc6794b 100644
--- a/src/java.desktop/share/native/libfreetype/src/truetype/ttinterp.h
+++ b/src/java.desktop/share/native/libfreetype/src/truetype/ttinterp.h
@@ -4,7 +4,7 @@
  *
  *   TrueType bytecode interpreter (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/truetype/ttobjs.c b/src/java.desktop/share/native/libfreetype/src/truetype/ttobjs.c
index 5b56af711df47..d0ac31812045f 100644
--- a/src/java.desktop/share/native/libfreetype/src/truetype/ttobjs.c
+++ b/src/java.desktop/share/native/libfreetype/src/truetype/ttobjs.c
@@ -4,7 +4,7 @@
  *
  *   Objects manager (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -115,7 +115,7 @@
   FT_LOCAL_DEF( FT_Error )
   tt_glyphzone_new( FT_Memory     memory,
                     FT_UShort     maxPoints,
-                    FT_Short      maxContours,
+                    FT_UShort     maxContours,
                     TT_GlyphZone  zone )
   {
     FT_Error  error;
@@ -152,18 +152,20 @@
   static const FT_String*
   tt_skip_pdffont_random_tag( const FT_String*  name )
   {
-    unsigned int  i;
-
-
-    if ( ft_strlen( name ) < 8 || name[6] != '+' )
-      return name;
-
-    for ( i = 0; i < 6; i++ )
-      if ( !ft_isupper( name[i] ) )
-        return name;
+    if ( ft_isupper( name[0] ) &&
+         ft_isupper( name[1] ) &&
+         ft_isupper( name[2] ) &&
+         ft_isupper( name[3] ) &&
+         ft_isupper( name[4] ) &&
+         ft_isupper( name[5] ) &&
+              '+' == name[6]   &&
+                     name[7]   )
+    {
+      FT_TRACE7(( "name without randomization tag: %s\n", name + 7 ));
+      return name + 7;
+    }
 
-    FT_TRACE7(( "name without randomization tag: %s\n", name + 7 ));
-    return name + 7;
+    return name;
   }
 
 
@@ -254,17 +256,20 @@
   {
     FT_Error   error;
     FT_UInt32  checksum = 0;
-    FT_UInt    i;
+    FT_Byte*   p;
+    FT_Int     shift;
 
 
     if ( FT_FRAME_ENTER( length ) )
       return 0;
 
+    p = (FT_Byte*)stream->cursor;
+
     for ( ; length > 3; length -= 4 )
-      checksum += (FT_UInt32)FT_GET_ULONG();
+      checksum += FT_NEXT_ULONG( p );
 
-    for ( i = 3; length > 0; length--, i-- )
-      checksum += (FT_UInt32)FT_GET_BYTE() << ( i * 8 );
+    for ( shift = 24; length > 0; length--, shift -=8 )
+      checksum += (FT_UInt32)FT_NEXT_BYTE( p ) << shift;
 
     FT_FRAME_EXIT();
 
@@ -782,8 +787,7 @@
       FT_UInt  instance_index = (FT_UInt)face_index >> 16;
 
 
-      if ( FT_HAS_MULTIPLE_MASTERS( ttface ) &&
-           instance_index > 0                )
+      if ( FT_HAS_MULTIPLE_MASTERS( ttface ) )
       {
         error = FT_Set_Named_Instance( ttface, instance_index );
         if ( error )
@@ -990,16 +994,16 @@
     FT_Error        error;
     FT_UInt         i;
 
-    /* unscaled CVT values are already stored in 26.6 format */
-    FT_Fixed  scale = size->ttmetrics.scale >> 6;
-
 
     /* Scale the cvt values to the new ppem.            */
     /* By default, we use the y ppem value for scaling. */
     FT_TRACE6(( "CVT values:\n" ));
     for ( i = 0; i < size->cvt_size; i++ )
     {
-      size->cvt[i] = FT_MulFix( face->cvt[i], scale );
+      /* Unscaled CVT values are already stored in 26.6 format.            */
+      /* Note that this scaling operation is very sensitive to rounding;   */
+      /* the integer division by 64 must be applied to the first argument. */
+      size->cvt[i] = FT_MulFix( face->cvt[i] / 64, size->ttmetrics.scale );
       FT_TRACE6(( "  %3d: %f (%f)\n",
                   i, (double)face->cvt[i] / 64, (double)size->cvt[i] / 64 ));
     }
diff --git a/src/java.desktop/share/native/libfreetype/src/truetype/ttobjs.h b/src/java.desktop/share/native/libfreetype/src/truetype/ttobjs.h
index 40eb37b4c43ea..9c36ca783620e 100644
--- a/src/java.desktop/share/native/libfreetype/src/truetype/ttobjs.h
+++ b/src/java.desktop/share/native/libfreetype/src/truetype/ttobjs.h
@@ -4,7 +4,7 @@
  *
  *   Objects manager (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -105,7 +105,7 @@ FT_BEGIN_HEADER
   FT_LOCAL( FT_Error )
   tt_glyphzone_new( FT_Memory     memory,
                     FT_UShort     maxPoints,
-                    FT_Short      maxContours,
+                    FT_UShort     maxContours,
                     TT_GlyphZone  zone );
 
 #endif /* TT_USE_BYTECODE_INTERPRETER */
diff --git a/src/java.desktop/share/native/libfreetype/src/truetype/ttpload.c b/src/java.desktop/share/native/libfreetype/src/truetype/ttpload.c
index 54a64c7b462fc..9505b5f179f26 100644
--- a/src/java.desktop/share/native/libfreetype/src/truetype/ttpload.c
+++ b/src/java.desktop/share/native/libfreetype/src/truetype/ttpload.c
@@ -4,7 +4,7 @@
  *
  *   TrueType-specific tables loader (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/truetype/ttpload.h b/src/java.desktop/share/native/libfreetype/src/truetype/ttpload.h
index ed229fa4616df..bc32b58020c33 100644
--- a/src/java.desktop/share/native/libfreetype/src/truetype/ttpload.h
+++ b/src/java.desktop/share/native/libfreetype/src/truetype/ttpload.h
@@ -4,7 +4,7 @@
  *
  *   TrueType-specific tables loader (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/type1/t1afm.c b/src/java.desktop/share/native/libfreetype/src/type1/t1afm.c
index d9b9398b013ad..a63cd4dc48af1 100644
--- a/src/java.desktop/share/native/libfreetype/src/type1/t1afm.c
+++ b/src/java.desktop/share/native/libfreetype/src/type1/t1afm.c
@@ -4,7 +4,7 @@
  *
  *   AFM support for Type 1 fonts (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/type1/t1afm.h b/src/java.desktop/share/native/libfreetype/src/type1/t1afm.h
index e0d5aa5a88277..7f5cdda191f4b 100644
--- a/src/java.desktop/share/native/libfreetype/src/type1/t1afm.h
+++ b/src/java.desktop/share/native/libfreetype/src/type1/t1afm.h
@@ -4,7 +4,7 @@
  *
  *   AFM support for Type 1 fonts (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/type1/t1driver.c b/src/java.desktop/share/native/libfreetype/src/type1/t1driver.c
index a4cdf372a9e31..8ed01914a5a58 100644
--- a/src/java.desktop/share/native/libfreetype/src/type1/t1driver.c
+++ b/src/java.desktop/share/native/libfreetype/src/type1/t1driver.c
@@ -4,7 +4,7 @@
  *
  *   Type 1 driver interface (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -312,10 +312,7 @@
       {
         retval = ft_strlen( type1->glyph_names[idx] ) + 1;
         if ( value && value_len >= retval )
-        {
           ft_memcpy( value, (void *)( type1->glyph_names[idx] ), retval );
-          ((FT_Char *)value)[retval - 1] = (FT_Char)'\0';
-        }
       }
       break;
 
@@ -344,11 +341,8 @@
       {
         retval = ft_strlen( type1->encoding.char_name[idx] ) + 1;
         if ( value && value_len >= retval )
-        {
           ft_memcpy( value, (void *)( type1->encoding.char_name[idx] ),
-                     retval - 1 );
-          ((FT_Char *)value)[retval - 1] = (FT_Char)'\0';
-        }
+                     retval );
       }
       break;
 
diff --git a/src/java.desktop/share/native/libfreetype/src/type1/t1driver.h b/src/java.desktop/share/native/libfreetype/src/type1/t1driver.h
index ee7fcf43e01db..5ff52b55b1a86 100644
--- a/src/java.desktop/share/native/libfreetype/src/type1/t1driver.h
+++ b/src/java.desktop/share/native/libfreetype/src/type1/t1driver.h
@@ -4,7 +4,7 @@
  *
  *   High-level Type 1 driver interface (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/type1/t1errors.h b/src/java.desktop/share/native/libfreetype/src/type1/t1errors.h
index 2fbd1e513f3ec..8aeb24ae188b3 100644
--- a/src/java.desktop/share/native/libfreetype/src/type1/t1errors.h
+++ b/src/java.desktop/share/native/libfreetype/src/type1/t1errors.h
@@ -4,7 +4,7 @@
  *
  *   Type 1 error codes (specification only).
  *
- * Copyright (C) 2001-2023 by
+ * Copyright (C) 2001-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/type1/t1gload.c b/src/java.desktop/share/native/libfreetype/src/type1/t1gload.c
index a32a4649d6d53..c29e682510c25 100644
--- a/src/java.desktop/share/native/libfreetype/src/type1/t1gload.c
+++ b/src/java.desktop/share/native/libfreetype/src/type1/t1gload.c
@@ -4,7 +4,7 @@
  *
  *   Type 1 Glyph Loader (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/type1/t1gload.h b/src/java.desktop/share/native/libfreetype/src/type1/t1gload.h
index c06484758a597..17a6a5941e32a 100644
--- a/src/java.desktop/share/native/libfreetype/src/type1/t1gload.h
+++ b/src/java.desktop/share/native/libfreetype/src/type1/t1gload.h
@@ -4,7 +4,7 @@
  *
  *   Type 1 Glyph Loader (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/type1/t1load.c b/src/java.desktop/share/native/libfreetype/src/type1/t1load.c
index be7cd0fd5e97e..ee7fb42a517a7 100644
--- a/src/java.desktop/share/native/libfreetype/src/type1/t1load.c
+++ b/src/java.desktop/share/native/libfreetype/src/type1/t1load.c
@@ -4,7 +4,7 @@
  *
  *   Type 1 font loader (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -751,6 +751,7 @@
         PS_DesignMap  dmap = blend->design_map + n;
 
 
+        FT_FREE( dmap->blend_points );
         FT_FREE( dmap->design_points );
         dmap->num_points = 0;
       }
@@ -1043,9 +1044,9 @@
       }
 
       /* allocate design map data */
-      if ( FT_QNEW_ARRAY( map->design_points, num_points * 2 ) )
+      if ( FT_QNEW_ARRAY( map->design_points, num_points ) ||
+           FT_QNEW_ARRAY( map->blend_points,  num_points ) )
         goto Exit;
-      map->blend_points = map->design_points + num_points;
       map->num_points   = (FT_Byte)num_points;
 
       for ( p = 0; p < num_points; p++ )
@@ -1876,9 +1877,8 @@
         }
 
         /* t1_decrypt() shouldn't write to base -- make temporary copy */
-        if ( FT_QALLOC( temp, size ) )
+        if ( FT_DUP( temp, base, size ) )
           goto Fail;
-        FT_MEM_COPY( temp, base, size );
         psaux->t1_decrypt( temp, size, 4330 );
         size -= (FT_ULong)t1face->type1.private_dict.lenIV;
         error = T1_Add_Table( table,
@@ -2090,9 +2090,8 @@
           }
 
           /* t1_decrypt() shouldn't write to base -- make temporary copy */
-          if ( FT_QALLOC( temp, size ) )
+          if ( FT_DUP( temp, base, size ) )
             goto Fail;
-          FT_MEM_COPY( temp, base, size );
           psaux->t1_decrypt( temp, size, 4330 );
           size -= (FT_ULong)t1face->type1.private_dict.lenIV;
           error = T1_Add_Table( code_table,
@@ -2284,7 +2283,7 @@
                        T1_FIELD_DICT_PRIVATE )
 #endif
 
-    { 0, T1_FIELD_LOCATION_CID_INFO, T1_FIELD_TYPE_NONE, 0, 0, 0, 0, 0, 0 }
+    T1_FIELD_ZERO
   };
 
 
@@ -2392,18 +2391,13 @@
           T1_Field  keyword = (T1_Field)t1_keywords;
 
 
-          for (;;)
+          while ( keyword->len )
           {
-            FT_Byte*  name;
+            FT_Byte*  name = (FT_Byte*)keyword->ident;
 
 
-            name = (FT_Byte*)keyword->ident;
-            if ( !name )
-              break;
-
-            if ( cur[0] == name[0]                      &&
-                 len == ft_strlen( (const char *)name ) &&
-                 ft_memcmp( cur, name, len ) == 0       )
+            if ( keyword->len == len              &&
+                 ft_memcmp( cur, name, len ) == 0 )
             {
               /* We found it -- run the parsing callback!     */
               /* We record every instance of every field      */
diff --git a/src/java.desktop/share/native/libfreetype/src/type1/t1load.h b/src/java.desktop/share/native/libfreetype/src/type1/t1load.h
index d8c9d2d8abe93..a45efa7cb7ba7 100644
--- a/src/java.desktop/share/native/libfreetype/src/type1/t1load.h
+++ b/src/java.desktop/share/native/libfreetype/src/type1/t1load.h
@@ -4,7 +4,7 @@
  *
  *   Type 1 font loader (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/type1/t1objs.c b/src/java.desktop/share/native/libfreetype/src/type1/t1objs.c
index 69e4fd5065e5f..b1b27c31fe3b6 100644
--- a/src/java.desktop/share/native/libfreetype/src/type1/t1objs.c
+++ b/src/java.desktop/share/native/libfreetype/src/type1/t1objs.c
@@ -4,7 +4,7 @@
  *
  *   Type 1 objects manager (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/type1/t1objs.h b/src/java.desktop/share/native/libfreetype/src/type1/t1objs.h
index 03847b27e96ac..3809370c1e085 100644
--- a/src/java.desktop/share/native/libfreetype/src/type1/t1objs.h
+++ b/src/java.desktop/share/native/libfreetype/src/type1/t1objs.h
@@ -4,7 +4,7 @@
  *
  *   Type 1 objects manager (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/type1/t1parse.c b/src/java.desktop/share/native/libfreetype/src/type1/t1parse.c
index 6dec6c16c3e70..3717ea7c57207 100644
--- a/src/java.desktop/share/native/libfreetype/src/type1/t1parse.c
+++ b/src/java.desktop/share/native/libfreetype/src/type1/t1parse.c
@@ -4,7 +4,7 @@
  *
  *   Type 1 parser (body).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/type1/t1parse.h b/src/java.desktop/share/native/libfreetype/src/type1/t1parse.h
index 0d9a2865df08e..a0a2134d45c02 100644
--- a/src/java.desktop/share/native/libfreetype/src/type1/t1parse.h
+++ b/src/java.desktop/share/native/libfreetype/src/type1/t1parse.h
@@ -4,7 +4,7 @@
  *
  *   Type 1 parser (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libfreetype/src/type1/t1tokens.h b/src/java.desktop/share/native/libfreetype/src/type1/t1tokens.h
index 40f3609262203..5a3d2f1ef0874 100644
--- a/src/java.desktop/share/native/libfreetype/src/type1/t1tokens.h
+++ b/src/java.desktop/share/native/libfreetype/src/type1/t1tokens.h
@@ -4,7 +4,7 @@
  *
  *   Type 1 tokenizer (specification).
  *
- * Copyright (C) 1996-2023 by
+ * Copyright (C) 1996-2024 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Color/CBDT/CBDT.hh b/src/java.desktop/share/native/libharfbuzz/OT/Color/CBDT/CBDT.hh
index d7b2b13e4d38c..b8f6fa4054b6f 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Color/CBDT/CBDT.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Color/CBDT/CBDT.hh
@@ -204,6 +204,7 @@ struct IndexSubtable
   {
     TRACE_SANITIZE (this);
     if (!u.header.sanitize (c)) return_trace (false);
+    hb_barrier ();
     switch (u.header.indexFormat)
     {
     case 1: return_trace (u.format1.sanitize (c, glyph_count));
@@ -378,6 +379,7 @@ struct IndexSubtableRecord
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
+                  hb_barrier () &&
                   firstGlyphIndex <= lastGlyphIndex &&
                   offsetToSubtable.sanitize (c, base, lastGlyphIndex - firstGlyphIndex + 1));
   }
@@ -635,6 +637,7 @@ struct BitmapSizeTable
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
+                  hb_barrier () &&
                   indexSubtableArrayOffset.sanitize (c, base, numberOfIndexSubtables) &&
                   horizontal.sanitize (c) &&
                   vertical.sanitize (c));
@@ -738,7 +741,9 @@ struct CBLC
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
+                  hb_barrier () &&
                   likely (version.major == 2 || version.major == 3) &&
+                  hb_barrier () &&
                   sizeTables.sanitize (c, this));
   }
 
@@ -936,31 +941,33 @@ struct CBDT
       }
     }
 
-    bool has_data () const { return cbdt.get_length (); }
+    bool has_data () const { return cbdt->version.major; }
 
     bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const
     {
+      if (!has_data ()) return false;
+
       hb_glyph_extents_t extents;
       hb_glyph_extents_t pixel_extents;
-      hb_blob_t *blob = reference_png (font, glyph);
-
-      if (unlikely (blob == hb_blob_get_empty ()))
+      if (unlikely (!font->get_glyph_extents (glyph, &extents, false)))
         return false;
 
-      if (unlikely (!hb_font_get_glyph_extents (font, glyph, &extents)))
+      if (unlikely (!get_extents (font, glyph, &pixel_extents, false)))
         return false;
 
-      if (unlikely (!get_extents (font, glyph, &pixel_extents, false)))
+      hb_blob_t *blob = reference_png (font, glyph);
+      if (unlikely (hb_blob_is_immutable (blob)))
         return false;
 
       bool ret = funcs->image (data,
                                blob,
                                pixel_extents.width, -pixel_extents.height,
                                HB_PAINT_IMAGE_FORMAT_PNG,
-                               font->slant_xy,
+                               0.f,
                                &extents);
 
       hb_blob_destroy (blob);
+
       return ret;
     }
 
@@ -975,6 +982,7 @@ struct CBDT
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
+                  hb_barrier () &&
                   likely (version.major == 2 || version.major == 3));
   }
 
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/COLR.hh b/src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/COLR.hh
index fb2c42a88f080..73e6b4c305ba6 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/COLR.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/COLR.hh
@@ -29,11 +29,15 @@
 #define OT_COLOR_COLR_COLR_HH
 
 #include "../../../hb.hh"
+#include "../../../hb-decycler.hh"
 #include "../../../hb-open-type.hh"
 #include "../../../hb-ot-var-common.hh"
 #include "../../../hb-paint.hh"
+#include "../../../hb-paint-bounded.hh"
 #include "../../../hb-paint-extents.hh"
 
+#include "../CPAL/CPAL.hh"
+
 /*
  * COLR -- Color
  * https://docs.microsoft.com/en-us/typography/opentype/spec/colr
@@ -44,6 +48,12 @@ namespace OT {
 struct hb_paint_context_t;
 }
 
+struct hb_colr_scratch_t
+{
+  hb_paint_bounded_context_t paint_bounded;
+  hb_paint_extents_context_t paint_extents;
+};
+
 namespace OT {
 
 struct COLR;
@@ -66,13 +76,13 @@ public:
   hb_paint_funcs_t *funcs;
   void *data;
   hb_font_t *font;
-  unsigned int palette_index;
+  hb_array_t palette;
   hb_color_t foreground;
-  VarStoreInstancer &instancer;
-  hb_map_t current_glyphs;
-  hb_map_t current_layers;
+  ItemVarStoreInstancer &instancer;
+  hb_decycler_t glyphs_decycler;
+  hb_decycler_t layers_decycler;
   int depth_left = HB_MAX_NESTING_LEVEL;
-  int edge_count = HB_COLRV1_MAX_EDGE_COUNT;
+  int edge_count = HB_MAX_GRAPH_EDGE_COUNT;
 
   hb_paint_context_t (const void *base_,
                       hb_paint_funcs_t *funcs_,
@@ -80,15 +90,34 @@ public:
                       hb_font_t *font_,
                       unsigned int palette_,
                       hb_color_t foreground_,
-                      VarStoreInstancer &instancer_) :
+                      ItemVarStoreInstancer &instancer_) :
     base (base_),
     funcs (funcs_),
     data (data_),
     font (font_),
-    palette_index (palette_),
+    palette (
+#ifndef HB_NO_COLOR
+             // https://github.com/harfbuzz/harfbuzz/issues/5116
+             font->face->table.CPAL->get_palette_colors (palette_ < font->face->table.CPAL->get_palette_count () ? palette_ : 0)
+#endif
+    ),
     foreground (foreground_),
     instancer (instancer_)
-  { }
+  {
+    if (font->is_synthetic ())
+    {
+      font = hb_font_create_sub_font (font);
+      hb_font_set_synthetic_bold (font, 0, 0, true);
+      hb_font_set_synthetic_slant (font, 0);
+    }
+    else
+      hb_font_reference (font);
+  }
+
+  ~hb_paint_context_t ()
+  {
+    hb_font_destroy (font);
+  }
 
   hb_color_t get_color (unsigned int color_index, float alpha, hb_bool_t *is_foreground)
   {
@@ -99,12 +128,7 @@ public:
     if (color_index != 0xffff)
     {
       if (!funcs->custom_palette_color (data, color_index, &color))
-      {
-        unsigned int clen = 1;
-        hb_face_t *face = hb_font_get_face (font);
-
-        hb_ot_color_palette_get_colors (face, palette_index, color_index, &clen, &color);
-      }
+        color = palette[color_index];
 
       *is_foreground = false;
     }
@@ -159,23 +183,35 @@ struct hb_colrv1_closure_context_t :
   void add_palette_index (unsigned palette_index)
   { palette_indices->add (palette_index); }
 
+  void add_var_idxes (unsigned first_var_idx, unsigned num_idxes)
+  {
+    if (!num_idxes || first_var_idx == VarIdx::NO_VARIATION) return;
+    variation_indices->add_range (first_var_idx, first_var_idx + num_idxes - 1);
+  }
+
   public:
   const void *base;
   hb_set_t visited_paint;
   hb_set_t *glyphs;
   hb_set_t *layer_indices;
   hb_set_t *palette_indices;
+  hb_set_t *variation_indices;
+  unsigned num_var_idxes;
   unsigned nesting_level_left;
 
   hb_colrv1_closure_context_t (const void *base_,
                                hb_set_t *glyphs_,
                                hb_set_t *layer_indices_,
                                hb_set_t *palette_indices_,
+                               hb_set_t *variation_indices_,
+                               unsigned num_var_idxes_ = 1,
                                unsigned nesting_level_left_ = HB_MAX_NESTING_LEVEL) :
                           base (base_),
                           glyphs (glyphs_),
                           layer_indices (layer_indices_),
                           palette_indices (palette_indices_),
+                          variation_indices (variation_indices_),
+                          num_var_idxes (num_var_idxes_),
                           nesting_level_left (nesting_level_left_)
   {}
 };
@@ -242,18 +278,33 @@ struct Variable
   }
 
   void closurev1 (hb_colrv1_closure_context_t* c) const
-  { value.closurev1 (c); }
+  {
+    c->num_var_idxes = 0;
+    // update c->num_var_idxes during value closure
+    value.closurev1 (c);
+    c->add_var_idxes (varIdxBase, c->num_var_idxes);
+  }
 
   bool subset (hb_subset_context_t *c,
-               const VarStoreInstancer &instancer) const
+               const ItemVarStoreInstancer &instancer) const
   {
     TRACE_SUBSET (this);
     if (!value.subset (c, instancer, varIdxBase)) return_trace (false);
     if (c->plan->all_axes_pinned)
       return_trace (true);
 
-    //TODO: update varIdxBase for partial-instancing
-    return_trace (c->serializer->embed (varIdxBase));
+    VarIdx new_varidx;
+    new_varidx = varIdxBase;
+    if (varIdxBase != VarIdx::NO_VARIATION)
+    {
+      hb_pair_t *new_varidx_delta;
+      if (!c->plan->colrv1_variation_idx_delta_map.has (varIdxBase, &new_varidx_delta))
+        return_trace (false);
+
+      new_varidx = hb_first (*new_varidx_delta);
+    }
+
+    return_trace (c->serializer->embed (new_varidx));
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -270,7 +321,7 @@ struct Variable
 
   void get_color_stop (hb_paint_context_t *c,
                        hb_color_stop_t *stop,
-                       const VarStoreInstancer &instancer) const
+                       const ItemVarStoreInstancer &instancer) const
   {
     value.get_color_stop (c, stop, varIdxBase, instancer);
   }
@@ -305,7 +356,7 @@ struct NoVariable
   { value.closurev1 (c); }
 
   bool subset (hb_subset_context_t *c,
-               const VarStoreInstancer &instancer) const
+               const ItemVarStoreInstancer &instancer) const
   {
     TRACE_SUBSET (this);
     return_trace (value.subset (c, instancer, varIdxBase));
@@ -325,7 +376,7 @@ struct NoVariable
 
   void get_color_stop (hb_paint_context_t *c,
                        hb_color_stop_t *stop,
-                       const VarStoreInstancer &instancer) const
+                       const ItemVarStoreInstancer &instancer) const
   {
     value.get_color_stop (c, stop, VarIdx::NO_VARIATION, instancer);
   }
@@ -345,10 +396,13 @@ struct NoVariable
 struct ColorStop
 {
   void closurev1 (hb_colrv1_closure_context_t* c) const
-  { c->add_palette_index (paletteIndex); }
+  {
+    c->add_palette_index (paletteIndex);
+    c->num_var_idxes = 2;
+  }
 
   bool subset (hb_subset_context_t *c,
-               const VarStoreInstancer &instancer,
+               const ItemVarStoreInstancer &instancer,
                uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
@@ -374,7 +428,7 @@ struct ColorStop
   void get_color_stop (hb_paint_context_t *c,
                        hb_color_stop_t *out,
                        uint32_t varIdx,
-                       const VarStoreInstancer &instancer) const
+                       const ItemVarStoreInstancer &instancer) const
   {
     out->offset = stopOffset.to_float(instancer (varIdx, 0));
     out->color = c->get_color (paletteIndex,
@@ -410,7 +464,7 @@ struct ColorLine
   }
 
   bool subset (hb_subset_context_t *c,
-               const VarStoreInstancer &instancer) const
+               const ItemVarStoreInstancer &instancer) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->start_embed (this);
@@ -439,7 +493,7 @@ struct ColorLine
                    unsigned int start,
                    unsigned int *count,
                    hb_color_stop_t *color_stops,
-                   const VarStoreInstancer &instancer) const
+                   const ItemVarStoreInstancer &instancer) const
   {
     unsigned int len = stops.len;
 
@@ -542,8 +596,11 @@ struct Affine2x3
     return_trace (c->check_struct (this));
   }
 
+  void closurev1 (hb_colrv1_closure_context_t* c) const
+  { c->num_var_idxes = 6; }
+
   bool subset (hb_subset_context_t *c,
-               const VarStoreInstancer &instancer,
+               const ItemVarStoreInstancer &instancer,
                uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
@@ -588,7 +645,7 @@ struct PaintColrLayers
   void closurev1 (hb_colrv1_closure_context_t* c) const;
 
   bool subset (hb_subset_context_t *c,
-               const VarStoreInstancer &instancer HB_UNUSED) const
+               const ItemVarStoreInstancer &instancer HB_UNUSED) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (this);
@@ -617,10 +674,13 @@ struct PaintColrLayers
 struct PaintSolid
 {
   void closurev1 (hb_colrv1_closure_context_t* c) const
-  { c->add_palette_index (paletteIndex); }
+  {
+    c->add_palette_index (paletteIndex);
+    c->num_var_idxes = 1;
+  }
 
   bool subset (hb_subset_context_t *c,
-               const VarStoreInstancer &instancer,
+               const ItemVarStoreInstancer &instancer,
                uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
@@ -666,10 +726,13 @@ template  class Var>
 struct PaintLinearGradient
 {
   void closurev1 (hb_colrv1_closure_context_t* c) const
-  { (this+colorLine).closurev1 (c); }
+  {
+    (this+colorLine).closurev1 (c);
+    c->num_var_idxes = 6;
+  }
 
   bool subset (hb_subset_context_t *c,
-               const VarStoreInstancer &instancer,
+               const ItemVarStoreInstancer &instancer,
                uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
@@ -733,10 +796,13 @@ template  class Var>
 struct PaintRadialGradient
 {
   void closurev1 (hb_colrv1_closure_context_t* c) const
-  { (this+colorLine).closurev1 (c); }
+  {
+    (this+colorLine).closurev1 (c);
+    c->num_var_idxes = 6;
+  }
 
   bool subset (hb_subset_context_t *c,
-               const VarStoreInstancer &instancer,
+               const ItemVarStoreInstancer &instancer,
                uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
@@ -800,10 +866,13 @@ template  class Var>
 struct PaintSweepGradient
 {
   void closurev1 (hb_colrv1_closure_context_t* c) const
-  { (this+colorLine).closurev1 (c); }
+  {
+    (this+colorLine).closurev1 (c);
+    c->num_var_idxes = 4;
+  }
 
   bool subset (hb_subset_context_t *c,
-               const VarStoreInstancer &instancer,
+               const ItemVarStoreInstancer &instancer,
                uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
@@ -863,7 +932,7 @@ struct PaintGlyph
   void closurev1 (hb_colrv1_closure_context_t* c) const;
 
   bool subset (hb_subset_context_t *c,
-               const VarStoreInstancer &instancer) const
+               const ItemVarStoreInstancer &instancer) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (this);
@@ -885,9 +954,9 @@ struct PaintGlyph
   void paint_glyph (hb_paint_context_t *c) const
   {
     TRACE_PAINT (this);
-    c->funcs->push_inverse_root_transform (c->data, c->font);
+    c->funcs->push_inverse_font_transform (c->data, c->font);
     c->funcs->push_clip_glyph (c->data, gid, c->font);
-    c->funcs->push_root_transform (c->data, c->font);
+    c->funcs->push_font_transform (c->data, c->font);
     c->recurse (this+paint);
     c->funcs->pop_transform (c->data);
     c->funcs->pop_clip (c->data);
@@ -906,7 +975,7 @@ struct PaintColrGlyph
   void closurev1 (hb_colrv1_closure_context_t* c) const;
 
   bool subset (hb_subset_context_t *c,
-               const VarStoreInstancer &instancer HB_UNUSED) const
+               const ItemVarStoreInstancer &instancer HB_UNUSED) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (this);
@@ -936,7 +1005,7 @@ struct PaintTransform
   HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
 
   bool subset (hb_subset_context_t *c,
-               const VarStoreInstancer &instancer) const
+               const ItemVarStoreInstancer &instancer) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (this);
@@ -958,7 +1027,7 @@ struct PaintTransform
   void paint_glyph (hb_paint_context_t *c) const
   {
     TRACE_PAINT (this);
-    (this+transform).paint_glyph (c);
+    (this+transform).paint_glyph (c); // This does a push_transform()
     c->recurse (this+src);
     c->funcs->pop_transform (c->data);
   }
@@ -975,7 +1044,7 @@ struct PaintTranslate
   HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
 
   bool subset (hb_subset_context_t *c,
-               const VarStoreInstancer &instancer,
+               const ItemVarStoreInstancer &instancer,
                uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
@@ -1024,7 +1093,7 @@ struct PaintScale
   HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
 
   bool subset (hb_subset_context_t *c,
-               const VarStoreInstancer &instancer,
+               const ItemVarStoreInstancer &instancer,
                uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
@@ -1073,7 +1142,7 @@ struct PaintScaleAroundCenter
   HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
 
   bool subset (hb_subset_context_t *c,
-               const VarStoreInstancer &instancer,
+               const ItemVarStoreInstancer &instancer,
                uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
@@ -1132,7 +1201,7 @@ struct PaintScaleUniform
   HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
 
   bool subset (hb_subset_context_t *c,
-               const VarStoreInstancer &instancer,
+               const ItemVarStoreInstancer &instancer,
                uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
@@ -1176,7 +1245,7 @@ struct PaintScaleUniformAroundCenter
   HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
 
   bool subset (hb_subset_context_t *c,
-               const VarStoreInstancer &instancer,
+               const ItemVarStoreInstancer &instancer,
                uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
@@ -1232,7 +1301,7 @@ struct PaintRotate
   HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
 
   bool subset (hb_subset_context_t *c,
-               const VarStoreInstancer &instancer,
+               const ItemVarStoreInstancer &instancer,
                uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
@@ -1276,7 +1345,7 @@ struct PaintRotateAroundCenter
   HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
 
   bool subset (hb_subset_context_t *c,
-               const VarStoreInstancer &instancer,
+               const ItemVarStoreInstancer &instancer,
                uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
@@ -1332,7 +1401,7 @@ struct PaintSkew
   HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
 
   bool subset (hb_subset_context_t *c,
-               const VarStoreInstancer &instancer,
+               const ItemVarStoreInstancer &instancer,
                uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
@@ -1381,7 +1450,7 @@ struct PaintSkewAroundCenter
   HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
 
   bool subset (hb_subset_context_t *c,
-               const VarStoreInstancer &instancer,
+               const ItemVarStoreInstancer &instancer,
                uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
@@ -1440,7 +1509,7 @@ struct PaintComposite
   void closurev1 (hb_colrv1_closure_context_t* c) const;
 
   bool subset (hb_subset_context_t *c,
-               const VarStoreInstancer &instancer) const
+               const ItemVarStoreInstancer &instancer) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (this);
@@ -1464,10 +1533,12 @@ struct PaintComposite
   void paint_glyph (hb_paint_context_t *c) const
   {
     TRACE_PAINT (this);
+    c->funcs->push_group (c->data);
     c->recurse (this+backdrop);
     c->funcs->push_group (c->data);
     c->recurse (this+src);
     c->funcs->pop_group (c->data, (hb_paint_composite_mode_t) (int) mode);
+    c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER);
   }
 
   HBUINT8               format; /* format = 32 */
@@ -1491,7 +1562,7 @@ struct ClipBoxFormat1
     return_trace (c->check_struct (this));
   }
 
-  void get_clip_box (ClipBoxData &clip_box, const VarStoreInstancer &instancer HB_UNUSED) const
+  void get_clip_box (ClipBoxData &clip_box, const ItemVarStoreInstancer &instancer HB_UNUSED) const
   {
     clip_box.xMin = xMin;
     clip_box.yMin = yMin;
@@ -1500,7 +1571,7 @@ struct ClipBoxFormat1
   }
 
   bool subset (hb_subset_context_t *c,
-               const VarStoreInstancer &instancer,
+               const ItemVarStoreInstancer &instancer,
                uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
@@ -1533,7 +1604,7 @@ struct ClipBoxFormat1
 
 struct ClipBoxFormat2 : Variable
 {
-  void get_clip_box (ClipBoxData &clip_box, const VarStoreInstancer &instancer) const
+  void get_clip_box (ClipBoxData &clip_box, const ItemVarStoreInstancer &instancer) const
   {
     value.get_clip_box(clip_box, instancer);
     if (instancer)
@@ -1544,12 +1615,15 @@ struct ClipBoxFormat2 : Variable
       clip_box.yMax += roundf (instancer (varIdxBase, 3));
     }
   }
+
+  void closurev1 (hb_colrv1_closure_context_t* c) const
+  { c->variation_indices->add_range (varIdxBase, varIdxBase + 3); }
 };
 
 struct ClipBox
 {
   bool subset (hb_subset_context_t *c,
-               const VarStoreInstancer &instancer) const
+               const ItemVarStoreInstancer &instancer) const
   {
     TRACE_SUBSET (this);
     switch (u.format) {
@@ -1559,6 +1633,14 @@ struct ClipBox
     }
   }
 
+  void closurev1 (hb_colrv1_closure_context_t* c) const
+  {
+    switch (u.format) {
+    case 2: u.format2.closurev1 (c); return;
+    default:return;
+    }
+  }
+
   template 
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   {
@@ -1572,7 +1654,7 @@ struct ClipBox
   }
 
   bool get_extents (hb_glyph_extents_t *extents,
-                    const VarStoreInstancer &instancer) const
+                    const ItemVarStoreInstancer &instancer) const
   {
     ClipBoxData clip_box;
     switch (u.format) {
@@ -1606,9 +1688,15 @@ struct ClipRecord
   int cmp (hb_codepoint_t g) const
   { return g < startGlyphID ? -1 : g <= endGlyphID ? 0 : +1; }
 
+  void closurev1 (hb_colrv1_closure_context_t* c, const void *base) const
+  {
+    if (!c->glyphs->intersects (startGlyphID, endGlyphID)) return;
+    (base+clipBox).closurev1 (c);
+  }
+
   bool subset (hb_subset_context_t *c,
                const void *base,
-               const VarStoreInstancer &instancer) const
+               const ItemVarStoreInstancer &instancer) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (*this);
@@ -1625,7 +1713,7 @@ struct ClipRecord
 
   bool get_extents (hb_glyph_extents_t *extents,
                     const void *base,
-                    const VarStoreInstancer &instancer) const
+                    const ItemVarStoreInstancer &instancer) const
   {
     return (base+clipBox).get_extents (extents, instancer);
   }
@@ -1642,7 +1730,7 @@ DECLARE_NULL_NAMESPACE_BYTES (OT, ClipRecord);
 struct ClipList
 {
   unsigned serialize_clip_records (hb_subset_context_t *c,
-                                   const VarStoreInstancer &instancer,
+                                   const ItemVarStoreInstancer &instancer,
                                    const hb_set_t& gids,
                                    const hb_map_t& gid_offset_map) const
   {
@@ -1695,7 +1783,7 @@ struct ClipList
   }
 
   bool subset (hb_subset_context_t *c,
-               const VarStoreInstancer &instancer) const
+               const ItemVarStoreInstancer &instancer) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->start_embed (*this);
@@ -1735,7 +1823,7 @@ struct ClipList
   bool
   get_extents (hb_codepoint_t gid,
                hb_glyph_extents_t *extents,
-               const VarStoreInstancer &instancer) const
+               const ItemVarStoreInstancer &instancer) const
   {
     auto *rec = clips.as_array ().bsearch (gid);
     if (rec)
@@ -1855,7 +1943,7 @@ struct BaseGlyphPaintRecord
 
   bool serialize (hb_serialize_context_t *s, const hb_map_t* glyph_map,
                   const void* src_base, hb_subset_context_t *c,
-                  const VarStoreInstancer &instancer) const
+                  const ItemVarStoreInstancer &instancer) const
   {
     TRACE_SERIALIZE (this);
     auto *out = s->embed (this);
@@ -1884,7 +1972,7 @@ struct BaseGlyphPaintRecord
 struct BaseGlyphList : SortedArray32Of
 {
   bool subset (hb_subset_context_t *c,
-               const VarStoreInstancer &instancer) const
+               const ItemVarStoreInstancer &instancer) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->start_embed (this);
@@ -1916,7 +2004,7 @@ struct LayerList : Array32OfOffset32To
   { return this+(*this)[i]; }
 
   bool subset (hb_subset_context_t *c,
-               const VarStoreInstancer &instancer) const
+               const ItemVarStoreInstancer &instancer) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->start_embed (this);
@@ -1941,17 +2029,90 @@ struct LayerList : Array32OfOffset32To
   }
 };
 
+struct delta_set_index_map_subset_plan_t
+{
+  unsigned get_inner_bit_count () const { return inner_bit_count; }
+  unsigned get_width ()           const { return ((outer_bit_count + inner_bit_count + 7) / 8); }
+  hb_array_t get_output_map () const { return output_map.as_array (); }
+
+  delta_set_index_map_subset_plan_t (const hb_map_t &new_deltaset_idx_varidx_map)
+  {
+    map_count = 0;
+    outer_bit_count = 0;
+    inner_bit_count = 1;
+    output_map.init ();
+
+    /* search backwards */
+    unsigned count = new_deltaset_idx_varidx_map.get_population ();
+    if (!count) return;
+
+    unsigned last_idx = (unsigned)-1;
+    unsigned last_varidx = (unsigned)-1;
+
+    for (unsigned i = count; i; i--)
+    {
+      unsigned delta_set_idx = i - 1;
+      unsigned var_idx = new_deltaset_idx_varidx_map.get (delta_set_idx);
+      if (i == count)
+      {
+        last_idx = delta_set_idx;
+        last_varidx = var_idx;
+        continue;
+      }
+      if (var_idx != last_varidx)
+        break;
+      last_idx = delta_set_idx;
+    }
+
+    map_count = last_idx + 1;
+  }
+
+  bool remap (const hb_map_t &new_deltaset_idx_varidx_map)
+  {
+    /* recalculate bit_count */
+    outer_bit_count = 1;
+    inner_bit_count = 1;
+
+    if (unlikely (!output_map.resize (map_count, false))) return false;
+
+    for (unsigned idx = 0; idx < map_count; idx++)
+    {
+      uint32_t *var_idx;
+      if (!new_deltaset_idx_varidx_map.has (idx, &var_idx)) return false;
+      output_map.arrayZ[idx] = *var_idx;
+
+      unsigned outer = (*var_idx) >> 16;
+      unsigned bit_count = (outer == 0) ? 1 : hb_bit_storage (outer);
+      outer_bit_count = hb_max (bit_count, outer_bit_count);
+
+      unsigned inner = (*var_idx) & 0xFFFF;
+      bit_count = (inner == 0) ? 1 : hb_bit_storage (inner);
+      inner_bit_count = hb_max (bit_count, inner_bit_count);
+    }
+    return true;
+  }
+
+  private:
+  unsigned map_count;
+  unsigned outer_bit_count;
+  unsigned inner_bit_count;
+  hb_vector_t output_map;
+};
+
 struct COLR
 {
   static constexpr hb_tag_t tableTag = HB_OT_TAG_COLR;
 
+  bool has_data () const { return has_v0_data () || version; }
+
   bool has_v0_data () const { return numBaseGlyphs; }
   bool has_v1_data () const
   {
-    if (version == 1)
-      return (this+baseGlyphList).len > 0;
+    if (version < 1)
+      return false;
+    hb_barrier ();
 
-    return false;
+    return (this+baseGlyphList).len > 0;
   }
 
   unsigned int get_glyph_layers (hb_codepoint_t       glyph,
@@ -1977,7 +2138,53 @@ struct COLR
   {
     accelerator_t (hb_face_t *face)
     { colr = hb_sanitize_context_t ().reference_table (face); }
-    ~accelerator_t () { this->colr.destroy (); }
+
+    ~accelerator_t ()
+    {
+      auto *scratch = cached_scratch.get_relaxed ();
+      if (scratch)
+      {
+        scratch->~hb_colr_scratch_t ();
+        hb_free (scratch);
+      }
+
+      colr.destroy ();
+    }
+
+
+    bool has_data () const { return colr->has_data (); }
+
+#ifndef HB_NO_PAINT
+    bool
+    get_extents (hb_font_t *font,
+                 hb_codepoint_t glyph,
+                 hb_glyph_extents_t *extents) const
+    {
+      if (unlikely (!has_data ())) return false;
+
+      hb_colr_scratch_t *scratch = acquire_scratch ();
+      if (unlikely (!scratch)) return true;
+      bool ret = colr->get_extents (font, glyph, extents, *scratch);
+      release_scratch (scratch);
+      return ret;
+    }
+
+    bool paint_glyph (hb_font_t *font,
+                      hb_codepoint_t glyph,
+                      hb_paint_funcs_t *funcs, void *data,
+                      unsigned int palette_index,
+                      hb_color_t foreground,
+                      bool clip = true) const
+    {
+      if (unlikely (!has_data ())) return false;
+
+      hb_colr_scratch_t *scratch = acquire_scratch ();
+      if (unlikely (!scratch)) return true;
+      bool ret = colr->paint_glyph (font, glyph, funcs, data, palette_index, foreground, clip, *scratch);
+      release_scratch (scratch);
+      return ret;
+    }
+#endif
 
     bool is_valid () { return colr.get_blob ()->length; }
 
@@ -1991,11 +2198,55 @@ struct COLR
 
     void closure_forV1 (hb_set_t *glyphset,
                         hb_set_t *layer_indices,
-                        hb_set_t *palette_indices) const
-    { colr->closure_forV1 (glyphset, layer_indices, palette_indices); }
+                        hb_set_t *palette_indices,
+                        hb_set_t *variation_indices,
+                        hb_set_t *delta_set_indices) const
+    { colr->closure_forV1 (glyphset, layer_indices, palette_indices, variation_indices, delta_set_indices); }
+
+    bool has_var_store () const
+    { return colr->has_var_store (); }
+
+    const ItemVariationStore &get_var_store () const
+    { return colr->get_var_store (); }
+    const ItemVariationStore *get_var_store_ptr () const
+    { return colr->get_var_store_ptr (); }
+
+    bool has_delta_set_index_map () const
+    { return colr->has_delta_set_index_map (); }
+
+    const DeltaSetIndexMap &get_delta_set_index_map () const
+    { return colr->get_delta_set_index_map (); }
+    const DeltaSetIndexMap *get_delta_set_index_map_ptr () const
+    { return colr->get_delta_set_index_map_ptr (); }
 
     private:
+
+    hb_colr_scratch_t *acquire_scratch () const
+    {
+      hb_colr_scratch_t *scratch = cached_scratch.get_acquire ();
+
+      if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr)))
+      {
+        scratch = (hb_colr_scratch_t *) hb_calloc (1, sizeof (hb_colr_scratch_t));
+        if (unlikely (!scratch))
+          return nullptr;
+      }
+
+      return scratch;
+    }
+    void release_scratch (hb_colr_scratch_t *scratch) const
+    {
+      if (!cached_scratch.cmpexch (nullptr, scratch))
+      {
+        scratch->~hb_colr_scratch_t ();
+        hb_free (scratch);
+      }
+    }
+
+    public:
     hb_blob_ptr_t colr;
+    private:
+    mutable hb_atomic_t cached_scratch;
   };
 
   void closure_glyphs (hb_codepoint_t glyph,
@@ -2029,12 +2280,16 @@ struct COLR
 
   void closure_forV1 (hb_set_t *glyphset,
                       hb_set_t *layer_indices,
-                      hb_set_t *palette_indices) const
+                      hb_set_t *palette_indices,
+                      hb_set_t *variation_indices,
+                      hb_set_t *delta_set_indices) const
   {
-    if (version != 1) return;
+    if (version < 1) return;
+    hb_barrier ();
+
     hb_set_t visited_glyphs;
 
-    hb_colrv1_closure_context_t c (this, &visited_glyphs, layer_indices, palette_indices);
+    hb_colrv1_closure_context_t c (this, &visited_glyphs, layer_indices, palette_indices, variation_indices);
     const BaseGlyphList &baseglyph_paintrecords = this+baseGlyphList;
 
     for (const BaseGlyphPaintRecord &baseglyph_paintrecord: baseglyph_paintrecords.iter ())
@@ -2046,6 +2301,22 @@ struct COLR
       paint.dispatch (&c);
     }
     hb_set_union (glyphset, &visited_glyphs);
+
+    const ClipList &cliplist = this+clipList;
+    c.glyphs = glyphset;
+    for (const ClipRecord &clip_record : cliplist.clips.iter())
+      clip_record.closurev1 (&c, &cliplist);
+
+    // if a DeltaSetIndexMap is included, collected variation indices are
+    // actually delta set indices, we need to map them into variation indices
+    if (has_delta_set_index_map ())
+    {
+      const DeltaSetIndexMap &var_idx_map = this+varIdxMap;
+      delta_set_indices->set (*variation_indices);
+      variation_indices->clear ();
+      for (unsigned delta_set_idx : *delta_set_indices)
+        variation_indices->add (var_idx_map.map (delta_set_idx));
+    }
   }
 
   const LayerList& get_layerList () const
@@ -2054,14 +2325,37 @@ struct COLR
   const BaseGlyphList& get_baseglyphList () const
   { return (this+baseGlyphList); }
 
+  bool has_var_store () const
+  { return version >= 1 && hb_barrier () && varStore != 0; }
+
+  bool has_delta_set_index_map () const
+  { return version >= 1 && hb_barrier () && varIdxMap != 0; }
+
+  bool has_clip_list () const
+  { return version >= 1 && hb_barrier () && clipList != 0; }
+
+  const DeltaSetIndexMap &get_delta_set_index_map () const
+  { return has_delta_set_index_map () && hb_barrier () ? this+varIdxMap : Null (DeltaSetIndexMap); }
+  const DeltaSetIndexMap *get_delta_set_index_map_ptr () const
+  { return has_delta_set_index_map () && hb_barrier () ? &(this+varIdxMap) : nullptr; }
+
+  const ItemVariationStore &get_var_store () const
+  { return has_var_store () && hb_barrier () ? this+varStore : Null (ItemVariationStore); }
+  const ItemVariationStore *get_var_store_ptr () const
+  { return has_var_store () && hb_barrier () ? &(this+varStore) : nullptr; }
+
+  const ClipList &get_clip_list () const
+  { return has_clip_list () && hb_barrier () ? this+clipList : Null (ClipList); }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
+                  hb_barrier () &&
                   (this+baseGlyphsZ).sanitize (c, numBaseGlyphs) &&
                   (this+layersZ).sanitize (c, numLayers) &&
                   (version == 0 ||
-                   (version == 1 &&
+                   (hb_barrier () &&
                     baseGlyphList.sanitize (c, this) &&
                     layerList.sanitize (c, this) &&
                     clipList.sanitize (c, this) &&
@@ -2127,6 +2421,94 @@ struct COLR
     return record;
   }
 
+  bool downgrade_to_V0 (const hb_set_t &glyphset) const
+  {
+    //no more COLRv1 glyphs, downgrade to version 0
+    for (const BaseGlyphPaintRecord& _ : get_baseglyphList ())
+      if (glyphset.has (_.glyphId))
+        return false;
+
+    return true;
+  }
+
+  bool subset_varstore (hb_subset_context_t *c,
+                        COLR* out /* OUT */) const
+  {
+    TRACE_SUBSET (this);
+    if (!varStore || c->plan->all_axes_pinned ||
+        !c->plan->colrv1_variation_idx_delta_map)
+      return_trace (true);
+
+    const ItemVariationStore& var_store = this+varStore;
+    if (c->plan->normalized_coords)
+    {
+      item_variations_t item_vars;
+      /* turn off varstore optimization when varIdxMap is null, so we maintain
+       * original var_idx sequence */
+      bool optimize = (varIdxMap != 0) ? true : false;
+      if (!item_vars.instantiate (var_store, c->plan,
+                                  optimize, /* optimization */
+                                  optimize, /* use_no_variation_idx = false */
+                                  c->plan->colrv1_varstore_inner_maps.as_array ()))
+        return_trace (false);
+
+      /* do not serialize varStore if there's no variation data after
+       * instancing: region_list or var_data is empty */
+      if (item_vars.get_region_list () &&
+          item_vars.get_vardata_encodings () &&
+          !out->varStore.serialize_serialize (c->serializer,
+                                              item_vars.has_long_word (),
+                                              c->plan->axis_tags,
+                                              item_vars.get_region_list (),
+                                              item_vars.get_vardata_encodings ()))
+        return_trace (false);
+
+      /* if varstore is optimized, update colrv1_new_deltaset_idx_varidx_map in
+       * subset plan.
+       * If varstore is empty after instancing, varidx_map would be empty and
+       * all var_idxes will be updated to VarIdx::NO_VARIATION */
+      if (optimize)
+      {
+        const hb_map_t &varidx_map = item_vars.get_varidx_map ();
+        for (auto _ : c->plan->colrv1_new_deltaset_idx_varidx_map.iter_ref ())
+        {
+          uint32_t varidx = _.second;
+          uint32_t *new_varidx;
+          if (varidx_map.has (varidx, &new_varidx))
+            _.second = *new_varidx;
+          else
+            _.second = VarIdx::NO_VARIATION;
+        }
+      }
+    }
+    else
+    {
+      if (unlikely (!out->varStore.serialize_serialize (c->serializer,
+                                                        &var_store,
+                                                        c->plan->colrv1_varstore_inner_maps.as_array ())))
+        return_trace (false);
+    }
+
+    return_trace (true);
+  }
+
+  bool subset_delta_set_index_map (hb_subset_context_t *c,
+                                   COLR* out /* OUT */) const
+  {
+    TRACE_SUBSET (this);
+    if (!varIdxMap || c->plan->all_axes_pinned ||
+        !c->plan->colrv1_new_deltaset_idx_varidx_map)
+      return_trace (true);
+
+    const hb_map_t &deltaset_idx_varidx_map = c->plan->colrv1_new_deltaset_idx_varidx_map;
+    delta_set_index_map_subset_plan_t index_map_plan (deltaset_idx_varidx_map);
+
+    if (unlikely (!index_map_plan.remap (deltaset_idx_varidx_map)))
+      return_trace (false);
+
+    return_trace (out->varIdxMap.serialize_serialize (c->serializer, index_map_plan));
+  }
+
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
@@ -2195,34 +2577,30 @@ struct COLR
     auto *colr_prime = c->serializer->start_embed ();
     if (unlikely (!c->serializer->extend_min (colr_prime)))  return_trace (false);
 
-    if (version == 0)
-    return_trace (colr_prime->serialize_V0 (c->serializer, version, base_it, layer_it));
+    if (version == 0 || downgrade_to_V0 (glyphset))
+      return_trace (colr_prime->serialize_V0 (c->serializer, 0, base_it, layer_it));
+
+    hb_barrier ();
 
-    auto snap = c->serializer->snapshot ();
+    //start version 1
     if (!c->serializer->allocate_size (5 * HBUINT32::static_size)) return_trace (false);
+    if (!colr_prime->serialize_V0 (c->serializer, version, base_it, layer_it)) return_trace (false);
 
-    VarStoreInstancer instancer (varStore ? &(this+varStore) : nullptr,
-                                 varIdxMap ? &(this+varIdxMap) : nullptr,
-                                 c->plan->normalized_coords.as_array ());
+    /* subset ItemVariationStore first, cause varidx_map needs to be updated
+     * after instancing */
+    if (!subset_varstore (c, colr_prime)) return_trace (false);
 
-    if (!colr_prime->baseGlyphList.serialize_subset (c, baseGlyphList, this, instancer))
-    {
-      if (c->serializer->in_error ()) return_trace (false);
-      //no more COLRv1 glyphs: downgrade to version 0
-      c->serializer->revert (snap);
-      return_trace (colr_prime->serialize_V0 (c->serializer, 0, base_it, layer_it));
-    }
+    ItemVarStoreInstancer instancer (get_var_store_ptr (),
+                                     get_delta_set_index_map_ptr (),
+                                     c->plan->normalized_coords.as_array ());
 
-    if (!colr_prime->serialize_V0 (c->serializer, version, base_it, layer_it)) return_trace (false);
+    if (!colr_prime->baseGlyphList.serialize_subset (c, baseGlyphList, this, instancer))
+      return_trace (false);
 
     colr_prime->layerList.serialize_subset (c, layerList, this, instancer);
     colr_prime->clipList.serialize_subset (c, clipList, this, instancer);
-    if (!varStore || c->plan->all_axes_pinned)
-      return_trace (true);
 
-    colr_prime->varIdxMap.serialize_copy (c->serializer, varIdxMap, this);
-    colr_prime->varStore.serialize_copy (c->serializer, varStore, this);
-    return_trace (true);
+    return_trace (subset_delta_set_index_map (c, colr_prime));
   }
 
   const Paint *get_base_glyph_paint (hb_codepoint_t glyph) const
@@ -2240,14 +2618,15 @@ struct COLR
 
 #ifndef HB_NO_PAINT
   bool
-  get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
+  get_extents (hb_font_t *font,
+               hb_codepoint_t glyph,
+               hb_glyph_extents_t *extents,
+               hb_colr_scratch_t &scratch) const
   {
-    if (version != 1)
-      return false;
 
-    VarStoreInstancer instancer (&(this+varStore),
-                                 &(this+varIdxMap),
-                                 hb_array (font->coords, font->num_coords));
+    ItemVarStoreInstancer instancer (get_var_store_ptr (),
+                                     get_delta_set_index_map_ptr (),
+                                     hb_array (font->coords, font->num_coords));
 
     if (get_clip (glyph, extents, instancer))
     {
@@ -2256,10 +2635,10 @@ struct COLR
     }
 
     auto *extents_funcs = hb_paint_extents_get_funcs ();
-    hb_paint_extents_context_t extents_data;
-    bool ret = paint_glyph (font, glyph, extents_funcs, &extents_data, 0, HB_COLOR(0,0,0,0));
+    scratch.paint_extents.clear ();
+    bool ret = paint_glyph (font, glyph, extents_funcs, &scratch.paint_extents, 0, HB_COLOR(0,0,0,0), true, scratch);
 
-    hb_extents_t e = extents_data.get_extents ();
+    auto e = scratch.paint_extents.get_extents ();
     if (e.is_void ())
     {
       extents->x_bearing = 0;
@@ -2282,8 +2661,10 @@ struct COLR
   bool
   has_paint_for_glyph (hb_codepoint_t glyph) const
   {
-    if (version == 1)
+    if (version >= 1)
     {
+      hb_barrier ();
+
       const Paint *paint = get_base_glyph_paint (glyph);
 
       return paint != nullptr;
@@ -2294,34 +2675,39 @@ struct COLR
 
   bool get_clip (hb_codepoint_t glyph,
                  hb_glyph_extents_t *extents,
-                 const VarStoreInstancer instancer) const
+                 const ItemVarStoreInstancer instancer) const
   {
-    return (this+clipList).get_extents (glyph,
+    return get_clip_list ().get_extents (glyph,
                                         extents,
                                         instancer);
   }
 
 #ifndef HB_NO_PAINT
   bool
-  paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, unsigned int palette_index, hb_color_t foreground, bool clip = true) const
-  {
-    VarStoreInstancer instancer (&(this+varStore),
-                                 &(this+varIdxMap),
-                                 hb_array (font->coords, font->num_coords));
+  paint_glyph (hb_font_t *font,
+               hb_codepoint_t glyph,
+               hb_paint_funcs_t *funcs, void *data,
+               unsigned int palette_index, hb_color_t foreground,
+               bool clip,
+               hb_colr_scratch_t &scratch) const
+  {
+    ItemVarStoreInstancer instancer (get_var_store_ptr (),
+                                     get_delta_set_index_map_ptr (),
+                                     hb_array (font->coords, font->num_coords));
     hb_paint_context_t c (this, funcs, data, font, palette_index, foreground, instancer);
-    c.current_glyphs.add (glyph);
 
-    if (version == 1)
+    hb_decycler_node_t node (c.glyphs_decycler);
+    node.visit (glyph);
+
+    if (version >= 1)
     {
+      hb_barrier ();
+
       const Paint *paint = get_base_glyph_paint (glyph);
       if (paint)
       {
         // COLRv1 glyph
 
-        VarStoreInstancer instancer (&(this+varStore),
-                                     &(this+varIdxMap),
-                                     hb_array (font->coords, font->num_coords));
-
         bool is_bounded = true;
         if (clip)
         {
@@ -2337,26 +2723,26 @@ struct COLR
           }
           else
           {
-            auto *extents_funcs = hb_paint_extents_get_funcs ();
-            hb_paint_extents_context_t extents_data;
+            clip = false;
+            is_bounded = false;
+          }
+
+          if (!is_bounded)
+          {
+            auto *bounded_funcs = hb_paint_bounded_get_funcs ();
+            scratch.paint_bounded.clear ();
 
             paint_glyph (font, glyph,
-                         extents_funcs, &extents_data,
+                         bounded_funcs, &scratch.paint_bounded,
                          palette_index, foreground,
-                         false);
+                         false,
+                         scratch);
 
-            hb_extents_t extents = extents_data.get_extents ();
-            is_bounded = extents_data.is_bounded ();
-
-            c.funcs->push_clip_rectangle (c.data,
-                                          extents.xmin,
-                                          extents.ymin,
-                                          extents.xmax,
-                                          extents.ymax);
+            is_bounded = scratch.paint_bounded.is_bounded ();
           }
         }
 
-        c.funcs->push_root_transform (c.data, font);
+        c.funcs->push_font_transform (c.data, font);
 
         if (is_bounded)
           c.recurse (*paint);
@@ -2404,7 +2790,7 @@ struct COLR
   Offset32To                 layerList;
   Offset32To                  clipList;   // Offset to ClipList table (may be NULL)
   Offset32To          varIdxMap;  // Offset to DeltaSetIndexMap table (may be NULL)
-  Offset32To            varStore;
+  Offset32To        varStore;
   public:
   DEFINE_SIZE_MIN (14);
 };
@@ -2427,19 +2813,14 @@ void PaintColrLayers::paint_glyph (hb_paint_context_t *c) const
 {
   TRACE_PAINT (this);
   const LayerList &paint_offset_lists = c->get_colr_table ()->get_layerList ();
+  hb_decycler_node_t node (c->layers_decycler);
   for (unsigned i = firstLayerIndex; i < firstLayerIndex + numLayers; i++)
   {
-    if (unlikely (c->current_layers.has (i)))
-      continue;
-
-    c->current_layers.add (i);
+    if (unlikely (!node.visit (i)))
+      return;
 
     const Paint &paint = paint_offset_lists.get_paint (i);
-    c->funcs->push_group (c->data);
     c->recurse (paint);
-    c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER);
-
-    c->current_layers.del (i);
   }
 }
 
@@ -2447,16 +2828,14 @@ void PaintColrGlyph::paint_glyph (hb_paint_context_t *c) const
 {
   TRACE_PAINT (this);
 
-  if (unlikely (c->current_glyphs.has (gid)))
+  hb_decycler_node_t node (c->glyphs_decycler);
+  if (unlikely (!node.visit (gid)))
     return;
 
-  c->current_glyphs.add (gid);
-
-  c->funcs->push_inverse_root_transform (c->data, c->font);
+  c->funcs->push_inverse_font_transform (c->data, c->font);
   if (c->funcs->color_glyph (c->data, gid, c->font))
   {
     c->funcs->pop_transform (c->data);
-    c->current_glyphs.del (gid);
     return;
   }
   c->funcs->pop_transform (c->data);
@@ -2479,8 +2858,6 @@ void PaintColrGlyph::paint_glyph (hb_paint_context_t *c) const
 
   if (has_clip_box)
     c->funcs->pop_clip (c->data);
-
-  c->current_glyphs.del (gid);
 }
 
 } /* namespace OT */
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/colrv1-closure.hh b/src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/colrv1-closure.hh
index 705863d4ade5c..9ed0aa5632aef 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/colrv1-closure.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/colrv1-closure.hh
@@ -66,34 +66,64 @@ HB_INTERNAL void PaintColrGlyph::closurev1 (hb_colrv1_closure_context_t* c) cons
 
 template  class Var>
 HB_INTERNAL void PaintTransform::closurev1 (hb_colrv1_closure_context_t* c) const
-{ (this+src).dispatch (c); }
+{
+  (this+src).dispatch (c);
+  (this+transform).closurev1 (c);
+}
 
 HB_INTERNAL void PaintTranslate::closurev1 (hb_colrv1_closure_context_t* c) const
-{ (this+src).dispatch (c); }
+{
+  (this+src).dispatch (c);
+  c->num_var_idxes = 2;
+}
 
 HB_INTERNAL void PaintScale::closurev1 (hb_colrv1_closure_context_t* c) const
-{ (this+src).dispatch (c); }
+{
+  (this+src).dispatch (c);
+  c->num_var_idxes = 2;
+}
 
 HB_INTERNAL void PaintScaleAroundCenter::closurev1 (hb_colrv1_closure_context_t* c) const
-{ (this+src).dispatch (c); }
+{
+  (this+src).dispatch (c);
+  c->num_var_idxes = 4;
+}
 
 HB_INTERNAL void PaintScaleUniform::closurev1 (hb_colrv1_closure_context_t* c) const
-{ (this+src).dispatch (c); }
+{
+  (this+src).dispatch (c);
+  c->num_var_idxes = 1;
+}
 
 HB_INTERNAL void PaintScaleUniformAroundCenter::closurev1 (hb_colrv1_closure_context_t* c) const
-{ (this+src).dispatch (c); }
+{
+  (this+src).dispatch (c);
+  c->num_var_idxes = 3;
+}
 
 HB_INTERNAL void PaintRotate::closurev1 (hb_colrv1_closure_context_t* c) const
-{ (this+src).dispatch (c); }
+{
+  (this+src).dispatch (c);
+  c->num_var_idxes = 1;
+}
 
 HB_INTERNAL void PaintRotateAroundCenter::closurev1 (hb_colrv1_closure_context_t* c) const
-{ (this+src).dispatch (c); }
+{
+  (this+src).dispatch (c);
+  c->num_var_idxes = 3;
+}
 
 HB_INTERNAL void PaintSkew::closurev1 (hb_colrv1_closure_context_t* c) const
-{ (this+src).dispatch (c); }
+{
+  (this+src).dispatch (c);
+  c->num_var_idxes = 2;
+}
 
 HB_INTERNAL void PaintSkewAroundCenter::closurev1 (hb_colrv1_closure_context_t* c) const
-{ (this+src).dispatch (c); }
+{
+  (this+src).dispatch (c);
+  c->num_var_idxes = 4;
+}
 
 HB_INTERNAL void PaintComposite::closurev1 (hb_colrv1_closure_context_t* c) const
 {
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Color/CPAL/CPAL.hh b/src/java.desktop/share/native/libharfbuzz/OT/Color/CPAL/CPAL.hh
index ed8f5957e9615..51fc1b52af40d 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Color/CPAL/CPAL.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Color/CPAL/CPAL.hh
@@ -187,6 +187,14 @@ struct CPAL
   hb_ot_name_id_t get_color_name_id (unsigned int color_index) const
   { return v1 ().get_color_name_id (this, color_index, numColors); }
 
+  hb_array_t get_palette_colors (unsigned int palette_index) const
+  {
+    if (unlikely (palette_index >= numPalettes))
+      return hb_array_t ();
+    unsigned int start_index = colorRecordIndicesZ[palette_index];
+    hb_array_t all_colors ((this+colorRecordsZ).arrayZ, numColorRecords);
+    return all_colors.sub_array (start_index, numColors);
+  }
   unsigned int get_palette_colors (unsigned int  palette_index,
                                    unsigned int  start_offset,
                                    unsigned int *color_count, /* IN/OUT.  May be NULL. */
@@ -214,13 +222,17 @@ struct CPAL
                          hb_set_t *nameids_to_retain /* OUT */) const
   {
     if (version == 1)
+    {
+      hb_barrier ();
       v1 ().collect_name_ids (this, numPalettes, numColors, color_index_map, nameids_to_retain);
+    }
   }
 
   private:
   const CPALV1Tail& v1 () const
   {
     if (version == 0) return Null (CPALV1Tail);
+    hb_barrier ();
     return StructAfter (*this);
   }
 
@@ -312,7 +324,10 @@ struct CPAL
       return_trace (false);
 
     if (version == 1)
+    {
+      hb_barrier ();
       return_trace (v1 ().serialize (c->serializer, numPalettes, numColors, this, color_index_map));
+    }
 
     return_trace (true);
   }
@@ -321,6 +336,7 @@ struct CPAL
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
+                  hb_barrier () &&
                   (this+colorRecordsZ).sanitize (c, numColorRecords) &&
                   colorRecordIndicesZ.sanitize (c, numPalettes) &&
                   (version == 0 || v1 ().sanitize (c, this, numPalettes, numColors)));
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Color/sbix/sbix.hh b/src/java.desktop/share/native/libharfbuzz/OT/Color/sbix/sbix.hh
index 20b06ab13e378..b77486a434039 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Color/sbix/sbix.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Color/sbix/sbix.hh
@@ -237,27 +237,28 @@ struct sbix
 
       int x_offset = 0, y_offset = 0;
       unsigned int strike_ppem = 0;
-      hb_blob_t *blob = reference_png (font, glyph, &x_offset, &y_offset, &strike_ppem);
       hb_glyph_extents_t extents;
       hb_glyph_extents_t pixel_extents;
 
-      if (blob == hb_blob_get_empty ())
+      if (!font->get_glyph_extents (glyph, &extents, false))
         return false;
 
-      if (!hb_font_get_glyph_extents (font, glyph, &extents))
+      if (unlikely (!get_extents (font, glyph, &pixel_extents, false)))
         return false;
 
-      if (unlikely (!get_extents (font, glyph, &pixel_extents, false)))
+      hb_blob_t *blob = reference_png (font, glyph, &x_offset, &y_offset, &strike_ppem);
+      if (hb_blob_is_immutable (blob))
         return false;
 
       bool ret = funcs->image (data,
                                blob,
                                pixel_extents.width, -pixel_extents.height,
                                HB_PAINT_IMAGE_FORMAT_PNG,
-                               font->slant_xy,
+                               0.f,
                                &extents);
 
       hb_blob_destroy (blob);
+
       return ret;
     }
 
@@ -368,6 +369,7 @@ struct sbix
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this) &&
+                          hb_barrier () &&
                           version >= 1 &&
                           strikes.sanitize (c, this)));
   }
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Color/svg/svg.hh b/src/java.desktop/share/native/libharfbuzz/OT/Color/svg/svg.hh
index c8ff6ab743e8f..9249abf707580 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Color/svg/svg.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Color/svg/svg.hh
@@ -56,6 +56,7 @@ struct SVGDocumentIndexEntry
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
+                  hb_barrier () &&
                   svgDoc.sanitize (c, base, svgDocLength));
   }
 
@@ -103,15 +104,16 @@ struct SVG
       if (blob == hb_blob_get_empty ())
         return false;
 
-      funcs->image (data,
-                    blob,
-                    0, 0,
-                    HB_PAINT_IMAGE_FORMAT_SVG,
-                    font->slant_xy,
-                    nullptr);
+      bool ret = funcs->image (data,
+                               blob,
+                               0, 0,
+                               HB_PAINT_IMAGE_FORMAT_SVG,
+                               0.f,
+                               nullptr);
 
       hb_blob_destroy (blob);
-      return true;
+
+      return ret;
     }
 
     private:
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/Coverage.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/Coverage.hh
index 257b2a3361ac5..35d73c7b8582b 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/Coverage.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/Coverage.hh
@@ -64,6 +64,7 @@ struct Coverage
   {
     TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return_trace (false);
+    hb_barrier ();
     switch (u.format)
     {
     case 1: return_trace (u.format1.sanitize (c));
@@ -95,6 +96,15 @@ struct Coverage
     default:return NOT_COVERED;
     }
   }
+  unsigned int get_coverage (hb_codepoint_t glyph_id,
+                             hb_ot_lookup_cache_t *cache) const
+  {
+    unsigned coverage;
+    if (cache && cache->get (glyph_id, &coverage)) return coverage;
+    coverage = get_coverage (glyph_id);
+    if (cache) cache->set (glyph_id, coverage);
+    return coverage;
+  }
 
   unsigned get_population () const
   {
@@ -200,6 +210,19 @@ struct Coverage
     }
   }
 
+  unsigned cost () const
+  {
+    switch (u.format) {
+    case 1: hb_barrier (); return u.format1.cost ();
+    case 2: hb_barrier (); return u.format2.cost ();
+#ifndef HB_NO_BEYOND_64K
+    case 3: hb_barrier (); return u.format3.cost ();
+    case 4: hb_barrier (); return u.format4.cost ();
+#endif
+    default:return 0u;
+    }
+  }
+
   /* Might return false if array looks unsorted.
    * Used for faster rejection of corrupt data. */
   template 
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat1.hh
index 995f1ebdbda4b..97ea51089ecaa 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat1.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat1.hh
@@ -77,7 +77,7 @@ struct CoverageFormat1_3
 
   bool intersects (const hb_set_t *glyphs) const
   {
-    if (glyphArray.len > glyphs->get_population () * hb_bit_storage ((unsigned) glyphArray.len) / 2)
+    if (glyphArray.len > glyphs->get_population () * hb_bit_storage ((unsigned) glyphArray.len))
     {
       for (auto g : *glyphs)
         if (get_coverage (g) != NOT_COVERED)
@@ -103,6 +103,8 @@ struct CoverageFormat1_3
         intersect_glyphs << glyphArray[i];
   }
 
+  unsigned cost () const { return hb_bit_storage ((unsigned) glyphArray.len); /* bsearch cost */ }
+
   template 
   bool collect_coverage (set_t *glyphs) const
   { return glyphs->add_sorted_array (glyphArray.as_array ()); }
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat2.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat2.hh
index d47c7eea99252..0c9bd09cbf7ae 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat2.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat2.hh
@@ -120,7 +120,7 @@ struct CoverageFormat2_4
 
   bool intersects (const hb_set_t *glyphs) const
   {
-    if (rangeRecord.len > glyphs->get_population () * hb_bit_storage ((unsigned) rangeRecord.len) / 2)
+    if (rangeRecord.len > glyphs->get_population () * hb_bit_storage ((unsigned) rangeRecord.len))
     {
       for (auto g : *glyphs)
         if (get_coverage (g) != NOT_COVERED)
@@ -157,6 +157,8 @@ struct CoverageFormat2_4
     }
   }
 
+  unsigned cost () const { return hb_bit_storage ((unsigned) rangeRecord.len); /* bsearch cost */ }
+
   template 
   bool collect_coverage (set_t *glyphs) const
   {
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GDEF/GDEF.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GDEF/GDEF.hh
index 98543f56c3bcc..ceba37b061986 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GDEF/GDEF.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GDEF/GDEF.hh
@@ -189,7 +189,7 @@ struct CaretValueFormat3
   friend struct CaretValue;
 
   hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction,
-                                 const VariationStore &var_store) const
+                                 const ItemVariationStore &var_store) const
   {
     return HB_DIRECTION_IS_HORIZONTAL (direction) ?
            font->em_scale_x (coordinate) + (this+deviceTable).get_x_delta (font, var_store) :
@@ -205,20 +205,19 @@ struct CaretValueFormat3
 
     unsigned varidx = (this+deviceTable).get_variation_index ();
     hb_pair_t *new_varidx_delta;
-    if (!c->plan->layout_variation_idx_delta_map.has (varidx, &new_varidx_delta))
-      return_trace (false);
+    if (c->plan->layout_variation_idx_delta_map.has (varidx, &new_varidx_delta)) {
+      uint32_t new_varidx = hb_first (*new_varidx_delta);
+      int delta = hb_second (*new_varidx_delta);
+      if (delta != 0)
+      {
+        if (!c->serializer->check_assign (out->coordinate, coordinate + delta, HB_SERIALIZE_ERROR_INT_OVERFLOW))
+          return_trace (false);
+      }
 
-    uint32_t new_varidx = hb_first (*new_varidx_delta);
-    int delta = hb_second (*new_varidx_delta);
-    if (delta != 0)
-    {
-      if (!c->serializer->check_assign (out->coordinate, coordinate + delta, HB_SERIALIZE_ERROR_INT_OVERFLOW))
-        return_trace (false);
+      if (new_varidx == HB_OT_LAYOUT_NO_VARIATIONS_INDEX)
+        return_trace (c->serializer->check_assign (out->caretValueFormat, 1, HB_SERIALIZE_ERROR_INT_OVERFLOW));
     }
 
-    if (new_varidx == HB_OT_LAYOUT_NO_VARIATIONS_INDEX)
-      return_trace (c->serializer->check_assign (out->caretValueFormat, 1, HB_SERIALIZE_ERROR_INT_OVERFLOW));
-
     if (!c->serializer->embed (deviceTable))
       return_trace (false);
 
@@ -251,7 +250,7 @@ struct CaretValue
   hb_position_t get_caret_value (hb_font_t *font,
                                  hb_direction_t direction,
                                  hb_codepoint_t glyph_id,
-                                 const VariationStore &var_store) const
+                                 const ItemVariationStore &var_store) const
   {
     switch (u.format) {
     case 1: return u.format1.get_caret_value (font, direction);
@@ -291,6 +290,7 @@ struct CaretValue
   {
     TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return_trace (false);
+    hb_barrier ();
     switch (u.format) {
     case 1: return_trace (u.format1.sanitize (c));
     case 2: return_trace (u.format2.sanitize (c));
@@ -315,7 +315,7 @@ struct LigGlyph
   unsigned get_lig_carets (hb_font_t            *font,
                            hb_direction_t        direction,
                            hb_codepoint_t        glyph_id,
-                           const VariationStore &var_store,
+                           const ItemVariationStore &var_store,
                            unsigned              start_offset,
                            unsigned             *caret_count /* IN/OUT */,
                            hb_position_t        *caret_array /* OUT */) const
@@ -371,7 +371,7 @@ struct LigCaretList
   unsigned int get_lig_carets (hb_font_t *font,
                                hb_direction_t direction,
                                hb_codepoint_t glyph_id,
-                               const VariationStore &var_store,
+                               const ItemVariationStore &var_store,
                                unsigned int start_offset,
                                unsigned int *caret_count /* IN/OUT */,
                                hb_position_t *caret_array /* OUT */) const
@@ -441,6 +441,20 @@ struct MarkGlyphSetsFormat1
   bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const
   { return (this+coverage[set_index]).get_coverage (glyph_id) != NOT_COVERED; }
 
+  void collect_used_mark_sets (const hb_set_t& glyph_set,
+                               hb_set_t& used_mark_sets /* OUT */) const
+  {
+    unsigned i = 0;
+    for (const auto &offset : coverage)
+     {
+       const auto &cov = this+offset;
+       if (cov.intersects (&glyph_set))
+         used_mark_sets.add (i);
+
+       i++;
+     }
+  }
+
   template 
   void collect_coverage (hb_vector_t &sets) const
   {
@@ -461,6 +475,7 @@ struct MarkGlyphSetsFormat1
     bool ret = true;
     for (const Offset32To& offset : coverage.iter ())
     {
+      auto snap = c->serializer->snapshot ();
       auto *o = out->coverage.serialize_append (c->serializer);
       if (unlikely (!o))
       {
@@ -468,11 +483,17 @@ struct MarkGlyphSetsFormat1
         break;
       }
 
-      //not using o->serialize_subset (c, offset, this, out) here because
-      //OTS doesn't allow null offset.
-      //See issue: https://github.com/khaledhosny/ots/issues/172
+      //skip empty coverage
       c->serializer->push ();
-      c->dispatch (this+offset);
+      bool res = false;
+      if (offset) res = c->dispatch (this+offset);
+      if (!res)
+      {
+        c->serializer->pop_discard ();
+        c->serializer->revert (snap);
+        (out->coverage.len)--;
+        continue;
+      }
       c->serializer->add_link (*o, c->serializer->pop_pack ());
     }
 
@@ -513,6 +534,15 @@ struct MarkGlyphSets
     }
   }
 
+  void collect_used_mark_sets (const hb_set_t& glyph_set,
+                               hb_set_t& used_mark_sets /* OUT */) const
+  {
+    switch (u.format) {
+    case 1: u.format1.collect_used_mark_sets (glyph_set, used_mark_sets); return;
+    default:return;
+    }
+  }
+
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
@@ -526,6 +556,7 @@ struct MarkGlyphSets
   {
     TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return_trace (false);
+    hb_barrier ();
     switch (u.format) {
     case 1: return_trace (u.format1.sanitize (c));
     default:return_trace (true);
@@ -577,7 +608,7 @@ struct GDEFVersion1_2
                                          * definitions--from beginning of GDEF
                                          * header (may be NULL).  Introduced
                                          * in version 0x00010002. */
-  Offset32To
+  Offset32To
                 varStore;               /* Offset to the table of Item Variation
                                          * Store--from beginning of GDEF
                                          * header (may be NULL).  Introduced
@@ -600,8 +631,9 @@ struct GDEFVersion1_2
                   attachList.sanitize (c, this) &&
                   ligCaretList.sanitize (c, this) &&
                   markAttachClassDef.sanitize (c, this) &&
-                  (version.to_int () < 0x00010002u || markGlyphSetsDef.sanitize (c, this)) &&
-                  (version.to_int () < 0x00010003u || varStore.sanitize (c, this)));
+                  hb_barrier () &&
+                  ((version.to_int () < 0x00010002u && hb_barrier ()) || markGlyphSetsDef.sanitize (c, this)) &&
+                  ((version.to_int () < 0x00010003u && hb_barrier ()) || varStore.sanitize (c, this)));
   }
 
   static void remap_varidx_after_instantiation (const hb_map_t& varidx_map,
@@ -627,23 +659,23 @@ struct GDEFVersion1_2
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    auto *out = c->serializer->embed (*this);
-    if (unlikely (!out)) return_trace (false);
-
-    bool subset_glyphclassdef = out->glyphClassDef.serialize_subset (c, glyphClassDef, this, nullptr, false, true);
-    bool subset_attachlist = out->attachList.serialize_subset (c, attachList, this);
-    bool subset_ligcaretlist = out->ligCaretList.serialize_subset (c, ligCaretList, this);
-    bool subset_markattachclassdef = out->markAttachClassDef.serialize_subset (c, markAttachClassDef, this, nullptr, false, true);
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
 
-    bool subset_markglyphsetsdef = false;
-    if (version.to_int () >= 0x00010002u)
-    {
-      subset_markglyphsetsdef = out->markGlyphSetsDef.serialize_subset (c, markGlyphSetsDef, this);
-    }
+    // Push var store first (if it's needed) so that it's last in the
+    // serialization order. Some font consumers assume that varstore runs to
+    // the end of the GDEF table.
+    // See: https://github.com/harfbuzz/harfbuzz/issues/4636
+    auto snapshot_version0 = c->serializer->snapshot ();
+    if (unlikely (version.to_int () >= 0x00010002u && hb_barrier () && !c->serializer->embed (markGlyphSetsDef)))
+      return_trace (false);
 
     bool subset_varstore = false;
-    if (version.to_int () >= 0x00010003u)
+    unsigned varstore_index = (unsigned) -1;
+    auto snapshot_version2 = c->serializer->snapshot ();
+    if (version.to_int () >= 0x00010003u && hb_barrier ())
     {
+      if (unlikely (!c->serializer->embed (varStore))) return_trace (false);
       if (c->plan->all_axes_pinned)
         out->varStore = 0;
       else if (c->plan->normalized_coords)
@@ -652,27 +684,56 @@ struct GDEFVersion1_2
         {
           item_variations_t item_vars;
           if (item_vars.instantiate (this+varStore, c->plan, true, true,
-                                     c->plan->gdef_varstore_inner_maps.as_array ()))
+                                     c->plan->gdef_varstore_inner_maps.as_array ())) {
             subset_varstore = out->varStore.serialize_serialize (c->serializer,
                                                                  item_vars.has_long_word (),
                                                                  c->plan->axis_tags,
                                                                  item_vars.get_region_list (),
                                                                  item_vars.get_vardata_encodings ());
+            varstore_index = c->serializer->last_added_child_index();
+          }
           remap_varidx_after_instantiation (item_vars.get_varidx_map (),
                                             c->plan->layout_variation_idx_delta_map);
         }
       }
       else
+      {
         subset_varstore = out->varStore.serialize_subset (c, varStore, this, c->plan->gdef_varstore_inner_maps.as_array ());
+        varstore_index = c->serializer->last_added_child_index();
+      }
+    }
+
+    out->version.major = version.major;
+    out->version.minor = version.minor;
+
+    if (!subset_varstore && version.to_int () >= 0x00010002u) {
+      c->serializer->revert (snapshot_version2);
+    }
+
+    bool subset_markglyphsetsdef = false;
+    if (version.to_int () >= 0x00010002u && hb_barrier ())
+    {
+      subset_markglyphsetsdef = out->markGlyphSetsDef.serialize_subset (c, markGlyphSetsDef, this);
     }
 
     if (subset_varstore)
     {
       out->version.minor = 3;
+      c->plan->has_gdef_varstore = true;
     } else if (subset_markglyphsetsdef) {
       out->version.minor = 2;
     } else  {
       out->version.minor = 0;
+      c->serializer->revert (snapshot_version0);
+    }
+
+    bool subset_glyphclassdef = out->glyphClassDef.serialize_subset (c, glyphClassDef, this, nullptr, false, true);
+    bool subset_attachlist = out->attachList.serialize_subset (c, attachList, this);
+    bool subset_markattachclassdef = out->markAttachClassDef.serialize_subset (c, markAttachClassDef, this, nullptr, false, true);
+    bool subset_ligcaretlist = out->ligCaretList.serialize_subset (c, ligCaretList, this);
+
+    if (subset_varstore && varstore_index != (unsigned) -1) {
+      c->serializer->repack_last(varstore_index);
     }
 
     return_trace (subset_glyphclassdef || subset_attachlist ||
@@ -709,6 +770,7 @@ struct GDEF
   {
     TRACE_SANITIZE (this);
     if (unlikely (!u.version.sanitize (c))) return_trace (false);
+    hb_barrier ();
     switch (u.version.major) {
     case 1: return_trace (u.version1.sanitize (c));
 #ifndef HB_NO_BEYOND_64K
@@ -812,7 +874,7 @@ struct GDEF
   bool has_mark_glyph_sets () const
   {
     switch (u.version.major) {
-    case 1: return u.version.to_int () >= 0x00010002u && u.version1.markGlyphSetsDef != 0;
+    case 1: return u.version.to_int () >= 0x00010002u && hb_barrier () && u.version1.markGlyphSetsDef != 0;
 #ifndef HB_NO_BEYOND_64K
     case 2: return u.version2.markGlyphSetsDef != 0;
 #endif
@@ -822,7 +884,7 @@ struct GDEF
   const MarkGlyphSets &get_mark_glyph_sets () const
   {
     switch (u.version.major) {
-    case 1: return u.version.to_int () >= 0x00010002u ? this+u.version1.markGlyphSetsDef : Null(MarkGlyphSets);
+    case 1: return u.version.to_int () >= 0x00010002u && hb_barrier () ? this+u.version1.markGlyphSetsDef : Null(MarkGlyphSets);
 #ifndef HB_NO_BEYOND_64K
     case 2: return this+u.version2.markGlyphSetsDef;
 #endif
@@ -832,21 +894,21 @@ struct GDEF
   bool has_var_store () const
   {
     switch (u.version.major) {
-    case 1: return u.version.to_int () >= 0x00010003u && u.version1.varStore != 0;
+    case 1: return u.version.to_int () >= 0x00010003u && hb_barrier () && u.version1.varStore != 0;
 #ifndef HB_NO_BEYOND_64K
     case 2: return u.version2.varStore != 0;
 #endif
     default: return false;
     }
   }
-  const VariationStore &get_var_store () const
+  const ItemVariationStore &get_var_store () const
   {
     switch (u.version.major) {
-    case 1: return u.version.to_int () >= 0x00010003u ? this+u.version1.varStore : Null(VariationStore);
+    case 1: return u.version.to_int () >= 0x00010003u && hb_barrier () ? this+u.version1.varStore : Null(ItemVariationStore);
 #ifndef HB_NO_BEYOND_64K
     case 2: return this+u.version2.varStore;
 #endif
-    default: return Null(VariationStore);
+    default: return Null(ItemVariationStore);
     }
   }
 
@@ -952,54 +1014,14 @@ struct GDEF
     hb_blob_ptr_t table;
 #ifndef HB_NO_GDEF_CACHE
     hb_vector_t mark_glyph_set_digests;
-    mutable hb_cache_t<21, 3, 8> glyph_props_cache;
+    mutable hb_cache_t<21, 3> glyph_props_cache;
+    static_assert (sizeof (glyph_props_cache) == 512, "");
 #endif
   };
 
   void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
   { get_lig_caret_list ().collect_variation_indices (c); }
 
-  void remap_layout_variation_indices (const hb_set_t *layout_variation_indices,
-                                       const hb_vector_t& normalized_coords,
-                                       bool calculate_delta, /* not pinned at default */
-                                       bool no_variations, /* all axes pinned */
-                                       hb_hashmap_t> *layout_variation_idx_delta_map /* OUT */) const
-  {
-    if (!has_var_store ()) return;
-    const VariationStore &var_store = get_var_store ();
-    float *store_cache = var_store.create_cache ();
-
-    unsigned new_major = 0, new_minor = 0;
-    unsigned last_major = (layout_variation_indices->get_min ()) >> 16;
-    for (unsigned idx : layout_variation_indices->iter ())
-    {
-      int delta = 0;
-      if (calculate_delta)
-        delta = roundf (var_store.get_delta (idx, normalized_coords.arrayZ,
-                                             normalized_coords.length, store_cache));
-
-      if (no_variations)
-      {
-        layout_variation_idx_delta_map->set (idx, hb_pair_t (HB_OT_LAYOUT_NO_VARIATIONS_INDEX, delta));
-        continue;
-      }
-
-      uint16_t major = idx >> 16;
-      if (major >= var_store.get_sub_table_count ()) break;
-      if (major != last_major)
-      {
-        new_minor = 0;
-        ++new_major;
-      }
-
-      unsigned new_idx = (new_major << 16) + new_minor;
-      layout_variation_idx_delta_map->set (idx, hb_pair_t (new_idx, delta));
-      ++new_minor;
-      last_major = major;
-    }
-    var_store.destroy_cache (store_cache);
-  }
-
   protected:
   union {
   FixedVersion<>                version;        /* Version identifier */
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/Anchor.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/Anchor.hh
index 49e76e77509e6..7802e397f4cf5 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/Anchor.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/Anchor.hh
@@ -25,6 +25,7 @@ struct Anchor
   {
     TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return_trace (false);
+    hb_barrier ();
     switch (u.format) {
     case 1: return_trace (u.format1.sanitize (c));
     case 2: return_trace (u.format2.sanitize (c));
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/AnchorFormat3.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/AnchorFormat3.hh
index 23821a49c77ad..61bd90310a51a 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/AnchorFormat3.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/AnchorFormat3.hh
@@ -38,9 +38,15 @@ struct AnchorFormat3
     *y = font->em_fscale_y (yCoordinate);
 
     if ((font->x_ppem || font->num_coords) && xDeviceTable.sanitize (&c->sanitizer, this))
+    {
+      hb_barrier ();
       *x += (this+xDeviceTable).get_x_delta (font, c->var_store, c->var_store_cache);
+    }
     if ((font->y_ppem || font->num_coords) && yDeviceTable.sanitize (&c->sanitizer, this))
+    {
+      hb_barrier ();
       *y += (this+yDeviceTable).get_y_delta (font, c->var_store, c->var_store_cache);
+    }
   }
 
   bool subset (hb_subset_context_t *c) const
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/AnchorMatrix.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/AnchorMatrix.hh
index b61f1413ea5a9..9da9fff50ba89 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/AnchorMatrix.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/AnchorMatrix.hh
@@ -8,7 +8,7 @@ namespace GPOS_impl {
 struct AnchorMatrix
 {
   HBUINT16      rows;                   /* Number of rows */
-  UnsizedArrayOf>
+  UnsizedArrayOf>
                 matrixZ;                /* Matrix of offsets to Anchor tables--
                                          * from beginning of AnchorMatrix table */
   public:
@@ -18,6 +18,7 @@ struct AnchorMatrix
   {
     TRACE_SANITIZE (this);
     if (!c->check_struct (this)) return_trace (false);
+    hb_barrier ();
     if (unlikely (hb_unsigned_mul_overflows (rows, cols))) return_trace (false);
     unsigned int count = rows * cols;
     if (!c->check_array (matrixZ.arrayZ, count)) return_trace (false);
@@ -25,6 +26,7 @@ struct AnchorMatrix
     if (c->lazy_some_gpos)
       return_trace (true);
 
+    hb_barrier ();
     for (unsigned int i = 0; i < count; i++)
       if (!matrixZ[i].sanitize (c, this)) return_trace (false);
     return_trace (true);
@@ -38,6 +40,7 @@ struct AnchorMatrix
     if (unlikely (row >= rows || col >= cols)) return Null (Anchor);
     auto &offset = matrixZ[row * cols + col];
     if (unlikely (!offset.sanitize (&c->sanitizer, this))) return Null (Anchor);
+    hb_barrier ();
     *found = !offset.is_null ();
     return this+offset;
   }
@@ -65,15 +68,14 @@ struct AnchorMatrix
     if (unlikely (!c->serializer->extend_min (out)))  return_trace (false);
 
     out->rows = num_rows;
-    bool ret = false;
     for (const unsigned i : index_iter)
     {
       auto *offset = c->serializer->embed (matrixZ[i]);
       if (!offset) return_trace (false);
-      ret |= offset->serialize_subset (c, matrixZ[i], this);
+      offset->serialize_subset (c, matrixZ[i], this);
     }
 
-    return_trace (ret);
+    return_trace (true);
   }
 };
 
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/Common.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/Common.hh
index 408197454f1c7..696d25d75c889 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/Common.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/Common.hh
@@ -23,7 +23,7 @@ static void SinglePos_serialize (hb_serialize_context_t *c,
                                  const SrcLookup *src,
                                  Iterator it,
                                  const hb_hashmap_t> *layout_variation_idx_delta_map,
-                                 bool all_axes_pinned);
+                                 unsigned new_format);
 
 
 }
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/CursivePosFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/CursivePosFormat1.hh
index 3a2957af1a543..361aaed658a97 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/CursivePosFormat1.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/CursivePosFormat1.hh
@@ -11,21 +11,21 @@ struct EntryExitRecord
 {
   friend struct CursivePosFormat1;
 
-  bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  bool sanitize (hb_sanitize_context_t *c, const struct CursivePosFormat1 *base) const
   {
     TRACE_SANITIZE (this);
     return_trace (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base));
   }
 
   void collect_variation_indices (hb_collect_variation_indices_context_t *c,
-                                  const void *src_base) const
+                                  const struct CursivePosFormat1 *src_base) const
   {
     (src_base+entryAnchor).collect_variation_indices (c);
     (src_base+exitAnchor).collect_variation_indices (c);
   }
 
   bool subset (hb_subset_context_t *c,
-               const void *src_base) const
+               const struct CursivePosFormat1 *src_base) const
   {
     TRACE_SERIALIZE (this);
     auto *out = c->serializer->embed (this);
@@ -38,11 +38,11 @@ struct EntryExitRecord
   }
 
   protected:
-  Offset16To
+  Offset16To
                 entryAnchor;            /* Offset to EntryAnchor table--from
                                          * beginning of CursivePos
                                          * subtable--may be NULL */
-  Offset16To
+  Offset16To
                 exitAnchor;             /* Offset to ExitAnchor table--from
                                          * beginning of CursivePos
                                          * subtable--may be NULL */
@@ -128,6 +128,7 @@ struct CursivePosFormat1
     const EntryExitRecord &this_record = entryExitRecord[(this+coverage).get_coverage  (buffer->cur().codepoint)];
     if (!this_record.entryAnchor ||
         unlikely (!this_record.entryAnchor.sanitize (&c->sanitizer, this))) return_trace (false);
+    hb_barrier ();
 
     hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
     skippy_iter.reset_fast (buffer->idx);
@@ -145,6 +146,7 @@ struct CursivePosFormat1
       buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
       return_trace (false);
     }
+    hb_barrier ();
 
     unsigned int i = skippy_iter.idx;
     unsigned int j = buffer->idx;
@@ -262,7 +264,7 @@ struct CursivePosFormat1
             hb_requires (hb_is_iterator (Iterator))>
   void serialize (hb_subset_context_t *c,
                   Iterator it,
-                  const void *src_base)
+                  const struct CursivePosFormat1 *src_base)
   {
     if (unlikely (!c->serializer->extend_min ((*this)))) return;
     this->format = 1;
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/GPOS.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/GPOS.hh
index f4af98b25fd73..ce3f74d8c3be3 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/GPOS.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/GPOS.hh
@@ -152,8 +152,11 @@ GPOS::position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer)
     for (unsigned i = 0; i < len; i++)
       propagate_attachment_offsets (pos, len, i, direction);
 
-  if (unlikely (font->slant))
+  if (unlikely (font->slant_xy) &&
+      HB_DIRECTION_IS_HORIZONTAL (direction))
   {
+    /* Slanting shaping results is only supported for horizontal text,
+     * as it gets weird otherwise. */
     for (unsigned i = 0; i < len; i++)
       if (unlikely (pos[i].y_offset))
         pos[i].x_offset += roundf (font->slant_xy * pos[i].y_offset);
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkMarkPosFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkMarkPosFormat1.hh
index ea196581aff85..6519e79b44395 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkMarkPosFormat1.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkMarkPosFormat1.hh
@@ -42,6 +42,7 @@ struct MarkMarkPosFormat1_2
                   mark1Coverage.sanitize (c, this) &&
                   mark2Coverage.sanitize (c, this) &&
                   mark1Array.sanitize (c, this) &&
+                  hb_barrier () &&
                   mark2Array.sanitize (c, this, (unsigned int) classCount));
   }
 
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat1.hh
index 478c72df750bf..2748882f52701 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat1.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat1.hh
@@ -36,6 +36,7 @@ struct PairPosFormat1_3
     TRACE_SANITIZE (this);
 
     if (!c->check_struct (this)) return_trace (false);
+    hb_barrier ();
 
     unsigned int len1 = valueFormat[0].get_len ();
     unsigned int len2 = valueFormat[1].get_len ();
@@ -53,7 +54,7 @@ struct PairPosFormat1_3
   {
     auto &cov = this+coverage;
 
-    if (pairSet.len > glyphs->get_population () * hb_bit_storage ((unsigned) pairSet.len) / 4)
+    if (pairSet.len > glyphs->get_population () * hb_bit_storage ((unsigned) pairSet.len))
     {
       for (hb_codepoint_t g : glyphs->iter())
       {
@@ -102,12 +103,50 @@ struct PairPosFormat1_3
 
   const Coverage &get_coverage () const { return this+coverage; }
 
-  bool apply (hb_ot_apply_context_t *c) const
+  unsigned cache_cost () const
+  {
+    return (this+coverage).cost ();
+  }
+  static void * cache_func (void *p, hb_ot_lookup_cache_op_t op)
+  {
+    switch (op)
+    {
+      case hb_ot_lookup_cache_op_t::CREATE:
+      {
+        hb_ot_lookup_cache_t *cache = (hb_ot_lookup_cache_t *) hb_malloc (sizeof (hb_ot_lookup_cache_t));
+        if (likely (cache))
+          cache->clear ();
+        return cache;
+      }
+      case hb_ot_lookup_cache_op_t::ENTER:
+        return (void *) true;
+      case hb_ot_lookup_cache_op_t::LEAVE:
+        return nullptr;
+      case hb_ot_lookup_cache_op_t::DESTROY:
+      {
+        hb_ot_lookup_cache_t *cache = (hb_ot_lookup_cache_t *) p;
+        hb_free (cache);
+        return nullptr;
+      }
+    }
+    return nullptr;
+  }
+
+  bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); }
+  bool apply (hb_ot_apply_context_t *c) const { return _apply (c, false); }
+  bool _apply (hb_ot_apply_context_t *c, bool cached) const
   {
     TRACE_APPLY (this);
+
     hb_buffer_t *buffer = c->buffer;
+
+#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+    hb_ot_lookup_cache_t *cache = cached ? (hb_ot_lookup_cache_t *) c->lookup_accel->cache : nullptr;
+    unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint, cache);
+#else
     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+#endif
+    if (index == NOT_COVERED) return_trace (false);
 
     hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
     skippy_iter.reset_fast (buffer->idx);
@@ -131,21 +170,34 @@ struct PairPosFormat1_3
     auto *out = c->serializer->start_embed (*this);
     if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
     out->format = format;
-    out->valueFormat[0] = valueFormat[0];
-    out->valueFormat[1] = valueFormat[1];
-    if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
+
+    hb_pair_t newFormats = hb_pair (valueFormat[0], valueFormat[1]);
+
+    if (c->plan->normalized_coords)
     {
-      hb_pair_t newFormats = compute_effective_value_formats (glyphset);
-      out->valueFormat[0] = newFormats.first;
-      out->valueFormat[1] = newFormats.second;
+      /* all device flags will be dropped when full instancing, no need to strip
+       * hints, also do not strip emtpy cause we don't compute the new default
+       * value during stripping */
+      newFormats = compute_effective_value_formats (glyphset, false, false, &c->plan->layout_variation_idx_delta_map);
     }
-
-    if (c->plan->all_axes_pinned)
+    /* do not strip hints for VF */
+    else if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
     {
-      out->valueFormat[0] = out->valueFormat[0].drop_device_table_flags ();
-      out->valueFormat[1] = out->valueFormat[1].drop_device_table_flags ();
+      hb_blob_t* blob = hb_face_reference_table (c->plan->source, HB_TAG ('f','v','a','r'));
+      bool has_fvar = (blob != hb_blob_get_empty ());
+      hb_blob_destroy (blob);
+
+      bool strip = !has_fvar;
+      /* special case: strip hints when a VF has no GDEF varstore after
+       * subsetting*/
+      if (has_fvar && !c->plan->has_gdef_varstore)
+        strip = true;
+      newFormats = compute_effective_value_formats (glyphset, strip, true);
     }
 
+    out->valueFormat[0] = newFormats.first;
+    out->valueFormat[1] = newFormats.second;
+
     hb_sorted_vector_t new_coverage;
 
     + hb_zip (this+coverage, pairSet)
@@ -175,7 +227,9 @@ struct PairPosFormat1_3
   }
 
 
-  hb_pair_t compute_effective_value_formats (const hb_set_t& glyphset) const
+  hb_pair_t compute_effective_value_formats (const hb_set_t& glyphset,
+                                                                 bool strip_hints, bool strip_empty,
+                                                                 const hb_hashmap_t> *varidx_delta_map = nullptr) const
   {
     unsigned record_size = PairSet::get_size (valueFormat);
 
@@ -195,8 +249,8 @@ struct PairPosFormat1_3
       {
         if (record->intersects (glyphset))
         {
-          format1 = format1 | valueFormat[0].get_effective_format (record->get_values_1 ());
-          format2 = format2 | valueFormat[1].get_effective_format (record->get_values_2 (valueFormat[0]));
+          format1 = format1 | valueFormat[0].get_effective_format (record->get_values_1 (), strip_hints, strip_empty, &set, varidx_delta_map);
+          format2 = format2 | valueFormat[1].get_effective_format (record->get_values_2 (valueFormat[0]), strip_hints, strip_empty, &set, varidx_delta_map);
         }
         record = &StructAtOffset (record, record_size);
       }
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat2.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat2.hh
index ce6eec4f20697..d85b1ac2c1777 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat2.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat2.hh
@@ -8,7 +8,7 @@ namespace Layout {
 namespace GPOS_impl {
 
 template 
-struct PairPosFormat2_4
+struct PairPosFormat2_4 : ValueBase
 {
   protected:
   HBUINT16      format;                 /* Format identifier--format = 2 */
@@ -123,12 +123,61 @@ struct PairPosFormat2_4
 
   const Coverage &get_coverage () const { return this+coverage; }
 
-  bool apply (hb_ot_apply_context_t *c) const
+  struct pair_pos_cache_t
+  {
+    hb_ot_lookup_cache_t coverage;
+    hb_ot_lookup_cache_t first;
+    hb_ot_lookup_cache_t second;
+  };
+
+  unsigned cache_cost () const
+  {
+    return (this+coverage).cost () + (this+classDef1).cost () + (this+classDef2).cost ();
+  }
+  static void * cache_func (void *p, hb_ot_lookup_cache_op_t op)
+  {
+    switch (op)
+    {
+      case hb_ot_lookup_cache_op_t::CREATE:
+      {
+        pair_pos_cache_t *cache = (pair_pos_cache_t *) hb_malloc (sizeof (pair_pos_cache_t));
+        if (likely (cache))
+        {
+          cache->coverage.clear ();
+          cache->first.clear ();
+          cache->second.clear ();
+        }
+        return cache;
+      }
+      case hb_ot_lookup_cache_op_t::ENTER:
+        return (void *) true;
+      case hb_ot_lookup_cache_op_t::LEAVE:
+        return nullptr;
+      case hb_ot_lookup_cache_op_t::DESTROY:
+        {
+          pair_pos_cache_t *cache = (pair_pos_cache_t *) p;
+          hb_free (cache);
+          return nullptr;
+        }
+    }
+    return nullptr;
+  }
+
+  bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); }
+  bool apply (hb_ot_apply_context_t *c) const { return _apply (c, false); }
+  bool _apply (hb_ot_apply_context_t *c, bool cached) const
   {
     TRACE_APPLY (this);
+
     hb_buffer_t *buffer = c->buffer;
+
+#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+    pair_pos_cache_t *cache = cached ? (pair_pos_cache_t *) c->lookup_accel->cache : nullptr;
+    unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint, cache ? &cache->coverage : nullptr);
+#else
     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+#endif
+    if (index == NOT_COVERED) return_trace (false);
 
     hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
     skippy_iter.reset_fast (buffer->idx);
@@ -139,14 +188,13 @@ struct PairPosFormat2_4
       return_trace (false);
     }
 
-    unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint);
-    if (!klass2)
-    {
-      buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1);
-      return_trace (false);
-    }
-
+#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+    unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint, cache ? &cache->first : nullptr);
+    unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint, cache ? &cache->second : nullptr);
+#else
     unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint);
+    unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint);
+#endif
     if (unlikely (klass1 >= class1Count || klass2 >= class2Count))
     {
       buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1);
@@ -287,18 +335,31 @@ struct PairPosFormat2_4
     unsigned len2 = valueFormat2.get_len ();
 
     hb_pair_t newFormats = hb_pair (valueFormat1, valueFormat2);
-    if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
-      newFormats = compute_effective_value_formats (klass1_map, klass2_map);
-
-    out->valueFormat1 = newFormats.first;
-    out->valueFormat2 = newFormats.second;
 
-    if (c->plan->all_axes_pinned)
+    if (c->plan->normalized_coords)
     {
-      out->valueFormat1 = out->valueFormat1.drop_device_table_flags ();
-      out->valueFormat2 = out->valueFormat2.drop_device_table_flags ();
+      /* in case of full instancing, all var device flags will be dropped so no
+       * need to strip hints here */
+      newFormats = compute_effective_value_formats (klass1_map, klass2_map, false, false, &c->plan->layout_variation_idx_delta_map);
+    }
+    /* do not strip hints for VF */
+    else if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
+    {
+      hb_blob_t* blob = hb_face_reference_table (c->plan->source, HB_TAG ('f','v','a','r'));
+      bool has_fvar = (blob != hb_blob_get_empty ());
+      hb_blob_destroy (blob);
+
+      bool strip = !has_fvar;
+      /* special case: strip hints when a VF has no GDEF varstore after
+       * subsetting*/
+      if (has_fvar && !c->plan->has_gdef_varstore)
+        strip = true;
+      newFormats = compute_effective_value_formats (klass1_map, klass2_map, strip, true);
     }
 
+    out->valueFormat1 = newFormats.first;
+    out->valueFormat2 = newFormats.second;
+
     unsigned total_len = len1 + len2;
     hb_vector_t class2_idxs (+ hb_range ((unsigned) class2Count) | hb_filter (klass2_map));
     for (unsigned class1_idx : + hb_range ((unsigned) class1Count) | hb_filter (klass1_map))
@@ -311,22 +372,15 @@ struct PairPosFormat2_4
       }
     }
 
-    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
-    const hb_map_t &glyph_map = *c->plan->glyph_map;
-
-    auto it =
-    + hb_iter (this+coverage)
-    | hb_filter (glyphset)
-    | hb_map_retains_sorting (glyph_map)
-    ;
-
-    out->coverage.serialize_serialize (c->serializer, it);
-    return_trace (out->class1Count && out->class2Count && bool (it));
+    bool ret = out->coverage.serialize_subset(c, coverage, this);
+    return_trace (out->class1Count && out->class2Count && ret);
   }
 
 
   hb_pair_t compute_effective_value_formats (const hb_map_t& klass1_map,
-                                                                 const hb_map_t& klass2_map) const
+                                                                 const hb_map_t& klass2_map,
+                                                                 bool strip_hints, bool strip_empty,
+                                                                 const hb_hashmap_t> *varidx_delta_map = nullptr) const
   {
     unsigned len1 = valueFormat1.get_len ();
     unsigned len2 = valueFormat2.get_len ();
@@ -340,8 +394,8 @@ struct PairPosFormat2_4
       for (unsigned class2_idx : + hb_range ((unsigned) class2Count) | hb_filter (klass2_map))
       {
         unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * record_size;
-        format1 = format1 | valueFormat1.get_effective_format (&values[idx]);
-        format2 = format2 | valueFormat2.get_effective_format (&values[idx + len1]);
+        format1 = format1 | valueFormat1.get_effective_format (&values[idx], strip_hints, strip_empty, this, varidx_delta_map);
+        format2 = format2 | valueFormat2.get_effective_format (&values[idx + len1], strip_hints, strip_empty, this, varidx_delta_map);
       }
 
       if (format1 == valueFormat1 && format2 == valueFormat2)
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairSet.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairSet.hh
index 7ccec1df841d0..e610fcd75170e 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairSet.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairSet.hh
@@ -9,7 +9,7 @@ namespace GPOS_impl {
 
 
 template 
-struct PairSet
+struct PairSet : ValueBase
 {
   template 
   friend struct PairPosFormat1_3;
@@ -45,10 +45,12 @@ struct PairSet
   bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const
   {
     TRACE_SANITIZE (this);
-    if (!(c->check_struct (this)
-       && c->check_range (&firstPairValueRecord,
+    if (!(c->check_struct (this) &&
+          hb_barrier () &&
+          c->check_range (&firstPairValueRecord,
                           len,
                           closure->stride))) return_trace (false);
+    hb_barrier ();
 
     unsigned int count = len;
     const PairValueRecord *record = &firstPairValueRecord;
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairValueRecord.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairValueRecord.hh
index b32abe46d21b6..fe9595f1266ce 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairValueRecord.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairValueRecord.hh
@@ -29,7 +29,7 @@ struct PairValueRecord
 
   struct context_t
   {
-    const void          *base;
+    const ValueBase     *base;
     const ValueFormat   *valueFormats;
     const ValueFormat   *newFormats;
     unsigned            len1; /* valueFormats[0].get_len() */
@@ -62,7 +62,7 @@ struct PairValueRecord
 
   void collect_variation_indices (hb_collect_variation_indices_context_t *c,
                                   const ValueFormat *valueFormats,
-                                  const void *base) const
+                                  const ValueBase *base) const
   {
     unsigned record1_len = valueFormats[0].get_len ();
     unsigned record2_len = valueFormats[1].get_len ();
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePos.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePos.hh
index 3af6c49965943..a0243a218c581 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePos.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePos.hh
@@ -39,14 +39,12 @@ struct SinglePos
                   const SrcLookup* src,
                   Iterator glyph_val_iter_pairs,
                   const hb_hashmap_t> *layout_variation_idx_delta_map,
-                  bool all_axes_pinned)
+                  unsigned newFormat)
   {
     if (unlikely (!c->extend_min (u.format))) return;
     unsigned format = 2;
-    ValueFormat new_format = src->get_value_format ();
-
-    if (all_axes_pinned)
-      new_format = new_format.drop_device_table_flags ();
+    ValueFormat new_format;
+    new_format = newFormat;
 
     if (glyph_val_iter_pairs)
       format = get_format (glyph_val_iter_pairs);
@@ -89,8 +87,8 @@ SinglePos_serialize (hb_serialize_context_t *c,
                      const SrcLookup *src,
                      Iterator it,
                      const hb_hashmap_t> *layout_variation_idx_delta_map,
-                     bool all_axes_pinned)
-{ c->start_embed ()->serialize (c, src, it, layout_variation_idx_delta_map, all_axes_pinned); }
+                     unsigned new_format)
+{ c->start_embed ()->serialize (c, src, it, layout_variation_idx_delta_map, new_format); }
 
 
 }
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePosFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePosFormat1.hh
index 8e21c5f8e713b..1a14be020f936 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePosFormat1.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePosFormat1.hh
@@ -8,7 +8,7 @@ namespace OT {
 namespace Layout {
 namespace GPOS_impl {
 
-struct SinglePosFormat1
+struct SinglePosFormat1 : ValueBase
 {
   protected:
   HBUINT16      format;                 /* Format identifier--format = 1 */
@@ -28,6 +28,7 @@ struct SinglePosFormat1
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
                   coverage.sanitize (c, this) &&
+                  hb_barrier () &&
                   /* The coverage  table may use a range to represent a set
                    * of glyphs, which means a small number of bytes can
                    * generate a large glyph set. Manually modify the
@@ -66,7 +67,7 @@ struct SinglePosFormat1
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+    if (index == NOT_COVERED) return_trace (false);
 
     if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
     {
@@ -146,6 +147,30 @@ struct SinglePosFormat1
     hb_set_t intersection;
     (this+coverage).intersect_set (glyphset, intersection);
 
+    unsigned new_format = valueFormat;
+
+    if (c->plan->normalized_coords)
+    {
+      new_format = valueFormat.get_effective_format (values.arrayZ, false, false, this, &c->plan->layout_variation_idx_delta_map);
+    }
+    /* do not strip hints for VF */
+    else if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
+    {
+      hb_blob_t* blob = hb_face_reference_table (c->plan->source, HB_TAG ('f','v','a','r'));
+      bool has_fvar = (blob != hb_blob_get_empty ());
+      hb_blob_destroy (blob);
+
+      bool strip = !has_fvar;
+      /* special case: strip hints when a VF has no GDEF varstore after
+       * subsetting*/
+      if (has_fvar && !c->plan->has_gdef_varstore)
+        strip = true;
+      new_format = valueFormat.get_effective_format (values.arrayZ,
+                                                     strip, /* strip hints */
+                                                     true, /* strip empty */
+                                                     this, nullptr);
+    }
+
     auto it =
     + hb_iter (intersection)
     | hb_map_retains_sorting (glyph_map)
@@ -153,7 +178,7 @@ struct SinglePosFormat1
     ;
 
     bool ret = bool (it);
-    SinglePos_serialize (c->serializer, this, it, &c->plan->layout_variation_idx_delta_map, c->plan->all_axes_pinned);
+    SinglePos_serialize (c->serializer, this, it, &c->plan->layout_variation_idx_delta_map, new_format);
     return_trace (ret);
   }
 };
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePosFormat2.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePosFormat2.hh
index ddc4c18ec1c49..455796b4b27e7 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePosFormat2.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePosFormat2.hh
@@ -7,7 +7,7 @@ namespace OT {
 namespace Layout {
 namespace GPOS_impl {
 
-struct SinglePosFormat2
+struct SinglePosFormat2 : ValueBase
 {
   protected:
   HBUINT16      format;                 /* Format identifier--format = 2 */
@@ -66,7 +66,7 @@ struct SinglePosFormat2
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+    if (index == NOT_COVERED) return_trace (false);
 
     if (unlikely (index >= valueCount)) return_trace (false);
 
@@ -143,6 +143,37 @@ struct SinglePosFormat2
     coverage.serialize_serialize (c, glyphs);
   }
 
+  template
+  unsigned compute_effective_format (const hb_face_t *face,
+                                     Iterator it,
+                                     bool is_instancing, bool strip_hints,
+                                     bool has_gdef_varstore,
+                                     const hb_hashmap_t> *varidx_delta_map) const
+  {
+    hb_blob_t* blob = hb_face_reference_table (face, HB_TAG ('f','v','a','r'));
+    bool has_fvar = (blob != hb_blob_get_empty ());
+    hb_blob_destroy (blob);
+
+    unsigned new_format = 0;
+    if (is_instancing)
+    {
+      new_format = new_format | valueFormat.get_effective_format (+ it | hb_map (hb_second), false, false, this, varidx_delta_map);
+    }
+    /* do not strip hints for VF */
+    else if (strip_hints)
+    {
+      bool strip = !has_fvar;
+      if (has_fvar && !has_gdef_varstore)
+        strip = true;
+      new_format = new_format | valueFormat.get_effective_format (+ it | hb_map (hb_second), strip, true, this, nullptr);
+    }
+    else
+      new_format = valueFormat;
+
+    return new_format;
+  }
+
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
@@ -163,8 +194,13 @@ struct SinglePosFormat2
                               })
     ;
 
+    unsigned new_format = compute_effective_format (c->plan->source, it,
+                                                    bool (c->plan->normalized_coords),
+                                                    bool (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING),
+                                                    c->plan->has_gdef_varstore,
+                                                    &c->plan->layout_variation_idx_delta_map);
     bool ret = bool (it);
-    SinglePos_serialize (c->serializer, this, it, &c->plan->layout_variation_idx_delta_map, c->plan->all_axes_pinned);
+    SinglePos_serialize (c->serializer, this, it, &c->plan->layout_variation_idx_delta_map, new_format);
     return_trace (ret);
   }
 };
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/ValueFormat.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/ValueFormat.hh
index 8618cddad1c73..731d1ffca1a56 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/ValueFormat.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/ValueFormat.hh
@@ -9,6 +9,8 @@ namespace GPOS_impl {
 
 typedef HBUINT16 Value;
 
+struct ValueBase {}; // Dummy base class tag for OffsetTo bases.
+
 typedef UnsizedArrayOf ValueRecord;
 
 struct ValueFormat : HBUINT16
@@ -78,7 +80,7 @@ struct ValueFormat : HBUINT16
   }
 
   bool apply_value (hb_ot_apply_context_t *c,
-                    const void            *base,
+                    const ValueBase       *base,
                     const Value           *values,
                     hb_glyph_position_t   &glyph_pos) const
   {
@@ -114,7 +116,7 @@ struct ValueFormat : HBUINT16
 
     if (!use_x_device && !use_y_device) return ret;
 
-    const VariationStore &store = c->var_store;
+    const ItemVariationStore &store = c->var_store;
     auto *cache = c->var_store_cache;
 
     /* pixel -> fractional pixel */
@@ -142,11 +144,29 @@ struct ValueFormat : HBUINT16
     return ret;
   }
 
-  unsigned int get_effective_format (const Value *values) const
+  unsigned int get_effective_format (const Value *values, bool strip_hints, bool strip_empty, const ValueBase *base,
+                                     const hb_hashmap_t> *varidx_delta_map) const
   {
     unsigned int format = *this;
     for (unsigned flag = xPlacement; flag <= yAdvDevice; flag = flag << 1) {
-      if (format & flag) should_drop (*values++, (Flags) flag, &format);
+      if (format & flag)
+      {
+        if (strip_hints && flag >= xPlaDevice)
+        {
+          format = format & ~flag;
+          values++;
+          continue;
+        }
+        if (varidx_delta_map && flag >= xPlaDevice)
+        {
+          update_var_flag (values++, (Flags) flag, &format, base, varidx_delta_map);
+          continue;
+        }
+        /* do not strip empty when instancing, cause we don't know whether the new
+         * default value is 0 or not */
+        if (strip_empty) should_drop (*values, (Flags) flag, &format);
+        values++;
+      }
     }
 
     return format;
@@ -154,18 +174,19 @@ struct ValueFormat : HBUINT16
 
   template
-  unsigned int get_effective_format (Iterator it) const {
+  unsigned int get_effective_format (Iterator it, bool strip_hints, bool strip_empty, const ValueBase *base,
+                                     const hb_hashmap_t> *varidx_delta_map) const {
     unsigned int new_format = 0;
 
     for (const hb_array_t& values : it)
-      new_format = new_format | get_effective_format (&values);
+      new_format = new_format | get_effective_format (&values, strip_hints, strip_empty, base, varidx_delta_map);
 
     return new_format;
   }
 
   void copy_values (hb_serialize_context_t *c,
                     unsigned int new_format,
-                    const void *base,
+                    const ValueBase *base,
                     const Value *values,
                     const hb_hashmap_t> *layout_variation_idx_delta_map) const
   {
@@ -217,7 +238,7 @@ struct ValueFormat : HBUINT16
   }
 
   void collect_variation_indices (hb_collect_variation_indices_context_t *c,
-                                  const void *base,
+                                  const ValueBase *base,
                                   const hb_array_t& values) const
   {
     unsigned format = *this;
@@ -251,17 +272,8 @@ struct ValueFormat : HBUINT16
     }
   }
 
-  unsigned drop_device_table_flags () const
-  {
-    unsigned format = *this;
-    for (unsigned flag = xPlaDevice; flag <= yAdvDevice; flag = flag << 1)
-      format = format & ~flag;
-
-    return format;
-  }
-
   private:
-  bool sanitize_value_devices (hb_sanitize_context_t *c, const void *base, const Value *values) const
+  bool sanitize_value_devices (hb_sanitize_context_t *c, const ValueBase *base, const Value *values) const
   {
     unsigned int format = *this;
 
@@ -278,17 +290,17 @@ struct ValueFormat : HBUINT16
     return true;
   }
 
-  static inline Offset16To& get_device (Value* value)
+  static inline Offset16To& get_device (Value* value)
   {
-    return *static_cast *> (value);
+    return *static_cast *> (value);
   }
-  static inline const Offset16To& get_device (const Value* value)
+  static inline const Offset16To& get_device (const Value* value)
   {
-    return *static_cast *> (value);
+    return *static_cast *> (value);
   }
   static inline const Device& get_device (const Value* value,
                                           bool *worked,
-                                          const void *base,
+                                          const ValueBase *base,
                                           hb_sanitize_context_t &c)
   {
     if (worked) *worked |= bool (*value);
@@ -296,12 +308,13 @@ struct ValueFormat : HBUINT16
 
     if (unlikely (!offset.sanitize (&c, base)))
       return Null(Device);
+    hb_barrier ();
 
     return base + offset;
   }
 
   void add_delta_to_value (HBINT16 *value,
-                           const void *base,
+                           const ValueBase *base,
                            const Value *src_value,
                            const hb_hashmap_t> *layout_variation_idx_delta_map) const
   {
@@ -313,7 +326,8 @@ struct ValueFormat : HBUINT16
     *value += hb_second (*varidx_delta);
   }
 
-  bool copy_device (hb_serialize_context_t *c, const void *base,
+  bool copy_device (hb_serialize_context_t *c,
+                    const ValueBase *base,
                     const Value *src_value,
                     const hb_hashmap_t> *layout_variation_idx_delta_map,
                     unsigned int new_format, Flags flag) const
@@ -354,7 +368,7 @@ struct ValueFormat : HBUINT16
     return (format & devices) != 0;
   }
 
-  bool sanitize_value (hb_sanitize_context_t *c, const void *base, const Value *values) const
+  bool sanitize_value (hb_sanitize_context_t *c, const ValueBase *base, const Value *values) const
   {
     TRACE_SANITIZE (this);
 
@@ -366,7 +380,7 @@ struct ValueFormat : HBUINT16
     return_trace (!has_device () || sanitize_value_devices (c, base, values));
   }
 
-  bool sanitize_values (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count) const
+  bool sanitize_values (hb_sanitize_context_t *c, const ValueBase *base, const Value *values, unsigned int count) const
   {
     TRACE_SANITIZE (this);
     unsigned size = get_size ();
@@ -376,11 +390,12 @@ struct ValueFormat : HBUINT16
     if (c->lazy_some_gpos)
       return_trace (true);
 
+    hb_barrier ();
     return_trace (sanitize_values_stride_unsafe (c, base, values, count, size));
   }
 
   /* Just sanitize referenced Device tables.  Doesn't check the values themselves. */
-  bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count, unsigned int stride) const
+  bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, const ValueBase *base, const Value *values, unsigned int count, unsigned int stride) const
   {
     TRACE_SANITIZE (this);
 
@@ -403,6 +418,20 @@ struct ValueFormat : HBUINT16
     *format = *format & ~flag;
   }
 
+  void update_var_flag (const Value* value, Flags flag,
+                        unsigned int* format, const ValueBase *base,
+                        const hb_hashmap_t> *varidx_delta_map) const
+  {
+    if (*value)
+    {
+      unsigned varidx = (base + get_device (value)).get_variation_index ();
+      hb_pair_t *varidx_delta;
+      if (varidx_delta_map->has (varidx, &varidx_delta) &&
+          varidx_delta->first != HB_OT_LAYOUT_NO_VARIATIONS_INDEX)
+        return;
+    }
+    *format = *format & ~flag;
+  }
 };
 
 }
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSubstFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSubstFormat1.hh
index adec65d58646f..421a6e0662788 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSubstFormat1.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSubstFormat1.hh
@@ -74,7 +74,7 @@ struct AlternateSubstFormat1_2
     TRACE_APPLY (this);
 
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+    if (index == NOT_COVERED) return_trace (false);
 
     return_trace ((this+alternateSet[index]).apply (c));
   }
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Ligature.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Ligature.hh
index de4a111b46cfb..726da458fac35 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Ligature.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Ligature.hh
@@ -90,8 +90,17 @@ struct Ligature
 
     unsigned int total_component_count = 0;
 
+    if (unlikely (count > HB_MAX_CONTEXT_LENGTH)) return false;
+    unsigned match_positions_stack[4];
+    unsigned *match_positions = match_positions_stack;
+    if (unlikely (count > ARRAY_LENGTH (match_positions_stack)))
+    {
+      match_positions = (unsigned *) hb_malloc (hb_max (count, 1u) * sizeof (unsigned));
+      if (unlikely (!match_positions))
+        return_trace (false);
+    }
+
     unsigned int match_end = 0;
-    unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
 
     if (likely (!match_input (c, count,
                               &component[1],
@@ -102,6 +111,8 @@ struct Ligature
                               &total_component_count)))
     {
       c->buffer->unsafe_to_concat (c->buffer->idx, match_end);
+      if (match_positions != match_positions_stack)
+        hb_free (match_positions);
       return_trace (false);
     }
 
@@ -145,6 +156,8 @@ struct Ligature
                           pos);
     }
 
+    if (match_positions != match_positions_stack)
+      hb_free (match_positions);
     return_trace (true);
   }
 
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSubstFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSubstFormat1.hh
index 5c7df97d13aea..6ae24b3375403 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSubstFormat1.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSubstFormat1.hh
@@ -78,12 +78,49 @@ struct LigatureSubstFormat1_2
     return lig_set.would_apply (c);
   }
 
-  bool apply (hb_ot_apply_context_t *c) const
+  unsigned cache_cost () const
   {
-    TRACE_APPLY (this);
+    return (this+coverage).cost ();
+  }
+  static void * cache_func (void *p, hb_ot_lookup_cache_op_t op)
+  {
+    switch (op)
+    {
+      case hb_ot_lookup_cache_op_t::CREATE:
+      {
+        hb_ot_lookup_cache_t *cache = (hb_ot_lookup_cache_t *) hb_malloc (sizeof (hb_ot_lookup_cache_t));
+        if (likely (cache))
+          cache->clear ();
+        return cache;
+      }
+      case hb_ot_lookup_cache_op_t::ENTER:
+        return (void *) true;
+      case hb_ot_lookup_cache_op_t::LEAVE:
+        return nullptr;
+      case hb_ot_lookup_cache_op_t::DESTROY:
+      {
+        hb_ot_lookup_cache_t *cache = (hb_ot_lookup_cache_t *) p;
+        hb_free (cache);
+        return nullptr;
+      }
+    }
+    return nullptr;
+  }
 
-    unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+  bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); }
+  bool apply (hb_ot_apply_context_t *c) const { return _apply (c, false); }
+  bool _apply (hb_ot_apply_context_t *c, bool cached) const
+  {
+    TRACE_APPLY (this);
+    hb_buffer_t *buffer = c->buffer;
+
+#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+    hb_ot_lookup_cache_t *cache = cached ? (hb_ot_lookup_cache_t *) c->lookup_accel->cache : nullptr;
+    unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint, cache);
+#else
+    unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
+#endif
+    if (index == NOT_COVERED) return_trace (false);
 
     const auto &lig_set = this+ligatureSet[index];
     return_trace (lig_set.apply (c));
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/MultipleSubstFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/MultipleSubstFormat1.hh
index 4a9972c29cc51..aec8d0f2c07f8 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/MultipleSubstFormat1.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/MultipleSubstFormat1.hh
@@ -66,7 +66,7 @@ struct MultipleSubstFormat1_2
     TRACE_APPLY (this);
 
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+    if (index == NOT_COVERED) return_trace (false);
 
     return_trace ((this+sequence[index]).apply (c));
   }
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh
index ec6dfa476476c..400a9e737911f 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh
@@ -33,9 +33,11 @@ struct ReverseChainSingleSubstFormat1
     TRACE_SANITIZE (this);
     if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this)))
       return_trace (false);
+    hb_barrier ();
     const auto &lookahead = StructAfter (backtrack);
     if (!lookahead.sanitize (c, this))
       return_trace (false);
+    hb_barrier ();
     const auto &substitute = StructAfter (lookahead);
     return_trace (substitute.sanitize (c));
   }
@@ -109,12 +111,12 @@ struct ReverseChainSingleSubstFormat1
   bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
+    unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
+    if (index == NOT_COVERED) return_trace (false);
+
     if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL))
       return_trace (false); /* No chaining to this type */
 
-    unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
-
     const auto &lookahead = StructAfter (backtrack);
     const auto &substitute = StructAfter (lookahead);
 
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat1.hh
index 268487c5ae770..be6cd820d2842 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat1.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat1.hh
@@ -128,7 +128,7 @@ struct SingleSubstFormat1_3
     TRACE_APPLY (this);
     hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
     unsigned int index = (this+coverage).get_coverage (glyph_id);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+    if (index == NOT_COVERED) return_trace (false);
 
     hb_codepoint_t d = deltaGlyphID;
     hb_codepoint_t mask = get_mask ();
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat2.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat2.hh
index 518900116710e..e909646045111 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat2.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat2.hh
@@ -104,7 +104,7 @@ struct SingleSubstFormat2_4
   {
     TRACE_APPLY (this);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+    if (index == NOT_COVERED) return_trace (false);
 
     if (unlikely (index >= substitute.len)) return_trace (false);
 
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/types.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/types.hh
index 6a43403e94b2d..527f64114b406 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/types.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/types.hh
@@ -29,6 +29,9 @@
 #ifndef OT_LAYOUT_TYPES_HH
 #define OT_LAYOUT_TYPES_HH
 
+using hb_ot_lookup_cache_t = hb_cache_t<15, 8, 7>;
+static_assert (sizeof (hb_ot_lookup_cache_t) == 256, "");
+
 namespace OT {
 namespace Layout {
 
@@ -38,8 +41,8 @@ struct SmallTypes {
   using HBUINT = HBUINT16;
   using HBGlyphID = HBGlyphID16;
   using Offset = Offset16;
-  template 
-  using OffsetTo = OT::Offset16To;
+  template 
+  using OffsetTo = OT::Offset16To;
   template 
   using ArrayOf = OT::Array16Of;
   template 
@@ -52,8 +55,8 @@ struct MediumTypes {
   using HBUINT = HBUINT24;
   using HBGlyphID = HBGlyphID24;
   using Offset = Offset24;
-  template 
-  using OffsetTo = OT::Offset24To;
+  template 
+  using OffsetTo = OT::Offset24To;
   template 
   using ArrayOf = OT::Array24Of;
   template 
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Var/VARC/VARC.hh b/src/java.desktop/share/native/libharfbuzz/OT/Var/VARC/VARC.hh
new file mode 100644
index 0000000000000..2ea1b6bca3289
--- /dev/null
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Var/VARC/VARC.hh
@@ -0,0 +1,264 @@
+#ifndef OT_VAR_VARC_VARC_HH
+#define OT_VAR_VARC_VARC_HH
+
+#include "../../../hb-decycler.hh"
+#include "../../../hb-geometry.hh"
+#include "../../../hb-ot-layout-common.hh"
+#include "../../../hb-ot-glyf-table.hh"
+#include "../../../hb-ot-cff2-table.hh"
+#include "../../../hb-ot-cff1-table.hh"
+
+#include "coord-setter.hh"
+
+namespace OT {
+
+//namespace Var {
+
+/*
+ * VARC -- Variable Composites
+ * https://github.com/harfbuzz/boring-expansion-spec/blob/main/VARC.md
+ */
+
+#ifndef HB_NO_VAR_COMPOSITES
+
+struct hb_varc_scratch_t
+{
+  hb_vector_t axisIndices;
+  hb_vector_t axisValues;
+  hb_glyf_scratch_t glyf_scratch;
+};
+
+struct hb_varc_context_t
+{
+  hb_font_t *font;
+  hb_draw_session_t *draw_session;
+  hb_extents_t *extents;
+  mutable hb_decycler_t decycler;
+  mutable signed edges_left;
+  mutable signed depth_left;
+  hb_varc_scratch_t &scratch;
+};
+
+struct VarComponent
+{
+  enum class flags_t : uint32_t
+  {
+    RESET_UNSPECIFIED_AXES      = 1u << 0,
+    HAVE_AXES                   = 1u << 1,
+    AXIS_VALUES_HAVE_VARIATION  = 1u << 2,
+    TRANSFORM_HAS_VARIATION     = 1u << 3,
+    HAVE_TRANSLATE_X            = 1u << 4,
+    HAVE_TRANSLATE_Y            = 1u << 5,
+    HAVE_ROTATION               = 1u << 6,
+    HAVE_CONDITION              = 1u << 7,
+    HAVE_SCALE_X                = 1u << 8,
+    HAVE_SCALE_Y                = 1u << 9,
+    HAVE_TCENTER_X              = 1u << 10,
+    HAVE_TCENTER_Y              = 1u << 11,
+    GID_IS_24BIT                = 1u << 12,
+    HAVE_SKEW_X                 = 1u << 13,
+    HAVE_SKEW_Y                 = 1u << 14,
+    RESERVED_MASK               = ~((1u << 15) - 1),
+  };
+
+  HB_INTERNAL hb_ubytes_t
+  get_path_at (const hb_varc_context_t &c,
+               hb_codepoint_t parent_gid,
+               hb_array_t coords,
+               hb_transform_t transform,
+               hb_ubytes_t record,
+               VarRegionList::cache_t *cache = nullptr) const;
+};
+
+struct VarCompositeGlyph
+{
+  static void
+  get_path_at (const hb_varc_context_t &c,
+               hb_codepoint_t gid,
+               hb_array_t coords,
+               hb_transform_t transform,
+               hb_ubytes_t record,
+               VarRegionList::cache_t *cache)
+  {
+    while (record)
+    {
+      const VarComponent &comp = * (const VarComponent *) (record.arrayZ);
+      record = comp.get_path_at (c,
+                                 gid,
+                                 coords, transform,
+                                 record,
+                                 cache);
+    }
+  }
+};
+
+HB_MARK_AS_FLAG_T (VarComponent::flags_t);
+
+struct VARC
+{
+  friend struct VarComponent;
+
+  static constexpr hb_tag_t tableTag = HB_TAG ('V', 'A', 'R', 'C');
+
+  HB_INTERNAL bool
+  get_path_at (const hb_varc_context_t &c,
+               hb_codepoint_t gid,
+               hb_array_t coords,
+               hb_transform_t transform = HB_TRANSFORM_IDENTITY,
+               hb_codepoint_t parent_gid = HB_CODEPOINT_INVALID,
+               VarRegionList::cache_t *parent_cache = nullptr) const;
+
+  bool
+  get_path (hb_font_t *font,
+            hb_codepoint_t gid,
+            hb_draw_session_t &draw_session,
+            hb_varc_scratch_t &scratch) const
+  {
+    hb_varc_context_t c {font,
+                         &draw_session,
+                         nullptr,
+                         hb_decycler_t {},
+                         HB_MAX_GRAPH_EDGE_COUNT,
+                         HB_MAX_NESTING_LEVEL,
+                         scratch};
+
+    return get_path_at (c, gid,
+                        hb_array (font->coords, font->num_coords));
+  }
+
+  bool
+  get_extents (hb_font_t *font,
+               hb_codepoint_t gid,
+               hb_extents_t *extents,
+               hb_varc_scratch_t &scratch) const
+  {
+    hb_varc_context_t c {font,
+                         nullptr,
+                         extents,
+                         hb_decycler_t {},
+                         HB_MAX_GRAPH_EDGE_COUNT,
+                         HB_MAX_NESTING_LEVEL,
+                         scratch};
+
+    return get_path_at (c, gid,
+                        hb_array (font->coords, font->num_coords));
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (version.sanitize (c) &&
+                  hb_barrier () &&
+                  version.major == 1 &&
+                  coverage.sanitize (c, this) &&
+                  varStore.sanitize (c, this) &&
+                  conditionList.sanitize (c, this) &&
+                  axisIndicesList.sanitize (c, this) &&
+                  glyphRecords.sanitize (c, this));
+  }
+
+  struct accelerator_t
+  {
+    friend struct VarComponent;
+
+    accelerator_t (hb_face_t *face)
+    {
+      table = hb_sanitize_context_t ().reference_table (face);
+    }
+    ~accelerator_t ()
+    {
+      auto *scratch = cached_scratch.get_relaxed ();
+      if (scratch)
+      {
+        scratch->~hb_varc_scratch_t ();
+        hb_free (scratch);
+      }
+
+      table.destroy ();
+    }
+
+    bool
+    get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session) const
+    {
+      if (!table->has_data ()) return false;
+
+      auto *scratch = acquire_scratch ();
+      if (unlikely (!scratch)) return true;
+      bool ret = table->get_path (font, gid, draw_session, *scratch);
+      release_scratch (scratch);
+      return ret;
+    }
+
+    bool
+    get_extents (hb_font_t *font,
+                 hb_codepoint_t gid,
+                 hb_glyph_extents_t *extents) const
+    {
+      if (!table->has_data ()) return false;
+
+      hb_extents_t f_extents;
+
+      auto *scratch = acquire_scratch ();
+      if (unlikely (!scratch)) return true;
+      bool ret = table->get_extents (font, gid, &f_extents, *scratch);
+      release_scratch (scratch);
+
+      if (ret)
+        *extents = f_extents.to_glyph_extents (font->x_scale < 0, font->y_scale < 0);
+
+      return ret;
+    }
+
+    private:
+
+    hb_varc_scratch_t *acquire_scratch () const
+    {
+      hb_varc_scratch_t *scratch = cached_scratch.get_acquire ();
+
+      if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr)))
+      {
+        scratch = (hb_varc_scratch_t *) hb_calloc (1, sizeof (hb_varc_scratch_t));
+        if (unlikely (!scratch))
+          return nullptr;
+      }
+
+      return scratch;
+    }
+    void release_scratch (hb_varc_scratch_t *scratch) const
+    {
+      if (!cached_scratch.cmpexch (nullptr, scratch))
+      {
+        scratch->~hb_varc_scratch_t ();
+        hb_free (scratch);
+      }
+    }
+
+    private:
+    hb_blob_ptr_t table;
+    mutable hb_atomic_t cached_scratch;
+  };
+
+  bool has_data () const { return version.major != 0; }
+
+  protected:
+  FixedVersion<> version; /* Version identifier */
+  Offset32To coverage;
+  Offset32To varStore;
+  Offset32To conditionList;
+  Offset32To axisIndicesList;
+  Offset32To*/> glyphRecords;
+  public:
+  DEFINE_SIZE_STATIC (24);
+};
+
+struct VARC_accelerator_t : VARC::accelerator_t {
+  VARC_accelerator_t (hb_face_t *face) : VARC::accelerator_t (face) {}
+};
+
+#endif
+
+//}
+
+}
+
+#endif  /* OT_VAR_VARC_VARC_HH */
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Var/VARC/coord-setter.hh b/src/java.desktop/share/native/libharfbuzz/OT/Var/VARC/coord-setter.hh
new file mode 100644
index 0000000000000..70c0968e1e405
--- /dev/null
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Var/VARC/coord-setter.hh
@@ -0,0 +1,63 @@
+#ifndef OT_VAR_VARC_COORD_SETTER_HH
+#define OT_VAR_VARC_COORD_SETTER_HH
+
+
+#include "../../../hb.hh"
+
+
+namespace OT {
+//namespace Var {
+
+
+struct coord_setter_t
+{
+  coord_setter_t (hb_array_t coords_)
+  {
+    length = coords_.length;
+    if (length <= ARRAY_LENGTH (static_coords))
+      hb_memcpy (static_coords, coords_.arrayZ, length * sizeof (int));
+    else
+      dynamic_coords.extend (coords_);
+  }
+
+  int& operator [] (unsigned idx)
+  {
+    if (unlikely (idx >= HB_VAR_COMPOSITE_MAX_AXES))
+      return Crap(int);
+
+    if (length <= ARRAY_LENGTH (static_coords))
+    {
+      if (idx < ARRAY_LENGTH (static_coords))
+      {
+        while (length <= idx)
+          static_coords[length++] = 0;
+        return static_coords[idx];
+      }
+      else
+        dynamic_coords.extend (hb_array (static_coords, length));
+    }
+
+    if (dynamic_coords.length <= idx)
+    {
+      if (unlikely (!dynamic_coords.resize (idx + 1)))
+        return Crap(int);
+      length = idx + 1;
+    }
+    return dynamic_coords.arrayZ[idx];
+  }
+
+  hb_array_t get_coords ()
+  { return length <= ARRAY_LENGTH (static_coords) ? hb_array (static_coords, length) : dynamic_coords.as_array (); }
+
+  private:
+  hb_vector_t dynamic_coords;
+  unsigned length;
+  int static_coords[sizeof (void *) * 8];
+};
+
+
+//} // namespace Var
+
+} // namespace OT
+
+#endif /* OT_VAR_VARC_COORD_SETTER_HH */
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/CompositeGlyph.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/CompositeGlyph.hh
index 151c1ac48cb7c..fb347770e6f42 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/CompositeGlyph.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/CompositeGlyph.hh
@@ -143,7 +143,7 @@ struct CompositeGlyphRecord
     float matrix[4];
     contour_point_t trans;
     get_transformation (matrix, trans);
-    if (unlikely (!points.alloc (points.length + 4))) return false; // For phantom points
+    if (unlikely (!points.alloc (points.length + 1 + 4))) return false; // For phantom points
     points.push (trans);
     return true;
   }
@@ -240,7 +240,8 @@ struct CompositeGlyphRecord
     }
     if (is_anchored ()) tx = ty = 0;
 
-    trans.init ((float) tx, (float) ty);
+    /* set is_end_point flag to true, used by IUP delta optimization */
+    trans.init ((float) tx, (float) ty, true);
 
     {
       const F2DOT14 *points = (const F2DOT14 *) p;
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/Glyph.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/Glyph.hh
index b295e41510fd8..1805df262aa04 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/Glyph.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/Glyph.hh
@@ -7,8 +7,6 @@
 #include "GlyphHeader.hh"
 #include "SimpleGlyph.hh"
 #include "CompositeGlyph.hh"
-#include "VarCompositeGlyph.hh"
-#include "coord-setter.hh"
 
 
 namespace OT {
@@ -33,9 +31,6 @@ struct Glyph
     EMPTY,
     SIMPLE,
     COMPOSITE,
-#ifndef HB_NO_VAR_COMPOSITES
-    VAR_COMPOSITE,
-#endif
   };
 
   public:
@@ -44,22 +39,10 @@ struct Glyph
     if (type != COMPOSITE) return composite_iter_t ();
     return CompositeGlyph (*header, bytes).iter ();
   }
-  var_composite_iter_t get_var_composite_iterator () const
-  {
-#ifndef HB_NO_VAR_COMPOSITES
-    if (type != VAR_COMPOSITE) return var_composite_iter_t ();
-    return VarCompositeGlyph (*header, bytes).iter ();
-#else
-    return var_composite_iter_t ();
-#endif
-  }
 
   const hb_bytes_t trim_padding () const
   {
     switch (type) {
-#ifndef HB_NO_VAR_COMPOSITES
-    case VAR_COMPOSITE: return VarCompositeGlyph (*header, bytes).trim_padding ();
-#endif
     case COMPOSITE: return CompositeGlyph (*header, bytes).trim_padding ();
     case SIMPLE:    return SimpleGlyph (*header, bytes).trim_padding ();
     case EMPTY:     return bytes;
@@ -70,9 +53,6 @@ struct Glyph
   void drop_hints ()
   {
     switch (type) {
-#ifndef HB_NO_VAR_COMPOSITES
-    case VAR_COMPOSITE: return; // No hinting
-#endif
     case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints (); return;
     case SIMPLE:    SimpleGlyph (*header, bytes).drop_hints (); return;
     case EMPTY:     return;
@@ -82,9 +62,6 @@ struct Glyph
   void set_overlaps_flag ()
   {
     switch (type) {
-#ifndef HB_NO_VAR_COMPOSITES
-    case VAR_COMPOSITE: return; // No overlaps flag
-#endif
     case COMPOSITE: CompositeGlyph (*header, bytes).set_overlaps_flag (); return;
     case SIMPLE:    SimpleGlyph (*header, bytes).set_overlaps_flag (); return;
     case EMPTY:     return;
@@ -94,15 +71,15 @@ struct Glyph
   void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const
   {
     switch (type) {
-#ifndef HB_NO_VAR_COMPOSITES
-    case VAR_COMPOSITE: return; // No hinting
-#endif
     case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints_bytes (dest_start); return;
     case SIMPLE:    SimpleGlyph (*header, bytes).drop_hints_bytes (dest_start, dest_end); return;
     case EMPTY:     return;
     }
   }
 
+  bool is_composite () const
+  { return type == COMPOSITE; }
+
   bool get_all_points_without_var (const hb_face_t *face,
                                    contour_point_vector_t &points /* OUT */) const
   {
@@ -117,14 +94,6 @@ struct Glyph
         if (unlikely (!item.get_points (points))) return false;
       break;
     }
-#ifndef HB_NO_VAR_COMPOSITES
-    case VAR_COMPOSITE:
-    {
-      for (auto &item : get_var_composite_iterator ())
-        if (unlikely (!item.get_points (points))) return false;
-      break;
-    }
-#endif
     case EMPTY:
       break;
     }
@@ -282,7 +251,8 @@ struct Glyph
       composite_contours_p = nullptr;
     }
 
-    if (!get_points (font, glyf, all_points, &points_with_deltas, head_maxp_info_p, composite_contours_p, false, false))
+    hb_glyf_scratch_t scratch;
+    if (!get_points (font, glyf, all_points, scratch, &points_with_deltas, head_maxp_info_p, composite_contours_p, false, false))
       return false;
 
     // .notdef, set type to empty so we only update metrics and don't compile bytes for
@@ -300,13 +270,6 @@ struct Glyph
     {
       switch (type)
       {
-#ifndef HB_NO_VAR_COMPOSITES
-      case VAR_COMPOSITE:
-        // TODO
-        dest_end = hb_bytes_t ();
-        break;
-#endif
-
       case COMPOSITE:
         if (!CompositeGlyph (*header, bytes).compile_bytes_with_deltas (dest_start,
                                                                         points_with_deltas,
@@ -343,27 +306,23 @@ struct Glyph
   template 
   bool get_points (hb_font_t *font, const accelerator_t &glyf_accelerator,
                    contour_point_vector_t &all_points /* OUT */,
+                   hb_glyf_scratch_t &scratch,
                    contour_point_vector_t *points_with_deltas = nullptr, /* OUT */
                    head_maxp_info_t * head_maxp_info = nullptr, /* OUT */
                    unsigned *composite_contours = nullptr, /* OUT */
                    bool shift_points_hori = true,
                    bool use_my_metrics = true,
                    bool phantom_only = false,
-                   hb_array_t coords = hb_array_t (),
-                   hb_map_t *current_glyphs = nullptr,
+                   hb_array_t coords = hb_array_t (),
                    unsigned int depth = 0,
                    unsigned *edge_count = nullptr) const
   {
     if (unlikely (depth > HB_MAX_NESTING_LEVEL)) return false;
     unsigned stack_edge_count = 0;
     if (!edge_count) edge_count = &stack_edge_count;
-    if (unlikely (*edge_count > HB_GLYF_MAX_EDGE_COUNT)) return false;
+    if (unlikely (*edge_count > HB_MAX_GRAPH_EDGE_COUNT)) return false;
     (*edge_count)++;
 
-    hb_map_t current_glyphs_stack;
-    if (current_glyphs == nullptr)
-      current_glyphs = ¤t_glyphs_stack;
-
     if (head_maxp_info)
     {
       head_maxp_info->maxComponentDepth = hb_max (head_maxp_info->maxComponentDepth, depth);
@@ -372,8 +331,7 @@ struct Glyph
     if (!coords)
       coords = hb_array (font->coords, font->num_coords);
 
-    contour_point_vector_t stack_points;
-    contour_point_vector_t &points = type == SIMPLE ? all_points : stack_points;
+    contour_point_vector_t &points = type == SIMPLE ? all_points : scratch.comp_points;
     unsigned old_length = points.length;
 
     switch (type) {
@@ -391,14 +349,6 @@ struct Glyph
         if (unlikely (!item.get_points (points))) return false;
       break;
     }
-#ifndef HB_NO_VAR_COMPOSITES
-    case VAR_COMPOSITE:
-    {
-      for (auto &item : get_var_composite_iterator ())
-        if (unlikely (!item.get_points (points))) return false;
-      break;
-    }
-#endif
     case EMPTY:
       break;
     }
@@ -434,36 +384,53 @@ struct Glyph
 
 #ifndef HB_NO_VAR
     if (coords)
-      glyf_accelerator.gvar->apply_deltas_to_points (gid,
-                                                     coords,
-                                                     points.as_array ().sub_array (old_length),
-                                                     phantom_only && type == SIMPLE);
+    {
+#ifndef HB_NO_BEYOND_64K
+      if (glyf_accelerator.GVAR->has_data ())
+        glyf_accelerator.GVAR->apply_deltas_to_points (gid,
+                                                       coords,
+                                                       points.as_array ().sub_array (old_length),
+                                                       scratch,
+                                                       phantom_only && type == SIMPLE);
+      else
+#endif
+        glyf_accelerator.gvar->apply_deltas_to_points (gid,
+                                                       coords,
+                                                       points.as_array ().sub_array (old_length),
+                                                       scratch,
+                                                       phantom_only && type == SIMPLE);
+    }
 #endif
 
     // mainly used by CompositeGlyph calculating new X/Y offset value so no need to extend it
     // with child glyphs' points
     if (points_with_deltas != nullptr && depth == 0 && type == COMPOSITE)
     {
-      if (unlikely (!points_with_deltas->resize (points.length))) return false;
+      assert (old_length == 0);
       *points_with_deltas = points;
     }
 
+    float shift = 0;
     switch (type) {
     case SIMPLE:
       if (depth == 0 && head_maxp_info)
         head_maxp_info->maxPoints = hb_max (head_maxp_info->maxPoints, all_points.length - old_length - 4);
+      shift = phantoms[PHANTOM_LEFT].x;
       break;
     case COMPOSITE:
     {
+      hb_decycler_node_t decycler_node (scratch.decycler);
+
       unsigned int comp_index = 0;
       for (auto &item : get_composite_iterator ())
       {
         hb_codepoint_t item_gid = item.get_gid ();
 
-        if (unlikely (current_glyphs->has (item_gid)))
+        if (unlikely (!decycler_node.visit (item_gid)))
+        {
+          comp_index++;
           continue;
-
-        current_glyphs->add (item_gid);
+        }
 
         unsigned old_count = all_points.length;
 
@@ -472,6 +439,7 @@ struct Glyph
                                        .get_points (font,
                                                     glyf_accelerator,
                                                     all_points,
+                                                    scratch,
                                                     points_with_deltas,
                                                     head_maxp_info,
                                                     composite_contours,
@@ -479,14 +447,16 @@ struct Glyph
                                                     use_my_metrics,
                                                     phantom_only,
                                                     coords,
-                                                    current_glyphs,
                                                     depth + 1,
                                                     edge_count)))
         {
-          current_glyphs->del (item_gid);
+          points.resize (old_length);
           return false;
         }
 
+        // points might have been reallocated. Relocate phantoms.
+        phantoms = points.as_array ().sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT);
+
         auto comp_points = all_points.as_array ().sub_array (old_count);
 
         /* Copy phantom points from component if USE_MY_METRICS flag set */
@@ -501,7 +471,7 @@ struct Glyph
           item.get_transformation (matrix, default_trans);
 
           /* Apply component transformation & translation (with deltas applied) */
-          item.transform_points (comp_points, matrix, points[comp_index]);
+          item.transform_points (comp_points, matrix, points[old_length + comp_index]);
         }
 
         if (item.is_anchored () && !phantom_only)
@@ -522,12 +492,11 @@ struct Glyph
 
         if (all_points.length > HB_GLYF_MAX_POINTS)
         {
-          current_glyphs->del (item_gid);
+          points.resize (old_length);
           return false;
         }
 
         comp_index++;
-        current_glyphs->del (item_gid);
       }
 
       if (head_maxp_info && depth == 0)
@@ -538,84 +507,13 @@ struct Glyph
         head_maxp_info->maxComponentElements = hb_max (head_maxp_info->maxComponentElements, comp_index);
       }
       all_points.extend (phantoms);
+      shift = phantoms[PHANTOM_LEFT].x;
+      points.resize (old_length);
     } break;
-#ifndef HB_NO_VAR_COMPOSITES
-    case VAR_COMPOSITE:
-    {
-      hb_array_t points_left = points.as_array ();
-      for (auto &item : get_var_composite_iterator ())
-      {
-        hb_codepoint_t item_gid = item.get_gid ();
-
-        if (unlikely (current_glyphs->has (item_gid)))
-          continue;
-
-        current_glyphs->add (item_gid);
-
-        unsigned item_num_points = item.get_num_points ();
-        hb_array_t record_points = points_left.sub_array (0, item_num_points);
-        assert (record_points.length == item_num_points);
-
-        auto component_coords = coords;
-        /* Copying coords is expensive; so we have put an arbitrary
-         * limit on the max number of coords for now. */
-        if (item.is_reset_unspecified_axes () ||
-            coords.length > HB_GLYF_VAR_COMPOSITE_MAX_AXES)
-          component_coords = hb_array ();
-
-        coord_setter_t coord_setter (component_coords);
-        item.set_variations (coord_setter, record_points);
-
-        unsigned old_count = all_points.length;
-
-        if (unlikely ((!phantom_only || (use_my_metrics && item.is_use_my_metrics ())) &&
-                      !glyf_accelerator.glyph_for_gid (item_gid)
-                                       .get_points (font,
-                                                    glyf_accelerator,
-                                                    all_points,
-                                                    points_with_deltas,
-                                                    head_maxp_info,
-                                                    nullptr,
-                                                    shift_points_hori,
-                                                    use_my_metrics,
-                                                    phantom_only,
-                                                    coord_setter.get_coords (),
-                                                    current_glyphs,
-                                                    depth + 1,
-                                                    edge_count)))
-        {
-          current_glyphs->del (item_gid);
-          return false;
-        }
-
-        auto comp_points = all_points.as_array ().sub_array (old_count);
-
-        /* Apply component transformation */
-        if (comp_points) // Empty in case of phantom_only
-          item.transform_points (record_points, comp_points);
-
-        /* Copy phantom points from component if USE_MY_METRICS flag set */
-        if (use_my_metrics && item.is_use_my_metrics ())
-          for (unsigned int i = 0; i < PHANTOM_COUNT; i++)
-            phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i];
-
-        all_points.resize (all_points.length - PHANTOM_COUNT);
-
-        if (all_points.length > HB_GLYF_MAX_POINTS)
-        {
-          current_glyphs->del (item_gid);
-          return false;
-        }
-
-        points_left += item_num_points;
-
-        current_glyphs->del (item_gid);
-      }
-      all_points.extend (phantoms);
-    } break;
-#endif
     case EMPTY:
       all_points.extend (phantoms);
+      shift = phantoms[PHANTOM_LEFT].x;
+      points.resize (old_length);
       break;
     }
 
@@ -624,10 +522,9 @@ struct Glyph
       /* Undocumented rasterizer behavior:
        * Shift points horizontally by the updated left side bearing
        */
-      int v = -phantoms[PHANTOM_LEFT].x;
-      if (v)
+      if (shift)
         for (auto &point : all_points)
-          point.x += v;
+          point.x -= shift;
     }
 
     return !all_points.in_error ();
@@ -658,10 +555,7 @@ struct Glyph
     int num_contours = header->numberOfContours;
     if (unlikely (num_contours == 0)) type = EMPTY;
     else if (num_contours > 0) type = SIMPLE;
-    else if (num_contours == -1) type = COMPOSITE;
-#ifndef HB_NO_VAR_COMPOSITES
-    else if (num_contours == -2) type = VAR_COMPOSITE;
-#endif
+    else if (num_contours <= -1) type = COMPOSITE;
     else type = EMPTY; // Spec deviation; Spec says COMPOSITE, but not seen in the wild.
   }
 
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/SimpleGlyph.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/SimpleGlyph.hh
index 5088397266d24..601e1303792c2 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/SimpleGlyph.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/SimpleGlyph.hh
@@ -127,19 +127,20 @@ struct SimpleGlyph
                           hb_array_t points_ /* IN/OUT */,
                           const HBUINT8 *end)
   {
+    auto *points = points_.arrayZ;
     unsigned count = points_.length;
     for (unsigned int i = 0; i < count;)
     {
       if (unlikely (p + 1 > end)) return false;
       uint8_t flag = *p++;
-      points_.arrayZ[i++].flag = flag;
+      points[i++].flag = flag;
       if (flag & FLAG_REPEAT)
       {
         if (unlikely (p + 1 > end)) return false;
         unsigned int repeat_count = *p++;
         unsigned stop = hb_min (i + repeat_count, count);
         for (; i < stop; i++)
-          points_.arrayZ[i].flag = flag;
+          points[i].flag = flag;
       }
     }
     return true;
@@ -160,10 +161,7 @@ struct SimpleGlyph
       if (flag & short_flag)
       {
         if (unlikely (p + 1 > end)) return false;
-        if (flag & same_flag)
-          v += *p++;
-        else
-          v -= *p++;
+        v += (bool(flag & same_flag) * 2 - 1) * *p++;
       }
       else
       {
@@ -190,7 +188,7 @@ struct SimpleGlyph
     unsigned int num_points = endPtsOfContours[num_contours - 1] + 1;
 
     unsigned old_length = points.length;
-    points.alloc (points.length + num_points + 4, true); // Allocate for phantom points, to avoid a possible copy
+    points.alloc (points.length + num_points + 4); // Allocate for phantom points, to avoid a possible copy
     if (unlikely (!points.resize (points.length + num_points, false))) return false;
     auto points_ = points.as_array ().sub_array (old_length);
     if (!phantom_only)
@@ -281,9 +279,9 @@ struct SimpleGlyph
     unsigned num_points = all_points.length - 4;
 
     hb_vector_t flags, x_coords, y_coords;
-    if (unlikely (!flags.alloc (num_points, true))) return false;
-    if (unlikely (!x_coords.alloc (2*num_points, true))) return false;
-    if (unlikely (!y_coords.alloc (2*num_points, true))) return false;
+    if (unlikely (!flags.alloc_exact (num_points))) return false;
+    if (unlikely (!x_coords.alloc_exact (2*num_points))) return false;
+    if (unlikely (!y_coords.alloc_exact (2*num_points))) return false;
 
     unsigned lastflag = 255, repeat = 0;
     int prev_x = 0, prev_y = 0;
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/SubsetGlyph.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/SubsetGlyph.hh
index 9c04d890d1abc..643b0226492ef 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/SubsetGlyph.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/SubsetGlyph.hh
@@ -53,23 +53,12 @@ struct SubsetGlyph
       if (plan->new_gid_for_old_gid (_.get_gid(), &new_gid))
         const_cast (_).set_gid (new_gid);
     }
-#ifndef HB_NO_VAR_COMPOSITES
-    for (auto &_ : Glyph (dest_glyph).get_var_composite_iterator ())
-    {
-      hb_codepoint_t new_gid;
-      if (plan->new_gid_for_old_gid (_.get_gid(), &new_gid))
-        const_cast (_).set_gid (new_gid);
-    }
-#endif
 
 #ifndef HB_NO_BEYOND_64K
     auto it = Glyph (dest_glyph).get_composite_iterator ();
     if (it)
     {
-      /* lower GID24 to GID16 in components if possible.
-       *
-       * TODO: VarComposite. Not as critical, since VarComposite supports
-       * gid24 from the first version. */
+      /* lower GID24 to GID16 in components if possible. */
       char *p = it ? (char *) &*it : nullptr;
       char *q = p;
       const char *end = dest_glyph.arrayZ + dest_glyph.length;
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/VarCompositeGlyph.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/VarCompositeGlyph.hh
deleted file mode 100644
index 4f29f0aab37c5..0000000000000
--- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/VarCompositeGlyph.hh
+++ /dev/null
@@ -1,401 +0,0 @@
-#ifndef OT_GLYF_VARCOMPOSITEGLYPH_HH
-#define OT_GLYF_VARCOMPOSITEGLYPH_HH
-
-
-#include "../../hb-open-type.hh"
-#include "coord-setter.hh"
-
-
-namespace OT {
-namespace glyf_impl {
-
-
-struct VarCompositeGlyphRecord
-{
-  protected:
-  enum var_composite_glyph_flag_t
-  {
-    USE_MY_METRICS              = 0x0001,
-    AXIS_INDICES_ARE_SHORT      = 0x0002,
-    UNIFORM_SCALE               = 0x0004,
-    HAVE_TRANSLATE_X            = 0x0008,
-    HAVE_TRANSLATE_Y            = 0x0010,
-    HAVE_ROTATION               = 0x0020,
-    HAVE_SCALE_X                = 0x0040,
-    HAVE_SCALE_Y                = 0x0080,
-    HAVE_SKEW_X                 = 0x0100,
-    HAVE_SKEW_Y                 = 0x0200,
-    HAVE_TCENTER_X              = 0x0400,
-    HAVE_TCENTER_Y              = 0x0800,
-    GID_IS_24BIT                = 0x1000,
-    AXES_HAVE_VARIATION         = 0x2000,
-    RESET_UNSPECIFIED_AXES      = 0x4000,
-  };
-
-  public:
-
-  unsigned int get_size () const
-  {
-    unsigned fl = flags;
-    unsigned int size = min_size;
-
-    unsigned axis_width = (fl & AXIS_INDICES_ARE_SHORT) ? 4 : 3;
-    size += numAxes * axis_width;
-
-    if (fl & GID_IS_24BIT)      size += 1;
-
-    // 2 bytes each for the following flags
-    fl = fl & (HAVE_TRANSLATE_X | HAVE_TRANSLATE_Y |
-               HAVE_ROTATION |
-               HAVE_SCALE_X | HAVE_SCALE_Y |
-               HAVE_SKEW_X | HAVE_SKEW_Y |
-               HAVE_TCENTER_X | HAVE_TCENTER_Y);
-    size += hb_popcount (fl) * 2;
-
-    return size;
-  }
-
-  bool has_more () const { return true; }
-
-  bool is_use_my_metrics () const { return flags & USE_MY_METRICS; }
-  bool is_reset_unspecified_axes () const { return flags & RESET_UNSPECIFIED_AXES; }
-
-  hb_codepoint_t get_gid () const
-  {
-    if (flags & GID_IS_24BIT)
-      return * (const HBGlyphID24 *) &pad;
-    else
-      return * (const HBGlyphID16 *) &pad;
-  }
-
-  void set_gid (hb_codepoint_t gid)
-  {
-    if (flags & GID_IS_24BIT)
-      * (HBGlyphID24 *) &pad = gid;
-    else
-      * (HBGlyphID16 *) &pad = gid;
-  }
-
-  unsigned get_numAxes () const
-  {
-    return numAxes;
-  }
-
-  unsigned get_num_points () const
-  {
-    unsigned fl = flags;
-    unsigned num = 0;
-    if (fl & AXES_HAVE_VARIATION)                       num += numAxes;
-
-    /* Hopefully faster code, relying on the value of the flags. */
-    fl = (((fl & (HAVE_TRANSLATE_Y | HAVE_SCALE_Y | HAVE_SKEW_Y | HAVE_TCENTER_Y)) >> 1) | fl) &
-         (HAVE_TRANSLATE_X | HAVE_ROTATION | HAVE_SCALE_X | HAVE_SKEW_X | HAVE_TCENTER_X);
-    num += hb_popcount (fl);
-    return num;
-
-    /* Slower but more readable code. */
-    if (fl & (HAVE_TRANSLATE_X | HAVE_TRANSLATE_Y))     num++;
-    if (fl & HAVE_ROTATION)                             num++;
-    if (fl & (HAVE_SCALE_X | HAVE_SCALE_Y))             num++;
-    if (fl & (HAVE_SKEW_X | HAVE_SKEW_Y))               num++;
-    if (fl & (HAVE_TCENTER_X | HAVE_TCENTER_Y))         num++;
-    return num;
-  }
-
-  void transform_points (hb_array_t record_points,
-                         hb_array_t points) const
-  {
-    float matrix[4];
-    contour_point_t trans;
-
-    get_transformation_from_points (record_points.arrayZ, matrix, trans);
-
-    auto arrayZ = points.arrayZ;
-    unsigned count = points.length;
-
-    if (matrix[0] != 1.f || matrix[1] != 0.f ||
-        matrix[2] != 0.f || matrix[3] != 1.f)
-      for (unsigned i = 0; i < count; i++)
-        arrayZ[i].transform (matrix);
-
-    if (trans.x != 0.f || trans.y != 0.f)
-      for (unsigned i = 0; i < count; i++)
-        arrayZ[i].translate (trans);
-  }
-
-  static inline void transform (float (&matrix)[4], contour_point_t &trans,
-                                float (other)[6])
-  {
-    // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L268
-    float xx1 = other[0];
-    float xy1 = other[1];
-    float yx1 = other[2];
-    float yy1 = other[3];
-    float dx1 = other[4];
-    float dy1 = other[5];
-    float xx2 = matrix[0];
-    float xy2 = matrix[1];
-    float yx2 = matrix[2];
-    float yy2 = matrix[3];
-    float dx2 = trans.x;
-    float dy2 = trans.y;
-
-    matrix[0] = xx1*xx2 + xy1*yx2;
-    matrix[1] = xx1*xy2 + xy1*yy2;
-    matrix[2] = yx1*xx2 + yy1*yx2;
-    matrix[3] = yx1*xy2 + yy1*yy2;
-    trans.x = xx2*dx1 + yx2*dy1 + dx2;
-    trans.y = xy2*dx1 + yy2*dy1 + dy2;
-  }
-
-  static void translate (float (&matrix)[4], contour_point_t &trans,
-                         float translateX, float translateY)
-  {
-    if (!translateX && !translateY)
-      return;
-
-    trans.x += matrix[0] * translateX + matrix[2] * translateY;
-    trans.y += matrix[1] * translateX + matrix[3] * translateY;
-  }
-
-  static void scale (float (&matrix)[4], contour_point_t &trans,
-                     float scaleX, float scaleY)
-  {
-    if (scaleX == 1.f && scaleY == 1.f)
-      return;
-
-    matrix[0] *= scaleX;
-    matrix[1] *= scaleX;
-    matrix[2] *= scaleY;
-    matrix[3] *= scaleY;
-  }
-
-  static void rotate (float (&matrix)[4], contour_point_t &trans,
-                      float rotation)
-  {
-    if (!rotation)
-      return;
-
-    // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L240
-    rotation = rotation * HB_PI;
-    float c;
-    float s;
-#ifdef HAVE_SINCOSF
-    sincosf (rotation, &s, &c);
-#else
-    c = cosf (rotation);
-    s = sinf (rotation);
-#endif
-    float other[6] = {c, s, -s, c, 0.f, 0.f};
-    transform (matrix, trans, other);
-  }
-
-  static void skew (float (&matrix)[4], contour_point_t &trans,
-                    float skewX, float skewY)
-  {
-    if (!skewX && !skewY)
-      return;
-
-    // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L255
-    skewX = skewX * HB_PI;
-    skewY = skewY * HB_PI;
-    float other[6] = {1.f,
-                      skewY ? tanf (skewY) : 0.f,
-                      skewX ? tanf (skewX) : 0.f,
-                      1.f,
-                      0.f, 0.f};
-    transform (matrix, trans, other);
-  }
-
-  bool get_points (contour_point_vector_t &points) const
-  {
-    unsigned num_points = get_num_points ();
-
-    points.alloc (points.length + num_points + 4); // For phantom points
-    if (unlikely (!points.resize (points.length + num_points, false))) return false;
-    contour_point_t *rec_points = points.arrayZ + (points.length - num_points);
-    hb_memset (rec_points, 0, num_points * sizeof (rec_points[0]));
-
-    unsigned fl = flags;
-
-    unsigned num_axes = numAxes;
-    unsigned axis_width = (fl & AXIS_INDICES_ARE_SHORT) ? 2 : 1;
-    unsigned axes_size = num_axes * axis_width;
-
-    const F2DOT14 *q = (const F2DOT14 *) (axes_size +
-                                          (fl & GID_IS_24BIT ? 3 : 2) +
-                                          (const HBUINT8 *) &pad);
-
-    unsigned count = num_axes;
-    if (fl & AXES_HAVE_VARIATION)
-    {
-      for (unsigned i = 0; i < count; i++)
-        rec_points++->x = q++->to_int ();
-    }
-    else
-      q += count;
-
-    const HBUINT16 *p = (const HBUINT16 *) q;
-
-    if (fl & (HAVE_TRANSLATE_X | HAVE_TRANSLATE_Y))
-    {
-      int translateX = (fl & HAVE_TRANSLATE_X) ? * (const FWORD *) p++ : 0;
-      int translateY = (fl & HAVE_TRANSLATE_Y) ? * (const FWORD *) p++ : 0;
-      rec_points->x = translateX;
-      rec_points->y = translateY;
-      rec_points++;
-    }
-    if (fl & HAVE_ROTATION)
-    {
-      int rotation = (fl & HAVE_ROTATION) ? ((const F4DOT12 *) p++)->to_int () : 0;
-      rec_points->x = rotation;
-      rec_points++;
-    }
-    if (fl & (HAVE_SCALE_X | HAVE_SCALE_Y))
-    {
-      int scaleX = (fl & HAVE_SCALE_X) ? ((const F6DOT10 *) p++)->to_int () : 1 << 10;
-      int scaleY = (fl & HAVE_SCALE_Y) ? ((const F6DOT10 *) p++)->to_int () : 1 << 10;
-      if ((fl & UNIFORM_SCALE) && !(fl & HAVE_SCALE_Y))
-        scaleY = scaleX;
-      rec_points->x = scaleX;
-      rec_points->y = scaleY;
-      rec_points++;
-    }
-    if (fl & (HAVE_SKEW_X | HAVE_SKEW_Y))
-    {
-      int skewX = (fl & HAVE_SKEW_X) ? ((const F4DOT12 *) p++)->to_int () : 0;
-      int skewY = (fl & HAVE_SKEW_Y) ? ((const F4DOT12 *) p++)->to_int () : 0;
-      rec_points->x = skewX;
-      rec_points->y = skewY;
-      rec_points++;
-    }
-    if (fl & (HAVE_TCENTER_X | HAVE_TCENTER_Y))
-    {
-      int tCenterX = (fl & HAVE_TCENTER_X) ? * (const FWORD *) p++ : 0;
-      int tCenterY = (fl & HAVE_TCENTER_Y) ? * (const FWORD *) p++ : 0;
-      rec_points->x = tCenterX;
-      rec_points->y = tCenterY;
-      rec_points++;
-    }
-
-    return true;
-  }
-
-  void get_transformation_from_points (const contour_point_t *rec_points,
-                                       float (&matrix)[4], contour_point_t &trans) const
-  {
-    unsigned fl = flags;
-
-    if (fl & AXES_HAVE_VARIATION)
-      rec_points += numAxes;
-
-    matrix[0] = matrix[3] = 1.f;
-    matrix[1] = matrix[2] = 0.f;
-    trans.init (0.f, 0.f);
-
-    float translateX = 0.f;
-    float translateY = 0.f;
-    float rotation = 0.f;
-    float scaleX = 1.f;
-    float scaleY = 1.f;
-    float skewX = 0.f;
-    float skewY = 0.f;
-    float tCenterX = 0.f;
-    float tCenterY = 0.f;
-
-    if (fl & (HAVE_TRANSLATE_X | HAVE_TRANSLATE_Y))
-    {
-      translateX = rec_points->x;
-      translateY = rec_points->y;
-      rec_points++;
-    }
-    if (fl & HAVE_ROTATION)
-    {
-      rotation = rec_points->x / (1 << 12);
-      rec_points++;
-    }
-    if (fl & (HAVE_SCALE_X | HAVE_SCALE_Y))
-    {
-      scaleX = rec_points->x / (1 << 10);
-      scaleY = rec_points->y / (1 << 10);
-      rec_points++;
-    }
-    if (fl & (HAVE_SKEW_X | HAVE_SKEW_Y))
-    {
-      skewX = rec_points->x / (1 << 12);
-      skewY = rec_points->y / (1 << 12);
-      rec_points++;
-    }
-    if (fl & (HAVE_TCENTER_X | HAVE_TCENTER_Y))
-    {
-      tCenterX = rec_points->x;
-      tCenterY = rec_points->y;
-      rec_points++;
-    }
-
-    translate (matrix, trans, translateX + tCenterX, translateY + tCenterY);
-    rotate (matrix, trans, rotation);
-    scale (matrix, trans, scaleX, scaleY);
-    skew (matrix, trans, -skewX, skewY);
-    translate (matrix, trans, -tCenterX, -tCenterY);
-  }
-
-  void set_variations (coord_setter_t &setter,
-                       hb_array_t rec_points) const
-  {
-    bool have_variations = flags & AXES_HAVE_VARIATION;
-    unsigned axis_width = (flags & AXIS_INDICES_ARE_SHORT) ? 2 : 1;
-    unsigned num_axes = numAxes;
-
-    const HBUINT8  *p = (const HBUINT8 *)  (((HBUINT8 *) &numAxes) + numAxes.static_size + (flags & GID_IS_24BIT ? 3 : 2));
-    const HBUINT16 *q = (const HBUINT16 *) (((HBUINT8 *) &numAxes) + numAxes.static_size + (flags & GID_IS_24BIT ? 3 : 2));
-
-    const F2DOT14 *a = (const F2DOT14 *) ((HBUINT8 *) (axis_width == 1 ? (p + num_axes) : (HBUINT8 *) (q + num_axes)));
-
-    unsigned count = num_axes;
-    for (unsigned i = 0; i < count; i++)
-    {
-      unsigned axis_index = axis_width == 1 ? (unsigned) *p++ : (unsigned) *q++;
-
-      signed v = have_variations ? rec_points.arrayZ[i].x : a++->to_int ();
-
-      v = hb_clamp (v, -(1<<14), (1<<14));
-      setter[axis_index] = v;
-    }
-  }
-
-  protected:
-  HBUINT16      flags;
-  HBUINT8       numAxes;
-  HBUINT16      pad;
-  public:
-  DEFINE_SIZE_MIN (5);
-};
-
-using var_composite_iter_t = composite_iter_tmpl;
-
-struct VarCompositeGlyph
-{
-  const GlyphHeader &header;
-  hb_bytes_t bytes;
-  VarCompositeGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) :
-    header (header_), bytes (bytes_) {}
-
-  var_composite_iter_t iter () const
-  { return var_composite_iter_t (bytes, &StructAfter (header)); }
-
-  const hb_bytes_t trim_padding () const
-  {
-    unsigned length = GlyphHeader::static_size;
-    for (auto &comp : iter ())
-      length += comp.get_size ();
-    return bytes.sub_array (0, length);
-  }
-};
-
-
-} /* namespace glyf_impl */
-} /* namespace OT */
-
-
-#endif /* OT_GLYF_VARCOMPOSITEGLYPH_HH */
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/coord-setter.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/coord-setter.hh
deleted file mode 100644
index cf05929362f59..0000000000000
--- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/coord-setter.hh
+++ /dev/null
@@ -1,36 +0,0 @@
-#ifndef OT_GLYF_COORD_SETTER_HH
-#define OT_GLYF_COORD_SETTER_HH
-
-
-#include "../../hb.hh"
-
-
-namespace OT {
-namespace glyf_impl {
-
-
-struct coord_setter_t
-{
-  coord_setter_t (hb_array_t coords) :
-    coords (coords) {}
-
-  int& operator [] (unsigned idx)
-  {
-    if (unlikely (idx >= HB_GLYF_VAR_COMPOSITE_MAX_AXES))
-      return Crap(int);
-    if (coords.length < idx + 1)
-      coords.resize (idx + 1);
-    return coords[idx];
-  }
-
-  hb_array_t get_coords ()
-  { return coords.as_array (); }
-
-  hb_vector_t coords;
-};
-
-
-} /* namespace glyf_impl */
-} /* namespace OT */
-
-#endif /* OT_GLYF_COORD_SETTER_HH */
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf-helpers.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf-helpers.hh
index 3462e4d1ea5fd..bd07e941393f3 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf-helpers.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf-helpers.hh
@@ -38,7 +38,7 @@ _write_loca (IteratorIn&& it,
 
     unsigned padded_size = *it++;
     offset += padded_size;
-    DEBUG_MSG (SUBSET, nullptr, "loca entry gid %u offset %u padded-size %u", gid, offset, padded_size);
+    DEBUG_MSG (SUBSET, nullptr, "loca entry gid %" PRIu32 " offset %u padded-size %u", gid, offset, padded_size);
     value = offset >> right_shift;
     *dest++ = value;
 
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf.hh
index 175e1de308c51..d9e5fedfa92ab 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf.hh
@@ -94,7 +94,7 @@ struct glyf
     }
 
     hb_vector_t padded_offsets;
-    if (unlikely (!padded_offsets.alloc (c->plan->new_to_old_gid_list.length, true)))
+    if (unlikely (!padded_offsets.alloc_exact (c->plan->new_to_old_gid_list.length)))
       return_trace (false);
 
     hb_vector_t glyphs;
@@ -172,6 +172,9 @@ struct glyf_accelerator_t
     glyf_table = nullptr;
 #ifndef HB_NO_VAR
     gvar = nullptr;
+#ifndef HB_NO_BEYOND_64K
+    GVAR = nullptr;
+#endif
 #endif
     hmtx = nullptr;
 #ifndef HB_NO_VERTICAL
@@ -187,6 +190,9 @@ struct glyf_accelerator_t
     glyf_table = hb_sanitize_context_t ().reference_table (face);
 #ifndef HB_NO_VAR
     gvar = face->table.gvar;
+#ifndef HB_NO_BEYOND_64K
+    GVAR = face->table.GVAR;
+#endif
 #endif
     hmtx = face->table.hmtx;
 #ifndef HB_NO_VERTICAL
@@ -198,6 +204,13 @@ struct glyf_accelerator_t
   }
   ~glyf_accelerator_t ()
   {
+    auto *scratch = cached_scratch.get_relaxed ();
+    if (scratch)
+    {
+      scratch->~hb_glyf_scratch_t ();
+      hb_free (scratch);
+    }
+
     glyf_table.destroy ();
   }
 
@@ -205,18 +218,17 @@ struct glyf_accelerator_t
 
   protected:
   template
-  bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer) const
+  bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer,
+                   hb_array_t coords,
+                   hb_glyf_scratch_t &scratch) const
   {
     if (gid >= num_glyphs) return false;
 
-    /* Making this allocfree is not that easy
-       https://github.com/harfbuzz/harfbuzz/issues/2095
-       mostly because of gvar handling in VF fonts,
-       perhaps a separate path for non-VF fonts can be considered */
-    contour_point_vector_t all_points;
+    auto &all_points = scratch.all_points;
+    all_points.resize (0);
 
     bool phantom_only = !consumer.is_consuming_contour_points ();
-    if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, nullptr, nullptr, nullptr, true, true, phantom_only)))
+    if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, scratch, nullptr, nullptr, nullptr, true, true, phantom_only, coords)))
       return false;
 
     unsigned count = all_points.length;
@@ -225,8 +237,61 @@ struct glyf_accelerator_t
 
     if (consumer.is_consuming_contour_points ())
     {
-      for (auto &point : all_points.as_array ().sub_array (0, count))
-        consumer.consume_point (point);
+      auto *points = all_points.arrayZ;
+
+      if (false)
+      {
+        /* Our path-builder was designed to work with this simple loop.
+         * But FreeType and CoreText do it differently, so we match those
+         * with the other, more complicated, code branch below. */
+        for (unsigned i = 0; i < count; i++)
+        {
+          consumer.consume_point (points[i]);
+          if (points[i].is_end_point)
+            consumer.contour_end ();
+        }
+      }
+      else
+      {
+        for (unsigned i = 0; i < count; i++)
+        {
+          // Start of a contour.
+          if (points[i].flag & glyf_impl::SimpleGlyph::FLAG_ON_CURVE)
+          {
+            // First point is on-curve. Draw the contour.
+            for (; i < count; i++)
+            {
+              consumer.consume_point (points[i]);
+              if (points[i].is_end_point)
+              {
+                consumer.contour_end ();
+                break;
+              }
+            }
+          }
+          else
+          {
+            unsigned start = i;
+
+            // Find end of the contour.
+            for (; i < count; i++)
+              if (points[i].is_end_point)
+                break;
+
+            unsigned end = i;
+
+            // Enough to start from the end. Our path-builder takes care of the rest.
+            if (likely (end < count)) // Can only fail in case of alloc failure *maybe*.
+              consumer.consume_point (points[end]);
+
+            for (i = start; i < end; i++)
+              consumer.consume_point (points[i]);
+
+            consumer.contour_end ();
+          }
+        }
+      }
+
       consumer.points_end ();
     }
 
@@ -299,6 +364,7 @@ struct glyf_accelerator_t
 
     HB_ALWAYS_INLINE
     void consume_point (const contour_point_t &point) { bounds.add (point); }
+    void contour_end () {}
     void points_end () { bounds.get_extents (font, extents, scaled); }
 
     bool is_consuming_contour_points () { return extents; }
@@ -314,7 +380,12 @@ struct glyf_accelerator_t
 
     contour_point_t phantoms[glyf_impl::PHANTOM_COUNT];
     if (font->num_coords)
-      success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false));
+    {
+      hb_glyf_scratch_t scratch;
+      success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false),
+                            hb_array (font->coords, font->num_coords),
+                            scratch);
+    }
 
     if (unlikely (!success))
       return
@@ -334,9 +405,11 @@ struct glyf_accelerator_t
     if (unlikely (gid >= num_glyphs)) return false;
 
     hb_glyph_extents_t extents;
-
+    hb_glyf_scratch_t scratch;
     contour_point_t phantoms[glyf_impl::PHANTOM_COUNT];
-    if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms, false))))
+    if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms, false),
+                               hb_array (font->coords, font->num_coords),
+                               scratch)))
       return false;
 
     *lsb = is_vertical
@@ -356,26 +429,33 @@ struct glyf_accelerator_t
   }
 
   public:
-  bool get_extents (hb_font_t *font, hb_codepoint_t gid, hb_glyph_extents_t *extents) const
+
+  bool get_extents (hb_font_t *font,
+                    hb_codepoint_t gid,
+                    hb_glyph_extents_t *extents) const
+  { return get_extents_at (font, gid, extents, hb_array (font->coords, font->num_coords)); }
+
+  bool get_extents_at (hb_font_t *font,
+                       hb_codepoint_t gid,
+                       hb_glyph_extents_t *extents,
+                       hb_array_t coords) const
   {
     if (unlikely (gid >= num_glyphs)) return false;
 
 #ifndef HB_NO_VAR
-    if (font->num_coords)
-      return get_points (font, gid, points_aggregator_t (font, extents, nullptr, true));
+    if (coords)
+    {
+      hb_glyf_scratch_t scratch;
+      return get_points (font,
+                         gid,
+                         points_aggregator_t (font, extents, nullptr, true),
+                         coords,
+                         scratch);
+    }
 #endif
     return glyph_for_gid (gid).get_extents_without_var_scaled (font, *this, extents);
   }
 
-  bool paint_glyph (hb_font_t *font, hb_codepoint_t gid, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const
-  {
-    funcs->push_clip_glyph (data, gid, font);
-    funcs->color (data, true, foreground);
-    funcs->pop_clip (data);
-
-    return true;
-  }
-
   const glyf_impl::Glyph
   glyph_for_gid (hb_codepoint_t gid, bool needs_padding_removal = false) const
   {
@@ -406,10 +486,52 @@ struct glyf_accelerator_t
 
   bool
   get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session) const
-  { return get_points (font, gid, glyf_impl::path_builder_t (font, draw_session)); }
+  {
+    if (!has_data ()) return false;
+
+    hb_glyf_scratch_t *scratch;
+
+    // Borrow the cached strach buffer.
+    {
+      scratch = cached_scratch.get_acquire ();
+      if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr)))
+      {
+        scratch = (hb_glyf_scratch_t *) hb_calloc (1, sizeof (hb_glyf_scratch_t));
+        if (unlikely (!scratch))
+          return true;
+      }
+    }
+
+    bool ret = get_points (font, gid, glyf_impl::path_builder_t (font, draw_session),
+                           hb_array (font->coords, font->num_coords),
+                           *scratch);
+
+    // Put it back.
+    if (!cached_scratch.cmpexch (nullptr, scratch))
+    {
+      scratch->~hb_glyf_scratch_t ();
+      hb_free (scratch);
+    }
+
+    return ret;
+  }
+
+  bool
+  get_path_at (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session,
+               hb_array_t coords,
+               hb_glyf_scratch_t &scratch) const
+  {
+    if (!has_data ()) return false;
+    return get_points (font, gid, glyf_impl::path_builder_t (font, draw_session),
+                       coords,
+                       scratch);
+  }
 
 #ifndef HB_NO_VAR
   const gvar_accelerator_t *gvar;
+#ifndef HB_NO_BEYOND_64K
+  const GVAR_accelerator_t *GVAR;
+#endif
 #endif
   const hmtx_accelerator_t *hmtx;
 #ifndef HB_NO_VERTICAL
@@ -421,6 +543,7 @@ struct glyf_accelerator_t
   unsigned int num_glyphs;
   hb_blob_ptr_t loca_table;
   hb_blob_ptr_t glyf_table;
+  mutable hb_atomic_t cached_scratch;
 };
 
 
@@ -430,7 +553,7 @@ glyf::_populate_subset_glyphs (const hb_subset_plan_t   *plan,
                                hb_vector_t& glyphs /* OUT */) const
 {
   OT::glyf_accelerator_t glyf (plan->source);
-  if (!glyphs.alloc (plan->new_to_old_gid_list.length, true)) return false;
+  if (!glyphs.alloc_exact (plan->new_to_old_gid_list.length)) return false;
 
   for (const auto &pair : plan->new_to_old_gid_list)
   {
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/path-builder.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/path-builder.hh
index d56ea3e45ff4b..f5495f923328b 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/path-builder.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/path-builder.hh
@@ -42,7 +42,7 @@ struct path_builder_t
   {
     bool is_on_curve = point.flag & glyf_impl::SimpleGlyph::FLAG_ON_CURVE;
 #ifdef HB_NO_CUBIC_GLYF
-    bool is_cubic = false;
+    constexpr bool is_cubic = false;
 #else
     bool is_cubic = !is_on_curve && (point.flag & glyf_impl::SimpleGlyph::FLAG_CUBIC);
 #endif
@@ -124,58 +124,60 @@ struct path_builder_t
       }
     }
 
-    if (unlikely (point.is_end_point))
-    {
-      if (first_offcurve && last_offcurve)
-      {
-        optional_point_t mid = last_offcurve.mid (first_offcurve2 ?
-                                                  first_offcurve2 :
-                                                  first_offcurve);
-        if (last_offcurve2)
-          draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
-                                  last_offcurve.x, last_offcurve.y,
-                                  mid.x, mid.y);
-        else
-          draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
-                                     mid.x, mid.y);
-        last_offcurve = optional_point_t ();
-      }
-      /* now check the rest */
+  }
 
-      if (first_offcurve && first_oncurve)
-      {
-        if (first_offcurve2)
-          draw_session->cubic_to (first_offcurve2.x, first_offcurve2.y,
-                                  first_offcurve.x, first_offcurve.y,
-                                  first_oncurve.x, first_oncurve.y);
-        else
-          draw_session->quadratic_to (first_offcurve.x, first_offcurve.y,
-                                     first_oncurve.x, first_oncurve.y);
-      }
-      else if (last_offcurve && first_oncurve)
-      {
-        if (last_offcurve2)
-          draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
-                                  last_offcurve.x, last_offcurve.y,
-                                  first_oncurve.x, first_oncurve.y);
-        else
-          draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
-                                     first_oncurve.x, first_oncurve.y);
-      }
-      else if (first_oncurve)
-        draw_session->line_to (first_oncurve.x, first_oncurve.y);
-      else if (first_offcurve)
-      {
-        float x = first_offcurve.x, y = first_offcurve.y;
-        draw_session->move_to (x, y);
-        draw_session->quadratic_to (x, y, x, y);
-      }
+  void contour_end ()
+  {
+    if (first_offcurve && last_offcurve)
+    {
+      optional_point_t mid = last_offcurve.mid (first_offcurve2 ?
+                                                first_offcurve2 :
+                                                first_offcurve);
+      if (last_offcurve2)
+        draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
+                                last_offcurve.x, last_offcurve.y,
+                                mid.x, mid.y);
+      else
+        draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
+                                   mid.x, mid.y);
+      last_offcurve = optional_point_t ();
+    }
+    /* now check the rest */
 
-      /* Getting ready for the next contour */
-      first_oncurve = first_offcurve = last_offcurve = last_offcurve2 = optional_point_t ();
-      draw_session->close_path ();
+    if (first_offcurve && first_oncurve)
+    {
+      if (first_offcurve2)
+        draw_session->cubic_to (first_offcurve2.x, first_offcurve2.y,
+                                first_offcurve.x, first_offcurve.y,
+                                first_oncurve.x, first_oncurve.y);
+      else
+        draw_session->quadratic_to (first_offcurve.x, first_offcurve.y,
+                                   first_oncurve.x, first_oncurve.y);
     }
+    else if (last_offcurve && first_oncurve)
+    {
+      if (last_offcurve2)
+        draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
+                                last_offcurve.x, last_offcurve.y,
+                                first_oncurve.x, first_oncurve.y);
+      else
+        draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
+                                   first_oncurve.x, first_oncurve.y);
+    }
+    else if (first_oncurve)
+      draw_session->line_to (first_oncurve.x, first_oncurve.y);
+    else if (first_offcurve)
+    {
+      float x = first_offcurve.x, y = first_offcurve.y;
+      draw_session->move_to (x, y);
+      draw_session->quadratic_to (x, y, x, y);
+    }
+
+    /* Getting ready for the next contour */
+    first_oncurve = first_offcurve = last_offcurve = last_offcurve2 = optional_point_t ();
+    draw_session->close_path ();
   }
+
   void points_end () {}
 
   bool is_consuming_contour_points () { return true; }
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/name/name.hh b/src/java.desktop/share/native/libharfbuzz/OT/name/name.hh
index f14c2da2de68f..b440379afbe67 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/name/name.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/name/name.hh
@@ -242,7 +242,9 @@ struct NameRecord
   bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) && offset.sanitize (c, base, length));
+    return_trace (c->check_struct (this) &&
+                  hb_barrier () &&
+                  offset.sanitize (c, base, length));
   }
 
   HBUINT16      platformID;     /* Platform ID. */
@@ -465,6 +467,7 @@ struct name
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
+                  hb_barrier () &&
                   likely (format == 0 || format == 1) &&
                   c->check_array (nameRecordZ.arrayZ, count) &&
                   c->check_range (this, stringOffset) &&
@@ -482,7 +485,7 @@ struct name
       const hb_array_t all_names (this->table->nameRecordZ.arrayZ,
                                                     this->table->count);
 
-      this->names.alloc (all_names.length, true);
+      this->names.alloc_exact (all_names.length);
 
       for (unsigned int i = 0; i < all_names.length; i++)
       {
diff --git a/src/java.desktop/share/native/libharfbuzz/UPDATING.txt b/src/java.desktop/share/native/libharfbuzz/UPDATING.txt
index 3f72983a45576..d536fad05ac50 100644
--- a/src/java.desktop/share/native/libharfbuzz/UPDATING.txt
+++ b/src/java.desktop/share/native/libharfbuzz/UPDATING.txt
@@ -53,9 +53,9 @@ STEP 2: BUILD CHANGES INCREMENTALLY
 
 STEP 3: COMPILER WARNINGS AND SETTING FLAGS
 -------------------------------------------
-- Update make parameters in Awt2DLibraries.gmk
+- Update make parameters in make/modules/java.desktop/lib/ClientLibraries.gmk
   Since we don't use configure we need to manually specify the options
-  we need in the Harfbuzz section of Awt2DLibraries.gmk.
+  we need in the Harfbuzz section of ClientLibraries.gmk.
   As well as adding new options, we may need to clean up obsolete options.
   Note there may be platform variations in the flags.
 
diff --git a/src/java.desktop/share/native/libharfbuzz/graph/classdef-graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/classdef-graph.hh
index c1432883ffaae..da6378820bbc8 100644
--- a/src/java.desktop/share/native/libharfbuzz/graph/classdef-graph.hh
+++ b/src/java.desktop/share/native/libharfbuzz/graph/classdef-graph.hh
@@ -39,6 +39,7 @@ struct ClassDefFormat1 : public OT::ClassDefFormat1_3
     int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
     constexpr unsigned min_size = OT::ClassDefFormat1_3::min_size;
     if (vertex_len < min_size) return false;
+    hb_barrier ();
     return vertex_len >= min_size + classValue.get_size () - classValue.len.get_size ();
   }
 };
@@ -50,6 +51,7 @@ struct ClassDefFormat2 : public OT::ClassDefFormat2_4
     int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
     constexpr unsigned min_size = OT::ClassDefFormat2_4::min_size;
     if (vertex_len < min_size) return false;
+    hb_barrier ();
     return vertex_len >= min_size + rangeRecord.get_size () - rangeRecord.len.get_size ();
   }
 };
@@ -114,6 +116,7 @@ struct ClassDef : public OT::ClassDef
   {
     int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
     if (vertex_len < OT::ClassDef::min_size) return false;
+    hb_barrier ();
     switch (u.format)
     {
     case 1: return ((ClassDefFormat1*)this)->sanitize (vertex);
@@ -131,20 +134,23 @@ struct ClassDef : public OT::ClassDef
 
 struct class_def_size_estimator_t
 {
+  // TODO(garretrieger): update to support beyond64k coverage/classdef tables.
+  constexpr static unsigned class_def_format1_base_size = 6;
+  constexpr static unsigned class_def_format2_base_size = 4;
+  constexpr static unsigned coverage_base_size = 4;
+  constexpr static unsigned bytes_per_range = 6;
+  constexpr static unsigned bytes_per_glyph = 2;
+
   template
   class_def_size_estimator_t (It glyph_and_class)
-      : gids_consecutive (true), num_ranges_per_class (), glyphs_per_class ()
+      : num_ranges_per_class (), glyphs_per_class ()
   {
-    unsigned last_gid = (unsigned) -1;
+    reset();
     for (auto p : + glyph_and_class)
     {
       unsigned gid = p.first;
       unsigned klass = p.second;
 
-      if (last_gid != (unsigned) -1 && gid != last_gid + 1)
-        gids_consecutive = false;
-      last_gid = gid;
-
       hb_set_t* glyphs;
       if (glyphs_per_class.has (klass, &glyphs) && glyphs) {
         glyphs->add (gid);
@@ -174,28 +180,54 @@ struct class_def_size_estimator_t
     }
   }
 
-  // Incremental increase in the Coverage and ClassDef table size
-  // (worst case) if all glyphs associated with 'klass' were added.
-  unsigned incremental_coverage_size (unsigned klass) const
+  void reset() {
+    class_def_1_size = class_def_format1_base_size;
+    class_def_2_size = class_def_format2_base_size;
+    included_glyphs.clear();
+    included_classes.clear();
+  }
+
+  // Compute the size of coverage for all glyphs added via 'add_class_def_size'.
+  unsigned coverage_size () const
   {
-    // Coverage takes 2 bytes per glyph worst case,
-    return 2 * glyphs_per_class.get (klass).get_population ();
+    unsigned format1_size = coverage_base_size + bytes_per_glyph * included_glyphs.get_population();
+    unsigned format2_size = coverage_base_size + bytes_per_range * num_glyph_ranges();
+    return hb_min(format1_size, format2_size);
   }
 
-  // Incremental increase in the Coverage and ClassDef table size
-  // (worst case) if all glyphs associated with 'klass' were added.
-  unsigned incremental_class_def_size (unsigned klass) const
+  // Compute the new size of the ClassDef table if all glyphs associated with 'klass' were added.
+  unsigned add_class_def_size (unsigned klass)
   {
-    // ClassDef takes 6 bytes per range
-    unsigned class_def_2_size = 6 * num_ranges_per_class.get (klass);
-    if (gids_consecutive)
-    {
-      // ClassDef1 takes 2 bytes per glyph, but only can be used
-      // when gids are consecutive.
-      return hb_min (2 * glyphs_per_class.get (klass).get_population (), class_def_2_size);
+    if (!included_classes.has(klass)) {
+      hb_set_t* glyphs = nullptr;
+      if (glyphs_per_class.has(klass, &glyphs)) {
+        included_glyphs.union_(*glyphs);
+      }
+
+      class_def_1_size = class_def_format1_base_size;
+      if (!included_glyphs.is_empty()) {
+        unsigned min_glyph = included_glyphs.get_min();
+        unsigned max_glyph = included_glyphs.get_max();
+        class_def_1_size += bytes_per_glyph * (max_glyph - min_glyph + 1);
+      }
+
+      class_def_2_size += bytes_per_range * num_ranges_per_class.get (klass);
+
+      included_classes.add(klass);
     }
 
-    return class_def_2_size;
+    return hb_min (class_def_1_size, class_def_2_size);
+  }
+
+  unsigned num_glyph_ranges() const {
+    hb_codepoint_t start = HB_SET_VALUE_INVALID;
+    hb_codepoint_t end = HB_SET_VALUE_INVALID;
+
+    unsigned count = 0;
+    while (included_glyphs.next_range (&start, &end)) {
+        count++;
+    }
+    return count;
   }
 
   bool in_error ()
@@ -211,9 +243,12 @@ struct class_def_size_estimator_t
   }
 
  private:
-  bool gids_consecutive;
   hb_hashmap_t num_ranges_per_class;
   hb_hashmap_t glyphs_per_class;
+  hb_set_t included_classes;
+  hb_set_t included_glyphs;
+  unsigned class_def_1_size;
+  unsigned class_def_2_size;
 };
 
 
diff --git a/src/java.desktop/share/native/libharfbuzz/graph/coverage-graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/coverage-graph.hh
index 4f44e076d1f3e..61ca063e34519 100644
--- a/src/java.desktop/share/native/libharfbuzz/graph/coverage-graph.hh
+++ b/src/java.desktop/share/native/libharfbuzz/graph/coverage-graph.hh
@@ -39,6 +39,7 @@ struct CoverageFormat1 : public OT::Layout::Common::CoverageFormat1_3::min_size;
     if (vertex_len < min_size) return false;
+    hb_barrier ();
     return vertex_len >= min_size + glyphArray.get_size () - glyphArray.len.get_size ();
   }
 };
@@ -50,6 +51,7 @@ struct CoverageFormat2 : public OT::Layout::Common::CoverageFormat2_4::min_size;
     if (vertex_len < min_size) return false;
+    hb_barrier ();
     return vertex_len >= min_size + rangeRecord.get_size () - rangeRecord.len.get_size ();
   }
 };
@@ -138,6 +140,7 @@ struct Coverage : public OT::Layout::Common::Coverage
   {
     int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
     if (vertex_len < OT::Layout::Common::Coverage::min_size) return false;
+    hb_barrier ();
     switch (u.format)
     {
     case 1: return ((CoverageFormat1*)this)->sanitize (vertex);
diff --git a/src/java.desktop/share/native/libharfbuzz/graph/graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/graph.hh
index 4a1f7ebf5a5c1..ed1026f58663c 100644
--- a/src/java.desktop/share/native/libharfbuzz/graph/graph.hh
+++ b/src/java.desktop/share/native/libharfbuzz/graph/graph.hh
@@ -195,6 +195,15 @@ struct graph_t
       return incoming_edges_;
     }
 
+    unsigned incoming_edges_from_parent (unsigned parent_index) const {
+      if (single_parent != (unsigned) -1) {
+        return single_parent == parent_index ? 1 : 0;
+      }
+
+      unsigned* count;
+      return  parents.has(parent_index, &count) ? *count : 0;
+    }
+
     void reset_parents ()
     {
       incoming_edges_ = 0;
@@ -334,6 +343,16 @@ struct graph_t
       return true;
     }
 
+    bool give_max_priority ()
+    {
+      bool result = false;
+      while (!has_max_priority()) {
+        result = true;
+        priority++;
+      }
+      return result;
+    }
+
     bool has_max_priority () const {
       return priority >= 3;
     }
@@ -349,7 +368,7 @@ struct graph_t
       // it's parent where possible.
 
       int64_t modified_distance =
-          hb_min (hb_max(distance + distance_modifier (), 0), 0x7FFFFFFFFFF);
+          hb_clamp (distance + distance_modifier (), (int64_t) 0, 0x7FFFFFFFFFF);
       if (has_max_priority ()) {
         modified_distance = 0;
       }
@@ -567,6 +586,7 @@ struct graph_t
     update_distances ();
 
     hb_priority_queue_t queue;
+    queue.alloc (vertices_.length);
     hb_vector_t &sorted_graph = vertices_scratch_;
     if (unlikely (!check_success (sorted_graph.resize (vertices_.length)))) return;
     hb_vector_t id_map;
@@ -1022,6 +1042,11 @@ struct graph_t
    * Creates a copy of child and re-assigns the link from
    * parent to the clone. The copy is a shallow copy, objects
    * linked from child are not duplicated.
+   *
+   * Returns the index of the newly created duplicate.
+   *
+   * If the child_idx only has incoming edges from parent_idx, this
+   * will do nothing and return the original child_idx.
    */
   unsigned duplicate_if_shared (unsigned parent_idx, unsigned child_idx)
   {
@@ -1035,18 +1060,20 @@ struct graph_t
    * Creates a copy of child and re-assigns the link from
    * parent to the clone. The copy is a shallow copy, objects
    * linked from child are not duplicated.
+   *
+   * Returns the index of the newly created duplicate.
+   *
+   * If the child_idx only has incoming edges from parent_idx,
+   * duplication isn't possible and this will return -1.
    */
   unsigned duplicate (unsigned parent_idx, unsigned child_idx)
   {
     update_parents ();
 
-    unsigned links_to_child = 0;
-    for (const auto& l : vertices_[parent_idx].obj.all_links ())
-    {
-      if (l.objidx == child_idx) links_to_child++;
-    }
+    const auto& child = vertices_[child_idx];
+    unsigned links_to_child = child.incoming_edges_from_parent(parent_idx);
 
-    if (vertices_[child_idx].incoming_edges () <= links_to_child)
+    if (child.incoming_edges () <= links_to_child)
     {
       // Can't duplicate this node, doing so would orphan the original one as all remaining links
       // to child are from parent.
@@ -1059,7 +1086,7 @@ struct graph_t
                parent_idx, child_idx);
 
     unsigned clone_idx = duplicate (child_idx);
-    if (clone_idx == (unsigned) -1) return false;
+    if (clone_idx == (unsigned) -1) return -1;
     // duplicate shifts the root node idx, so if parent_idx was root update it.
     if (parent_idx == clone_idx) parent_idx++;
 
@@ -1075,6 +1102,62 @@ struct graph_t
     return clone_idx;
   }
 
+  /*
+   * Creates a copy of child and re-assigns the links from
+   * parents to the clone. The copy is a shallow copy, objects
+   * linked from child are not duplicated.
+   *
+   * Returns the index of the newly created duplicate.
+   *
+   * If the child_idx only has incoming edges from parents,
+   * duplication isn't possible or duplication fails and this will
+   * return -1.
+   */
+  unsigned duplicate (const hb_set_t* parents, unsigned child_idx)
+  {
+    if (parents->is_empty()) {
+      return -1;
+    }
+
+    update_parents ();
+
+    const auto& child = vertices_[child_idx];
+    unsigned links_to_child = 0;
+    unsigned last_parent = parents->get_max();
+    unsigned first_parent = parents->get_min();
+    for (unsigned parent_idx : *parents) {
+      links_to_child += child.incoming_edges_from_parent(parent_idx);
+    }
+
+    if (child.incoming_edges () <= links_to_child)
+    {
+      // Can't duplicate this node, doing so would orphan the original one as all remaining links
+      // to child are from parent.
+      DEBUG_MSG (SUBSET_REPACK, nullptr, "  Not duplicating %u, ..., %u => %u", first_parent, last_parent, child_idx);
+      return -1;
+    }
+
+    DEBUG_MSG (SUBSET_REPACK, nullptr, "  Duplicating %u, ..., %u => %u", first_parent, last_parent, child_idx);
+
+    unsigned clone_idx = duplicate (child_idx);
+    if (clone_idx == (unsigned) -1) return false;
+
+    for (unsigned parent_idx : *parents) {
+      // duplicate shifts the root node idx, so if parent_idx was root update it.
+      if (parent_idx == clone_idx) parent_idx++;
+      auto& parent = vertices_[parent_idx];
+      for (auto& l : parent.obj.all_links_writer ())
+      {
+        if (l.objidx != child_idx)
+          continue;
+
+        reassign_link (l, parent_idx, clone_idx);
+      }
+    }
+
+    return clone_idx;
+  }
+
 
   /*
    * Adds a new node to the graph, not connected to anything.
@@ -1370,6 +1453,7 @@ struct graph_t
     vertices_.tail ().distance = 0;
 
     hb_priority_queue_t queue;
+    queue.alloc (count);
     queue.insert (0, vertices_.length - 1);
 
     hb_vector_t visited;
diff --git a/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-graph.hh
index a5f9223e6059e..7ad2ba430b717 100644
--- a/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-graph.hh
+++ b/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-graph.hh
@@ -76,6 +76,7 @@ struct Lookup : public OT::Lookup
   {
     int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
     if (vertex_len < OT::Lookup::min_size) return false;
+    hb_barrier ();
     return vertex_len >= this->get_size ();
   }
 
@@ -351,6 +352,7 @@ struct LookupList : public OT::LookupList
   {
     int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
     if (vertex_len < OT::LookupList::min_size) return false;
+    hb_barrier ();
     return vertex_len >= OT::LookupList::item_size * this->len;
   }
 };
@@ -364,6 +366,7 @@ struct GSTAR : public OT::GSUBGPOS
     GSTAR* gstar = (GSTAR*) r.obj.head;
     if (!gstar || !gstar->sanitize (r))
       return nullptr;
+    hb_barrier ();
 
     return gstar;
   }
@@ -383,6 +386,7 @@ struct GSTAR : public OT::GSUBGPOS
   {
     int64_t len = vertex.obj.tail - vertex.obj.head;
     if (len < OT::GSUBGPOS::min_size) return false;
+    hb_barrier ();
     return len >= get_size ();
   }
 
diff --git a/src/java.desktop/share/native/libharfbuzz/graph/markbasepos-graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/markbasepos-graph.hh
index ae5ebd0d16756..fb4166128a952 100644
--- a/src/java.desktop/share/native/libharfbuzz/graph/markbasepos-graph.hh
+++ b/src/java.desktop/share/native/libharfbuzz/graph/markbasepos-graph.hh
@@ -40,6 +40,7 @@ struct AnchorMatrix : public OT::Layout::GPOS_impl::AnchorMatrix
   {
     int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
     if (vertex_len < AnchorMatrix::min_size) return false;
+    hb_barrier ();
 
     return vertex_len >= AnchorMatrix::min_size +
         OT::Offset16::static_size * class_count * this->rows;
@@ -128,6 +129,7 @@ struct MarkArray : public OT::Layout::GPOS_impl::MarkArray
     int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
     unsigned min_size = MarkArray::min_size;
     if (vertex_len < min_size) return false;
+    hb_barrier ();
 
     return vertex_len >= get_size ();
   }
@@ -495,6 +497,7 @@ struct MarkBasePos : public OT::Layout::GPOS_impl::MarkBasePos
   {
     int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
     if (vertex_len < u.format.get_size ()) return false;
+    hb_barrier ();
 
     switch (u.format) {
     case 1:
diff --git a/src/java.desktop/share/native/libharfbuzz/graph/pairpos-graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/pairpos-graph.hh
index ad158cc9e8fae..fd46861de462a 100644
--- a/src/java.desktop/share/native/libharfbuzz/graph/pairpos-graph.hh
+++ b/src/java.desktop/share/native/libharfbuzz/graph/pairpos-graph.hh
@@ -42,6 +42,7 @@ struct PairPosFormat1 : public OT::Layout::GPOS_impl::PairPosFormat1_3::min_size;
     if (vertex_len < min_size) return false;
+    hb_barrier ();
 
     return vertex_len >=
         min_size + pairSet.get_size () - pairSet.len.get_size();
@@ -198,6 +199,7 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4::min_size;
     if (vertex_len < min_size) return false;
+    hb_barrier ();
 
     const unsigned class1_count = class1Count;
     return vertex_len >=
@@ -245,8 +247,8 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4check_struct (this) &&
+                          hb_barrier () &&
                           version == 0 &&
                           c->check_range (this, anchorData) &&
                           lookupTable.sanitize (c, this, &(this+anchorData))));
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-bsln-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-bsln-table.hh
index 24d53e224c788..b14540f3a7141 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-bsln-table.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-bsln-table.hh
@@ -123,6 +123,7 @@ struct bsln
     TRACE_SANITIZE (this);
     if (unlikely (!(c->check_struct (this) && defaultBaseline < 32)))
       return_trace (false);
+    hb_barrier ();
 
     switch (format)
     {
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh
index b2d1b7b67e0cf..d5a44bf473c33 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh
@@ -29,7 +29,13 @@
 
 #include "hb-aat-layout.hh"
 #include "hb-aat-map.hh"
+#include "hb-ot-layout-common.hh"
+#include "hb-ot-layout-gdef-table.hh"
 #include "hb-open-type.hh"
+#include "hb-cache.hh"
+#include "hb-bit-set.hh"
+#include "hb-bit-page.hh"
+
 
 namespace OT {
 struct GDEF;
@@ -39,15 +45,73 @@ namespace AAT {
 
 using namespace OT;
 
-
 struct ankr;
 
+using hb_aat_class_cache_t = hb_cache_t<15, 8, 7>;
+static_assert (sizeof (hb_aat_class_cache_t) == 256, "");
+
+struct hb_aat_scratch_t
+{
+  hb_aat_scratch_t () = default;
+  hb_aat_scratch_t (const hb_aat_scratch_t &) = delete;
+
+  hb_aat_scratch_t (hb_aat_scratch_t &&o)
+  {
+    buffer_glyph_set.set_relaxed (o.buffer_glyph_set.get_relaxed ());
+    o.buffer_glyph_set.set_relaxed (nullptr);
+  }
+  hb_aat_scratch_t & operator = (hb_aat_scratch_t &&o)
+  {
+    buffer_glyph_set.set_relaxed (o.buffer_glyph_set.get_relaxed ());
+    o.buffer_glyph_set.set_relaxed (nullptr);
+    return *this;
+  }
+  ~hb_aat_scratch_t ()
+  {
+    auto *s = buffer_glyph_set.get_relaxed ();
+    if (unlikely (!s))
+      return;
+    s->fini ();
+    hb_free (s);
+  }
+
+  hb_bit_set_t *create_buffer_glyph_set () const
+  {
+    hb_bit_set_t *s = buffer_glyph_set.get_acquire ();
+    if (s && buffer_glyph_set.cmpexch (s, nullptr))
+      return s;
+
+    s = (hb_bit_set_t *) hb_calloc (1, sizeof (hb_bit_set_t));
+    if (unlikely (!s))
+      return nullptr;
+    s->init ();
+
+    return s;
+  }
+  void destroy_buffer_glyph_set (hb_bit_set_t *s) const
+  {
+    if (unlikely (!s))
+      return;
+    if (buffer_glyph_set.cmpexch (nullptr, s))
+      return;
+    s->fini ();
+    hb_free (s);
+  }
+
+  mutable hb_atomic_t buffer_glyph_set;
+};
+
+enum { DELETED_GLYPH = 0xFFFF };
+
+#define HB_BUFFER_SCRATCH_FLAG_AAT_HAS_DELETED HB_BUFFER_SCRATCH_FLAG_SHAPER0
+
 struct hb_aat_apply_context_t :
        hb_dispatch_context_t
 {
   const char *get_name () { return "APPLY"; }
-  template 
-  return_t dispatch (const T &obj) { return obj.apply (this); }
+  template 
+  return_t dispatch (const T &obj, Ts&&... ds)
+  { return obj.apply (this, std::forward (ds)...); }
   static return_t default_return_value () { return false; }
   bool stop_sublookup_iteration (return_t r) const { return r; }
 
@@ -57,8 +121,15 @@ struct hb_aat_apply_context_t :
   hb_buffer_t *buffer;
   hb_sanitize_context_t sanitizer;
   const ankr *ankr_table;
-  const OT::GDEF *gdef_table;
+  const OT::GDEF &gdef;
+  bool has_glyph_classes;
   const hb_sorted_vector_t *range_flags = nullptr;
+  bool using_buffer_glyph_set = false;
+  hb_bit_set_t *buffer_glyph_set = nullptr;
+  const hb_bit_set_t *left_set = nullptr;
+  const hb_bit_set_t *right_set = nullptr;
+  const hb_bit_set_t *machine_glyph_set = nullptr;
+  hb_aat_class_cache_t *machine_class_cache = nullptr;
   hb_mask_t subtable_flags = 0;
 
   /* Unused. For debug tracing only. */
@@ -74,6 +145,88 @@ struct hb_aat_apply_context_t :
   HB_INTERNAL void set_ankr_table (const AAT::ankr *ankr_table_);
 
   void set_lookup_index (unsigned int i) { lookup_index = i; }
+
+  void setup_buffer_glyph_set ()
+  {
+    using_buffer_glyph_set = buffer->len >= 4 && buffer_glyph_set;
+
+    if (likely (using_buffer_glyph_set))
+      buffer->collect_codepoints (*buffer_glyph_set);
+  }
+  bool buffer_intersects_machine () const
+  {
+    if (likely (using_buffer_glyph_set))
+      return buffer_glyph_set->intersects (*machine_glyph_set);
+
+    // Faster for shorter buffers.
+    for (unsigned i = 0; i < buffer->len; i++)
+      if (machine_glyph_set->has (buffer->info[i].codepoint))
+        return true;
+    return false;
+  }
+
+  template 
+  HB_NODISCARD bool output_glyphs (unsigned int count,
+                                   const T *glyphs)
+  {
+    if (likely (using_buffer_glyph_set))
+      buffer_glyph_set->add_array (glyphs, count);
+    for (unsigned int i = 0; i < count; i++)
+    {
+      if (glyphs[i] == DELETED_GLYPH)
+      {
+        buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_AAT_HAS_DELETED;
+        _hb_glyph_info_set_aat_deleted (&buffer->cur());
+      }
+      else
+      {
+#ifndef HB_NO_OT_LAYOUT
+        if (has_glyph_classes)
+          _hb_glyph_info_set_glyph_props (&buffer->cur(),
+                                          gdef.get_glyph_props (glyphs[i]));
+#endif
+      }
+      if (unlikely (!buffer->output_glyph (glyphs[i]))) return false;
+    }
+    return true;
+  }
+
+  HB_NODISCARD bool replace_glyph (hb_codepoint_t glyph)
+  {
+    if (glyph == DELETED_GLYPH)
+    {
+      buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_AAT_HAS_DELETED;
+      _hb_glyph_info_set_aat_deleted (&buffer->cur());
+    }
+
+    if (likely (using_buffer_glyph_set))
+      buffer_glyph_set->add (glyph);
+#ifndef HB_NO_OT_LAYOUT
+    if (has_glyph_classes)
+      _hb_glyph_info_set_glyph_props (&buffer->cur(),
+                                      gdef.get_glyph_props (glyph));
+#endif
+    return buffer->replace_glyph (glyph);
+  }
+
+  HB_NODISCARD bool delete_glyph ()
+  {
+    buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_AAT_HAS_DELETED;
+    _hb_glyph_info_set_aat_deleted (&buffer->cur());
+    return buffer->replace_glyph (DELETED_GLYPH);
+  }
+
+  void replace_glyph_inplace (unsigned i, hb_codepoint_t glyph)
+  {
+    buffer->info[i].codepoint = glyph;
+    if (likely (using_buffer_glyph_set))
+      buffer_glyph_set->add (glyph);
+#ifndef HB_NO_OT_LAYOUT
+    if (has_glyph_classes)
+      _hb_glyph_info_set_glyph_props (&buffer->info[i],
+                                      gdef.get_glyph_props (glyph));
+#endif
+  }
 };
 
 
@@ -95,6 +248,19 @@ struct LookupFormat0
     return &arrayZ[glyph_id];
   }
 
+  template 
+  void collect_glyphs (set_t &glyphs, unsigned num_glyphs) const
+  {
+    glyphs.add_range (0, num_glyphs - 1);
+  }
+  template 
+  void collect_glyphs_filtered (set_t &glyphs, unsigned num_glyphs, const filter_t &filter) const
+  {
+    for (unsigned i = 0; i < num_glyphs; i++)
+      if (filter (arrayZ[i]))
+        glyphs.add (i);
+  }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -123,6 +289,20 @@ struct LookupSegmentSingle
   int cmp (hb_codepoint_t g) const
   { return g < first ? -1 : g <= last ? 0 : +1 ; }
 
+  template 
+  void collect_glyphs (set_t &glyphs) const
+  {
+    if (first == DELETED_GLYPH) return;
+    glyphs.add_range (first, last);
+  }
+  template 
+  void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
+  {
+    if (first == DELETED_GLYPH) return;
+    if (!filter (value)) return;
+    glyphs.add_range (first, last);
+  }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -153,6 +333,21 @@ struct LookupFormat2
     return v ? &v->value : nullptr;
   }
 
+  template 
+  void collect_glyphs (set_t &glyphs) const
+  {
+    unsigned count = segments.get_length ();
+    for (unsigned int i = 0; i < count; i++)
+      segments[i].collect_glyphs (glyphs);
+  }
+  template 
+  void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
+  {
+    unsigned count = segments.get_length ();
+    for (unsigned int i = 0; i < count; i++)
+      segments[i].collect_glyphs_filtered (glyphs, filter);
+  }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -184,6 +379,22 @@ struct LookupSegmentArray
     return first <= glyph_id && glyph_id <= last ? &(base+valuesZ)[glyph_id - first] : nullptr;
   }
 
+  template 
+  void collect_glyphs (set_t &glyphs) const
+  {
+    if (first == DELETED_GLYPH) return;
+    glyphs.add_range (first, last);
+  }
+  template 
+  void collect_glyphs_filtered (set_t &glyphs, const void *base, const filter_t &filter) const
+  {
+    if (first == DELETED_GLYPH) return;
+    const auto &values = base+valuesZ;
+    for (hb_codepoint_t i = first; i <= last; i++)
+      if (filter (values[i - first]))
+        glyphs.add (i);
+  }
+
   int cmp (hb_codepoint_t g) const
   { return g < first ? -1 : g <= last ? 0 : +1; }
 
@@ -191,6 +402,7 @@ struct LookupSegmentArray
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
+                  hb_barrier () &&
                   first <= last &&
                   valuesZ.sanitize (c, base, last - first + 1));
   }
@@ -199,6 +411,7 @@ struct LookupSegmentArray
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
+                  hb_barrier () &&
                   first <= last &&
                   valuesZ.sanitize (c, base, last - first + 1, std::forward (ds)...));
   }
@@ -224,6 +437,21 @@ struct LookupFormat4
     return v ? v->get_value (glyph_id, this) : nullptr;
   }
 
+  template 
+  void collect_glyphs (set_t &glyphs) const
+  {
+    unsigned count = segments.get_length ();
+    for (unsigned i = 0; i < count; i++)
+      segments[i].collect_glyphs (glyphs);
+  }
+  template 
+  void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
+  {
+    unsigned count = segments.get_length ();
+    for (unsigned i = 0; i < count; i++)
+      segments[i].collect_glyphs_filtered (glyphs, this, filter);
+  }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -252,6 +480,20 @@ struct LookupSingle
 
   int cmp (hb_codepoint_t g) const { return glyph.cmp (g); }
 
+  template 
+  void collect_glyphs (set_t &glyphs) const
+  {
+    if (glyph == DELETED_GLYPH) return;
+    glyphs.add (glyph);
+  }
+  template 
+  void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
+  {
+    if (glyph == DELETED_GLYPH) return;
+    if (!filter (value)) return;
+    glyphs.add (glyph);
+  }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -281,6 +523,21 @@ struct LookupFormat6
     return v ? &v->value : nullptr;
   }
 
+  template 
+  void collect_glyphs (set_t &glyphs) const
+  {
+    unsigned count = entries.get_length ();
+    for (unsigned i = 0; i < count; i++)
+      entries[i].collect_glyphs (glyphs);
+  }
+  template 
+  void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
+  {
+    unsigned count = entries.get_length ();
+    for (unsigned i = 0; i < count; i++)
+      entries[i].collect_glyphs_filtered (glyphs, filter);
+  }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -312,6 +569,24 @@ struct LookupFormat8
            &valueArrayZ[glyph_id - firstGlyph] : nullptr;
   }
 
+  template 
+  void collect_glyphs (set_t &glyphs) const
+  {
+    if (unlikely (!glyphCount)) return;
+    if (firstGlyph == DELETED_GLYPH) return;
+    glyphs.add_range (firstGlyph, firstGlyph + glyphCount - 1);
+  }
+  template 
+  void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
+  {
+    if (unlikely (!glyphCount)) return;
+    if (firstGlyph == DELETED_GLYPH) return;
+    const T *p = valueArrayZ.arrayZ;
+    for (unsigned i = 0; i < glyphCount; i++)
+      if (filter (p[i]))
+        glyphs.add (firstGlyph + i);
+  }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -356,10 +631,19 @@ struct LookupFormat10
     return v;
   }
 
+  template 
+  void collect_glyphs (set_t &glyphs) const
+  {
+    if (unlikely (!glyphCount)) return;
+    if (firstGlyph == DELETED_GLYPH) return;
+    glyphs.add_range (firstGlyph, firstGlyph + glyphCount - 1);
+  }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
+                  hb_barrier () &&
                   valueSize <= 4 &&
                   valueArrayZ.sanitize (c, glyphCount * valueSize));
   }
@@ -383,11 +667,11 @@ struct Lookup
   const T* get_value (hb_codepoint_t glyph_id, unsigned int num_glyphs) const
   {
     switch (u.format) {
-    case 0: return u.format0.get_value (glyph_id, num_glyphs);
-    case 2: return u.format2.get_value (glyph_id);
-    case 4: return u.format4.get_value (glyph_id);
-    case 6: return u.format6.get_value (glyph_id);
-    case 8: return u.format8.get_value (glyph_id);
+    case 0: hb_barrier (); return u.format0.get_value (glyph_id, num_glyphs);
+    case 2: hb_barrier (); return u.format2.get_value (glyph_id);
+    case 4: hb_barrier (); return u.format4.get_value (glyph_id);
+    case 6: hb_barrier (); return u.format6.get_value (glyph_id);
+    case 8: hb_barrier (); return u.format8.get_value (glyph_id);
     default:return nullptr;
     }
   }
@@ -396,13 +680,39 @@ struct Lookup
   {
     switch (u.format) {
       /* Format 10 cannot return a pointer. */
-      case 10: return u.format10.get_value_or_null (glyph_id);
+      case 10: hb_barrier (); return u.format10.get_value_or_null (glyph_id);
       default:
       const T *v = get_value (glyph_id, num_glyphs);
       return v ? *v : Null (T);
     }
   }
 
+  template 
+  void collect_glyphs (set_t &glyphs, unsigned int num_glyphs) const
+  {
+    switch (u.format) {
+    case 0: hb_barrier (); u.format0.collect_glyphs (glyphs, num_glyphs); return;
+    case 2: hb_barrier (); u.format2.collect_glyphs (glyphs); return;
+    case 4: hb_barrier (); u.format4.collect_glyphs (glyphs); return;
+    case 6: hb_barrier (); u.format6.collect_glyphs (glyphs); return;
+    case 8: hb_barrier (); u.format8.collect_glyphs (glyphs); return;
+    case 10: hb_barrier (); u.format10.collect_glyphs (glyphs); return;
+    default:return;
+    }
+  }
+  template 
+  void collect_glyphs_filtered (set_t &glyphs, unsigned num_glyphs, const filter_t &filter) const
+  {
+    switch (u.format) {
+    case 0: hb_barrier (); u.format0.collect_glyphs_filtered (glyphs, num_glyphs, filter); return;
+    case 2: hb_barrier (); u.format2.collect_glyphs_filtered (glyphs, filter); return;
+    case 4: hb_barrier (); u.format4.collect_glyphs_filtered (glyphs, filter); return;
+    case 6: hb_barrier (); u.format6.collect_glyphs_filtered (glyphs, filter); return;
+    case 8: hb_barrier (); u.format8.collect_glyphs_filtered (glyphs, filter); return;
+    default:return;
+    }
+  }
+
   typename T::type get_class (hb_codepoint_t glyph_id,
                               unsigned int num_glyphs,
                               unsigned int outOfRange) const
@@ -415,13 +725,14 @@ struct Lookup
   {
     TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return_trace (false);
+    hb_barrier ();
     switch (u.format) {
-    case 0: return_trace (u.format0.sanitize (c));
-    case 2: return_trace (u.format2.sanitize (c));
-    case 4: return_trace (u.format4.sanitize (c));
-    case 6: return_trace (u.format6.sanitize (c));
-    case 8: return_trace (u.format8.sanitize (c));
-    case 10: return_trace (u.format10.sanitize (c));
+    case 0: hb_barrier (); return_trace (u.format0.sanitize (c));
+    case 2: hb_barrier (); return_trace (u.format2.sanitize (c));
+    case 4: hb_barrier (); return_trace (u.format4.sanitize (c));
+    case 6: hb_barrier (); return_trace (u.format6.sanitize (c));
+    case 8: hb_barrier (); return_trace (u.format8.sanitize (c));
+    case 10: hb_barrier (); return_trace (u.format10.sanitize (c));
     default:return_trace (true);
     }
   }
@@ -429,12 +740,13 @@ struct Lookup
   {
     TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return_trace (false);
+    hb_barrier ();
     switch (u.format) {
-    case 0: return_trace (u.format0.sanitize (c, base));
-    case 2: return_trace (u.format2.sanitize (c, base));
-    case 4: return_trace (u.format4.sanitize (c, base));
-    case 6: return_trace (u.format6.sanitize (c, base));
-    case 8: return_trace (u.format8.sanitize (c, base));
+    case 0: hb_barrier (); return_trace (u.format0.sanitize (c, base));
+    case 2: hb_barrier (); return_trace (u.format2.sanitize (c, base));
+    case 4: hb_barrier (); return_trace (u.format4.sanitize (c, base));
+    case 6: hb_barrier (); return_trace (u.format6.sanitize (c, base));
+    case 8: hb_barrier (); return_trace (u.format8.sanitize (c, base));
     case 10: return_trace (false); /* We don't support format10 here currently. */
     default:return_trace (true);
     }
@@ -455,8 +767,6 @@ struct Lookup
 };
 DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1 (AAT, Lookup, 2);
 
-enum { DELETED_GLYPH = 0xFFFF };
-
 /*
  * (Extended) State Table
  */
@@ -464,7 +774,7 @@ enum { DELETED_GLYPH = 0xFFFF };
 template 
 struct Entry
 {
-  // This does seem like it's ever called.
+  // This doesn't seem like it's ever called.
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -507,6 +817,14 @@ struct Entry
   DEFINE_SIZE_STATIC (4);
 };
 
+enum Class
+{
+  CLASS_END_OF_TEXT = 0,
+  CLASS_OUT_OF_BOUNDS = 1,
+  CLASS_DELETED_GLYPH = 2,
+  CLASS_END_OF_LINE = 3,
+};
+
 template 
 struct StateTable
 {
@@ -519,21 +837,57 @@ struct StateTable
     STATE_START_OF_TEXT = 0,
     STATE_START_OF_LINE = 1,
   };
-  enum Class
+
+  template 
+  void collect_glyphs (set_t &glyphs, unsigned num_glyphs) const
   {
-    CLASS_END_OF_TEXT = 0,
-    CLASS_OUT_OF_BOUNDS = 1,
-    CLASS_DELETED_GLYPH = 2,
-    CLASS_END_OF_LINE = 3,
-  };
+    (this+classTable).collect_glyphs (glyphs, num_glyphs);
+  }
+  template 
+  void collect_initial_glyphs (set_t &glyphs, unsigned num_glyphs, const table_t &table) const
+  {
+    unsigned num_classes = nClasses;
+
+    if (unlikely (num_classes > hb_bit_page_t::BITS))
+    {
+      (this+classTable).collect_glyphs (glyphs, num_glyphs);
+      return;
+    }
+
+    // Collect all classes going out from the start state.
+    hb_bit_page_t filter;
+
+    for (unsigned i = 0; i < num_classes; i++)
+    {
+      const auto &entry = get_entry (STATE_START_OF_TEXT, i);
+      if (new_state (entry.newState) == STATE_START_OF_TEXT &&
+          !table.is_action_initiable (entry) && !table.is_actionable (entry))
+        continue;
+
+      filter.add (i);
+    }
+
+    // And glyphs in those classes.
+
+    if (filter (CLASS_DELETED_GLYPH))
+      glyphs.add (DELETED_GLYPH);
+
+    (this+classTable).collect_glyphs_filtered (glyphs, num_glyphs, filter);
+  }
 
   int new_state (unsigned int newState) const
   { return Types::extended ? newState : ((int) newState - (int) stateArrayTable) / (int) nClasses; }
 
-  unsigned int get_class (hb_codepoint_t glyph_id, unsigned int num_glyphs) const
+  unsigned int get_class (hb_codepoint_t glyph_id,
+                          unsigned int num_glyphs,
+                          hb_aat_class_cache_t *cache = nullptr) const
   {
+    unsigned klass;
+    if (cache && cache->get (glyph_id, &klass)) return klass;
     if (unlikely (glyph_id == DELETED_GLYPH)) return CLASS_DELETED_GLYPH;
-    return (this+classTable).get_class (glyph_id, num_glyphs, 1);
+    klass = (this+classTable).get_class (glyph_id, num_glyphs, CLASS_OUT_OF_BOUNDS);
+    if (cache) cache->set (glyph_id, klass);
+    return klass;
   }
 
   const Entry *get_entries () const
@@ -541,13 +895,14 @@ struct StateTable
 
   const Entry &get_entry (int state, unsigned int klass) const
   {
-    if (unlikely (klass >= nClasses))
-      klass = StateTable::CLASS_OUT_OF_BOUNDS;
+    unsigned n_classes = nClasses;
+    if (unlikely (klass >= n_classes))
+      klass = CLASS_OUT_OF_BOUNDS;
 
     const HBUSHORT *states = (this+stateArrayTable).arrayZ;
     const Entry *entries = (this+entryTable).arrayZ;
 
-    unsigned int entry = states[state * nClasses + klass];
+    unsigned int entry = states[state * n_classes + klass];
     DEBUG_MSG (APPLY, nullptr, "e%u", entry);
 
     return entries[entry];
@@ -558,6 +913,7 @@ struct StateTable
   {
     TRACE_SANITIZE (this);
     if (unlikely (!(c->check_struct (this) &&
+                    hb_barrier () &&
                     nClasses >= 4 /* Ensure pre-defined classes fit.  */ &&
                     classTable.sanitize (c, this)))) return_trace (false);
 
@@ -684,6 +1040,22 @@ struct ClassTable
   {
     return get_class (glyph_id, outOfRange);
   }
+
+  template 
+  void collect_glyphs (set_t &glyphs, unsigned num_glyphs) const
+  {
+    for (unsigned i = 0; i < classArray.len; i++)
+      if (classArray.arrayZ[i] != CLASS_OUT_OF_BOUNDS)
+        glyphs.add (firstGlyph + i);
+  }
+  template 
+  void collect_glyphs_filtered (set_t &glyphs, unsigned num_glyphs, const filter_t &filter) const
+  {
+    for (unsigned i = 0; i < classArray.len; i++)
+      if (filter (classArray.arrayZ[i]))
+        glyphs.add (firstGlyph + i);
+  }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -697,6 +1069,38 @@ struct ClassTable
   DEFINE_SIZE_ARRAY (4, classArray);
 };
 
+struct SubtableGlyphCoverage
+{
+  bool sanitize (hb_sanitize_context_t *c, unsigned subtable_count) const
+  {
+    TRACE_SANITIZE (this);
+
+    if (unlikely (!c->check_array (&subtableOffsets, subtable_count)))
+      return_trace (false);
+
+    unsigned bytes = (c->get_num_glyphs () + CHAR_BIT - 1) / CHAR_BIT;
+    for (unsigned i = 0; i < subtable_count; i++)
+    {
+      uint32_t offset = (uint32_t) subtableOffsets[i];
+      if (offset == 0 || offset == 0xFFFFFFFF)
+        continue;
+      if (unlikely (!subtableOffsets[i].sanitize (c, this, bytes)))
+        return_trace (false);
+    }
+
+    return_trace (true);
+  }
+  protected:
+  UnsizedArrayOf>> subtableOffsets;
+                                            /* Array of offsets from the beginning of the
+                                             * subtable glyph coverage table to the glyph
+                                             * coverage bitfield for a given subtable; there
+                                             * is one offset for each subtable in the chain */
+  /* UnsizedArrayOf coverageBitfields; *//* The individual coverage bitfields. */
+  public:
+  DEFINE_SIZE_ARRAY (0, subtableOffsets);
+};
+
 struct ObsoleteTypes
 {
   static constexpr bool extended = false;
@@ -766,22 +1170,22 @@ struct ExtendedTypes
   }
 };
 
-template 
+template 
 struct StateTableDriver
 {
   using StateTableT = StateTable;
   using EntryT = Entry;
 
   StateTableDriver (const StateTableT &machine_,
-                    hb_buffer_t *buffer_,
                     hb_face_t *face_) :
               machine (machine_),
-              buffer (buffer_),
               num_glyphs (face_->get_num_glyphs ()) {}
 
   template 
   void drive (context_t *c, hb_aat_apply_context_t *ac)
   {
+    hb_buffer_t *buffer = ac->buffer;
+
     if (!c->in_place)
       buffer->clear_output ();
 
@@ -816,9 +1220,9 @@ struct StateTableDriver
         }
       }
 
-      unsigned int klass = buffer->idx < buffer->len ?
-                           machine.get_class (buffer->cur().codepoint, num_glyphs) :
-                           (unsigned) StateTableT::CLASS_END_OF_TEXT;
+      unsigned int klass = likely (buffer->idx < buffer->len) ?
+                           machine.get_class (buffer->cur().codepoint, num_glyphs, ac->machine_class_cache) :
+                           (unsigned) CLASS_END_OF_TEXT;
       DEBUG_MSG (APPLY, nullptr, "c%u at %u", klass, buffer->idx);
       const EntryT &entry = machine.get_entry (state, klass);
       const int next_state = machine.new_state (entry.newState);
@@ -851,44 +1255,39 @@ struct StateTableDriver
        *
        *   https://github.com/harfbuzz/harfbuzz/issues/2860
        */
-
-      const auto is_safe_to_break_extra = [&]()
-      {
-          /* 2c. */
-          const auto wouldbe_entry = machine.get_entry(StateTableT::STATE_START_OF_TEXT, klass);
-
-          /* 2c'. */
-          if (c->is_actionable (this, wouldbe_entry))
-              return false;
-
-          /* 2c". */
-          return next_state == machine.new_state(wouldbe_entry.newState)
-              && (entry.flags & context_t::DontAdvance) == (wouldbe_entry.flags & context_t::DontAdvance);
-      };
-
-      const auto is_safe_to_break = [&]()
-      {
+      const EntryT *wouldbe_entry;
+      bool is_safe_to_break =
+      (
           /* 1. */
-          if (c->is_actionable (this, entry))
-              return false;
+          !c->table->is_actionable (entry) &&
 
           /* 2. */
           // This one is meh, I know...
-          const auto ok =
+          (
                  state == StateTableT::STATE_START_OF_TEXT
-              || ((entry.flags & context_t::DontAdvance) && next_state == StateTableT::STATE_START_OF_TEXT)
-              || is_safe_to_break_extra();
-          if (!ok)
-              return false;
+              || ((entry.flags & Flags::DontAdvance) && next_state == StateTableT::STATE_START_OF_TEXT)
+              || (
+                    /* 2c. */
+                    wouldbe_entry = &machine.get_entry(StateTableT::STATE_START_OF_TEXT, klass)
+                    ,
+                    /* 2c'. */
+                    !c->table->is_actionable (*wouldbe_entry) &&
+                    /* 2c". */
+                    (
+                      next_state == machine.new_state(wouldbe_entry->newState) &&
+                      (entry.flags & Flags::DontAdvance) == (wouldbe_entry->flags & Flags::DontAdvance)
+                    )
+                 )
+          ) &&
 
           /* 3. */
-          return !c->is_actionable (this, machine.get_entry (state, StateTableT::CLASS_END_OF_TEXT));
-      };
+          !c->table->is_actionable (machine.get_entry (state, CLASS_END_OF_TEXT))
+      );
 
-      if (!is_safe_to_break () && buffer->backtrack_len () && buffer->idx < buffer->len)
+      if (!is_safe_to_break && buffer->backtrack_len () && buffer->idx < buffer->len)
         buffer->unsafe_to_break_from_outbuffer (buffer->backtrack_len () - 1, buffer->idx + 1);
 
-      c->transition (this, entry);
+      c->transition (buffer, this, entry);
 
       state = next_state;
       DEBUG_MSG (APPLY, nullptr, "s%d", state);
@@ -896,7 +1295,7 @@ struct StateTableDriver
       if (buffer->idx == buffer->len || unlikely (!buffer->successful))
         break;
 
-      if (!(entry.flags & context_t::DontAdvance) || buffer->max_ops-- <= 0)
+      if (!(entry.flags & Flags::DontAdvance) || buffer->max_ops-- <= 0)
         (void) buffer->next_glyph ();
     }
 
@@ -906,7 +1305,6 @@ struct StateTableDriver
 
   public:
   const StateTableT &machine;
-  hb_buffer_t *buffer;
   unsigned int num_glyphs;
 };
 
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-feat-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-feat-table.hh
index 04260a946168a..7b68a932c3f1e 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-feat-table.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-feat-table.hh
@@ -138,6 +138,7 @@ struct FeatureName
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this) &&
+                          hb_barrier () &&
                           (base+settingTableZ).sanitize (c, nSettings)));
   }
 
@@ -200,6 +201,7 @@ struct feat
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this) &&
+                          hb_barrier () &&
                           version.major == 1 &&
                           namesZ.sanitize (c, featureNameCount, this)));
   }
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-just-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-just-table.hh
index 0588310b53b78..193d14d94d075 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-just-table.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-just-table.hh
@@ -185,15 +185,16 @@ struct ActionSubrecord
     TRACE_SANITIZE (this);
     if (unlikely (!c->check_struct (this)))
       return_trace (false);
+    hb_barrier ();
 
     switch (u.header.actionType)
     {
-    case 0:  return_trace (u.decompositionAction.sanitize (c));
-    case 1:  return_trace (u.unconditionalAddGlyphAction.sanitize (c));
-    case 2:  return_trace (u.conditionalAddGlyphAction.sanitize (c));
-    // case 3: return_trace (u.stretchGlyphAction.sanitize (c));
-    case 4:  return_trace (u.decompositionAction.sanitize (c));
-    case 5:  return_trace (u.decompositionAction.sanitize (c));
+    case 0: hb_barrier ();  return_trace (u.decompositionAction.sanitize (c));
+    case 1: hb_barrier ();  return_trace (u.unconditionalAddGlyphAction.sanitize (c));
+    case 2: hb_barrier ();  return_trace (u.conditionalAddGlyphAction.sanitize (c));
+    // case 3: hb_barrier (); return_trace (u.stretchGlyphAction.sanitize (c));
+    case 4: hb_barrier ();  return_trace (u.decompositionAction.sanitize (c));
+    case 5: hb_barrier ();  return_trace (u.decompositionAction.sanitize (c));
     default: return_trace (true);
     }
   }
@@ -220,6 +221,7 @@ struct PostcompensationActionChain
     TRACE_SANITIZE (this);
     if (unlikely (!c->check_struct (this)))
       return_trace (false);
+    hb_barrier ();
 
     unsigned int offset = min_size;
     for (unsigned int i = 0; i < count; i++)
@@ -389,6 +391,7 @@ struct just
     TRACE_SANITIZE (this);
 
     return_trace (likely (c->check_struct (this) &&
+                          hb_barrier () &&
                           version.major == 1 &&
                           horizData.sanitize (c, this, this) &&
                           vertData.sanitize (c, this, this)));
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-kerx-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-kerx-table.hh
index 2bc7b03ce896f..ca4f224334699 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-kerx-table.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-kerx-table.hh
@@ -30,6 +30,7 @@
 
 #include "hb-kern.hh"
 #include "hb-aat-layout-ankr-table.hh"
+#include "hb-set-digest.hh"
 
 /*
  * kerx -- Extended Kerning
@@ -54,6 +55,7 @@ kerxTupleKern (int value,
   unsigned int offset = value;
   const FWORD *pv = &StructAtOffset (base, offset);
   if (unlikely (!c->sanitizer.check_array (pv, tupleCount))) return 0;
+  hb_barrier ();
   return *pv;
 }
 
@@ -81,7 +83,7 @@ struct KernPair
     return_trace (c->check_struct (this));
   }
 
-  protected:
+  public:
   HBGlyphID16   left;
   HBGlyphID16   right;
   FWORD         value;
@@ -105,10 +107,10 @@ struct KerxSubTableFormat0
     TRACE_APPLY (this);
 
     if (!c->plan->requested_kerning)
-      return false;
+      return_trace (false);
 
     if (header.coverage & header.Backwards)
-      return false;
+      return_trace (false);
 
     accelerator_t accel (*this, c);
     hb_kern_machine_t machine (accel, header.coverage & header.CrossStream);
@@ -117,6 +119,16 @@ struct KerxSubTableFormat0
     return_trace (true);
   }
 
+  template 
+  void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const
+  {
+    for (const KernPair& pair : pairs)
+    {
+      left_set.add (pair.left);
+      right_set.add (pair.right);
+    }
+  }
+
   struct accelerator_t
   {
     const KerxSubTableFormat0 &table;
@@ -127,7 +139,10 @@ struct KerxSubTableFormat0
                      table (table_), c (c_) {}
 
     int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
-    { return table.get_kerning (left, right, c); }
+    {
+      if (!(*c->left_set)[left] || !(*c->right_set)[right]) return 0;
+      return table.get_kerning (left, right, c);
+    }
   };
 
 
@@ -170,6 +185,9 @@ struct Format1Entry
     DEFINE_SIZE_STATIC (2);
   };
 
+  static bool initiateAction (const Entry &entry)
+  { return entry.flags & Push; }
+
   static bool performAction (const Entry &entry)
   { return entry.data.kernActionIndex != 0xFFFF; }
 
@@ -192,6 +210,9 @@ struct Format1Entry
 
   typedef void EntryData;
 
+  static bool initiateAction (const Entry &entry)
+  { return entry.flags & Push; }
+
   static bool performAction (const Entry &entry)
   { return entry.flags & Offset; }
 
@@ -208,13 +229,23 @@ struct KerxSubTableFormat1
   typedef Format1Entry Format1EntryT;
   typedef typename Format1EntryT::EntryData EntryData;
 
+  enum Flags
+  {
+    DontAdvance = Format1EntryT::DontAdvance,
+  };
+
+  bool is_action_initiable (const Entry &entry) const
+  {
+    return Format1EntryT::initiateAction (entry);
+  }
+  bool is_actionable (const Entry &entry) const
+  {
+    return Format1EntryT::performAction (entry);
+  }
+
   struct driver_context_t
   {
     static constexpr bool in_place = true;
-    enum
-    {
-      DontAdvance       = Format1EntryT::DontAdvance,
-    };
 
     driver_context_t (const KerxSubTableFormat1 *table_,
                       hb_aat_apply_context_t *c_) :
@@ -227,13 +258,10 @@ struct KerxSubTableFormat1
         depth (0),
         crossStream (table->header.coverage & table->header.CrossStream) {}
 
-    bool is_actionable (StateTableDriver *driver HB_UNUSED,
-                        const Entry &entry)
-    { return Format1EntryT::performAction (entry); }
-    void transition (StateTableDriver *driver,
+    void transition (hb_buffer_t *buffer,
+                     StateTableDriver *driver,
                      const Entry &entry)
     {
-      hb_buffer_t *buffer = driver->buffer;
       unsigned int flags = entry.flags;
 
       if (flags & Format1EntryT::Reset)
@@ -259,6 +287,7 @@ struct KerxSubTableFormat1
           depth = 0;
           return;
         }
+        hb_barrier ();
 
         hb_mask_t kern_mask = c->plan->kern_mask;
 
@@ -299,8 +328,9 @@ struct KerxSubTableFormat1
             }
             else if (buffer->info[idx].mask & kern_mask)
             {
-              o.x_advance += c->font->em_scale_x (v);
-              o.x_offset += c->font->em_scale_x (v);
+              auto scaled = c->font->em_scale_x (v);
+              o.x_advance += scaled;
+              o.x_offset += scaled;
             }
           }
           else
@@ -330,9 +360,10 @@ struct KerxSubTableFormat1
       }
     }
 
-    private:
+    public:
     hb_aat_apply_context_t *c;
     const KerxSubTableFormat1 *table;
+    private:
     const UnsizedArrayOf &kernAction;
     unsigned int stack[8];
     unsigned int depth;
@@ -349,7 +380,8 @@ struct KerxSubTableFormat1
 
     driver_context_t dc (this, c);
 
-    StateTableDriver driver (machine, c->buffer, c->font->face);
+    StateTableDriver driver (machine, c->font->face);
+
     driver.drive (&dc, c);
 
     return_trace (true);
@@ -363,12 +395,19 @@ struct KerxSubTableFormat1
                           machine.sanitize (c)));
   }
 
+  template 
+  void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const
+  {
+    machine.collect_initial_glyphs (left_set, num_glyphs, *this);
+    //machine.collect_glyphs (right_set, num_glyphs); // right_set is unused for machine kerning
+  }
+
   protected:
   KernSubTableHeader                            header;
   StateTable                  machine;
   NNOffsetTo, HBUINT>     kernAction;
   public:
-  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 5 * sizeof (HBUINT));
+  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + (StateTable::static_size + HBUINT::static_size));
 };
 
 template 
@@ -389,6 +428,7 @@ struct KerxSubTableFormat2
     kern_idx = Types::offsetToIndex (kern_idx, this, arrayZ.arrayZ);
     const FWORD *v = &arrayZ[kern_idx];
     if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
+    hb_barrier ();
 
     return kerxTupleKern (*v, header.tuple_count (), this, c);
   }
@@ -398,10 +438,10 @@ struct KerxSubTableFormat2
     TRACE_APPLY (this);
 
     if (!c->plan->requested_kerning)
-      return false;
+      return_trace (false);
 
     if (header.coverage & header.Backwards)
-      return false;
+      return_trace (false);
 
     accelerator_t accel (*this, c);
     hb_kern_machine_t machine (accel, header.coverage & header.CrossStream);
@@ -410,6 +450,13 @@ struct KerxSubTableFormat2
     return_trace (true);
   }
 
+  template 
+  void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const
+  {
+    (this+leftClassTable).collect_glyphs (left_set, num_glyphs);
+    (this+rightClassTable).collect_glyphs (right_set, num_glyphs);
+  }
+
   struct accelerator_t
   {
     const KerxSubTableFormat2 &table;
@@ -420,7 +467,10 @@ struct KerxSubTableFormat2
                      table (table_), c (c_) {}
 
     int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
-    { return table.get_kerning (left, right, c); }
+    {
+      if (!(*c->left_set)[left] || !(*c->right_set)[right]) return 0;
+      return table.get_kerning (left, right, c);
+    }
   };
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -429,6 +479,7 @@ struct KerxSubTableFormat2
     return_trace (likely (c->check_struct (this) &&
                           leftClassTable.sanitize (c, this) &&
                           rightClassTable.sanitize (c, this) &&
+                          hb_barrier () &&
                           c->check_range (this, array)));
   }
 
@@ -461,17 +512,26 @@ struct KerxSubTableFormat4
     DEFINE_SIZE_STATIC (2);
   };
 
-  struct driver_context_t
+  enum Flags
   {
-    static constexpr bool in_place = true;
-    enum Flags
-    {
-      Mark              = 0x8000,       /* If set, remember this glyph as the marked glyph. */
-      DontAdvance       = 0x4000,       /* If set, don't advance to the next glyph before
+    Mark                = 0x8000,       /* If set, remember this glyph as the marked glyph. */
+    DontAdvance         = 0x4000,       /* If set, don't advance to the next glyph before
                                          * going to the new state. */
-      Reserved          = 0x3FFF,       /* Not used; set to 0. */
-    };
+    Reserved            = 0x3FFF,       /* Not used; set to 0. */
+  };
+
+  bool is_action_initiable (const Entry &entry) const
+  {
+    return (entry.flags & Mark);
+  }
+  bool is_actionable (const Entry &entry) const
+  {
+    return entry.data.ankrActionIndex != 0xFFFF;
+  }
 
+  struct driver_context_t
+  {
+    static constexpr bool in_place = true;
     enum SubTableFlags
     {
       ActionType        = 0xC0000000,   /* A two-bit field containing the action type. */
@@ -481,22 +541,19 @@ struct KerxSubTableFormat4
                                          * point table. */
     };
 
-    driver_context_t (const KerxSubTableFormat4 *table,
+    driver_context_t (const KerxSubTableFormat4 *table_,
                       hb_aat_apply_context_t *c_) :
         c (c_),
+        table (table_),
         action_type ((table->flags & ActionType) >> 30),
         ankrData ((HBUINT16 *) ((const char *) &table->machine + (table->flags & Offset))),
         mark_set (false),
         mark (0) {}
 
-    bool is_actionable (StateTableDriver *driver HB_UNUSED,
-                        const Entry &entry)
-    { return entry.data.ankrActionIndex != 0xFFFF; }
-    void transition (StateTableDriver *driver,
+    void transition (hb_buffer_t *buffer,
+                     StateTableDriver *driver,
                      const Entry &entry)
     {
-      hb_buffer_t *buffer = driver->buffer;
-
       if (mark_set && entry.data.ankrActionIndex != 0xFFFF && buffer->idx < buffer->len)
       {
         hb_glyph_position_t &o = buffer->cur_pos();
@@ -509,6 +566,7 @@ struct KerxSubTableFormat4
                double the ankrActionIndex to get the correct offset here. */
             const HBUINT16 *data = &ankrData[entry.data.ankrActionIndex * 2];
             if (!c->sanitizer.check_array (data, 2)) return;
+            hb_barrier ();
             unsigned int markControlPoint = *data++;
             unsigned int currControlPoint = *data++;
             hb_position_t markX = 0;
@@ -537,6 +595,7 @@ struct KerxSubTableFormat4
                double the ankrActionIndex to get the correct offset here. */
             const HBUINT16 *data = &ankrData[entry.data.ankrActionIndex * 2];
             if (!c->sanitizer.check_array (data, 2)) return;
+            hb_barrier ();
             unsigned int markAnchorPoint = *data++;
             unsigned int currAnchorPoint = *data++;
             const Anchor &markAnchor = c->ankr_table->get_anchor (c->buffer->info[mark].codepoint,
@@ -557,6 +616,7 @@ struct KerxSubTableFormat4
                by 4 to get the correct offset for the given action. */
             const FWORD *data = (const FWORD *) &ankrData[entry.data.ankrActionIndex * 4];
             if (!c->sanitizer.check_array (data, 4)) return;
+            hb_barrier ();
             int markX = *data++;
             int markY = *data++;
             int currX = *data++;
@@ -579,8 +639,10 @@ struct KerxSubTableFormat4
       }
     }
 
-    private:
+    public:
     hb_aat_apply_context_t *c;
+    const KerxSubTableFormat4 *table;
+    private:
     unsigned int action_type;
     const HBUINT16 *ankrData;
     bool mark_set;
@@ -593,7 +655,8 @@ struct KerxSubTableFormat4
 
     driver_context_t dc (this, c);
 
-    StateTableDriver driver (machine, c->buffer, c->font->face);
+    StateTableDriver driver (machine, c->font->face);
+
     driver.drive (&dc, c);
 
     return_trace (true);
@@ -607,12 +670,19 @@ struct KerxSubTableFormat4
                           machine.sanitize (c)));
   }
 
+  template 
+  void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const
+  {
+    machine.collect_initial_glyphs (left_set, num_glyphs, *this);
+    //machine.collect_glyphs (right_set, num_glyphs); // right_set is unused for machine kerning
+  }
+
   protected:
   KernSubTableHeader            header;
   StateTable  machine;
   HBUINT32                      flags;
   public:
-  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 20);
+  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + (StateTable::static_size + HBUINT32::static_size));
 };
 
 template 
@@ -631,7 +701,7 @@ struct KerxSubTableFormat6
     unsigned int num_glyphs = c->sanitizer.get_num_glyphs ();
     if (is_long ())
     {
-      const typename U::Long &t = u.l;
+      const auto &t = u.l;
       unsigned int l = (this+t.rowIndexTable).get_value_or_null (left, num_glyphs);
       unsigned int r = (this+t.columnIndexTable).get_value_or_null (right, num_glyphs);
       unsigned int offset = l + r;
@@ -639,16 +709,18 @@ struct KerxSubTableFormat6
       if (unlikely (hb_unsigned_mul_overflows (offset, sizeof (FWORD32)))) return 0;
       const FWORD32 *v = &StructAtOffset (&(this+t.array), offset * sizeof (FWORD32));
       if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
+      hb_barrier ();
       return kerxTupleKern (*v, header.tuple_count (), &(this+vector), c);
     }
     else
     {
-      const typename U::Short &t = u.s;
+      const auto &t = u.s;
       unsigned int l = (this+t.rowIndexTable).get_value_or_null (left, num_glyphs);
       unsigned int r = (this+t.columnIndexTable).get_value_or_null (right, num_glyphs);
       unsigned int offset = l + r;
       const FWORD *v = &StructAtOffset (&(this+t.array), offset * sizeof (FWORD));
       if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
+      hb_barrier ();
       return kerxTupleKern (*v, header.tuple_count (), &(this+vector), c);
     }
   }
@@ -658,10 +730,10 @@ struct KerxSubTableFormat6
     TRACE_APPLY (this);
 
     if (!c->plan->requested_kerning)
-      return false;
+      return_trace (false);
 
     if (header.coverage & header.Backwards)
-      return false;
+      return_trace (false);
 
     accelerator_t accel (*this, c);
     hb_kern_machine_t machine (accel, header.coverage & header.CrossStream);
@@ -674,6 +746,7 @@ struct KerxSubTableFormat6
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this) &&
+                          hb_barrier () &&
                           (is_long () ?
                            (
                              u.l.rowIndexTable.sanitize (c, this) &&
@@ -688,6 +761,23 @@ struct KerxSubTableFormat6
                            c->check_range (this, vector))));
   }
 
+  template 
+  void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const
+  {
+    if (is_long ())
+    {
+      const auto &t = u.l;
+      (this+t.rowIndexTable).collect_glyphs (left_set, num_glyphs);
+      (this+t.columnIndexTable).collect_glyphs (right_set, num_glyphs);
+    }
+    else
+    {
+      const auto &t = u.s;
+      (this+t.rowIndexTable).collect_glyphs (left_set, num_glyphs);
+      (this+t.columnIndexTable).collect_glyphs (right_set, num_glyphs);
+    }
+  }
+
   struct accelerator_t
   {
     const KerxSubTableFormat6 &table;
@@ -698,7 +788,10 @@ struct KerxSubTableFormat6
                      table (table_), c (c_) {}
 
     int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
-    { return table.get_kerning (left, right, c); }
+    {
+      if (!(*c->left_set)[left] || !(*c->right_set)[right]) return 0;
+      return table.get_kerning (left, right, c);
+    }
   };
 
   protected:
@@ -784,12 +877,27 @@ struct KerxSubTable
     }
   }
 
+  template 
+  void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const
+  {
+    unsigned int subtable_type = get_type ();
+    switch (subtable_type) {
+    case 0:     u.format0.collect_glyphs (left_set, right_set, num_glyphs); return;
+    case 1:     u.format1.collect_glyphs (left_set, right_set, num_glyphs); return;
+    case 2:     u.format2.collect_glyphs (left_set, right_set, num_glyphs); return;
+    case 4:     u.format4.collect_glyphs (left_set, right_set, num_glyphs); return;
+    case 6:     u.format6.collect_glyphs (left_set, right_set, num_glyphs); return;
+    default:    return;
+    }
+  }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (!u.header.sanitize (c) ||
-        u.header.length <= u.header.static_size ||
-        !c->check_range (this, u.header.length))
+    if (!(u.header.sanitize (c) &&
+          hb_barrier () &&
+          u.header.length >= u.header.static_size &&
+          c->check_range (this, u.header.length)))
       return_trace (false);
 
     return_trace (dispatch (c));
@@ -813,6 +921,19 @@ struct KerxSubTable
  * The 'kerx' Table
  */
 
+struct kern_subtable_accelerator_data_t
+{
+  hb_bit_set_t left_set;
+  hb_bit_set_t right_set;
+  mutable hb_aat_class_cache_t class_cache;
+};
+
+struct kern_accelerator_data_t
+{
+  hb_vector_t subtable_accels;
+  hb_aat_scratch_t scratch;
+};
+
 template 
 struct KerxTable
 {
@@ -829,6 +950,9 @@ struct KerxTable
     {
       if (st->get_type () == 1)
         return true;
+
+      // TODO: What about format 4? What's this API used for anyway?
+
       st = &StructAfter (*st);
     }
     return false;
@@ -867,10 +991,13 @@ struct KerxTable
     return v;
   }
 
-  bool apply (AAT::hb_aat_apply_context_t *c) const
+  bool apply (AAT::hb_aat_apply_context_t *c,
+              const kern_accelerator_data_t &accel_data) const
   {
     c->buffer->unsafe_to_concat ();
 
+    c->setup_buffer_glyph_set ();
+
     typedef typename T::SubTable SubTable;
 
     bool ret = false;
@@ -882,12 +1009,25 @@ struct KerxTable
     {
       bool reverse;
 
+      auto &subtable_accel = accel_data.subtable_accels[i];
+
       if (!T::Types::extended && (st->u.header.coverage & st->u.header.Variation))
         goto skip;
 
       if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction) != st->u.header.is_horizontal ())
         goto skip;
 
+      c->left_set = &subtable_accel.left_set;
+      c->right_set = &subtable_accel.right_set;
+      c->machine_glyph_set = &subtable_accel.left_set;
+      c->machine_class_cache = &subtable_accel.class_cache;
+
+      if (!c->buffer_intersects_machine ())
+      {
+        (void) c->buffer->message (c->font, "skipped subtable %u because no glyph matches", c->lookup_index);
+        goto skip;
+      }
+
       reverse = bool (st->u.header.coverage & st->u.header.Backwards) !=
                 HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
 
@@ -936,9 +1076,10 @@ struct KerxTable
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (unlikely (!thiz()->version.sanitize (c) ||
-                  (unsigned) thiz()->version < (unsigned) T::minVersion ||
-                  !thiz()->tableCount.sanitize (c)))
+    if (unlikely (!(thiz()->version.sanitize (c) &&
+                    hb_barrier () &&
+                    (unsigned) thiz()->version >= (unsigned) T::minVersion &&
+                    thiz()->tableCount.sanitize (c))))
       return_trace (false);
 
     typedef typename T::SubTable SubTable;
@@ -949,6 +1090,7 @@ struct KerxTable
     {
       if (unlikely (!st->u.header.sanitize (c)))
         return_trace (false);
+      hb_barrier ();
       /* OpenType kern table has 2-byte subtable lengths.  That's limiting.
        * MS implementation also only supports one subtable, of format 0,
        * anyway.  Certain versions of some fonts, like Calibry, contain
@@ -964,8 +1106,66 @@ struct KerxTable
       st = &StructAfter (*st);
     }
 
+    unsigned majorVersion = thiz()->version;
+    if (sizeof (thiz()->version) == 4)
+      majorVersion = majorVersion >> 16;
+    if (majorVersion >= 3)
+    {
+      const SubtableGlyphCoverage *coverage = (const SubtableGlyphCoverage *) st;
+      if (!coverage->sanitize (c, count))
+        return_trace (false);
+    }
+
     return_trace (true);
   }
+
+  kern_accelerator_data_t create_accelerator_data (unsigned num_glyphs) const
+  {
+    kern_accelerator_data_t accel_data;
+
+    typedef typename T::SubTable SubTable;
+
+    const SubTable *st = &thiz()->firstSubTable;
+    unsigned int count = thiz()->tableCount;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      auto &subtable_accel = *accel_data.subtable_accels.push ();
+      if (unlikely (accel_data.subtable_accels.in_error ()))
+          return accel_data;
+
+      st->collect_glyphs (subtable_accel.left_set, subtable_accel.right_set, num_glyphs);
+      subtable_accel.class_cache.clear ();
+
+      st = &StructAfter (*st);
+    }
+
+    return accel_data;
+  }
+
+  struct accelerator_t
+  {
+    accelerator_t (hb_face_t *face)
+    {
+      hb_sanitize_context_t sc;
+      this->table = sc.reference_table (face);
+      this->accel_data = this->table->create_accelerator_data (face->get_num_glyphs ());
+    }
+    ~accelerator_t ()
+    {
+      this->table.destroy ();
+    }
+
+    hb_blob_t *get_blob () const { return table.get_blob (); }
+
+    bool apply (AAT::hb_aat_apply_context_t *c) const
+    {
+      return table->apply (c, accel_data);
+    }
+
+    hb_blob_ptr_t table;
+    kern_accelerator_data_t accel_data;
+    hb_aat_scratch_t scratch;
+  };
 };
 
 struct kerx : KerxTable
@@ -994,8 +1194,10 @@ struct kerx : KerxTable
   DEFINE_SIZE_MIN (8);
 };
 
+struct kerx_accelerator_t : kerx::accelerator_t {
+  kerx_accelerator_t (hb_face_t *face) : kerx::accelerator_t (face) {}
+};
 
 } /* namespace AAT */
 
-
 #endif /* HB_AAT_LAYOUT_KERX_TABLE_HH */
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-morx-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-morx-table.hh
index 81e126d5eb11a..92b07eec6e513 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-morx-table.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-morx-table.hh
@@ -29,8 +29,7 @@
 
 #include "hb-open-type.hh"
 #include "hb-aat-layout-common.hh"
-#include "hb-ot-layout-common.hh"
-#include "hb-ot-layout-gdef-table.hh"
+#include "hb-ot-layout.hh"
 #include "hb-aat-map.hh"
 
 /*
@@ -53,36 +52,42 @@ struct RearrangementSubtable
 
   typedef void EntryData;
 
-  struct driver_context_t
+  enum Flags
   {
-    static constexpr bool in_place = true;
-    enum Flags
-    {
-      MarkFirst         = 0x8000,       /* If set, make the current glyph the first
+    MarkFirst           = 0x8000,       /* If set, make the current glyph the first
                                          * glyph to be rearranged. */
-      DontAdvance       = 0x4000,       /* If set, don't advance to the next glyph
+    DontAdvance         = 0x4000,       /* If set, don't advance to the next glyph
                                          * before going to the new state. This means
                                          * that the glyph index doesn't change, even
                                          * if the glyph at that index has changed. */
-      MarkLast          = 0x2000,       /* If set, make the current glyph the last
+    MarkLast            = 0x2000,       /* If set, make the current glyph the last
                                          * glyph to be rearranged. */
-      Reserved          = 0x1FF0,       /* These bits are reserved and should be set to 0. */
-      Verb              = 0x000F,       /* The type of rearrangement specified. */
-    };
+    Reserved            = 0x1FF0,       /* These bits are reserved and should be set to 0. */
+    Verb                = 0x000F,       /* The type of rearrangement specified. */
+  };
+
+  bool is_action_initiable (const Entry &entry) const
+  {
+    return (entry.flags & MarkFirst);
+  }
+  bool is_actionable (const Entry &entry) const
+  {
+    return (entry.flags & Verb);
+  }
+
+  struct driver_context_t
+  {
+    static constexpr bool in_place = true;
 
-    driver_context_t (const RearrangementSubtable *table HB_UNUSED) :
+    driver_context_t (const RearrangementSubtable *table_) :
         ret (false),
+        table (table_),
         start (0), end (0) {}
 
-    bool is_actionable (StateTableDriver *driver HB_UNUSED,
-                        const Entry &entry)
-    {
-      return (entry.flags & Verb) && start < end;
-    }
-    void transition (StateTableDriver *driver,
+    void transition (hb_buffer_t *buffer,
+                     StateTableDriver *driver,
                      const Entry &entry)
     {
-      hb_buffer_t *buffer = driver->buffer;
       unsigned int flags = entry.flags;
 
       if (flags & MarkFirst)
@@ -157,6 +162,7 @@ struct RearrangementSubtable
 
     public:
     bool ret;
+    const RearrangementSubtable *table;
     private:
     unsigned int start;
     unsigned int end;
@@ -168,7 +174,8 @@ struct RearrangementSubtable
 
     driver_context_t dc (this);
 
-    StateTableDriver driver (machine, c->buffer, c->face);
+    StateTableDriver driver (machine, c->face);
+
     driver.drive (&dc, c);
 
     return_trace (dc.ret);
@@ -180,10 +187,10 @@ struct RearrangementSubtable
     return_trace (machine.sanitize (c));
   }
 
-  protected:
+  public:
   StateTable  machine;
   public:
-  DEFINE_SIZE_STATIC (16);
+  DEFINE_SIZE_STATIC ((StateTable::static_size));
 };
 
 template 
@@ -201,43 +208,40 @@ struct ContextualSubtable
     DEFINE_SIZE_STATIC (4);
   };
 
+  enum Flags
+  {
+    SetMark             = 0x8000,       /* If set, make the current glyph the marked glyph. */
+    DontAdvance         = 0x4000,       /* If set, don't advance to the next glyph before
+                                         * going to the new state. */
+    Reserved            = 0x3FFF,       /* These bits are reserved and should be set to 0. */
+  };
+
+  bool is_action_initiable (const Entry &entry) const
+  {
+    return (entry.flags & SetMark);
+  }
+  bool is_actionable (const Entry &entry) const
+  {
+    return entry.data.markIndex != 0xFFFF || entry.data.currentIndex != 0xFFFF;
+  }
+
   struct driver_context_t
   {
     static constexpr bool in_place = true;
-    enum Flags
-    {
-      SetMark           = 0x8000,       /* If set, make the current glyph the marked glyph. */
-      DontAdvance       = 0x4000,       /* If set, don't advance to the next glyph before
-                                         * going to the new state. */
-      Reserved          = 0x3FFF,       /* These bits are reserved and should be set to 0. */
-    };
 
     driver_context_t (const ContextualSubtable *table_,
                              hb_aat_apply_context_t *c_) :
         ret (false),
         c (c_),
-        gdef (*c->gdef_table),
+        table (table_),
         mark_set (false),
-        has_glyph_classes (gdef.has_glyph_classes ()),
         mark (0),
-        table (table_),
         subs (table+table->substitutionTables) {}
 
-    bool is_actionable (StateTableDriver *driver,
-                        const Entry &entry)
-    {
-      hb_buffer_t *buffer = driver->buffer;
-
-      if (buffer->idx == buffer->len && !mark_set)
-        return false;
-
-      return entry.data.markIndex != 0xFFFF || entry.data.currentIndex != 0xFFFF;
-    }
-    void transition (StateTableDriver *driver,
+    void transition (hb_buffer_t *buffer,
+                     StateTableDriver *driver,
                      const Entry &entry)
     {
-      hb_buffer_t *buffer = driver->buffer;
-
       /* Looks like CoreText applies neither mark nor current substitution for
        * end-of-text if mark was not explicitly set. */
       if (buffer->idx == buffer->len && !mark_set)
@@ -259,16 +263,15 @@ struct ContextualSubtable
         unsigned int offset = entry.data.markIndex + buffer->info[mark].codepoint;
         const UnsizedArrayOf &subs_old = (const UnsizedArrayOf &) subs;
         replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)];
-        if (!replacement->sanitize (&c->sanitizer) || !*replacement)
+        if (!(replacement->sanitize (&c->sanitizer) &&
+              hb_barrier () &&
+              *replacement))
           replacement = nullptr;
       }
       if (replacement)
       {
         buffer->unsafe_to_break (mark, hb_min (buffer->idx + 1, buffer->len));
-        buffer->info[mark].codepoint = *replacement;
-        if (has_glyph_classes)
-          _hb_glyph_info_set_glyph_props (&buffer->info[mark],
-                                          gdef.get_glyph_props (*replacement));
+        c->replace_glyph_inplace (mark, *replacement);
         ret = true;
       }
 
@@ -287,15 +290,14 @@ struct ContextualSubtable
         unsigned int offset = entry.data.currentIndex + buffer->info[idx].codepoint;
         const UnsizedArrayOf &subs_old = (const UnsizedArrayOf &) subs;
         replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)];
-        if (!replacement->sanitize (&c->sanitizer) || !*replacement)
+        if (!(replacement->sanitize (&c->sanitizer) &&
+              hb_barrier () &&
+              *replacement))
           replacement = nullptr;
       }
       if (replacement)
       {
-        buffer->info[idx].codepoint = *replacement;
-        if (has_glyph_classes)
-          _hb_glyph_info_set_glyph_props (&buffer->info[idx],
-                                          gdef.get_glyph_props (*replacement));
+        c->replace_glyph_inplace (idx, *replacement);
         ret = true;
       }
 
@@ -308,14 +310,12 @@ struct ContextualSubtable
 
     public:
     bool ret;
-    private:
     hb_aat_apply_context_t *c;
-    const OT::GDEF &gdef;
+    const ContextualSubtable *table;
+    private:
     bool mark_set;
-    bool has_glyph_classes;
     unsigned int mark;
-    const ContextualSubtable *table;
-    const UnsizedListOfOffset16To, HBUINT, false> &subs;
+    const UnsizedListOfOffset16To, HBUINT, void, false> &subs;
   };
 
   bool apply (hb_aat_apply_context_t *c) const
@@ -324,7 +324,8 @@ struct ContextualSubtable
 
     driver_context_t dc (this, c);
 
-    StateTableDriver driver (machine, c->buffer, c->face);
+    StateTableDriver driver (machine, c->face);
+
     driver.drive (&dc, c);
 
     return_trace (dc.ret);
@@ -336,6 +337,7 @@ struct ContextualSubtable
 
     unsigned int num_entries = 0;
     if (unlikely (!machine.sanitize (c, &num_entries))) return_trace (false);
+    hb_barrier ();
 
     if (!Types::extended)
       return_trace (substitutionTables.sanitize (c, this, 0));
@@ -356,13 +358,14 @@ struct ContextualSubtable
     return_trace (substitutionTables.sanitize (c, this, num_lookups));
   }
 
-  protected:
+  public:
   StateTable
                 machine;
-  NNOffsetTo, HBUINT, false>, HBUINT>
+  protected:
+  NNOffsetTo, HBUINT, void, false>, HBUINT>
                 substitutionTables;
   public:
-  DEFINE_SIZE_STATIC (20);
+  DEFINE_SIZE_STATIC ((StateTable::static_size + HBUINT::static_size));
 };
 
 
@@ -372,6 +375,16 @@ struct LigatureEntry;
 template <>
 struct LigatureEntry
 {
+
+  struct EntryData
+  {
+    HBUINT16    ligActionIndex; /* Index to the first ligActionTable entry
+                                 * for processing this group, if indicated
+                                 * by the flags. */
+    public:
+    DEFINE_SIZE_STATIC (2);
+  };
+
   enum Flags
   {
     SetComponent        = 0x8000,       /* Push this glyph onto the component stack for
@@ -383,14 +396,8 @@ struct LigatureEntry
     Reserved            = 0x1FFF,       /* These bits are reserved and should be set to 0. */
   };
 
-  struct EntryData
-  {
-    HBUINT16    ligActionIndex; /* Index to the first ligActionTable entry
-                                 * for processing this group, if indicated
-                                 * by the flags. */
-    public:
-    DEFINE_SIZE_STATIC (2);
-  };
+  static bool initiateAction (const Entry &entry)
+  { return entry.flags & SetComponent; }
 
   static bool performAction (const Entry &entry)
   { return entry.flags & PerformAction; }
@@ -401,6 +408,8 @@ struct LigatureEntry
 template <>
 struct LigatureEntry
 {
+  typedef void EntryData;
+
   enum Flags
   {
     SetComponent        = 0x8000,       /* Push this glyph onto the component stack for
@@ -412,7 +421,8 @@ struct LigatureEntry
                                          * multiple of 4. */
   };
 
-  typedef void EntryData;
+  static bool initiateAction (const Entry &entry)
+  { return entry.flags & SetComponent; }
 
   static bool performAction (const Entry &entry)
   { return entry.flags & Offset; }
@@ -430,13 +440,23 @@ struct LigatureSubtable
   typedef LigatureEntry LigatureEntryT;
   typedef typename LigatureEntryT::EntryData EntryData;
 
+  enum Flags
+  {
+    DontAdvance = LigatureEntryT::DontAdvance,
+  };
+
+  bool is_action_initiable (const Entry &entry) const
+  {
+    return LigatureEntryT::initiateAction (entry);
+  }
+  bool is_actionable (const Entry &entry) const
+  {
+    return LigatureEntryT::performAction (entry);
+  }
+
   struct driver_context_t
   {
     static constexpr bool in_place = false;
-    enum
-    {
-      DontAdvance       = LigatureEntryT::DontAdvance,
-    };
     enum LigActionFlags
     {
       LigActionLast     = 0x80000000,   /* This is the last action in the list. This also
@@ -459,16 +479,10 @@ struct LigatureSubtable
         ligature (table+table->ligature),
         match_length (0) {}
 
-    bool is_actionable (StateTableDriver *driver HB_UNUSED,
-                        const Entry &entry)
-    {
-      return LigatureEntryT::performAction (entry);
-    }
-    void transition (StateTableDriver *driver,
+    void transition (hb_buffer_t *buffer,
+                     StateTableDriver *driver,
                      const Entry &entry)
     {
-      hb_buffer_t *buffer = driver->buffer;
-
       DEBUG_MSG (APPLY, nullptr, "Ligature transition at %u", buffer->idx);
       if (entry.flags & LigatureEntryT::SetComponent)
       {
@@ -513,6 +527,7 @@ struct LigatureSubtable
           if (unlikely (!buffer->move_to (match_positions[--cursor % ARRAY_LENGTH (match_positions)]))) return;
 
           if (unlikely (!actionData->sanitize (&c->sanitizer))) break;
+          hb_barrier ();
           action = *actionData;
 
           uint32_t uoffset = action & LigActionOffset;
@@ -523,6 +538,7 @@ struct LigatureSubtable
           component_idx = Types::wordOffsetToIndex (component_idx, table, component.arrayZ);
           const HBUINT16 &componentData = component[component_idx];
           if (unlikely (!componentData.sanitize (&c->sanitizer))) break;
+          hb_barrier ();
           ligature_idx += componentData;
 
           DEBUG_MSG (APPLY, nullptr, "Action store %d last %d",
@@ -533,10 +549,11 @@ struct LigatureSubtable
             ligature_idx = Types::offsetToIndex (ligature_idx, table, ligature.arrayZ);
             const HBGlyphID16 &ligatureData = ligature[ligature_idx];
             if (unlikely (!ligatureData.sanitize (&c->sanitizer))) break;
+            hb_barrier ();
             hb_codepoint_t lig = ligatureData;
 
             DEBUG_MSG (APPLY, nullptr, "Produced ligature %u", lig);
-            if (unlikely (!buffer->replace_glyph (lig))) return;
+            if (unlikely (!c->replace_glyph (lig))) return;
 
             unsigned int lig_end = match_positions[(match_length - 1u) % ARRAY_LENGTH (match_positions)] + 1u;
             /* Now go and delete all subsequent components. */
@@ -544,7 +561,7 @@ struct LigatureSubtable
             {
               DEBUG_MSG (APPLY, nullptr, "Skipping ligature component");
               if (unlikely (!buffer->move_to (match_positions[--match_length % ARRAY_LENGTH (match_positions)]))) return;
-              if (unlikely (!buffer->replace_glyph (DELETED_GLYPH))) return;
+              if (!c->delete_glyph ()) return;
             }
 
             if (unlikely (!buffer->move_to (lig_end))) return;
@@ -560,9 +577,9 @@ struct LigatureSubtable
 
     public:
     bool ret;
-    private:
     hb_aat_apply_context_t *c;
     const LigatureSubtable *table;
+    private:
     const UnsizedArrayOf &ligAction;
     const UnsizedArrayOf &component;
     const UnsizedArrayOf &ligature;
@@ -576,7 +593,8 @@ struct LigatureSubtable
 
     driver_context_t dc (this, c);
 
-    StateTableDriver driver (machine, c->buffer, c->face);
+    StateTableDriver driver (machine, c->face);
+
     driver.drive (&dc, c);
 
     return_trace (dc.ret);
@@ -587,12 +605,14 @@ struct LigatureSubtable
     TRACE_SANITIZE (this);
     /* The rest of array sanitizations are done at run-time. */
     return_trace (c->check_struct (this) && machine.sanitize (c) &&
+                  hb_barrier () &&
                   ligAction && component && ligature);
   }
 
-  protected:
+  public:
   StateTable
                 machine;
+  protected:
   NNOffsetTo, HBUINT>
                 ligAction;      /* Offset to the ligature action table. */
   NNOffsetTo, HBUINT>
@@ -600,7 +620,7 @@ struct LigatureSubtable
   NNOffsetTo, HBUINT>
                 ligature;       /* Offset to the actual ligature lists. */
   public:
-  DEFINE_SIZE_STATIC (28);
+  DEFINE_SIZE_STATIC ((StateTable::static_size + 3 * HBUINT::static_size));
 };
 
 template 
@@ -610,9 +630,6 @@ struct NoncontextualSubtable
   {
     TRACE_APPLY (this);
 
-    const OT::GDEF &gdef (*c->gdef_table);
-    bool has_glyph_classes = gdef.has_glyph_classes ();
-
     bool ret = false;
     unsigned int num_glyphs = c->face->get_num_glyphs ();
 
@@ -642,10 +659,7 @@ struct NoncontextualSubtable
       const HBGlyphID16 *replacement = substitute.get_value (info[i].codepoint, num_glyphs);
       if (replacement)
       {
-        info[i].codepoint = *replacement;
-        if (has_glyph_classes)
-          _hb_glyph_info_set_glyph_props (&info[i],
-                                          gdef.get_glyph_props (*replacement));
+        c->replace_glyph_inplace (i, *replacement);
         ret = true;
       }
     }
@@ -653,6 +667,12 @@ struct NoncontextualSubtable
     return_trace (ret);
   }
 
+  template 
+  void collect_initial_glyphs (set_t &glyphs, unsigned num_glyphs) const
+  {
+    substitute.collect_glyphs (glyphs, num_glyphs);
+  }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -686,74 +706,80 @@ struct InsertionSubtable
     DEFINE_SIZE_STATIC (4);
   };
 
+  enum Flags
+  {
+    SetMark             = 0x8000,     /* If set, mark the current glyph. */
+    DontAdvance         = 0x4000,     /* If set, don't advance to the next glyph before
+                                       * going to the new state.  This does not mean
+                                       * that the glyph pointed to is the same one as
+                                       * before. If you've made insertions immediately
+                                       * downstream of the current glyph, the next glyph
+                                       * processed would in fact be the first one
+                                       * inserted. */
+    CurrentIsKashidaLike= 0x2000,     /* If set, and the currentInsertList is nonzero,
+                                       * then the specified glyph list will be inserted
+                                       * as a kashida-like insertion, either before or
+                                       * after the current glyph (depending on the state
+                                       * of the currentInsertBefore flag). If clear, and
+                                       * the currentInsertList is nonzero, then the
+                                       * specified glyph list will be inserted as a
+                                       * split-vowel-like insertion, either before or
+                                       * after the current glyph (depending on the state
+                                       * of the currentInsertBefore flag). */
+    MarkedIsKashidaLike= 0x1000,      /* If set, and the markedInsertList is nonzero,
+                                       * then the specified glyph list will be inserted
+                                       * as a kashida-like insertion, either before or
+                                       * after the marked glyph (depending on the state
+                                       * of the markedInsertBefore flag). If clear, and
+                                       * the markedInsertList is nonzero, then the
+                                       * specified glyph list will be inserted as a
+                                       * split-vowel-like insertion, either before or
+                                       * after the marked glyph (depending on the state
+                                       * of the markedInsertBefore flag). */
+    CurrentInsertBefore= 0x0800,      /* If set, specifies that insertions are to be made
+                                       * to the left of the current glyph. If clear,
+                                       * they're made to the right of the current glyph. */
+    MarkedInsertBefore= 0x0400,       /* If set, specifies that insertions are to be
+                                       * made to the left of the marked glyph. If clear,
+                                       * they're made to the right of the marked glyph. */
+    CurrentInsertCount= 0x3E0,        /* This 5-bit field is treated as a count of the
+                                       * number of glyphs to insert at the current
+                                       * position. Since zero means no insertions, the
+                                       * largest number of insertions at any given
+                                       * current location is 31 glyphs. */
+    MarkedInsertCount= 0x001F,        /* This 5-bit field is treated as a count of the
+                                       * number of glyphs to insert at the marked
+                                       * position. Since zero means no insertions, the
+                                       * largest number of insertions at any given
+                                       * marked location is 31 glyphs. */
+  };
+
+  bool is_action_initiable (const Entry &entry) const
+  {
+    return (entry.flags & SetMark);
+  }
+  bool is_actionable (const Entry &entry) const
+  {
+    return (entry.flags & (CurrentInsertCount | MarkedInsertCount)) &&
+           (entry.data.currentInsertIndex != 0xFFFF ||entry.data.markedInsertIndex != 0xFFFF);
+  }
+
   struct driver_context_t
   {
     static constexpr bool in_place = false;
-    enum Flags
-    {
-      SetMark           = 0x8000,       /* If set, mark the current glyph. */
-      DontAdvance       = 0x4000,       /* If set, don't advance to the next glyph before
-                                         * going to the new state.  This does not mean
-                                         * that the glyph pointed to is the same one as
-                                         * before. If you've made insertions immediately
-                                         * downstream of the current glyph, the next glyph
-                                         * processed would in fact be the first one
-                                         * inserted. */
-      CurrentIsKashidaLike= 0x2000,     /* If set, and the currentInsertList is nonzero,
-                                         * then the specified glyph list will be inserted
-                                         * as a kashida-like insertion, either before or
-                                         * after the current glyph (depending on the state
-                                         * of the currentInsertBefore flag). If clear, and
-                                         * the currentInsertList is nonzero, then the
-                                         * specified glyph list will be inserted as a
-                                         * split-vowel-like insertion, either before or
-                                         * after the current glyph (depending on the state
-                                         * of the currentInsertBefore flag). */
-      MarkedIsKashidaLike= 0x1000,      /* If set, and the markedInsertList is nonzero,
-                                         * then the specified glyph list will be inserted
-                                         * as a kashida-like insertion, either before or
-                                         * after the marked glyph (depending on the state
-                                         * of the markedInsertBefore flag). If clear, and
-                                         * the markedInsertList is nonzero, then the
-                                         * specified glyph list will be inserted as a
-                                         * split-vowel-like insertion, either before or
-                                         * after the marked glyph (depending on the state
-                                         * of the markedInsertBefore flag). */
-      CurrentInsertBefore= 0x0800,      /* If set, specifies that insertions are to be made
-                                         * to the left of the current glyph. If clear,
-                                         * they're made to the right of the current glyph. */
-      MarkedInsertBefore= 0x0400,       /* If set, specifies that insertions are to be
-                                         * made to the left of the marked glyph. If clear,
-                                         * they're made to the right of the marked glyph. */
-      CurrentInsertCount= 0x3E0,        /* This 5-bit field is treated as a count of the
-                                         * number of glyphs to insert at the current
-                                         * position. Since zero means no insertions, the
-                                         * largest number of insertions at any given
-                                         * current location is 31 glyphs. */
-      MarkedInsertCount= 0x001F,        /* This 5-bit field is treated as a count of the
-                                         * number of glyphs to insert at the marked
-                                         * position. Since zero means no insertions, the
-                                         * largest number of insertions at any given
-                                         * marked location is 31 glyphs. */
-    };
 
-    driver_context_t (const InsertionSubtable *table,
+    driver_context_t (const InsertionSubtable *table_,
                       hb_aat_apply_context_t *c_) :
         ret (false),
         c (c_),
+        table (table_),
         mark (0),
         insertionAction (table+table->insertionAction) {}
 
-    bool is_actionable (StateTableDriver *driver HB_UNUSED,
-                        const Entry &entry)
-    {
-      return (entry.flags & (CurrentInsertCount | MarkedInsertCount)) &&
-             (entry.data.currentInsertIndex != 0xFFFF ||entry.data.markedInsertIndex != 0xFFFF);
-    }
-    void transition (StateTableDriver *driver,
+    void transition (hb_buffer_t *buffer,
+                     StateTableDriver *driver,
                      const Entry &entry)
     {
-      hb_buffer_t *buffer = driver->buffer;
       unsigned int flags = entry.flags;
 
       unsigned mark_loc = buffer->out_len;
@@ -765,6 +791,7 @@ struct InsertionSubtable
         unsigned int start = entry.data.markedInsertIndex;
         const HBGlyphID16 *glyphs = &insertionAction[start];
         if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0;
+        hb_barrier ();
 
         bool before = flags & MarkedInsertBefore;
 
@@ -774,7 +801,8 @@ struct InsertionSubtable
         if (buffer->idx < buffer->len && !before)
           if (unlikely (!buffer->copy_glyph ())) return;
         /* TODO We ignore KashidaLike setting. */
-        if (unlikely (!buffer->replace_glyphs (0, count, glyphs))) return;
+        if (unlikely (!c->output_glyphs (count, glyphs))) return;
+        ret = true;
         if (buffer->idx < buffer->len && !before)
           buffer->skip_glyph ();
 
@@ -793,6 +821,7 @@ struct InsertionSubtable
         unsigned int start = entry.data.currentInsertIndex;
         const HBGlyphID16 *glyphs = &insertionAction[start];
         if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0;
+        hb_barrier ();
 
         bool before = flags & CurrentInsertBefore;
 
@@ -801,7 +830,8 @@ struct InsertionSubtable
         if (buffer->idx < buffer->len && !before)
           if (unlikely (!buffer->copy_glyph ())) return;
         /* TODO We ignore KashidaLike setting. */
-        if (unlikely (!buffer->replace_glyphs (0, count, glyphs))) return;
+        if (unlikely (!c->output_glyphs (count, glyphs))) return;
+        ret = true;
         if (buffer->idx < buffer->len && !before)
           buffer->skip_glyph ();
 
@@ -826,8 +856,9 @@ struct InsertionSubtable
 
     public:
     bool ret;
-    private:
     hb_aat_apply_context_t *c;
+    const InsertionSubtable *table;
+    private:
     unsigned int mark;
     const UnsizedArrayOf &insertionAction;
   };
@@ -838,7 +869,8 @@ struct InsertionSubtable
 
     driver_context_t dc (this, c);
 
-    StateTableDriver driver (machine, c->buffer, c->face);
+    StateTableDriver driver (machine, c->face);
+
     driver.drive (&dc, c);
 
     return_trace (dc.ret);
@@ -849,17 +881,19 @@ struct InsertionSubtable
     TRACE_SANITIZE (this);
     /* The rest of array sanitizations are done at run-time. */
     return_trace (c->check_struct (this) && machine.sanitize (c) &&
+                  hb_barrier () &&
                   insertionAction);
   }
 
-  protected:
+  public:
   StateTable
                 machine;
+  protected:
   NNOffsetTo, HBUINT>
                 insertionAction;        /* Byte offset from stateHeader to the start of
                                          * the insertion glyph table. */
   public:
-  DEFINE_SIZE_STATIC (20);
+  DEFINE_SIZE_STATIC ((StateTable::static_size + HBUINT::static_size));
 };
 
 
@@ -883,6 +917,107 @@ struct Feature
   DEFINE_SIZE_STATIC (12);
 };
 
+
+struct hb_accelerate_subtables_context_t :
+       hb_dispatch_context_t
+{
+  struct hb_applicable_t
+  {
+    friend struct hb_accelerate_subtables_context_t;
+    friend struct hb_aat_layout_lookup_accelerator_t;
+
+    public:
+    hb_bit_set_t glyph_set;
+    mutable hb_aat_class_cache_t class_cache;
+
+    template 
+    auto init_ (const T &obj_, unsigned num_glyphs, hb_priority<1>) HB_AUTO_RETURN
+    (
+      obj_.machine.collect_initial_glyphs (glyph_set, num_glyphs, obj_)
+    )
+
+    template 
+    void init_ (const T &obj_, unsigned num_glyphs, hb_priority<0>)
+    {
+      obj_.collect_initial_glyphs (glyph_set, num_glyphs);
+    }
+
+    template 
+    void init (const T &obj_, unsigned num_glyphs)
+    {
+      glyph_set.init ();
+      init_ (obj_, num_glyphs, hb_prioritize);
+      class_cache.clear ();
+    }
+
+    void
+    fini ()
+    {
+      glyph_set.fini ();
+    }
+  };
+
+  /* Dispatch interface. */
+  template 
+  return_t dispatch (const T &obj)
+  {
+    hb_applicable_t *entry = &array[i++];
+
+    entry->init (obj, num_glyphs);
+
+    return hb_empty_t ();
+  }
+  static return_t default_return_value () { return hb_empty_t (); }
+
+  bool stop_sublookup_iteration (return_t r) const { return false; }
+
+  hb_accelerate_subtables_context_t (hb_applicable_t *array_, unsigned num_glyphs_) :
+                                     hb_dispatch_context_t (),
+                                     array (array_), num_glyphs (num_glyphs_) {}
+
+  hb_applicable_t *array;
+  unsigned num_glyphs;
+  unsigned i = 0;
+};
+
+struct hb_aat_layout_chain_accelerator_t
+{
+  template 
+  static hb_aat_layout_chain_accelerator_t *create (const TChain &chain, unsigned num_glyphs)
+  {
+    unsigned count = chain.get_subtable_count ();
+
+    unsigned size = sizeof (hb_aat_layout_chain_accelerator_t) -
+                    HB_VAR_ARRAY * sizeof (hb_accelerate_subtables_context_t::hb_applicable_t) +
+                    count * sizeof (hb_accelerate_subtables_context_t::hb_applicable_t);
+
+    /* The following is a calloc because when we are collecting subtables,
+     * some of them might be invalid and hence not collect; as a result,
+     * we might not fill in all the count entries of the subtables array.
+     * Zeroing it allows the set digest to gatekeep it without having to
+     * initialize it further. */
+    auto *thiz = (hb_aat_layout_chain_accelerator_t *) hb_calloc (1, size);
+    if (unlikely (!thiz))
+      return nullptr;
+
+    thiz->count = count;
+
+    hb_accelerate_subtables_context_t c_accelerate_subtables (thiz->subtables, num_glyphs);
+    chain.dispatch (&c_accelerate_subtables);
+
+    return thiz;
+  }
+
+  void destroy ()
+  {
+    for (unsigned i = 0; i < count; i++)
+      subtables[i].fini ();
+  }
+
+  unsigned count;
+  hb_accelerate_subtables_context_t::hb_applicable_t subtables[HB_VAR_ARRAY];
+};
+
 template 
 struct ChainSubtable
 {
@@ -937,19 +1072,22 @@ struct ChainSubtable
   bool apply (hb_aat_apply_context_t *c) const
   {
     TRACE_APPLY (this);
-    hb_sanitize_with_object_t with (&c->sanitizer, this);
+    // Disabled for https://github.com/harfbuzz/harfbuzz/issues/4873
+    //hb_sanitize_with_object_t with (&c->sanitizer, this);
     return_trace (dispatch (c));
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (!length.sanitize (c) ||
-        length <= min_size ||
-        !c->check_range (this, length))
+    if (!(length.sanitize (c) &&
+          hb_barrier () &&
+          length >= min_size &&
+          c->check_range (this, length)))
       return_trace (false);
 
-    hb_sanitize_with_object_t with (c, this);
+    // Disabled for https://github.com/harfbuzz/harfbuzz/issues/4873
+    //hb_sanitize_with_object_t with (c, this);
     return_trace (dispatch (c));
   }
 
@@ -973,6 +1111,8 @@ struct Chain
 {
   typedef typename Types::HBUINT HBUINT;
 
+  unsigned get_subtable_count () const { return subtableCount; }
+
   hb_mask_t compile_flags (const hb_aat_map_builder_t *map) const
   {
     hb_mask_t flags = defaultFlags;
@@ -1013,7 +1153,8 @@ struct Chain
     return flags;
   }
 
-  void apply (hb_aat_apply_context_t *c) const
+  void apply (hb_aat_apply_context_t *c,
+              const hb_aat_layout_chain_accelerator_t *accel) const
   {
     const ChainSubtable *subtable = &StructAfter> (featureZ.as_array (featureCount));
     unsigned int count = subtableCount;
@@ -1021,16 +1162,28 @@ struct Chain
     {
       bool reverse;
 
+      auto coverage = subtable->get_coverage ();
+
+      hb_mask_t subtable_flags = subtable->subFeatureFlags;
       if (hb_none (hb_iter (c->range_flags) |
-                   hb_map ([&subtable] (const hb_aat_map_t::range_flags_t _) -> bool { return subtable->subFeatureFlags & (_.flags); })))
+                   hb_map ([subtable_flags] (const hb_aat_map_t::range_flags_t _) -> bool { return subtable_flags & (_.flags); })))
         goto skip;
-      c->subtable_flags = subtable->subFeatureFlags;
 
-      if (!(subtable->get_coverage() & ChainSubtable::AllDirections) &&
+      c->subtable_flags = subtable_flags;
+      c->machine_glyph_set = accel ? &accel->subtables[i].glyph_set : &Null(hb_bit_set_t);
+      c->machine_class_cache = accel ? &accel->subtables[i].class_cache : nullptr;
+
+      if (!(coverage & ChainSubtable::AllDirections) &&
           HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) !=
-          bool (subtable->get_coverage() & ChainSubtable::Vertical))
+          bool (coverage & ChainSubtable::Vertical))
         goto skip;
 
+      if (!c->buffer_intersects_machine ())
+      {
+        (void) c->buffer->message (c->font, "skipped chainsubtable %u because no glyph matches", c->lookup_index);
+        goto skip;
+      }
+
       /* Buffer contents is always in logical direction.  Determine if
        * we need to reverse before applying this subtable.  We reverse
        * back after if we did reverse indeed.
@@ -1058,9 +1211,9 @@ struct Chain
                                 (the order opposite that of the characters, which
                                 may be right-to-left or left-to-right).
        */
-      reverse = subtable->get_coverage () & ChainSubtable::Logical ?
-                bool (subtable->get_coverage () & ChainSubtable::Backwards) :
-                bool (subtable->get_coverage () & ChainSubtable::Backwards) !=
+      reverse = coverage & ChainSubtable::Logical ?
+                bool (coverage & ChainSubtable::Backwards) :
+                bool (coverage & ChainSubtable::Backwards) !=
                 HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
 
       if (!c->buffer->message (c->font, "start chainsubtable %u", c->lookup_index))
@@ -1086,12 +1239,28 @@ struct Chain
 
   unsigned int get_size () const { return length; }
 
-  bool sanitize (hb_sanitize_context_t *c, unsigned int version HB_UNUSED) const
+  template 
+  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+  {
+    const ChainSubtable *subtable = &StructAfter> (featureZ.as_array (featureCount));
+    unsigned int count = subtableCount;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      typename context_t::return_t ret = subtable->dispatch (c, std::forward (ds)...);
+      if (c->stop_sublookup_iteration (ret))
+        return ret;
+      subtable = &StructAfter> (*subtable);
+    }
+    return c->default_return_value ();
+  }
+
+  bool sanitize (hb_sanitize_context_t *c, unsigned int version) const
   {
     TRACE_SANITIZE (this);
-    if (!length.sanitize (c) ||
-        length < min_size ||
-        !c->check_range (this, length))
+    if (!(length.sanitize (c) &&
+          hb_barrier () &&
+          length >= min_size &&
+          c->check_range (this, length)))
       return_trace (false);
 
     if (!c->check_array (featureZ.arrayZ, featureCount))
@@ -1103,9 +1272,17 @@ struct Chain
     {
       if (!subtable->sanitize (c))
         return_trace (false);
+      hb_barrier ();
       subtable = &StructAfter> (*subtable);
     }
 
+    if (version >= 3)
+    {
+      const SubtableGlyphCoverage *coverage = (const SubtableGlyphCoverage *) subtable;
+      if (!coverage->sanitize (c, count))
+        return_trace (false);
+    }
+
     return_trace (true);
   }
 
@@ -1117,7 +1294,7 @@ struct Chain
 
   UnsizedArrayOf       featureZ;       /* Features. */
 /*ChainSubtable firstSubtable;*//* Subtables. */
-/*subtableGlyphCoverageArray*/  /* Only if version >= 3. We don't use. */
+/*SubtableGlyphCoverage coverages*//* Only if version >= 3. */
 
   public:
   DEFINE_SIZE_MIN (8 + 2 * sizeof (HBUINT));
@@ -1128,13 +1305,80 @@ struct Chain
  * The 'mort'/'morx' Table
  */
 
-template 
+template 
 struct mortmorx
 {
   static constexpr hb_tag_t tableTag = TAG;
 
   bool has_data () const { return version != 0; }
 
+  struct accelerator_t
+  {
+    accelerator_t (hb_face_t *face)
+    {
+      hb_sanitize_context_t sc;
+      this->table = sc.reference_table (face);
+
+      if (unlikely (this->table->is_blocklisted (this->table.get_blob (), face)))
+      {
+        hb_blob_destroy (this->table.get_blob ());
+        this->table = hb_blob_get_empty ();
+      }
+
+      this->chain_count = table->get_chain_count ();
+
+      this->accels = (hb_atomic_t *) hb_calloc (this->chain_count, sizeof (*accels));
+      if (unlikely (!this->accels))
+      {
+        this->chain_count = 0;
+        this->table.destroy ();
+        this->table = hb_blob_get_empty ();
+      }
+    }
+    ~accelerator_t ()
+    {
+      for (unsigned int i = 0; i < this->chain_count; i++)
+      {
+        if (this->accels[i])
+          this->accels[i]->destroy ();
+        hb_free (this->accels[i]);
+      }
+      hb_free (this->accels);
+      this->table.destroy ();
+    }
+
+    hb_blob_t *get_blob () const { return table.get_blob (); }
+
+    template 
+    hb_aat_layout_chain_accelerator_t *get_accel (unsigned chain_index, const Chain &chain, unsigned num_glyphs) const
+    {
+      if (unlikely (chain_index >= chain_count)) return nullptr;
+
+    retry:
+      auto *accel = accels[chain_index].get_acquire ();
+      if (unlikely (!accel))
+      {
+        accel = hb_aat_layout_chain_accelerator_t::create (chain, num_glyphs);
+        if (unlikely (!accel))
+          return nullptr;
+
+        if (unlikely (!accels[chain_index].cmpexch (nullptr, accel)))
+        {
+          hb_free (accel);
+          goto retry;
+        }
+      }
+
+      return accel;
+    }
+
+    hb_blob_ptr_t table;
+    unsigned int chain_count;
+    hb_atomic_t *accels;
+    hb_aat_scratch_t scratch;
+  };
+
+
   void compile_flags (const hb_aat_map_builder_t *mapper,
                       hb_aat_map_t *map) const
   {
@@ -1151,20 +1395,28 @@ struct mortmorx
     }
   }
 
+  unsigned get_chain_count () const
+  {
+    return chainCount;
+  }
   void apply (hb_aat_apply_context_t *c,
-              const hb_aat_map_t &map) const
+              const hb_aat_map_t &map,
+              const accelerator_t &accel) const
   {
     if (unlikely (!c->buffer->successful)) return;
 
     c->buffer->unsafe_to_concat ();
 
+    c->setup_buffer_glyph_set ();
+
     c->set_lookup_index (0);
     const Chain *chain = &firstChain;
     unsigned int count = chainCount;
     for (unsigned int i = 0; i < count; i++)
     {
+      auto *chain_accel = accel.get_accel (i, *chain, c->face->get_num_glyphs ());
       c->range_flags = &map.chain_flags[i];
-      chain->apply (c);
+      chain->apply (c, chain_accel);
       if (unlikely (!c->buffer->successful)) return;
       chain = &StructAfter> (*chain);
     }
@@ -1173,7 +1425,10 @@ struct mortmorx
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (!version.sanitize (c) || !version || !chainCount.sanitize (c))
+    if (!(version.sanitize (c) &&
+          hb_barrier () &&
+          version &&
+          chainCount.sanitize (c)))
       return_trace (false);
 
     const Chain *chain = &firstChain;
@@ -1182,6 +1437,7 @@ struct mortmorx
     {
       if (!chain->sanitize (c, version))
         return_trace (false);
+      hb_barrier ();
       chain = &StructAfter> (*chain);
     }
 
@@ -1200,8 +1456,24 @@ struct mortmorx
   DEFINE_SIZE_MIN (8);
 };
 
-struct morx : mortmorx {};
-struct mort : mortmorx {};
+struct morx : mortmorx
+{
+  HB_INTERNAL bool is_blocklisted (hb_blob_t *blob,
+                                   hb_face_t *face) const;
+};
+
+struct mort : mortmorx
+{
+  HB_INTERNAL bool is_blocklisted (hb_blob_t *blob,
+                                   hb_face_t *face) const;
+};
+
+struct morx_accelerator_t : morx::accelerator_t {
+  morx_accelerator_t (hb_face_t *face) : morx::accelerator_t (face) {}
+};
+struct mort_accelerator_t : mort::accelerator_t {
+  mort_accelerator_t (hb_face_t *face) : mort::accelerator_t (face) {}
+};
 
 
 } /* namespace AAT */
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-opbd-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-opbd-table.hh
index 959382f356fb0..f28ec326b4596 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-opbd-table.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-opbd-table.hh
@@ -133,8 +133,8 @@ struct opbd
   {
     switch (format)
     {
-    case 0: return u.format0.get_bounds (font, glyph_id, extents, this);
-    case 1: return u.format1.get_bounds (font, glyph_id, extents, this);
+    case 0: hb_barrier (); return u.format0.get_bounds (font, glyph_id, extents, this);
+    case 1: hb_barrier (); return u.format1.get_bounds (font, glyph_id, extents, this);
     default:return false;
     }
   }
@@ -144,11 +144,12 @@ struct opbd
     TRACE_SANITIZE (this);
     if (unlikely (!c->check_struct (this) || version.major != 1))
       return_trace (false);
+    hb_barrier ();
 
     switch (format)
     {
-    case 0: return_trace (u.format0.sanitize (c, this));
-    case 1: return_trace (u.format1.sanitize (c, this));
+    case 0: hb_barrier (); return_trace (u.format0.sanitize (c, this));
+    case 1: hb_barrier (); return_trace (u.format1.sanitize (c, this));
     default:return_trace (true);
     }
   }
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-trak-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-trak-table.hh
index 5c49d1f0562c0..4ee73ecee69f1 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-trak-table.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-trak-table.hh
@@ -31,6 +31,7 @@
 #include "hb-aat-layout-common.hh"
 #include "hb-ot-layout.hh"
 #include "hb-open-type.hh"
+#include "hb-ot-stat-table.hh"
 
 /*
  * trak -- Tracking
@@ -48,22 +49,74 @@ struct TrackTableEntry
 
   float get_track_value () const { return track.to_float (); }
 
-  int get_value (const void *base, unsigned int index,
-                 unsigned int table_size) const
-  { return (base+valuesZ).as_array (table_size)[index]; }
+  float interpolate_at (unsigned int idx,
+                        float ptem,
+                        const void *base,
+                        hb_array_t size_table) const
+  {
+    const FWORD *values = (base+valuesZ).arrayZ;
+
+    float s0 = size_table[idx].to_float ();
+    float s1 = size_table[idx + 1].to_float ();
+    int v0 = values[idx];
+    int v1 = values[idx + 1];
+
+    // Deal with font bugs.
+    if (unlikely (s1 < s0))
+    { hb_swap (s0, s1); hb_swap (v0, v1); }
+    if (unlikely (ptem < s0)) return v0;
+    if (unlikely (ptem > s1)) return v1;
+    if (unlikely (s0 == s1)) return (v0 + v1) * 0.5f;
+
+    float t = (ptem - s0) / (s1 - s0);
+    return v0 + t * (v1 - v0);
+  }
+
+  float get_value (float ptem,
+                   const void *base,
+                   hb_array_t size_table) const
+  {
+    const FWORD *values = (base+valuesZ).arrayZ;
+
+    unsigned int n_sizes = size_table.length;
+
+    /*
+     * Choose size.
+     */
+    if (!n_sizes) return 0.f;
+    if (n_sizes == 1) return values[0];
+
+    // At least two entries.
+
+    unsigned i;
+    for (i = 0; i < n_sizes; i++)
+      if (size_table[i].to_float () >= ptem)
+        break;
+
+    // Boundary conditions.
+    if (i == 0)       return values[0];
+    if (i == n_sizes) return values[n_sizes - 1];
+
+    // Exact match.
+    if (size_table[i].to_float () == ptem) return values[i];
+
+    // Interpolate.
+    return interpolate_at (i - 1, ptem, base, size_table);
+  }
 
   public:
-  bool sanitize (hb_sanitize_context_t *c, const void *base,
-                 unsigned int table_size) const
+  bool sanitize (hb_sanitize_context_t *c,
+                 const void *base,
+                 unsigned int n_sizes) const
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this) &&
-                          (valuesZ.sanitize (c, base, table_size))));
+                          (valuesZ.sanitize (c, base, n_sizes))));
   }
 
   protected:
   F16DOT16      track;          /* Track value for this record. */
-  NameID        trackNameID;    /* The 'name' table index for this track.
+  OT::NameID    trackNameID;    /* The 'name' table index for this track.
                                  * (a short word or phrase like "loose"
                                  * or "very tight") */
   NNOffset16To>
@@ -76,64 +129,45 @@ struct TrackTableEntry
 
 struct TrackData
 {
-  float interpolate_at (unsigned int idx,
-                        float target_size,
-                        const TrackTableEntry &trackTableEntry,
-                        const void *base) const
+  float get_tracking (const void *base, float ptem, float track = 0.f) const
   {
-    unsigned int sizes = nSizes;
-    hb_array_t size_table ((base+sizeTable).arrayZ, sizes);
+    unsigned count = nTracks;
+    hb_array_t size_table = (base+sizeTable).as_array (nSizes);
 
-    float s0 = size_table[idx].to_float ();
-    float s1 = size_table[idx + 1].to_float ();
-    float t = unlikely (s0 == s1) ? 0.f : (target_size - s0) / (s1 - s0);
-    return t * trackTableEntry.get_value (base, idx + 1, sizes) +
-           (1.f - t) * trackTableEntry.get_value (base, idx, sizes);
-  }
+    if (!count) return 0.f;
+    if (count == 1) return trackTable[0].get_value (ptem, base, size_table);
 
-  int get_tracking (const void *base, float ptem) const
-  {
-    /*
-     * Choose track.
-     */
-    const TrackTableEntry *trackTableEntry = nullptr;
-    unsigned int count = nTracks;
-    for (unsigned int i = 0; i < count; i++)
-    {
-      /* Note: Seems like the track entries are sorted by values.  But the
-       * spec doesn't explicitly say that.  It just mentions it in the example. */
+    // At least two entries.
 
-      /* For now we only seek for track entries with zero tracking value */
+    unsigned i = 0;
+    unsigned j = count - 1;
 
-      if (trackTable[i].get_track_value () == 0.f)
-      {
-        trackTableEntry = &trackTable[i];
-        break;
-      }
-    }
-    if (!trackTableEntry) return 0;
+    // Find the two entries that track is between.
+    while (i + 1 < count && trackTable[i + 1].get_track_value () <= track)
+      i++;
+    while (j > 0 && trackTable[j - 1].get_track_value () >= track)
+      j--;
 
-    /*
-     * Choose size.
-     */
-    unsigned int sizes = nSizes;
-    if (!sizes) return 0;
-    if (sizes == 1) return trackTableEntry->get_value (base, 0, sizes);
-
-    hb_array_t size_table ((base+sizeTable).arrayZ, sizes);
-    unsigned int size_index;
-    for (size_index = 0; size_index < sizes - 1; size_index++)
-      if (size_table[size_index].to_float () >= ptem)
-        break;
+    // Exact match.
+    if (i == j) return trackTable[i].get_value (ptem, base, size_table);
+
+    // Interpolate.
+
+    float t0 = trackTable[i].get_track_value ();
+    float t1 = trackTable[j].get_track_value ();
 
-    return roundf (interpolate_at (size_index ? size_index - 1 : 0, ptem,
-                                   *trackTableEntry, base));
+    float t = (track - t0) / (t1 - t0);
+
+    float a = trackTable[i].get_value (ptem, base, size_table);
+    float b = trackTable[j].get_value (ptem, base, size_table);
+    return a + t * (b - a);
   }
 
   bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this) &&
+                          hb_barrier () &&
                           sizeTable.sanitize (c, base, nSizes) &&
                           trackTable.sanitize (c, nTracks, base, nSizes)));
   }
@@ -157,42 +191,52 @@ struct trak
 
   bool has_data () const { return version.to_int (); }
 
-  bool apply (hb_aat_apply_context_t *c) const
+  hb_position_t get_h_tracking (hb_font_t *font, float track = 0.f) const
   {
-    TRACE_APPLY (this);
+    float ptem = font->ptem > 0.f ? font->ptem : HB_CORETEXT_DEFAULT_FONT_SIZE;
+    return font->em_scalef_x ((this+horizData).get_tracking (this, ptem, track));
+  }
+  hb_position_t get_v_tracking (hb_font_t *font, float track = 0.f) const
+  {
+    float ptem = font->ptem > 0.f ? font->ptem : HB_CORETEXT_DEFAULT_FONT_SIZE;
+    return font->em_scalef_y ((this+vertData).get_tracking (this, ptem, track));
+  }
+  hb_position_t get_tracking (hb_font_t *font, hb_direction_t dir, float track = 0.f) const
+  {
+#ifndef HB_NO_STYLE
+    if (!font->face->table.STAT->has_data ())
+      return 0;
+    return HB_DIRECTION_IS_HORIZONTAL (dir) ?
+      get_h_tracking (font, track) :
+      get_v_tracking (font, track);
+#else
+    return 0;
+#endif
+  }
 
-    hb_mask_t trak_mask = c->plan->trak_mask;
+  bool apply (hb_aat_apply_context_t *c, float track = 0.f) const
+  {
+    TRACE_APPLY (this);
 
-    const float ptem = c->font->ptem;
+    float ptem = c->font->ptem;
     if (unlikely (ptem <= 0.f))
-      return_trace (false);
+    {
+      /* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */
+      ptem = HB_CORETEXT_DEFAULT_FONT_SIZE;
+    }
 
     hb_buffer_t *buffer = c->buffer;
     if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
     {
-      const TrackData &trackData = this+horizData;
-      int tracking = trackData.get_tracking (this, ptem);
-      hb_position_t offset_to_add = c->font->em_scalef_x (tracking / 2);
-      hb_position_t advance_to_add = c->font->em_scalef_x (tracking);
+      hb_position_t advance_to_add = get_h_tracking (c->font, track);
       foreach_grapheme (buffer, start, end)
-      {
-        if (!(buffer->info[start].mask & trak_mask)) continue;
         buffer->pos[start].x_advance += advance_to_add;
-        buffer->pos[start].x_offset += offset_to_add;
-      }
     }
     else
     {
-      const TrackData &trackData = this+vertData;
-      int tracking = trackData.get_tracking (this, ptem);
-      hb_position_t offset_to_add = c->font->em_scalef_y (tracking / 2);
-      hb_position_t advance_to_add = c->font->em_scalef_y (tracking);
+      hb_position_t advance_to_add = get_v_tracking (c->font, track);
       foreach_grapheme (buffer, start, end)
-      {
-        if (!(buffer->info[start].mask & trak_mask)) continue;
         buffer->pos[start].y_advance += advance_to_add;
-        buffer->pos[start].y_offset += offset_to_add;
-      }
     }
 
     return_trace (true);
@@ -203,6 +247,7 @@ struct trak
     TRACE_SANITIZE (this);
 
     return_trace (likely (c->check_struct (this) &&
+                          hb_barrier () &&
                           version.major == 1 &&
                           horizData.sanitize (c, this, this) &&
                           vertData.sanitize (c, this, this)));
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.cc b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.cc
index fc5834c7ca1b1..b349c3026b62a 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.cc
+++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.cc
@@ -37,6 +37,9 @@
 #include "hb-aat-layout-trak-table.hh"
 #include "hb-aat-ltag-table.hh"
 
+#include "hb-ot-layout-gsub-table.hh"
+#include "hb-ot-layout-gdef-table.hh"
+
 
 /*
  * hb_aat_apply_context_t
@@ -55,13 +58,14 @@ AAT::hb_aat_apply_context_t::hb_aat_apply_context_t (const hb_ot_shape_plan_t *p
                                                        buffer (buffer_),
                                                        sanitizer (),
                                                        ankr_table (&Null (AAT::ankr)),
-                                                       gdef_table (
+                                                       gdef (
 #ifndef HB_NO_OT_LAYOUT
-                                                         face->table.GDEF->table
+                                                         *face->table.GDEF->table
 #else
-                                                         &Null (GDEF)
+                                                         Null (GDEF)
 #endif
                                                        ),
+                                                       has_glyph_classes (gdef.has_glyph_classes ()),
                                                        lookup_index (0)
 {
   sanitizer.init (blob);
@@ -200,25 +204,55 @@ hb_aat_layout_find_feature_mapping (hb_tag_t tag)
 #endif
 
 
-#ifndef HB_NO_AAT
+#ifndef HB_NO_AAT_SHAPE
 
 /*
  * mort/morx/kerx/trak
  */
 
 
+bool
+AAT::morx::is_blocklisted (hb_blob_t *blob,
+                           hb_face_t *face) const
+{
+#ifdef HB_NO_AAT_LAYOUT_BLOCKLIST
+  return false;
+#endif
+
+  switch HB_CODEPOINT_ENCODE3 (blob->length,
+                               face->table.GSUB->table.get_length (),
+                               face->table.GDEF->table.get_length ())
+  {
+    /* https://github.com/harfbuzz/harfbuzz/issues/4108
+       sha1sum:a71ca6813b7e56a772cffff7c24a5166b087197c  AALMAGHRIBI.ttf */
+    case HB_CODEPOINT_ENCODE3 (19892, 2794, 340):
+      return true;
+  }
+  return false;
+}
+
+bool
+AAT::mort::is_blocklisted (hb_blob_t *blob,
+                           hb_face_t *face) const
+{
+#ifdef HB_NO_AAT_LAYOUT_BLOCKLIST
+  return false;
+#endif
+  return false;
+}
+
 void
 hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper,
                            hb_aat_map_t *map)
 {
-  const AAT::morx& morx = *mapper->face->table.morx;
+  const AAT::morx& morx = *mapper->face->table.morx->table;
   if (morx.has_data ())
   {
     morx.compile_flags (mapper, map);
     return;
   }
 
-  const AAT::mort& mort = *mapper->face->table.mort;
+  const AAT::mort& mort = *mapper->face->table.mort->table;
   if (mort.has_data ())
   {
     mort.compile_flags (mapper, map);
@@ -243,8 +277,8 @@ hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper,
 hb_bool_t
 hb_aat_layout_has_substitution (hb_face_t *face)
 {
-  return face->table.morx->has_data () ||
-         face->table.mort->has_data ();
+  return face->table.morx->table->has_data () ||
+         face->table.mort->table->has_data ();
 }
 
 void
@@ -254,56 +288,56 @@ hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan,
                           const hb_feature_t *features,
                           unsigned num_features)
 {
-  hb_aat_map_builder_t builder (font->face, plan->props);
-  for (unsigned i = 0; i < num_features; i++)
-    builder.add_feature (features[i]);
   hb_aat_map_t map;
-  builder.compile (map);
-
-  hb_blob_t *morx_blob = font->face->table.morx.get_blob ();
-  const AAT::morx& morx = *morx_blob->as ();
-  if (morx.has_data ())
+  if (num_features)
   {
-    AAT::hb_aat_apply_context_t c (plan, font, buffer, morx_blob);
-    if (!buffer->message (font, "start table morx")) return;
-    morx.apply (&c, map);
-    (void) buffer->message (font, "end table morx");
-    return;
+    hb_aat_map_builder_t builder (font->face, plan->props);
+    for (unsigned i = 0; i < num_features; i++)
+      builder.add_feature (features[i]);
+    builder.compile (map);
   }
 
-  hb_blob_t *mort_blob = font->face->table.mort.get_blob ();
-  const AAT::mort& mort = *mort_blob->as ();
-  if (mort.has_data ())
   {
-    AAT::hb_aat_apply_context_t c (plan, font, buffer, mort_blob);
-    if (!buffer->message (font, "start table mort")) return;
-    mort.apply (&c, map);
-    (void) buffer->message (font, "end table mort");
-    return;
+    auto &accel = *font->face->table.morx;
+    const AAT::morx& morx = *accel.table;
+    if (morx.has_data ())
+    {
+      AAT::hb_aat_apply_context_t c (plan, font, buffer, accel.get_blob ());
+      if (!buffer->message (font, "start table morx")) return;
+      c.buffer_glyph_set = accel.scratch.create_buffer_glyph_set ();
+      morx.apply (&c, num_features ? map : plan->aat_map, accel);
+      accel.scratch.destroy_buffer_glyph_set (c.buffer_glyph_set);
+      c.buffer_glyph_set = nullptr;
+      (void) buffer->message (font, "end table morx");
+      return;
+    }
   }
-}
 
-void
-hb_aat_layout_zero_width_deleted_glyphs (hb_buffer_t *buffer)
-{
-  unsigned int count = buffer->len;
-  hb_glyph_info_t *info = buffer->info;
-  hb_glyph_position_t *pos = buffer->pos;
-  for (unsigned int i = 0; i < count; i++)
-    if (unlikely (info[i].codepoint == AAT::DELETED_GLYPH))
-      pos[i].x_advance = pos[i].y_advance = pos[i].x_offset = pos[i].y_offset = 0;
+  {
+    auto &accel = *font->face->table.mort;
+    const AAT::mort& mort = *accel.table;
+    if (mort.has_data ())
+    {
+      AAT::hb_aat_apply_context_t c (plan, font, buffer, accel.get_blob ());
+      if (!buffer->message (font, "start table mort")) return;
+      mort.apply (&c, num_features ? map : plan->aat_map, accel);
+      (void) buffer->message (font, "end table mort");
+      return;
+    }
+  }
 }
 
 static bool
 is_deleted_glyph (const hb_glyph_info_t *info)
 {
-  return info->codepoint == AAT::DELETED_GLYPH;
+  return _hb_glyph_info_is_aat_deleted (info);
 }
 
 void
 hb_aat_layout_remove_deleted_glyphs (hb_buffer_t *buffer)
 {
-  buffer->delete_glyphs_inplace (is_deleted_glyph);
+  if (buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_AAT_HAS_DELETED)
+    buffer->delete_glyphs_inplace (is_deleted_glyph);
 }
 
 /**
@@ -322,7 +356,7 @@ hb_aat_layout_remove_deleted_glyphs (hb_buffer_t *buffer)
 hb_bool_t
 hb_aat_layout_has_positioning (hb_face_t *face)
 {
-  return face->table.kerx->has_data ();
+  return face->table.kerx->table->has_data ();
 }
 
 void
@@ -330,13 +364,15 @@ hb_aat_layout_position (const hb_ot_shape_plan_t *plan,
                         hb_font_t *font,
                         hb_buffer_t *buffer)
 {
-  hb_blob_t *kerx_blob = font->face->table.kerx.get_blob ();
-  const AAT::kerx& kerx = *kerx_blob->as ();
+  auto &accel = *font->face->table.kerx;
 
-  AAT::hb_aat_apply_context_t c (plan, font, buffer, kerx_blob);
+  AAT::hb_aat_apply_context_t c (plan, font, buffer, accel.get_blob ());
   if (!buffer->message (font, "start table kerx")) return;
+  c.buffer_glyph_set = accel.scratch.create_buffer_glyph_set ();
   c.set_ankr_table (font->face->table.ankr.get ());
-  kerx.apply (&c);
+  accel.apply (&c);
+  accel.scratch.destroy_buffer_glyph_set (c.buffer_glyph_set);
+  c.buffer_glyph_set = nullptr;
   (void) buffer->message (font, "end table kerx");
 }
 
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.h b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.h
index c833ea8f62378..88e040d7e2b43 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.h
+++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.h
@@ -40,7 +40,7 @@ HB_BEGIN_DECLS
  * @HB_AAT_LAYOUT_FEATURE_TYPE_INVALID: Initial, unset feature type
  * @HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC: [All Typographic Features](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type0)
  * @HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES: [Ligatures](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type1)
- * @HB_AAT_LAYOUT_FEATURE_TYPE_CURISVE_CONNECTION: [Cursive Connection](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type2)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_CURSIVE_CONNECTION: [Cursive Connection](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type2)
  * @HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE: [Letter Case](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type3)
  * @HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION: [Vertical Substitution](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type4)
  * @HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT: [Linguistic Rearrangement](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type5)
@@ -88,7 +88,7 @@ typedef enum
 
   HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC                    = 0,
   HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES                          = 1,
-  HB_AAT_LAYOUT_FEATURE_TYPE_CURISVE_CONNECTION                 = 2,
+  HB_AAT_LAYOUT_FEATURE_TYPE_CURSIVE_CONNECTION                 = 2,
   HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE                        = 3,
   HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION              = 4,
   HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT           = 5,
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.hh
index 36bb0ef07ddb0..ffa54408ebf63 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.hh
@@ -32,6 +32,9 @@
 #include "hb-ot-shape.hh"
 #include "hb-aat-ltag-table.hh"
 
+/* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */
+#define HB_CORETEXT_DEFAULT_FONT_SIZE 12.f
+
 struct hb_aat_feature_mapping_t
 {
   hb_tag_t otFeatureTag;
@@ -57,9 +60,6 @@ hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan,
                           const hb_feature_t *features,
                           unsigned num_features);
 
-HB_INTERNAL void
-hb_aat_layout_zero_width_deleted_glyphs (hb_buffer_t *buffer);
-
 HB_INTERNAL void
 hb_aat_layout_remove_deleted_glyphs (hb_buffer_t *buffer);
 
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-ltag-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-ltag-table.hh
index 17b341ef277ea..da4dcb1212fd5 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-aat-ltag-table.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-ltag-table.hh
@@ -46,7 +46,9 @@ struct FTStringRange
   bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) && (base+tag).sanitize (c, length));
+    return_trace (c->check_struct (this) &&
+                  hb_barrier () &&
+                  (base+tag).sanitize (c, length));
   }
 
   protected:
@@ -73,6 +75,7 @@ struct ltag
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this) &&
+                          hb_barrier () &&
                           version >= 1 &&
                           tagRanges.sanitize (c, this)));
   }
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-map.cc b/src/java.desktop/share/native/libharfbuzz/hb-aat-map.cc
index 122f29dc6816f..306a95b7c4b84 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-aat-map.cc
+++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-map.cc
@@ -85,25 +85,31 @@ void
 hb_aat_map_builder_t::compile (hb_aat_map_t  &m)
 {
   /* Compute active features per range, and compile each. */
+  if (!features.length)
+  {
+    hb_aat_layout_compile_map (this, &m);
+    return;
+  }
 
   /* Sort features by start/end events. */
   hb_vector_t feature_events;
+  feature_events.alloc_exact (features.length * 2 + 1);
   for (unsigned int i = 0; i < features.length; i++)
   {
-    auto &feature = features[i];
+    auto &feature = features.arrayZ[i];
 
-    if (features[i].start == features[i].end)
+    if (feature.start == feature.end)
       continue;
 
     feature_event_t *event;
 
     event = feature_events.push ();
-    event->index = features[i].start;
+    event->index = feature.start;
     event->start = true;
     event->feature = feature.info;
 
     event = feature_events.push ();
-    event->index = features[i].end;
+    event->index = feature.end;
     event->start = false;
     event->feature = feature.info;
   }
@@ -139,12 +145,12 @@ hb_aat_map_builder_t::compile (hb_aat_map_t  &m)
         current_features.qsort ();
         unsigned int j = 0;
         for (unsigned int i = 1; i < current_features.length; i++)
-          if (current_features[i].type != current_features[j].type ||
+          if (current_features.arrayZ[i].type != current_features.arrayZ[j].type ||
               /* Nonexclusive feature selectors come in even/odd pairs to turn a setting on/off
                * respectively, so we mask out the low-order bit when checking for "duplicates"
                * (selectors referring to the same feature setting) here. */
-              (!current_features[i].is_exclusive && ((current_features[i].setting & ~1) != (current_features[j].setting & ~1))))
-            current_features[++j] = current_features[i];
+              (!current_features.arrayZ[i].is_exclusive && ((current_features.arrayZ[i].setting & ~1) != (current_features.arrayZ[j].setting & ~1))))
+            current_features.arrayZ[++j] = current_features.arrayZ[i];
         current_features.shrink (j + 1);
       }
 
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-algs.hh b/src/java.desktop/share/native/libharfbuzz/hb-algs.hh
index b2b7c2567392f..6e250bfdef0ea 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-algs.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-algs.hh
@@ -202,8 +202,12 @@ struct BEInt
 /* Floats. */
 
 /* We want our rounding towards +infinity. */
+static inline double
+_hb_roundf (double x) { return floor (x + .5); }
+
 static inline float
 _hb_roundf (float x) { return floorf (x + .5f); }
+
 #define roundf(x) _hb_roundf(x)
 
 
@@ -282,7 +286,7 @@ HB_FUNCOBJ (hb_bool);
 
 // Compression function for Merkle-Damgard construction.
 // This function is generated using the framework provided.
-#define mix(h) (                                        \
+#define fasthash_mix(h) (                                       \
                         (void) ((h) ^= (h) >> 23),              \
                         (void) ((h) *= 0x2127599bf4325c37ULL),  \
                         (h) ^= (h) >> 47)
@@ -306,7 +310,7 @@ static inline uint64_t fasthash64(const void *buf, size_t len, uint64_t seed)
 #pragma GCC diagnostic ignored "-Wcast-align"
             v  = * (const uint64_t *) (pos++);
 #pragma GCC diagnostic pop
-            h ^= mix(v);
+            h ^= fasthash_mix(v);
             h *= m;
           }
         }
@@ -316,7 +320,7 @@ static inline uint64_t fasthash64(const void *buf, size_t len, uint64_t seed)
           while (pos != end)
           {
             v  = pos++->v;
-            h ^= mix(v);
+            h ^= fasthash_mix(v);
             h *= m;
           }
         }
@@ -332,11 +336,11 @@ static inline uint64_t fasthash64(const void *buf, size_t len, uint64_t seed)
         case 3: v ^= (uint64_t)pos2[2] << 16; HB_FALLTHROUGH;
         case 2: v ^= (uint64_t)pos2[1] <<  8; HB_FALLTHROUGH;
         case 1: v ^= (uint64_t)pos2[0];
-                h ^= mix(v);
+                h ^= fasthash_mix(v);
                 h *= m;
         }
 
-        return mix(h);
+        return fasthash_mix(h);
 }
 
 static inline uint32_t fasthash32(const void *buf, size_t len, uint32_t seed)
@@ -671,7 +675,7 @@ struct hb_pair_t
     return 0;
   }
 
-  friend void swap (hb_pair_t& a, hb_pair_t& b)
+  friend void swap (hb_pair_t& a, hb_pair_t& b) noexcept
   {
     hb_swap (a.first, b.first);
     hb_swap (a.second, b.second);
@@ -1053,6 +1057,18 @@ _hb_cmp_method (const void *pkey, const void *pval, Ts... ds)
   return val.cmp (key, ds...);
 }
 
+template 
+static int
+_hb_cmp_operator (const void *pkey, const void *pval)
+{
+  const K& key = * (const K*) pkey;
+  const V& val = * (const V*) pval;
+
+  if (key < val) return -1;
+  if (key > val) return  1;
+  return 0;
+}
+
 template 
 static inline bool
 hb_bsearch_impl (unsigned *pos, /* Out */
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-array.hh b/src/java.desktop/share/native/libharfbuzz/hb-array.hh
index 439f18259cf52..d65bbc5c7115d 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-array.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-array.hh
@@ -47,6 +47,8 @@ enum hb_not_found_t
 template 
 struct hb_array_t : hb_iter_with_fallback_t, Type&>
 {
+  static constexpr bool realloc_move = true;
+
   /*
    * Constructors.
    */
@@ -249,7 +251,8 @@ struct hb_array_t : hb_iter_with_fallback_t, Type&>
     if (end < start + 2)
       return;
 
-    for (unsigned lhs = start, rhs = end - 1; lhs < rhs; lhs++, rhs--)
+    unsigned stop = start + (end - start) / 2;
+    for (unsigned lhs = start, rhs = end - 1; lhs < stop; lhs++, rhs--)
       hb_swap (arrayZ[rhs], arrayZ[lhs]);
   }
 
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh b/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh
index 459d82e0f2e78..80bd90e87dc33 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh
@@ -80,15 +80,14 @@ _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
 
 #include 
 
-#define _hb_memory_barrier()                    std::atomic_thread_fence(std::memory_order_ack_rel)
 #define _hb_memory_r_barrier()                  std::atomic_thread_fence(std::memory_order_acquire)
 #define _hb_memory_w_barrier()                  std::atomic_thread_fence(std::memory_order_release)
 
-#define hb_atomic_int_impl_add(AI, V)           (reinterpret_cast::type> *> (AI)->fetch_add ((V), std::memory_order_acq_rel))
-#define hb_atomic_int_impl_set_relaxed(AI, V)   (reinterpret_cast::type> *> (AI)->store ((V), std::memory_order_relaxed))
-#define hb_atomic_int_impl_set(AI, V)           (reinterpret_cast::type> *> (AI)->store ((V), std::memory_order_release))
-#define hb_atomic_int_impl_get_relaxed(AI)      (reinterpret_cast::type> const *> (AI)->load (std::memory_order_relaxed))
-#define hb_atomic_int_impl_get(AI)              (reinterpret_cast::type> const *> (AI)->load (std::memory_order_acquire))
+#define hb_atomic_int_impl_add(AI, V)           (reinterpret_cast::type> *> (AI)->fetch_add ((V), std::memory_order_acq_rel))
+#define hb_atomic_int_impl_set_relaxed(AI, V)   (reinterpret_cast::type> *> (AI)->store ((V), std::memory_order_relaxed))
+#define hb_atomic_int_impl_set(AI, V)           (reinterpret_cast::type> *> (AI)->store ((V), std::memory_order_release))
+#define hb_atomic_int_impl_get_relaxed(AI)      (reinterpret_cast::type> const *> (AI)->load (std::memory_order_relaxed))
+#define hb_atomic_int_impl_get(AI)              (reinterpret_cast::type> const *> (AI)->load (std::memory_order_acquire))
 
 #define hb_atomic_ptr_impl_set_relaxed(P, V)    (reinterpret_cast *> (P)->store ((V), std::memory_order_relaxed))
 #define hb_atomic_ptr_impl_get_relaxed(P)       (reinterpret_cast const *> (P)->load (std::memory_order_relaxed))
@@ -118,12 +117,12 @@ _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
  */
 #ifndef _hb_compiler_memory_r_barrier
 #if defined(__ATOMIC_ACQUIRE) // gcc-like
-#define _hb_compiler_memory_r_barrier() asm volatile("": : :"memory")
+static inline void _hb_compiler_memory_r_barrier () { asm volatile("": : :"memory"); }
 #elif !defined(_MSC_VER)
 #include 
 #define _hb_compiler_memory_r_barrier() std::atomic_signal_fence (std::memory_order_acquire)
 #else
-#define _hb_compiler_memory_r_barrier() do {} while (0)
+static inline void _hb_compiler_memory_r_barrier () {}
 #endif
 #endif
 
@@ -149,74 +148,66 @@ _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
 #define hb_atomic_ptr_impl_get_relaxed(P)       (*(P))
 #endif
 #ifndef hb_atomic_int_impl_set
-inline void hb_atomic_int_impl_set (int *AI, int v)     { _hb_memory_w_barrier (); *AI = v; }
-inline void hb_atomic_int_impl_set (short *AI, short v) { _hb_memory_w_barrier (); *AI = v; }
+template 
+inline void hb_atomic_int_impl_set (T *AI, T v) { _hb_memory_w_barrier (); *AI = v; }
 #endif
 #ifndef hb_atomic_int_impl_get
-inline int hb_atomic_int_impl_get (const int *AI)       { int v = *AI; _hb_memory_r_barrier (); return v; }
-inline short hb_atomic_int_impl_get (const short *AI)   { short v = *AI; _hb_memory_r_barrier (); return v; }
+template 
+inline T hb_atomic_int_impl_get (const T *AI)   { T v = *AI; _hb_memory_r_barrier (); return v; }
 #endif
 #ifndef hb_atomic_ptr_impl_get
 inline void *hb_atomic_ptr_impl_get (void ** const P)   { void *v = *P; _hb_memory_r_barrier (); return v; }
 #endif
 
 
-struct hb_atomic_short_t
+template 
+struct hb_atomic_t
 {
-  hb_atomic_short_t () = default;
-  constexpr hb_atomic_short_t (short v) : v (v) {}
+  hb_atomic_t () = default;
+  constexpr hb_atomic_t (T v) : v (v) {}
 
-  hb_atomic_short_t& operator = (short v_) { set_relaxed (v_); return *this; }
-  operator short () const { return get_relaxed (); }
+  hb_atomic_t& operator = (T v_) { set_relaxed (v_); return *this; }
+  operator T () const { return get_relaxed (); }
 
-  void set_relaxed (short v_) { hb_atomic_int_impl_set_relaxed (&v, v_); }
-  void set_release (short v_) { hb_atomic_int_impl_set (&v, v_); }
-  short get_relaxed () const { return hb_atomic_int_impl_get_relaxed (&v); }
-  short get_acquire () const { return hb_atomic_int_impl_get (&v); }
-  short inc () { return hb_atomic_int_impl_add (&v,  1); }
-  short dec () { return hb_atomic_int_impl_add (&v, -1); }
+  void set_relaxed (T v_) { hb_atomic_int_impl_set_relaxed (&v, v_); }
+  void set_release (T v_) { hb_atomic_int_impl_set (&v, v_); }
+  T get_relaxed () const { return hb_atomic_int_impl_get_relaxed (&v); }
+  T get_acquire () const { return hb_atomic_int_impl_get (&v); }
+  T inc () { return hb_atomic_int_impl_add (&v,  1); }
+  T dec () { return hb_atomic_int_impl_add (&v, -1); }
 
-  short v = 0;
-};
-
-struct hb_atomic_int_t
-{
-  hb_atomic_int_t () = default;
-  constexpr hb_atomic_int_t (int v) : v (v) {}
-
-  hb_atomic_int_t& operator = (int v_) { set_relaxed (v_); return *this; }
-  operator int () const { return get_relaxed (); }
-
-  void set_relaxed (int v_) { hb_atomic_int_impl_set_relaxed (&v, v_); }
-  void set_release (int v_) { hb_atomic_int_impl_set (&v, v_); }
-  int get_relaxed () const { return hb_atomic_int_impl_get_relaxed (&v); }
-  int get_acquire () const { return hb_atomic_int_impl_get (&v); }
-  int inc () { return hb_atomic_int_impl_add (&v,  1); }
-  int dec () { return hb_atomic_int_impl_add (&v, -1); }
+  int operator ++ (int) { return inc (); }
+  int operator -- (int) { return dec (); }
+  long operator |= (long v_) { set_relaxed (get_relaxed () | v_); return *this; }
 
-  int v = 0;
+  T v = 0;
 };
 
-template 
-struct hb_atomic_ptr_t
+template 
+struct hb_atomic_t
 {
-  typedef hb_remove_pointer

T; - - hb_atomic_ptr_t () = default; - constexpr hb_atomic_ptr_t (T* v) : v (v) {} - hb_atomic_ptr_t (const hb_atomic_ptr_t &other) = delete; + hb_atomic_t () = default; + constexpr hb_atomic_t (T* v) : v (v) {} + hb_atomic_t (const hb_atomic_t &other) = delete; void init (T* v_ = nullptr) { set_relaxed (v_); } void set_relaxed (T* v_) { hb_atomic_ptr_impl_set_relaxed (&v, v_); } T *get_relaxed () const { return (T *) hb_atomic_ptr_impl_get_relaxed (&v); } T *get_acquire () const { return (T *) hb_atomic_ptr_impl_get ((void **) &v); } - bool cmpexch (const T *old, T *new_) const { return hb_atomic_ptr_impl_cmpexch ((void **) &v, (void *) old, (void *) new_); } + bool cmpexch (const T *old, T *new_) { return hb_atomic_ptr_impl_cmpexch ((void **) &v, (void *) old, (void *) new_); } + operator bool () const { return get_acquire () != nullptr; } T * operator -> () const { return get_acquire (); } template operator C * () const { return get_acquire (); } T *v = nullptr; }; +static inline bool hb_barrier () +{ + _hb_compiler_memory_r_barrier (); + return true; +} + #endif /* HB_ATOMIC_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-bit-page.hh b/src/java.desktop/share/native/libharfbuzz/hb-bit-page.hh index 404a19ce557f8..9562a9674a551 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-bit-page.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-bit-page.hh @@ -78,6 +78,28 @@ struct hb_vector_size_t hb_vector_size_t operator ~ () const { return process (hb_bitwise_neg); } + operator bool () const + { + for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++) + if (v[i]) + return true; + return false; + } + operator unsigned int () const + { + unsigned int r = 0; + for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++) + r += hb_popcount (v[i]); + return r; + } + bool operator == (const hb_vector_size_t &o) const + { + for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++) + if (v[i] != o.v[i]) + return false; + return true; + } + hb_array_t iter () const { return hb_array (v); } @@ -89,6 +111,8 @@ struct hb_vector_size_t struct hb_bit_page_t { + hb_bit_page_t () { init0 (); } + void init0 () { v.init0 (); population = 0; } void init1 () { v.init1 (); population = PAGE_BITS; } @@ -101,10 +125,9 @@ struct hb_bit_page_t bool is_empty () const { if (has_population ()) return !population; - return - + hb_iter (v) - | hb_none - ; + bool empty = !v; + if (empty) population = 0; + return empty; } uint32_t hash () const { @@ -115,6 +138,10 @@ struct hb_bit_page_t void del (hb_codepoint_t g) { elt (g) &= ~mask (g); dirty (); } void set (hb_codepoint_t g, bool value) { if (value) add (g); else del (g); } bool get (hb_codepoint_t g) const { return elt (g) & mask (g); } + bool may_have (hb_codepoint_t g) const { return get (g); } + + bool operator [] (hb_codepoint_t g) const { return get (g); } + bool operator () (hb_codepoint_t g) const { return get (g); } void add_range (hb_codepoint_t a, hb_codepoint_t b) { @@ -220,13 +247,17 @@ struct hb_bit_page_t } bool operator == (const hb_bit_page_t &other) const { return is_equal (other); } - bool is_equal (const hb_bit_page_t &other) const + bool is_equal (const hb_bit_page_t &other) const { return v == other.v; } + bool intersects (const hb_bit_page_t &other) const { for (unsigned i = 0; i < len (); i++) - if (v[i] != other.v[i]) - return false; - return true; + if (v[i] & other.v[i]) + return true; + return false; } + bool may_intersect (const hb_bit_page_t &other) const + { return intersects (other); } + bool operator <= (const hb_bit_page_t &larger_page) const { return is_subset (larger_page); } bool is_subset (const hb_bit_page_t &larger_page) const { @@ -241,14 +272,10 @@ struct hb_bit_page_t } bool has_population () const { return population != UINT_MAX; } - unsigned int get_population () const + unsigned get_population () const { if (has_population ()) return population; - population = - + hb_iter (v) - | hb_reduce ([] (unsigned pop, const elt_t &_) { return pop + hb_popcount (_); }, 0u) - ; - return population; + return population = v; } bool next (hb_codepoint_t *codepoint) const diff --git a/src/java.desktop/share/native/libharfbuzz/hb-bit-set-invertible.hh b/src/java.desktop/share/native/libharfbuzz/hb-bit-set-invertible.hh index 2e335549e2603..740a2437671a6 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-bit-set-invertible.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-bit-set-invertible.hh @@ -39,10 +39,10 @@ struct hb_bit_set_invertible_t hb_bit_set_invertible_t () = default; hb_bit_set_invertible_t (const hb_bit_set_invertible_t& o) = default; - hb_bit_set_invertible_t (hb_bit_set_invertible_t&& other) : hb_bit_set_invertible_t () { hb_swap (*this, other); } + hb_bit_set_invertible_t (hb_bit_set_invertible_t&& other) noexcept : hb_bit_set_invertible_t () { hb_swap (*this, other); } hb_bit_set_invertible_t& operator= (const hb_bit_set_invertible_t& o) = default; - hb_bit_set_invertible_t& operator= (hb_bit_set_invertible_t&& other) { hb_swap (*this, other); return *this; } - friend void swap (hb_bit_set_invertible_t &a, hb_bit_set_invertible_t &b) + hb_bit_set_invertible_t& operator= (hb_bit_set_invertible_t&& other) noexcept { hb_swap (*this, other); return *this; } + friend void swap (hb_bit_set_invertible_t &a, hb_bit_set_invertible_t &b) noexcept { if (likely (!a.s.successful || !b.s.successful)) return; @@ -126,6 +126,7 @@ struct hb_bit_set_invertible_t { unlikely (inverted) ? (void) s.add_range (a, b) : s.del_range (a, b); } bool get (hb_codepoint_t g) const { return s.get (g) ^ inverted; } + bool may_have (hb_codepoint_t g) const { return get (g); } /* Has interface. */ bool operator [] (hb_codepoint_t k) const { return get (k); } @@ -139,6 +140,9 @@ struct hb_bit_set_invertible_t hb_bit_set_invertible_t& operator << (const hb_codepoint_pair_t& range) { add_range (range.first, range.second); return *this; } + bool may_intersect (const hb_bit_set_invertible_t &other) const + { return inverted || other.inverted || s.intersects (other.s); } + bool intersects (hb_codepoint_t first, hb_codepoint_t last) const { hb_codepoint_t c = first - 1; @@ -359,8 +363,8 @@ struct hb_bit_set_invertible_t typedef hb_codepoint_t __item_t__; hb_codepoint_t __item__ () const { return v; } bool __more__ () const { return v != INVALID; } - void __next__ () { s->next (&v); if (l) l--; } - void __prev__ () { s->previous (&v); } + void __next__ () { s->next (&v); if (likely (l)) l--; } + void __prev__ () { s->previous (&v); l++; } unsigned __len__ () const { return l; } iter_t end () const { return iter_t (*s, false); } bool operator != (const iter_t& o) const diff --git a/src/java.desktop/share/native/libharfbuzz/hb-bit-set.hh b/src/java.desktop/share/native/libharfbuzz/hb-bit-set.hh index b900711a33a97..799c360759965 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-bit-set.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-bit-set.hh @@ -38,10 +38,10 @@ struct hb_bit_set_t ~hb_bit_set_t () = default; hb_bit_set_t (const hb_bit_set_t& other) : hb_bit_set_t () { set (other, true); } - hb_bit_set_t ( hb_bit_set_t&& other) : hb_bit_set_t () { hb_swap (*this, other); } + hb_bit_set_t ( hb_bit_set_t&& other) noexcept : hb_bit_set_t () { hb_swap (*this, other); } hb_bit_set_t& operator= (const hb_bit_set_t& other) { set (other); return *this; } - hb_bit_set_t& operator= (hb_bit_set_t&& other) { hb_swap (*this, other); return *this; } - friend void swap (hb_bit_set_t &a, hb_bit_set_t &b) + hb_bit_set_t& operator= (hb_bit_set_t&& other) noexcept { hb_swap (*this, other); return *this; } + friend void swap (hb_bit_set_t &a, hb_bit_set_t &b) noexcept { if (likely (!a.successful || !b.successful)) return; @@ -77,7 +77,7 @@ struct hb_bit_set_t bool successful = true; /* Allocations successful */ mutable unsigned int population = 0; - mutable hb_atomic_int_t last_page_lookup = 0; + mutable hb_atomic_t last_page_lookup = 0; hb_sorted_vector_t page_map; hb_vector_t pages; @@ -88,10 +88,11 @@ struct hb_bit_set_t { if (unlikely (!successful)) return false; - if (pages.length == 0 && count == 1) + if (pages.length < count && (unsigned) pages.allocated < count && count <= 2) exact_size = true; // Most sets are small and local - if (unlikely (!pages.resize (count, clear, exact_size) || !page_map.resize (count, clear, exact_size))) + if (unlikely (!pages.resize (count, clear, exact_size) || + !page_map.resize (count, clear))) { pages.resize (page_map.length, clear, exact_size); successful = false; @@ -297,9 +298,9 @@ struct hb_bit_set_t unsigned int write_index = 0; for (unsigned int i = 0; i < page_map.length; i++) { - int m = (int) page_map[i].major; + int m = (int) page_map.arrayZ[i].major; if (m < ds || de < m) - page_map[write_index++] = page_map[i]; + page_map.arrayZ[write_index++] = page_map.arrayZ[i]; } compact (compact_workspace, write_index); resize (write_index); @@ -345,6 +346,7 @@ struct hb_bit_set_t return false; return page->get (g); } + bool may_have (hb_codepoint_t g) const { return get (g); } /* Has interface. */ bool operator [] (hb_codepoint_t k) const { return get (k); } @@ -358,6 +360,31 @@ struct hb_bit_set_t hb_bit_set_t& operator << (const hb_codepoint_pair_t& range) { add_range (range.first, range.second); return *this; } + bool intersects (const hb_bit_set_t &other) const + { + unsigned int na = pages.length; + unsigned int nb = other.pages.length; + + unsigned int a = 0, b = 0; + for (; a < na && b < nb; ) + { + if (page_map.arrayZ[a].major == other.page_map.arrayZ[b].major) + { + if (page_at (a).intersects (other.page_at (b))) + return true; + a++; + b++; + } + else if (page_map.arrayZ[a].major < other.page_map.arrayZ[b].major) + a++; + else + b++; + } + return false; + } + bool may_intersect (const hb_bit_set_t &other) const + { return intersects (other); } + bool intersects (hb_codepoint_t first, hb_codepoint_t last) const { hb_codepoint_t c = first - 1; @@ -389,7 +416,7 @@ struct hb_bit_set_t { if (page_at (a).is_empty ()) { a++; continue; } if (other.page_at (b).is_empty ()) { b++; continue; } - if (page_map[a].major != other.page_map[b].major || + if (page_map.arrayZ[a].major != other.page_map.arrayZ[b].major || !page_at (a).is_equal (other.page_at (b))) return false; a++; @@ -412,8 +439,8 @@ struct hb_bit_set_t uint32_t spi = 0; for (uint32_t lpi = 0; spi < page_map.length && lpi < larger_set.page_map.length; lpi++) { - uint32_t spm = page_map[spi].major; - uint32_t lpm = larger_set.page_map[lpi].major; + uint32_t spm = page_map.arrayZ[spi].major; + uint32_t lpm = larger_set.page_map.arrayZ[lpi].major; auto sp = page_at (spi); if (spm < lpm && !sp.is_empty ()) @@ -503,7 +530,7 @@ struct hb_bit_set_t for (; a < na && b < nb; ) { - if (page_map[a].major == other.page_map[b].major) + if (page_map.arrayZ[a].major == other.page_map.arrayZ[b].major) { if (!passthru_left) { @@ -512,7 +539,7 @@ struct hb_bit_set_t // passthru_left is set since no left side pages will be removed // in that case. if (write_index < a) - page_map[write_index] = page_map[a]; + page_map.arrayZ[write_index] = page_map.arrayZ[a]; write_index++; } @@ -520,7 +547,7 @@ struct hb_bit_set_t a++; b++; } - else if (page_map[a].major < other.page_map[b].major) + else if (page_map.arrayZ[a].major < other.page_map.arrayZ[b].major) { if (passthru_left) count++; @@ -765,8 +792,8 @@ struct hb_bit_set_t unsigned int initial_size = size; for (unsigned int i = start_page; i < page_map.length && size; i++) { - uint32_t base = major_start (page_map[i].major); - unsigned int n = pages[page_map[i].index].write (base, start_page_value, out, size); + uint32_t base = major_start (page_map.arrayZ[i].major); + unsigned int n = pages[page_map.arrayZ[i].index].write (base, start_page_value, out, size); out += n; size -= n; start_page_value = 0; @@ -814,8 +841,8 @@ struct hb_bit_set_t hb_codepoint_t next_value = codepoint + 1; for (unsigned int i=start_page; i= 0; i--) { - const auto& map = page_map[(unsigned) i]; - const auto& page = pages[map.index]; + const auto& map = page_map.arrayZ[(unsigned) i]; + const auto& page = pages.arrayZ[map.index]; if (!page.is_empty ()) return map.major * page_t::PAGE_BITS + page.get_max (); @@ -961,7 +988,7 @@ struct hb_bit_set_t return nullptr; last_page_lookup = i; - return &pages.arrayZ[page_map[i].index]; + return &pages.arrayZ[page_map.arrayZ[i].index]; } page_t &page_at (unsigned int i) { diff --git a/src/java.desktop/share/native/libharfbuzz/hb-bit-vector.hh b/src/java.desktop/share/native/libharfbuzz/hb-bit-vector.hh new file mode 100644 index 0000000000000..2a0ec3a071030 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-bit-vector.hh @@ -0,0 +1,195 @@ +/* + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_BIT_VECTOR_HH +#define HB_BIT_VECTOR_HH + +#include "hb.hh" + +#include "hb-atomic.hh" + +struct hb_min_max_t +{ + void add (hb_codepoint_t v) { min_v = hb_min (min_v, v); max_v = hb_max (max_v, v); } + void add_range (hb_codepoint_t a, hb_codepoint_t b) + { + min_v = hb_min (min_v, a); + max_v = hb_max (max_v, b); + } + + template + void union_ (const set_t &set) + { + hb_codepoint_t set_min = set.get_min (); + if (unlikely (set_min == HB_CODEPOINT_INVALID)) + return; + hb_codepoint_t set_max = set.get_max (); + min_v = hb_min (min_v, set_min); + max_v = hb_max (max_v, set_max); + } + + hb_codepoint_t get_min () const { return min_v; } + hb_codepoint_t get_max () const { return max_v; } + + private: + hb_codepoint_t min_v = HB_CODEPOINT_INVALID; + hb_codepoint_t max_v = 0; +}; + +template +struct hb_bit_vector_t +{ + using int_t = uint64_t; + using elt_t = typename std::conditional, int_t>::type; + + hb_bit_vector_t () = delete; + hb_bit_vector_t (const hb_bit_vector_t &other) = delete; + hb_bit_vector_t &operator= (const hb_bit_vector_t &other) = delete; + + // Move + hb_bit_vector_t (hb_bit_vector_t &&other) + : min_v (other.min_v), max_v (other.max_v), count (other.count), elts (other.elts) + { + other.min_v = other.max_v = other.count = 0; + other.elts = nullptr; + } + hb_bit_vector_t &operator= (hb_bit_vector_t &&other) + { + hb_swap (min_v, other.min_v); + hb_swap (max_v, other.max_v); + hb_swap (count, other.count); + hb_swap (elts, other.elts); + return *this; + } + + hb_bit_vector_t (unsigned min_v, unsigned max_v) + : min_v (min_v), max_v (max_v) + { + if (unlikely (min_v >= max_v)) + { + min_v = max_v = count = 0; + return; + } + + unsigned num = (max_v - min_v + sizeof (int_t) * 8) / (sizeof (int_t) * 8); + elts = (elt_t *) hb_calloc (num, sizeof (int_t)); + if (unlikely (!elts)) + { + min_v = max_v = count = 0; + return; + } + + count = max_v - min_v + 1; + } + ~hb_bit_vector_t () + { + hb_free (elts); + } + + void add (hb_codepoint_t g) { elt (g) |= mask (g); } + void del (hb_codepoint_t g) { elt (g) &= ~mask (g); } + void set (hb_codepoint_t g, bool value) { if (value) add (g); else del (g); } + bool get (hb_codepoint_t g) const { return elt (g) & mask (g); } + bool has (hb_codepoint_t g) const { return get (g); } + bool may_have (hb_codepoint_t g) const { return get (g); } + + bool operator [] (hb_codepoint_t g) const { return get (g); } + bool operator () (hb_codepoint_t g) const { return get (g); } + + void add_range (hb_codepoint_t a, hb_codepoint_t b) + { + if (unlikely (!count || a > b || a < min_v || b > max_v)) + return; + + elt_t *la = &elt (a); + elt_t *lb = &elt (b); + if (la == lb) + *la |= (mask (b) << 1) - mask(a); + else + { + *la |= ~(mask (a) - 1llu); + la++; + + hb_memset (la, 0xff, (char *) lb - (char *) la); + + *lb |= ((mask (b) << 1) - 1llu); + } + } + void del_range (hb_codepoint_t a, hb_codepoint_t b) + { + if (unlikely (!count || a > b || a < min_v || b > max_v)) + return; + + elt_t *la = &elt (a); + elt_t *lb = &elt (b); + if (la == lb) + *la &= ~((mask (b) << 1llu) - mask(a)); + else + { + *la &= mask (a) - 1; + la++; + + hb_memset (la, 0, (char *) lb - (char *) la); + + *lb &= ~((mask (b) << 1) - 1llu); + } + } + void set_range (hb_codepoint_t a, hb_codepoint_t b, bool v) + { if (v) add_range (a, b); else del_range (a, b); } + + template + void union_ (const set_t &set) + { + for (hb_codepoint_t g : set) + add (g); + } + + static const unsigned int ELT_BITS = sizeof (elt_t) * 8; + static constexpr unsigned ELT_MASK = ELT_BITS - 1; + + static constexpr elt_t zero = 0; + + elt_t &elt (hb_codepoint_t g) + { + g -= min_v; + if (unlikely (g >= count)) + return Crap(elt_t); + return elts[g / ELT_BITS]; + } + const elt_t& elt (hb_codepoint_t g) const + { + g -= min_v; + if (unlikely (g >= count)) + return Null(elt_t); + return elts[g / ELT_BITS]; + } + + static constexpr int_t mask (hb_codepoint_t g) { return elt_t (1) << (g & ELT_MASK); } + + hb_codepoint_t min_v = 0, max_v = 0, count = 0; + elt_t *elts = nullptr; +}; + + +#endif /* HB_BIT_VECTOR_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-blob.cc b/src/java.desktop/share/native/libharfbuzz/hb-blob.cc index 2a43afa1a435f..a19599fac09a0 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-blob.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-blob.cc @@ -598,6 +598,11 @@ _open_resource_fork (const char *file_name, hb_mapped_file_t *file) * Creates a new blob containing the data from the * specified binary font file. * + * The filename is passed directly to the system on all platforms, + * except on Windows, where the filename is interpreted as UTF-8. + * Only if the filename is not valid UTF-8, it will be interpreted + * according to the system codepage. + * * Returns: An #hb_blob_t pointer with the content of the file, * or hb_blob_get_empty() if failed. * @@ -612,10 +617,14 @@ hb_blob_create_from_file (const char *file_name) /** * hb_blob_create_from_file_or_fail: - * @file_name: A font filename + * @file_name: A filename * - * Creates a new blob containing the data from the - * specified binary font file. + * Creates a new blob containing the data from the specified file. + * + * The filename is passed directly to the system on all platforms, + * except on Windows, where the filename is interpreted as UTF-8. + * Only if the filename is not valid UTF-8, it will be interpreted + * according to the system codepage. * * Returns: An #hb_blob_t pointer with the content of the file, * or `NULL` if failed. @@ -672,10 +681,19 @@ hb_blob_create_from_file_or_fail (const char *file_name) if (unlikely (!file)) return nullptr; HANDLE fd; + int conversion; unsigned int size = strlen (file_name) + 1; wchar_t * wchar_file_name = (wchar_t *) hb_malloc (sizeof (wchar_t) * size); if (unlikely (!wchar_file_name)) goto fail_without_close; - mbstowcs (wchar_file_name, file_name, size); + + /* Assume file name is given in UTF-8 encoding */ + conversion = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, file_name, -1, wchar_file_name, size); + if (conversion <= 0) + { + /* Conversion failed due to invalid UTF-8 characters, + Repeat conversion based on system code page */ + mbstowcs(wchar_file_name, file_name, size); + } #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) { CREATEFILE2_EXTENDED_PARAMETERS ceparams = { 0 }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-json.hh b/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-json.hh index 2a90cbfe3eaef..255497b652d6a 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-json.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-json.hh @@ -32,62 +32,49 @@ #include "hb.hh" -#line 36 "hb-buffer-deserialize-json.hh" +#line 33 "hb-buffer-deserialize-json.hh" static const unsigned char _deserialize_json_trans_keys[] = { - 0u, 0u, 9u, 123u, 9u, 34u, 97u, 117u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, - 48u, 57u, 9u, 125u, 9u, 125u, 9u, 93u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, - 48u, 57u, 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, - 9u, 125u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, - 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, - 9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 34u, 92u, - 9u, 125u, 34u, 92u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, - 9u, 123u, 0u, 0u, 0 + 0u, 0u, 9u, 34u, 97u, 121u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, + 9u, 125u, 9u, 125u, 9u, 93u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, + 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u, + 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, + 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, + 9u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 34u, 92u, 9u, 125u, + 34u, 92u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, + 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 98u, 98u, 9u, 123u, 9u, 123u, 9u, 123u, + 0 }; static const char _deserialize_json_key_spans[] = { - 0, 115, 26, 21, 2, 1, 50, 49, - 10, 117, 117, 85, 117, 1, 50, 49, - 10, 117, 117, 1, 1, 50, 49, 117, - 117, 2, 1, 50, 49, 10, 117, 117, - 1, 50, 49, 10, 117, 117, 1, 1, - 50, 49, 117, 117, 1, 50, 49, 59, - 117, 59, 117, 117, 1, 50, 49, 117, - 115, 0 + 0, 26, 25, 2, 1, 50, 49, 10, + 117, 117, 85, 117, 1, 50, 49, 10, + 117, 117, 1, 1, 50, 49, 117, 117, + 2, 1, 50, 49, 10, 117, 117, 1, + 50, 49, 10, 117, 117, 1, 1, 50, + 49, 117, 117, 1, 50, 49, 59, 117, + 59, 117, 117, 1, 50, 49, 10, 117, + 1, 50, 49, 117, 1, 115, 115, 115 }; static const short _deserialize_json_index_offsets[] = { - 0, 0, 116, 143, 165, 168, 170, 221, - 271, 282, 400, 518, 604, 722, 724, 775, - 825, 836, 954, 1072, 1074, 1076, 1127, 1177, - 1295, 1413, 1416, 1418, 1469, 1519, 1530, 1648, - 1766, 1768, 1819, 1869, 1880, 1998, 2116, 2118, - 2120, 2171, 2221, 2339, 2457, 2459, 2510, 2560, - 2620, 2738, 2798, 2916, 3034, 3036, 3087, 3137, - 3255, 3371 + 0, 0, 27, 53, 56, 58, 109, 159, + 170, 288, 406, 492, 610, 612, 663, 713, + 724, 842, 960, 962, 964, 1015, 1065, 1183, + 1301, 1304, 1306, 1357, 1407, 1418, 1536, 1654, + 1656, 1707, 1757, 1768, 1886, 2004, 2006, 2008, + 2059, 2109, 2227, 2345, 2347, 2398, 2448, 2508, + 2626, 2686, 2804, 2922, 2924, 2975, 3025, 3036, + 3154, 3156, 3207, 3257, 3375, 3377, 3493, 3609 }; static const char _deserialize_json_indicies[] = { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 2, 1, 3, 1, 4, 5, + 1, 6, 7, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 2, 1, 3, 3, 3, - 3, 3, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 3, 1, 4, 1, - 5, 1, 6, 7, 1, 8, 9, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 10, 1, 11, 12, + 9, 1, 8, 10, 10, 1, 11, 12, 1, 13, 1, 13, 13, 13, 13, 13, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -121,7 +108,7 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 23, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 3, 1, 1, 1, + 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -131,18 +118,18 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 24, 1, 25, - 25, 25, 25, 25, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 24, 1, 24, + 24, 24, 24, 24, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 25, 1, + 1, 1, 1, 1, 1, 1, 24, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 26, 1, 1, 1, 1, 1, + 1, 1, 25, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 27, 1, 20, 20, 20, + 1, 1, 1, 25, 1, 20, 20, 20, 20, 20, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 20, 1, 1, 1, @@ -157,26 +144,26 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 22, 1, 28, 1, 28, 28, 28, - 28, 28, 1, 1, 1, 1, 1, 1, + 1, 22, 1, 26, 1, 26, 26, 26, + 26, 26, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 28, 1, 1, 1, + 1, 1, 1, 1, 26, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 29, 1, - 29, 29, 29, 29, 29, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 27, 1, + 27, 27, 27, 27, 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 29, + 1, 1, 1, 1, 1, 1, 1, 27, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 30, 1, 1, 31, - 32, 32, 32, 32, 32, 32, 32, 32, - 32, 1, 33, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 1, 35, 35, 35, - 35, 35, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 28, 1, 1, 29, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 1, 31, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 1, 33, 33, 33, + 33, 33, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 35, 1, 1, 1, + 1, 1, 1, 1, 33, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 36, 1, 1, 1, 1, 1, 1, 1, + 34, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -186,13 +173,13 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 37, 1, 35, 35, 35, 35, 35, + 1, 35, 1, 33, 33, 33, 33, 33, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 35, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 36, 1, - 1, 1, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 1, 1, 1, 1, + 1, 1, 33, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 34, 1, + 1, 1, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -200,25 +187,25 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 37, - 1, 38, 1, 39, 1, 39, 39, 39, - 39, 39, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 35, + 1, 36, 1, 37, 1, 37, 37, 37, + 37, 37, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 39, 1, 1, 1, + 1, 1, 1, 1, 37, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 40, 1, - 40, 40, 40, 40, 40, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 38, 1, + 38, 38, 38, 38, 38, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 40, + 1, 1, 1, 1, 1, 1, 1, 38, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 41, - 42, 42, 42, 42, 42, 42, 42, 42, - 42, 1, 43, 43, 43, 43, 43, 1, + 1, 1, 1, 1, 1, 1, 1, 39, + 40, 40, 40, 40, 40, 40, 40, 40, + 40, 1, 41, 41, 41, 41, 41, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 43, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 44, 1, 1, + 1, 41, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 42, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -228,41 +215,43 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 45, 1, - 43, 43, 43, 43, 43, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 43, 1, + 41, 41, 41, 41, 41, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 41, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 42, 1, 1, 1, 44, + 44, 44, 44, 44, 44, 44, 44, 44, + 44, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 43, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 44, 1, 1, 1, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 43, 1, 45, 46, + 1, 47, 1, 47, 47, 47, 47, 47, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 45, 1, 47, 48, - 1, 49, 1, 49, 49, 49, 49, 49, + 1, 1, 47, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 49, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 48, 1, 48, 48, + 48, 48, 48, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 48, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 50, 1, 50, 50, - 50, 50, 50, 1, 1, 1, 1, 1, + 1, 1, 49, 1, 1, 50, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 1, + 52, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 1, 54, 54, 54, 54, 54, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 51, 1, 1, 52, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 1, - 54, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 1, 56, 56, 56, 56, 56, + 1, 1, 54, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 55, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 56, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 57, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -270,43 +259,43 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 56, + 1, 54, 54, 54, 54, 54, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 58, - 1, 56, 56, 56, 56, 56, 1, 1, + 54, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 55, 1, 1, 1, + 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 56, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 57, 1, 1, 1, - 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 56, 1, 57, + 1, 57, 57, 57, 57, 57, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 58, 1, 59, - 1, 59, 59, 59, 59, 59, 1, 1, + 57, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 59, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 58, 1, 58, 58, 58, 58, + 58, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 58, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 60, 1, 60, 60, 60, 60, - 60, 1, 1, 1, 1, 1, 1, 1, + 59, 1, 1, 60, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 1, 62, 63, + 63, 63, 63, 63, 63, 63, 63, 63, + 1, 64, 64, 64, 64, 64, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 60, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 61, 1, 1, 62, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 1, 64, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 1, 66, 66, 66, 66, 66, 1, 1, + 64, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 65, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 66, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 67, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -314,42 +303,42 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 66, 1, 64, + 64, 64, 64, 64, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 64, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 68, 1, 66, - 66, 66, 66, 66, 1, 1, 1, 1, + 1, 1, 65, 1, 1, 1, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 66, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 67, 1, 1, 1, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 66, 1, 67, 1, 68, + 1, 68, 68, 68, 68, 68, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 68, 1, 69, 1, 70, - 1, 70, 70, 70, 70, 70, 1, 1, + 68, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 70, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 69, 1, 69, 69, 69, 69, + 69, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 69, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 71, 1, 71, 71, 71, 71, - 71, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 70, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 1, 72, 72, + 72, 72, 72, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 71, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 72, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 72, 73, 73, 73, 73, - 73, 73, 73, 73, 73, 1, 74, 74, - 74, 74, 74, 1, 1, 1, 1, 1, + 1, 73, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 74, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 75, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -357,73 +346,85 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 74, 1, 72, 72, 72, 72, + 72, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 72, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 73, + 1, 1, 1, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 76, 1, 74, 74, 74, 74, - 74, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 74, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 75, - 1, 1, 1, 77, 77, 77, 77, 77, - 77, 77, 77, 77, 77, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 74, 1, 76, 1, 76, 76, 76, 76, + 76, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 76, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 76, 1, 78, 1, 78, 78, 78, 78, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 77, 1, 77, + 77, 77, 77, 77, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 77, 1, 78, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 79, 80, + 80, 80, 80, 80, 80, 80, 80, 80, + 1, 82, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 83, 81, 84, 84, 84, + 84, 84, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 78, 1, 1, 1, 1, + 1, 1, 1, 1, 84, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 85, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 79, 1, 79, - 79, 79, 79, 79, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 79, 1, - 80, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 81, 82, - 82, 82, 82, 82, 82, 82, 82, 82, - 1, 84, 83, 83, 83, 83, 83, 83, - 83, 83, 83, 83, 83, 83, 83, 83, - 83, 83, 83, 83, 83, 83, 83, 83, - 83, 83, 83, 83, 83, 83, 83, 83, - 83, 83, 83, 83, 83, 83, 83, 83, - 83, 83, 83, 83, 83, 83, 83, 83, - 83, 83, 83, 83, 83, 83, 83, 83, - 83, 83, 83, 85, 83, 86, 86, 86, - 86, 86, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 86, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 87, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 86, 1, 81, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 88, 1, 83, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 81, 1, 87, + 87, 87, 87, 87, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 87, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 88, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 83, 1, 89, - 89, 89, 89, 89, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 89, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 90, 1, 1, 1, 1, 1, + 1, 1, 1, 89, 1, 87, 87, 87, + 87, 87, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 87, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 88, 1, 1, 1, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -431,52 +432,86 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 91, 1, 89, 89, 89, - 89, 89, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 89, 1, 1, 1, + 1, 89, 1, 91, 1, 91, 91, 91, + 91, 91, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 90, 1, 1, 1, 92, 92, 92, 92, - 92, 92, 92, 92, 92, 92, 1, 1, + 1, 1, 1, 1, 91, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 92, 1, + 92, 92, 92, 92, 92, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 92, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 93, 1, 1, 94, + 95, 95, 95, 95, 95, 95, 95, 95, + 95, 1, 23, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 1, 23, 23, 23, + 23, 23, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 23, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 91, 1, 93, 1, 93, 93, 93, - 93, 93, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 93, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 94, 1, - 94, 94, 94, 94, 94, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 94, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 95, - 96, 96, 96, 96, 96, 96, 96, 96, - 96, 1, 89, 89, 89, 89, 89, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 24, 1, 97, 1, 97, 97, 97, + 97, 97, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 97, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 98, 1, + 98, 98, 98, 98, 98, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 98, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 99, + 100, 100, 100, 100, 100, 100, 100, 100, + 100, 1, 87, 87, 87, 87, 87, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 87, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 88, 1, 1, + 1, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 89, 1, + 8, 1, 102, 102, 102, 102, 102, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 102, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 89, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 90, 1, 1, - 1, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 103, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 104, 1, 103, 103, + 103, 103, 103, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 103, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 91, 1, - 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -484,53 +519,65 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 104, 1, 25, 25, 25, 25, 25, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 25, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 2, 1, 1, 0 + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 104, 1, 0 }; static const char _deserialize_json_trans_targs[] = { - 1, 0, 2, 2, 3, 4, 19, 25, - 38, 44, 52, 5, 13, 6, 7, 8, - 9, 12, 9, 12, 10, 2, 11, 10, - 11, 11, 56, 57, 14, 15, 16, 17, - 18, 17, 18, 10, 2, 11, 20, 21, - 22, 23, 24, 10, 2, 11, 24, 26, - 32, 27, 28, 29, 30, 31, 30, 31, - 10, 2, 11, 33, 34, 35, 36, 37, - 36, 37, 10, 2, 11, 39, 40, 41, - 42, 43, 10, 2, 11, 43, 45, 46, - 47, 50, 51, 47, 48, 49, 10, 2, - 11, 10, 2, 11, 51, 53, 54, 50, - 55, 55 + 1, 0, 2, 3, 18, 24, 37, 43, + 51, 56, 60, 4, 12, 5, 6, 7, + 8, 11, 8, 11, 9, 1, 10, 9, + 10, 63, 13, 14, 15, 16, 17, 16, + 17, 9, 1, 10, 19, 20, 21, 22, + 23, 9, 1, 10, 23, 25, 31, 26, + 27, 28, 29, 30, 29, 30, 9, 1, + 10, 32, 33, 34, 35, 36, 35, 36, + 9, 1, 10, 38, 39, 40, 41, 42, + 9, 1, 10, 42, 44, 45, 46, 49, + 50, 46, 47, 48, 9, 1, 10, 9, + 1, 10, 50, 52, 53, 54, 9, 55, + 55, 57, 58, 49, 59, 59, 61, 62, + 1 }; static const char _deserialize_json_trans_actions[] = { - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 2, - 2, 2, 0, 0, 3, 3, 4, 0, - 5, 0, 0, 0, 0, 0, 2, 2, - 2, 0, 0, 6, 6, 7, 0, 0, - 0, 2, 2, 8, 8, 9, 0, 0, - 0, 0, 0, 2, 2, 2, 0, 0, - 10, 10, 11, 0, 0, 2, 2, 2, - 0, 0, 12, 12, 13, 0, 0, 0, - 2, 2, 14, 14, 15, 0, 0, 0, - 2, 16, 16, 0, 17, 0, 18, 18, - 19, 20, 20, 21, 17, 0, 0, 22, - 22, 23 + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, + 1, 1, 0, 0, 2, 2, 2, 0, + 0, 3, 0, 0, 1, 1, 1, 0, + 0, 4, 4, 4, 0, 0, 0, 1, + 1, 5, 5, 5, 0, 0, 0, 0, + 0, 1, 1, 1, 0, 0, 6, 6, + 6, 0, 0, 1, 1, 1, 0, 0, + 7, 7, 7, 0, 0, 0, 1, 1, + 8, 8, 8, 0, 0, 0, 1, 9, + 9, 0, 10, 0, 11, 11, 11, 12, + 12, 12, 10, 0, 0, 1, 1, 1, + 0, 0, 0, 13, 13, 14, 0, 0, + 15 }; -static const int deserialize_json_start = 1; -static const int deserialize_json_first_final = 56; +static const int deserialize_json_start = 61; +static const int deserialize_json_first_final = 61; static const int deserialize_json_error = 0; -static const int deserialize_json_en_main = 1; +static const int deserialize_json_en_main = 61; -#line 111 "hb-buffer-deserialize-json.rl" +#line 115 "hb-buffer-deserialize-json.rl" static hb_bool_t @@ -545,22 +592,17 @@ _hb_buffer_deserialize_json (hb_buffer_t *buffer, /* Ensure we have positions. */ (void) hb_buffer_get_glyph_positions (buffer, nullptr); - while (p < pe && ISSPACE (*p)) - p++; - if (p < pe && *p == (buffer->len ? ',' : '[')) - *end_ptr = ++p; - const char *tok = nullptr; int cs; hb_glyph_info_t info = {0}; hb_glyph_position_t pos = {0}; -#line 559 "hb-buffer-deserialize-json.hh" +#line 594 "hb-buffer-deserialize-json.hh" { cs = deserialize_json_start; } -#line 564 "hb-buffer-deserialize-json.hh" +#line 597 "hb-buffer-deserialize-json.hh" { int _slen; int _trans; @@ -585,14 +627,14 @@ _resume: goto _again; switch ( _deserialize_json_trans_actions[_trans] ) { - case 1: + case 15: #line 38 "hb-buffer-deserialize-json.rl" { hb_memset (&info, 0, sizeof (info)); hb_memset (&pos , 0, sizeof (pos )); } break; - case 5: + case 3: #line 43 "hb-buffer-deserialize-json.rl" { buffer->add_info (info); @@ -602,21 +644,21 @@ _resume: *end_ptr = p; } break; - case 2: + case 1: #line 51 "hb-buffer-deserialize-json.rl" { tok = p; } break; - case 17: + case 10: #line 55 "hb-buffer-deserialize-json.rl" { if (unlikely (!buffer->ensure_glyphs ())) return false; } break; - case 23: + case 14: #line 56 "hb-buffer-deserialize-json.rl" { if (unlikely (!buffer->ensure_unicode ())) return false; } break; - case 18: + case 11: #line 58 "hb-buffer-deserialize-json.rl" { /* TODO Unescape \" and \\ if found. */ @@ -626,35 +668,35 @@ _resume: return false; } break; - case 20: + case 12: #line 66 "hb-buffer-deserialize-json.rl" { if (!parse_uint (tok, p, &info.codepoint)) return false; } break; - case 8: + case 5: #line 67 "hb-buffer-deserialize-json.rl" { if (!parse_uint (tok, p, &info.cluster )) return false; } break; - case 10: + case 6: #line 68 "hb-buffer-deserialize-json.rl" { if (!parse_int (tok, p, &pos.x_offset )) return false; } break; - case 12: + case 7: #line 69 "hb-buffer-deserialize-json.rl" { if (!parse_int (tok, p, &pos.y_offset )) return false; } break; - case 3: + case 2: #line 70 "hb-buffer-deserialize-json.rl" { if (!parse_int (tok, p, &pos.x_advance)) return false; } break; - case 6: + case 4: #line 71 "hb-buffer-deserialize-json.rl" { if (!parse_int (tok, p, &pos.y_advance)) return false; } break; - case 14: + case 8: #line 72 "hb-buffer-deserialize-json.rl" { if (!parse_uint (tok, p, &info.mask )) return false; } break; - case 16: + case 9: #line 51 "hb-buffer-deserialize-json.rl" { tok = p; @@ -662,7 +704,7 @@ _resume: #line 55 "hb-buffer-deserialize-json.rl" { if (unlikely (!buffer->ensure_glyphs ())) return false; } break; - case 22: + case 13: #line 51 "hb-buffer-deserialize-json.rl" { tok = p; @@ -670,109 +712,7 @@ _resume: #line 56 "hb-buffer-deserialize-json.rl" { if (unlikely (!buffer->ensure_unicode ())) return false; } break; - case 19: -#line 58 "hb-buffer-deserialize-json.rl" - { - /* TODO Unescape \" and \\ if found. */ - if (!hb_font_glyph_from_string (font, - tok+1, p - tok - 2, /* Skip "" */ - &info.codepoint)) - return false; -} -#line 43 "hb-buffer-deserialize-json.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 21: -#line 66 "hb-buffer-deserialize-json.rl" - { if (!parse_uint (tok, p, &info.codepoint)) return false; } -#line 43 "hb-buffer-deserialize-json.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 9: -#line 67 "hb-buffer-deserialize-json.rl" - { if (!parse_uint (tok, p, &info.cluster )) return false; } -#line 43 "hb-buffer-deserialize-json.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 11: -#line 68 "hb-buffer-deserialize-json.rl" - { if (!parse_int (tok, p, &pos.x_offset )) return false; } -#line 43 "hb-buffer-deserialize-json.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 13: -#line 69 "hb-buffer-deserialize-json.rl" - { if (!parse_int (tok, p, &pos.y_offset )) return false; } -#line 43 "hb-buffer-deserialize-json.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 4: -#line 70 "hb-buffer-deserialize-json.rl" - { if (!parse_int (tok, p, &pos.x_advance)) return false; } -#line 43 "hb-buffer-deserialize-json.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 7: -#line 71 "hb-buffer-deserialize-json.rl" - { if (!parse_int (tok, p, &pos.y_advance)) return false; } -#line 43 "hb-buffer-deserialize-json.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 15: -#line 72 "hb-buffer-deserialize-json.rl" - { if (!parse_uint (tok, p, &info.mask )) return false; } -#line 43 "hb-buffer-deserialize-json.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; -#line 776 "hb-buffer-deserialize-json.hh" +#line 689 "hb-buffer-deserialize-json.hh" } _again: @@ -784,12 +724,12 @@ _again: _out: {} } -#line 137 "hb-buffer-deserialize-json.rl" +#line 136 "hb-buffer-deserialize-json.rl" *end_ptr = p; - return p == pe && *(p-1) != ']'; + return p == pe; } #endif /* HB_BUFFER_DESERIALIZE_JSON_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text-glyphs.hh b/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text-glyphs.hh index 8e526ce4e2a02..7857048637699 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text-glyphs.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text-glyphs.hh @@ -32,287 +32,344 @@ #include "hb.hh" -#line 36 "hb-buffer-deserialize-text-glyphs.hh" +#line 33 "hb-buffer-deserialize-text-glyphs.hh" static const unsigned char _deserialize_text_glyphs_trans_keys[] = { - 0u, 0u, 48u, 57u, 45u, 57u, 48u, 57u, 45u, 57u, 48u, 57u, 48u, 57u, 45u, 57u, - 48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 44u, 57u, 43u, 124u, 9u, 124u, 9u, 124u, - 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, - 9u, 124u, 9u, 124u, 9u, 124u, 0 + 0u, 0u, 35u, 124u, 48u, 57u, 60u, 124u, 45u, 57u, 48u, 57u, 44u, 44u, 45u, 57u, + 48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 62u, 62u, + 93u, 124u, 45u, 57u, 48u, 57u, 35u, 124u, 45u, 57u, 48u, 57u, 35u, 124u, 35u, 124u, + 35u, 124u, 35u, 124u, 35u, 124u, 35u, 124u, 48u, 57u, 35u, 124u, 45u, 57u, 48u, 57u, + 44u, 44u, 45u, 57u, 48u, 57u, 35u, 124u, 35u, 124u, 44u, 57u, 35u, 124u, 43u, 124u, + 35u, 124u, 48u, 62u, 44u, 57u, 44u, 57u, 44u, 57u, 48u, 124u, 35u, 124u, 35u, 124u, + 35u, 124u, 0 }; static const char _deserialize_text_glyphs_key_spans[] = { - 0, 10, 13, 10, 13, 10, 10, 13, - 10, 1, 13, 10, 14, 82, 116, 116, - 116, 116, 116, 116, 116, 116, 116, 116, - 116, 116, 116 + 0, 90, 10, 65, 13, 10, 1, 13, + 10, 1, 13, 10, 1, 13, 10, 1, + 32, 13, 10, 90, 13, 10, 90, 90, + 90, 90, 90, 90, 10, 90, 13, 10, + 1, 13, 10, 90, 90, 14, 90, 82, + 90, 15, 14, 14, 14, 77, 90, 90, + 90 }; static const short _deserialize_text_glyphs_index_offsets[] = { - 0, 0, 11, 25, 36, 50, 61, 72, - 86, 97, 99, 113, 124, 139, 222, 339, - 456, 573, 690, 807, 924, 1041, 1158, 1275, - 1392, 1509, 1626 + 0, 0, 91, 102, 168, 182, 193, 195, + 209, 220, 222, 236, 247, 249, 263, 274, + 276, 309, 323, 334, 425, 439, 450, 541, + 632, 723, 814, 905, 996, 1007, 1098, 1112, + 1123, 1125, 1139, 1150, 1241, 1332, 1347, 1438, + 1521, 1612, 1628, 1643, 1658, 1673, 1751, 1842, + 1933 }; static const char _deserialize_text_glyphs_indicies[] = { - 0, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 1, 3, 1, 1, 4, - 5, 5, 5, 5, 5, 5, 5, 5, - 5, 1, 6, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 1, 8, 1, 1, - 9, 10, 10, 10, 10, 10, 10, 10, - 10, 10, 1, 11, 12, 12, 12, 12, - 12, 12, 12, 12, 12, 1, 13, 14, - 14, 14, 14, 14, 14, 14, 14, 14, - 1, 15, 1, 1, 16, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 1, 18, + 1, 0, 0, 0, 0, 0, 0, + 0, 2, 3, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 5, 0, 0, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 7, 8, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 8, 0, 9, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 3, 11, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 12, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 12, + 3, 13, 3, 3, 14, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 3, 14, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 3, 16, 3, 17, 3, 3, 18, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 1, 20, 1, 21, 1, 1, 22, - 23, 23, 23, 23, 23, 23, 23, 23, - 23, 1, 24, 25, 25, 25, 25, 25, - 25, 25, 25, 25, 1, 20, 1, 1, - 1, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 1, 26, 26, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 26, 1, - 1, 26, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 26, 26, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 26, 1, 28, - 28, 28, 28, 28, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 28, 27, - 27, 29, 27, 27, 27, 27, 27, 27, - 27, 30, 1, 27, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 31, 27, 27, 32, 27, - 27, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 33, 1, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 28, 27, 34, 34, 34, 34, - 34, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 34, 26, 26, 35, 26, - 26, 26, 26, 26, 26, 26, 36, 1, - 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, - 37, 26, 26, 38, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 39, - 1, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 40, - 26, 41, 41, 41, 41, 41, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 41, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 42, 1, 43, 43, - 43, 43, 43, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 43, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 44, 1, 41, 41, 41, 41, 41, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 41, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 42, 1, - 46, 46, 46, 46, 46, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 46, - 1, 1, 47, 1, 1, 1, 1, 1, - 1, 1, 1, 48, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 49, 1, 50, 50, 50, - 50, 50, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 50, 1, 1, 51, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 52, 1, 50, 50, 50, 50, 50, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 50, 1, 1, 51, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 12, 12, 12, 12, 12, 12, 12, - 12, 12, 12, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 52, 1, 46, - 46, 46, 46, 46, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 46, 1, - 1, 47, 1, 1, 1, 1, 1, 1, - 1, 1, 48, 1, 1, 1, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 49, 1, 53, 53, 53, 53, - 53, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 53, 1, 1, 54, 1, - 1, 1, 1, 1, 1, 1, 55, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 56, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 57, - 1, 58, 58, 58, 58, 58, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 58, 1, 1, 59, 1, 1, 1, 1, - 1, 1, 1, 60, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 61, 1, 58, 58, - 58, 58, 58, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 58, 1, 1, - 59, 1, 1, 1, 1, 1, 1, 1, - 60, 1, 1, 1, 1, 25, 25, 25, - 25, 25, 25, 25, 25, 25, 25, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 61, 1, 53, 53, 53, 53, 53, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 53, 1, 1, 54, 1, 1, - 1, 1, 1, 1, 1, 55, 1, 1, - 1, 1, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 1, 1, 1, 1, - 1, 1, 56, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 57, 1, - 0 + 19, 3, 18, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 3, 20, 3, 21, + 3, 3, 22, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 3, 22, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 3, + 24, 3, 25, 3, 3, 26, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 3, + 26, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 3, 28, 3, 29, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 29, 3, 30, 3, + 3, 31, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 3, 33, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 3, 35, + 3, 3, 3, 3, 3, 3, 3, 3, + 36, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 37, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 38, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 38, 3, 39, 3, 3, 40, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 3, + 42, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 3, 44, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 45, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 46, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 46, 3, 44, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 3, 3, 45, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 46, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 46, + 3, 35, 3, 3, 3, 3, 3, 3, + 3, 3, 36, 3, 3, 3, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, + 3, 3, 37, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 38, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 38, 3, 1, 0, 0, 0, + 0, 0, 0, 0, 2, 3, 47, 0, + 0, 48, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 0, 0, 4, 5, 0, + 0, 6, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 7, 8, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 8, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 2, + 3, 0, 0, 0, 48, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 0, 0, + 4, 5, 0, 0, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 7, 8, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 8, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 2, 16, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 4, 5, 0, 0, 6, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 8, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 8, 0, 50, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 3, + 52, 3, 3, 3, 3, 3, 3, 3, + 53, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 54, 3, 3, 3, 55, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 56, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 56, 3, 57, 3, 3, 58, 59, + 59, 59, 59, 59, 59, 59, 59, 59, + 3, 60, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 3, 62, 3, 63, 3, + 3, 64, 65, 65, 65, 65, 65, 65, + 65, 65, 65, 3, 66, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 3, 68, + 3, 3, 3, 3, 3, 3, 3, 69, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 70, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 71, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 71, 3, 68, 3, 3, 3, 3, 3, + 3, 3, 69, 3, 3, 3, 3, 67, + 67, 67, 67, 67, 67, 67, 67, 67, + 67, 3, 3, 70, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 71, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 71, 3, 62, 3, 3, + 3, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 3, 52, 3, 3, 3, + 3, 3, 3, 3, 53, 3, 3, 3, + 3, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 3, 3, 54, 3, 3, + 3, 55, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 56, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 56, 3, 0, + 0, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 0, 3, 3, 0, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 0, 0, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 0, 3, 1, 0, 0, 0, 0, 0, + 0, 0, 2, 16, 0, 0, 0, 49, + 49, 49, 49, 49, 49, 49, 49, 49, + 49, 0, 0, 4, 5, 0, 0, 6, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 8, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 8, 0, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 3, + 3, 3, 3, 28, 3, 24, 3, 3, + 3, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 3, 20, 3, 3, 3, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 3, 16, 3, 3, 3, 15, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 3, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 3, 3, 11, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 12, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 12, 3, + 75, 74, 74, 74, 74, 74, 74, 74, + 76, 3, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 77, 78, 74, 74, 79, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 80, 81, 82, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 82, 74, 84, 83, 83, 83, 83, + 83, 83, 83, 85, 3, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 86, 87, 83, 83, + 88, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 89, 90, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 90, 83, 91, 74, + 74, 74, 74, 74, 74, 74, 92, 3, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 93, + 94, 74, 74, 95, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 81, + 96, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 96, + 74, 0 }; static const char _deserialize_text_glyphs_trans_targs[] = { - 16, 0, 18, 3, 19, 22, 19, 22, - 5, 20, 21, 20, 21, 23, 26, 8, - 9, 12, 9, 12, 10, 11, 24, 25, - 24, 25, 15, 15, 14, 1, 2, 6, - 7, 13, 15, 1, 2, 6, 7, 13, - 14, 17, 14, 17, 14, 18, 17, 1, - 4, 14, 17, 1, 14, 17, 1, 2, - 7, 14, 17, 1, 2, 14, 26 + 1, 2, 17, 0, 25, 28, 30, 39, + 47, 3, 45, 4, 47, 5, 6, 44, + 7, 8, 9, 43, 10, 11, 12, 42, + 13, 14, 15, 41, 16, 47, 18, 19, + 24, 19, 24, 2, 20, 4, 47, 21, + 22, 23, 22, 23, 2, 4, 47, 26, + 27, 40, 29, 38, 2, 17, 4, 30, + 47, 31, 32, 37, 32, 37, 33, 34, + 35, 36, 35, 36, 2, 17, 4, 47, + 38, 45, 1, 2, 17, 25, 28, 30, + 48, 39, 47, 1, 2, 17, 25, 28, + 30, 39, 47, 2, 17, 25, 28, 30, + 47 }; static const char _deserialize_text_glyphs_trans_actions[] = { - 1, 0, 1, 1, 1, 1, 0, 0, - 1, 1, 1, 0, 0, 1, 1, 1, - 1, 1, 0, 0, 2, 1, 1, 1, - 0, 0, 0, 4, 3, 5, 5, 5, - 5, 4, 6, 7, 7, 7, 7, 0, - 6, 8, 8, 0, 0, 0, 9, 10, - 10, 9, 11, 12, 11, 13, 14, 14, - 14, 13, 15, 16, 16, 15, 0 + 0, 1, 1, 0, 1, 1, 1, 0, + 1, 2, 2, 3, 3, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2, 2, + 2, 0, 0, 4, 4, 4, 4, 2, + 2, 2, 0, 0, 5, 5, 5, 0, + 0, 0, 2, 2, 6, 6, 6, 6, + 6, 2, 2, 2, 0, 0, 7, 2, + 2, 2, 0, 0, 8, 8, 8, 8, + 0, 0, 9, 10, 10, 10, 10, 10, + 9, 9, 10, 12, 13, 13, 13, 13, + 13, 12, 13, 14, 14, 14, 14, 14, + 14 }; static const char _deserialize_text_glyphs_eof_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 3, 6, - 8, 0, 8, 9, 11, 11, 9, 13, - 15, 15, 13 + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 11, + 0 }; -static const int deserialize_text_glyphs_start = 14; -static const int deserialize_text_glyphs_first_final = 14; +static const int deserialize_text_glyphs_start = 46; +static const int deserialize_text_glyphs_first_final = 46; static const int deserialize_text_glyphs_error = 0; -static const int deserialize_text_glyphs_en_main = 14; +static const int deserialize_text_glyphs_en_main = 46; -#line 98 "hb-buffer-deserialize-text-glyphs.rl" +#line 101 "hb-buffer-deserialize-text-glyphs.rl" static hb_bool_t @@ -322,39 +379,22 @@ _hb_buffer_deserialize_text_glyphs (hb_buffer_t *buffer, const char **end_ptr, hb_font_t *font) { - const char *p = buf, *pe = buf + buf_len, *eof = pe, *orig_pe = pe; + const char *p = buf, *pe = buf + buf_len, *eof = pe; /* Ensure we have positions. */ (void) hb_buffer_get_glyph_positions (buffer, nullptr); - while (p < pe && ISSPACE (*p)) - p++; - if (p < pe && *p == (buffer->len ? '|' : '[')) - *end_ptr = ++p; - - const char *end = strchr ((char *) p, ']'); - if (end) - pe = eof = end; - else - { - end = strrchr ((char *) p, '|'); - if (end) - pe = eof = end; - else - pe = eof = p; - } - const char *tok = nullptr; int cs; hb_glyph_info_t info = {0}; hb_glyph_position_t pos = {0}; -#line 353 "hb-buffer-deserialize-text-glyphs.hh" +#line 386 "hb-buffer-deserialize-text-glyphs.hh" { cs = deserialize_text_glyphs_start; } -#line 358 "hb-buffer-deserialize-text-glyphs.hh" +#line 389 "hb-buffer-deserialize-text-glyphs.hh" { int _slen; int _trans; @@ -379,14 +419,14 @@ _resume: goto _again; switch ( _deserialize_text_glyphs_trans_actions[_trans] ) { - case 1: -#line 51 "hb-buffer-deserialize-text-glyphs.rl" + case 2: +#line 50 "hb-buffer-deserialize-text-glyphs.rl" { tok = p; } break; - case 7: -#line 55 "hb-buffer-deserialize-text-glyphs.rl" + case 1: +#line 54 "hb-buffer-deserialize-text-glyphs.rl" { /* TODO Unescape delimiters. */ if (!hb_font_glyph_from_string (font, @@ -395,162 +435,124 @@ _resume: return false; } break; - case 14: -#line 63 "hb-buffer-deserialize-text-glyphs.rl" + case 6: +#line 62 "hb-buffer-deserialize-text-glyphs.rl" { if (!parse_uint (tok, p, &info.cluster )) return false; } break; - case 2: -#line 64 "hb-buffer-deserialize-text-glyphs.rl" + case 7: +#line 63 "hb-buffer-deserialize-text-glyphs.rl" { if (!parse_int (tok, p, &pos.x_offset )) return false; } break; - case 16: -#line 65 "hb-buffer-deserialize-text-glyphs.rl" + case 8: +#line 64 "hb-buffer-deserialize-text-glyphs.rl" { if (!parse_int (tok, p, &pos.y_offset )) return false; } break; - case 10: -#line 66 "hb-buffer-deserialize-text-glyphs.rl" + case 4: +#line 65 "hb-buffer-deserialize-text-glyphs.rl" { if (!parse_int (tok, p, &pos.x_advance)) return false; } break; - case 12: -#line 67 "hb-buffer-deserialize-text-glyphs.rl" + case 5: +#line 66 "hb-buffer-deserialize-text-glyphs.rl" { if (!parse_int (tok, p, &pos.y_advance)) return false; } break; - case 4: + case 3: +#line 67 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_uint (tok, p, &info.mask )) return false; } + break; + case 9: #line 38 "hb-buffer-deserialize-text-glyphs.rl" { hb_memset (&info, 0, sizeof (info)); hb_memset (&pos , 0, sizeof (pos )); } -#line 51 "hb-buffer-deserialize-text-glyphs.rl" +#line 50 "hb-buffer-deserialize-text-glyphs.rl" { tok = p; } break; - case 6: -#line 55 "hb-buffer-deserialize-text-glyphs.rl" + case 10: +#line 38 "hb-buffer-deserialize-text-glyphs.rl" { - /* TODO Unescape delimiters. */ - if (!hb_font_glyph_from_string (font, - tok, p - tok, - &info.codepoint)) - return false; + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); } -#line 43 "hb-buffer-deserialize-text-glyphs.rl" +#line 50 "hb-buffer-deserialize-text-glyphs.rl" { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; + tok = p; } - break; - case 13: -#line 63 "hb-buffer-deserialize-text-glyphs.rl" - { if (!parse_uint (tok, p, &info.cluster )) return false; } -#line 43 "hb-buffer-deserialize-text-glyphs.rl" +#line 54 "hb-buffer-deserialize-text-glyphs.rl" { - buffer->add_info (info); - if (unlikely (!buffer->successful)) + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; } break; - case 15: -#line 65 "hb-buffer-deserialize-text-glyphs.rl" - { if (!parse_int (tok, p, &pos.y_offset )) return false; } + case 12: #line 43 "hb-buffer-deserialize-text-glyphs.rl" { - buffer->add_info (info); + buffer->add_info_and_pos (info, pos); if (unlikely (!buffer->successful)) return false; - buffer->pos[buffer->len - 1] = pos; *end_ptr = p; } - break; - case 9: -#line 66 "hb-buffer-deserialize-text-glyphs.rl" - { if (!parse_int (tok, p, &pos.x_advance)) return false; } -#line 43 "hb-buffer-deserialize-text-glyphs.rl" +#line 38 "hb-buffer-deserialize-text-glyphs.rl" { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); } - break; - case 11: -#line 67 "hb-buffer-deserialize-text-glyphs.rl" - { if (!parse_int (tok, p, &pos.y_advance)) return false; } -#line 43 "hb-buffer-deserialize-text-glyphs.rl" +#line 50 "hb-buffer-deserialize-text-glyphs.rl" { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; + tok = p; } break; - case 8: -#line 68 "hb-buffer-deserialize-text-glyphs.rl" - { if (!parse_uint (tok, p, &info.mask )) return false; } -#line 43 "hb-buffer-deserialize-text-glyphs.rl" + case 14: +#line 54 "hb-buffer-deserialize-text-glyphs.rl" { - buffer->add_info (info); - if (unlikely (!buffer->successful)) + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; } - break; - case 5: #line 38 "hb-buffer-deserialize-text-glyphs.rl" { hb_memset (&info, 0, sizeof (info)); hb_memset (&pos , 0, sizeof (pos )); } -#line 51 "hb-buffer-deserialize-text-glyphs.rl" +#line 50 "hb-buffer-deserialize-text-glyphs.rl" { tok = p; } -#line 55 "hb-buffer-deserialize-text-glyphs.rl" + break; + case 13: +#line 43 "hb-buffer-deserialize-text-glyphs.rl" { - /* TODO Unescape delimiters. */ - if (!hb_font_glyph_from_string (font, - tok, p - tok, - &info.codepoint)) + buffer->add_info_and_pos (info, pos); + if (unlikely (!buffer->successful)) return false; + *end_ptr = p; } - break; - case 3: #line 38 "hb-buffer-deserialize-text-glyphs.rl" { hb_memset (&info, 0, sizeof (info)); hb_memset (&pos , 0, sizeof (pos )); } -#line 51 "hb-buffer-deserialize-text-glyphs.rl" +#line 50 "hb-buffer-deserialize-text-glyphs.rl" { tok = p; } -#line 55 "hb-buffer-deserialize-text-glyphs.rl" +#line 54 "hb-buffer-deserialize-text-glyphs.rl" { /* TODO Unescape delimiters. */ if (!hb_font_glyph_from_string (font, tok, p - tok, &info.codepoint)) return false; -} -#line 43 "hb-buffer-deserialize-text-glyphs.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; } break; -#line 554 "hb-buffer-deserialize-text-glyphs.hh" +#line 523 "hb-buffer-deserialize-text-glyphs.hh" } _again: @@ -562,127 +564,24 @@ _again: if ( p == eof ) { switch ( _deserialize_text_glyphs_eof_actions[cs] ) { - case 6: -#line 55 "hb-buffer-deserialize-text-glyphs.rl" - { - /* TODO Unescape delimiters. */ - if (!hb_font_glyph_from_string (font, - tok, p - tok, - &info.codepoint)) - return false; -} -#line 43 "hb-buffer-deserialize-text-glyphs.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 13: -#line 63 "hb-buffer-deserialize-text-glyphs.rl" - { if (!parse_uint (tok, p, &info.cluster )) return false; } -#line 43 "hb-buffer-deserialize-text-glyphs.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 15: -#line 65 "hb-buffer-deserialize-text-glyphs.rl" - { if (!parse_int (tok, p, &pos.y_offset )) return false; } -#line 43 "hb-buffer-deserialize-text-glyphs.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 9: -#line 66 "hb-buffer-deserialize-text-glyphs.rl" - { if (!parse_int (tok, p, &pos.x_advance)) return false; } -#line 43 "hb-buffer-deserialize-text-glyphs.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; case 11: -#line 67 "hb-buffer-deserialize-text-glyphs.rl" - { if (!parse_int (tok, p, &pos.y_advance)) return false; } -#line 43 "hb-buffer-deserialize-text-glyphs.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 8: -#line 68 "hb-buffer-deserialize-text-glyphs.rl" - { if (!parse_uint (tok, p, &info.mask )) return false; } -#line 43 "hb-buffer-deserialize-text-glyphs.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 3: -#line 38 "hb-buffer-deserialize-text-glyphs.rl" - { - hb_memset (&info, 0, sizeof (info)); - hb_memset (&pos , 0, sizeof (pos )); -} -#line 51 "hb-buffer-deserialize-text-glyphs.rl" - { - tok = p; -} -#line 55 "hb-buffer-deserialize-text-glyphs.rl" - { - /* TODO Unescape delimiters. */ - if (!hb_font_glyph_from_string (font, - tok, p - tok, - &info.codepoint)) - return false; -} #line 43 "hb-buffer-deserialize-text-glyphs.rl" { - buffer->add_info (info); + buffer->add_info_and_pos (info, pos); if (unlikely (!buffer->successful)) return false; - buffer->pos[buffer->len - 1] = pos; *end_ptr = p; } break; -#line 671 "hb-buffer-deserialize-text-glyphs.hh" +#line 542 "hb-buffer-deserialize-text-glyphs.hh" } } _out: {} } -#line 136 "hb-buffer-deserialize-text-glyphs.rl" - +#line 122 "hb-buffer-deserialize-text-glyphs.rl" - if (pe < orig_pe && *pe == ']') - { - pe++; - if (p == pe) - p++; - } *end_ptr = p; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text-unicode.hh b/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text-unicode.hh index 6a1706ccb725f..9e21beebd3483 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text-unicode.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text-unicode.hh @@ -34,135 +34,106 @@ #line 36 "hb-buffer-deserialize-text-unicode.hh" static const unsigned char _deserialize_text_unicode_trans_keys[] = { - 0u, 0u, 9u, 117u, 43u, 102u, 48u, 102u, 48u, 57u, 9u, 124u, 9u, 124u, 9u, 124u, - 9u, 124u, 0 + 0u, 0u, 43u, 102u, 48u, 102u, 48u, 124u, 48u, 57u, 62u, 124u, 48u, 124u, 60u, 117u, + 85u, 117u, 85u, 117u, 0 }; static const char _deserialize_text_unicode_key_spans[] = { - 0, 109, 60, 55, 10, 116, 116, 116, - 116 + 0, 60, 55, 77, 10, 63, 77, 58, + 33, 33 }; static const short _deserialize_text_unicode_index_offsets[] = { - 0, 0, 110, 171, 227, 238, 355, 472, - 589 + 0, 0, 61, 117, 195, 206, 270, 348, + 407, 441 }; static const char _deserialize_text_unicode_indicies[] = { - 0, 0, 0, 0, 0, 1, 1, + 0, 1, 1, 1, 1, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 2, + 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, + 2, 2, 2, 2, 2, 1, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 2, + 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, + 2, 2, 2, 2, 2, 1, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 1, 1, 1, 4, 5, 1, 1, 3, + 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3, + 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 5, 1, 6, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 1, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 2, 1, 3, - 1, 1, 1, 1, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 1, 1, - 1, 1, 1, 1, 1, 4, 4, 4, - 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 4, 4, 4, - 4, 4, 4, 1, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 1, 1, - 1, 1, 1, 1, 1, 4, 4, 4, - 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 4, 4, 4, - 4, 4, 4, 1, 5, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 1, 7, - 7, 7, 7, 7, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 7, 1, + 1, 1, 1, 1, 1, 8, 1, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 1, 1, 1, 1, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, - 1, 1, 1, 9, 1, 1, 1, 8, - 8, 8, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 8, - 8, 8, 8, 8, 8, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 10, 1, 11, 11, 11, 11, - 11, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 11, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 0, - 1, 12, 12, 12, 12, 12, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 12, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 8, 1, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 13, 1, 12, 12, - 12, 12, 12, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 12, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 14, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 11, 1, + 11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 11, 1, 12, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 13, 1, 0 + 1, 1, 12, 1, 0 }; static const char _deserialize_text_unicode_trans_targs[] = { - 1, 0, 2, 3, 5, 7, 8, 6, - 5, 4, 1, 6, 6, 1, 8 + 2, 0, 3, 3, 4, 9, 5, 6, + 9, 6, 8, 1, 1 }; static const char _deserialize_text_unicode_trans_actions[] = { - 0, 0, 1, 0, 2, 2, 2, 3, - 0, 4, 3, 0, 5, 5, 0 + 0, 0, 1, 0, 2, 2, 1, 1, + 3, 0, 0, 4, 6 }; static const char _deserialize_text_unicode_eof_actions[] = { - 0, 0, 0, 0, 0, 3, 0, 5, - 5 + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5 }; -static const int deserialize_text_unicode_start = 1; -static const int deserialize_text_unicode_first_final = 5; +static const int deserialize_text_unicode_start = 7; +static const int deserialize_text_unicode_first_final = 7; static const int deserialize_text_unicode_error = 0; -static const int deserialize_text_unicode_en_main = 1; +static const int deserialize_text_unicode_en_main = 7; -#line 79 "hb-buffer-deserialize-text-unicode.rl" +#line 80 "hb-buffer-deserialize-text-unicode.rl" static hb_bool_t @@ -172,37 +143,19 @@ _hb_buffer_deserialize_text_unicode (hb_buffer_t *buffer, const char **end_ptr, hb_font_t *font) { - const char *p = buf, *pe = buf + buf_len, *eof = pe, *orig_pe = pe; - - while (p < pe && ISSPACE (*p)) - p++; - if (p < pe && *p == (buffer->len ? '|' : '<')) - *end_ptr = ++p; - - const char *end = strchr ((char *) p, '>'); - if (end) - pe = eof = end; - else - { - end = strrchr ((char *) p, '|'); - if (end) - pe = eof = end; - else - pe = eof = p; - } - + const char *p = buf, *pe = buf + buf_len, *eof = pe; const char *tok = nullptr; int cs; hb_glyph_info_t info = {0}; const hb_glyph_position_t pos = {0}; -#line 201 "hb-buffer-deserialize-text-unicode.hh" +#line 154 "hb-buffer-deserialize-text-unicode.hh" { cs = deserialize_text_unicode_start; } -#line 206 "hb-buffer-deserialize-text-unicode.hh" +#line 159 "hb-buffer-deserialize-text-unicode.hh" { int _slen; int _trans; @@ -227,38 +180,27 @@ _resume: goto _again; switch ( _deserialize_text_unicode_trans_actions[_trans] ) { - case 1: + case 4: #line 38 "hb-buffer-deserialize-text-unicode.rl" { hb_memset (&info, 0, sizeof (info)); } break; - case 2: + case 1: #line 51 "hb-buffer-deserialize-text-unicode.rl" { tok = p; } break; - case 4: + case 2: #line 55 "hb-buffer-deserialize-text-unicode.rl" {if (!parse_hex (tok, p, &info.codepoint )) return false; } break; case 3: -#line 55 "hb-buffer-deserialize-text-unicode.rl" - {if (!parse_hex (tok, p, &info.codepoint )) return false; } -#line 42 "hb-buffer-deserialize-text-unicode.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - if (buffer->have_positions) - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 5: #line 57 "hb-buffer-deserialize-text-unicode.rl" { if (!parse_uint (tok, p, &info.cluster )) return false; } + break; + case 6: #line 42 "hb-buffer-deserialize-text-unicode.rl" { buffer->add_info (info); @@ -267,9 +209,13 @@ _resume: if (buffer->have_positions) buffer->pos[buffer->len - 1] = pos; *end_ptr = p; +} +#line 38 "hb-buffer-deserialize-text-unicode.rl" + { + hb_memset (&info, 0, sizeof (info)); } break; -#line 273 "hb-buffer-deserialize-text-unicode.hh" +#line 219 "hb-buffer-deserialize-text-unicode.hh" } _again: @@ -281,22 +227,7 @@ _again: if ( p == eof ) { switch ( _deserialize_text_unicode_eof_actions[cs] ) { - case 3: -#line 55 "hb-buffer-deserialize-text-unicode.rl" - {if (!parse_hex (tok, p, &info.codepoint )) return false; } -#line 42 "hb-buffer-deserialize-text-unicode.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - if (buffer->have_positions) - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; case 5: -#line 57 "hb-buffer-deserialize-text-unicode.rl" - { if (!parse_uint (tok, p, &info.cluster )) return false; } #line 42 "hb-buffer-deserialize-text-unicode.rl" { buffer->add_info (info); @@ -307,22 +238,15 @@ _again: *end_ptr = p; } break; -#line 311 "hb-buffer-deserialize-text-unicode.hh" +#line 242 "hb-buffer-deserialize-text-unicode.hh" } } _out: {} } -#line 115 "hb-buffer-deserialize-text-unicode.rl" - +#line 98 "hb-buffer-deserialize-text-unicode.rl" - if (pe < orig_pe && *pe == '>') - { - pe++; - if (p == pe) - p++; - } *end_ptr = p; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer-serialize.cc b/src/java.desktop/share/native/libharfbuzz/hb-buffer-serialize.cc index bb2cddcb65526..763172818c4e2 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer-serialize.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer-serialize.cc @@ -169,11 +169,13 @@ _hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer, if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS) { hb_glyph_extents_t extents; - hb_font_get_glyph_extents(font, info[i].codepoint, &extents); - p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"xb\":%d,\"yb\":%d", - extents.x_bearing, extents.y_bearing)); - p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"w\":%d,\"h\":%d", - extents.width, extents.height)); + if (hb_font_get_glyph_extents(font, info[i].codepoint, &extents)) + { + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"xb\":%d,\"yb\":%d", + extents.x_bearing, extents.y_bearing)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"w\":%d,\"h\":%d", + extents.width, extents.height)); + } } *p++ = '}'; @@ -318,8 +320,8 @@ _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer, if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS) { hb_glyph_extents_t extents; - hb_font_get_glyph_extents(font, info[i].codepoint, &extents); - p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "<%d,%d,%d,%d>", extents.x_bearing, extents.y_bearing, extents.width, extents.height)); + if (hb_font_get_glyph_extents(font, info[i].codepoint, &extents)) + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "<%d,%d,%d,%d>", extents.x_bearing, extents.y_bearing, extents.width, extents.height)); } if (i == end-1) { @@ -737,8 +739,7 @@ parse_hex (const char *pp, const char *end, uint32_t *pv) * Deserializes glyphs @buffer from textual representation in the format * produced by hb_buffer_serialize_glyphs(). * - * Return value: `true` if parse was successful, `false` if an error - * occurred. + * Return value: `true` if the full string was parsed, `false` otherwise. * * Since: 0.9.7 **/ @@ -810,8 +811,7 @@ hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, * Deserializes Unicode @buffer from textual representation in the format * produced by hb_buffer_serialize_unicode(). * - * Return value: `true` if parse was successful, `false` if an error - * occurred. + * Return value: `true` if the full string was parsed, `false` otherwise. * * Since: 2.7.3 **/ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer-verify.cc b/src/java.desktop/share/native/libharfbuzz/hb-buffer-verify.cc index 3bdea30ed36c7..56299d9b7e046 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer-verify.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer-verify.cc @@ -63,23 +63,24 @@ static bool buffer_verify_monotone (hb_buffer_t *buffer, hb_font_t *font) { - /* Check that clusters are monotone. */ - if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES || - buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) + if (!HB_BUFFER_CLUSTER_LEVEL_IS_MONOTONE (buffer->cluster_level)) { - bool is_forward = HB_DIRECTION_IS_FORWARD (hb_buffer_get_direction (buffer)); + /* Cannot perform this check without monotone clusters. */ + return true; + } - unsigned int num_glyphs; - hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, &num_glyphs); + bool is_forward = HB_DIRECTION_IS_FORWARD (hb_buffer_get_direction (buffer)); - for (unsigned int i = 1; i < num_glyphs; i++) - if (info[i-1].cluster != info[i].cluster && - (info[i-1].cluster < info[i].cluster) != is_forward) - { - buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "clusters are not monotone."); - return false; - } - } + unsigned int num_glyphs; + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, &num_glyphs); + + for (unsigned int i = 1; i < num_glyphs; i++) + if (info[i-1].cluster != info[i].cluster && + (info[i-1].cluster < info[i].cluster) != is_forward) + { + buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "clusters are not monotone."); + return false; + } return true; } @@ -92,8 +93,7 @@ buffer_verify_unsafe_to_break (hb_buffer_t *buffer, unsigned int num_features, const char * const *shapers) { - if (buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES && - buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) + if (!HB_BUFFER_CLUSTER_LEVEL_IS_MONOTONE (buffer->cluster_level)) { /* Cannot perform this check without monotone clusters. */ return true; @@ -149,7 +149,7 @@ buffer_verify_unsafe_to_break (hb_buffer_t *buffer, } assert (text_start < text_end); - if (0) + if (false) printf("start %u end %u text start %u end %u\n", start, end, text_start, text_end); hb_buffer_clear_contents (fragment); @@ -207,8 +207,7 @@ buffer_verify_unsafe_to_concat (hb_buffer_t *buffer, unsigned int num_features, const char * const *shapers) { - if (buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES && - buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) + if (!HB_BUFFER_CLUSTER_LEVEL_IS_MONOTONE (buffer->cluster_level)) { /* Cannot perform this check without monotone clusters. */ return true; @@ -288,7 +287,7 @@ buffer_verify_unsafe_to_concat (hb_buffer_t *buffer, } assert (text_start < text_end); - if (0) + if (false) printf("start %u end %u text start %u end %u\n", start, end, text_start, text_end); #if 0 @@ -412,7 +411,7 @@ hb_buffer_t::verify (hb_buffer_t *text_buffer, &len, HB_BUFFER_SERIALIZE_FORMAT_TEXT, HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS); - buffer_verify_error (this, font, BUFFER_VERIFY_ERROR "text was: %s.", bytes.arrayZ); + buffer_verify_error (this, font, BUFFER_VERIFY_ERROR "text was: %s.", bytes.arrayZ ? bytes.arrayZ : ""); } #endif } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc b/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc index 5f9329e07edf7..c0600fd839b72 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc @@ -271,6 +271,7 @@ hb_buffer_t::similar (const hb_buffer_t &src) replacement = src.replacement; invisible = src.invisible; not_found = src.not_found; + not_found_variation_selector = src.not_found_variation_selector; } void @@ -283,6 +284,7 @@ hb_buffer_t::reset () replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT; invisible = 0; not_found = 0; + not_found_variation_selector = HB_CODEPOINT_INVALID; clear (); } @@ -309,6 +311,7 @@ hb_buffer_t::clear () deallocate_var_all (); serial = 0; + random_state = 1; scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT; } @@ -367,6 +370,18 @@ hb_buffer_t::add_info (const hb_glyph_info_t &glyph_info) len++; } +void +hb_buffer_t::add_info_and_pos (const hb_glyph_info_t &glyph_info, + const hb_glyph_position_t &glyph_pos) +{ + if (unlikely (!ensure (len + 1))) return; + + info[len] = glyph_info; + assert (have_positions); + pos[len] = glyph_pos; + + len++; +} void @@ -515,7 +530,7 @@ void hb_buffer_t::merge_clusters_impl (unsigned int start, unsigned int end) { - if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS) + if (!HB_BUFFER_CLUSTER_LEVEL_IS_MONOTONE (cluster_level)) { unsafe_to_break (start, end); return; @@ -548,7 +563,7 @@ void hb_buffer_t::merge_out_clusters (unsigned int start, unsigned int end) { - if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS) + if (!HB_BUFFER_CLUSTER_LEVEL_IS_MONOTONE (cluster_level)) return; if (unlikely (end - start < 2)) @@ -704,6 +719,7 @@ DEFINE_NULL_INSTANCE (hb_buffer_t) = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT, 0, /* invisible */ 0, /* not_found */ + HB_CODEPOINT_INVALID, /* not_found_variation_selector */ HB_BUFFER_CONTENT_TYPE_INVALID, @@ -1359,6 +1375,89 @@ hb_buffer_get_not_found_glyph (const hb_buffer_t *buffer) return buffer->not_found; } +/** + * hb_buffer_set_not_found_variation_selector_glyph: + * @buffer: An #hb_buffer_t + * @not_found_variation_selector: the not-found-variation-selector #hb_codepoint_t + * + * Sets the #hb_codepoint_t that replaces variation-selector characters not resolved + * in the font during shaping. + * + * The not-found-variation-selector glyph defaults to #HB_CODEPOINT_INVALID, + * in which case an unresolved variation-selector will be removed from the glyph + * string during shaping. This API allows for changing that and retaining a glyph, + * such that the situation can be detected by the client and handled accordingly + * (e.g. by using a different font). + * + * Since: 10.0.0 + **/ +void +hb_buffer_set_not_found_variation_selector_glyph (hb_buffer_t *buffer, + hb_codepoint_t not_found_variation_selector) +{ + buffer->not_found_variation_selector = not_found_variation_selector; +} + +/** + * hb_buffer_get_not_found_variation_selector_glyph: + * @buffer: An #hb_buffer_t + * + * See hb_buffer_set_not_found_variation_selector_glyph(). + * + * Return value: + * The @buffer not-found-variation-selector #hb_codepoint_t + * + * Since: 10.0.0 + **/ +hb_codepoint_t +hb_buffer_get_not_found_variation_selector_glyph (const hb_buffer_t *buffer) +{ + return buffer->not_found_variation_selector; +} + +/** + * hb_buffer_set_random_state: + * @buffer: An #hb_buffer_t + * @state: the new random state + * + * Sets the random state of the buffer. The state changes + * every time a glyph uses randomness (eg. the `rand` + * OpenType feature). This function together with + * hb_buffer_get_random_state() allow for transferring + * the current random state to a subsequent buffer, to + * get better randomness distribution. + * + * Defaults to 1 and when buffer contents are cleared. + * A value of 0 disables randomness during shaping. + * + * Since: 8.4.0 + **/ +void +hb_buffer_set_random_state (hb_buffer_t *buffer, + unsigned state) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->random_state = state; +} + +/** + * hb_buffer_get_random_state: + * @buffer: An #hb_buffer_t + * + * See hb_buffer_set_random_state(). + * + * Return value: + * The @buffer random state + * + * Since: 8.4.0 + **/ +unsigned +hb_buffer_get_random_state (const hb_buffer_t *buffer) +{ + return buffer->random_state; +} /** * hb_buffer_clear_contents: @@ -1896,7 +1995,7 @@ hb_buffer_add_codepoints (hb_buffer_t *buffer, * @buffer: An #hb_buffer_t * @source: source #hb_buffer_t * @start: start index into source buffer to copy. Use 0 to copy from start of buffer. - * @end: end index into source buffer to copy. Use @HB_FEATURE_GLOBAL_END to copy to end of buffer. + * @end: end index into source buffer to copy. Use @UINT_MAX (or ((unsigned int) -1)) to copy to end of buffer. * * Append (part of) contents of another buffer to this buffer. * diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer.h b/src/java.desktop/share/native/libharfbuzz/hb-buffer.h index 6fc215d162779..332001ef1712f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer.h @@ -422,6 +422,7 @@ hb_buffer_get_flags (const hb_buffer_t *buffer); * @HB_BUFFER_CLUSTER_LEVEL_CHARACTERS: Don't group cluster values. * @HB_BUFFER_CLUSTER_LEVEL_DEFAULT: Default cluster level, * equal to @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES. + * @HB_BUFFER_CLUSTER_LEVEL_GRAPHEMES: Only group clusters, but don't enforce monotone order. * * Data type for holding HarfBuzz's clustering behavior options. The cluster level * dictates one aspect of how HarfBuzz will treat non-base characters @@ -429,11 +430,26 @@ hb_buffer_get_flags (const hb_buffer_t *buffer); * * In @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES, non-base * characters are merged into the cluster of the base character that precedes them. + * There is also cluster merging every time the clusters will otherwise become non-monotone. * * In @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS, non-base characters are initially * assigned their own cluster values, which are not merged into preceding base * clusters. This allows HarfBuzz to perform additional operations like reorder - * sequences of adjacent marks. + * sequences of adjacent marks. The output is still monotone, but the cluster + * values are more granular. + * + * In @HB_BUFFER_CLUSTER_LEVEL_CHARACTERS, non-base characters are assigned their + * own cluster values, which are not merged into preceding base clusters. Moreover, + * the cluster values are not merged into monotone order. This is the most granular + * cluster level, and it is useful for clients that need to know the exact cluster + * values of each character, but is harder to use for clients, since clusters + * might appear in any order. + * + * In @HB_BUFFER_CLUSTER_LEVEL_GRAPHEMES, non-base characters are merged into the + * cluster of the base character that precedes them. This is similar to the Unicode + * Grapheme Cluster algorithm, but it is not exactly the same. The output is + * not forced to be monotone. This is useful for clients that want to use HarfBuzz + * as a cheap implementation of the Unicode Grapheme Cluster algorithm. * * @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES is the default, because it maintains * backward compatibility with older versions of HarfBuzz. New client programs that @@ -446,9 +462,52 @@ typedef enum { HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES = 0, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS = 1, HB_BUFFER_CLUSTER_LEVEL_CHARACTERS = 2, + HB_BUFFER_CLUSTER_LEVEL_GRAPHEMES = 3, HB_BUFFER_CLUSTER_LEVEL_DEFAULT = HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES } hb_buffer_cluster_level_t; +/** + * HB_BUFFER_CLUSTER_LEVEL_IS_MONOTONE: + * @level: #hb_buffer_cluster_level_t to test + * + * Tests whether a cluster level groups cluster values into monotone order. + * Requires that the level be valid. + * + * Since: 11.0.0 + */ +#define HB_BUFFER_CLUSTER_LEVEL_IS_MONOTONE(level) \ + ((bool) ((1u << (unsigned) (level)) & \ + ((1u << HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) | \ + (1u << HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS)))) + +/** + * HB_BUFFER_CLUSTER_LEVEL_IS_GRAPHEMES: + * @level: #hb_buffer_cluster_level_t to test + * + * Tests whether a cluster level groups cluster values by graphemes. Requires + * that the level be valid. + * + * Since: 11.0.0 + */ +#define HB_BUFFER_CLUSTER_LEVEL_IS_GRAPHEMES(level) \ + ((bool) ((1u << (unsigned) (level)) & \ + ((1u << HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) | \ + (1u << HB_BUFFER_CLUSTER_LEVEL_GRAPHEMES)))) + +/** + * HB_BUFFER_CLUSTER_LEVEL_IS_CHARACTERS + * @level: #hb_buffer_cluster_level_t to test + * + * Tests whether a cluster level does not group cluster values by graphemes. + * Requires that the level be valid. + * + * Since: 11.0.0 + */ +#define HB_BUFFER_CLUSTER_LEVEL_IS_CHARACTERS(level) \ + ((bool) ((1u << (unsigned) (level)) & \ + ((1u << HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARCATERS) | \ + (1u << HB_BUFFER_CLUSTER_LEVEL_CHARACTERS)))) + HB_EXTERN void hb_buffer_set_cluster_level (hb_buffer_t *buffer, hb_buffer_cluster_level_t cluster_level); @@ -487,6 +546,19 @@ hb_buffer_set_not_found_glyph (hb_buffer_t *buffer, HB_EXTERN hb_codepoint_t hb_buffer_get_not_found_glyph (const hb_buffer_t *buffer); +HB_EXTERN void +hb_buffer_set_not_found_variation_selector_glyph (hb_buffer_t *buffer, + hb_codepoint_t not_found_variation_selector); + +HB_EXTERN hb_codepoint_t +hb_buffer_get_not_found_variation_selector_glyph (const hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_set_random_state (hb_buffer_t *buffer, + unsigned state); + +HB_EXTERN unsigned +hb_buffer_get_random_state (const hb_buffer_t *buffer); /* * Content API. diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh b/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh index 7f8ce14e90d58..81c0f4a72e32a 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh @@ -32,7 +32,6 @@ #include "hb.hh" #include "hb-unicode.hh" -#include "hb-set-digest.hh" static_assert ((sizeof (hb_glyph_info_t) == 20), ""); @@ -52,6 +51,7 @@ enum hb_buffer_scratch_flags_t { HB_BUFFER_SCRATCH_FLAG_HAS_CGJ = 0x00000010u, HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS = 0x00000020u, HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE = 0x00000040u, + HB_BUFFER_SCRATCH_FLAG_HAS_VARIATION_SELECTOR_FALLBACK= 0x00000080u, /* Reserved for shapers' internal use. */ HB_BUFFER_SCRATCH_FLAG_SHAPER0 = 0x01000000u, @@ -80,6 +80,7 @@ struct hb_buffer_t hb_codepoint_t replacement; /* U+FFFD or something else. */ hb_codepoint_t invisible; /* 0 or something else. */ hb_codepoint_t not_found; /* 0 or something else. */ + hb_codepoint_t not_found_variation_selector; /* HB_CODEPOINT_INVALID or something else. */ /* * Buffer contents @@ -116,6 +117,7 @@ struct hb_buffer_t uint8_t allocated_var_bits; uint8_t serial; + uint32_t random_state; hb_buffer_scratch_flags_t scratch_flags; /* Have space-fallback, etc. */ unsigned int max_len; /* Maximum allowed len. */ int max_ops; /* Maximum allowed operations. */ @@ -179,22 +181,24 @@ struct hb_buffer_t allocated_var_bits = 0; } + HB_ALWAYS_INLINE hb_glyph_info_t &cur (unsigned int i = 0) { return info[idx + i]; } + HB_ALWAYS_INLINE hb_glyph_info_t cur (unsigned int i = 0) const { return info[idx + i]; } + HB_ALWAYS_INLINE hb_glyph_position_t &cur_pos (unsigned int i = 0) { return pos[idx + i]; } + HB_ALWAYS_INLINE hb_glyph_position_t cur_pos (unsigned int i = 0) const { return pos[idx + i]; } + HB_ALWAYS_INLINE hb_glyph_info_t &prev () { return out_info[out_len ? out_len - 1 : 0]; } + HB_ALWAYS_INLINE hb_glyph_info_t prev () const { return out_info[out_len ? out_len - 1 : 0]; } - hb_set_digest_t digest () const - { - hb_set_digest_t d; - d.init (); - d.add_array (&info[0].codepoint, len, sizeof (info[0])); - return d; - } + template + void collect_codepoints (set_t &d) const + { d.clear (); d.add_array (&info[0].codepoint, len, sizeof (info[0])); } HB_INTERNAL void similar (const hb_buffer_t &src); HB_INTERNAL void reset (); @@ -225,6 +229,8 @@ struct hb_buffer_t HB_INTERNAL void add (hb_codepoint_t codepoint, unsigned int cluster); HB_INTERNAL void add_info (const hb_glyph_info_t &glyph_info); + HB_INTERNAL void add_info_and_pos (const hb_glyph_info_t &glyph_info, + const hb_glyph_position_t &glyph_pos); void reverse_range (unsigned start, unsigned end) { @@ -406,6 +412,9 @@ struct hb_buffer_t { end = hb_min (end, len); + if (unlikely (end - start > 255)) + return; + if (interior && !from_out_buffer && end - start < 2) return; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cache.hh b/src/java.desktop/share/native/libharfbuzz/hb-cache.hh index 8c7e0c655e6f5..7d09ca89d7f93 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-cache.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-cache.hh @@ -30,18 +30,31 @@ #include "hb.hh" -/* Implements a lockfree cache for int->int functions. +/* Implements a lockfree and thread-safe cache for int->int functions, + * using (optionally) _relaxed_ atomic integer operations. * - * The cache is a fixed-size array of 16-bit or 32-bit integers. - * The key is split into two parts: the cache index and the rest. + * The cache is a fixed-size array of 16-bit or 32-bit integers, + * typically 256 elements. * - * The cache index is used to index into the array. The rest is used - * to store the key and the value. + * The key is split into two parts: the cache index (high bits) + * and the rest (low bits). + * + * The cache index is used to index into the array. The array + * member is a 16-bit or 32-bit integer that is used *both* + * to store the low bits of the key, and the value. * * The value is stored in the least significant bits of the integer. - * The key is stored in the most significant bits of the integer. - * The key is shifted by cache_bits to the left to make room for the - * value. + * The low bits of the key are stored in the most significant bits + * of the integer. + * + * A cache hit is detected by comparing the low bits of the key + * with the high bits of the integer at the array position indexed + * by the high bits of the key. If they match, the value is extracted + * from the least significant bits of the integer and returned. + * Otherwise, a cache miss is reported. + * + * Cache operations (storage and retrieval) involve just a few + * arithmetic operations and a single memory access. */ template ::type, + hb_atomic_t, + hb_atomic_t>::type, typename std::conditional::type + unsigned short, + unsigned int>::type >::type; static_assert ((key_bits >= cache_bits), ""); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-common.hh index 8d9ff5faf6d7e..79d7c4c7133bf 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-common.hh @@ -522,7 +522,7 @@ struct parsed_values_t void alloc (unsigned n) { - values.alloc (n, true); + values.alloc_exact (n); } void add_op (op_code_t op, const byte_str_ref_t& str_ref = byte_str_ref_t (), const VAL &v = VAL ()) @@ -624,7 +624,6 @@ struct opset_t } else { /* invalid unknown operator */ env.clear_args (); - env.set_error (); } break; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-dict-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-dict-common.hh index 0c0cec9986a84..6a227e5e81eed 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-dict-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-dict-common.hh @@ -54,8 +54,8 @@ struct top_dict_values_t : dict_values_t } void fini () { dict_values_t::fini (); } - unsigned int charStringsOffset; - unsigned int FDArrayOffset; + int charStringsOffset; + int FDArrayOffset; }; struct dict_opset_t : opset_t @@ -84,7 +84,7 @@ struct dict_opset_t : opset_t enum Nibble { DECIMAL=10, EXP_POS, EXP_NEG, RESERVED, NEG, END }; - char buf[32]; + char buf[32] = {0}; unsigned char byte = 0; for (unsigned i = 0, count = 0; count < ARRAY_LENGTH (buf); ++i, ++count) { @@ -157,11 +157,11 @@ struct top_dict_opset_t : dict_opset_t { switch (op) { case OpCode_CharStrings: - dictval.charStringsOffset = env.argStack.pop_uint (); + dictval.charStringsOffset = env.argStack.pop_int (); env.clear_args (); break; case OpCode_FDArray: - dictval.FDArrayOffset = env.argStack.pop_uint (); + dictval.FDArrayOffset = env.argStack.pop_int (); env.clear_args (); break; case OpCode_FontMatrix: diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cff2-interp-cs.hh b/src/java.desktop/share/native/libharfbuzz/hb-cff2-interp-cs.hh index c970633e06416..0eacdd92ab8d2 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-cff2-interp-cs.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-cff2-interp-cs.hh @@ -71,22 +71,49 @@ struct cff2_cs_interp_env_t : cs_interp_env_t template cff2_cs_interp_env_t (const hb_ubytes_t &str, ACC &acc, unsigned int fd, const int *coords_=nullptr, unsigned int num_coords_=0) - : SUPER (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs) + : SUPER (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs), + cached_scalars_vector (&acc.cached_scalars_vector) { coords = coords_; num_coords = num_coords_; varStore = acc.varStore; - seen_blend = false; - seen_vsindex_ = false; - scalars.init (); do_blend = num_coords && coords && varStore->size; set_ivs (acc.privateDicts[fd].ivs); } - void fini () + ~cff2_cs_interp_env_t () { - scalars.fini (); - SUPER::fini (); + release_scalars_vector (scalars); + } + + hb_vector_t *acquire_scalars_vector () const + { + hb_vector_t *scalars = cached_scalars_vector->get_acquire (); + + if (!scalars || !cached_scalars_vector->cmpexch (scalars, nullptr)) + { + scalars = (hb_vector_t *) hb_calloc (1, sizeof (hb_vector_t)); + if (unlikely (!scalars)) + return nullptr; + scalars->init (); + } + + return scalars; + } + + void release_scalars_vector (hb_vector_t *scalars) const + { + if (!scalars) + return; + + scalars->clear (); + + if (!cached_scalars_vector->cmpexch (nullptr, scalars)) + { + scalars->fini (); + hb_free (scalars); + } + scalars = nullptr; } op_code_t fetch_op () @@ -115,14 +142,20 @@ struct cff2_cs_interp_env_t : cs_interp_env_t { if (!seen_blend) { - region_count = varStore->varStore.get_region_index_count (get_ivs ()); - if (do_blend) + scalars = acquire_scalars_vector (); + if (unlikely (!scalars)) + SUPER::set_error (); + else { - if (unlikely (!scalars.resize_exact (region_count))) - SUPER::set_error (); - else - varStore->varStore.get_region_scalars (get_ivs (), coords, num_coords, - &scalars[0], region_count); + region_count = varStore->varStore.get_region_index_count (get_ivs ()); + if (do_blend) + { + if (unlikely (!scalars->resize_exact (region_count))) + SUPER::set_error (); + else + varStore->varStore.get_region_scalars (get_ivs (), coords, num_coords, + &(*scalars)[0], region_count); + } } seen_blend = true; } @@ -153,11 +186,11 @@ struct cff2_cs_interp_env_t : cs_interp_env_t double v = 0; if (do_blend) { - if (likely (scalars.length == deltas.length)) + if (likely (scalars && scalars->length == deltas.length)) { - unsigned count = scalars.length; + unsigned count = scalars->length; for (unsigned i = 0; i < count; i++) - v += (double) scalars.arrayZ[i] * deltas.arrayZ[i].to_real (); + v += (double) scalars->arrayZ[i] * deltas.arrayZ[i].to_real (); } } return v; @@ -168,13 +201,14 @@ struct cff2_cs_interp_env_t : cs_interp_env_t protected: const int *coords; unsigned int num_coords; - const CFF2VariationStore *varStore; + const CFF2ItemVariationStore *varStore; unsigned int region_count; unsigned int ivs; - hb_vector_t scalars; + hb_vector_t *scalars = nullptr; + hb_atomic_t *> *cached_scalars_vector = nullptr; bool do_blend; - bool seen_vsindex_; - bool seen_blend; + bool seen_vsindex_ = false; + bool seen_blend = false; typedef cs_interp_env_t SUPER; }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-common.cc b/src/java.desktop/share/native/libharfbuzz/hb-common.cc index 3afab4284e68a..5c8546a378c04 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-common.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-common.cc @@ -42,7 +42,7 @@ /* hb_options_t */ -hb_atomic_int_t _hb_options; +hb_atomic_t _hb_options; void _hb_options_init () @@ -273,7 +273,7 @@ struct hb_language_item_t { /* Thread-safe lockfree language list */ -static hb_atomic_ptr_t langs; +static hb_atomic_t langs; static inline void free_langs () @@ -403,7 +403,7 @@ hb_language_to_string (hb_language_t language) hb_language_t hb_language_get_default () { - static hb_atomic_ptr_t default_language; + static hb_atomic_t default_language; hb_language_t language = default_language; if (unlikely (language == HB_LANGUAGE_INVALID)) @@ -625,6 +625,9 @@ hb_script_get_horizontal_direction (hb_script_t script) /* Unicode-14.0 additions */ case HB_SCRIPT_OLD_UYGHUR: + /* Unicode-16.0 additions */ + case HB_SCRIPT_GARAY: + return HB_DIRECTION_RTL; @@ -965,6 +968,9 @@ hb_feature_from_string (const char *str, int len, * understood by hb_feature_from_string(). The client in responsible for * allocating big enough size for @buf, 128 bytes is more than enough. * + * Note that the feature value will be omitted if it is '1', but the + * string won't include any whitespace. + * * Since: 0.9.5 **/ void @@ -996,7 +1002,7 @@ hb_feature_to_string (hb_feature_t *feature, if (feature->value > 1) { s[len++] = '='; - len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value)); + len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%" PRIu32, feature->value)); } assert (len < ARRAY_LENGTH (s)); len = hb_min (len, size - 1); @@ -1118,6 +1124,8 @@ get_C_locale () * understood by hb_variation_from_string(). The client in responsible for * allocating big enough size for @buf, 128 bytes is more than enough. * + * Note that the string won't include any whitespace. + * * Since: 1.4.2 */ void @@ -1209,6 +1217,58 @@ uint8_t return hb_color_get_blue (color); } +/** + * hb_malloc: + * @size: The size of the memory to allocate. + * + * Allocates @size bytes of memory, using the allocator set at + * compile-time. Typically just malloc(). + * + * Return value: A pointer to the allocated memory. + * + * Since: 11.0.0 + **/ +void* hb_malloc(size_t size) { return hb_malloc_impl (size); } + +/** + * hb_calloc: + * @nmemb: The number of elements to allocate. + * @size: The size of each element. + * + * Allocates @nmemb elements of @size bytes each, initialized to zero, + * using the allocator set at compile-time. Typically just calloc(). + * + * Return value: A pointer to the allocated memory. + * + * Since: 11.0.0 + **/ +void* hb_calloc(size_t nmemb, size_t size) { return hb_calloc_impl (nmemb, size); } + +/** + * hb_realloc: + * @ptr: The pointer to the memory to reallocate. + * @size: The new size of the memory. + * + * Reallocates the memory pointed to by @ptr to @size bytes, using the + * allocator set at compile-time. Typically just realloc(). + * + * Return value: A pointer to the reallocated memory. + * + * Since: 11.0.0 + **/ +void* hb_realloc(void *ptr, size_t size) { return hb_realloc_impl (ptr, size); } + +/** + * hb_free: + * @ptr: The pointer to the memory to free. + * + * Frees the memory pointed to by @ptr, using the allocator set at + * compile-time. Typically just free(). + * + * Since: 11.0.0 + **/ +void hb_free(void *ptr) { hb_free_impl (ptr); } + /* If there is no visibility control, then hb-static.cc will NOT * define anything. Instead, we get it to define one set in here diff --git a/src/java.desktop/share/native/libharfbuzz/hb-common.h b/src/java.desktop/share/native/libharfbuzz/hb-common.h index 0d7956764cff6..5a0ef65392d0b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-common.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-common.h @@ -47,14 +47,10 @@ # endif /* !__cplusplus */ #endif -#if defined (_SVR4) || defined (SVR4) || defined (__OpenBSD__) || \ - defined (_sgi) || defined (__sun) || defined (sun) || \ - defined (__digital__) || defined (__HP_cc) -# include -#elif defined (_AIX) +#if defined (_AIX) # include #elif defined (_MSC_VER) && _MSC_VER < 1600 -/* VS 2010 (_MSC_VER 1600) has stdint.h */ +/* VS 2010 (_MSC_VER 1600) has stdint.h */ typedef __int8 int8_t; typedef unsigned __int8 uint8_t; typedef __int16 int16_t; @@ -63,11 +59,13 @@ typedef __int32 int32_t; typedef unsigned __int32 uint32_t; typedef __int64 int64_t; typedef unsigned __int64 uint64_t; -#elif defined (__KERNEL__) -# include -#else +#elif defined (_MSC_VER) && _MSC_VER < 1800 +/* VS 2013 (_MSC_VER 1800) has inttypes.h */ # include +#else +# include #endif +#include #if defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) #define HB_DEPRECATED __attribute__((__deprecated__)) @@ -340,419 +338,7 @@ HB_EXTERN hb_bool_t hb_language_matches (hb_language_t language, hb_language_t specific); -/** - * hb_script_t: - * @HB_SCRIPT_COMMON: `Zyyy` - * @HB_SCRIPT_INHERITED: `Zinh` - * @HB_SCRIPT_UNKNOWN: `Zzzz` - * @HB_SCRIPT_ARABIC: `Arab` - * @HB_SCRIPT_ARMENIAN: `Armn` - * @HB_SCRIPT_BENGALI: `Beng` - * @HB_SCRIPT_CYRILLIC: `Cyrl` - * @HB_SCRIPT_DEVANAGARI: `Deva` - * @HB_SCRIPT_GEORGIAN: `Geor` - * @HB_SCRIPT_GREEK: `Grek` - * @HB_SCRIPT_GUJARATI: `Gujr` - * @HB_SCRIPT_GURMUKHI: `Guru` - * @HB_SCRIPT_HANGUL: `Hang` - * @HB_SCRIPT_HAN: `Hani` - * @HB_SCRIPT_HEBREW: `Hebr` - * @HB_SCRIPT_HIRAGANA: `Hira` - * @HB_SCRIPT_KANNADA: `Knda` - * @HB_SCRIPT_KATAKANA: `Kana` - * @HB_SCRIPT_LAO: `Laoo` - * @HB_SCRIPT_LATIN: `Latn` - * @HB_SCRIPT_MALAYALAM: `Mlym` - * @HB_SCRIPT_ORIYA: `Orya` - * @HB_SCRIPT_TAMIL: `Taml` - * @HB_SCRIPT_TELUGU: `Telu` - * @HB_SCRIPT_THAI: `Thai` - * @HB_SCRIPT_TIBETAN: `Tibt` - * @HB_SCRIPT_BOPOMOFO: `Bopo` - * @HB_SCRIPT_BRAILLE: `Brai` - * @HB_SCRIPT_CANADIAN_SYLLABICS: `Cans` - * @HB_SCRIPT_CHEROKEE: `Cher` - * @HB_SCRIPT_ETHIOPIC: `Ethi` - * @HB_SCRIPT_KHMER: `Khmr` - * @HB_SCRIPT_MONGOLIAN: `Mong` - * @HB_SCRIPT_MYANMAR: `Mymr` - * @HB_SCRIPT_OGHAM: `Ogam` - * @HB_SCRIPT_RUNIC: `Runr` - * @HB_SCRIPT_SINHALA: `Sinh` - * @HB_SCRIPT_SYRIAC: `Syrc` - * @HB_SCRIPT_THAANA: `Thaa` - * @HB_SCRIPT_YI: `Yiii` - * @HB_SCRIPT_DESERET: `Dsrt` - * @HB_SCRIPT_GOTHIC: `Goth` - * @HB_SCRIPT_OLD_ITALIC: `Ital` - * @HB_SCRIPT_BUHID: `Buhd` - * @HB_SCRIPT_HANUNOO: `Hano` - * @HB_SCRIPT_TAGALOG: `Tglg` - * @HB_SCRIPT_TAGBANWA: `Tagb` - * @HB_SCRIPT_CYPRIOT: `Cprt` - * @HB_SCRIPT_LIMBU: `Limb` - * @HB_SCRIPT_LINEAR_B: `Linb` - * @HB_SCRIPT_OSMANYA: `Osma` - * @HB_SCRIPT_SHAVIAN: `Shaw` - * @HB_SCRIPT_TAI_LE: `Tale` - * @HB_SCRIPT_UGARITIC: `Ugar` - * @HB_SCRIPT_BUGINESE: `Bugi` - * @HB_SCRIPT_COPTIC: `Copt` - * @HB_SCRIPT_GLAGOLITIC: `Glag` - * @HB_SCRIPT_KHAROSHTHI: `Khar` - * @HB_SCRIPT_NEW_TAI_LUE: `Talu` - * @HB_SCRIPT_OLD_PERSIAN: `Xpeo` - * @HB_SCRIPT_SYLOTI_NAGRI: `Sylo` - * @HB_SCRIPT_TIFINAGH: `Tfng` - * @HB_SCRIPT_BALINESE: `Bali` - * @HB_SCRIPT_CUNEIFORM: `Xsux` - * @HB_SCRIPT_NKO: `Nkoo` - * @HB_SCRIPT_PHAGS_PA: `Phag` - * @HB_SCRIPT_PHOENICIAN: `Phnx` - * @HB_SCRIPT_CARIAN: `Cari` - * @HB_SCRIPT_CHAM: `Cham` - * @HB_SCRIPT_KAYAH_LI: `Kali` - * @HB_SCRIPT_LEPCHA: `Lepc` - * @HB_SCRIPT_LYCIAN: `Lyci` - * @HB_SCRIPT_LYDIAN: `Lydi` - * @HB_SCRIPT_OL_CHIKI: `Olck` - * @HB_SCRIPT_REJANG: `Rjng` - * @HB_SCRIPT_SAURASHTRA: `Saur` - * @HB_SCRIPT_SUNDANESE: `Sund` - * @HB_SCRIPT_VAI: `Vaii` - * @HB_SCRIPT_AVESTAN: `Avst` - * @HB_SCRIPT_BAMUM: `Bamu` - * @HB_SCRIPT_EGYPTIAN_HIEROGLYPHS: `Egyp` - * @HB_SCRIPT_IMPERIAL_ARAMAIC: `Armi` - * @HB_SCRIPT_INSCRIPTIONAL_PAHLAVI: `Phli` - * @HB_SCRIPT_INSCRIPTIONAL_PARTHIAN: `Prti` - * @HB_SCRIPT_JAVANESE: `Java` - * @HB_SCRIPT_KAITHI: `Kthi` - * @HB_SCRIPT_LISU: `Lisu` - * @HB_SCRIPT_MEETEI_MAYEK: `Mtei` - * @HB_SCRIPT_OLD_SOUTH_ARABIAN: `Sarb` - * @HB_SCRIPT_OLD_TURKIC: `Orkh` - * @HB_SCRIPT_SAMARITAN: `Samr` - * @HB_SCRIPT_TAI_THAM: `Lana` - * @HB_SCRIPT_TAI_VIET: `Tavt` - * @HB_SCRIPT_BATAK: `Batk` - * @HB_SCRIPT_BRAHMI: `Brah` - * @HB_SCRIPT_MANDAIC: `Mand` - * @HB_SCRIPT_CHAKMA: `Cakm` - * @HB_SCRIPT_MEROITIC_CURSIVE: `Merc` - * @HB_SCRIPT_MEROITIC_HIEROGLYPHS: `Mero` - * @HB_SCRIPT_MIAO: `Plrd` - * @HB_SCRIPT_SHARADA: `Shrd` - * @HB_SCRIPT_SORA_SOMPENG: `Sora` - * @HB_SCRIPT_TAKRI: `Takr` - * @HB_SCRIPT_BASSA_VAH: `Bass`, Since: 0.9.30 - * @HB_SCRIPT_CAUCASIAN_ALBANIAN: `Aghb`, Since: 0.9.30 - * @HB_SCRIPT_DUPLOYAN: `Dupl`, Since: 0.9.30 - * @HB_SCRIPT_ELBASAN: `Elba`, Since: 0.9.30 - * @HB_SCRIPT_GRANTHA: `Gran`, Since: 0.9.30 - * @HB_SCRIPT_KHOJKI: `Khoj`, Since: 0.9.30 - * @HB_SCRIPT_KHUDAWADI: `Sind`, Since: 0.9.30 - * @HB_SCRIPT_LINEAR_A: `Lina`, Since: 0.9.30 - * @HB_SCRIPT_MAHAJANI: `Mahj`, Since: 0.9.30 - * @HB_SCRIPT_MANICHAEAN: `Mani`, Since: 0.9.30 - * @HB_SCRIPT_MENDE_KIKAKUI: `Mend`, Since: 0.9.30 - * @HB_SCRIPT_MODI: `Modi`, Since: 0.9.30 - * @HB_SCRIPT_MRO: `Mroo`, Since: 0.9.30 - * @HB_SCRIPT_NABATAEAN: `Nbat`, Since: 0.9.30 - * @HB_SCRIPT_OLD_NORTH_ARABIAN: `Narb`, Since: 0.9.30 - * @HB_SCRIPT_OLD_PERMIC: `Perm`, Since: 0.9.30 - * @HB_SCRIPT_PAHAWH_HMONG: `Hmng`, Since: 0.9.30 - * @HB_SCRIPT_PALMYRENE: `Palm`, Since: 0.9.30 - * @HB_SCRIPT_PAU_CIN_HAU: `Pauc`, Since: 0.9.30 - * @HB_SCRIPT_PSALTER_PAHLAVI: `Phlp`, Since: 0.9.30 - * @HB_SCRIPT_SIDDHAM: `Sidd`, Since: 0.9.30 - * @HB_SCRIPT_TIRHUTA: `Tirh`, Since: 0.9.30 - * @HB_SCRIPT_WARANG_CITI: `Wara`, Since: 0.9.30 - * @HB_SCRIPT_AHOM: `Ahom`, Since: 0.9.30 - * @HB_SCRIPT_ANATOLIAN_HIEROGLYPHS: `Hluw`, Since: 0.9.30 - * @HB_SCRIPT_HATRAN: `Hatr`, Since: 0.9.30 - * @HB_SCRIPT_MULTANI: `Mult`, Since: 0.9.30 - * @HB_SCRIPT_OLD_HUNGARIAN: `Hung`, Since: 0.9.30 - * @HB_SCRIPT_SIGNWRITING: `Sgnw`, Since: 0.9.30 - * @HB_SCRIPT_ADLAM: `Adlm`, Since: 1.3.0 - * @HB_SCRIPT_BHAIKSUKI: `Bhks`, Since: 1.3.0 - * @HB_SCRIPT_MARCHEN: `Marc`, Since: 1.3.0 - * @HB_SCRIPT_OSAGE: `Osge`, Since: 1.3.0 - * @HB_SCRIPT_TANGUT: `Tang`, Since: 1.3.0 - * @HB_SCRIPT_NEWA: `Newa`, Since: 1.3.0 - * @HB_SCRIPT_MASARAM_GONDI: `Gonm`, Since: 1.6.0 - * @HB_SCRIPT_NUSHU: `Nshu`, Since: 1.6.0 - * @HB_SCRIPT_SOYOMBO: `Soyo`, Since: 1.6.0 - * @HB_SCRIPT_ZANABAZAR_SQUARE: `Zanb`, Since: 1.6.0 - * @HB_SCRIPT_DOGRA: `Dogr`, Since: 1.8.0 - * @HB_SCRIPT_GUNJALA_GONDI: `Gong`, Since: 1.8.0 - * @HB_SCRIPT_HANIFI_ROHINGYA: `Rohg`, Since: 1.8.0 - * @HB_SCRIPT_MAKASAR: `Maka`, Since: 1.8.0 - * @HB_SCRIPT_MEDEFAIDRIN: `Medf`, Since: 1.8.0 - * @HB_SCRIPT_OLD_SOGDIAN: `Sogo`, Since: 1.8.0 - * @HB_SCRIPT_SOGDIAN: `Sogd`, Since: 1.8.0 - * @HB_SCRIPT_ELYMAIC: `Elym`, Since: 2.4.0 - * @HB_SCRIPT_NANDINAGARI: `Nand`, Since: 2.4.0 - * @HB_SCRIPT_NYIAKENG_PUACHUE_HMONG: `Hmnp`, Since: 2.4.0 - * @HB_SCRIPT_WANCHO: `Wcho`, Since: 2.4.0 - * @HB_SCRIPT_CHORASMIAN: `Chrs`, Since: 2.6.7 - * @HB_SCRIPT_DIVES_AKURU: `Diak`, Since: 2.6.7 - * @HB_SCRIPT_KHITAN_SMALL_SCRIPT: `Kits`, Since: 2.6.7 - * @HB_SCRIPT_YEZIDI: `Yezi`, Since: 2.6.7 - * @HB_SCRIPT_CYPRO_MINOAN: `Cpmn`, Since: 3.0.0 - * @HB_SCRIPT_OLD_UYGHUR: `Ougr`, Since: 3.0.0 - * @HB_SCRIPT_TANGSA: `Tnsa`, Since: 3.0.0 - * @HB_SCRIPT_TOTO: `Toto`, Since: 3.0.0 - * @HB_SCRIPT_VITHKUQI: `Vith`, Since: 3.0.0 - * @HB_SCRIPT_MATH: `Zmth`, Since: 3.4.0 - * @HB_SCRIPT_KAWI: `Kawi`, Since: 5.2.0 - * @HB_SCRIPT_NAG_MUNDARI: `Nagm`, Since: 5.2.0 - * @HB_SCRIPT_INVALID: No script set - * - * Data type for scripts. Each #hb_script_t's value is an #hb_tag_t corresponding - * to the four-letter values defined by [ISO 15924](https://unicode.org/iso15924/). - * - * See also the Script (sc) property of the Unicode Character Database. - * - **/ - -/* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */ -typedef enum -{ - HB_SCRIPT_COMMON = HB_TAG ('Z','y','y','y'), /*1.1*/ - HB_SCRIPT_INHERITED = HB_TAG ('Z','i','n','h'), /*1.1*/ - HB_SCRIPT_UNKNOWN = HB_TAG ('Z','z','z','z'), /*5.0*/ - - HB_SCRIPT_ARABIC = HB_TAG ('A','r','a','b'), /*1.1*/ - HB_SCRIPT_ARMENIAN = HB_TAG ('A','r','m','n'), /*1.1*/ - HB_SCRIPT_BENGALI = HB_TAG ('B','e','n','g'), /*1.1*/ - HB_SCRIPT_CYRILLIC = HB_TAG ('C','y','r','l'), /*1.1*/ - HB_SCRIPT_DEVANAGARI = HB_TAG ('D','e','v','a'), /*1.1*/ - HB_SCRIPT_GEORGIAN = HB_TAG ('G','e','o','r'), /*1.1*/ - HB_SCRIPT_GREEK = HB_TAG ('G','r','e','k'), /*1.1*/ - HB_SCRIPT_GUJARATI = HB_TAG ('G','u','j','r'), /*1.1*/ - HB_SCRIPT_GURMUKHI = HB_TAG ('G','u','r','u'), /*1.1*/ - HB_SCRIPT_HANGUL = HB_TAG ('H','a','n','g'), /*1.1*/ - HB_SCRIPT_HAN = HB_TAG ('H','a','n','i'), /*1.1*/ - HB_SCRIPT_HEBREW = HB_TAG ('H','e','b','r'), /*1.1*/ - HB_SCRIPT_HIRAGANA = HB_TAG ('H','i','r','a'), /*1.1*/ - HB_SCRIPT_KANNADA = HB_TAG ('K','n','d','a'), /*1.1*/ - HB_SCRIPT_KATAKANA = HB_TAG ('K','a','n','a'), /*1.1*/ - HB_SCRIPT_LAO = HB_TAG ('L','a','o','o'), /*1.1*/ - HB_SCRIPT_LATIN = HB_TAG ('L','a','t','n'), /*1.1*/ - HB_SCRIPT_MALAYALAM = HB_TAG ('M','l','y','m'), /*1.1*/ - HB_SCRIPT_ORIYA = HB_TAG ('O','r','y','a'), /*1.1*/ - HB_SCRIPT_TAMIL = HB_TAG ('T','a','m','l'), /*1.1*/ - HB_SCRIPT_TELUGU = HB_TAG ('T','e','l','u'), /*1.1*/ - HB_SCRIPT_THAI = HB_TAG ('T','h','a','i'), /*1.1*/ - - HB_SCRIPT_TIBETAN = HB_TAG ('T','i','b','t'), /*2.0*/ - - HB_SCRIPT_BOPOMOFO = HB_TAG ('B','o','p','o'), /*3.0*/ - HB_SCRIPT_BRAILLE = HB_TAG ('B','r','a','i'), /*3.0*/ - HB_SCRIPT_CANADIAN_SYLLABICS = HB_TAG ('C','a','n','s'), /*3.0*/ - HB_SCRIPT_CHEROKEE = HB_TAG ('C','h','e','r'), /*3.0*/ - HB_SCRIPT_ETHIOPIC = HB_TAG ('E','t','h','i'), /*3.0*/ - HB_SCRIPT_KHMER = HB_TAG ('K','h','m','r'), /*3.0*/ - HB_SCRIPT_MONGOLIAN = HB_TAG ('M','o','n','g'), /*3.0*/ - HB_SCRIPT_MYANMAR = HB_TAG ('M','y','m','r'), /*3.0*/ - HB_SCRIPT_OGHAM = HB_TAG ('O','g','a','m'), /*3.0*/ - HB_SCRIPT_RUNIC = HB_TAG ('R','u','n','r'), /*3.0*/ - HB_SCRIPT_SINHALA = HB_TAG ('S','i','n','h'), /*3.0*/ - HB_SCRIPT_SYRIAC = HB_TAG ('S','y','r','c'), /*3.0*/ - HB_SCRIPT_THAANA = HB_TAG ('T','h','a','a'), /*3.0*/ - HB_SCRIPT_YI = HB_TAG ('Y','i','i','i'), /*3.0*/ - - HB_SCRIPT_DESERET = HB_TAG ('D','s','r','t'), /*3.1*/ - HB_SCRIPT_GOTHIC = HB_TAG ('G','o','t','h'), /*3.1*/ - HB_SCRIPT_OLD_ITALIC = HB_TAG ('I','t','a','l'), /*3.1*/ - - HB_SCRIPT_BUHID = HB_TAG ('B','u','h','d'), /*3.2*/ - HB_SCRIPT_HANUNOO = HB_TAG ('H','a','n','o'), /*3.2*/ - HB_SCRIPT_TAGALOG = HB_TAG ('T','g','l','g'), /*3.2*/ - HB_SCRIPT_TAGBANWA = HB_TAG ('T','a','g','b'), /*3.2*/ - - HB_SCRIPT_CYPRIOT = HB_TAG ('C','p','r','t'), /*4.0*/ - HB_SCRIPT_LIMBU = HB_TAG ('L','i','m','b'), /*4.0*/ - HB_SCRIPT_LINEAR_B = HB_TAG ('L','i','n','b'), /*4.0*/ - HB_SCRIPT_OSMANYA = HB_TAG ('O','s','m','a'), /*4.0*/ - HB_SCRIPT_SHAVIAN = HB_TAG ('S','h','a','w'), /*4.0*/ - HB_SCRIPT_TAI_LE = HB_TAG ('T','a','l','e'), /*4.0*/ - HB_SCRIPT_UGARITIC = HB_TAG ('U','g','a','r'), /*4.0*/ - - HB_SCRIPT_BUGINESE = HB_TAG ('B','u','g','i'), /*4.1*/ - HB_SCRIPT_COPTIC = HB_TAG ('C','o','p','t'), /*4.1*/ - HB_SCRIPT_GLAGOLITIC = HB_TAG ('G','l','a','g'), /*4.1*/ - HB_SCRIPT_KHAROSHTHI = HB_TAG ('K','h','a','r'), /*4.1*/ - HB_SCRIPT_NEW_TAI_LUE = HB_TAG ('T','a','l','u'), /*4.1*/ - HB_SCRIPT_OLD_PERSIAN = HB_TAG ('X','p','e','o'), /*4.1*/ - HB_SCRIPT_SYLOTI_NAGRI = HB_TAG ('S','y','l','o'), /*4.1*/ - HB_SCRIPT_TIFINAGH = HB_TAG ('T','f','n','g'), /*4.1*/ - - HB_SCRIPT_BALINESE = HB_TAG ('B','a','l','i'), /*5.0*/ - HB_SCRIPT_CUNEIFORM = HB_TAG ('X','s','u','x'), /*5.0*/ - HB_SCRIPT_NKO = HB_TAG ('N','k','o','o'), /*5.0*/ - HB_SCRIPT_PHAGS_PA = HB_TAG ('P','h','a','g'), /*5.0*/ - HB_SCRIPT_PHOENICIAN = HB_TAG ('P','h','n','x'), /*5.0*/ - - HB_SCRIPT_CARIAN = HB_TAG ('C','a','r','i'), /*5.1*/ - HB_SCRIPT_CHAM = HB_TAG ('C','h','a','m'), /*5.1*/ - HB_SCRIPT_KAYAH_LI = HB_TAG ('K','a','l','i'), /*5.1*/ - HB_SCRIPT_LEPCHA = HB_TAG ('L','e','p','c'), /*5.1*/ - HB_SCRIPT_LYCIAN = HB_TAG ('L','y','c','i'), /*5.1*/ - HB_SCRIPT_LYDIAN = HB_TAG ('L','y','d','i'), /*5.1*/ - HB_SCRIPT_OL_CHIKI = HB_TAG ('O','l','c','k'), /*5.1*/ - HB_SCRIPT_REJANG = HB_TAG ('R','j','n','g'), /*5.1*/ - HB_SCRIPT_SAURASHTRA = HB_TAG ('S','a','u','r'), /*5.1*/ - HB_SCRIPT_SUNDANESE = HB_TAG ('S','u','n','d'), /*5.1*/ - HB_SCRIPT_VAI = HB_TAG ('V','a','i','i'), /*5.1*/ - - HB_SCRIPT_AVESTAN = HB_TAG ('A','v','s','t'), /*5.2*/ - HB_SCRIPT_BAMUM = HB_TAG ('B','a','m','u'), /*5.2*/ - HB_SCRIPT_EGYPTIAN_HIEROGLYPHS = HB_TAG ('E','g','y','p'), /*5.2*/ - HB_SCRIPT_IMPERIAL_ARAMAIC = HB_TAG ('A','r','m','i'), /*5.2*/ - HB_SCRIPT_INSCRIPTIONAL_PAHLAVI = HB_TAG ('P','h','l','i'), /*5.2*/ - HB_SCRIPT_INSCRIPTIONAL_PARTHIAN = HB_TAG ('P','r','t','i'), /*5.2*/ - HB_SCRIPT_JAVANESE = HB_TAG ('J','a','v','a'), /*5.2*/ - HB_SCRIPT_KAITHI = HB_TAG ('K','t','h','i'), /*5.2*/ - HB_SCRIPT_LISU = HB_TAG ('L','i','s','u'), /*5.2*/ - HB_SCRIPT_MEETEI_MAYEK = HB_TAG ('M','t','e','i'), /*5.2*/ - HB_SCRIPT_OLD_SOUTH_ARABIAN = HB_TAG ('S','a','r','b'), /*5.2*/ - HB_SCRIPT_OLD_TURKIC = HB_TAG ('O','r','k','h'), /*5.2*/ - HB_SCRIPT_SAMARITAN = HB_TAG ('S','a','m','r'), /*5.2*/ - HB_SCRIPT_TAI_THAM = HB_TAG ('L','a','n','a'), /*5.2*/ - HB_SCRIPT_TAI_VIET = HB_TAG ('T','a','v','t'), /*5.2*/ - - HB_SCRIPT_BATAK = HB_TAG ('B','a','t','k'), /*6.0*/ - HB_SCRIPT_BRAHMI = HB_TAG ('B','r','a','h'), /*6.0*/ - HB_SCRIPT_MANDAIC = HB_TAG ('M','a','n','d'), /*6.0*/ - - HB_SCRIPT_CHAKMA = HB_TAG ('C','a','k','m'), /*6.1*/ - HB_SCRIPT_MEROITIC_CURSIVE = HB_TAG ('M','e','r','c'), /*6.1*/ - HB_SCRIPT_MEROITIC_HIEROGLYPHS = HB_TAG ('M','e','r','o'), /*6.1*/ - HB_SCRIPT_MIAO = HB_TAG ('P','l','r','d'), /*6.1*/ - HB_SCRIPT_SHARADA = HB_TAG ('S','h','r','d'), /*6.1*/ - HB_SCRIPT_SORA_SOMPENG = HB_TAG ('S','o','r','a'), /*6.1*/ - HB_SCRIPT_TAKRI = HB_TAG ('T','a','k','r'), /*6.1*/ - - /* - * Since: 0.9.30 - */ - HB_SCRIPT_BASSA_VAH = HB_TAG ('B','a','s','s'), /*7.0*/ - HB_SCRIPT_CAUCASIAN_ALBANIAN = HB_TAG ('A','g','h','b'), /*7.0*/ - HB_SCRIPT_DUPLOYAN = HB_TAG ('D','u','p','l'), /*7.0*/ - HB_SCRIPT_ELBASAN = HB_TAG ('E','l','b','a'), /*7.0*/ - HB_SCRIPT_GRANTHA = HB_TAG ('G','r','a','n'), /*7.0*/ - HB_SCRIPT_KHOJKI = HB_TAG ('K','h','o','j'), /*7.0*/ - HB_SCRIPT_KHUDAWADI = HB_TAG ('S','i','n','d'), /*7.0*/ - HB_SCRIPT_LINEAR_A = HB_TAG ('L','i','n','a'), /*7.0*/ - HB_SCRIPT_MAHAJANI = HB_TAG ('M','a','h','j'), /*7.0*/ - HB_SCRIPT_MANICHAEAN = HB_TAG ('M','a','n','i'), /*7.0*/ - HB_SCRIPT_MENDE_KIKAKUI = HB_TAG ('M','e','n','d'), /*7.0*/ - HB_SCRIPT_MODI = HB_TAG ('M','o','d','i'), /*7.0*/ - HB_SCRIPT_MRO = HB_TAG ('M','r','o','o'), /*7.0*/ - HB_SCRIPT_NABATAEAN = HB_TAG ('N','b','a','t'), /*7.0*/ - HB_SCRIPT_OLD_NORTH_ARABIAN = HB_TAG ('N','a','r','b'), /*7.0*/ - HB_SCRIPT_OLD_PERMIC = HB_TAG ('P','e','r','m'), /*7.0*/ - HB_SCRIPT_PAHAWH_HMONG = HB_TAG ('H','m','n','g'), /*7.0*/ - HB_SCRIPT_PALMYRENE = HB_TAG ('P','a','l','m'), /*7.0*/ - HB_SCRIPT_PAU_CIN_HAU = HB_TAG ('P','a','u','c'), /*7.0*/ - HB_SCRIPT_PSALTER_PAHLAVI = HB_TAG ('P','h','l','p'), /*7.0*/ - HB_SCRIPT_SIDDHAM = HB_TAG ('S','i','d','d'), /*7.0*/ - HB_SCRIPT_TIRHUTA = HB_TAG ('T','i','r','h'), /*7.0*/ - HB_SCRIPT_WARANG_CITI = HB_TAG ('W','a','r','a'), /*7.0*/ - - HB_SCRIPT_AHOM = HB_TAG ('A','h','o','m'), /*8.0*/ - HB_SCRIPT_ANATOLIAN_HIEROGLYPHS = HB_TAG ('H','l','u','w'), /*8.0*/ - HB_SCRIPT_HATRAN = HB_TAG ('H','a','t','r'), /*8.0*/ - HB_SCRIPT_MULTANI = HB_TAG ('M','u','l','t'), /*8.0*/ - HB_SCRIPT_OLD_HUNGARIAN = HB_TAG ('H','u','n','g'), /*8.0*/ - HB_SCRIPT_SIGNWRITING = HB_TAG ('S','g','n','w'), /*8.0*/ - - /* - * Since 1.3.0 - */ - HB_SCRIPT_ADLAM = HB_TAG ('A','d','l','m'), /*9.0*/ - HB_SCRIPT_BHAIKSUKI = HB_TAG ('B','h','k','s'), /*9.0*/ - HB_SCRIPT_MARCHEN = HB_TAG ('M','a','r','c'), /*9.0*/ - HB_SCRIPT_OSAGE = HB_TAG ('O','s','g','e'), /*9.0*/ - HB_SCRIPT_TANGUT = HB_TAG ('T','a','n','g'), /*9.0*/ - HB_SCRIPT_NEWA = HB_TAG ('N','e','w','a'), /*9.0*/ - - /* - * Since 1.6.0 - */ - HB_SCRIPT_MASARAM_GONDI = HB_TAG ('G','o','n','m'), /*10.0*/ - HB_SCRIPT_NUSHU = HB_TAG ('N','s','h','u'), /*10.0*/ - HB_SCRIPT_SOYOMBO = HB_TAG ('S','o','y','o'), /*10.0*/ - HB_SCRIPT_ZANABAZAR_SQUARE = HB_TAG ('Z','a','n','b'), /*10.0*/ - - /* - * Since 1.8.0 - */ - HB_SCRIPT_DOGRA = HB_TAG ('D','o','g','r'), /*11.0*/ - HB_SCRIPT_GUNJALA_GONDI = HB_TAG ('G','o','n','g'), /*11.0*/ - HB_SCRIPT_HANIFI_ROHINGYA = HB_TAG ('R','o','h','g'), /*11.0*/ - HB_SCRIPT_MAKASAR = HB_TAG ('M','a','k','a'), /*11.0*/ - HB_SCRIPT_MEDEFAIDRIN = HB_TAG ('M','e','d','f'), /*11.0*/ - HB_SCRIPT_OLD_SOGDIAN = HB_TAG ('S','o','g','o'), /*11.0*/ - HB_SCRIPT_SOGDIAN = HB_TAG ('S','o','g','d'), /*11.0*/ - - /* - * Since 2.4.0 - */ - HB_SCRIPT_ELYMAIC = HB_TAG ('E','l','y','m'), /*12.0*/ - HB_SCRIPT_NANDINAGARI = HB_TAG ('N','a','n','d'), /*12.0*/ - HB_SCRIPT_NYIAKENG_PUACHUE_HMONG = HB_TAG ('H','m','n','p'), /*12.0*/ - HB_SCRIPT_WANCHO = HB_TAG ('W','c','h','o'), /*12.0*/ - - /* - * Since 2.6.7 - */ - HB_SCRIPT_CHORASMIAN = HB_TAG ('C','h','r','s'), /*13.0*/ - HB_SCRIPT_DIVES_AKURU = HB_TAG ('D','i','a','k'), /*13.0*/ - HB_SCRIPT_KHITAN_SMALL_SCRIPT = HB_TAG ('K','i','t','s'), /*13.0*/ - HB_SCRIPT_YEZIDI = HB_TAG ('Y','e','z','i'), /*13.0*/ - - /* - * Since 3.0.0 - */ - HB_SCRIPT_CYPRO_MINOAN = HB_TAG ('C','p','m','n'), /*14.0*/ - HB_SCRIPT_OLD_UYGHUR = HB_TAG ('O','u','g','r'), /*14.0*/ - HB_SCRIPT_TANGSA = HB_TAG ('T','n','s','a'), /*14.0*/ - HB_SCRIPT_TOTO = HB_TAG ('T','o','t','o'), /*14.0*/ - HB_SCRIPT_VITHKUQI = HB_TAG ('V','i','t','h'), /*14.0*/ - - /* - * Since 3.4.0 - */ - HB_SCRIPT_MATH = HB_TAG ('Z','m','t','h'), - - /* - * Since 5.2.0 - */ - HB_SCRIPT_KAWI = HB_TAG ('K','a','w','i'), /*15.0*/ - HB_SCRIPT_NAG_MUNDARI = HB_TAG ('N','a','g','m'), /*15.0*/ - - /* No script set. */ - HB_SCRIPT_INVALID = HB_TAG_NONE, - - /*< private >*/ - - /* Dummy values to ensure any hb_tag_t value can be passed/stored as hb_script_t - * without risking undefined behavior. We have two, for historical reasons. - * HB_TAG_MAX used to be unsigned, but that was invalid Ansi C, so was changed - * to _HB_SCRIPT_MAX_VALUE to be equal to HB_TAG_MAX_SIGNED as well. - * - * See this thread for technicalities: - * - * https://lists.freedesktop.org/archives/harfbuzz/2014-March/004150.html - */ - _HB_SCRIPT_MAX_VALUE = HB_TAG_MAX_SIGNED, /*< skip >*/ - _HB_SCRIPT_MAX_VALUE_SIGNED = HB_TAG_MAX_SIGNED /*< skip >*/ - -} hb_script_t; - +#include "hb-script-list.h" /* Script functions */ @@ -933,6 +519,16 @@ typedef struct hb_glyph_extents_t { */ typedef struct hb_font_t hb_font_t; +/* Not of much use to clients. */ +HB_EXTERN void* +hb_malloc (size_t size); +HB_EXTERN void* +hb_calloc (size_t nmemb, size_t size); +HB_EXTERN void* +hb_realloc (void *ptr, size_t size); +HB_EXTERN void +hb_free (void *ptr); + HB_END_DECLS #endif /* HB_COMMON_H */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-config.hh b/src/java.desktop/share/native/libharfbuzz/hb-config.hh index 816c55c7d367a..3956690da3535 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-config.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-config.hh @@ -56,7 +56,6 @@ #ifdef HB_LEAN #define HB_DISABLE_DEPRECATED -#define HB_NDEBUG #define HB_NO_ATEXIT #define HB_NO_BUFFER_MESSAGE #define HB_NO_BUFFER_SERIALIZE @@ -69,8 +68,6 @@ #define HB_NO_FACE_COLLECT_UNICODES #define HB_NO_GETENV #define HB_NO_HINTING -#define HB_NO_LANGUAGE_LONG -#define HB_NO_LANGUAGE_PRIVATE_SUBTAG #define HB_NO_LAYOUT_FEATURE_PARAMS #define HB_NO_LAYOUT_COLLECT_GLYPHS #define HB_NO_LAYOUT_RARELY_USED @@ -118,6 +115,10 @@ #define HB_NO_VAR_COMPOSITES #endif +#ifdef HB_NO_VAR +#define HB_NO_VAR_COMPOSITES +#endif + #ifdef HB_DISABLE_DEPRECATED #define HB_IF_NOT_DEPRECATED(x) #else @@ -145,6 +146,7 @@ #ifdef HB_NO_DRAW #define HB_NO_OUTLINE +#define HB_NO_PAINT #endif #ifdef HB_NO_GETENV @@ -156,6 +158,7 @@ #define HB_NO_FALLBACK_SHAPE #define HB_NO_OT_KERN #define HB_NO_OT_LAYOUT_BLOCKLIST +#define HB_NO_AAT_LAYOUT_BLOCKLIST #define HB_NO_OT_SHAPE_FALLBACK #endif @@ -189,7 +192,6 @@ #ifdef HB_MINIMIZE_MEMORY_USAGE #define HB_NO_GDEF_CACHE #define HB_NO_OT_LAYOUT_LOOKUP_CACHE -#define HB_NO_OT_FONT_ADVANCE_CACHE #define HB_NO_OT_FONT_CMAP_CACHE #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cplusplus.hh b/src/java.desktop/share/native/libharfbuzz/hb-cplusplus.hh index eac4ff03ed5f7..82d397415279f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-cplusplus.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-cplusplus.hh @@ -27,9 +27,6 @@ #include "hb.h" -HB_BEGIN_DECLS -HB_END_DECLS - #ifdef __cplusplus #include @@ -56,15 +53,15 @@ struct shared_ptr explicit shared_ptr (T *p = nullptr) : p (p) {} shared_ptr (const shared_ptr &o) : p (v::reference (o.p)) {} - shared_ptr (shared_ptr &&o) : p (o.p) { o.p = nullptr; } + shared_ptr (shared_ptr &&o) noexcept : p (o.p) { o.p = nullptr; } shared_ptr& operator = (const shared_ptr &o) { if (p != o.p) { destroy (); p = o.p; reference (); } return *this; } - shared_ptr& operator = (shared_ptr &&o) { v::destroy (p); p = o.p; o.p = nullptr; return *this; } + shared_ptr& operator = (shared_ptr &&o) noexcept { v::destroy (p); p = o.p; o.p = nullptr; return *this; } ~shared_ptr () { v::destroy (p); p = nullptr; } T* get() const { return p; } - void swap (shared_ptr &o) { std::swap (p, o.p); } - friend void swap (shared_ptr &a, shared_ptr &b) { std::swap (a.p, b.p); } + void swap (shared_ptr &o) noexcept { std::swap (p, o.p); } + friend void swap (shared_ptr &a, shared_ptr &b) noexcept { std::swap (a.p, b.p); } operator T * () const { return p; } T& operator * () const { return *get (); } @@ -98,16 +95,16 @@ struct unique_ptr explicit unique_ptr (T *p = nullptr) : p (p) {} unique_ptr (const unique_ptr &o) = delete; - unique_ptr (unique_ptr &&o) : p (o.p) { o.p = nullptr; } + unique_ptr (unique_ptr &&o) noexcept : p (o.p) { o.p = nullptr; } unique_ptr& operator = (const unique_ptr &o) = delete; - unique_ptr& operator = (unique_ptr &&o) { v::destroy (p); p = o.p; o.p = nullptr; return *this; } + unique_ptr& operator = (unique_ptr &&o) noexcept { v::destroy (p); p = o.p; o.p = nullptr; return *this; } ~unique_ptr () { v::destroy (p); p = nullptr; } T* get() const { return p; } T* release () { T* v = p; p = nullptr; return v; } - void swap (unique_ptr &o) { std::swap (p, o.p); } - friend void swap (unique_ptr &a, unique_ptr &b) { std::swap (a.p, b.p); } + void swap (unique_ptr &o) noexcept { std::swap (p, o.p); } + friend void swap (unique_ptr &a, unique_ptr &b) noexcept { std::swap (a.p, b.p); } operator T * () const { return p; } T& operator * () const { return *get (); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-debug.hh b/src/java.desktop/share/native/libharfbuzz/hb-debug.hh index 341e61e57c696..1e9a18b93261e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-debug.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-debug.hh @@ -49,15 +49,15 @@ struct hb_options_t }; union hb_options_union_t { - int i; + unsigned i; hb_options_t opts; }; -static_assert ((sizeof (hb_atomic_int_t) >= sizeof (hb_options_union_t)), ""); +static_assert ((sizeof (hb_atomic_t) >= sizeof (hb_options_union_t)), ""); HB_INTERNAL void _hb_options_init (); -extern HB_INTERNAL hb_atomic_int_t _hb_options; +extern HB_INTERNAL hb_atomic_t _hb_options; static inline hb_options_t hb_options () diff --git a/src/java.desktop/share/native/libharfbuzz/hb-decycler.hh b/src/java.desktop/share/native/libharfbuzz/hb-decycler.hh new file mode 100644 index 0000000000000..95b961027bf22 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-decycler.hh @@ -0,0 +1,164 @@ +/* + * Copyright © 2025 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_DECYCLER_HH +#define HB_DECYCLER_HH + +#include "hb.hh" + +/* + * hb_decycler_t is an efficient cycle detector for graph traversal. + * It's a simple tortoise-and-hare algorithm with a twist: it's + * designed to detect cycles while traversing a graph in a DFS manner, + * instead of just a linked list. + * + * For Floyd's tortoise and hare algorithm, see: + * https://en.wikipedia.org/wiki/Cycle_detection#Floyd's_tortoise_and_hare + * + * hb_decycler_t is O(n) in the number of nodes in the DFS traversal + * if there are no cycles. Unlike Floyd's algorithm, hb_decycler_t + * can be used in a DFS traversal, where the graph is not a simple + * linked list, but a tree with possible cycles. Like Floyd's algorithm, + * it is constant-memory (~three pointers). + * + * The decycler works by creating an implicit linked-list on the stack, + * of the path from the root to the current node, and apply Floyd's + * algorithm on that list as it goes. + * + * The decycler is malloc-free, and as such, much faster to use than a + * hb_set_t or hb_map_t equivalent. + * + * The decycler detects cycles in the graph *eventually*, not *immediately*. + * That is, it may not detect a cycle until the cycle is fully traversed, + * even multiple times. See Floyd's algorithm analysis for details. + * + * The implementation saves a pointer storage on the stack by combining + * this->u.decycler and this->u.next into a union. This is possible because + * at any point we only need one of those values. The invariant is that + * after construction, and before destruction, of a node, the u.decycler + * field is always valid. The u.next field is only valid when the node is + * in the traversal path, parent to another node. + * + * There are three method's: + * + * - hb_decycler_node_t() constructor: Creates a new node in the traversal. + * The constructor takes a reference to the decycler object and inserts + * itself as the latest node in the traversal path, by advancing the hare + * pointer, and for every other descent, advancing the tortoise pointer. + * + * - ~hb_decycler_node_t() destructor: Restores the decycler object to its + * previous state by removing the node from the traversal path. + * + * - bool visit(uintptr_t value): Called on every node in the graph. Returns + * true if the node is not part of a cycle, and false if it is. The value + * parameter is used to detect cycles. It's the caller's responsibility + * to ensure that the value is unique for each node in the graph. + * The cycle detection is as simple as comparing the value to the value + * held by the tortoise pointer, which is the Floyd's algorithm. + * + * For usage examples see test-decycler.cc. + */ + +struct hb_decycler_node_t; + +struct hb_decycler_t +{ + friend struct hb_decycler_node_t; + + private: + bool tortoise_awake = false; + hb_decycler_node_t *tortoise = nullptr; + hb_decycler_node_t *hare = nullptr; +}; + +struct hb_decycler_node_t +{ + hb_decycler_node_t (hb_decycler_t &decycler) + { + u.decycler = &decycler; + + decycler.tortoise_awake = !decycler.tortoise_awake; + + if (!decycler.tortoise) + { + // First node. + assert (decycler.tortoise_awake); + assert (!decycler.hare); + decycler.tortoise = decycler.hare = this; + return; + } + + if (decycler.tortoise_awake) + decycler.tortoise = decycler.tortoise->u.next; // Time to move. + + this->prev = decycler.hare; + decycler.hare->u.next = this; + decycler.hare = this; + } + + ~hb_decycler_node_t () + { + hb_decycler_t &decycler = *u.decycler; + + // Inverse of the constructor. + + assert (decycler.hare == this); + decycler.hare = prev; + if (prev) + prev->u.decycler = &decycler; + + assert (decycler.tortoise); + if (decycler.tortoise_awake) + decycler.tortoise = decycler.tortoise->prev; + + decycler.tortoise_awake = !decycler.tortoise_awake; + } + + bool visit (uintptr_t value_) + { + value = value_; + + hb_decycler_t &decycler = *u.decycler; + + if (decycler.tortoise == this) + return true; // First node; not a cycle. + + if (decycler.tortoise->value == value) + return false; // Cycle detected. + + return true; + } + + private: + union { + hb_decycler_t *decycler; + hb_decycler_node_t *next; + } u = {nullptr}; + hb_decycler_node_t *prev = nullptr; + uintptr_t value = 0; +}; + +#endif /* HB_DECYCLER_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h b/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h index 200e8ff49288b..927563c4c40a9 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h @@ -56,7 +56,7 @@ HB_BEGIN_DECLS /** * HB_SCRIPT_CANADIAN_ABORIGINAL: * - * Use #HB_SCRIPT_CANADIAN_SYLLABICS instead: + * Use #HB_SCRIPT_CANADIAN_SYLLABICS instead. * * Deprecated: 0.9.20 */ @@ -275,6 +275,48 @@ typedef void (*hb_font_get_glyph_shape_func_t) (hb_font_t *font, void *font_data hb_draw_funcs_t *draw_funcs, void *draw_data, void *user_data); +/** + * hb_font_draw_glyph_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @draw_funcs: The draw functions to send the shape data to + * @draw_data: The data accompanying the draw functions + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * Since: 7.0.0 + * XDeprecated: REPLACEME: Use hb_font_draw_glyph_func_or_fail_t instead. + **/ +typedef void (*hb_font_draw_glyph_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data, + void *user_data); + +/** + * hb_font_paint_glyph_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @paint_funcs: The paint functions to use + * @paint_data: The data accompanying the paint functions + * @palette_index: The color palette to use + * @foreground: The foreground color + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * Since: 7.0.0 + * XDeprecated: REPLACEME: Use hb_font_paint_glyph_or_fail_func_t instead. + */ +typedef hb_bool_t (*hb_font_paint_glyph_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette_index, + hb_color_t foreground, + void *user_data); + /** * hb_font_funcs_set_glyph_shape_func: * @ffuncs: A font-function structure @@ -288,19 +330,64 @@ typedef void (*hb_font_get_glyph_shape_func_t) (hb_font_t *font, void *font_data * Since: 4.0.0 * Deprecated: 7.0.0: Use hb_font_funcs_set_draw_glyph_func() instead **/ -HB_DEPRECATED_FOR (hb_font_funcs_set_draw_glyph_func) +HB_DEPRECATED_FOR (hb_font_funcs_set_draw_glyph_or_fail_func) HB_EXTERN void hb_font_funcs_set_glyph_shape_func (hb_font_funcs_t *ffuncs, hb_font_get_glyph_shape_func_t func, void *user_data, hb_destroy_func_t destroy); -HB_DEPRECATED_FOR (hb_font_draw_glyph) +/** + * hb_font_funcs_set_draw_glyph_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_draw_glyph_func_t. + * + * Since: 7.0.0 + * XDeprecated: REPLACEME: Use hb_font_funcs_set_draw_glyph_or_fail_func instead. + **/ +HB_DEPRECATED_FOR (hb_font_funcs_set_draw_glyph_or_fail_func) +HB_EXTERN void +hb_font_funcs_set_draw_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_draw_glyph_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_paint_glyph_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is no longer needed + * + * Sets the implementation function for #hb_font_paint_glyph_func_t. + * + * Since: 7.0.0 + * XDeprecated: REPLACEME: Use hb_font_funcs_set_paint_glyph_or_fail_func() instead. + */ +HB_DEPRECATED_FOR (hb_font_funcs_set_paint_glyph_or_fail_func) +HB_EXTERN void +hb_font_funcs_set_paint_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_paint_glyph_func_t func, + void *user_data, hb_destroy_func_t destroy); + +HB_DEPRECATED_FOR (hb_font_draw_glyph_or_fail) HB_EXTERN void hb_font_get_glyph_shape (hb_font_t *font, hb_codepoint_t glyph, hb_draw_funcs_t *dfuncs, void *draw_data); +/** + * HB_AAT_LAYOUT_FEATURE_TYPE_CURISVE_CONNECTION: + * + * Use #HB_AAT_LAYOUT_FEATURE_TYPE_CURSIVE_CONNECTION instead. + * + * Deprecated: 8.3.0 + */ +#define HB_AAT_LAYOUT_FEATURE_TYPE_CURISVE_CONNECTION HB_AAT_LAYOUT_FEATURE_TYPE_CURSIVE_CONNECTION + #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-draw.cc b/src/java.desktop/share/native/libharfbuzz/hb-draw.cc index f2821578b65f6..8764ffe712961 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-draw.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-draw.cc @@ -28,6 +28,11 @@ #include "hb-draw.hh" +#include "hb-geometry.hh" + +#include "hb-machinery.hh" + + /** * SECTION:hb-draw * @title: hb-draw @@ -455,4 +460,92 @@ hb_draw_close_path (hb_draw_funcs_t *dfuncs, void *draw_data, } +static void +hb_draw_extents_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_extents_t *extents = (hb_extents_t *) data; + + extents->add_point (to_x, to_y); +} + +static void +hb_draw_extents_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_extents_t *extents = (hb_extents_t *) data; + + extents->add_point (to_x, to_y); +} + +static void +hb_draw_extents_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float control_x, float control_y, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_extents_t *extents = (hb_extents_t *) data; + + extents->add_point (control_x, control_y); + extents->add_point (to_x, to_y); +} + +static void +hb_draw_extents_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_extents_t *extents = (hb_extents_t *) data; + + extents->add_point (control1_x, control1_y); + extents->add_point (control2_x, control2_y); + extents->add_point (to_x, to_y); +} + +static inline void free_static_draw_extents_funcs (); + +static struct hb_draw_extents_funcs_lazy_loader_t : hb_draw_funcs_lazy_loader_t +{ + static hb_draw_funcs_t *create () + { + hb_draw_funcs_t *funcs = hb_draw_funcs_create (); + + hb_draw_funcs_set_move_to_func (funcs, hb_draw_extents_move_to, nullptr, nullptr); + hb_draw_funcs_set_line_to_func (funcs, hb_draw_extents_line_to, nullptr, nullptr); + hb_draw_funcs_set_quadratic_to_func (funcs, hb_draw_extents_quadratic_to, nullptr, nullptr); + hb_draw_funcs_set_cubic_to_func (funcs, hb_draw_extents_cubic_to, nullptr, nullptr); + + hb_draw_funcs_make_immutable (funcs); + + hb_atexit (free_static_draw_extents_funcs); + + return funcs; + } +} static_draw_extents_funcs; + +static inline +void free_static_draw_extents_funcs () +{ + static_draw_extents_funcs.free_instance (); +} + +hb_draw_funcs_t * +hb_draw_extents_get_funcs () +{ + return static_draw_extents_funcs.get_unconst (); +} + + #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-draw.h b/src/java.desktop/share/native/libharfbuzz/hb-draw.h index eba791d10c08d..06299e17a7853 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-draw.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-draw.h @@ -70,7 +70,7 @@ typedef struct hb_draw_state_t { * * The default #hb_draw_state_t at the start of glyph drawing. */ -#define HB_DRAW_STATE_DEFAULT {0, 0.f, 0.f, 0.f, 0.f, {0.}, {0.}, {0.}} +#define HB_DRAW_STATE_DEFAULT {0, 0.f, 0.f, 0.f, 0.f, {0}, {0}, {0}, {0}, {0}, {0}, {0}} /** diff --git a/src/java.desktop/share/native/libharfbuzz/hb-draw.hh b/src/java.desktop/share/native/libharfbuzz/hb-draw.hh index e1adf9ddc9f5c..224e10939feef 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-draw.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-draw.hh @@ -99,6 +99,7 @@ struct hb_draw_funcs_t float to_x, float to_y) { if (unlikely (st.path_open)) close_path (draw_data, st); + st.current_x = to_x; st.current_y = to_y; } @@ -109,7 +110,9 @@ struct hb_draw_funcs_t float to_x, float to_y) { if (unlikely (!st.path_open)) start_path (draw_data, st); + emit_line_to (draw_data, st, to_x, to_y); + st.current_x = to_x; st.current_y = to_y; } @@ -121,7 +124,9 @@ struct hb_draw_funcs_t float to_x, float to_y) { if (unlikely (!st.path_open)) start_path (draw_data, st); + emit_quadratic_to (draw_data, st, control_x, control_y, to_x, to_y); + st.current_x = to_x; st.current_y = to_y; } @@ -134,7 +139,9 @@ struct hb_draw_funcs_t float to_x, float to_y) { if (unlikely (!st.path_open)) start_path (draw_data, st); + emit_cubic_to (draw_data, st, control1_x, control1_y, control2_x, control2_y, to_x, to_y); + st.current_x = to_x; st.current_y = to_y; } @@ -168,9 +175,8 @@ DECLARE_NULL_INSTANCE (hb_draw_funcs_t); struct hb_draw_session_t { - hb_draw_session_t (hb_draw_funcs_t *funcs_, void *draw_data_, float slant_ = 0.f) - : slant {slant_}, not_slanted {slant == 0.f}, - funcs {funcs_}, draw_data {draw_data_}, st HB_DRAW_STATE_DEFAULT + hb_draw_session_t (hb_draw_funcs_t *funcs_, void *draw_data_) + : funcs {funcs_}, draw_data {draw_data_}, st HB_DRAW_STATE_DEFAULT {} ~hb_draw_session_t () { close_path (); } @@ -178,36 +184,23 @@ struct hb_draw_session_t HB_ALWAYS_INLINE void move_to (float to_x, float to_y) { - if (likely (not_slanted)) - funcs->move_to (draw_data, st, - to_x, to_y); - else - funcs->move_to (draw_data, st, - to_x + to_y * slant, to_y); + funcs->move_to (draw_data, st, + to_x, to_y); } HB_ALWAYS_INLINE void line_to (float to_x, float to_y) { - if (likely (not_slanted)) - funcs->line_to (draw_data, st, - to_x, to_y); - else - funcs->line_to (draw_data, st, - to_x + to_y * slant, to_y); + funcs->line_to (draw_data, st, + to_x, to_y); } void HB_ALWAYS_INLINE quadratic_to (float control_x, float control_y, float to_x, float to_y) { - if (likely (not_slanted)) - funcs->quadratic_to (draw_data, st, - control_x, control_y, - to_x, to_y); - else - funcs->quadratic_to (draw_data, st, - control_x + control_y * slant, control_y, - to_x + to_y * slant, to_y); + funcs->quadratic_to (draw_data, st, + control_x, control_y, + to_x, to_y); } void HB_ALWAYS_INLINE @@ -215,16 +208,10 @@ struct hb_draw_session_t float control2_x, float control2_y, float to_x, float to_y) { - if (likely (not_slanted)) - funcs->cubic_to (draw_data, st, - control1_x, control1_y, - control2_x, control2_y, - to_x, to_y); - else - funcs->cubic_to (draw_data, st, - control1_x + control1_y * slant, control1_y, - control2_x + control2_y * slant, control2_y, - to_x + to_y * slant, to_y); + funcs->cubic_to (draw_data, st, + control1_x, control1_y, + control2_x, control2_y, + to_x, to_y); } HB_ALWAYS_INLINE void close_path () @@ -232,12 +219,15 @@ struct hb_draw_session_t funcs->close_path (draw_data, st); } - protected: - float slant; - bool not_slanted; + public: hb_draw_funcs_t *funcs; void *draw_data; hb_draw_state_t st; }; + +HB_INTERNAL hb_draw_funcs_t * +hb_draw_extents_get_funcs (); + + #endif /* HB_DRAW_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-face-builder.cc b/src/java.desktop/share/native/libharfbuzz/hb-face-builder.cc index ff723ac700040..6b7e1913024b5 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-face-builder.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-face-builder.cc @@ -42,7 +42,7 @@ struct face_table_info_t { hb_blob_t* data; - signed order; + unsigned order; }; struct hb_face_builder_data_t @@ -153,6 +153,50 @@ _hb_face_builder_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void return hb_blob_reference (data->tables[tag].data); } +static unsigned +_hb_face_builder_get_table_tags (const hb_face_t *face HB_UNUSED, + unsigned int start_offset, + unsigned int *table_count, + hb_tag_t *table_tags, + void *user_data) +{ + hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data; + + unsigned population = data->tables.get_population (); + + if (!table_count) + return population; + + if (unlikely (start_offset >= population)) + { + if (table_count) + *table_count = 0; + return population; + } + + // Sort the tags. + hb_vector_t sorted_tags; + data->tables.keys () | hb_sink (sorted_tags); + if (unlikely (sorted_tags.in_error ())) + { + // Not much to do... + } + sorted_tags.qsort ([] (const void* a, const void* b) { + return * (hb_tag_t *) a < * (hb_tag_t *) b ? -1 : + * (hb_tag_t *) a == * (hb_tag_t *) b ? 0 : + +1; + }); + + auto array = sorted_tags.as_array ().sub_array (start_offset, table_count); + auto out = hb_array (table_tags, *table_count); + + + array.iter () + | hb_sink (out) + ; + + return population; +} + /** * hb_face_builder_create: @@ -171,9 +215,16 @@ hb_face_builder_create () hb_face_builder_data_t *data = _hb_face_builder_data_create (); if (unlikely (!data)) return hb_face_get_empty (); - return hb_face_create_for_tables (_hb_face_builder_reference_table, - data, - _hb_face_builder_data_destroy); + hb_face_t *face = hb_face_create_for_tables (_hb_face_builder_reference_table, + data, + _hb_face_builder_data_destroy); + + hb_face_set_get_table_tags_func (face, + _hb_face_builder_get_table_tags, + data, + nullptr); + + return face; } /** @@ -199,7 +250,7 @@ hb_face_builder_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob) hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data; hb_blob_t* previous = data->tables.get (tag).data; - if (!data->tables.set (tag, face_table_info_t {hb_blob_reference (blob), -1})) + if (!data->tables.set (tag, face_table_info_t {hb_blob_reference (blob), (unsigned) -1})) { hb_blob_destroy (blob); return false; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-face.cc b/src/java.desktop/share/native/libharfbuzz/hb-face.cc index bc19bb9d5c970..9d59d7c9468ae 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-face.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-face.cc @@ -34,6 +34,16 @@ #include "hb-ot-face.hh" #include "hb-ot-cmap-table.hh" +#ifdef HAVE_FREETYPE +#include "hb-ft.h" +#endif +#ifdef HAVE_CORETEXT +#include "hb-coretext.h" +#endif +#ifdef HAVE_DIRECTWRITE +#include "hb-directwrite.h" +#endif + /** * SECTION:hb-face @@ -72,14 +82,14 @@ hb_face_count (hb_blob_t *blob) if (unlikely (!blob)) return 0; - /* TODO We shouldn't be sanitizing blob. Port to run sanitizer and return if not sane. */ - /* Make API signature const after. */ - hb_blob_t *sanitized = hb_sanitize_context_t ().sanitize_blob (hb_blob_reference (blob)); - const OT::OpenTypeFontFile& ot = *sanitized->as (); - unsigned int ret = ot.get_face_count (); - hb_blob_destroy (sanitized); + hb_sanitize_context_t c (blob); + + const char *start = hb_blob_get_data (blob, nullptr); + auto *ot = reinterpret_cast (const_cast (start)); + if (unlikely (!ot->sanitize (&c))) + return 0; - return ret; + return ot->get_face_count (); } /* @@ -90,10 +100,6 @@ DEFINE_NULL_INSTANCE (hb_face_t) = { HB_OBJECT_HEADER_STATIC, - nullptr, /* reference_table_func */ - nullptr, /* user_data */ - nullptr, /* destroy */ - 0, /* index */ 1000, /* upem */ 0, /* num_glyphs */ @@ -110,8 +116,9 @@ DEFINE_NULL_INSTANCE (hb_face_t) = * * Variant of hb_face_create(), built for those cases where it is more * convenient to provide data for individual tables instead of the whole font - * data. With the caveat that hb_face_get_table_tags() does not currently work - * with faces created this way. + * data. With the caveat that hb_face_get_table_tags() would not work + * with faces created this way. You can address that by calling the + * hb_face_set_get_table_tags_func() function and setting the appropriate callback. * * Creates a new face object from the specified @user_data and @reference_table_func, * with the @destroy callback. @@ -194,6 +201,22 @@ _hb_face_for_data_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void return blob; } +static unsigned +_hb_face_for_data_get_table_tags (const hb_face_t *face HB_UNUSED, + unsigned int start_offset, + unsigned int *table_count, + hb_tag_t *table_tags, + void *user_data) +{ + hb_face_for_data_closure_t *data = (hb_face_for_data_closure_t *) user_data; + + const OT::OpenTypeFontFile &ot_file = *data->blob->as (); + const OT::OpenTypeFontFace &ot_face = ot_file.get_face (data->index); + + return ot_face.get_table_tags (start_offset, table_count, table_tags); +} + + /** * hb_face_create: * @blob: #hb_blob_t to work upon @@ -240,12 +263,275 @@ hb_face_create (hb_blob_t *blob, face = hb_face_create_for_tables (_hb_face_for_data_reference_table, closure, _hb_face_for_data_closure_destroy); + hb_face_set_get_table_tags_func (face, + _hb_face_for_data_get_table_tags, + closure, + nullptr); face->index = index; return face; } +/** + * hb_face_create_or_fail: + * @blob: #hb_blob_t to work upon + * @index: The index of the face within @blob + * + * Like hb_face_create(), but returns `NULL` if the blob data + * contains no usable font face at the specified index. + * + * Return value: (transfer full): The new face object, or `NULL` if + * no face is found at the specified index. + * + * Since: 10.1.0 + **/ +hb_face_t * +hb_face_create_or_fail (hb_blob_t *blob, + unsigned int index) +{ + unsigned num_faces = hb_face_count (blob); + if (index >= num_faces) + return nullptr; + + hb_face_t *face = hb_face_create (blob, index); + if (hb_object_is_immutable (face)) + return nullptr; + + return face; +} + +#ifndef HB_NO_OPEN +/** + * hb_face_create_from_file_or_fail: + * @file_name: A font filename + * @index: The index of the face within the file + * + * A thin wrapper around hb_blob_create_from_file_or_fail() + * followed by hb_face_create_or_fail(). + * + * Return value: (transfer full): The new face object, or `NULL` if + * no face is found at the specified index or the file cannot be read. + * + * Since: 10.1.0 + **/ +HB_EXTERN hb_face_t * +hb_face_create_from_file_or_fail (const char *file_name, + unsigned int index) +{ + hb_blob_t *blob = hb_blob_create_from_file_or_fail (file_name); + if (unlikely (!blob)) + return nullptr; + + hb_face_t *face = hb_face_create_or_fail (blob, index); + hb_blob_destroy (blob); + + return face; +} + +static struct supported_face_loaders_t { + char name[16]; + hb_face_t * (*from_file) (const char *font_file, unsigned face_index); + hb_face_t * (*from_blob) (hb_blob_t *blob, unsigned face_index); +} supported_face_loaders[] = +{ + {"ot", +#ifndef HB_NO_OPEN + hb_face_create_from_file_or_fail, +#else + nullptr, +#endif + hb_face_create_or_fail + }, +#ifdef HAVE_FREETYPE + {"ft", + hb_ft_face_create_from_file_or_fail, + hb_ft_face_create_from_blob_or_fail + }, +#endif +#ifdef HAVE_CORETEXT + {"coretext", + hb_coretext_face_create_from_file_or_fail, + hb_coretext_face_create_from_blob_or_fail + }, +#endif +#ifdef HAVE_DIRECTWRITE + {"directwrite", + hb_directwrite_face_create_from_file_or_fail, + hb_directwrite_face_create_from_blob_or_fail + }, +#endif +}; + +static const char *get_default_loader_name () +{ + static hb_atomic_t static_loader_name; + const char *loader_name = static_loader_name.get_acquire (); + if (!loader_name) + { + loader_name = getenv ("HB_FACE_LOADER"); + if (!loader_name) + loader_name = ""; + if (!static_loader_name.cmpexch (nullptr, loader_name)) + loader_name = static_loader_name.get_acquire (); + } + return loader_name; +} + +/** + * hb_face_create_from_file_or_fail_using: + * @file_name: A font filename + * @index: The index of the face within the file + * @loader_name: (nullable): The name of the loader to use, or `NULL` + * + * A thin wrapper around the face loader functions registered with HarfBuzz. + * If @loader_name is `NULL` or the empty string, the first available loader + * is used. + * + * For example, the FreeType ("ft") loader might be able to load + * WOFF and WOFF2 files if FreeType is built with those features, + * whereas the OpenType ("ot") loader will not. + * + * Return value: (transfer full): The new face object, or `NULL` if + * the file cannot be read or the loader fails to load the face. + * + * Since: 11.0.0 + **/ +hb_face_t * +hb_face_create_from_file_or_fail_using (const char *file_name, + unsigned int index, + const char *loader_name) +{ + // Duplicated in hb_face_create_or_fail_using + bool retry = false; + if (!loader_name || !*loader_name) + { + loader_name = get_default_loader_name (); + retry = true; + } + if (loader_name && !*loader_name) loader_name = nullptr; + +retry: + for (unsigned i = 0; i < ARRAY_LENGTH (supported_face_loaders); i++) + { + if (!loader_name || (supported_face_loaders[i].from_file && !strcmp (supported_face_loaders[i].name, loader_name))) + return supported_face_loaders[i].from_file (file_name, index); + } + + if (retry) + { + retry = false; + loader_name = nullptr; + goto retry; + } + + return nullptr; +} + +/** + * hb_face_create_or_fail_using: + * @blob: #hb_blob_t to work upon + * @index: The index of the face within @blob + * @loader_name: (nullable): The name of the loader to use, or `NULL` + * + * A thin wrapper around the face loader functions registered with HarfBuzz. + * If @loader_name is `NULL` or the empty string, the first available loader + * is used. + * + * For example, the FreeType ("ft") loader might be able to load + * WOFF and WOFF2 files if FreeType is built with those features, + * whereas the OpenType ("ot") loader will not. + * + * Return value: (transfer full): The new face object, or `NULL` if + * the loader fails to load the face. + * + * Since: 11.0.0 + **/ +hb_face_t * +hb_face_create_or_fail_using (hb_blob_t *blob, + unsigned int index, + const char *loader_name) +{ + // Duplicated in hb_face_create_from_file_or_fail_using + bool retry = false; + if (!loader_name || !*loader_name) + { + loader_name = get_default_loader_name (); + retry = true; + } + if (loader_name && !*loader_name) loader_name = nullptr; + +retry: + for (unsigned i = 0; i < ARRAY_LENGTH (supported_face_loaders); i++) + { + if (!loader_name || (supported_face_loaders[i].from_blob && !strcmp (supported_face_loaders[i].name, loader_name))) + return supported_face_loaders[i].from_blob (blob, index); + } + + if (retry) + { + retry = false; + loader_name = nullptr; + goto retry; + } + + return nullptr; +} + +static inline void free_static_face_loader_list (); + +static const char * const nil_face_loader_list[] = {nullptr}; + +static struct hb_face_loader_list_lazy_loader_t : hb_lazy_loader_t +{ + static const char ** create () + { + const char **face_loader_list = (const char **) hb_calloc (1 + ARRAY_LENGTH (supported_face_loaders), sizeof (const char *)); + if (unlikely (!face_loader_list)) + return nullptr; + + unsigned i; + for (i = 0; i < ARRAY_LENGTH (supported_face_loaders); i++) + face_loader_list[i] = supported_face_loaders[i].name; + face_loader_list[i] = nullptr; + + hb_atexit (free_static_face_loader_list); + + return face_loader_list; + } + static void destroy (const char **l) + { hb_free (l); } + static const char * const * get_null () + { return nil_face_loader_list; } +} static_face_loader_list; + +static inline +void free_static_face_loader_list () +{ + static_face_loader_list.free_instance (); +} + +/** + * hb_face_list_loaders: + * + * Retrieves the list of face loaders supported by HarfBuzz. + * + * Return value: (transfer none) (array zero-terminated=1): a + * `NULL`-terminated array of supported face loaders + * constant strings. The returned array is owned by HarfBuzz + * and should not be modified or freed. + * + * Since: 11.0.0 + **/ +const char ** +hb_face_list_loaders () +{ + return static_face_loader_list.get_unconst (); +} +#endif + + /** * hb_face_get_empty: * @@ -306,6 +592,9 @@ hb_face_destroy (hb_face_t *face) face->data.fini (); face->table.fini (); + if (face->get_table_tags_destroy) + face->get_table_tags_destroy (face->get_table_tags_user_data); + if (face->destroy) face->destroy (face->user_data); @@ -383,7 +672,7 @@ hb_face_make_immutable (hb_face_t *face) * Since: 0.9.2 **/ hb_bool_t -hb_face_is_immutable (const hb_face_t *face) +hb_face_is_immutable (hb_face_t *face) { return hb_object_is_immutable (face); } @@ -395,7 +684,8 @@ hb_face_is_immutable (const hb_face_t *face) * @tag: The #hb_tag_t of the table to query * * Fetches a reference to the specified table within - * the specified face. + * the specified face. Returns an empty blob if referencing table data is not + * possible. * * Return value: (transfer full): A pointer to the @tag table within @face * @@ -415,9 +705,10 @@ hb_face_reference_table (const hb_face_t *face, * hb_face_reference_blob: * @face: A face object * - * Fetches a pointer to the binary blob that contains the - * specified face. Returns an empty blob if referencing face data is not - * possible. + * Fetches a pointer to the binary blob that contains the specified face. + * If referencing the face data is not possible, this function creates a blob + * out of individual table blobs if hb_face_get_table_tags() works with this + * face, otherwise it returns an empty blob. * * Return value: (transfer full): A pointer to the blob for @face * @@ -426,7 +717,41 @@ hb_face_reference_table (const hb_face_t *face, hb_blob_t * hb_face_reference_blob (hb_face_t *face) { - return face->reference_table (HB_TAG_NONE); + hb_blob_t *blob = face->reference_table (HB_TAG_NONE); + + if (blob == hb_blob_get_empty ()) + { + // If referencing the face blob is not possible (e.g. not implemented by the + // font functions), use face builder to create a blob out of individual + // table blobs. + unsigned total_count = hb_face_get_table_tags (face, 0, nullptr, nullptr); + if (total_count) + { + hb_tag_t tags[64]; + unsigned count = ARRAY_LENGTH (tags); + hb_face_t* builder = hb_face_builder_create (); + + for (unsigned offset = 0; offset < total_count; offset += count) + { + hb_face_get_table_tags (face, offset, &count, tags); + if (unlikely (!count)) + break; // Allocation error + for (unsigned i = 0; i < count; i++) + { + if (unlikely (!tags[i])) + continue; + hb_blob_t *table = hb_face_reference_table (face, tags[i]); + hb_face_builder_add_table (builder, tags[i], table); + hb_blob_destroy (table); + } + } + + blob = hb_face_reference_blob (builder); + hb_face_destroy (builder); + } + } + + return blob; } /** @@ -547,6 +872,38 @@ hb_face_get_glyph_count (const hb_face_t *face) return face->get_num_glyphs (); } +/** + * hb_face_set_get_table_tags_func: + * @face: A face object + * @func: (closure user_data) (destroy destroy) (scope notified): The table-tag-fetching function + * @user_data: A pointer to the user data, to be destroyed by @destroy when not needed anymore + * @destroy: (nullable): A callback to call when @func is not needed anymore + * + * Sets the table-tag-fetching function for the specified face object. + * + * Since: 10.0.0 + */ +HB_EXTERN void +hb_face_set_get_table_tags_func (hb_face_t *face, + hb_get_table_tags_func_t func, + void *user_data, + hb_destroy_func_t destroy) +{ + if (hb_object_is_immutable (face)) + { + if (destroy) + destroy (user_data); + return; + } + + if (face->get_table_tags_destroy) + face->get_table_tags_destroy (face->get_table_tags_user_data); + + face->get_table_tags_func = func; + face->get_table_tags_user_data = user_data; + face->get_table_tags_destroy = destroy; +} + /** * hb_face_get_table_tags: * @face: A face object @@ -568,19 +925,14 @@ hb_face_get_table_tags (const hb_face_t *face, unsigned int *table_count, /* IN/OUT */ hb_tag_t *table_tags /* OUT */) { - if (face->destroy != (hb_destroy_func_t) _hb_face_for_data_closure_destroy) + if (!face->get_table_tags_func) { if (table_count) *table_count = 0; return 0; } - hb_face_for_data_closure_t *data = (hb_face_for_data_closure_t *) face->user_data; - - const OT::OpenTypeFontFile &ot_file = *data->blob->as (); - const OT::OpenTypeFontFace &ot_face = ot_file.get_face (data->index); - - return ot_face.get_table_tags (start_offset, table_count, table_tags); + return face->get_table_tags_func (face, start_offset, table_count, table_tags, face->get_table_tags_user_data); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-face.h b/src/java.desktop/share/native/libharfbuzz/hb-face.h index 2e5fb1509272b..2c130f00feec8 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-face.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-face.h @@ -59,15 +59,42 @@ HB_EXTERN hb_face_t * hb_face_create (hb_blob_t *blob, unsigned int index); +HB_EXTERN hb_face_t * +hb_face_create_or_fail (hb_blob_t *blob, + unsigned int index); + +HB_EXTERN hb_face_t * +hb_face_create_or_fail_using (hb_blob_t *blob, + unsigned int index, + const char *loader_name); + +HB_EXTERN hb_face_t * +hb_face_create_from_file_or_fail (const char *file_name, + unsigned int index); + +HB_EXTERN hb_face_t * +hb_face_create_from_file_or_fail_using (const char *file_name, + unsigned int index, + const char *loader_name); + +HB_EXTERN const char ** +hb_face_list_loaders (void); + + /** * hb_reference_table_func_t: * @face: an #hb_face_t to reference table for * @tag: the tag of the table to reference * @user_data: User data pointer passed by the caller * - * Callback function for hb_face_create_for_tables(). + * Callback function for hb_face_create_for_tables(). The @tag is the tag of the + * table to reference, and the special tag #HB_TAG_NONE is used to reference the + * blob of the face itself. If referencing the face blob is not possible, it is + * recommended to set hb_get_table_tags_func_t on the @face to allow + * hb_face_reference_blob() to create a face blob out of individual table blobs. * - * Return value: (transfer full): A pointer to the @tag table within @face + * Return value: (transfer full): A pointer to the @tag table within @face or + * `NULL` if the table is not found or cannot be referenced. * * Since: 0.9.2 */ @@ -104,7 +131,7 @@ HB_EXTERN void hb_face_make_immutable (hb_face_t *face); HB_EXTERN hb_bool_t -hb_face_is_immutable (const hb_face_t *face); +hb_face_is_immutable (hb_face_t *face); HB_EXTERN hb_blob_t * @@ -135,6 +162,34 @@ hb_face_set_glyph_count (hb_face_t *face, HB_EXTERN unsigned int hb_face_get_glyph_count (const hb_face_t *face); + +/** + * hb_get_table_tags_func_t: + * @face: A face object + * @start_offset: The index of first table tag to retrieve + * @table_count: (inout): Input = the maximum number of table tags to return; + * Output = the actual number of table tags returned (may be zero) + * @table_tags: (out) (array length=table_count): The array of table tags found + * @user_data: User data pointer passed by the caller + * + * Callback function for hb_face_get_table_tags(). + * + * Return value: Total number of tables, or zero if it is not possible to list + * + * Since: 10.0.0 + */ +typedef unsigned int (*hb_get_table_tags_func_t) (const hb_face_t *face, + unsigned int start_offset, + unsigned int *table_count, /* IN/OUT */ + hb_tag_t *table_tags /* OUT */, + void *user_data); + +HB_EXTERN void +hb_face_set_get_table_tags_func (hb_face_t *face, + hb_get_table_tags_func_t func, + void *user_data, + hb_destroy_func_t destroy); + HB_EXTERN unsigned int hb_face_get_table_tags (const hb_face_t *face, unsigned int start_offset, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-face.hh b/src/java.desktop/share/native/libharfbuzz/hb-face.hh index 4c6b252e88eaf..e58e8f9b94c3e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-face.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-face.hh @@ -48,13 +48,17 @@ struct hb_face_t { hb_object_header_t header; + unsigned int index; /* Face index in a collection, zero-based. */ + mutable hb_atomic_t upem; /* Units-per-EM. */ + mutable hb_atomic_t num_glyphs;/* Number of glyphs. */ + hb_reference_table_func_t reference_table_func; void *user_data; hb_destroy_func_t destroy; - unsigned int index; /* Face index in a collection, zero-based. */ - mutable hb_atomic_int_t upem; /* Units-per-EM. */ - mutable hb_atomic_int_t num_glyphs; /* Number of glyphs. */ + hb_get_table_tags_func_t get_table_tags_func; + void *get_table_tags_user_data; + hb_destroy_func_t get_table_tags_destroy; hb_shaper_object_dataset_t data;/* Various shaper data. */ hb_ot_face_t table; /* All the face's tables. */ @@ -66,7 +70,7 @@ struct hb_face_t plan_node_t *next; }; #ifndef HB_NO_SHAPER - hb_atomic_ptr_t shape_plans; + hb_atomic_t shape_plans; #endif hb_blob_t *reference_table (hb_tag_t tag) const diff --git a/src/java.desktop/share/native/libharfbuzz/hb-font.cc b/src/java.desktop/share/native/libharfbuzz/hb-font.cc index 9ce55bbeb89f3..849855e2c2297 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-font.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-font.cc @@ -38,6 +38,22 @@ #include "hb-ot-var-avar-table.hh" #include "hb-ot-var-fvar-table.hh" +#ifndef HB_NO_OT_FONT +#include "hb-ot.h" +#endif +#ifdef HAVE_FREETYPE +#include "hb-ft.h" +#endif +#ifdef HAVE_FONTATIONS +#include "hb-fontations.h" +#endif +#ifdef HAVE_CORETEXT +#include "hb-coretext.h" +#endif +#ifdef HAVE_DIRECTWRITE +#include "hb-directwrite.h" +#endif + /** * SECTION:hb-font @@ -87,7 +103,7 @@ hb_font_get_font_h_extents_default (hb_font_t *font, hb_font_extents_t *extents, void *user_data HB_UNUSED) { - hb_bool_t ret = font->parent->get_font_h_extents (extents); + hb_bool_t ret = font->parent->get_font_h_extents (extents, false); if (ret) { extents->ascender = font->parent_scale_y_distance (extents->ascender); extents->descender = font->parent_scale_y_distance (extents->descender); @@ -112,7 +128,7 @@ hb_font_get_font_v_extents_default (hb_font_t *font, hb_font_extents_t *extents, void *user_data HB_UNUSED) { - hb_bool_t ret = font->parent->get_font_v_extents (extents); + hb_bool_t ret = font->parent->get_font_v_extents (extents, false); if (ret) { extents->ascender = font->parent_scale_x_distance (extents->ascender); extents->descender = font->parent_scale_x_distance (extents->descender); @@ -218,10 +234,10 @@ hb_font_get_glyph_h_advance_default (hb_font_t *font, if (font->has_glyph_h_advances_func_set ()) { hb_position_t ret; - font->get_glyph_h_advances (1, &glyph, 0, &ret, 0); + font->get_glyph_h_advances (1, &glyph, 0, &ret, 0, false); return ret; } - return font->parent_scale_x_distance (font->parent->get_glyph_h_advance (glyph)); + return font->parent_scale_x_distance (font->parent->get_glyph_h_advance (glyph, false)); } static hb_position_t @@ -231,7 +247,7 @@ hb_font_get_glyph_v_advance_nil (hb_font_t *font, void *user_data HB_UNUSED) { /* TODO use font_extents.ascender+descender */ - return font->y_scale; + return -font->y_scale; } static hb_position_t @@ -243,10 +259,10 @@ hb_font_get_glyph_v_advance_default (hb_font_t *font, if (font->has_glyph_v_advances_func_set ()) { hb_position_t ret; - font->get_glyph_v_advances (1, &glyph, 0, &ret, 0); + font->get_glyph_v_advances (1, &glyph, 0, &ret, 0, false); return ret; } - return font->parent_scale_y_distance (font->parent->get_glyph_v_advance (glyph)); + return font->parent_scale_y_distance (font->parent->get_glyph_v_advance (glyph, false)); } #define hb_font_get_glyph_h_advances_nil hb_font_get_glyph_h_advances_default @@ -265,7 +281,7 @@ hb_font_get_glyph_h_advances_default (hb_font_t* font, { for (unsigned int i = 0; i < count; i++) { - *first_advance = font->get_glyph_h_advance (*first_glyph); + *first_advance = font->get_glyph_h_advance (*first_glyph, false); first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); } @@ -274,7 +290,8 @@ hb_font_get_glyph_h_advances_default (hb_font_t* font, font->parent->get_glyph_h_advances (count, first_glyph, glyph_stride, - first_advance, advance_stride); + first_advance, advance_stride, + false); for (unsigned int i = 0; i < count; i++) { *first_advance = font->parent_scale_x_distance (*first_advance); @@ -297,7 +314,7 @@ hb_font_get_glyph_v_advances_default (hb_font_t* font, { for (unsigned int i = 0; i < count; i++) { - *first_advance = font->get_glyph_v_advance (*first_glyph); + *first_advance = font->get_glyph_v_advance (*first_glyph, false); first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); } @@ -306,7 +323,8 @@ hb_font_get_glyph_v_advances_default (hb_font_t* font, font->parent->get_glyph_v_advances (count, first_glyph, glyph_stride, - first_advance, advance_stride); + first_advance, advance_stride, + false); for (unsigned int i = 0; i < count; i++) { *first_advance = font->parent_scale_y_distance (*first_advance); @@ -426,7 +444,7 @@ hb_font_get_glyph_extents_default (hb_font_t *font, hb_glyph_extents_t *extents, void *user_data HB_UNUSED) { - hb_bool_t ret = font->parent->get_glyph_extents (glyph, extents); + hb_bool_t ret = font->parent->get_glyph_extents (glyph, extents, false); if (ret) { font->parent_scale_position (&extents->x_bearing, &extents->y_bearing); font->parent_scale_distance (&extents->width, &extents->height); @@ -456,7 +474,7 @@ hb_font_get_glyph_contour_point_default (hb_font_t *font, hb_position_t *y, void *user_data HB_UNUSED) { - hb_bool_t ret = font->parent->get_glyph_contour_point (glyph, point_index, x, y); + hb_bool_t ret = font->parent->get_glyph_contour_point (glyph, point_index, x, y, false); if (ret) font->parent_scale_position (x, y); return ret; @@ -508,26 +526,28 @@ hb_font_get_glyph_from_name_default (hb_font_t *font, return font->parent->get_glyph_from_name (name, len, glyph); } -static void -hb_font_draw_glyph_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - hb_draw_funcs_t *draw_funcs, - void *draw_data, - void *user_data HB_UNUSED) +static hb_bool_t +hb_font_draw_glyph_or_fail_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, + void *draw_data, + void *user_data HB_UNUSED) { + return false; } -static void -hb_font_paint_glyph_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t glyph HB_UNUSED, - hb_paint_funcs_t *paint_funcs HB_UNUSED, - void *paint_data HB_UNUSED, - unsigned int palette HB_UNUSED, - hb_color_t foreground HB_UNUSED, - void *user_data HB_UNUSED) +static hb_bool_t +hb_font_paint_glyph_or_fail_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + hb_paint_funcs_t *paint_funcs HB_UNUSED, + void *paint_data HB_UNUSED, + unsigned int palette HB_UNUSED, + hb_color_t foreground HB_UNUSED, + void *user_data HB_UNUSED) { + return false; } typedef struct hb_font_draw_glyph_default_adaptor_t { @@ -535,7 +555,6 @@ typedef struct hb_font_draw_glyph_default_adaptor_t { void *draw_data; float x_scale; float y_scale; - float slant; } hb_font_draw_glyph_default_adaptor_t; static void @@ -548,10 +567,9 @@ hb_draw_move_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data; float x_scale = adaptor->x_scale; float y_scale = adaptor->y_scale; - float slant = adaptor->slant; adaptor->draw_funcs->emit_move_to (adaptor->draw_data, *st, - x_scale * to_x + slant * to_y, y_scale * to_y); + x_scale * to_x, y_scale * to_y); } static void @@ -563,13 +581,12 @@ hb_draw_line_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data, hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data; float x_scale = adaptor->x_scale; float y_scale = adaptor->y_scale; - float slant = adaptor->slant; - st->current_x = st->current_x * x_scale + st->current_y * slant; + st->current_x = st->current_x * x_scale; st->current_y = st->current_y * y_scale; adaptor->draw_funcs->emit_line_to (adaptor->draw_data, *st, - x_scale * to_x + slant * to_y, y_scale * to_y); + x_scale * to_x, y_scale * to_y); } static void @@ -582,14 +599,13 @@ hb_draw_quadratic_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data; float x_scale = adaptor->x_scale; float y_scale = adaptor->y_scale; - float slant = adaptor->slant; - st->current_x = st->current_x * x_scale + st->current_y * slant; + st->current_x = st->current_x * x_scale; st->current_y = st->current_y * y_scale; adaptor->draw_funcs->emit_quadratic_to (adaptor->draw_data, *st, - x_scale * control_x + slant * control_y, y_scale * control_y, - x_scale * to_x + slant * to_y, y_scale * to_y); + x_scale * control_x, y_scale * control_y, + x_scale * to_x, y_scale * to_y); } static void @@ -603,15 +619,14 @@ hb_draw_cubic_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data, hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data; float x_scale = adaptor->x_scale; float y_scale = adaptor->y_scale; - float slant = adaptor->slant; - st->current_x = st->current_x * x_scale + st->current_y * slant; + st->current_x = st->current_x * x_scale; st->current_y = st->current_y * y_scale; adaptor->draw_funcs->emit_cubic_to (adaptor->draw_data, *st, - x_scale * control1_x + slant * control1_y, y_scale * control1_y, - x_scale * control2_x + slant * control2_y, y_scale * control2_y, - x_scale * to_x + slant * to_y, y_scale * to_y); + x_scale * control1_x, y_scale * control1_y, + x_scale * control2_x, y_scale * control2_y, + x_scale * to_x, y_scale * to_y); } static void @@ -634,49 +649,47 @@ static const hb_draw_funcs_t _hb_draw_funcs_default = { } }; -static void -hb_font_draw_glyph_default (hb_font_t *font, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - hb_draw_funcs_t *draw_funcs, - void *draw_data, - void *user_data HB_UNUSED) +static hb_bool_t +hb_font_draw_glyph_or_fail_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, + void *draw_data, + void *user_data HB_UNUSED) { hb_font_draw_glyph_default_adaptor_t adaptor = { draw_funcs, draw_data, font->parent->x_scale ? (float) font->x_scale / (float) font->parent->x_scale : 0.f, - font->parent->y_scale ? (float) font->y_scale / (float) font->parent->y_scale : 0.f, - font->parent->y_scale ? (font->slant - font->parent->slant) * - (float) font->x_scale / (float) font->parent->y_scale : 0.f + font->parent->y_scale ? (float) font->y_scale / (float) font->parent->y_scale : 0.f }; - font->parent->draw_glyph (glyph, - const_cast (&_hb_draw_funcs_default), - &adaptor); + return font->parent->draw_glyph_or_fail (glyph, + const_cast (&_hb_draw_funcs_default), + &adaptor, + false); } -static void -hb_font_paint_glyph_default (hb_font_t *font, - void *font_data, - hb_codepoint_t glyph, - hb_paint_funcs_t *paint_funcs, - void *paint_data, - unsigned int palette, - hb_color_t foreground, - void *user_data) +static hb_bool_t +hb_font_paint_glyph_or_fail_default (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, + void *paint_data, + unsigned int palette, + hb_color_t foreground, + void *user_data) { paint_funcs->push_transform (paint_data, - font->parent->x_scale ? (float) font->x_scale / (float) font->parent->x_scale : 0.f, - font->parent->y_scale ? (font->slant - font->parent->slant) * - (float) font->x_scale / (float) font->parent->y_scale : 0.f, - 0.f, - font->parent->y_scale ? (float) font->y_scale / (float) font->parent->y_scale : 0.f, - 0.f, 0.f); + font->parent->x_scale ? (float) font->x_scale / (float) font->parent->x_scale : 0, 0, + 0, font->parent->y_scale ? (float) font->y_scale / (float) font->parent->y_scale : 0, + 0, 0); - font->parent->paint_glyph (glyph, paint_funcs, paint_data, palette, foreground); + bool ret = font->parent->paint_glyph_or_fail (glyph, paint_funcs, paint_data, palette, foreground); paint_funcs->pop_transform (paint_data); + + return ret; } DEFINE_NULL_INSTANCE (hb_font_funcs_t) = @@ -1413,6 +1426,92 @@ hb_font_get_glyph_shape (hb_font_t *font, } #endif +/** + * hb_font_draw_glyph_or_fail: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID + * @dfuncs: #hb_draw_funcs_t to draw to + * @draw_data: User data to pass to draw callbacks + * + * Draws the outline that corresponds to a glyph in the specified @font. + * + * This is a newer name for hb_font_draw_glyph(), that returns `false` + * if the font has no outlines for the glyph. + * + * The outline is returned by way of calls to the callbacks of the @dfuncs + * objects, with @draw_data passed to them. + * + * Return value: `true` if glyph was drawn, `false` otherwise + * + * XSince: REPLACEME + **/ +hb_bool_t +hb_font_draw_glyph_or_fail (hb_font_t *font, + hb_codepoint_t glyph, + hb_draw_funcs_t *dfuncs, void *draw_data) +{ + return font->draw_glyph_or_fail (glyph, dfuncs, draw_data); +} + +/** + * hb_font_paint_glyph_or_fail: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID + * @pfuncs: #hb_paint_funcs_t to paint with + * @paint_data: User data to pass to paint callbacks + * @palette_index: The index of the font's color palette to use + * @foreground: The foreground color, unpremultipled + * + * Paints a color glyph. + * + * This function is similar to, but lower-level than, + * hb_font_paint_glyph(). It is suitable for clients that + * need more control. If there are no color glyphs available, + * it will return `false`. The client can then fall back to + * hb_font_draw_glyph_or_fail() for the monochrome outline glyph. + * + * The painting instructions are returned by way of calls to + * the callbacks of the @funcs object, with @paint_data passed + * to them. + * + * If the font has color palettes (see hb_ot_color_has_palettes()), + * then @palette_index selects the palette to use. If the font only + * has one palette, this will be 0. + * + * Return value: `true` if glyph was painted, `false` otherwise + * + * XSince: REPLACEME + */ +hb_bool_t +hb_font_paint_glyph_or_fail (hb_font_t *font, + hb_codepoint_t glyph, + hb_paint_funcs_t *pfuncs, void *paint_data, + unsigned int palette_index, + hb_color_t foreground) +{ + return font->paint_glyph_or_fail (glyph, pfuncs, paint_data, palette_index, foreground); +} + +/* A bit higher-level, and with fallback */ + +void +hb_font_t::paint_glyph (hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette, + hb_color_t foreground) +{ + if (paint_glyph_or_fail (glyph, + paint_funcs, paint_data, + palette, foreground)) + return; + + /* Fallback for outline glyph. */ + paint_funcs->push_clip_glyph (paint_data, glyph, this); + paint_funcs->color (paint_data, true, foreground); + paint_funcs->pop_clip (paint_data); +} + + /** * hb_font_draw_glyph: * @font: #hb_font_t to work upon @@ -1422,6 +1521,9 @@ hb_font_get_glyph_shape (hb_font_t *font, * * Draws the outline that corresponds to a glyph in the specified @font. * + * This is an older name for hb_font_draw_glyph_or_fail(), with no + * return value. + * * The outline is returned by way of calls to the callbacks of the @dfuncs * objects, with @draw_data passed to them. * @@ -1429,10 +1531,10 @@ hb_font_get_glyph_shape (hb_font_t *font, **/ void hb_font_draw_glyph (hb_font_t *font, - hb_codepoint_t glyph, - hb_draw_funcs_t *dfuncs, void *draw_data) + hb_codepoint_t glyph, + hb_draw_funcs_t *dfuncs, void *draw_data) { - font->draw_glyph (glyph, dfuncs, draw_data); + (void) hb_font_draw_glyph_or_fail (font, glyph, dfuncs, draw_data); } /** @@ -1444,7 +1546,10 @@ hb_font_draw_glyph (hb_font_t *font, * @palette_index: The index of the font's color palette to use * @foreground: The foreground color, unpremultipled * - * Paints the glyph. + * Paints the glyph. This function is similar to + * hb_font_paint_glyph_or_fail(), but if painting a color glyph + * failed, it will fall back to painting an outline monochrome + * glyph. * * The painting instructions are returned by way of calls to * the callbacks of the @funcs object, with @paint_data passed @@ -1466,8 +1571,6 @@ hb_font_paint_glyph (hb_font_t *font, font->paint_glyph (glyph, pfuncs, paint_data, palette_index, foreground); } -/* A bit higher-level, and with fallback */ - /** * hb_font_get_extents_for_direction: * @font: #hb_font_t to work upon @@ -1854,10 +1957,7 @@ hb_font_create (hb_face_t *face) { hb_font_t *font = _hb_font_create (face); -#ifndef HB_NO_OT_FONT - /* Install our in-house, very lightweight, funcs. */ - hb_ot_font_set_funcs (font); -#endif + hb_font_set_funcs_using (font, nullptr); #ifndef HB_NO_VAR if (face && face->index >> 16) @@ -1880,7 +1980,8 @@ _hb_font_adopt_var_coords (hb_font_t *font, font->design_coords = design_coords; font->num_coords = coords_length; - font->mults_changed (); // Easiest to call this to drop cached data + font->changed (); + font->serial_coords = font->serial; } /** @@ -1935,7 +2036,8 @@ hb_font_create_sub_font (hb_font_t *parent) } } - font->mults_changed (); + font->changed (); + font->serial_coords = font->serial; return font; } @@ -2023,7 +2125,7 @@ hb_font_set_user_data (hb_font_t *font, hb_bool_t replace) { if (!hb_object_is_immutable (font)) - font->serial++; + font->changed (); return hb_object_set_user_data (font, key, data, destroy, replace); } @@ -2098,7 +2200,7 @@ hb_font_is_immutable (hb_font_t *font) unsigned int hb_font_get_serial (hb_font_t *font) { - return font->serial; + return font->serial.get_acquire (); } /** @@ -2117,9 +2219,7 @@ hb_font_changed (hb_font_t *font) if (hb_object_is_immutable (font)) return; - font->serial++; - - font->mults_changed (); + font->changed (); } /** @@ -2141,8 +2241,6 @@ hb_font_set_parent (hb_font_t *font, if (parent == font->parent) return; - font->serial++; - if (!parent) parent = hb_font_get_empty (); @@ -2151,6 +2249,8 @@ hb_font_set_parent (hb_font_t *font, font->parent = hb_font_reference (parent); hb_font_destroy (old); + + font->changed (); } /** @@ -2188,8 +2288,6 @@ hb_font_set_face (hb_font_t *font, if (face == font->face) return; - font->serial++; - if (unlikely (!face)) face = hb_face_get_empty (); @@ -2197,9 +2295,12 @@ hb_font_set_face (hb_font_t *font, hb_face_make_immutable (face); font->face = hb_face_reference (face); - font->mults_changed (); + font->changed (); hb_face_destroy (old); + + font->changed (); + font->serial_coords = font->serial; } /** @@ -2244,8 +2345,6 @@ hb_font_set_funcs (hb_font_t *font, return; } - font->serial++; - if (font->destroy) font->destroy (font->user_data); @@ -2257,6 +2356,8 @@ hb_font_set_funcs (hb_font_t *font, font->klass = klass; font->user_data = font_data; font->destroy = destroy; + + font->changed (); } /** @@ -2283,15 +2384,151 @@ hb_font_set_funcs_data (hb_font_t *font, return; } - font->serial++; - if (font->destroy) font->destroy (font->user_data); font->user_data = font_data; font->destroy = destroy; + + font->changed (); +} + +static struct supported_font_funcs_t { + char name[16]; + void (*func) (hb_font_t *); +} supported_font_funcs[] = +{ +#ifndef HB_NO_OT_FONT + {"ot", hb_ot_font_set_funcs}, +#endif +#ifdef HAVE_FREETYPE + {"ft", hb_ft_font_set_funcs}, +#endif +#ifdef HAVE_FONTATIONS + {"fontations",hb_fontations_font_set_funcs}, +#endif +#ifdef HAVE_CORETEXT + {"coretext", hb_coretext_font_set_funcs}, +#endif +#ifdef HAVE_DIRECTWRITE + {"directwrite",hb_directwrite_font_set_funcs}, +#endif +}; + +static const char *get_default_funcs_name () +{ + static hb_atomic_t static_funcs_name; + const char *name = static_funcs_name.get_acquire (); + if (!name) + { + name = getenv ("HB_FONT_FUNCS"); + if (!name) + name = ""; + if (!static_funcs_name.cmpexch (nullptr, name)) + name = static_funcs_name.get_acquire (); + } + return name; +} + +/** + * hb_font_set_funcs_using: + * @font: #hb_font_t to work upon + * @name: The name of the font-functions structure to use, or `NULL` + * + * Sets the font-functions structure to use for a font, based on the + * specified name. + * + * If @name is `NULL` or the empty string, the default (first) functioning font-functions + * are used. This default can be changed by setting the `HB_FONT_FUNCS` environment + * variable to the name of the desired font-functions. + * + * Return value: `true` if the font-functions was found and set, `false` otherwise + * + * Since: 11.0.0 + **/ +hb_bool_t +hb_font_set_funcs_using (hb_font_t *font, + const char *name) +{ + bool retry = false; + + if (!name || !*name) + { + name = get_default_funcs_name (); + retry = true; + } + if (name && !*name) name = nullptr; + +retry: + for (unsigned i = 0; i < ARRAY_LENGTH (supported_font_funcs); i++) + if (!name || strcmp (supported_font_funcs[i].name, name) == 0) + { + supported_font_funcs[i].func (font); + if (name || font->klass != hb_font_funcs_get_empty ()) + return true; + } + + if (retry) + { + retry = false; + name = nullptr; + goto retry; + } + + return false; } +static inline void free_static_font_funcs_list (); + +static const char * const nil_font_funcs_list[] = {nullptr}; + +static struct hb_font_funcs_list_lazy_loader_t : hb_lazy_loader_t +{ + static const char ** create () + { + const char **font_funcs_list = (const char **) hb_calloc (1 + ARRAY_LENGTH (supported_font_funcs), sizeof (const char *)); + if (unlikely (!font_funcs_list)) + return nullptr; + + unsigned i; + for (i = 0; i < ARRAY_LENGTH (supported_font_funcs); i++) + font_funcs_list[i] = supported_font_funcs[i].name; + font_funcs_list[i] = nullptr; + + hb_atexit (free_static_font_funcs_list); + + return font_funcs_list; + } + static void destroy (const char **l) + { hb_free (l); } + static const char * const * get_null () + { return nil_font_funcs_list; } +} static_font_funcs_list; + +static inline +void free_static_font_funcs_list () +{ + static_font_funcs_list.free_instance (); +} + +/** + * hb_font_list_funcs: + * + * Retrieves the list of font functions supported by HarfBuzz. + * + * Return value: (transfer none) (array zero-terminated=1): a + * `NULL`-terminated array of supported font functions + * constant strings. The returned array is owned by HarfBuzz + * and should not be modified or freed. + * + * Since: 11.0.0 + **/ +const char ** +hb_font_list_funcs () +{ + return static_font_funcs_list.get_unconst (); +} /** * hb_font_set_scale: @@ -2339,11 +2576,10 @@ hb_font_set_scale (hb_font_t *font, if (font->x_scale == x_scale && font->y_scale == y_scale) return; - font->serial++; - font->x_scale = x_scale; font->y_scale = y_scale; - font->mults_changed (); + + font->changed (); } /** @@ -2390,10 +2626,10 @@ hb_font_set_ppem (hb_font_t *font, if (font->x_ppem == x_ppem && font->y_ppem == y_ppem) return; - font->serial++; - font->x_ppem = x_ppem; font->y_ppem = y_ppem; + + font->changed (); } /** @@ -2437,9 +2673,9 @@ hb_font_set_ptem (hb_font_t *font, if (font->ptem == ptem) return; - font->serial++; - font->ptem = ptem; + + font->changed (); } /** @@ -2459,6 +2695,23 @@ hb_font_get_ptem (hb_font_t *font) return font->ptem; } +/** + * hb_font_is_synthetic: + * @font: #hb_font_t to work upon + * + * Tests whether a font is synthetic. A synthetic font is one + * that has either synthetic slant or synthetic bold set on it. + * + * Return value: `true` if the font is synthetic, `false` otherwise. + * + * XSince: REPLACEME + */ +hb_bool_t +hb_font_is_synthetic (hb_font_t *font) +{ + return font->is_synthetic (); +} + /** * hb_font_set_synthetic_bold: * @font: #hb_font_t to work upon @@ -2476,7 +2729,7 @@ hb_font_get_ptem (hb_font_t *font) * points of the glyph shape. * * Synthetic boldness is applied when rendering a glyph via - * hb_font_draw_glyph(). + * hb_font_draw_glyph_or_fail(). * * If @in_place is `false`, then glyph advance-widths are also * adjusted, otherwise they are not. The in-place mode is @@ -2499,12 +2752,11 @@ hb_font_set_synthetic_bold (hb_font_t *font, font->embolden_in_place == (bool) in_place) return; - font->serial++; - font->x_embolden = x_embolden; font->y_embolden = y_embolden; font->embolden_in_place = in_place; - font->mults_changed (); + + font->changed (); } /** @@ -2541,7 +2793,7 @@ hb_font_get_synthetic_bold (hb_font_t *font, * HarfBuzz needs to know this value to adjust shaping results, * metrics, and style values to match the slanted rendering. * - * Note: The glyph shape fetched via the hb_font_draw_glyph() + * Note: The glyph shape fetched via the hb_font_draw_glyph_or_fail() * function is slanted to reflect this value as well. * * Note: The slant value is a ratio. For example, a @@ -2558,10 +2810,9 @@ hb_font_set_synthetic_slant (hb_font_t *font, float slant) if (font->slant == slant) return; - font->serial++; - font->slant = slant; - font->mults_changed (); + + font->changed (); } /** @@ -2607,8 +2858,6 @@ hb_font_set_variations (hb_font_t *font, if (hb_object_is_immutable (font)) return; - font->serial_coords = ++font->serial; - if (!variations_length && font->instance_index == HB_FONT_NO_VAR_NAMED_INSTANCE) { hb_font_set_var_coords_normalized (font, nullptr, 0); @@ -2677,8 +2926,6 @@ hb_font_set_variation (hb_font_t *font, if (hb_object_is_immutable (font)) return; - font->serial_coords = ++font->serial; - // TODO Share some of this code with set_variations() const OT::fvar &fvar = *font->face->table.fvar; @@ -2749,8 +2996,6 @@ hb_font_set_var_coords_design (hb_font_t *font, if (hb_object_is_immutable (font)) return; - font->serial_coords = ++font->serial; - int *normalized = coords_length ? (int *) hb_calloc (coords_length, sizeof (int)) : nullptr; float *design_coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (float)) : nullptr; @@ -2787,8 +3032,6 @@ hb_font_set_var_named_instance (hb_font_t *font, if (font->instance_index == instance_index) return; - font->serial_coords = ++font->serial; - font->instance_index = instance_index; hb_font_set_variations (font, nullptr, 0); } @@ -2834,8 +3077,6 @@ hb_font_set_var_coords_normalized (hb_font_t *font, if (hb_object_is_immutable (font)) return; - font->serial_coords = ++font->serial; - int *copy = coords_length ? (int *) hb_calloc (coords_length, sizeof (coords[0])) : nullptr; int *unmapped = coords_length ? (int *) hb_calloc (coords_length, sizeof (coords[0])) : nullptr; float *design_coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (design_coords[0])) : nullptr; @@ -3058,12 +3299,134 @@ hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs, #ifndef HB_DISABLE_DEPRECATED + +struct hb_draw_glyph_closure_t +{ + hb_font_draw_glyph_func_t func; + void *user_data; + hb_destroy_func_t destroy; +}; +static hb_bool_t +hb_font_draw_glyph_trampoline (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, + void *draw_data, + void *user_data) +{ + hb_draw_glyph_closure_t *closure = (hb_draw_glyph_closure_t *) user_data; + closure->func (font, font_data, glyph, draw_funcs, draw_data, closure->user_data); + return true; +} +static void +hb_font_draw_glyph_closure_destroy (void *user_data) +{ + hb_draw_glyph_closure_t *closure = (hb_draw_glyph_closure_t *) user_data; + + if (closure->destroy) + closure->destroy (closure->user_data); + hb_free (closure); +} +static void +_hb_font_funcs_set_draw_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_draw_glyph_func_t func, + void *user_data, + hb_destroy_func_t destroy /* May be NULL. */) +{ + if (hb_object_is_immutable (ffuncs)) + { + if (destroy) + destroy (user_data); + return; + } + hb_draw_glyph_closure_t *closure = (hb_draw_glyph_closure_t *) hb_calloc (1, sizeof (hb_draw_glyph_closure_t)); + if (unlikely (!closure)) + { + if (destroy) + destroy (user_data); + return; + } + closure->func = func; + closure->user_data = user_data; + closure->destroy = destroy; + + hb_font_funcs_set_draw_glyph_or_fail_func (ffuncs, + hb_font_draw_glyph_trampoline, + closure, + hb_font_draw_glyph_closure_destroy); +} +void +hb_font_funcs_set_draw_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_draw_glyph_func_t func, + void *user_data, + hb_destroy_func_t destroy /* May be NULL. */) +{ + _hb_font_funcs_set_draw_glyph_func (ffuncs, func, user_data, destroy); +} void hb_font_funcs_set_glyph_shape_func (hb_font_funcs_t *ffuncs, hb_font_get_glyph_shape_func_t func, void *user_data, hb_destroy_func_t destroy /* May be NULL. */) { - hb_font_funcs_set_draw_glyph_func (ffuncs, func, user_data, destroy); + _hb_font_funcs_set_draw_glyph_func (ffuncs, func, user_data, destroy); +} + +struct hb_paint_glyph_closure_t +{ + hb_font_paint_glyph_func_t func; + void *user_data; + hb_destroy_func_t destroy; +}; +static hb_bool_t +hb_font_paint_glyph_trampoline (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, + void *paint_data, + unsigned int palette, + hb_color_t foreground, + void *user_data) +{ + hb_paint_glyph_closure_t *closure = (hb_paint_glyph_closure_t *) user_data; + closure->func (font, font_data, glyph, paint_funcs, paint_data, palette, foreground, closure->user_data); + return true; +} +static void +hb_font_paint_glyph_closure_destroy (void *user_data) +{ + hb_paint_glyph_closure_t *closure = (hb_paint_glyph_closure_t *) user_data; + + if (closure->destroy) + closure->destroy (closure->user_data); + hb_free (closure); +} +void +hb_font_funcs_set_paint_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_paint_glyph_func_t func, + void *user_data, + hb_destroy_func_t destroy /* May be NULL. */) +{ + if (hb_object_is_immutable (ffuncs)) + { + if (destroy) + destroy (user_data); + return; + } + hb_paint_glyph_closure_t *closure = (hb_paint_glyph_closure_t *) hb_calloc (1, sizeof (hb_paint_glyph_closure_t)); + if (unlikely (!closure)) + { + if (destroy) + destroy (user_data); + return; + } + closure->func = func; + closure->user_data = user_data; + closure->destroy = destroy; + + hb_font_funcs_set_paint_glyph_or_fail_func (ffuncs, + hb_font_paint_glyph_trampoline, + closure, + hb_font_paint_glyph_closure_destroy); } #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-font.h b/src/java.desktop/share/native/libharfbuzz/hb-font.h index f16658d9d8876..4e267ba6a80d0 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-font.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-font.h @@ -486,7 +486,7 @@ typedef hb_bool_t (*hb_font_get_glyph_from_name_func_t) (hb_font_t *font, void * void *user_data); /** - * hb_font_draw_glyph_func_t: + * hb_font_draw_glyph_or_fail_func_t: * @font: #hb_font_t to work upon * @font_data: @font user data pointer * @glyph: The glyph ID to query @@ -496,16 +496,17 @@ typedef hb_bool_t (*hb_font_get_glyph_from_name_func_t) (hb_font_t *font, void * * * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. * - * Since: 7.0.0 + * Return value: `true` if glyph was drawn, `false` otherwise * + * XSince: REPLACEME **/ -typedef void (*hb_font_draw_glyph_func_t) (hb_font_t *font, void *font_data, - hb_codepoint_t glyph, - hb_draw_funcs_t *draw_funcs, void *draw_data, - void *user_data); +typedef hb_bool_t (*hb_font_draw_glyph_or_fail_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data, + void *user_data); /** - * hb_font_paint_glyph_func_t: + * hb_font_paint_glyph_or_fail_func_t: * @font: #hb_font_t to work upon * @font_data: @font user data pointer * @glyph: The glyph ID to query @@ -517,14 +518,16 @@ typedef void (*hb_font_draw_glyph_func_t) (hb_font_t *font, void *font_data, * * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. * - * Since: 7.0.0 + * Return value: `true` if glyph was painted, `false` otherwise + * + * XSince: REPLACEME */ -typedef void (*hb_font_paint_glyph_func_t) (hb_font_t *font, void *font_data, - hb_codepoint_t glyph, - hb_paint_funcs_t *paint_funcs, void *paint_data, - unsigned int palette_index, - hb_color_t foreground, - void *user_data); +typedef hb_bool_t (*hb_font_paint_glyph_or_fail_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette_index, + hb_color_t foreground, + void *user_data); /* func setters */ @@ -785,36 +788,36 @@ hb_font_funcs_set_glyph_from_name_func (hb_font_funcs_t *ffuncs, void *user_data, hb_destroy_func_t destroy); /** - * hb_font_funcs_set_draw_glyph_func: + * hb_font_funcs_set_draw_glyph_or_fail_func: * @ffuncs: A font-function structure * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign * @user_data: Data to pass to @func * @destroy: (nullable): The function to call when @user_data is not needed anymore * - * Sets the implementation function for #hb_font_draw_glyph_func_t. + * Sets the implementation function for #hb_font_draw_glyph_or_fail_func_t. * - * Since: 7.0.0 + * XSince: REPLACEME **/ HB_EXTERN void -hb_font_funcs_set_draw_glyph_func (hb_font_funcs_t *ffuncs, - hb_font_draw_glyph_func_t func, - void *user_data, hb_destroy_func_t destroy); +hb_font_funcs_set_draw_glyph_or_fail_func (hb_font_funcs_t *ffuncs, + hb_font_draw_glyph_or_fail_func_t func, + void *user_data, hb_destroy_func_t destroy); /** - * hb_font_funcs_set_paint_glyph_func: + * hb_font_funcs_set_paint_glyph_or_fail_func: * @ffuncs: A font-function structure * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign * @user_data: Data to pass to @func * @destroy: (nullable): The function to call when @user_data is no longer needed * - * Sets the implementation function for #hb_font_paint_glyph_func_t. + * Sets the implementation function for #hb_font_paint_glyph_or_fail_func_t. * - * Since: 7.0.0 + * XSince: REPLACEME */ HB_EXTERN void -hb_font_funcs_set_paint_glyph_func (hb_font_funcs_t *ffuncs, - hb_font_paint_glyph_func_t func, - void *user_data, hb_destroy_func_t destroy); +hb_font_funcs_set_paint_glyph_or_fail_func (hb_font_funcs_t *ffuncs, + hb_font_paint_glyph_or_fail_func_t func, + void *user_data, hb_destroy_func_t destroy); /* func dispatch */ @@ -896,17 +899,17 @@ hb_font_get_glyph_from_name (hb_font_t *font, const char *name, int len, /* -1 means nul-terminated */ hb_codepoint_t *glyph); -HB_EXTERN void -hb_font_draw_glyph (hb_font_t *font, - hb_codepoint_t glyph, - hb_draw_funcs_t *dfuncs, void *draw_data); +HB_EXTERN hb_bool_t +hb_font_draw_glyph_or_fail (hb_font_t *font, + hb_codepoint_t glyph, + hb_draw_funcs_t *dfuncs, void *draw_data); -HB_EXTERN void -hb_font_paint_glyph (hb_font_t *font, - hb_codepoint_t glyph, - hb_paint_funcs_t *pfuncs, void *paint_data, - unsigned int palette_index, - hb_color_t foreground); +HB_EXTERN hb_bool_t +hb_font_paint_glyph_or_fail (hb_font_t *font, + hb_codepoint_t glyph, + hb_paint_funcs_t *pfuncs, void *paint_data, + unsigned int palette_index, + hb_color_t foreground); /* high-level funcs, with fallback */ @@ -979,6 +982,19 @@ hb_font_glyph_from_string (hb_font_t *font, const char *s, int len, /* -1 means nul-terminated */ hb_codepoint_t *glyph); +/* Older alias for hb_font_draw_glyph_or_fail() with no return value. */ +HB_EXTERN void +hb_font_draw_glyph (hb_font_t *font, + hb_codepoint_t glyph, + hb_draw_funcs_t *dfuncs, void *draw_data); + +/* Paints color glyph; if failed, draws outline glyph. */ +HB_EXTERN void +hb_font_paint_glyph (hb_font_t *font, + hb_codepoint_t glyph, + hb_paint_funcs_t *pfuncs, void *paint_data, + unsigned int palette_index, + hb_color_t foreground); /* * hb_font_t @@ -1052,6 +1068,12 @@ hb_font_set_funcs_data (hb_font_t *font, void *font_data, hb_destroy_func_t destroy); +HB_EXTERN hb_bool_t +hb_font_set_funcs_using (hb_font_t *font, + const char *name); + +HB_EXTERN const char ** +hb_font_list_funcs (void); HB_EXTERN void hb_font_set_scale (hb_font_t *font, @@ -1086,6 +1108,9 @@ hb_font_set_ptem (hb_font_t *font, float ptem); HB_EXTERN float hb_font_get_ptem (hb_font_t *font); +HB_EXTERN hb_bool_t +hb_font_is_synthetic (hb_font_t *font); + HB_EXTERN void hb_font_set_synthetic_bold (hb_font_t *font, float x_embolden, float y_embolden, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-font.hh b/src/java.desktop/share/native/libharfbuzz/hb-font.hh index c3198830cd33b..69d8d4b09dfc0 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-font.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-font.hh @@ -32,7 +32,11 @@ #include "hb.hh" #include "hb-face.hh" +#include "hb-atomic.hh" +#include "hb-draw.hh" +#include "hb-paint-extents.hh" #include "hb-shaper.hh" +#include "hb-outline.hh" /* @@ -57,8 +61,8 @@ HB_FONT_FUNC_IMPLEMENT (get_,glyph_contour_point) \ HB_FONT_FUNC_IMPLEMENT (get_,glyph_name) \ HB_FONT_FUNC_IMPLEMENT (get_,glyph_from_name) \ - HB_FONT_FUNC_IMPLEMENT (,draw_glyph) \ - HB_FONT_FUNC_IMPLEMENT (,paint_glyph) \ + HB_FONT_FUNC_IMPLEMENT (,draw_glyph_or_fail) \ + HB_FONT_FUNC_IMPLEMENT (,paint_glyph_or_fail) \ /* ^--- Add new callbacks here */ struct hb_font_funcs_t @@ -105,8 +109,8 @@ DECLARE_NULL_INSTANCE (hb_font_funcs_t); struct hb_font_t { hb_object_header_t header; - unsigned int serial; - unsigned int serial_coords; + hb_atomic_t serial; + hb_atomic_t serial_coords; hb_font_t *parent; hb_face_t *face; @@ -191,23 +195,35 @@ struct hb_font_t void scale_glyph_extents (hb_glyph_extents_t *extents) { - float x1 = em_fscale_x (extents->x_bearing); - float y1 = em_fscale_y (extents->y_bearing); - float x2 = em_fscale_x (extents->x_bearing + extents->width); - float y2 = em_fscale_y (extents->y_bearing + extents->height); + float x1 = em_scale_x (extents->x_bearing); + float y1 = em_scale_y (extents->y_bearing); + float x2 = em_scale_x (extents->x_bearing + extents->width); + float y2 = em_scale_y (extents->y_bearing + extents->height); - /* Apply slant. */ + extents->x_bearing = roundf (x1); + extents->y_bearing = roundf (y1); + extents->width = roundf (x2) - extents->x_bearing; + extents->height = roundf (y2) - extents->y_bearing; + } + + void synthetic_glyph_extents (hb_glyph_extents_t *extents) + { + /* Slant. */ if (slant_xy) { - x1 += hb_min (y1 * slant_xy, y2 * slant_xy); - x2 += hb_max (y1 * slant_xy, y2 * slant_xy); - } + hb_position_t x1 = extents->x_bearing; + hb_position_t y1 = extents->y_bearing; + hb_position_t x2 = extents->x_bearing + extents->width; + hb_position_t y2 = extents->y_bearing + extents->height; + + x1 += floorf (hb_min (y1 * slant_xy, y2 * slant_xy)); + x2 += ceilf (hb_max (y1 * slant_xy, y2 * slant_xy)); - extents->x_bearing = floorf (x1); - extents->y_bearing = floorf (y1); - extents->width = ceilf (x2) - extents->x_bearing; - extents->height = ceilf (y2) - extents->y_bearing; + extents->x_bearing = x1; + extents->width = x2 - extents->x_bearing; + } + /* Embolden. */ if (x_strength || y_strength) { /* Y */ @@ -250,19 +266,45 @@ struct hb_font_t HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT - hb_bool_t get_font_h_extents (hb_font_extents_t *extents) + hb_bool_t get_font_h_extents (hb_font_extents_t *extents, + bool synthetic = true) { hb_memset (extents, 0, sizeof (*extents)); - return klass->get.f.font_h_extents (this, user_data, - extents, - !klass->user_data ? nullptr : klass->user_data->font_h_extents); + bool ret = klass->get.f.font_h_extents (this, user_data, + extents, + !klass->user_data ? nullptr : klass->user_data->font_h_extents); + + if (synthetic && ret) + { + /* Embolden */ + int y_shift = y_scale < 0 ? -y_strength : y_strength; + extents->ascender += y_shift; + } + + return ret; } - hb_bool_t get_font_v_extents (hb_font_extents_t *extents) + hb_bool_t get_font_v_extents (hb_font_extents_t *extents, + bool synthetic = true) { hb_memset (extents, 0, sizeof (*extents)); - return klass->get.f.font_v_extents (this, user_data, - extents, - !klass->user_data ? nullptr : klass->user_data->font_v_extents); + bool ret = klass->get.f.font_v_extents (this, user_data, + extents, + !klass->user_data ? nullptr : klass->user_data->font_v_extents); + + if (synthetic && ret) + { + /* Embolden */ + int x_shift = x_scale < 0 ? -x_strength : x_strength; + if (embolden_in_place) + { + extents->ascender += x_shift / 2; + extents->descender -= x_shift - x_shift / 2; + } + else + extents->ascender += x_shift; + } + + return ret; } bool has_glyph (hb_codepoint_t unicode) @@ -303,44 +345,88 @@ struct hb_font_t !klass->user_data ? nullptr : klass->user_data->variation_glyph); } - hb_position_t get_glyph_h_advance (hb_codepoint_t glyph) + hb_position_t get_glyph_h_advance (hb_codepoint_t glyph, + bool synthetic = true) { - return klass->get.f.glyph_h_advance (this, user_data, - glyph, - !klass->user_data ? nullptr : klass->user_data->glyph_h_advance); + hb_position_t advance = klass->get.f.glyph_h_advance (this, user_data, + glyph, + !klass->user_data ? nullptr : klass->user_data->glyph_h_advance); + + if (synthetic && x_strength && !embolden_in_place) + { + /* Embolden */ + hb_position_t strength = x_scale >= 0 ? x_strength : -x_strength; + advance += advance ? strength : 0; + } + + return advance; } - hb_position_t get_glyph_v_advance (hb_codepoint_t glyph) + hb_position_t get_glyph_v_advance (hb_codepoint_t glyph, + bool synthetic = true) { - return klass->get.f.glyph_v_advance (this, user_data, - glyph, - !klass->user_data ? nullptr : klass->user_data->glyph_v_advance); + hb_position_t advance = klass->get.f.glyph_v_advance (this, user_data, + glyph, + !klass->user_data ? nullptr : klass->user_data->glyph_v_advance); + + if (synthetic && y_strength && !embolden_in_place) + { + /* Embolden */ + hb_position_t strength = y_scale >= 0 ? y_strength : -y_strength; + advance += advance ? strength : 0; + } + + return advance; } void get_glyph_h_advances (unsigned int count, const hb_codepoint_t *first_glyph, unsigned int glyph_stride, hb_position_t *first_advance, - unsigned int advance_stride) + unsigned int advance_stride, + bool synthetic = true) { - return klass->get.f.glyph_h_advances (this, user_data, - count, - first_glyph, glyph_stride, - first_advance, advance_stride, - !klass->user_data ? nullptr : klass->user_data->glyph_h_advances); + klass->get.f.glyph_h_advances (this, user_data, + count, + first_glyph, glyph_stride, + first_advance, advance_stride, + !klass->user_data ? nullptr : klass->user_data->glyph_h_advances); + + if (synthetic && x_strength && !embolden_in_place) + { + /* Embolden */ + hb_position_t strength = x_scale >= 0 ? x_strength : -x_strength; + for (unsigned int i = 0; i < count; i++) + { + *first_advance += *first_advance ? strength : 0; + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + } } void get_glyph_v_advances (unsigned int count, const hb_codepoint_t *first_glyph, unsigned int glyph_stride, hb_position_t *first_advance, - unsigned int advance_stride) + unsigned int advance_stride, + bool synthetic = true) { - return klass->get.f.glyph_v_advances (this, user_data, - count, - first_glyph, glyph_stride, - first_advance, advance_stride, - !klass->user_data ? nullptr : klass->user_data->glyph_v_advances); + klass->get.f.glyph_v_advances (this, user_data, + count, + first_glyph, glyph_stride, + first_advance, advance_stride, + !klass->user_data ? nullptr : klass->user_data->glyph_v_advances); + + if (synthetic && y_strength && !embolden_in_place) + { + /* Embolden */ + hb_position_t strength = y_scale >= 0 ? y_strength : -y_strength; + for (unsigned int i = 0; i < count; i++) + { + *first_advance += *first_advance ? strength : 0; + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + } } hb_bool_t get_glyph_h_origin (hb_codepoint_t glyph, @@ -386,23 +472,82 @@ struct hb_font_t } hb_bool_t get_glyph_extents (hb_codepoint_t glyph, - hb_glyph_extents_t *extents) + hb_glyph_extents_t *extents, + bool synthetic = true) { hb_memset (extents, 0, sizeof (*extents)); - return klass->get.f.glyph_extents (this, user_data, - glyph, - extents, - !klass->user_data ? nullptr : klass->user_data->glyph_extents); + + /* This is rather messy, but necessary. */ + + if (!synthetic) + { + return klass->get.f.glyph_extents (this, user_data, + glyph, + extents, + !klass->user_data ? nullptr : klass->user_data->glyph_extents); + } + if (!is_synthetic () && + klass->get.f.glyph_extents (this, user_data, + glyph, + extents, + !klass->user_data ? nullptr : klass->user_data->glyph_extents)) + return true; + + /* Try getting extents from paint(), then draw(), *then* get_extents() + * and apply synthetic settings in the last case. */ + + hb_paint_extents_context_t paint_extents; + if (paint_glyph_or_fail (glyph, + hb_paint_extents_get_funcs (), &paint_extents, + 0, 0)) + { + *extents = paint_extents.get_extents ().to_glyph_extents (); + return true; + } + + hb_extents_t draw_extents; + if (draw_glyph_or_fail (glyph, + hb_draw_extents_get_funcs (), &draw_extents)) + { + *extents = draw_extents.to_glyph_extents (); + return true; + } + + bool ret = klass->get.f.glyph_extents (this, user_data, + glyph, + extents, + !klass->user_data ? nullptr : klass->user_data->glyph_extents); + if (ret) + synthetic_glyph_extents (extents); + + return ret; } hb_bool_t get_glyph_contour_point (hb_codepoint_t glyph, unsigned int point_index, - hb_position_t *x, hb_position_t *y) + hb_position_t *x, hb_position_t *y, + bool synthetic = true) { *x = *y = 0; - return klass->get.f.glyph_contour_point (this, user_data, - glyph, point_index, - x, y, - !klass->user_data ? nullptr : klass->user_data->glyph_contour_point); + bool ret = klass->get.f.glyph_contour_point (this, user_data, + glyph, point_index, + x, y, + !klass->user_data ? nullptr : klass->user_data->glyph_contour_point); + + if (synthetic && ret) + { + /* Slant */ + if (slant_xy) + *x += roundf (*y * slant_xy); + + /* Embolden */ + if (!embolden_in_place) + { + int x_shift = x_scale < 0 ? -x_strength : x_strength; + *x += x_shift; + } + } + + return ret; } hb_bool_t get_glyph_name (hb_codepoint_t glyph, @@ -426,29 +571,88 @@ struct hb_font_t !klass->user_data ? nullptr : klass->user_data->glyph_from_name); } - void draw_glyph (hb_codepoint_t glyph, - hb_draw_funcs_t *draw_funcs, void *draw_data) + bool draw_glyph_or_fail (hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data, + bool synthetic = true) { - klass->get.f.draw_glyph (this, user_data, - glyph, - draw_funcs, draw_data, - !klass->user_data ? nullptr : klass->user_data->draw_glyph); +#ifndef HB_NO_OUTLINE + bool embolden = x_strength || y_strength; + bool slanted = slant_xy; + synthetic = synthetic && (embolden || slanted); +#else + synthetic = false; +#endif + + if (!synthetic) + { + return klass->get.f.draw_glyph_or_fail (this, user_data, + glyph, + draw_funcs, draw_data, + !klass->user_data ? nullptr : klass->user_data->draw_glyph_or_fail); + } + +#ifndef HB_NO_OUTLINE + + hb_outline_t outline; + if (!klass->get.f.draw_glyph_or_fail (this, user_data, + glyph, + hb_outline_recording_pen_get_funcs (), &outline, + !klass->user_data ? nullptr : klass->user_data->draw_glyph_or_fail)) + return false; + + // Slant before embolden; produces nicer results. + + if (slanted) + outline.slant (slant_xy); + + if (embolden) + { + float x_shift = embolden_in_place ? 0 : (float) x_strength / 2; + float y_shift = (float) y_strength / 2; + if (x_scale < 0) x_shift = -x_shift; + if (y_scale < 0) y_shift = -y_shift; + outline.embolden (x_strength, y_strength, x_shift, y_shift); + } + + outline.replay (draw_funcs, draw_data); + + return true; +#endif } - void paint_glyph (hb_codepoint_t glyph, - hb_paint_funcs_t *paint_funcs, void *paint_data, - unsigned int palette, - hb_color_t foreground) + bool paint_glyph_or_fail (hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette, + hb_color_t foreground, + bool synthetic = true) { - klass->get.f.paint_glyph (this, user_data, - glyph, - paint_funcs, paint_data, - palette, foreground, - !klass->user_data ? nullptr : klass->user_data->paint_glyph); + /* Slant */ + if (synthetic && slant_xy) + hb_paint_push_transform (paint_funcs, paint_data, + 1.f, 0.f, + slant_xy, 1.f, + 0.f, 0.f); + + bool ret = klass->get.f.paint_glyph_or_fail (this, user_data, + glyph, + paint_funcs, paint_data, + palette, foreground, + !klass->user_data ? nullptr : klass->user_data->paint_glyph_or_fail); + + if (synthetic && slant_xy) + hb_paint_pop_transform (paint_funcs, paint_data); + + return ret; } /* A bit higher-level, and with fallback */ + HB_INTERNAL + void paint_glyph (hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette, + hb_color_t foreground); + void get_h_extents_with_fallback (hb_font_extents_t *extents) { if (!get_font_h_extents (extents)) @@ -651,7 +855,7 @@ struct hb_font_t { if (get_glyph_name (glyph, s, size)) return; - if (size && snprintf (s, size, "gid%u", glyph) < 0) + if (size && snprintf (s, size, "gid%" PRIu32, glyph) < 0) *s = '\0'; } @@ -686,7 +890,12 @@ struct hb_font_t return false; } - void mults_changed () + bool is_synthetic () const + { + return x_embolden || y_embolden || slant; + } + + void changed () { float upem = face->get_upem (); @@ -697,12 +906,14 @@ struct hb_font_t bool y_neg = y_scale < 0; y_mult = (y_neg ? -((int64_t) -y_scale << 16) : ((int64_t) y_scale << 16)) / upem; - x_strength = fabsf (roundf (x_scale * x_embolden)); - y_strength = fabsf (roundf (y_scale * y_embolden)); + x_strength = roundf (abs (x_scale) * x_embolden); + y_strength = roundf (abs (y_scale) * y_embolden); slant_xy = y_scale ? slant * x_scale / y_scale : 0.f; data.fini (); + + serial++; } hb_position_t em_mult (int16_t v, int64_t mult) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ft.cc b/src/java.desktop/share/native/libharfbuzz/hb-ft.cc index 32f5d3012da80..e9c0e734a5335 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ft.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ft.cc @@ -41,6 +41,7 @@ #include "hb-ot-shaper-arabic-pua.hh" #include "hb-paint.hh" +#include FT_MODULE_H #include FT_ADVANCES_H #include FT_MULTIPLE_MASTERS_H #include FT_OUTLINE_H @@ -96,7 +97,7 @@ struct hb_ft_font_t mutable hb_mutex_t lock; /* Protects members below. */ FT_Face ft_face; - mutable unsigned cached_serial; + mutable hb_atomic_t cached_serial; mutable hb_ft_advance_cache_t advance_cache; }; @@ -113,7 +114,7 @@ _hb_ft_font_create (FT_Face ft_face, bool symbol, bool unref) ft_font->load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING; - ft_font->cached_serial = (unsigned) -1; + ft_font->cached_serial = UINT_MAX; new (&ft_font->advance_cache) hb_ft_advance_cache_t; return ft_font; @@ -208,9 +209,10 @@ _hb_ft_hb_font_check_changed (hb_font_t *font, { if (font->serial != ft_font->cached_serial) { + hb_lock_t lock (ft_font->lock); _hb_ft_hb_font_changed (font, ft_font->ft_face); ft_font->advance_cache.clear (); - ft_font->cached_serial = font->serial; + ft_font->cached_serial.set_release (font->serial.get_acquire ()); return true; } return false; @@ -225,7 +227,7 @@ _hb_ft_hb_font_check_changed (hb_font_t *font, * Sets the FT_Load_Glyph load flags for the specified #hb_font_t. * * For more information, see - * https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_load_xxx + * * * This function works with #hb_font_t objects created by * hb_ft_font_create() or hb_ft_font_create_referenced(). @@ -253,7 +255,7 @@ hb_ft_font_set_load_flags (hb_font_t *font, int load_flags) * Fetches the FT_Load_Glyph load flags of the specified #hb_font_t. * * For more information, see - * https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_load_xxx + * * * This function works with #hb_font_t objects created by * hb_ft_font_create() or hb_ft_font_create_referenced(). @@ -274,7 +276,7 @@ hb_ft_font_get_load_flags (hb_font_t *font) } /** - * hb_ft_font_get_face: (skip) + * hb_ft_font_get_ft_face: (skip) * @font: #hb_font_t to work upon * * Fetches the FT_Face associated with the specified #hb_font_t @@ -285,10 +287,10 @@ hb_ft_font_get_load_flags (hb_font_t *font) * * Return value: (nullable): the FT_Face found or `NULL` * - * Since: 0.9.2 + * Since: 10.4.0 **/ FT_Face -hb_ft_font_get_face (hb_font_t *font) +hb_ft_font_get_ft_face (hb_font_t *font) { if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) return nullptr; @@ -298,6 +300,31 @@ hb_ft_font_get_face (hb_font_t *font) return ft_font->ft_face; } +#ifndef HB_DISABLE_DEPRECATED + +/** + * hb_ft_font_get_face: (skip) + * @font: #hb_font_t to work upon + * + * Fetches the FT_Face associated with the specified #hb_font_t + * font object. + * + * This function works with #hb_font_t objects created by + * hb_ft_font_create() or hb_ft_font_create_referenced(). + * + * Return value: (nullable): the FT_Face found or `NULL` + * + * Since: 0.9.2 + * Deprecated: 10.4.0: Use hb_ft_font_get_ft_face() instead. + **/ +FT_Face +hb_ft_font_get_face (hb_font_t *font) +{ + return hb_ft_font_get_ft_face (font); +} + +#endif + /** * hb_ft_font_lock_face: (skip) * @font: #hb_font_t to work upon @@ -448,7 +475,8 @@ hb_ft_get_glyph_h_advances (hb_font_t* font, void* font_data, void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; - hb_position_t *orig_first_advance = first_advance; + _hb_ft_hb_font_check_changed (font, ft_font); + hb_lock_t lock (ft_font->lock); FT_Face ft_face = ft_font->ft_face; int load_flags = ft_font->load_flags; @@ -489,18 +517,6 @@ hb_ft_get_glyph_h_advances (hb_font_t* font, void* font_data, first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); } - - if (font->x_strength && !font->embolden_in_place) - { - /* Emboldening. */ - hb_position_t x_strength = font->x_scale >= 0 ? font->x_strength : -font->x_strength; - first_advance = orig_first_advance; - for (unsigned int i = 0; i < count; i++) - { - *first_advance += *first_advance ? x_strength : 0; - first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); - } - } } #ifndef HB_NO_VERTICAL @@ -511,6 +527,8 @@ hb_ft_get_glyph_v_advance (hb_font_t *font, void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + _hb_ft_hb_font_check_changed (font, ft_font); + hb_lock_t lock (ft_font->lock); FT_Fixed v; float y_mult; @@ -531,13 +549,11 @@ hb_ft_get_glyph_v_advance (hb_font_t *font, if (unlikely (FT_Get_Advance (ft_font->ft_face, glyph, ft_font->load_flags | FT_LOAD_VERTICAL_LAYOUT, &v))) return 0; - v = (int) (y_mult * v); - /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates * have a Y growing upward. Hence the extra negation. */ + v = ((-v + (1<<9)) >> 10); - hb_position_t y_strength = font->y_scale >= 0 ? font->y_strength : -font->y_strength; - return ((-v + (1<<9)) >> 10) + (font->embolden_in_place ? 0 : y_strength); + return (hb_position_t) (y_mult * v); } #endif @@ -551,6 +567,8 @@ hb_ft_get_glyph_v_origin (hb_font_t *font, void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + _hb_ft_hb_font_check_changed (font, ft_font); + hb_lock_t lock (ft_font->lock); FT_Face ft_face = ft_font->ft_face; float x_mult, y_mult; @@ -595,6 +613,8 @@ hb_ft_get_glyph_h_kerning (hb_font_t *font, void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + _hb_ft_hb_font_check_changed (font, ft_font); + hb_lock_t lock (ft_font->lock); FT_Vector kerningv; @@ -606,6 +626,41 @@ hb_ft_get_glyph_h_kerning (hb_font_t *font, } #endif +static bool +hb_ft_is_colr_glyph (hb_font_t *font, + void *font_data, + hb_codepoint_t gid) +{ +#ifndef HB_NO_PAINT +#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 21300 + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + FT_Face ft_face = ft_font->ft_face; + + + /* COLRv1 */ + FT_OpaquePaint paint = {0}; + if (FT_Get_Color_Glyph_Paint (ft_face, gid, + FT_COLOR_NO_ROOT_TRANSFORM, + &paint)) + return true; + + /* COLRv0 */ + FT_LayerIterator iterator; + FT_UInt layer_glyph_index; + FT_UInt layer_color_index; + iterator.p = NULL; + if (FT_Get_Color_Glyph_Layer (ft_face, + gid, + &layer_glyph_index, + &layer_color_index, + &iterator)) + return true; +#endif +#endif + + return false; +} + static hb_bool_t hb_ft_get_glyph_extents (hb_font_t *font, void *font_data, @@ -613,11 +668,17 @@ hb_ft_get_glyph_extents (hb_font_t *font, hb_glyph_extents_t *extents, void *user_data HB_UNUSED) { + // FreeType doesn't return COLR glyph extents. + if (hb_ft_is_colr_glyph (font, font_data, glyph)) + return false; + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + _hb_ft_hb_font_check_changed (font, ft_font); + hb_lock_t lock (ft_font->lock); FT_Face ft_face = ft_font->ft_face; float x_mult, y_mult; - float slant_xy = font->slant_xy; + #ifdef HAVE_FT_GET_TRANSFORM if (ft_font->transform) { @@ -645,33 +706,10 @@ hb_ft_get_glyph_extents (hb_font_t *font, float x2 = x1 + x_mult * ft_face->glyph->metrics.width; float y2 = y1 + y_mult * -ft_face->glyph->metrics.height; - /* Apply slant. */ - if (slant_xy) - { - x1 += hb_min (y1 * slant_xy, y2 * slant_xy); - x2 += hb_max (y1 * slant_xy, y2 * slant_xy); - } - - extents->x_bearing = floorf (x1); - extents->y_bearing = floorf (y1); - extents->width = ceilf (x2) - extents->x_bearing; - extents->height = ceilf (y2) - extents->y_bearing; - - if (font->x_strength || font->y_strength) - { - /* Y */ - int y_shift = font->y_strength; - if (font->y_scale < 0) y_shift = -y_shift; - extents->y_bearing += y_shift; - extents->height -= y_shift; - - /* X */ - int x_shift = font->x_strength; - if (font->x_scale < 0) x_shift = -x_shift; - if (font->embolden_in_place) - extents->x_bearing -= x_shift / 2; - extents->width += x_shift; - } + extents->x_bearing = roundf (x1); + extents->y_bearing = roundf (y1); + extents->width = roundf (x2) - extents->x_bearing; + extents->height = roundf (y2) - extents->y_bearing; return true; } @@ -686,6 +724,8 @@ hb_ft_get_glyph_contour_point (hb_font_t *font HB_UNUSED, void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + _hb_ft_hb_font_check_changed (font, ft_font); + hb_lock_t lock (ft_font->lock); FT_Face ft_face = ft_font->ft_face; @@ -763,6 +803,8 @@ hb_ft_get_font_h_extents (hb_font_t *font HB_UNUSED, void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + _hb_ft_hb_font_check_changed (font, ft_font); + hb_lock_t lock (ft_font->lock); FT_Face ft_face = ft_font->ft_face; float y_mult; @@ -794,7 +836,7 @@ hb_ft_get_font_h_extents (hb_font_t *font HB_UNUSED, metrics->line_gap = ft_face->size->metrics.height - (metrics->ascender - metrics->descender); } - metrics->ascender = (hb_position_t) (y_mult * (metrics->ascender + font->y_strength)); + metrics->ascender = (hb_position_t) (y_mult * metrics->ascender); metrics->descender = (hb_position_t) (y_mult * metrics->descender); metrics->line_gap = (hb_position_t) (y_mult * metrics->line_gap); @@ -845,23 +887,25 @@ _hb_ft_cubic_to (const FT_Vector *control1, return FT_Err_Ok; } -static void -hb_ft_draw_glyph (hb_font_t *font, - void *font_data, - hb_codepoint_t glyph, - hb_draw_funcs_t *draw_funcs, void *draw_data, - void *user_data HB_UNUSED) +static hb_bool_t +hb_ft_draw_glyph_or_fail (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data, + void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + _hb_ft_hb_font_check_changed (font, ft_font); + hb_lock_t lock (ft_font->lock); FT_Face ft_face = ft_font->ft_face; if (unlikely (FT_Load_Glyph (ft_face, glyph, FT_LOAD_NO_BITMAP | ft_font->load_flags))) - return; + return false; if (ft_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) - return; + return false; const FT_Outline_Funcs outline_funcs = { _hb_ft_move_to, @@ -872,43 +916,13 @@ hb_ft_draw_glyph (hb_font_t *font, 0, /* delta */ }; - hb_draw_session_t draw_session (draw_funcs, draw_data, font->slant_xy); - - /* Embolden */ - if (font->x_strength || font->y_strength) - { - FT_Outline_EmboldenXY (&ft_face->glyph->outline, font->x_strength, font->y_strength); - - int x_shift = 0; - int y_shift = 0; - if (font->embolden_in_place) - { - /* Undo the FreeType shift. */ - x_shift = -font->x_strength / 2; - y_shift = 0; - if (font->y_scale < 0) y_shift = -font->y_strength; - } - else - { - /* FreeType applied things in the wrong direction for negative scale; fix up. */ - if (font->x_scale < 0) x_shift = -font->x_strength; - if (font->y_scale < 0) y_shift = -font->y_strength; - } - if (x_shift || y_shift) - { - auto &outline = ft_face->glyph->outline; - for (auto &point : hb_iter (outline.points, outline.contours[outline.n_contours - 1] + 1)) - { - point.x += x_shift; - point.y += y_shift; - } - } - } - + hb_draw_session_t draw_session {draw_funcs, draw_data}; FT_Outline_Decompose (&ft_face->glyph->outline, &outline_funcs, &draw_session); + + return true; } #endif @@ -917,25 +931,31 @@ hb_ft_draw_glyph (hb_font_t *font, #include "hb-ft-colr.hh" -static void -hb_ft_paint_glyph (hb_font_t *font, - void *font_data, - hb_codepoint_t gid, - hb_paint_funcs_t *paint_funcs, void *paint_data, - unsigned int palette_index, - hb_color_t foreground, - void *user_data) +static hb_bool_t +hb_ft_paint_glyph_or_fail (hb_font_t *font, + void *font_data, + hb_codepoint_t gid, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette_index, + hb_color_t foreground, + void *user_data) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + _hb_ft_hb_font_check_changed (font, ft_font); + hb_lock_t lock (ft_font->lock); FT_Face ft_face = ft_font->ft_face; + FT_Long load_flags = ft_font->load_flags | FT_LOAD_COLOR; +#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 21301 + load_flags |= FT_LOAD_NO_SVG; +#endif + /* We release the lock before calling into glyph callbacks, such that * eg. draw API can call back into the face.*/ - if (unlikely (FT_Load_Glyph (ft_face, gid, - ft_font->load_flags | FT_LOAD_COLOR))) - return; + if (unlikely (FT_Load_Glyph (ft_face, gid, load_flags))) + return false; if (ft_face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) { @@ -943,26 +963,21 @@ hb_ft_paint_glyph (hb_font_t *font, paint_funcs, paint_data, palette_index, foreground, user_data)) - return; - - /* Simple outline. */ - ft_font->lock.unlock (); - paint_funcs->push_clip_glyph (paint_data, gid, font); - ft_font->lock.lock (); - paint_funcs->color (paint_data, true, foreground); - paint_funcs->pop_clip (paint_data); + return true; - return; + // Outline glyph + return false; } auto *glyph = ft_face->glyph; if (glyph->format == FT_GLYPH_FORMAT_BITMAP) { + bool ret = false; auto &bitmap = glyph->bitmap; if (bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) { if (bitmap.pitch != (signed) bitmap.width * 4) - return; + return ret; ft_font->lock.unlock (); @@ -972,27 +987,26 @@ hb_ft_paint_glyph (hb_font_t *font, nullptr, nullptr); hb_glyph_extents_t extents; - if (!hb_font_get_glyph_extents (font, gid, &extents)) + if (!font->get_glyph_extents (gid, &extents, false)) goto out; - if (!paint_funcs->image (paint_data, - blob, - bitmap.width, - bitmap.rows, - HB_PAINT_IMAGE_FORMAT_BGRA, - font->slant_xy, - &extents)) - { - /* TODO Try a forced outline load and paint? */ - } + if (paint_funcs->image (paint_data, + blob, + bitmap.width, + bitmap.rows, + HB_PAINT_IMAGE_FORMAT_BGRA, + 0.f, + &extents)) + ret = true; out: hb_blob_destroy (blob); ft_font->lock.lock (); } - return; + return ret; } + return false; } #endif #endif @@ -1012,10 +1026,8 @@ static struct hb_ft_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t= 21300 - hb_font_funcs_set_paint_glyph_func (funcs, hb_ft_paint_glyph, nullptr, nullptr); + hb_font_funcs_set_paint_glyph_or_fail_func (funcs, hb_ft_paint_glyph_or_fail, nullptr, nullptr); #endif #endif @@ -1104,6 +1115,45 @@ _hb_ft_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data buffer, hb_free); } +static unsigned +_hb_ft_get_table_tags (const hb_face_t *face HB_UNUSED, + unsigned int start_offset, + unsigned int *table_count, + hb_tag_t *table_tags, + void *user_data) +{ + FT_Face ft_face = (FT_Face) user_data; + + FT_ULong population = 0; + FT_Sfnt_Table_Info (ft_face, + 0, // table_index; ignored + nullptr, + &population); + + if (!table_count) + return population; + else + *table_count = 0; + + if (unlikely (start_offset >= population)) + return population; + + unsigned end_offset = hb_min (start_offset + *table_count, (unsigned) population); + if (unlikely (end_offset < start_offset)) + return population; + + *table_count = end_offset - start_offset; + for (unsigned i = start_offset; i < end_offset; i++) + { + FT_ULong tag = 0, length; + FT_Sfnt_Table_Info (ft_face, i, &tag, &length); + table_tags[i - start_offset] = tag; + } + + return population; +} + + /** * hb_ft_face_create: * @ft_face: (destroy destroy) (scope notified): FT_Face to work upon @@ -1145,6 +1195,7 @@ hb_ft_face_create (FT_Face ft_face, hb_blob_destroy (blob); } else { face = hb_face_create_for_tables (_hb_ft_reference_table, ft_face, destroy); + hb_face_set_get_table_tags_func (face, _hb_ft_get_table_tags, ft_face, nullptr); } hb_face_set_index (face, ft_face->face_index); @@ -1215,7 +1266,7 @@ hb_ft_face_finalize (void *arg) hb_face_t * hb_ft_face_create_cached (FT_Face ft_face) { - if (unlikely (!ft_face->generic.data || ft_face->generic.finalizer != (FT_Generic_Finalizer) hb_ft_face_finalize)) + if (unlikely (!ft_face->generic.data || ft_face->generic.finalizer != hb_ft_face_finalize)) { if (ft_face->generic.finalizer) ft_face->generic.finalizer (ft_face); @@ -1245,7 +1296,7 @@ hb_ft_face_create_cached (FT_Face ft_face) * * If you know you have valid reasons not to use hb_ft_font_create_referenced(), * then it is the client program's responsibility to destroy @ft_face - * after the #hb_font_t font object has been destroyed. + * only after the #hb_font_t font object has been destroyed. * * HarfBuzz will use the @destroy callback on the #hb_font_t font object * if it is supplied when you use this function. However, even if @destroy @@ -1349,6 +1400,10 @@ hb_ft_font_changed (hb_font_t *font) * variation-axis settings on the @font. * This call is fast if nothing has changed on @font. * + * Note that as of version 11.0.0, calling this function is not necessary, + * as HarfBuzz will automatically detect changes to the font and update + * the underlying FT_Face as needed. + * * Return value: true if changed, false otherwise * * Since: 4.4.0 @@ -1392,6 +1447,24 @@ hb_ft_font_create_referenced (FT_Face ft_face) return hb_ft_font_create (ft_face, _hb_ft_face_destroy); } + +static void * _hb_ft_alloc (FT_Memory memory, long size) +{ return hb_malloc (size); } + +static void _hb_ft_free (FT_Memory memory, void *block) +{ hb_free (block); } + +static void * _hb_ft_realloc (FT_Memory memory, long cur_size, long new_size, void *block) +{ return hb_realloc (block, new_size); } + +static FT_MemoryRec_ m = +{ + nullptr, + _hb_ft_alloc, + _hb_ft_free, + _hb_ft_realloc +}; + static inline void free_static_ft_library (); static struct hb_ft_library_lazy_loader_t : hb_lazy_loader_t, @@ -1400,16 +1473,19 @@ static struct hb_ft_library_lazy_loader_t : hb_lazy_loader_tgeneric.data); +} + +static void +destroy_ft_library (void *arg) +{ + FT_Done_Library ((FT_Library) arg); +} + +/** + * hb_ft_face_create_from_file_or_fail: + * @file_name: A font filename + * @index: The index of the face within the file + * + * Creates an #hb_face_t face object from the specified + * font file and face index. + * + * This is similar in functionality to hb_face_create_from_file_or_fail(), + * but uses the FreeType library for loading the font file. This can + * be useful, for example, to load WOFF and WOFF2 font data. + * + * Return value: (transfer full): The new face object, or `NULL` if + * no face is found at the specified index or the file cannot be read. + * + * Since: 10.1.0 + */ +hb_face_t * +hb_ft_face_create_from_file_or_fail (const char *file_name, + unsigned int index) +{ + FT_Library ft_library = reference_ft_library (); + if (unlikely (!ft_library)) + { + DEBUG_MSG (FT, ft_library, "reference_ft_library failed"); + return nullptr; + } + + FT_Face ft_face; + if (unlikely (FT_New_Face (ft_library, + file_name, + index, + &ft_face))) + return nullptr; + + hb_face_t *face = hb_ft_face_create_referenced (ft_face); + FT_Done_Face (ft_face); + + ft_face->generic.data = ft_library; + ft_face->generic.finalizer = finalize_ft_library; + + if (hb_face_is_immutable (face)) + return nullptr; + + return face; +} + +static hb_user_data_key_t ft_blob_key = {0}; + +static void +_destroy_blob (void *p) +{ + hb_blob_destroy ((hb_blob_t *) p); +} + +/** + * hb_ft_face_create_from_blob_or_fail: + * @blob: A blob + * @index: The index of the face within the blob + * + * Creates an #hb_face_t face object from the specified + * font blob and face index. + * + * This is similar in functionality to hb_face_create_from_blob_or_fail(), + * but uses the FreeType library for loading the font blob. This can + * be useful, for example, to load WOFF and WOFF2 font data. + * + * Return value: (transfer full): The new face object, or `NULL` if + * loading fails (eg. blob does not contain valid font data). + * + * Since: 11.0.0 + */ +hb_face_t * +hb_ft_face_create_from_blob_or_fail (hb_blob_t *blob, + unsigned int index) +{ + FT_Library ft_library = reference_ft_library (); + if (unlikely (!ft_library)) + { + DEBUG_MSG (FT, ft_library, "reference_ft_library failed"); + return nullptr; + } + + hb_blob_make_immutable (blob); + unsigned blob_size; + const char *blob_data = hb_blob_get_data (blob, &blob_size); + + FT_Face ft_face; + if (unlikely (FT_New_Memory_Face (ft_library, + (const FT_Byte *) blob_data, + blob_size, + index, + &ft_face))) + return nullptr; + + hb_face_t *face = hb_ft_face_create_referenced (ft_face); + FT_Done_Face (ft_face); + + ft_face->generic.data = ft_library; + ft_face->generic.finalizer = finalize_ft_library; + + if (hb_face_is_immutable (face)) + return nullptr; + + // Hook the blob to the hb_face_t, since FT_Face still needs it. + hb_blob_reference (blob); + if (!hb_face_set_user_data (face, &ft_blob_key, blob, _destroy_blob, true)) + { + hb_blob_destroy (blob); + hb_face_destroy (face); + return nullptr; + } + + return face; } static void @@ -1465,32 +1678,47 @@ _release_blob (void *arg) void hb_ft_font_set_funcs (hb_font_t *font) { + // In case of failure... + hb_font_set_funcs (font, + hb_font_funcs_get_empty (), + nullptr, nullptr); + hb_blob_t *blob = hb_face_reference_blob (font->face); unsigned int blob_length; const char *blob_data = hb_blob_get_data (blob, &blob_length); if (unlikely (!blob_length)) DEBUG_MSG (FT, font, "Font face has empty blob"); - FT_Face ft_face = nullptr; - FT_Error err = FT_New_Memory_Face (get_ft_library (), - (const FT_Byte *) blob_data, - blob_length, - hb_face_get_index (font->face), - &ft_face); + FT_Library ft_library = reference_ft_library (); + if (unlikely (!ft_library)) + { + hb_blob_destroy (blob); + DEBUG_MSG (FT, font, "reference_ft_library failed"); + return; + } - if (unlikely (err)) { + FT_Face ft_face = nullptr; + if (unlikely (FT_New_Memory_Face (ft_library, + (const FT_Byte *) blob_data, + blob_length, + hb_face_get_index (font->face), + &ft_face))) + { hb_blob_destroy (blob); - DEBUG_MSG (FT, font, "Font face FT_New_Memory_Face() failed"); + DEBUG_MSG (FT, font, "FT_New_Memory_Face() failed"); return; } if (FT_Select_Charmap (ft_face, FT_ENCODING_MS_SYMBOL)) FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE); - + // Hook the blob to the FT_Face ft_face->generic.data = blob; ft_face->generic.finalizer = _release_blob; + // And the FT_Library to the blob + hb_blob_set_user_data (blob, &ft_library_key, ft_library, destroy_ft_library, true); + _hb_ft_font_set_funcs (font, ft_face, true); hb_ft_font_set_load_flags (font, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ft.h b/src/java.desktop/share/native/libharfbuzz/hb-ft.h index b2d148014c06a..9f83b91abb642 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ft.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-ft.h @@ -84,6 +84,13 @@ hb_ft_face_create_cached (FT_Face ft_face); HB_EXTERN hb_face_t * hb_ft_face_create_referenced (FT_Face ft_face); +HB_EXTERN hb_face_t * +hb_ft_face_create_from_file_or_fail (const char *file_name, + unsigned int index); + +HB_EXTERN hb_face_t * +hb_ft_face_create_from_blob_or_fail (hb_blob_t *blob, + unsigned int index); /* * hb-font from ft-face. @@ -108,7 +115,7 @@ HB_EXTERN hb_font_t * hb_ft_font_create_referenced (FT_Face ft_face); HB_EXTERN FT_Face -hb_ft_font_get_face (hb_font_t *font); +hb_ft_font_get_ft_face (hb_font_t *font); HB_EXTERN FT_Face hb_ft_font_lock_face (hb_font_t *font); @@ -139,6 +146,13 @@ hb_ft_hb_font_changed (hb_font_t *font); HB_EXTERN void hb_ft_font_set_funcs (hb_font_t *font); +#ifndef HB_DISABLE_DEPRECATED + +HB_DEPRECATED_FOR (hb_ft_font_get_ft_face) +HB_EXTERN FT_Face +hb_ft_font_get_face (hb_font_t *font); + +#endif HB_END_DECLS diff --git a/src/java.desktop/share/native/libharfbuzz/hb-geometry.hh b/src/java.desktop/share/native/libharfbuzz/hb-geometry.hh new file mode 100644 index 0000000000000..795f29f6ab084 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-geometry.hh @@ -0,0 +1,321 @@ +/* + * Copyright © 2022 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ +#ifndef HB_GEOMETRY_HH +#define HB_GEOMETRY_HH + +#include "hb.hh" + + +struct hb_extents_t +{ + hb_extents_t () {} + hb_extents_t (const hb_glyph_extents_t &extents) : + xmin (hb_min (extents.x_bearing, extents.x_bearing + extents.width)), + ymin (hb_min (extents.y_bearing, extents.y_bearing + extents.height)), + xmax (hb_max (extents.x_bearing, extents.x_bearing + extents.width)), + ymax (hb_max (extents.y_bearing, extents.y_bearing + extents.height)) {} + hb_extents_t (float xmin, float ymin, float xmax, float ymax) : + xmin (xmin), ymin (ymin), xmax (xmax), ymax (ymax) {} + + bool is_empty () const { return xmin >= xmax || ymin >= ymax; } + bool is_void () const { return xmin > xmax; } + + void union_ (const hb_extents_t &o) + { + if (o.is_empty ()) return; + if (is_empty ()) + { + *this = o; + return; + } + xmin = hb_min (xmin, o.xmin); + ymin = hb_min (ymin, o.ymin); + xmax = hb_max (xmax, o.xmax); + ymax = hb_max (ymax, o.ymax); + } + + void intersect (const hb_extents_t &o) + { + if (o.is_empty () || is_empty ()) + { + *this = hb_extents_t {}; + return; + } + xmin = hb_max (xmin, o.xmin); + ymin = hb_max (ymin, o.ymin); + xmax = hb_min (xmax, o.xmax); + ymax = hb_min (ymax, o.ymax); + } + + void + add_point (float x, float y) + { + if (unlikely (is_void ())) + { + xmin = xmax = x; + ymin = ymax = y; + } + else + { + xmin = hb_min (xmin, x); + ymin = hb_min (ymin, y); + xmax = hb_max (xmax, x); + ymax = hb_max (ymax, y); + } + } + + hb_glyph_extents_t to_glyph_extents (bool xneg = false, bool yneg = false) const + { + hb_position_t x0 = (hb_position_t) roundf (xmin); + hb_position_t y0 = (hb_position_t) roundf (ymin); + hb_position_t x1 = (hb_position_t) roundf (xmax); + hb_position_t y1 = (hb_position_t) roundf (ymax); + return hb_glyph_extents_t {xneg ? x1 : x0, + yneg ? y0 : y1, + xneg ? x0 - x1 : x1 - x0, + yneg ? y1 - y0 : y0 - y1}; + } + + float xmin = 0.f; + float ymin = 0.f; + float xmax = -1.f; + float ymax = -1.f; +}; + +struct hb_transform_t +{ + hb_transform_t () {} + hb_transform_t (float xx, float yx, + float xy, float yy, + float x0, float y0) : + xx (xx), yx (yx), xy (xy), yy (yy), x0 (x0), y0 (y0) {} + + bool is_identity () const + { + return xx == 1.f && yx == 0.f && + xy == 0.f && yy == 1.f && + x0 == 0.f && y0 == 0.f; + } + + void multiply (const hb_transform_t &o) + { + /* Copied from cairo, with "o" being "a" there and "this" being "b" there. */ + hb_transform_t r; + + r.xx = o.xx * xx + o.yx * xy; + r.yx = o.xx * yx + o.yx * yy; + + r.xy = o.xy * xx + o.yy * xy; + r.yy = o.xy * yx + o.yy * yy; + + r.x0 = o.x0 * xx + o.y0 * xy + x0; + r.y0 = o.x0 * yx + o.y0 * yy + y0; + + *this = r; + } + + void transform_distance (float &dx, float &dy) const + { + float new_x = xx * dx + xy * dy; + float new_y = yx * dx + yy * dy; + dx = new_x; + dy = new_y; + } + + void transform_point (float &x, float &y) const + { + transform_distance (x, y); + x += x0; + y += y0; + } + + void transform_extents (hb_extents_t &extents) const + { + float quad_x[4], quad_y[4]; + + quad_x[0] = extents.xmin; + quad_y[0] = extents.ymin; + quad_x[1] = extents.xmin; + quad_y[1] = extents.ymax; + quad_x[2] = extents.xmax; + quad_y[2] = extents.ymin; + quad_x[3] = extents.xmax; + quad_y[3] = extents.ymax; + + extents = hb_extents_t {}; + for (unsigned i = 0; i < 4; i++) + { + transform_point (quad_x[i], quad_y[i]); + extents.add_point (quad_x[i], quad_y[i]); + } + } + + void transform (const hb_transform_t &o) { multiply (o); } + + void translate (float x, float y) + { + if (x == 0.f && y == 0.f) + return; + + x0 += xx * x + xy * y; + y0 += yx * x + yy * y; + } + + void scale (float scaleX, float scaleY) + { + if (scaleX == 1.f && scaleY == 1.f) + return; + + xx *= scaleX; + yx *= scaleX; + xy *= scaleY; + yy *= scaleY; + } + + void rotate (float rotation) + { + if (rotation == 0.f) + return; + + // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L240 + rotation = rotation * HB_PI; + float c; + float s; +#ifdef HAVE_SINCOSF + sincosf (rotation, &s, &c); +#else + c = cosf (rotation); + s = sinf (rotation); +#endif + auto other = hb_transform_t{c, s, -s, c, 0.f, 0.f}; + transform (other); + } + + void skew (float skewX, float skewY) + { + if (skewX == 0.f && skewY == 0.f) + return; + + // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L255 + skewX = skewX * HB_PI; + skewY = skewY * HB_PI; + auto other = hb_transform_t{1.f, + skewY ? tanf (skewY) : 0.f, + skewX ? tanf (skewX) : 0.f, + 1.f, + 0.f, 0.f}; + transform (other); + } + + float xx = 1.f; + float yx = 0.f; + float xy = 0.f; + float yy = 1.f; + float x0 = 0.f; + float y0 = 0.f; +}; + +#define HB_TRANSFORM_IDENTITY hb_transform_t{1.f, 0.f, 0.f, 1.f, 0.f, 0.f} + +struct hb_bounds_t +{ + enum status_t { + UNBOUNDED, + BOUNDED, + EMPTY, + }; + + hb_bounds_t (status_t status = UNBOUNDED) : status (status) {} + hb_bounds_t (const hb_extents_t &extents) : + status (extents.is_empty () ? EMPTY : BOUNDED), extents (extents) {} + + void union_ (const hb_bounds_t &o) + { + if (o.status == UNBOUNDED) + status = UNBOUNDED; + else if (o.status == BOUNDED) + { + if (status == EMPTY) + *this = o; + else if (status == BOUNDED) + extents.union_ (o.extents); + } + } + + void intersect (const hb_bounds_t &o) + { + if (o.status == EMPTY) + status = EMPTY; + else if (o.status == BOUNDED) + { + if (status == UNBOUNDED) + *this = o; + else if (status == BOUNDED) + { + extents.intersect (o.extents); + if (extents.is_empty ()) + status = EMPTY; + } + } + } + + status_t status; + hb_extents_t extents; +}; + +struct hb_transform_decomposed_t +{ + float translateX = 0; + float translateY = 0; + float rotation = 0; // in degrees, counter-clockwise + float scaleX = 1; + float scaleY = 1; + float skewX = 0; // in degrees, counter-clockwise + float skewY = 0; // in degrees, counter-clockwise + float tCenterX = 0; + float tCenterY = 0; + + operator bool () const + { + return translateX || translateY || + rotation || + scaleX != 1 || scaleY != 1 || + skewX || skewY || + tCenterX || tCenterY; + } + + hb_transform_t to_transform () const + { + hb_transform_t t; + t.translate (translateX + tCenterX, translateY + tCenterY); + t.rotate (rotation); + t.scale (scaleX, scaleY); + t.skew (-skewX, skewY); + t.translate (-tCenterX, -tCenterY); + return t; + } +}; + + +#endif /* HB_GEOMETRY_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-iter.hh b/src/java.desktop/share/native/libharfbuzz/hb-iter.hh index ad45dcf2c1d4b..21d9544a74b0e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-iter.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-iter.hh @@ -324,6 +324,16 @@ struct hb_is_sink_of (hb_is_source_of(Iter, Item) && Iter::is_sorted_iterator) +struct +{ + template + unsigned operator () (const Iterable &_) const { return hb_len (hb_iter (_)); } + + unsigned operator () (unsigned _) const { return _; } +} +HB_FUNCOBJ (hb_len_of); + /* Range-based 'for' for iterables. */ template private: /* Must only have one pointer. */ - hb_atomic_ptr_t instance; + mutable hb_atomic_t instance; }; /* Specializations. */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-map.hh b/src/java.desktop/share/native/libharfbuzz/hb-map.hh index d31323733cc5a..76206342f5774 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-map.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-map.hh @@ -42,13 +42,37 @@ template struct hb_hashmap_t { + static constexpr bool realloc_move = true; + hb_hashmap_t () { init (); } ~hb_hashmap_t () { fini (); } - hb_hashmap_t (const hb_hashmap_t& o) : hb_hashmap_t () { alloc (o.population); hb_copy (o, *this); } - hb_hashmap_t (hb_hashmap_t&& o) : hb_hashmap_t () { hb_swap (*this, o); } + hb_hashmap_t (const hb_hashmap_t& o) : hb_hashmap_t () + { + if (unlikely (!o.mask)) return; + + if (item_t::is_trivial) + { + items = (item_t *) hb_malloc (sizeof (item_t) * (o.mask + 1)); + if (unlikely (!items)) + { + successful = false; + return; + } + population = o.population; + occupancy = o.occupancy; + mask = o.mask; + prime = o.prime; + max_chain_length = o.max_chain_length; + memcpy (items, o.items, sizeof (item_t) * (mask + 1)); + return; + } + + alloc (o.population); hb_copy (o, *this); + } + hb_hashmap_t (hb_hashmap_t&& o) noexcept : hb_hashmap_t () { hb_swap (*this, o); } hb_hashmap_t& operator= (const hb_hashmap_t& o) { reset (); alloc (o.population); hb_copy (o, *this); return *this; } - hb_hashmap_t& operator= (hb_hashmap_t&& o) { hb_swap (*this, o); return *this; } + hb_hashmap_t& operator= (hb_hashmap_t&& o) noexcept { hb_swap (*this, o); return *this; } hb_hashmap_t (std::initializer_list> lst) : hb_hashmap_t () { @@ -113,26 +137,23 @@ struct hb_hashmap_t }; hb_object_header_t header; - unsigned int successful : 1; /* Allocations successful */ - unsigned int population : 31; /* Not including tombstones. */ + bool successful; /* Allocations successful */ + unsigned short max_chain_length; + unsigned int population; /* Not including tombstones. */ unsigned int occupancy; /* Including tombstones. */ unsigned int mask; unsigned int prime; - unsigned int max_chain_length; item_t *items; - friend void swap (hb_hashmap_t& a, hb_hashmap_t& b) + friend void swap (hb_hashmap_t& a, hb_hashmap_t& b) noexcept { if (unlikely (!a.successful || !b.successful)) return; - unsigned tmp = a.population; - a.population = b.population; - b.population = tmp; - //hb_swap (a.population, b.population); + hb_swap (a.max_chain_length, b.max_chain_length); + hb_swap (a.population, b.population); hb_swap (a.occupancy, b.occupancy); hb_swap (a.mask, b.mask); hb_swap (a.prime, b.prime); - hb_swap (a.max_chain_length, b.max_chain_length); hb_swap (a.items, b.items); } void init () @@ -140,10 +161,10 @@ struct hb_hashmap_t hb_object_init (this); successful = true; + max_chain_length = 0; population = occupancy = 0; mask = 0; prime = 0; - max_chain_length = 0; items = nullptr; } void fini () @@ -209,9 +230,10 @@ struct hb_hashmap_t old_items[i].hash, std::move (old_items[i].value)); } - if (!item_t::is_trivial) - old_items[i].~item_t (); } + if (!item_t::is_trivial) + for (unsigned int i = 0; i < old_size; i++) + old_items[i].~item_t (); hb_free (old_items); @@ -285,7 +307,7 @@ struct hb_hashmap_t const V& get_with_hash (const K &key, uint32_t hash) const { if (!items) return item_t::default_value (); - auto *item = fetch_item (key, hb_hash (key)); + auto *item = fetch_item (key, hash); if (item) return item->value; return item_t::default_value (); @@ -533,7 +555,7 @@ struct hb_map_t : hb_hashmap_t lst) : hashmap (lst) {} diff --git a/src/java.desktop/share/native/libharfbuzz/hb-mutex.hh b/src/java.desktop/share/native/libharfbuzz/hb-mutex.hh index 72012bd2e641e..4b6c5a118aeb7 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-mutex.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-mutex.hh @@ -99,6 +99,8 @@ struct hb_mutex_t hb_mutex_t () { init (); } ~hb_mutex_t () { fini (); } + hb_mutex_t (const hb_mutex_t &) = delete; + hb_mutex_t &operator= (const hb_mutex_t &) = delete; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" @@ -114,6 +116,10 @@ struct hb_lock_t hb_lock_t (hb_mutex_t &mutex_) : mutex (&mutex_) { mutex->lock (); } hb_lock_t (hb_mutex_t *mutex_) : mutex (mutex_) { if (mutex) mutex->lock (); } ~hb_lock_t () { if (mutex) mutex->unlock (); } + + hb_lock_t (const hb_lock_t &) = delete; + hb_lock_t &operator= (const hb_lock_t &) = delete; + private: hb_mutex_t *mutex; }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-null.hh b/src/java.desktop/share/native/libharfbuzz/hb-null.hh index 4a5270e340025..63ec9a7c64d07 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-null.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-null.hh @@ -176,7 +176,7 @@ template static inline Type& Crap () { static_assert (hb_null_size (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE."); Type *obj = reinterpret_cast (_hb_CrapPool); - memcpy (obj, std::addressof (Null (Type)), sizeof (*obj)); + memcpy (reinterpret_cast(obj), std::addressof (Null (Type)), sizeof (*obj)); return *obj; } template diff --git a/src/java.desktop/share/native/libharfbuzz/hb-object.hh b/src/java.desktop/share/native/libharfbuzz/hb-object.hh index ef675f835f119..fa9a249b2e234 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-object.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-object.hh @@ -142,7 +142,7 @@ struct hb_lockable_set_t struct hb_reference_count_t { - mutable hb_atomic_int_t ref_count; + mutable hb_atomic_t ref_count; void init (int v = 1) { ref_count = v; } int get_relaxed () const { return ref_count; } @@ -213,8 +213,8 @@ struct hb_user_data_array_t struct hb_object_header_t { hb_reference_count_t ref_count; - mutable hb_atomic_int_t writable = 0; - hb_atomic_ptr_t user_data; + mutable hb_atomic_t writable = false; + hb_atomic_t user_data; bool is_inert () const { return !ref_count.get_relaxed (); } }; @@ -325,7 +325,7 @@ retry: hb_user_data_array_t *user_data = obj->header.user_data.get_acquire (); if (unlikely (!user_data)) { - user_data = (hb_user_data_array_t *) hb_calloc (sizeof (hb_user_data_array_t), 1); + user_data = (hb_user_data_array_t *) hb_calloc (1, sizeof (hb_user_data_array_t)); if (unlikely (!user_data)) return false; user_data->init (); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-open-file.hh b/src/java.desktop/share/native/libharfbuzz/hb-open-file.hh index 387b1430ac1ec..382ee2ddaf24c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-open-file.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-open-file.hh @@ -250,7 +250,7 @@ struct TTCHeader { switch (u.header.version.major) { case 2: /* version 2 is compatible with version 1 */ - case 1: return u.version1.get_face_count (); + case 1: hb_barrier (); return u.version1.get_face_count (); default:return 0; } } @@ -258,7 +258,7 @@ struct TTCHeader { switch (u.header.version.major) { case 2: /* version 2 is compatible with version 1 */ - case 1: return u.version1.get_face (i); + case 1: hb_barrier (); return u.version1.get_face (i); default:return Null (OpenTypeFontFace); } } @@ -267,9 +267,10 @@ struct TTCHeader { TRACE_SANITIZE (this); if (unlikely (!u.header.version.sanitize (c))) return_trace (false); + hb_barrier (); switch (u.header.version.major) { case 2: /* version 2 is compatible with version 1 */ - case 1: return_trace (u.version1.sanitize (c)); + case 1: hb_barrier (); return_trace (u.version1.sanitize (c)); default:return_trace (true); } } @@ -302,6 +303,7 @@ struct ResourceRecord TRACE_SANITIZE (this); return_trace (c->check_struct (this) && offset.sanitize (c, data_base) && + hb_barrier () && get_face (data_base).sanitize (c)); } @@ -337,6 +339,7 @@ struct ResourceTypeRecord { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && resourcesZ.sanitize (c, type_base, get_resource_count (), data_base)); @@ -385,6 +388,7 @@ struct ResourceMap { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && typeList.sanitize (c, this, &(this+typeList), data_base)); @@ -428,6 +432,7 @@ struct ResourceForkHeader { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && data.sanitize (c, this, dataLen) && map.sanitize (c, this, &(this+data))); } @@ -508,6 +513,7 @@ struct OpenTypeFontFile { TRACE_SANITIZE (this); if (unlikely (!u.tag.sanitize (c))) return_trace (false); + hb_barrier (); switch (u.tag) { case CFFTag: /* All the non-collection tags */ case TrueTag: diff --git a/src/java.desktop/share/native/libharfbuzz/hb-open-type.hh b/src/java.desktop/share/native/libharfbuzz/hb-open-type.hh index 25142da44a6cb..75d87f11ea589 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-open-type.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-open-type.hh @@ -86,21 +86,12 @@ struct IntType return pb->cmp (*pa); } - template ::value && - sizeof (Type2) < sizeof (int) && - sizeof (Type) < sizeof (int))> - int cmp (Type2 a) const - { - Type b = v; - return (int) a - (int) b; - } template int cmp (Type2 a) const { Type b = v; - return a < b ? -1 : a == b ? 0 : +1; + return (a > b) - (a < b); } bool sanitize (hb_sanitize_context_t *c) const { @@ -132,6 +123,89 @@ struct HBUINT15 : HBUINT16 DEFINE_SIZE_STATIC (2); }; +/* 32-bit unsigned integer with variable encoding. */ +struct HBUINT32VAR +{ + unsigned get_size () const + { + unsigned b0 = v[0]; + if (b0 < 0x80) + return 1; + else if (b0 < 0xC0) + return 2; + else if (b0 < 0xE0) + return 3; + else if (b0 < 0xF0) + return 4; + else + return 5; + } + + static unsigned get_size (uint32_t v) + { + if (v < 0x80) + return 1; + else if (v < 0x4000) + return 2; + else if (v < 0x200000) + return 3; + else if (v < 0x10000000) + return 4; + else + return 5; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_range (v, 1) && + hb_barrier () && + c->check_range (v, get_size ())); + } + + operator uint32_t () const + { + unsigned b0 = v[0]; + if (b0 < 0x80) + return b0; + else if (b0 < 0xC0) + return ((b0 & 0x3F) << 8) | v[1]; + else if (b0 < 0xE0) + return ((b0 & 0x1F) << 16) | (v[1] << 8) | v[2]; + else if (b0 < 0xF0) + return ((b0 & 0x0F) << 24) | (v[1] << 16) | (v[2] << 8) | v[3]; + else + return (v[1] << 24) | (v[2] << 16) | (v[3] << 8) | v[4]; + } + + static bool serialize (hb_serialize_context_t *c, uint32_t v) + { + unsigned len = get_size (v); + + unsigned char *buf = c->allocate_size (len, false); + if (unlikely (!buf)) + return false; + + unsigned char *p = buf + len; + for (unsigned i = 0; i < len; i++) + { + *--p = v & 0xFF; + v >>= 8; + } + + if (len > 1) + buf[0] |= ((1 << (len - 1)) - 1) << (9 - len); + + return true; + } + + protected: + unsigned char v[5]; + + public: + DEFINE_SIZE_MIN (1); +}; + /* 16-bit signed integer (HBINT16) that describes a quantity in FUnits. */ typedef HBINT16 FWORD; @@ -149,6 +223,7 @@ struct HBFixed : Type operator signed () const = delete; operator unsigned () const = delete; + explicit operator float () const { return to_float (); } typename Type::type to_int () const { return Type::v; } void set_int (typename Type::type i ) { Type::v = i; } float to_float (float offset = 0) const { return ((int32_t) Type::v + offset) / shift; } @@ -215,11 +290,6 @@ typedef Index NameID; struct VarIdx : HBUINT32 { static constexpr unsigned NO_VARIATION = 0xFFFFFFFFu; static_assert (NO_VARIATION == HB_OT_LAYOUT_NO_VARIATIONS_INDEX, ""); - static uint32_t add (uint32_t i, unsigned short v) - { - if (i == NO_VARIATION) return i; - return i + v; - } VarIdx& operator = (uint32_t i) { HBUINT32::operator= (i); return *this; } }; DECLARE_NULL_NAMESPACE_BYTES (OT, VarIdx); @@ -309,7 +379,7 @@ struct _hb_has_null static Type *get_crap () { return &Crap (Type); } }; -template +template struct OffsetTo : Offset { using target_t = Type; @@ -335,22 +405,22 @@ struct OffsetTo : Offset } template + hb_enable_if (hb_is_convertible (const Base, const BaseType *))> friend const Type& operator + (const Base &base, const OffsetTo &offset) { return offset ((const void *) base); } template + hb_enable_if (hb_is_convertible (const Base, const BaseType *))> friend const Type& operator + (const OffsetTo &offset, const Base &base) { return offset ((const void *) base); } template + hb_enable_if (hb_is_convertible (Base, BaseType *))> friend Type& operator + (Base &&base, OffsetTo &offset) { return offset ((void *) base); } template + hb_enable_if (hb_is_convertible (Base, BaseType *))> friend Type& operator + (OffsetTo &offset, Base &&base) { return offset ((void *) base); } - template + template bool serialize_subset (hb_subset_context_t *c, const OffsetTo& src, - const void *src_base, Ts&&... ds) + const Base *src_base, Ts&&... ds) { *this = 0; if (src.is_null ()) @@ -414,10 +484,11 @@ struct OffsetTo : Offset const void *src_base, unsigned dst_bias = 0) { return serialize_copy (c, src, src_base, dst_bias, hb_serialize_context_t::Head); } - bool sanitize_shallow (hb_sanitize_context_t *c, const void *base) const + bool sanitize_shallow (hb_sanitize_context_t *c, const BaseType *base) const { TRACE_SANITIZE (this); if (unlikely (!c->check_struct (this))) return_trace (false); + hb_barrier (); //if (unlikely (this->is_null ())) return_trace (true); if (unlikely ((const char *) base + (unsigned) *this < (const char *) base)) return_trace (false); return_trace (true); @@ -427,10 +498,11 @@ struct OffsetTo : Offset #ifndef HB_OPTIMIZE_SIZE HB_ALWAYS_INLINE #endif - bool sanitize (hb_sanitize_context_t *c, const void *base, Ts&&... ds) const + bool sanitize (hb_sanitize_context_t *c, const BaseType *base, Ts&&... ds) const { TRACE_SANITIZE (this); return_trace (sanitize_shallow (c, base) && + hb_barrier () && (this->is_null () || c->dispatch (StructAtOffset (base, *this), std::forward (ds)...) || neuter (c))); @@ -445,14 +517,14 @@ struct OffsetTo : Offset DEFINE_SIZE_STATIC (sizeof (OffsetType)); }; /* Partial specializations. */ -template using Offset16To = OffsetTo; -template using Offset24To = OffsetTo; -template using Offset32To = OffsetTo; +template using Offset16To = OffsetTo; +template using Offset24To = OffsetTo; +template using Offset32To = OffsetTo; -template using NNOffsetTo = OffsetTo; -template using NNOffset16To = Offset16To; -template using NNOffset24To = Offset24To; -template using NNOffset32To = Offset32To; +template using NNOffsetTo = OffsetTo; +template using NNOffset16To = Offset16To; +template using NNOffset24To = Offset24To; +template using NNOffset32To = Offset32To; /* @@ -536,6 +608,7 @@ struct UnsizedArrayOf TRACE_SANITIZE (this); if (unlikely (!sanitize_shallow (c, count))) return_trace (false); if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true); + hb_barrier (); for (unsigned int i = 0; i < count; i++) if (unlikely (!c->dispatch (arrayZ[i], std::forward (ds)...))) return_trace (false); @@ -555,27 +628,27 @@ struct UnsizedArrayOf }; /* Unsized array of offset's */ -template -using UnsizedArray16OfOffsetTo = UnsizedArrayOf>; +template +using UnsizedArray16OfOffsetTo = UnsizedArrayOf>; /* Unsized array of offsets relative to the beginning of the array itself. */ -template -struct UnsizedListOfOffset16To : UnsizedArray16OfOffsetTo +template +struct UnsizedListOfOffset16To : UnsizedArray16OfOffsetTo { const Type& operator [] (int i_) const { unsigned int i = (unsigned int) i_; - const OffsetTo *p = &this->arrayZ[i]; + const OffsetTo *p = &this->arrayZ[i]; if (unlikely ((const void *) p < (const void *) this->arrayZ)) return Null (Type); /* Overflowed. */ - _hb_compiler_memory_r_barrier (); + hb_barrier (); return this+*p; } Type& operator [] (int i_) { unsigned int i = (unsigned int) i_; - const OffsetTo *p = &this->arrayZ[i]; + const OffsetTo *p = &this->arrayZ[i]; if (unlikely ((const void *) p < (const void *) this->arrayZ)) return Crap (Type); /* Overflowed. */ - _hb_compiler_memory_r_barrier (); + hb_barrier (); return this+*p; } @@ -583,7 +656,7 @@ struct UnsizedListOfOffset16To : UnsizedArray16OfOffsetTo + return_trace ((UnsizedArray16OfOffsetTo ::sanitize (c, count, this, std::forward (ds)...))); } }; @@ -626,14 +699,14 @@ struct ArrayOf { unsigned int i = (unsigned int) i_; if (unlikely (i >= len)) return Null (Type); - _hb_compiler_memory_r_barrier (); + hb_barrier (); return arrayZ[i]; } Type& operator [] (int i_) { unsigned int i = (unsigned int) i_; if (unlikely (i >= len)) return Crap (Type); - _hb_compiler_memory_r_barrier (); + hb_barrier (); return arrayZ[i]; } @@ -725,6 +798,7 @@ struct ArrayOf TRACE_SANITIZE (this); if (unlikely (!sanitize_shallow (c))) return_trace (false); if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true); + hb_barrier (); unsigned int count = len; for (unsigned int i = 0; i < count; i++) if (unlikely (!c->dispatch (arrayZ[i], std::forward (ds)...))) @@ -735,7 +809,9 @@ struct ArrayOf bool sanitize_shallow (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (len.sanitize (c) && c->check_array_sized (arrayZ, len, sizeof (LenType))); + return_trace (len.sanitize (c) && + hb_barrier () && + c->check_array_sized (arrayZ, len, sizeof (LenType))); } public: @@ -750,6 +826,7 @@ template using Array32Of = ArrayOf; using PString = ArrayOf; /* Array of Offset's */ +template using Array8OfOffset24To = ArrayOf, HBUINT8>; template using Array16OfOffset16To = ArrayOf, HBUINT16>; template using Array16OfOffset32To = ArrayOf, HBUINT16>; template using Array32OfOffset32To = ArrayOf, HBUINT32>; @@ -762,14 +839,14 @@ struct List16OfOffsetTo : ArrayOf, HBUINT16> { unsigned int i = (unsigned int) i_; if (unlikely (i >= this->len)) return Null (Type); - _hb_compiler_memory_r_barrier (); + hb_barrier (); return this+this->arrayZ[i]; } const Type& operator [] (int i_) { unsigned int i = (unsigned int) i_; if (unlikely (i >= this->len)) return Crap (Type); - _hb_compiler_memory_r_barrier (); + hb_barrier (); return this+this->arrayZ[i]; } @@ -807,14 +884,14 @@ struct HeadlessArrayOf { unsigned int i = (unsigned int) i_; if (unlikely (i >= lenP1 || !i)) return Null (Type); - _hb_compiler_memory_r_barrier (); + hb_barrier (); return arrayZ[i-1]; } Type& operator [] (int i_) { unsigned int i = (unsigned int) i_; if (unlikely (i >= lenP1 || !i)) return Crap (Type); - _hb_compiler_memory_r_barrier (); + hb_barrier (); return arrayZ[i-1]; } unsigned int get_size () const @@ -866,6 +943,7 @@ struct HeadlessArrayOf TRACE_SANITIZE (this); if (unlikely (!sanitize_shallow (c))) return_trace (false); if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true); + hb_barrier (); unsigned int count = get_length (); for (unsigned int i = 0; i < count; i++) if (unlikely (!c->dispatch (arrayZ[i], std::forward (ds)...))) @@ -878,6 +956,7 @@ struct HeadlessArrayOf { TRACE_SANITIZE (this); return_trace (lenP1.sanitize (c) && + hb_barrier () && (!lenP1 || c->check_array_sized (arrayZ, lenP1 - 1, sizeof (LenType)))); } @@ -899,14 +978,14 @@ struct ArrayOfM1 { unsigned int i = (unsigned int) i_; if (unlikely (i > lenM1)) return Null (Type); - _hb_compiler_memory_r_barrier (); + hb_barrier (); return arrayZ[i]; } Type& operator [] (int i_) { unsigned int i = (unsigned int) i_; if (unlikely (i > lenM1)) return Crap (Type); - _hb_compiler_memory_r_barrier (); + hb_barrier (); return arrayZ[i]; } unsigned int get_size () const @@ -919,6 +998,7 @@ struct ArrayOfM1 TRACE_SANITIZE (this); if (unlikely (!sanitize_shallow (c))) return_trace (false); if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true); + hb_barrier (); unsigned int count = lenM1 + 1; for (unsigned int i = 0; i < count; i++) if (unlikely (!c->dispatch (arrayZ[i], std::forward (ds)...))) @@ -931,6 +1011,7 @@ struct ArrayOfM1 { TRACE_SANITIZE (this); return_trace (lenM1.sanitize (c) && + hb_barrier () && (c->check_array_sized (arrayZ, lenM1 + 1, sizeof (LenType)))); } @@ -975,6 +1056,13 @@ struct SortedArrayOf : ArrayOf return_trace (ret); } + SortedArrayOf* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + SortedArrayOf* out = reinterpret_cast (ArrayOf::copy (c)); + return_trace (out); + } + template Type &bsearch (const T &x, Type ¬_found = Crap (Type)) { return *as_array ().bsearch (x, ¬_found); } @@ -1082,14 +1170,14 @@ struct VarSizedBinSearchArrayOf { unsigned int i = (unsigned int) i_; if (unlikely (i >= get_length ())) return Null (Type); - _hb_compiler_memory_r_barrier (); + hb_barrier (); return StructAtOffset (&bytesZ, i * header.unitSize); } Type& operator [] (int i_) { unsigned int i = (unsigned int) i_; if (unlikely (i >= get_length ())) return Crap (Type); - _hb_compiler_memory_r_barrier (); + hb_barrier (); return StructAtOffset (&bytesZ, i * header.unitSize); } unsigned int get_length () const @@ -1104,6 +1192,7 @@ struct VarSizedBinSearchArrayOf TRACE_SANITIZE (this); if (unlikely (!sanitize_shallow (c))) return_trace (false); if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true); + hb_barrier (); unsigned int count = get_length (); for (unsigned int i = 0; i < count; i++) if (unlikely (!(*this)[i].sanitize (c, std::forward (ds)...))) @@ -1130,6 +1219,7 @@ struct VarSizedBinSearchArrayOf { TRACE_SANITIZE (this); return_trace (header.sanitize (c) && + hb_barrier () && Type::static_size <= header.unitSize && c->check_range (bytesZ.arrayZ, header.nUnits, @@ -1144,6 +1234,810 @@ struct VarSizedBinSearchArrayOf }; +/* CFF INDEX */ + +template +struct CFFIndex +{ + unsigned int offset_array_size () const + { return offSize * (count + 1); } + + template + bool serialize (hb_serialize_context_t *c, + const Iterable &iterable, + const unsigned *p_data_size = nullptr, + unsigned min_off_size = 0) + { + TRACE_SERIALIZE (this); + unsigned data_size; + if (p_data_size) + data_size = *p_data_size; + else + total_size (iterable, &data_size); + + auto it = hb_iter (iterable); + if (unlikely (!serialize_header (c, +it, data_size, min_off_size))) return_trace (false); + unsigned char *ret = c->allocate_size (data_size, false); + if (unlikely (!ret)) return_trace (false); + for (const auto &_ : +it) + { + unsigned len = _.length; + if (!len) + continue; + if (len <= 1) + { + *ret++ = *_.arrayZ; + continue; + } + hb_memcpy (ret, _.arrayZ, len); + ret += len; + } + return_trace (true); + } + + template + bool serialize_header (hb_serialize_context_t *c, + Iterator it, + unsigned data_size, + unsigned min_off_size = 0) + { + TRACE_SERIALIZE (this); + + unsigned off_size = (hb_bit_storage (data_size + 1) + 7) / 8; + off_size = hb_max(min_off_size, off_size); + + /* serialize CFFIndex header */ + if (unlikely (!c->extend_min (this))) return_trace (false); + this->count = hb_len (it); + if (!this->count) return_trace (true); + if (unlikely (!c->extend (this->offSize))) return_trace (false); + this->offSize = off_size; + if (unlikely (!c->allocate_size (off_size * (this->count + 1), false))) + return_trace (false); + + /* serialize indices */ + unsigned int offset = 1; + if (HB_OPTIMIZE_SIZE_VAL) + { + unsigned int i = 0; + for (const auto &_ : +it) + { + set_offset_at (i++, offset); + offset += hb_len_of (_); + } + set_offset_at (i, offset); + } + else + switch (off_size) + { + case 1: + { + HBUINT8 *p = (HBUINT8 *) offsets; + for (const auto &_ : +it) + { + *p++ = offset; + offset += hb_len_of (_); + } + *p = offset; + } + break; + case 2: + { + HBUINT16 *p = (HBUINT16 *) offsets; + for (const auto &_ : +it) + { + *p++ = offset; + offset += hb_len_of (_); + } + *p = offset; + } + break; + case 3: + { + HBUINT24 *p = (HBUINT24 *) offsets; + for (const auto &_ : +it) + { + *p++ = offset; + offset += hb_len_of (_); + } + *p = offset; + } + break; + case 4: + { + HBUINT32 *p = (HBUINT32 *) offsets; + for (const auto &_ : +it) + { + *p++ = offset; + offset += hb_len_of (_); + } + *p = offset; + } + break; + default: + break; + } + + assert (offset == data_size + 1); + return_trace (true); + } + + template + static unsigned total_size (const Iterable &iterable, unsigned *data_size = nullptr, unsigned min_off_size = 0) + { + auto it = + hb_iter (iterable); + if (!it) + { + if (data_size) *data_size = 0; + return min_size; + } + + unsigned total = 0; + for (const auto &_ : +it) + total += hb_len_of (_); + + if (data_size) *data_size = total; + + unsigned off_size = (hb_bit_storage (total + 1) + 7) / 8; + off_size = hb_max(min_off_size, off_size); + + return min_size + HBUINT8::static_size + (hb_len (it) + 1) * off_size + total; + } + + void set_offset_at (unsigned int index, unsigned int offset) + { + assert (index <= count); + + unsigned int size = offSize; + const HBUINT8 *p = offsets; + switch (size) + { + case 1: ((HBUINT8 *) p)[index] = offset; break; + case 2: ((HBUINT16 *) p)[index] = offset; break; + case 3: ((HBUINT24 *) p)[index] = offset; break; + case 4: ((HBUINT32 *) p)[index] = offset; break; + default: return; + } + } + + private: + unsigned int offset_at (unsigned int index) const + { + assert (index <= count); + + unsigned int size = offSize; + const HBUINT8 *p = offsets; + switch (size) + { + case 1: return ((HBUINT8 *) p)[index]; + case 2: return ((HBUINT16 *) p)[index]; + case 3: return ((HBUINT24 *) p)[index]; + case 4: return ((HBUINT32 *) p)[index]; + default: return 0; + } + } + + const unsigned char *data_base () const + { return (const unsigned char *) this + min_size + offSize.static_size - 1 + offset_array_size (); } + public: + + hb_ubytes_t operator [] (unsigned int index) const + { + if (unlikely (index >= count)) return hb_ubytes_t (); + hb_barrier (); + unsigned offset0 = offset_at (index); + unsigned offset1 = offset_at (index + 1); + if (unlikely (offset1 < offset0 || offset1 > offset_at (count))) + return hb_ubytes_t (); + return hb_ubytes_t (data_base () + offset0, offset1 - offset0); + } + + unsigned int get_size () const + { + if (count) + return min_size + offSize.static_size + offset_array_size () + (offset_at (count) - 1); + return min_size; /* empty CFFIndex contains count only */ + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + hb_barrier () && + (count == 0 || /* empty INDEX */ + (count < count + 1u && + c->check_struct (&offSize) && offSize >= 1 && offSize <= 4 && + c->check_array (offsets, offSize, count + 1u) && + c->check_range (data_base (), offset_at (count)))))); + } + + public: + COUNT count; /* Number of object data. Note there are (count+1) offsets */ + private: + HBUINT8 offSize; /* The byte size of each offset in the offsets array. */ + HBUINT8 offsets[HB_VAR_ARRAY]; + /* The array of (count + 1) offsets into objects array (1-base). */ + /* HBUINT8 data[HB_VAR_ARRAY]; Object data */ + public: + DEFINE_SIZE_MIN (COUNT::static_size); +}; +typedef CFFIndex CFF1Index; +typedef CFFIndex CFF2Index; + + +/* TupleValues */ +struct TupleValues +{ + enum packed_value_flag_t + { + VALUES_ARE_ZEROS = 0x80, + VALUES_ARE_BYTES = 0x00, + VALUES_ARE_WORDS = 0x40, + VALUES_ARE_LONGS = 0xC0, + VALUES_SIZE_MASK = 0xC0, + VALUE_RUN_COUNT_MASK = 0x3F + }; + + static unsigned compile (hb_array_t values, /* IN */ + hb_array_t encoded_bytes /* OUT */) + { + unsigned num_values = values.length; + unsigned encoded_len = 0; + unsigned i = 0; + while (i < num_values) + { + int val = values.arrayZ[i]; + if (val == 0) + encoded_len += encode_value_run_as_zeroes (i, encoded_bytes.sub_array (encoded_len), values); + else if (val >= -128 && val <= 127) + encoded_len += encode_value_run_as_bytes (i, encoded_bytes.sub_array (encoded_len), values); + else if (val >= -32768 && val <= 32767) + encoded_len += encode_value_run_as_words (i, encoded_bytes.sub_array (encoded_len), values); + else + encoded_len += encode_value_run_as_longs (i, encoded_bytes.sub_array (encoded_len), values); + } + return encoded_len; + } + + static unsigned encode_value_run_as_zeroes (unsigned& i, + hb_array_t encoded_bytes, + hb_array_t values) + { + unsigned num_values = values.length; + unsigned run_length = 0; + auto it = encoded_bytes.iter (); + unsigned encoded_len = 0; + while (i < num_values && values.arrayZ[i] == 0) + { + i++; + run_length++; + } + + while (run_length >= 64) + { + *it++ = char (VALUES_ARE_ZEROS | 63); + run_length -= 64; + encoded_len++; + } + + if (run_length) + { + *it++ = char (VALUES_ARE_ZEROS | (run_length - 1)); + encoded_len++; + } + return encoded_len; + } + + static unsigned encode_value_run_as_bytes (unsigned &i, + hb_array_t encoded_bytes, + hb_array_t values) + { + unsigned start = i; + unsigned num_values = values.length; + while (i < num_values) + { + int val = values.arrayZ[i]; + if (val > 127 || val < -128) + break; + + /* from fonttools: if there're 2 or more zeros in a sequence, + * it is better to start a new run to save bytes. */ + if (val == 0 && i + 1 < num_values && values.arrayZ[i+1] == 0) + break; + + i++; + } + unsigned run_length = i - start; + + unsigned encoded_len = 0; + auto it = encoded_bytes.iter (); + + while (run_length >= 64) + { + *it++ = (VALUES_ARE_BYTES | 63); + encoded_len++; + + for (unsigned j = 0; j < 64; j++) + { + *it++ = static_cast (values.arrayZ[start + j]); + encoded_len++; + } + + start += 64; + run_length -= 64; + } + + if (run_length) + { + *it++ = (VALUES_ARE_BYTES | (run_length - 1)); + encoded_len++; + + while (start < i) + { + *it++ = static_cast (values.arrayZ[start++]); + encoded_len++; + } + } + + return encoded_len; + } + + static unsigned encode_value_run_as_words (unsigned &i, + hb_array_t encoded_bytes, + hb_array_t values) + { + unsigned start = i; + unsigned num_values = values.length; + while (i < num_values) + { + int val = values.arrayZ[i]; + + /* start a new run for a single zero value*/ + if (val == 0) break; + + /* from fonttools: continue word-encoded run if there's only one + * single value in the range [-128, 127] because it is more compact. + * Only start a new run when there're 2 continuous such values. */ + if (val >= -128 && val <= 127 && + i + 1 < num_values && + values.arrayZ[i+1] >= -128 && values.arrayZ[i+1] <= 127) + break; + + i++; + } + + unsigned run_length = i - start; + auto it = encoded_bytes.iter (); + unsigned encoded_len = 0; + while (run_length >= 64) + { + *it++ = (VALUES_ARE_WORDS | 63); + encoded_len++; + + for (unsigned j = 0; j < 64; j++) + { + int16_t value_val = values.arrayZ[start + j]; + *it++ = static_cast (value_val >> 8); + *it++ = static_cast (value_val & 0xFF); + + encoded_len += 2; + } + + start += 64; + run_length -= 64; + } + + if (run_length) + { + *it++ = (VALUES_ARE_WORDS | (run_length - 1)); + encoded_len++; + while (start < i) + { + int16_t value_val = values.arrayZ[start++]; + *it++ = static_cast (value_val >> 8); + *it++ = static_cast (value_val & 0xFF); + + encoded_len += 2; + } + } + return encoded_len; + } + + static unsigned encode_value_run_as_longs (unsigned &i, + hb_array_t encoded_bytes, + hb_array_t values) + { + unsigned start = i; + unsigned num_values = values.length; + while (i < num_values) + { + int val = values.arrayZ[i]; + + if (val >= -32768 && val <= 32767) + break; + + i++; + } + + unsigned run_length = i - start; + auto it = encoded_bytes.iter (); + unsigned encoded_len = 0; + while (run_length >= 64) + { + *it++ = (VALUES_ARE_LONGS | 63); + encoded_len++; + + for (unsigned j = 0; j < 64; j++) + { + int32_t value_val = values.arrayZ[start + j]; + *it++ = static_cast (value_val >> 24); + *it++ = static_cast (value_val >> 16); + *it++ = static_cast (value_val >> 8); + *it++ = static_cast (value_val & 0xFF); + + encoded_len += 4; + } + + start += 64; + run_length -= 64; + } + + if (run_length) + { + *it++ = (VALUES_ARE_LONGS | (run_length - 1)); + encoded_len++; + while (start < i) + { + int32_t value_val = values.arrayZ[start++]; + *it++ = static_cast (value_val >> 24); + *it++ = static_cast (value_val >> 16); + *it++ = static_cast (value_val >> 8); + *it++ = static_cast (value_val & 0xFF); + + encoded_len += 4; + } + } + return encoded_len; + } + + template + static bool decompile (const HBUINT8 *&p /* IN/OUT */, + hb_vector_t &values /* IN/OUT */, + const HBUINT8 *end, + bool consume_all = false) + { + unsigned i = 0; + unsigned count = consume_all ? UINT_MAX : values.length; + if (consume_all) + values.alloc ((end - p) / 2); + while (i < count) + { + if (unlikely (p + 1 > end)) return consume_all; + unsigned control = *p++; + unsigned run_count = (control & VALUE_RUN_COUNT_MASK) + 1; + if (consume_all) + { + if (unlikely (!values.resize (values.length + run_count, false))) + return false; + } + unsigned stop = i + run_count; + if (unlikely (stop > count)) return false; + if ((control & VALUES_SIZE_MASK) == VALUES_ARE_ZEROS) + { + for (; i < stop; i++) + values.arrayZ[i] = 0; + } + else if ((control & VALUES_SIZE_MASK) == VALUES_ARE_WORDS) + { + if (unlikely (p + run_count * HBINT16::static_size > end)) return false; +#ifndef HB_OPTIMIZE_SIZE + for (; i + 3 < stop; i += 4) + { + values.arrayZ[i] = * (const HBINT16 *) p; + p += HBINT16::static_size; + values.arrayZ[i + 1] = * (const HBINT16 *) p; + p += HBINT16::static_size; + values.arrayZ[i + 2] = * (const HBINT16 *) p; + p += HBINT16::static_size; + values.arrayZ[i + 3] = * (const HBINT16 *) p; + p += HBINT16::static_size; + } +#endif + for (; i < stop; i++) + { + values.arrayZ[i] = * (const HBINT16 *) p; + p += HBINT16::static_size; + } + } + else if ((control & VALUES_SIZE_MASK) == VALUES_ARE_LONGS) + { + if (unlikely (p + run_count * HBINT32::static_size > end)) return false; + for (; i < stop; i++) + { + values.arrayZ[i] = * (const HBINT32 *) p; + p += HBINT32::static_size; + } + } + else if ((control & VALUES_SIZE_MASK) == VALUES_ARE_BYTES) + { + if (unlikely (p + run_count > end)) return false; +#ifndef HB_OPTIMIZE_SIZE + for (; i + 3 < stop; i += 4) + { + values.arrayZ[i] = * (const HBINT8 *) p++; + values.arrayZ[i + 1] = * (const HBINT8 *) p++; + values.arrayZ[i + 2] = * (const HBINT8 *) p++; + values.arrayZ[i + 3] = * (const HBINT8 *) p++; + } +#endif + for (; i < stop; i++) + values.arrayZ[i] = * (const HBINT8 *) p++; + } + } + return true; + } + + struct iter_t : hb_iter_with_fallback_t + { + iter_t (const unsigned char *p_, unsigned len_) + : p (p_), endp (p_ + len_) + { if (ensure_run ()) read_value (); } + + private: + const unsigned char *p; + const unsigned char * const endp; + int current_value = 0; + signed run_count = 0; + unsigned width = 0; + + bool ensure_run () + { + if (likely (run_count > 0)) return true; + + if (unlikely (p >= endp)) + { + run_count = 0; + current_value = 0; + return false; + } + + unsigned control = *p++; + run_count = (control & VALUE_RUN_COUNT_MASK) + 1; + width = control & VALUES_SIZE_MASK; + switch (width) + { + case VALUES_ARE_ZEROS: width = 0; break; + case VALUES_ARE_BYTES: width = HBINT8::static_size; break; + case VALUES_ARE_WORDS: width = HBINT16::static_size; break; + case VALUES_ARE_LONGS: width = HBINT32::static_size; break; + default: assert (false); + } + + if (unlikely (p + run_count * width > endp)) + { + run_count = 0; + current_value = 0; + return false; + } + + return true; + } + void read_value () + { + switch (width) + { + case 0: current_value = 0; break; + case 1: current_value = * (const HBINT8 *) p; break; + case 2: current_value = * (const HBINT16 *) p; break; + case 4: current_value = * (const HBINT32 *) p; break; + } + p += width; + } + + public: + + typedef int __item_t__; + __item_t__ __item__ () const + { return current_value; } + + bool __more__ () const { return run_count || p < endp; } + void __next__ () + { + run_count--; + if (unlikely (!ensure_run ())) + return; + read_value (); + } + void __forward__ (unsigned n) + { + if (unlikely (!ensure_run ())) + return; + while (n) + { + unsigned i = hb_min (n, (unsigned) run_count); + run_count -= i; + n -= i; + p += (i - 1) * width; + if (unlikely (!ensure_run ())) + return; + read_value (); + } + } + bool operator != (const iter_t& o) const + { return p != o.p || run_count != o.run_count; } + iter_t __end__ () const + { + iter_t it (endp, 0); + return it; + } + }; + + struct fetcher_t + { + fetcher_t (const unsigned char *p_, unsigned len_) + : p (p_), end (p_ + len_) {} + + private: + const unsigned char *p; + const unsigned char * const end; + signed run_count = 0; + unsigned width = 0; + + bool ensure_run () + { + if (run_count > 0) return true; + + if (unlikely (p >= end)) + { + run_count = 0; + return false; + } + + unsigned control = *p++; + run_count = (control & VALUE_RUN_COUNT_MASK) + 1; + width = control & VALUES_SIZE_MASK; + switch (width) + { + case VALUES_ARE_ZEROS: width = 0; break; + case VALUES_ARE_BYTES: width = HBINT8::static_size; break; + case VALUES_ARE_WORDS: width = HBINT16::static_size; break; + case VALUES_ARE_LONGS: width = HBINT32::static_size; break; + default: assert (false); + } + + if (unlikely (p + run_count * width > end)) + { + run_count = 0; + return false; + } + + return true; + } + + void skip (unsigned n) + { + while (n) + { + if (unlikely (!ensure_run ())) + return; + unsigned i = hb_min (n, (unsigned) run_count); + run_count -= i; + n -= i; + p += i * width; + } + } + + template + void _add_to (hb_array_t out, float scale = 1.0f) + { + unsigned n = out.length; + float *arrayZ = out.arrayZ; + + for (unsigned i = 0; i < n;) + { + if (unlikely (!ensure_run ())) + break; + unsigned count = hb_min (n - i, (unsigned) run_count); + switch (width) + { + case 0: + { + arrayZ += count; + break; + } + case 1: + { + const auto *pp = (const HBINT8 *) p; + unsigned j = 0; +#ifndef HB_OPTIMIZE_SIZE + for (; j + 3 < count; j += 4) + { + *arrayZ++ += scaled ? *pp++ * scale : *pp++; + *arrayZ++ += scaled ? *pp++ * scale : *pp++; + *arrayZ++ += scaled ? *pp++ * scale : *pp++; + *arrayZ++ += scaled ? *pp++ * scale : *pp++; + } +#endif + for (; j < count; j++) + *arrayZ++ += scaled ? *pp++ * scale : *pp++; + + p = (const unsigned char *) pp; + } + break; + case 2: + { + const auto *pp = (const HBINT16 *) p; + unsigned j = 0; +#ifndef HB_OPTIMIZE_SIZE + for (; j + 3 < count; j += 4) + { + *arrayZ++ += scaled ? *pp++ * scale : *pp++; + *arrayZ++ += scaled ? *pp++ * scale : *pp++; + *arrayZ++ += scaled ? *pp++ * scale : *pp++; + *arrayZ++ += scaled ? *pp++ * scale : *pp++; + } +#endif + for (; j < count; j++) + *arrayZ++ += scaled ? *pp++ * scale : *pp++; + + p = (const unsigned char *) pp; + } + break; + case 4: + { + const auto *pp = (const HBINT32 *) p; + for (unsigned j = 0; j < count; j++) + *arrayZ++ += scaled ? *pp++ * scale : *pp++; + + p = (const unsigned char *) pp; + } + break; + } + run_count -= count; + i += count; + } + } + + public: + void add_to (hb_array_t out, float scale = 1.0f) + { + unsigned n = out.length; + + if (scale == 0.0f) + { + skip (n); + return; + } + +#ifndef HB_OPTIMIZE_SIZE + if (scale == 1.0f) + _add_to (out); + else +#endif + _add_to (out, scale); + } + }; +}; + +struct TupleList : CFF2Index +{ + TupleValues::iter_t operator [] (unsigned i) const + { + auto bytes = CFF2Index::operator [] (i); + return TupleValues::iter_t (bytes.arrayZ, bytes.length); + } + + TupleValues::fetcher_t fetcher (unsigned i) const + { + auto bytes = CFF2Index::operator [] (i); + return TupleValues::fetcher_t (bytes.arrayZ, bytes.length); + } +}; + + } /* namespace OT */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff-common.hh index 081c333f50f52..1e3e0be510629 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff-common.hh @@ -41,10 +41,21 @@ using namespace OT; using objidx_t = hb_serialize_context_t::objidx_t; using whence_t = hb_serialize_context_t::whence_t; -/* utility macro */ -template -static inline const Type& StructAtOffsetOrNull (const void *P, unsigned int offset) -{ return offset ? StructAtOffset (P, offset) : Null (Type); } +/* CFF offsets can technically be negative */ +template +static inline const Type& StructAtOffsetOrNull (const void *P, int offset, hb_sanitize_context_t &sc, Ts&&... ds) +{ + if (!offset) return Null (Type); + + const char *p = (const char *) P + offset; + if (!sc.check_point (p)) return Null (Type); + + const Type &obj = *reinterpret_cast (p); + if (!obj.sanitize (&sc, std::forward (ds)...)) return Null (Type); + + return obj; +} + struct code_pair_t { @@ -57,241 +68,6 @@ using str_buff_t = hb_vector_t; using str_buff_vec_t = hb_vector_t; using glyph_to_sid_map_t = hb_vector_t; -struct length_f_t -{ - template - unsigned operator () (const Iterable &_) const { return hb_len (hb_iter (_)); } - - unsigned operator () (unsigned _) const { return _; } -} -HB_FUNCOBJ (length_f); - -/* CFF INDEX */ -template -struct CFFIndex -{ - unsigned int offset_array_size () const - { return offSize * (count + 1); } - - template - bool serialize (hb_serialize_context_t *c, - const Iterable &iterable, - const unsigned *p_data_size = nullptr) - { - TRACE_SERIALIZE (this); - unsigned data_size; - if (p_data_size) - data_size = *p_data_size; - else - total_size (iterable, &data_size); - - auto it = hb_iter (iterable); - if (unlikely (!serialize_header (c, +it, data_size))) return_trace (false); - unsigned char *ret = c->allocate_size (data_size, false); - if (unlikely (!ret)) return_trace (false); - for (const auto &_ : +it) - { - unsigned len = _.length; - if (!len) - continue; - if (len <= 1) - { - *ret++ = *_.arrayZ; - continue; - } - hb_memcpy (ret, _.arrayZ, len); - ret += len; - } - return_trace (true); - } - - template - bool serialize_header (hb_serialize_context_t *c, - Iterator it, - unsigned data_size) - { - TRACE_SERIALIZE (this); - - unsigned off_size = (hb_bit_storage (data_size + 1) + 7) / 8; - - /* serialize CFFIndex header */ - if (unlikely (!c->extend_min (this))) return_trace (false); - this->count = hb_len (it); - if (!this->count) return_trace (true); - if (unlikely (!c->extend (this->offSize))) return_trace (false); - this->offSize = off_size; - if (unlikely (!c->allocate_size (off_size * (this->count + 1), false))) - return_trace (false); - - /* serialize indices */ - unsigned int offset = 1; - if (HB_OPTIMIZE_SIZE_VAL) - { - unsigned int i = 0; - for (const auto &_ : +it) - { - set_offset_at (i++, offset); - offset += length_f (_); - } - set_offset_at (i, offset); - } - else - switch (off_size) - { - case 1: - { - HBUINT8 *p = (HBUINT8 *) offsets; - for (const auto &_ : +it) - { - *p++ = offset; - offset += length_f (_); - } - *p = offset; - } - break; - case 2: - { - HBUINT16 *p = (HBUINT16 *) offsets; - for (const auto &_ : +it) - { - *p++ = offset; - offset += length_f (_); - } - *p = offset; - } - break; - case 3: - { - HBUINT24 *p = (HBUINT24 *) offsets; - for (const auto &_ : +it) - { - *p++ = offset; - offset += length_f (_); - } - *p = offset; - } - break; - case 4: - { - HBUINT32 *p = (HBUINT32 *) offsets; - for (const auto &_ : +it) - { - *p++ = offset; - offset += length_f (_); - } - *p = offset; - } - break; - default: - break; - } - - assert (offset == data_size + 1); - return_trace (true); - } - - template - static unsigned total_size (const Iterable &iterable, unsigned *data_size = nullptr) - { - auto it = + hb_iter (iterable); - if (!it) - { - if (data_size) *data_size = 0; - return min_size; - } - - unsigned total = 0; - for (const auto &_ : +it) - total += length_f (_); - - if (data_size) *data_size = total; - - unsigned off_size = (hb_bit_storage (total + 1) + 7) / 8; - - return min_size + HBUINT8::static_size + (hb_len (it) + 1) * off_size + total; - } - - void set_offset_at (unsigned int index, unsigned int offset) - { - assert (index <= count); - - unsigned int size = offSize; - const HBUINT8 *p = offsets; - switch (size) - { - case 1: ((HBUINT8 *) p)[index] = offset; break; - case 2: ((HBUINT16 *) p)[index] = offset; break; - case 3: ((HBUINT24 *) p)[index] = offset; break; - case 4: ((HBUINT32 *) p)[index] = offset; break; - default: return; - } - } - - private: - unsigned int offset_at (unsigned int index) const - { - assert (index <= count); - - unsigned int size = offSize; - const HBUINT8 *p = offsets; - switch (size) - { - case 1: return ((HBUINT8 *) p)[index]; - case 2: return ((HBUINT16 *) p)[index]; - case 3: return ((HBUINT24 *) p)[index]; - case 4: return ((HBUINT32 *) p)[index]; - default: return 0; - } - } - - const unsigned char *data_base () const - { return (const unsigned char *) this + min_size + offSize.static_size - 1 + offset_array_size (); } - public: - - hb_ubytes_t operator [] (unsigned int index) const - { - if (unlikely (index >= count)) return hb_ubytes_t (); - _hb_compiler_memory_r_barrier (); - unsigned offset0 = offset_at (index); - unsigned offset1 = offset_at (index + 1); - if (unlikely (offset1 < offset0 || offset1 > offset_at (count))) - return hb_ubytes_t (); - return hb_ubytes_t (data_base () + offset0, offset1 - offset0); - } - - unsigned int get_size () const - { - if (count) - return min_size + offSize.static_size + offset_array_size () + (offset_at (count) - 1); - return min_size; /* empty CFFIndex contains count only */ - } - - bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (likely (c->check_struct (this) && - (count == 0 || /* empty INDEX */ - (count < count + 1u && - c->check_struct (&offSize) && offSize >= 1 && offSize <= 4 && - c->check_array (offsets, offSize, count + 1u) && - c->check_array ((const HBUINT8*) data_base (), 1, offset_at (count)))))); - } - - public: - COUNT count; /* Number of object data. Note there are (count+1) offsets */ - private: - HBUINT8 offSize; /* The byte size of each offset in the offsets array. */ - HBUINT8 offsets[HB_VAR_ARRAY]; - /* The array of (count + 1) offsets into objects array (1-base). */ - /* HBUINT8 data[HB_VAR_ARRAY]; Object data */ - public: - DEFINE_SIZE_MIN (COUNT::static_size); -}; - /* Top Dict, Font Dict, Private Dict */ struct Dict : UnsizedByteStr { @@ -412,6 +188,7 @@ struct FDSelect0 { TRACE_SANITIZE (this); if (unlikely (!(c->check_struct (this)))) return_trace (false); + hb_barrier (); if (unlikely (!c->check_array (fds, c->get_num_glyphs ()))) return_trace (false); @@ -438,7 +215,9 @@ struct FDSelect3_4_Range bool sanitize (hb_sanitize_context_t *c, const void * /*nullptr*/, unsigned int fdcount) const { TRACE_SANITIZE (this); - return_trace (first < c->get_num_glyphs () && (fd < fdcount)); + return_trace (c->check_struct (this) && + hb_barrier () && + first < c->get_num_glyphs () && (fd < fdcount)); } GID_TYPE first; @@ -456,15 +235,20 @@ struct FDSelect3_4 bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const { TRACE_SANITIZE (this); - if (unlikely (!c->check_struct (this) || !ranges.sanitize (c, nullptr, fdcount) || - (nRanges () == 0) || ranges[0].first != 0)) + if (unlikely (!(c->check_struct (this) && + ranges.sanitize (c, nullptr, fdcount) && + hb_barrier () && + (nRanges () != 0) && + ranges[0].first == 0))) return_trace (false); for (unsigned int i = 1; i < nRanges (); i++) if (unlikely (ranges[i - 1].first >= ranges[i].first)) return_trace (false); - if (unlikely (!sentinel().sanitize (c) || (sentinel() != c->get_num_glyphs ()))) + if (unlikely (!(sentinel().sanitize (c) && + hb_barrier () && + (sentinel() == c->get_num_glyphs ())))) return_trace (false); return_trace (true); @@ -524,8 +308,8 @@ struct FDSelect { switch (format) { - case 0: return format.static_size + u.format0.get_size (num_glyphs); - case 3: return format.static_size + u.format3.get_size (); + case 0: hb_barrier (); return format.static_size + u.format0.get_size (num_glyphs); + case 3: hb_barrier (); return format.static_size + u.format3.get_size (); default:return 0; } } @@ -536,8 +320,8 @@ struct FDSelect switch (format) { - case 0: return u.format0.get_fd (glyph); - case 3: return u.format3.get_fd (glyph); + case 0: hb_barrier (); return u.format0.get_fd (glyph); + case 3: hb_barrier (); return u.format3.get_fd (glyph); default:return 0; } } @@ -548,8 +332,8 @@ struct FDSelect switch (format) { - case 0: return u.format0.get_fd_range (glyph); - case 3: return u.format3.get_fd_range (glyph); + case 0: hb_barrier (); return u.format0.get_fd_range (glyph); + case 3: hb_barrier (); return u.format3.get_fd_range (glyph); default:return {0, 1}; } } @@ -559,11 +343,12 @@ struct FDSelect TRACE_SANITIZE (this); if (unlikely (!c->check_struct (this))) return_trace (false); + hb_barrier (); switch (format) { - case 0: return_trace (u.format0.sanitize (c, fdcount)); - case 3: return_trace (u.format3.sanitize (c, fdcount)); + case 0: hb_barrier (); return_trace (u.format0.sanitize (c, fdcount)); + case 3: hb_barrier (); return_trace (u.format3.sanitize (c, fdcount)); default:return_trace (false); } } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.cc index 6fcc8c4658782..b06936f94c898 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.cc @@ -553,15 +553,6 @@ bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoin return true; } -bool OT::cff1::accelerator_t::paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const -{ - funcs->push_clip_glyph (data, glyph, font); - funcs->color (data, true, foreground); - funcs->pop_clip (data); - - return true; -} - bool OT::cff1::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const { #ifdef HB_NO_OT_FONT_CFF diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.hh index d1310c66fde18..27c4d31b9cee2 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.hh @@ -51,9 +51,6 @@ namespace CFF { enum EncodingID { StandardEncoding = 0, ExpertEncoding = 1 }; enum CharsetID { ISOAdobeCharset = 0, ExpertCharset = 1, ExpertSubsetCharset = 2 }; -typedef CFFIndex CFF1Index; - -typedef CFFIndex CFF1Index; typedef CFF1Index CFF1CharStrings; typedef Subrs CFF1Subrs; @@ -242,8 +239,8 @@ struct Encoding unsigned int size = min_size; switch (table_format ()) { - case 0: size += u.format0.get_size (); break; - case 1: size += u.format1.get_size (); break; + case 0: hb_barrier (); size += u.format0.get_size (); break; + case 1: hb_barrier (); size += u.format1.get_size (); break; } if (has_supplement ()) size += suppEncData ().get_size (); @@ -254,8 +251,8 @@ struct Encoding { switch (table_format ()) { - case 0: return u.format0.get_code (glyph); - case 1: return u.format1.get_code (glyph); + case 0: hb_barrier (); return u.format0.get_code (glyph); + case 1: hb_barrier (); return u.format1.get_code (glyph); default:return 0; } } @@ -275,11 +272,12 @@ struct Encoding TRACE_SANITIZE (this); if (unlikely (!c->check_struct (this))) return_trace (false); + hb_barrier (); switch (table_format ()) { - case 0: if (unlikely (!u.format0.sanitize (c))) { return_trace (false); } break; - case 1: if (unlikely (!u.format1.sanitize (c))) { return_trace (false); } break; + case 0: hb_barrier (); if (unlikely (!u.format0.sanitize (c))) { return_trace (false); } break; + case 1: hb_barrier (); if (unlikely (!u.format1.sanitize (c))) { return_trace (false); } break; default:return_trace (false); } return_trace (likely (!has_supplement () || suppEncData ().sanitize (c))); @@ -290,8 +288,8 @@ struct Encoding { switch (table_format ()) { - case 0: return StructAfter (u.format0.codes[u.format0.nCodes ()-1]); - case 1: return StructAfter (u.format1.ranges[u.format1.nRanges ()-1]); + case 0: hb_barrier (); return StructAfter (u.format0.codes[u.format0.nCodes ()-1]); + case 1: hb_barrier (); return StructAfter (u.format1.ranges[u.format1.nRanges ()-1]); default:return Null (CFF1SuppEncData); } } @@ -376,13 +374,13 @@ struct Charset1_2 { bool sanitize (hb_sanitize_context_t *c, unsigned int num_glyphs, unsigned *num_charset_entries) const { TRACE_SANITIZE (this); - if (unlikely (!c->check_struct (this))) - return_trace (false); num_glyphs--; unsigned i; for (i = 0; num_glyphs > 0; i++) { - if (unlikely (!ranges[i].sanitize (c) || (num_glyphs < ranges[i].nLeft + 1))) + if (unlikely (!(ranges[i].sanitize (c) && + hb_barrier () && + (num_glyphs >= ranges[i].nLeft + 1)))) return_trace (false); num_glyphs -= (ranges[i].nLeft + 1); } @@ -569,9 +567,9 @@ struct Charset { switch (format) { - case 0: return min_size + u.format0.get_size (num_glyphs); - case 1: return min_size + u.format1.get_size (num_glyphs); - case 2: return min_size + u.format2.get_size (num_glyphs); + case 0: hb_barrier (); return min_size + u.format0.get_size (num_glyphs); + case 1: hb_barrier (); return min_size + u.format1.get_size (num_glyphs); + case 2: hb_barrier (); return min_size + u.format2.get_size (num_glyphs); default:return 0; } } @@ -581,9 +579,9 @@ struct Charset { switch (format) { - case 0: return u.format0.get_sid (glyph, num_glyphs); - case 1: return u.format1.get_sid (glyph, num_glyphs, cache); - case 2: return u.format2.get_sid (glyph, num_glyphs, cache); + case 0: hb_barrier (); return u.format0.get_sid (glyph, num_glyphs); + case 1: hb_barrier (); return u.format1.get_sid (glyph, num_glyphs, cache); + case 2: hb_barrier (); return u.format2.get_sid (glyph, num_glyphs, cache); default:return 0; } } @@ -592,9 +590,9 @@ struct Charset { switch (format) { - case 0: u.format0.collect_glyph_to_sid_map (mapping, num_glyphs); return; - case 1: u.format1.collect_glyph_to_sid_map (mapping, num_glyphs); return; - case 2: u.format2.collect_glyph_to_sid_map (mapping, num_glyphs); return; + case 0: hb_barrier (); u.format0.collect_glyph_to_sid_map (mapping, num_glyphs); return; + case 1: hb_barrier (); u.format1.collect_glyph_to_sid_map (mapping, num_glyphs); return; + case 2: hb_barrier (); u.format2.collect_glyph_to_sid_map (mapping, num_glyphs); return; default:return; } } @@ -603,9 +601,9 @@ struct Charset { switch (format) { - case 0: return u.format0.get_glyph (sid, num_glyphs); - case 1: return u.format1.get_glyph (sid, num_glyphs); - case 2: return u.format2.get_glyph (sid, num_glyphs); + case 0: hb_barrier (); return u.format0.get_glyph (sid, num_glyphs); + case 1: hb_barrier (); return u.format1.get_glyph (sid, num_glyphs); + case 2: hb_barrier (); return u.format2.get_glyph (sid, num_glyphs); default:return 0; } } @@ -615,12 +613,13 @@ struct Charset TRACE_SANITIZE (this); if (unlikely (!c->check_struct (this))) return_trace (false); + hb_barrier (); switch (format) { - case 0: return_trace (u.format0.sanitize (c, c->get_num_glyphs (), num_charset_entries)); - case 1: return_trace (u.format1.sanitize (c, c->get_num_glyphs (), num_charset_entries)); - case 2: return_trace (u.format2.sanitize (c, c->get_num_glyphs (), num_charset_entries)); + case 0: hb_barrier (); return_trace (u.format0.sanitize (c, c->get_num_glyphs (), num_charset_entries)); + case 1: hb_barrier (); return_trace (u.format1.sanitize (c, c->get_num_glyphs (), num_charset_entries)); + case 2: hb_barrier (); return_trace (u.format2.sanitize (c, c->get_num_glyphs (), num_charset_entries)); default:return_trace (false); } } @@ -761,9 +760,9 @@ struct cff1_top_dict_values_t : top_dict_values_t unsigned int ros_supplement; unsigned int cidCount; - unsigned int EncodingOffset; - unsigned int CharsetOffset; - unsigned int FDSelectOffset; + int EncodingOffset; + int CharsetOffset; + int FDSelectOffset; table_info_t privateDictInfo; }; @@ -819,24 +818,24 @@ struct cff1_top_dict_opset_t : top_dict_opset_t break; case OpCode_Encoding: - dictval.EncodingOffset = env.argStack.pop_uint (); + dictval.EncodingOffset = env.argStack.pop_int (); env.clear_args (); if (unlikely (dictval.EncodingOffset == 0)) return; break; case OpCode_charset: - dictval.CharsetOffset = env.argStack.pop_uint (); + dictval.CharsetOffset = env.argStack.pop_int (); env.clear_args (); if (unlikely (dictval.CharsetOffset == 0)) return; break; case OpCode_FDSelect: - dictval.FDSelectOffset = env.argStack.pop_uint (); + dictval.FDSelectOffset = env.argStack.pop_int (); env.clear_args (); break; case OpCode_Private: - dictval.privateDictInfo.offset = env.argStack.pop_uint (); + dictval.privateDictInfo.offset = env.argStack.pop_int (); dictval.privateDictInfo.size = env.argStack.pop_uint (); env.clear_args (); break; @@ -911,7 +910,7 @@ struct cff1_private_dict_values_base_t : dict_values_t } void fini () { dict_values_t::fini (); } - unsigned int subrsOffset; + int subrsOffset; const CFF1Subrs *localSubrs; }; @@ -946,7 +945,7 @@ struct cff1_private_dict_opset_t : dict_opset_t env.clear_args (); break; case OpCode_Subrs: - dictval.subrsOffset = env.argStack.pop_uint (); + dictval.subrsOffset = env.argStack.pop_int (); env.clear_args (); break; @@ -988,7 +987,7 @@ struct cff1_private_dict_opset_subset_t : dict_opset_t break; case OpCode_Subrs: - dictval.subrsOffset = env.argStack.pop_uint (); + dictval.subrsOffset = env.argStack.pop_int (); env.clear_args (); break; @@ -1055,6 +1054,7 @@ struct cff1 { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && likely (version.major == 1)); } @@ -1073,7 +1073,7 @@ struct cff1 this->blob = sc.reference_table (face); - /* setup for run-time santization */ + /* setup for run-time sanitization */ sc.init (this->blob); sc.start_processing (); @@ -1085,14 +1085,17 @@ struct cff1 nameIndex = &cff->nameIndex (cff); if ((nameIndex == &Null (CFF1NameIndex)) || !nameIndex->sanitize (&sc)) goto fail; + hb_barrier (); - topDictIndex = &StructAtOffset (nameIndex, nameIndex->get_size ()); - if ((topDictIndex == &Null (CFF1TopDictIndex)) || !topDictIndex->sanitize (&sc) || (topDictIndex->count == 0)) + topDictIndex = &StructAtOffsetOrNull (nameIndex, nameIndex->get_size (), sc); + if (topDictIndex == &Null (CFF1TopDictIndex) || (topDictIndex->count == 0)) goto fail; + hb_barrier (); { /* parse top dict */ const hb_ubytes_t topDictStr = (*topDictIndex)[0]; if (unlikely (!topDictStr.sanitize (&sc))) goto fail; + hb_barrier (); cff1_top_dict_interp_env_t env (topDictStr); cff1_top_dict_interpreter_t top_interp (env); if (unlikely (!top_interp.interpret (topDict))) goto fail; @@ -1102,17 +1105,17 @@ struct cff1 charset = &Null (Charset); else { - charset = &StructAtOffsetOrNull (cff, topDict.CharsetOffset); - if (unlikely ((charset == &Null (Charset)) || !charset->sanitize (&sc, &num_charset_entries))) goto fail; + charset = &StructAtOffsetOrNull (cff, topDict.CharsetOffset, sc, &num_charset_entries); + if (unlikely (charset == &Null (Charset))) goto fail; } fdCount = 1; if (is_CID ()) { - fdArray = &StructAtOffsetOrNull (cff, topDict.FDArrayOffset); - fdSelect = &StructAtOffsetOrNull (cff, topDict.FDSelectOffset); - if (unlikely ((fdArray == &Null (CFF1FDArray)) || !fdArray->sanitize (&sc) || - (fdSelect == &Null (CFF1FDSelect)) || !fdSelect->sanitize (&sc, fdArray->count))) + fdArray = &StructAtOffsetOrNull (cff, topDict.FDArrayOffset, sc); + fdSelect = &StructAtOffsetOrNull (cff, topDict.FDSelectOffset, sc, fdArray->count); + if (unlikely (fdArray == &Null (CFF1FDArray) || + fdSelect == &Null (CFF1FDSelect))) goto fail; fdCount = fdArray->count; @@ -1132,22 +1135,18 @@ struct cff1 { if (!is_predef_encoding ()) { - encoding = &StructAtOffsetOrNull (cff, topDict.EncodingOffset); - if (unlikely ((encoding == &Null (Encoding)) || !encoding->sanitize (&sc))) goto fail; + encoding = &StructAtOffsetOrNull (cff, topDict.EncodingOffset, sc); + if (unlikely (encoding == &Null (Encoding))) goto fail; } } - stringIndex = &StructAtOffset (topDictIndex, topDictIndex->get_size ()); - if ((stringIndex == &Null (CFF1StringIndex)) || !stringIndex->sanitize (&sc)) + stringIndex = &StructAtOffsetOrNull (topDictIndex, topDictIndex->get_size (), sc); + if (stringIndex == &Null (CFF1StringIndex)) goto fail; - globalSubrs = &StructAtOffset (stringIndex, stringIndex->get_size ()); - if ((globalSubrs != &Null (CFF1Subrs)) && !globalSubrs->sanitize (&sc)) - goto fail; - - charStrings = &StructAtOffsetOrNull (cff, topDict.charStringsOffset); - - if ((charStrings == &Null (CFF1CharStrings)) || unlikely (!charStrings->sanitize (&sc))) + globalSubrs = &StructAtOffsetOrNull (stringIndex, stringIndex->get_size (), sc); + charStrings = &StructAtOffsetOrNull (cff, topDict.charStringsOffset, sc); + if (charStrings == &Null (CFF1CharStrings)) goto fail; num_glyphs = charStrings->count; @@ -1166,6 +1165,7 @@ struct cff1 { hb_ubytes_t fontDictStr = (*fdArray)[i]; if (unlikely (!fontDictStr.sanitize (&sc))) goto fail; + hb_barrier (); cff1_font_dict_values_t *font; cff1_top_dict_interp_env_t env (fontDictStr); cff1_font_dict_interpreter_t font_interp (env); @@ -1175,17 +1175,15 @@ struct cff1 font->init (); if (unlikely (!font_interp.interpret (*font))) goto fail; PRIVDICTVAL *priv = &privateDicts[i]; - const hb_ubytes_t privDictStr = StructAtOffset (cff, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size); - if (unlikely (!privDictStr.sanitize (&sc))) goto fail; + const hb_ubytes_t privDictStr = StructAtOffsetOrNull (cff, font->privateDictInfo.offset, sc, font->privateDictInfo.size).as_ubytes (font->privateDictInfo.size); + if (unlikely (font->privateDictInfo.size && + privDictStr == (const unsigned char *) &Null (UnsizedByteStr))) goto fail; num_interp_env_t env2 (privDictStr); dict_interpreter_t priv_interp (env2); priv->init (); if (unlikely (!priv_interp.interpret (*priv))) goto fail; - priv->localSubrs = &StructAtOffsetOrNull (&privDictStr, priv->subrsOffset); - if (priv->localSubrs != &Null (CFF1Subrs) && - unlikely (!priv->localSubrs->sanitize (&sc))) - goto fail; + priv->localSubrs = &StructAtOffsetOrNull (&privDictStr, priv->subrsOffset, sc); } } else /* non-CID */ @@ -1193,17 +1191,16 @@ struct cff1 cff1_top_dict_values_t *font = &topDict; PRIVDICTVAL *priv = &privateDicts[0]; - const hb_ubytes_t privDictStr = StructAtOffset (cff, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size); - if (unlikely (!privDictStr.sanitize (&sc))) goto fail; + const hb_ubytes_t privDictStr = StructAtOffsetOrNull (cff, font->privateDictInfo.offset, sc, font->privateDictInfo.size).as_ubytes (font->privateDictInfo.size); + if (font->privateDictInfo.size && + unlikely (privDictStr == (const unsigned char *) &Null (UnsizedByteStr))) goto fail; num_interp_env_t env (privDictStr); dict_interpreter_t priv_interp (env); priv->init (); if (unlikely (!priv_interp.interpret (*priv))) goto fail; - priv->localSubrs = &StructAtOffsetOrNull (&privDictStr, priv->subrsOffset); - if (priv->localSubrs != &Null (CFF1Subrs) && - unlikely (!priv->localSubrs->sanitize (&sc))) - goto fail; + priv->localSubrs = &StructAtOffsetOrNull (&privDictStr, priv->subrsOffset, sc); + hb_barrier (); } return; @@ -1420,7 +1417,7 @@ struct cff1 hb_sorted_vector_t *names = glyph_names.get_acquire (); if (unlikely (!names)) { - names = (hb_sorted_vector_t *) hb_calloc (sizeof (hb_sorted_vector_t), 1); + names = (hb_sorted_vector_t *) hb_calloc (1, sizeof (hb_sorted_vector_t)); if (likely (names)) { names->init (); @@ -1467,7 +1464,6 @@ struct cff1 } HB_INTERNAL bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const; - HB_INTERNAL bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const; HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const; private: @@ -1489,7 +1485,7 @@ struct cff1 int cmp (const gname_t &a) const { return cmp (&a, this); } }; - mutable hb_atomic_ptr_t> glyph_names; + mutable hb_atomic_t *> glyph_names; typedef accelerator_templ_t SUPER; }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.cc index ed430ee59d56b..b2702106eccbe 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.cc @@ -102,6 +102,14 @@ struct cff2_cs_opset_extents_t : cff2_cs_opset_tcoords, font->num_coords)); +} + +bool OT::cff2::accelerator_t::get_extents_at (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + hb_array_t coords) const { #ifdef HB_NO_OT_FONT_CFF /* XXX Remove check when this code moves to .hh file. */ @@ -112,7 +120,7 @@ bool OT::cff2::accelerator_t::get_extents (hb_font_t *font, unsigned int fd = fdSelect->get_fd (glyph); const hb_ubytes_t str = (*charStrings)[glyph]; - cff2_cs_interp_env_t env (str, *this, fd, font->coords, font->num_coords); + cff2_cs_interp_env_t env (str, *this, fd, coords.arrayZ, coords.length); cff2_cs_interpreter_t interp (env); cff2_extents_param_t param; if (unlikely (!interp.interpret (param))) return false; @@ -143,15 +151,6 @@ bool OT::cff2::accelerator_t::get_extents (hb_font_t *font, return true; } -bool OT::cff2::accelerator_t::paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const -{ - funcs->push_clip_glyph (data, glyph, font); - funcs->color (data, true, foreground); - funcs->pop_clip (data); - - return true; -} - struct cff2_path_param_t { cff2_path_param_t (hb_font_t *font_, hb_draw_session_t &draw_session_) @@ -202,6 +201,11 @@ struct cff2_path_procs_path_t : path_procs_t {}; bool OT::cff2::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const +{ + return get_path_at (font, glyph, draw_session, hb_array (font->coords, font->num_coords)); +} + +bool OT::cff2::accelerator_t::get_path_at (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session, hb_array_t coords) const { #ifdef HB_NO_OT_FONT_CFF /* XXX Remove check when this code moves to .hh file. */ @@ -212,7 +216,7 @@ bool OT::cff2::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, h unsigned int fd = fdSelect->get_fd (glyph); const hb_ubytes_t str = (*charStrings)[glyph]; - cff2_cs_interp_env_t env (str, *this, fd, font->coords, font->num_coords); + cff2_cs_interp_env_t env (str, *this, fd, coords.arrayZ, coords.length); cff2_cs_interpreter_t interp (env); cff2_path_param_t param (font, draw_session); if (unlikely (!interp.interpret (param))) return false; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.hh index db10f22ec52e6..7e94e38e115c9 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.hh @@ -40,8 +40,6 @@ namespace CFF { */ #define HB_OT_TAG_CFF2 HB_TAG('C','F','F','2') -typedef CFFIndex CFF2Index; - typedef CFF2Index CFF2CharStrings; typedef Subrs CFF2Subrs; @@ -64,9 +62,9 @@ struct CFF2FDSelect { switch (format) { - case 0: return format.static_size + u.format0.get_size (num_glyphs); - case 3: return format.static_size + u.format3.get_size (); - case 4: return format.static_size + u.format4.get_size (); + case 0: hb_barrier (); return format.static_size + u.format0.get_size (num_glyphs); + case 3: hb_barrier (); return format.static_size + u.format3.get_size (); + case 4: hb_barrier (); return format.static_size + u.format4.get_size (); default:return 0; } } @@ -78,9 +76,9 @@ struct CFF2FDSelect switch (format) { - case 0: return u.format0.get_fd (glyph); - case 3: return u.format3.get_fd (glyph); - case 4: return u.format4.get_fd (glyph); + case 0: hb_barrier (); return u.format0.get_fd (glyph); + case 3: hb_barrier (); return u.format3.get_fd (glyph); + case 4: hb_barrier (); return u.format4.get_fd (glyph); default:return 0; } } @@ -90,12 +88,13 @@ struct CFF2FDSelect TRACE_SANITIZE (this); if (unlikely (!c->check_struct (this))) return_trace (false); + hb_barrier (); switch (format) { - case 0: return_trace (u.format0.sanitize (c, fdcount)); - case 3: return_trace (u.format3.sanitize (c, fdcount)); - case 4: return_trace (u.format4.sanitize (c, fdcount)); + case 0: hb_barrier (); return_trace (u.format0.sanitize (c, fdcount)); + case 3: hb_barrier (); return_trace (u.format3.sanitize (c, fdcount)); + case 4: hb_barrier (); return_trace (u.format4.sanitize (c, fdcount)); default:return_trace (false); } } @@ -110,19 +109,22 @@ struct CFF2FDSelect DEFINE_SIZE_MIN (2); }; -struct CFF2VariationStore +struct CFF2ItemVariationStore { bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (likely (c->check_struct (this)) && c->check_range (&varStore, size) && varStore.sanitize (c)); + return_trace (c->check_struct (this) && + hb_barrier () && + c->check_range (&varStore, size) && + varStore.sanitize (c)); } - bool serialize (hb_serialize_context_t *c, const CFF2VariationStore *varStore) + bool serialize (hb_serialize_context_t *c, const CFF2ItemVariationStore *varStore) { TRACE_SERIALIZE (this); unsigned int size_ = varStore->get_size (); - CFF2VariationStore *dest = c->allocate_size (size_); + CFF2ItemVariationStore *dest = c->allocate_size (size_); if (unlikely (!dest)) return_trace (false); hb_memcpy (dest, varStore, size_); return_trace (true); @@ -131,9 +133,9 @@ struct CFF2VariationStore unsigned int get_size () const { return HBUINT16::static_size + size; } HBUINT16 size; - VariationStore varStore; + ItemVariationStore varStore; - DEFINE_SIZE_MIN (2 + VariationStore::min_size); + DEFINE_SIZE_MIN (2 + ItemVariationStore::min_size); }; struct cff2_top_dict_values_t : top_dict_values_t<> @@ -146,8 +148,8 @@ struct cff2_top_dict_values_t : top_dict_values_t<> } void fini () { top_dict_values_t<>::fini (); } - unsigned int vstoreOffset; - unsigned int FDSelectOffset; + int vstoreOffset; + int FDSelectOffset; }; struct cff2_top_dict_opset_t : top_dict_opset_t<> @@ -165,11 +167,11 @@ struct cff2_top_dict_opset_t : top_dict_opset_t<> break; case OpCode_vstore: - dictval.vstoreOffset = env.argStack.pop_uint (); + dictval.vstoreOffset = env.argStack.pop_int (); env.clear_args (); break; case OpCode_FDSelect: - dictval.FDSelectOffset = env.argStack.pop_uint (); + dictval.FDSelectOffset = env.argStack.pop_int (); env.clear_args (); break; @@ -237,7 +239,7 @@ struct cff2_private_dict_values_base_t : dict_values_t } void fini () { dict_values_t::fini (); } - unsigned int subrsOffset; + int subrsOffset; const CFF2Subrs *localSubrs; unsigned int ivs; }; @@ -291,7 +293,7 @@ struct cff2_private_dict_opset_t : dict_opset_t env.clear_args (); break; case OpCode_Subrs: - dictval.subrsOffset = env.argStack.pop_uint (); + dictval.subrsOffset = env.argStack.pop_int (); env.clear_args (); break; case OpCode_vsindexdict: @@ -340,7 +342,7 @@ struct cff2_private_dict_opset_subset_t : dict_opset_t return; case OpCode_Subrs: - dictval.subrsOffset = env.argStack.pop_uint (); + dictval.subrsOffset = env.argStack.pop_int (); env.clear_args (); break; @@ -384,6 +386,7 @@ struct cff2 { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && likely (version.major == 2)); } @@ -402,7 +405,7 @@ struct cff2 this->blob = sc.reference_table (face); - /* setup for run-time santization */ + /* setup for run-time sanitization */ sc.init (this->blob); sc.start_processing (); @@ -414,23 +417,22 @@ struct cff2 { /* parse top dict */ hb_ubytes_t topDictStr = (cff2 + cff2->topDict).as_ubytes (cff2->topDictSize); if (unlikely (!topDictStr.sanitize (&sc))) goto fail; + hb_barrier (); num_interp_env_t env (topDictStr); cff2_top_dict_interpreter_t top_interp (env); topDict.init (); if (unlikely (!top_interp.interpret (topDict))) goto fail; } - globalSubrs = &StructAtOffset (cff2, cff2->topDict + cff2->topDictSize); - varStore = &StructAtOffsetOrNull (cff2, topDict.vstoreOffset); - charStrings = &StructAtOffsetOrNull (cff2, topDict.charStringsOffset); - fdArray = &StructAtOffsetOrNull (cff2, topDict.FDArrayOffset); - fdSelect = &StructAtOffsetOrNull (cff2, topDict.FDSelectOffset); - - if (((varStore != &Null (CFF2VariationStore)) && unlikely (!varStore->sanitize (&sc))) || - (charStrings == &Null (CFF2CharStrings)) || unlikely (!charStrings->sanitize (&sc)) || - (globalSubrs == &Null (CFF2Subrs)) || unlikely (!globalSubrs->sanitize (&sc)) || - (fdArray == &Null (CFF2FDArray)) || unlikely (!fdArray->sanitize (&sc)) || - (((fdSelect != &Null (CFF2FDSelect)) && unlikely (!fdSelect->sanitize (&sc, fdArray->count))))) + globalSubrs = &StructAtOffsetOrNull (cff2, cff2->topDict + cff2->topDictSize, sc); + varStore = &StructAtOffsetOrNull (cff2, topDict.vstoreOffset, sc); + charStrings = &StructAtOffsetOrNull (cff2, topDict.charStringsOffset, sc); + fdArray = &StructAtOffsetOrNull (cff2, topDict.FDArrayOffset, sc); + fdSelect = &StructAtOffsetOrNull (cff2, topDict.FDSelectOffset, sc, fdArray->count); + + if (charStrings == &Null (CFF2CharStrings) || + globalSubrs == &Null (CFF2Subrs) || + fdArray == &Null (CFF2FDArray)) goto fail; num_glyphs = charStrings->count; @@ -446,6 +448,7 @@ struct cff2 { const hb_ubytes_t fontDictStr = (*fdArray)[i]; if (unlikely (!fontDictStr.sanitize (&sc))) goto fail; + hb_barrier (); cff2_font_dict_values_t *font; num_interp_env_t env (fontDictStr); cff2_font_dict_interpreter_t font_interp (env); @@ -454,17 +457,15 @@ struct cff2 font->init (); if (unlikely (!font_interp.interpret (*font))) goto fail; - const hb_ubytes_t privDictStr = StructAtOffsetOrNull (cff2, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size); - if (unlikely (!privDictStr.sanitize (&sc))) goto fail; + const hb_ubytes_t privDictStr = StructAtOffsetOrNull (cff2, font->privateDictInfo.offset, sc, font->privateDictInfo.size).as_ubytes (font->privateDictInfo.size); + if (unlikely (font->privateDictInfo.size && + privDictStr == (const unsigned char *) &Null (UnsizedByteStr))) goto fail; cff2_priv_dict_interp_env_t env2 (privDictStr); dict_interpreter_t priv_interp (env2); privateDicts[i].init (); if (unlikely (!priv_interp.interpret (privateDicts[i]))) goto fail; - privateDicts[i].localSubrs = &StructAtOffsetOrNull (&privDictStr[0], privateDicts[i].subrsOffset); - if (privateDicts[i].localSubrs != &Null (CFF2Subrs) && - unlikely (!privateDicts[i].localSubrs->sanitize (&sc))) - goto fail; + privateDicts[i].localSubrs = &StructAtOffsetOrNull (&privDictStr[0], privateDicts[i].subrsOffset, sc); } return; @@ -481,6 +482,13 @@ struct cff2 privateDicts.fini (); hb_blob_destroy (blob); blob = nullptr; + + auto *scalars = cached_scalars_vector.get_acquire (); + if (scalars && cached_scalars_vector.cmpexch (scalars, nullptr)) + { + scalars->fini (); + hb_free (scalars); + } } hb_vector_t *create_glyph_to_sid_map () const @@ -499,7 +507,7 @@ struct cff2 hb_blob_t *blob = nullptr; cff2_top_dict_values_t topDict; const CFF2Subrs *globalSubrs = nullptr; - const CFF2VariationStore *varStore = nullptr; + const CFF2ItemVariationStore *varStore = nullptr; const CFF2CharStrings *charStrings = nullptr; const CFF2FDArray *fdArray = nullptr; const CFF2FDSelect *fdSelect = nullptr; @@ -508,6 +516,8 @@ struct cff2 hb_vector_t fontDicts; hb_vector_t privateDicts; + mutable hb_atomic_t *> cached_scalars_vector; + unsigned int num_glyphs = 0; }; @@ -518,8 +528,12 @@ struct cff2 HB_INTERNAL bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const; - HB_INTERNAL bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const; + HB_INTERNAL bool get_extents_at (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + hb_array_t coords) const; HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const; + HB_INTERNAL bool get_path_at (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session, hb_array_t coords) const; }; struct accelerator_subset_t : accelerator_templ_t diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cmap-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-cmap-table.hh index 7e6ced3df4c0d..6bf37c062fc3b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cmap-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cmap-table.hh @@ -41,6 +41,148 @@ namespace OT { +static inline uint8_t unicode_to_macroman (hb_codepoint_t u) +{ + static const struct unicode_to_macroman_t + { + uint16_t unicode; + uint8_t macroman; + } + mapping[] = + { + { 0x00A0, 0xCA }, + { 0x00A1, 0xC1 }, + { 0x00A2, 0xA2 }, + { 0x00A3, 0xA3 }, + { 0x00A5, 0xB4 }, + { 0x00A7, 0xA4 }, + { 0x00A8, 0xAC }, + { 0x00A9, 0xA9 }, + { 0x00AA, 0xBB }, + { 0x00AB, 0xC7 }, + { 0x00AC, 0xC2 }, + { 0x00AE, 0xA8 }, + { 0x00AF, 0xF8 }, + { 0x00B0, 0xA1 }, + { 0x00B1, 0xB1 }, + { 0x00B4, 0xAB }, + { 0x00B5, 0xB5 }, + { 0x00B6, 0xA6 }, + { 0x00B7, 0xE1 }, + { 0x00B8, 0xFC }, + { 0x00BA, 0xBC }, + { 0x00BB, 0xC8 }, + { 0x00BF, 0xC0 }, + { 0x00C0, 0xCB }, + { 0x00C1, 0xE7 }, + { 0x00C2, 0xE5 }, + { 0x00C3, 0xCC }, + { 0x00C4, 0x80 }, + { 0x00C5, 0x81 }, + { 0x00C6, 0xAE }, + { 0x00C7, 0x82 }, + { 0x00C8, 0xE9 }, + { 0x00C9, 0x83 }, + { 0x00CA, 0xE6 }, + { 0x00CB, 0xE8 }, + { 0x00CC, 0xED }, + { 0x00CD, 0xEA }, + { 0x00CE, 0xEB }, + { 0x00CF, 0xEC }, + { 0x00D1, 0x84 }, + { 0x00D2, 0xF1 }, + { 0x00D3, 0xEE }, + { 0x00D4, 0xEF }, + { 0x00D5, 0xCD }, + { 0x00D6, 0x85 }, + { 0x00D8, 0xAF }, + { 0x00D9, 0xF4 }, + { 0x00DA, 0xF2 }, + { 0x00DB, 0xF3 }, + { 0x00DC, 0x86 }, + { 0x00DF, 0xA7 }, + { 0x00E0, 0x88 }, + { 0x00E1, 0x87 }, + { 0x00E2, 0x89 }, + { 0x00E3, 0x8B }, + { 0x00E4, 0x8A }, + { 0x00E5, 0x8C }, + { 0x00E6, 0xBE }, + { 0x00E7, 0x8D }, + { 0x00E8, 0x8F }, + { 0x00E9, 0x8E }, + { 0x00EA, 0x90 }, + { 0x00EB, 0x91 }, + { 0x00EC, 0x93 }, + { 0x00ED, 0x92 }, + { 0x00EE, 0x94 }, + { 0x00EF, 0x95 }, + { 0x00F1, 0x96 }, + { 0x00F2, 0x98 }, + { 0x00F3, 0x97 }, + { 0x00F4, 0x99 }, + { 0x00F5, 0x9B }, + { 0x00F6, 0x9A }, + { 0x00F7, 0xD6 }, + { 0x00F8, 0xBF }, + { 0x00F9, 0x9D }, + { 0x00FA, 0x9C }, + { 0x00FB, 0x9E }, + { 0x00FC, 0x9F }, + { 0x00FF, 0xD8 }, + { 0x0131, 0xF5 }, + { 0x0152, 0xCE }, + { 0x0153, 0xCF }, + { 0x0178, 0xD9 }, + { 0x0192, 0xC4 }, + { 0x02C6, 0xF6 }, + { 0x02C7, 0xFF }, + { 0x02D8, 0xF9 }, + { 0x02D9, 0xFA }, + { 0x02DA, 0xFB }, + { 0x02DB, 0xFE }, + { 0x02DC, 0xF7 }, + { 0x02DD, 0xFD }, + { 0x03A9, 0xBD }, + { 0x03C0, 0xB9 }, + { 0x2013, 0xD0 }, + { 0x2014, 0xD1 }, + { 0x2018, 0xD4 }, + { 0x2019, 0xD5 }, + { 0x201A, 0xE2 }, + { 0x201C, 0xD2 }, + { 0x201D, 0xD3 }, + { 0x201E, 0xE3 }, + { 0x2020, 0xA0 }, + { 0x2021, 0xE0 }, + { 0x2022, 0xA5 }, + { 0x2026, 0xC9 }, + { 0x2030, 0xE4 }, + { 0x2039, 0xDC }, + { 0x203A, 0xDD }, + { 0x2044, 0xDA }, + { 0x20AC, 0xDB }, + { 0x2122, 0xAA }, + { 0x2202, 0xB6 }, + { 0x2206, 0xC6 }, + { 0x220F, 0xB8 }, + { 0x2211, 0xB7 }, + { 0x221A, 0xC3 }, + { 0x221E, 0xB0 }, + { 0x222B, 0xBA }, + { 0x2248, 0xC5 }, + { 0x2260, 0xAD }, + { 0x2264, 0xB2 }, + { 0x2265, 0xB3 }, + { 0x25CA, 0xD7 }, + { 0xF8FF, 0xF0 }, + { 0xFB01, 0xDE }, + { 0xFB02, 0xDF }, + }; + auto *c = hb_bsearch (u, mapping, ARRAY_LENGTH (mapping), sizeof (mapping[0]), + _hb_cmp_operator); + return c ? c->macroman : 0; +} struct CmapSubtableFormat0 { @@ -556,6 +698,7 @@ struct CmapSubtableFormat4 TRACE_SANITIZE (this); if (unlikely (!c->check_struct (this))) return_trace (false); + hb_barrier (); if (unlikely (!c->check_range (this, length))) { @@ -742,10 +885,11 @@ struct CmapSubtableLongSegmented unsigned num_glyphs) const { hb_codepoint_t last_end = 0; - for (unsigned i = 0; i < this->groups.len; i++) + unsigned count = this->groups.len; + for (unsigned i = 0; i < count; i++) { - hb_codepoint_t start = this->groups[i].startCharCode; - hb_codepoint_t end = hb_min ((hb_codepoint_t) this->groups[i].endCharCode, + hb_codepoint_t start = this->groups.arrayZ[i].startCharCode; + hb_codepoint_t end = hb_min ((hb_codepoint_t) this->groups.arrayZ[i].endCharCode, (hb_codepoint_t) HB_UNICODE_MAX); if (unlikely (start > end || start < last_end)) { // Range is not in order and is invalid, skip it. @@ -754,7 +898,7 @@ struct CmapSubtableLongSegmented last_end = end; - hb_codepoint_t gid = this->groups[i].glyphID; + hb_codepoint_t gid = this->groups.arrayZ[i].glyphID; if (!gid) { if (T::formatNumber == 13) continue; @@ -767,9 +911,9 @@ struct CmapSubtableLongSegmented mapping->alloc (mapping->get_population () + end - start + 1); + unicodes->add_range (start, end); for (unsigned cp = start; cp <= end; cp++) { - unicodes->add (cp); mapping->set (cp, gid); gid += T::increment; } @@ -1253,6 +1397,9 @@ struct CmapSubtableFormat14 hb_vector_t> obj_indices; for (int i = src_tbl->record.len - 1; i >= 0; i--) { + if (!unicodes->has(src_tbl->record[i].varSelector)) + continue; + hb_pair_t result = src_tbl->record[i].copy (c, unicodes, glyphs_requested, glyph_map, base); if (result.first || result.second) obj_indices.push (result); @@ -1309,6 +1456,7 @@ struct CmapSubtableFormat14 { + hb_iter (record) | hb_filter (hb_bool, &VariationSelectorRecord::nonDefaultUVS) + | hb_filter (unicodes, &VariationSelectorRecord::varSelector) | hb_map (&VariationSelectorRecord::nonDefaultUVS) | hb_map (hb_add (this)) | hb_apply ([=] (const NonDefaultUVS& _) { _.closure_glyphs (unicodes, glyphset); }) @@ -1353,12 +1501,12 @@ struct CmapSubtable hb_codepoint_t *glyph) const { switch (u.format) { - case 0: return u.format0 .get_glyph (codepoint, glyph); - case 4: return u.format4 .get_glyph (codepoint, glyph); - case 6: return u.format6 .get_glyph (codepoint, glyph); - case 10: return u.format10.get_glyph (codepoint, glyph); - case 12: return u.format12.get_glyph (codepoint, glyph); - case 13: return u.format13.get_glyph (codepoint, glyph); + case 0: hb_barrier (); return u.format0 .get_glyph (codepoint, glyph); + case 4: hb_barrier (); return u.format4 .get_glyph (codepoint, glyph); + case 6: hb_barrier (); return u.format6 .get_glyph (codepoint, glyph); + case 10: hb_barrier (); return u.format10.get_glyph (codepoint, glyph); + case 12: hb_barrier (); return u.format12.get_glyph (codepoint, glyph); + case 13: hb_barrier (); return u.format13.get_glyph (codepoint, glyph); case 14: default: return false; } @@ -1366,12 +1514,12 @@ struct CmapSubtable void collect_unicodes (hb_set_t *out, unsigned int num_glyphs = UINT_MAX) const { switch (u.format) { - case 0: u.format0 .collect_unicodes (out); return; - case 4: u.format4 .collect_unicodes (out); return; - case 6: u.format6 .collect_unicodes (out); return; - case 10: u.format10.collect_unicodes (out); return; - case 12: u.format12.collect_unicodes (out, num_glyphs); return; - case 13: u.format13.collect_unicodes (out, num_glyphs); return; + case 0: hb_barrier (); u.format0 .collect_unicodes (out); return; + case 4: hb_barrier (); u.format4 .collect_unicodes (out); return; + case 6: hb_barrier (); u.format6 .collect_unicodes (out); return; + case 10: hb_barrier (); u.format10.collect_unicodes (out); return; + case 12: hb_barrier (); u.format12.collect_unicodes (out, num_glyphs); return; + case 13: hb_barrier (); u.format13.collect_unicodes (out, num_glyphs); return; case 14: default: return; } @@ -1382,12 +1530,12 @@ struct CmapSubtable unsigned num_glyphs = UINT_MAX) const { switch (u.format) { - case 0: u.format0 .collect_mapping (unicodes, mapping); return; - case 4: u.format4 .collect_mapping (unicodes, mapping); return; - case 6: u.format6 .collect_mapping (unicodes, mapping); return; - case 10: u.format10.collect_mapping (unicodes, mapping); return; - case 12: u.format12.collect_mapping (unicodes, mapping, num_glyphs); return; - case 13: u.format13.collect_mapping (unicodes, mapping, num_glyphs); return; + case 0: hb_barrier (); u.format0 .collect_mapping (unicodes, mapping); return; + case 4: hb_barrier (); u.format4 .collect_mapping (unicodes, mapping); return; + case 6: hb_barrier (); u.format6 .collect_mapping (unicodes, mapping); return; + case 10: hb_barrier (); u.format10.collect_mapping (unicodes, mapping); return; + case 12: hb_barrier (); u.format12.collect_mapping (unicodes, mapping, num_glyphs); return; + case 13: hb_barrier (); u.format13.collect_mapping (unicodes, mapping, num_glyphs); return; case 14: default: return; } @@ -1396,12 +1544,12 @@ struct CmapSubtable unsigned get_language () const { switch (u.format) { - case 0: return u.format0 .get_language (); - case 4: return u.format4 .get_language (); - case 6: return u.format6 .get_language (); - case 10: return u.format10.get_language (); - case 12: return u.format12.get_language (); - case 13: return u.format13.get_language (); + case 0: hb_barrier (); return u.format0 .get_language (); + case 4: hb_barrier (); return u.format4 .get_language (); + case 6: hb_barrier (); return u.format6 .get_language (); + case 10: hb_barrier (); return u.format10.get_language (); + case 12: hb_barrier (); return u.format12.get_language (); + case 13: hb_barrier (); return u.format13.get_language (); case 14: default: return 0; } @@ -1416,9 +1564,9 @@ struct CmapSubtable const void *base) { switch (format) { - case 4: return u.format4.serialize (c, it); - case 12: return u.format12.serialize (c, it); - case 14: return u.format14.serialize (c, &plan->unicodes, &plan->glyphs_requested, plan->glyph_map, base); + case 4: hb_barrier (); return u.format4.serialize (c, it); + case 12: hb_barrier (); return u.format12.serialize (c, it); + case 14: hb_barrier (); return u.format14.serialize (c, &plan->unicodes, &plan->glyphs_requested, plan->glyph_map, base); default: return; } } @@ -1427,14 +1575,15 @@ struct CmapSubtable { TRACE_SANITIZE (this); if (!u.format.sanitize (c)) return_trace (false); + hb_barrier (); switch (u.format) { - case 0: return_trace (u.format0 .sanitize (c)); - case 4: return_trace (u.format4 .sanitize (c)); - case 6: return_trace (u.format6 .sanitize (c)); - case 10: return_trace (u.format10.sanitize (c)); - case 12: return_trace (u.format12.sanitize (c)); - case 13: return_trace (u.format13.sanitize (c)); - case 14: return_trace (u.format14.sanitize (c)); + case 0: hb_barrier (); return_trace (u.format0 .sanitize (c)); + case 4: hb_barrier (); return_trace (u.format4 .sanitize (c)); + case 6: hb_barrier (); return_trace (u.format6 .sanitize (c)); + case 10: hb_barrier (); return_trace (u.format10.sanitize (c)); + case 12: hb_barrier (); return_trace (u.format12.sanitize (c)); + case 13: hb_barrier (); return_trace (u.format13.sanitize (c)); + case 14: hb_barrier (); return_trace (u.format14.sanitize (c)); default:return_trace (true); } } @@ -1462,8 +1611,11 @@ struct EncodingRecord int ret; ret = platformID.cmp (other.platformID); if (ret) return ret; - ret = encodingID.cmp (other.encodingID); - if (ret) return ret; + if (other.encodingID != 0xFFFF) + { + ret = encodingID.cmp (other.encodingID); + if (ret) return ret; + } return 0; } @@ -1811,9 +1963,13 @@ struct cmap c->plan)); } - const CmapSubtable *find_best_subtable (bool *symbol = nullptr) const + const CmapSubtable *find_best_subtable (bool *symbol = nullptr, + bool *mac = nullptr, + bool *macroman = nullptr) const { if (symbol) *symbol = false; + if (mac) *mac = false; + if (macroman) *macroman = false; const CmapSubtable *subtable; @@ -1838,19 +1994,34 @@ struct cmap if ((subtable = this->find_subtable (0, 1))) return subtable; if ((subtable = this->find_subtable (0, 0))) return subtable; + /* MacRoman subtable. */ + if ((subtable = this->find_subtable (1, 0))) + { + if (mac) *mac = true; + if (macroman) *macroman = true; + return subtable; + } + /* Any other Mac subtable; we just map ASCII for these. */ + if ((subtable = this->find_subtable (1, 0xFFFF))) + { + if (mac) *mac = true; + return subtable; + } + /* Meh. */ return &Null (CmapSubtable); } struct accelerator_t { - using cache_t = hb_cache_t<21, 16, 8, true>; + using cache_t = hb_cache_t<21, 19>; + static_assert (sizeof (cache_t) == 1024, ""); accelerator_t (hb_face_t *face) { this->table = hb_sanitize_context_t ().reference_table (face); - bool symbol; - this->subtable = table->find_best_subtable (&symbol); + bool symbol, mac, macroman; + this->subtable = table->find_best_subtable (&symbol, &mac, ¯oman); this->subtable_uvs = &Null (CmapSubtableFormat14); { const CmapSubtable *st = table->find_subtable (0, 5); @@ -1858,7 +2029,16 @@ struct cmap subtable_uvs = &st->u.format14; } +#ifndef HB_NO_OT_FONT_CMAP_CACHE + cache = (cache_t *) hb_malloc (sizeof (cache_t)); + if (cache) + new (cache) cache_t (); + else + return; // Such that get_glyph_funcZ remains null. +#endif + this->get_glyph_data = subtable; +#ifndef HB_NO_CMAP_LEGACY_SUBTABLES if (unlikely (symbol)) { switch ((unsigned) face->table.OS2->get_font_page ()) { @@ -1878,65 +2058,83 @@ struct cmap break; } } + else if (unlikely (macroman)) + { + this->get_glyph_funcZ = get_glyph_from_macroman; + } + else if (unlikely (mac)) + { + this->get_glyph_funcZ = get_glyph_from_ascii; + } else +#endif { switch (subtable->u.format) { - /* Accelerate format 4 and format 12. */ - default: - this->get_glyph_funcZ = get_glyph_from; - break; - case 12: - this->get_glyph_funcZ = get_glyph_from; - break; - case 4: - { - this->format4_accel.init (&subtable->u.format4); - this->get_glyph_data = &this->format4_accel; - this->get_glyph_funcZ = this->format4_accel.get_glyph_func; - break; - } + /* Accelerate format 4 and format 12. */ + default: + this->get_glyph_funcZ = get_glyph_from; + break; + case 12: + this->get_glyph_funcZ = get_glyph_from; + break; + case 4: + { + this->format4_accel.init (&subtable->u.format4); + this->get_glyph_data = &this->format4_accel; + this->get_glyph_funcZ = this->format4_accel.get_glyph_func; + break; + } } } } - ~accelerator_t () { this->table.destroy (); } + ~accelerator_t () + { +#ifndef HB_NO_OT_FONT_CMAP_CACHE + hb_free (cache); +#endif + table.destroy (); + } inline bool _cached_get (hb_codepoint_t unicode, - hb_codepoint_t *glyph, - cache_t *cache) const + hb_codepoint_t *glyph) const { +#ifndef HB_NO_OT_FONT_CMAP_CACHE + // cache is always non-null if we have a get_glyph_funcZ unsigned v; - if (cache && cache->get (unicode, &v)) + if (cache->get (unicode, &v)) { *glyph = v; return true; } +#endif bool ret = this->get_glyph_funcZ (this->get_glyph_data, unicode, glyph); - if (cache && ret) +#ifndef HB_NO_OT_FONT_CMAP_CACHE + if (ret) cache->set (unicode, *glyph); +#endif + return ret; } bool get_nominal_glyph (hb_codepoint_t unicode, - hb_codepoint_t *glyph, - cache_t *cache = nullptr) const + hb_codepoint_t *glyph) const { - if (unlikely (!this->get_glyph_funcZ)) return 0; - return _cached_get (unicode, glyph, cache); + if (unlikely (!this->get_glyph_funcZ)) return false; + return _cached_get (unicode, glyph); } unsigned int get_nominal_glyphs (unsigned int count, const hb_codepoint_t *first_unicode, unsigned int unicode_stride, hb_codepoint_t *first_glyph, - unsigned int glyph_stride, - cache_t *cache = nullptr) const + unsigned int glyph_stride) const { if (unlikely (!this->get_glyph_funcZ)) return 0; unsigned int done; for (done = 0; - done < count && _cached_get (*first_unicode, first_glyph, cache); + done < count && _cached_get (*first_unicode, first_glyph); done++) { first_unicode = &StructAtOffsetUnaligned (first_unicode, unicode_stride); @@ -1947,8 +2145,7 @@ struct cmap bool get_variation_glyph (hb_codepoint_t unicode, hb_codepoint_t variation_selector, - hb_codepoint_t *glyph, - cache_t *cache = nullptr) const + hb_codepoint_t *glyph) const { switch (this->subtable_uvs->get_glyph_variant (unicode, variation_selector, @@ -1959,7 +2156,7 @@ struct cmap case GLYPH_VARIANT_USE_DEFAULT: break; } - return get_nominal_glyph (unicode, glyph, cache); + return get_nominal_glyph (unicode, glyph); } void collect_unicodes (hb_set_t *out, unsigned int num_glyphs) const @@ -2003,15 +2200,41 @@ struct cmap return false; } + template + HB_INTERNAL static bool get_glyph_from_ascii (const void *obj, + hb_codepoint_t codepoint, + hb_codepoint_t *glyph) + { + const Type *typed_obj = (const Type *) obj; + return codepoint < 0x80 && typed_obj->get_glyph (codepoint, glyph); + } + + template + HB_INTERNAL static bool get_glyph_from_macroman (const void *obj, + hb_codepoint_t codepoint, + hb_codepoint_t *glyph) + { + if (get_glyph_from_ascii (obj, codepoint, glyph)) + return true; + + const Type *typed_obj = (const Type *) obj; + unsigned c = unicode_to_macroman (codepoint); + return c && typed_obj->get_glyph (c, glyph); + } + private: hb_nonnull_ptr_t subtable; hb_nonnull_ptr_t subtable_uvs; - hb_cmap_get_glyph_func_t get_glyph_funcZ; - const void *get_glyph_data; + hb_cmap_get_glyph_func_t get_glyph_funcZ = nullptr; + const void *get_glyph_data = nullptr; CmapSubtableFormat4::accelerator_t format4_accel; +#ifndef HB_NO_OT_FONT_CMAP_CACHE + cache_t *cache = nullptr; +#endif + public: hb_blob_ptr_t table; }; @@ -2032,34 +2255,13 @@ struct cmap return &(this+result.subtable); } - const EncodingRecord *find_encodingrec (unsigned int platform_id, - unsigned int encoding_id) const - { - EncodingRecord key; - key.platformID = platform_id; - key.encodingID = encoding_id; - - return encodingRecord.as_array ().bsearch (key); - } - - bool find_subtable (unsigned format) const - { - auto it = - + hb_iter (encodingRecord) - | hb_map (&EncodingRecord::subtable) - | hb_map (hb_add (this)) - | hb_filter ([&] (const CmapSubtable& _) { return _.u.format == format; }) - ; - - return it.len (); - } - public: bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && likely (version == 0) && encodingRecord.sanitize (c, this)); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-color.cc index ef46fe5429a57..2d9b8e7cad2dd 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-color.cc @@ -204,7 +204,7 @@ hb_ot_color_palette_get_colors (hb_face_t *face, hb_bool_t hb_ot_color_has_layers (hb_face_t *face) { - return face->table.COLR->has_v0_data (); + return face->table.COLR->colr->has_v0_data (); } /** @@ -221,7 +221,7 @@ hb_ot_color_has_layers (hb_face_t *face) hb_bool_t hb_ot_color_has_paint (hb_face_t *face) { - return face->table.COLR->has_v1_data (); + return face->table.COLR->colr->has_v1_data (); } /** @@ -240,7 +240,7 @@ hb_bool_t hb_ot_color_glyph_has_paint (hb_face_t *face, hb_codepoint_t glyph) { - return face->table.COLR->has_paint_for_glyph (glyph); + return face->table.COLR->colr->has_paint_for_glyph (glyph); } /** @@ -266,7 +266,7 @@ hb_ot_color_glyph_get_layers (hb_face_t *face, unsigned int *layer_count, /* IN/OUT. May be NULL. */ hb_ot_color_layer_t *layers /* OUT. May be NULL. */) { - return face->table.COLR->get_glyph_layers (glyph, start_offset, layer_count, layers); + return face->table.COLR->colr->get_glyph_layers (glyph, start_offset, layer_count, layers); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-face-table-list.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-face-table-list.hh index b552dfdd9daed..afc8aa476e290 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-face-table-list.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-face-table-list.hh @@ -95,12 +95,18 @@ HB_OT_CORE_TABLE (OT, fvar) HB_OT_CORE_TABLE (OT, avar) HB_OT_CORE_TABLE (OT, cvar) HB_OT_ACCELERATOR (OT, gvar) +#ifndef HB_NO_BEYOND_64K +HB_OT_ACCELERATOR (OT, GVAR) +#endif HB_OT_CORE_TABLE (OT, MVAR) +#ifndef HB_NO_VAR_COMPOSITES +HB_OT_ACCELERATOR (OT, VARC) +#endif #endif /* Legacy kern. */ #ifndef HB_NO_OT_KERN -HB_OT_CORE_TABLE (OT, kern) +HB_OT_ACCELERATOR (OT, kern) #endif /* OpenType shaping. */ @@ -118,9 +124,9 @@ HB_OT_CORE_TABLE (OT, BASE) /* AAT shaping. */ #ifndef HB_NO_AAT -HB_OT_TABLE (AAT, morx) -HB_OT_TABLE (AAT, mort) -HB_OT_TABLE (AAT, kerx) +HB_OT_ACCELERATOR (AAT, morx) +HB_OT_ACCELERATOR (AAT, mort) +HB_OT_ACCELERATOR (AAT, kerx) HB_OT_TABLE (AAT, ankr) HB_OT_TABLE (AAT, trak) HB_OT_TABLE (AAT, ltag) @@ -130,7 +136,7 @@ HB_OT_TABLE (AAT, feat) /* OpenType color fonts. */ #ifndef HB_NO_COLOR -HB_OT_CORE_TABLE (OT, COLR) +HB_OT_ACCELERATOR (OT, COLR) HB_OT_CORE_TABLE (OT, CPAL) HB_OT_ACCELERATOR (OT, CBDT) HB_OT_ACCELERATOR (OT, sbix) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-face.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-face.cc index 2243ee0287414..d17f088073c30 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-face.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-face.cc @@ -36,11 +36,15 @@ #include "hb-ot-name-table.hh" #include "hb-ot-post-table.hh" #include "OT/Color/CBDT/CBDT.hh" +#include "OT/Color/COLR/COLR.hh" #include "OT/Color/sbix/sbix.hh" #include "OT/Color/svg/svg.hh" #include "hb-ot-layout-gdef-table.hh" #include "hb-ot-layout-gsub-table.hh" #include "hb-ot-layout-gpos-table.hh" +#include "hb-ot-var-varc-table.hh" +#include "hb-aat-layout-kerx-table.hh" +#include "hb-aat-layout-morx-table.hh" void hb_ot_face_t::init0 (hb_face_t *face) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-font.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-font.cc index deec909b22e79..0238bb346a95b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-font.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-font.cc @@ -34,7 +34,6 @@ #include "hb-font.hh" #include "hb-machinery.hh" #include "hb-ot-face.hh" -#include "hb-outline.hh" #include "hb-ot-cmap-table.hh" #include "hb-ot-glyf-table.hh" @@ -42,7 +41,8 @@ #include "hb-ot-cff1-table.hh" #include "hb-ot-hmtx-table.hh" #include "hb-ot-post-table.hh" -#include "hb-ot-stat-table.hh" // Just so we compile it; unused otherwise. +#include "hb-ot-stat-table.hh" +#include "hb-ot-var-varc-table.hh" #include "hb-ot-vorg-table.hh" #include "OT/Color/CBDT/CBDT.hh" #include "OT/Color/COLR/COLR.hh" @@ -61,24 +61,111 @@ * never need to call these functions directly. **/ -using hb_ot_font_cmap_cache_t = hb_cache_t<21, 16, 8, true>; -using hb_ot_font_advance_cache_t = hb_cache_t<24, 16, 8, true>; - -#ifndef HB_NO_OT_FONT_CMAP_CACHE -static hb_user_data_key_t hb_ot_font_cmap_cache_user_data_key; -#endif +using hb_ot_font_advance_cache_t = hb_cache_t<24, 16>; +static_assert (sizeof (hb_ot_font_advance_cache_t) == 1024, ""); struct hb_ot_font_t { const hb_ot_face_t *ot_face; -#ifndef HB_NO_OT_FONT_CMAP_CACHE - hb_ot_font_cmap_cache_t *cmap_cache; -#endif - /* h_advance caching */ - mutable hb_atomic_int_t cached_coords_serial; - mutable hb_atomic_ptr_t advance_cache; + mutable hb_atomic_t cached_coords_serial; + struct advance_cache_t + { + mutable hb_atomic_t advance_cache; + mutable hb_atomic_t varStore_cache; + + ~advance_cache_t () + { + clear (); + } + + hb_ot_font_advance_cache_t *acquire_advance_cache () const + { + retry: + auto *cache = advance_cache.get_acquire (); + if (!cache) + { + cache = (hb_ot_font_advance_cache_t *) hb_malloc (sizeof (hb_ot_font_advance_cache_t)); + if (!cache) + return nullptr; + new (cache) hb_ot_font_advance_cache_t; + return cache; + } + if (advance_cache.cmpexch (cache, nullptr)) + return cache; + else + goto retry; + } + void release_advance_cache (hb_ot_font_advance_cache_t *cache) const + { + if (!cache) + return; + if (!advance_cache.cmpexch (nullptr, cache)) + hb_free (cache); + } + void clear_advance_cache () const + { + retry: + auto *cache = advance_cache.get_acquire (); + if (!cache) + return; + if (advance_cache.cmpexch (cache, nullptr)) + hb_free (cache); + else + goto retry; + } + + OT::ItemVariationStore::cache_t *acquire_varStore_cache (const OT::ItemVariationStore &varStore) const + { + retry: + auto *cache = varStore_cache.get_acquire (); + if (!cache) + return varStore.create_cache (); + if (varStore_cache.cmpexch (cache, nullptr)) + return cache; + else + goto retry; + } + void release_varStore_cache (OT::ItemVariationStore::cache_t *cache) const + { + if (!cache) + return; + if (!varStore_cache.cmpexch (nullptr, cache)) + OT::ItemVariationStore::destroy_cache (cache); + } + void clear_varStore_cache () const + { + retry: + auto *cache = varStore_cache.get_acquire (); + if (!cache) + return; + if (varStore_cache.cmpexch (cache, nullptr)) + OT::ItemVariationStore::destroy_cache (cache); + else + goto retry; + } + + void clear () const + { + clear_advance_cache (); + clear_varStore_cache (); + } + + } h, v; + + void check_serial (hb_font_t *font) const + { + int font_serial = font->serial_coords.get_acquire (); + + if (cached_coords_serial.get_acquire () == font_serial) + return; + + h.clear (); + v.clear (); + + cached_coords_serial.set_release (font_serial); + } }; static hb_ot_font_t * @@ -90,35 +177,6 @@ _hb_ot_font_create (hb_font_t *font) ot_font->ot_face = &font->face->table; -#ifndef HB_NO_OT_FONT_CMAP_CACHE - // retry: - auto *cmap_cache = (hb_ot_font_cmap_cache_t *) hb_face_get_user_data (font->face, - &hb_ot_font_cmap_cache_user_data_key); - if (!cmap_cache) - { - cmap_cache = (hb_ot_font_cmap_cache_t *) hb_malloc (sizeof (hb_ot_font_cmap_cache_t)); - if (unlikely (!cmap_cache)) goto out; - new (cmap_cache) hb_ot_font_cmap_cache_t (); - if (unlikely (!hb_face_set_user_data (font->face, - &hb_ot_font_cmap_cache_user_data_key, - cmap_cache, - hb_free, - false))) - { - hb_free (cmap_cache); - cmap_cache = nullptr; - /* Normally we would retry here, but that would - * infinite-loop if the face is the empty-face. - * Just let it go and this font will be uncached if it - * happened to collide with another thread creating the - * cache at the same time. */ - // goto retry; - } - } - out: - ot_font->cmap_cache = cmap_cache; -#endif - return ot_font; } @@ -127,8 +185,7 @@ _hb_ot_font_destroy (void *font_data) { hb_ot_font_t *ot_font = (hb_ot_font_t *) font_data; - auto *cache = ot_font->advance_cache.get_relaxed (); - hb_free (cache); + ot_font->~hb_ot_font_t (); hb_free (ot_font); } @@ -142,11 +199,7 @@ hb_ot_get_nominal_glyph (hb_font_t *font HB_UNUSED, { const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; const hb_ot_face_t *ot_face = ot_font->ot_face; - hb_ot_font_cmap_cache_t *cmap_cache = nullptr; -#ifndef HB_NO_OT_FONT_CMAP_CACHE - cmap_cache = ot_font->cmap_cache; -#endif - return ot_face->cmap->get_nominal_glyph (unicode, glyph, cmap_cache); + return ot_face->cmap->get_nominal_glyph (unicode, glyph); } static unsigned int @@ -161,14 +214,9 @@ hb_ot_get_nominal_glyphs (hb_font_t *font HB_UNUSED, { const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; const hb_ot_face_t *ot_face = ot_font->ot_face; - hb_ot_font_cmap_cache_t *cmap_cache = nullptr; -#ifndef HB_NO_OT_FONT_CMAP_CACHE - cmap_cache = ot_font->cmap_cache; -#endif return ot_face->cmap->get_nominal_glyphs (count, first_unicode, unicode_stride, - first_glyph, glyph_stride, - cmap_cache); + first_glyph, glyph_stride); } static hb_bool_t @@ -181,13 +229,8 @@ hb_ot_get_variation_glyph (hb_font_t *font HB_UNUSED, { const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; const hb_ot_face_t *ot_face = ot_font->ot_face; - hb_ot_font_cmap_cache_t *cmap_cache = nullptr; -#ifndef HB_NO_OT_FONT_CMAP_CACHE - cmap_cache = ot_font->cmap_cache; -#endif return ot_face->cmap->get_variation_glyph (unicode, - variation_selector, glyph, - cmap_cache); + variation_selector, glyph); } static void @@ -204,43 +247,20 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data, const hb_ot_face_t *ot_face = ot_font->ot_face; const OT::hmtx_accelerator_t &hmtx = *ot_face->hmtx; - hb_position_t *orig_first_advance = first_advance; - -#if !defined(HB_NO_VAR) && !defined(HB_NO_OT_FONT_ADVANCE_CACHE) + ot_font->check_serial (font); const OT::HVAR &HVAR = *hmtx.var_table; - const OT::VariationStore &varStore = &HVAR + HVAR.varStore; - OT::VariationStore::cache_t *varStore_cache = font->num_coords * count >= 128 ? varStore.create_cache () : nullptr; + const OT::ItemVariationStore &varStore = &HVAR + HVAR.varStore; + OT::ItemVariationStore::cache_t *varStore_cache = ot_font->h.acquire_varStore_cache (varStore); - bool use_cache = font->num_coords; -#else - OT::VariationStore::cache_t *varStore_cache = nullptr; - bool use_cache = false; -#endif + hb_ot_font_advance_cache_t *advance_cache = nullptr; - hb_ot_font_advance_cache_t *cache = nullptr; + bool use_cache = font->num_coords; if (use_cache) { - retry: - cache = ot_font->advance_cache.get_acquire (); - if (unlikely (!cache)) - { - cache = (hb_ot_font_advance_cache_t *) hb_malloc (sizeof (hb_ot_font_advance_cache_t)); - if (unlikely (!cache)) - { - use_cache = false; - goto out; - } - new (cache) hb_ot_font_advance_cache_t; - - if (unlikely (!ot_font->advance_cache.cmpexch (nullptr, cache))) - { - hb_free (cache); - goto retry; - } - ot_font->cached_coords_serial.set_release (font->serial_coords); - } + advance_cache = ot_font->h.acquire_advance_cache (); + if (!advance_cache) + use_cache = false; } - out: if (!use_cache) { @@ -253,44 +273,26 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data, } else { /* Use cache. */ - if (ot_font->cached_coords_serial.get_acquire () != (int) font->serial_coords) - { - ot_font->advance_cache->clear (); - ot_font->cached_coords_serial.set_release (font->serial_coords); - } - for (unsigned int i = 0; i < count; i++) { hb_position_t v; unsigned cv; - if (ot_font->advance_cache->get (*first_glyph, &cv)) + if (advance_cache->get (*first_glyph, &cv)) v = cv; else { v = hmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache); - ot_font->advance_cache->set (*first_glyph, v); + advance_cache->set (*first_glyph, v); } *first_advance = font->em_scale_x (v); first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); } - } - -#if !defined(HB_NO_VAR) && !defined(HB_NO_OT_FONT_ADVANCE_CACHE) - OT::VariationStore::destroy_cache (varStore_cache); -#endif - if (font->x_strength && !font->embolden_in_place) - { - /* Emboldening. */ - hb_position_t x_strength = font->x_scale >= 0 ? font->x_strength : -font->x_strength; - first_advance = orig_first_advance; - for (unsigned int i = 0; i < count; i++) - { - *first_advance += *first_advance ? x_strength : 0; - first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); - } + ot_font->h.release_advance_cache (advance_cache); } + + ot_font->h.release_varStore_cache (varStore_cache); } #ifndef HB_NO_VERTICAL @@ -307,17 +309,13 @@ hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data, const hb_ot_face_t *ot_face = ot_font->ot_face; const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; - hb_position_t *orig_first_advance = first_advance; - if (vmtx.has_data ()) { -#if !defined(HB_NO_VAR) && !defined(HB_NO_OT_FONT_ADVANCE_CACHE) + ot_font->check_serial (font); const OT::VVAR &VVAR = *vmtx.var_table; - const OT::VariationStore &varStore = &VVAR + VVAR.varStore; - OT::VariationStore::cache_t *varStore_cache = font->num_coords ? varStore.create_cache () : nullptr; -#else - OT::VariationStore::cache_t *varStore_cache = nullptr; -#endif + const OT::ItemVariationStore &varStore = &VVAR + VVAR.varStore; + OT::ItemVariationStore::cache_t *varStore_cache = ot_font->v.acquire_varStore_cache (varStore); + // TODO Use advance_cache. for (unsigned int i = 0; i < count; i++) { @@ -326,9 +324,7 @@ hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data, first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); } -#if !defined(HB_NO_VAR) && !defined(HB_NO_OT_FONT_ADVANCE_CACHE) - OT::VariationStore::destroy_cache (varStore_cache); -#endif + ot_font->v.release_varStore_cache (varStore_cache); } else { @@ -343,18 +339,6 @@ hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data, first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); } } - - if (font->y_strength && !font->embolden_in_place) - { - /* Emboldening. */ - hb_position_t y_strength = font->y_scale >= 0 ? font->y_strength : -font->y_strength; - first_advance = orig_first_advance; - for (unsigned int i = 0; i < count; i++) - { - *first_advance += *first_advance ? y_strength : 0; - first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); - } - } } #endif @@ -391,7 +375,8 @@ hb_ot_get_glyph_v_origin (hb_font_t *font, } hb_glyph_extents_t extents = {0}; - if (ot_face->glyf->get_extents (font, glyph, &extents)) + + if (hb_font_get_glyph_extents (font, glyph, &extents)) { const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; int tsb = 0; @@ -404,7 +389,7 @@ hb_ot_get_glyph_v_origin (hb_font_t *font, hb_font_extents_t font_extents; font->get_h_extents_with_fallback (&font_extents); hb_position_t advance = font_extents.ascender - font_extents.descender; - int diff = advance - -extents.height; + hb_position_t diff = advance - -extents.height; *y = extents.y_bearing + (diff >> 1); return true; } @@ -433,6 +418,9 @@ hb_ot_get_glyph_extents (hb_font_t *font, #endif #if !defined(HB_NO_COLOR) && !defined(HB_NO_PAINT) if (ot_face->COLR->get_extents (font, glyph, extents)) return true; +#endif +#ifndef HB_NO_VAR_COMPOSITES + if (ot_face->VARC->get_extents (font, glyph, extents)) return true; #endif if (ot_face->glyf->get_extents (font, glyph, extents)) return true; #ifndef HB_NO_OT_FONT_CFF @@ -484,16 +472,9 @@ hb_ot_get_font_h_extents (hb_font_t *font, hb_font_extents_t *metrics, void *user_data HB_UNUSED) { - bool ret = _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER, &metrics->ascender) && - _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER, &metrics->descender) && - _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP, &metrics->line_gap); - - /* Embolden */ - int y_shift = font->y_strength; - if (font->y_scale < 0) y_shift = -y_shift; - metrics->ascender += y_shift; - - return ret; + return _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER, &metrics->ascender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER, &metrics->descender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP, &metrics->line_gap); } #ifndef HB_NO_VERTICAL @@ -510,64 +491,46 @@ hb_ot_get_font_v_extents (hb_font_t *font, #endif #ifndef HB_NO_DRAW -static void -hb_ot_draw_glyph (hb_font_t *font, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - hb_draw_funcs_t *draw_funcs, void *draw_data, - void *user_data) +static hb_bool_t +hb_ot_draw_glyph_or_fail (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data, + void *user_data) { - bool embolden = font->x_strength || font->y_strength; - hb_outline_t outline; - - { // Need draw_session to be destructed before emboldening. - hb_draw_session_t draw_session (embolden ? hb_outline_recording_pen_get_funcs () : draw_funcs, - embolden ? &outline : draw_data, font->slant_xy); - if (!font->face->table.glyf->get_path (font, glyph, draw_session)) + hb_draw_session_t draw_session {draw_funcs, draw_data}; +#ifndef HB_NO_VAR_COMPOSITES + if (font->face->table.VARC->get_path (font, glyph, draw_session)) return true; +#endif + // Keep the following in synch with VARC::get_path_at() + if (font->face->table.glyf->get_path (font, glyph, draw_session)) return true; #ifndef HB_NO_CFF - if (!font->face->table.cff2->get_path (font, glyph, draw_session)) - if (!font->face->table.cff1->get_path (font, glyph, draw_session)) + if (font->face->table.cff2->get_path (font, glyph, draw_session)) return true; + if (font->face->table.cff1->get_path (font, glyph, draw_session)) return true; #endif - {} - } - - if (embolden) - { - float x_shift = font->embolden_in_place ? 0 : (float) font->x_strength / 2; - float y_shift = (float) font->y_strength / 2; - if (font->x_scale < 0) x_shift = -x_shift; - if (font->y_scale < 0) y_shift = -y_shift; - outline.embolden (font->x_strength, font->y_strength, - x_shift, y_shift); - - outline.replay (draw_funcs, draw_data); - } + return false; } #endif #ifndef HB_NO_PAINT -static void -hb_ot_paint_glyph (hb_font_t *font, - void *font_data, - hb_codepoint_t glyph, - hb_paint_funcs_t *paint_funcs, void *paint_data, - unsigned int palette, - hb_color_t foreground, - void *user_data) +static hb_bool_t +hb_ot_paint_glyph_or_fail (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette, + hb_color_t foreground, + void *user_data) { #ifndef HB_NO_COLOR - if (font->face->table.COLR->paint_glyph (font, glyph, paint_funcs, paint_data, palette, foreground)) return; - if (font->face->table.SVG->paint_glyph (font, glyph, paint_funcs, paint_data)) return; + if (font->face->table.COLR->paint_glyph (font, glyph, paint_funcs, paint_data, palette, foreground)) return true; + if (font->face->table.SVG->paint_glyph (font, glyph, paint_funcs, paint_data)) return true; #ifndef HB_NO_OT_FONT_BITMAP - if (font->face->table.CBDT->paint_glyph (font, glyph, paint_funcs, paint_data)) return; - if (font->face->table.sbix->paint_glyph (font, glyph, paint_funcs, paint_data)) return; + if (font->face->table.CBDT->paint_glyph (font, glyph, paint_funcs, paint_data)) return true; + if (font->face->table.sbix->paint_glyph (font, glyph, paint_funcs, paint_data)) return true; #endif #endif - if (font->face->table.glyf->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return; -#ifndef HB_NO_CFF - if (font->face->table.cff2->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return; - if (font->face->table.cff1->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return; -#endif + return false; } #endif @@ -594,11 +557,11 @@ static struct hb_ot_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_tcheck_struct (this) && + hb_barrier () && c->check_range (this, sizeDeviceRecord))); } @@ -94,7 +95,7 @@ struct hdmx bool serialize (hb_serialize_context_t *c, unsigned version, Iterator it, - const hb_vector_t &new_to_old_gid_list, + hb_array_t new_to_old_gid_list, unsigned num_glyphs) { TRACE_SERIALIZE (this); @@ -152,6 +153,7 @@ struct hdmx { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && !hb_unsigned_mul_overflows (numRecords, sizeDeviceRecord) && min_size + numRecords * sizeDeviceRecord > numRecords * sizeDeviceRecord && sizeDeviceRecord >= DeviceRecord::min_size && diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-head-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-head-table.hh index ab057fcfe4f10..34535e2286d82 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-head-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-head-table.hh @@ -103,6 +103,7 @@ struct head { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && version.major == 1 && magicNumber == 0x5F0F3CF5u); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-hhea-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-hhea-table.hh index 37ef8744572d3..5e3be5be525ba 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-hhea-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-hhea-table.hh @@ -50,7 +50,9 @@ struct _hea bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && likely (version.major == 1)); + return_trace (c->check_struct (this) && + hb_barrier () && + likely (version.major == 1)); } public: diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-hmtx-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-hmtx-table.hh index 39e1f4830a588..67b58b1768346 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-hmtx-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-hmtx-table.hh @@ -30,6 +30,7 @@ #include "hb-open-type.hh" #include "hb-ot-maxp-table.hh" #include "hb-ot-hhea-table.hh" +#include "hb-ot-os2-table.hh" #include "hb-ot-var-hvar-table.hh" #include "hb-ot-var-mvar-table.hh" #include "hb-ot-metrics.hh" @@ -145,6 +146,29 @@ struct hmtxvmtx table->minTrailingBearing = min_rsb; table->maxExtent = max_extent; } + + if (T::is_horizontal) + { + const auto &OS2 = *c->plan->source->table.OS2; + if (OS2.has_data () && + table->ascender == OS2.sTypoAscender && + table->descender == OS2.sTypoDescender && + table->lineGap == OS2.sTypoLineGap) + { + table->ascender = static_cast (roundf (OS2.sTypoAscender + + MVAR.get_var (HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER, + c->plan->normalized_coords.arrayZ, + c->plan->normalized_coords.length))); + table->descender = static_cast (roundf (OS2.sTypoDescender + + MVAR.get_var (HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER, + c->plan->normalized_coords.arrayZ, + c->plan->normalized_coords.length))); + table->lineGap = static_cast (roundf (OS2.sTypoLineGap + + MVAR.get_var (HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP, + c->plan->normalized_coords.arrayZ, + c->plan->normalized_coords.length))); + } + } } #endif @@ -158,7 +182,7 @@ struct hmtxvmtx hb_requires (hb_is_iterator (Iterator))> void serialize (hb_serialize_context_t *c, Iterator it, - const hb_vector_t new_to_old_gid_list, + hb_array_t new_to_old_gid_list, unsigned num_long_metrics, unsigned total_num_metrics) { @@ -335,7 +359,13 @@ struct hmtxvmtx return true; } - return _glyf_get_leading_bearing_with_var_unscaled (font, glyph, T::tableTag == HB_OT_TAG_vmtx, lsb); + // If there's no vmtx data, the phantom points from glyf table are not accurate, + // so we cannot take the next path. + bool is_vertical = T::tableTag == HB_OT_TAG_vmtx; + if (is_vertical && !has_data ()) + return false; + + return _glyf_get_leading_bearing_with_var_unscaled (font, glyph, is_vertical, lsb); #else return false; #endif @@ -374,7 +404,7 @@ struct hmtxvmtx unsigned get_advance_with_var_unscaled (hb_codepoint_t glyph, hb_font_t *font, - VariationStore::cache_t *store_cache = nullptr) const + ItemVariationStore::cache_t *store_cache = nullptr) const { unsigned int advance = get_advance_without_var_unscaled (glyph); @@ -387,7 +417,8 @@ struct hmtxvmtx font->coords, font->num_coords, store_cache)); - return _glyf_get_advance_with_var_unscaled (font, glyph, T::tableTag == HB_OT_TAG_vmtx); + unsigned glyf_advance = _glyf_get_advance_with_var_unscaled (font, glyph, T::tableTag == HB_OT_TAG_vmtx); + return glyf_advance ? glyf_advance : advance; #else return advance; #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-kern-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-kern-table.hh index 291683369aa35..97d012e58b817 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-kern-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-kern-table.hh @@ -27,6 +27,7 @@ #ifndef HB_OT_KERN_TABLE_HH #define HB_OT_KERN_TABLE_HH +#include "hb-aat-layout-common.hh" #include "hb-aat-layout-kerx-table.hh" @@ -79,12 +80,23 @@ struct KernSubTableFormat3 { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && c->check_range (kernValueZ, kernValueCount * sizeof (FWORD) + glyphCount * 2 + leftClassCount * rightClassCount)); } + template + void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const + { + set_t set; + if (likely (glyphCount)) + set.add_range (0, glyphCount - 1); + left_set.union_ (set); + right_set.union_ (set); + } + protected: KernSubTableHeader header; @@ -121,7 +133,7 @@ struct KernSubTable { switch (get_type ()) { /* This method hooks up to hb_font_t's get_h_kerning. Only support Format0. */ - case 0: return u.format0.get_kerning (left, right); + case 0: hb_barrier (); return u.format0.get_kerning (left, right); default:return 0; } } @@ -134,22 +146,36 @@ struct KernSubTable switch (subtable_type) { case 0: return_trace (c->dispatch (u.format0)); #ifndef HB_NO_AAT_SHAPE - case 1: return_trace (u.header.apple ? c->dispatch (u.format1, std::forward (ds)...) : c->default_return_value ()); + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); #endif case 2: return_trace (c->dispatch (u.format2)); #ifndef HB_NO_AAT_SHAPE - case 3: return_trace (u.header.apple ? c->dispatch (u.format3, std::forward (ds)...) : c->default_return_value ()); + case 3: return_trace (c->dispatch (u.format3, std::forward (ds)...)); #endif default: return_trace (c->default_return_value ()); } } + template + void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const + { + unsigned int subtable_type = get_type (); + switch (subtable_type) { + case 0: u.format0.collect_glyphs (left_set, right_set, num_glyphs); return; + case 1: u.format1.collect_glyphs (left_set, right_set, num_glyphs); return; + case 2: u.format2.collect_glyphs (left_set, right_set, num_glyphs); return; + case 3: u.format3.collect_glyphs (left_set, right_set, num_glyphs); return; + default: return; + } + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (unlikely (!u.header.sanitize (c) || - u.header.length < u.header.min_size || - !c->check_range (this, u.header.length))) return_trace (false); + if (unlikely (!(u.header.sanitize (c) && + hb_barrier () && + u.header.length >= u.header.min_size && + c->check_range (this, u.header.length)))) return_trace (false); return_trace (dispatch (c)); } @@ -286,9 +312,9 @@ struct kern bool has_state_machine () const { switch (get_type ()) { - case 0: return u.ot.has_state_machine (); + case 0: hb_barrier (); return u.ot.has_state_machine (); #ifndef HB_NO_AAT_SHAPE - case 1: return u.aat.has_state_machine (); + case 1: hb_barrier (); return u.aat.has_state_machine (); #endif default:return false; } @@ -297,9 +323,9 @@ struct kern bool has_cross_stream () const { switch (get_type ()) { - case 0: return u.ot.has_cross_stream (); + case 0: hb_barrier (); return u.ot.has_cross_stream (); #ifndef HB_NO_AAT_SHAPE - case 1: return u.aat.has_cross_stream (); + case 1: hb_barrier (); return u.aat.has_cross_stream (); #endif default:return false; } @@ -308,16 +334,17 @@ struct kern int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const { switch (get_type ()) { - case 0: return u.ot.get_h_kerning (left, right); + case 0: hb_barrier (); return u.ot.get_h_kerning (left, right); #ifndef HB_NO_AAT_SHAPE - case 1: return u.aat.get_h_kerning (left, right); + case 1: hb_barrier (); return u.aat.get_h_kerning (left, right); #endif default:return 0; } } - bool apply (AAT::hb_aat_apply_context_t *c) const - { return dispatch (c); } + bool apply (AAT::hb_aat_apply_context_t *c, + const AAT::kern_accelerator_data_t &accel_data) const + { return dispatch (c, accel_data); } template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const @@ -337,9 +364,46 @@ struct kern { TRACE_SANITIZE (this); if (!u.version32.sanitize (c)) return_trace (false); + hb_barrier (); return_trace (dispatch (c)); } + AAT::kern_accelerator_data_t create_accelerator_data (unsigned num_glyphs) const + { + switch (get_type ()) { + case 0: hb_barrier (); return u.ot.create_accelerator_data (num_glyphs); +#ifndef HB_NO_AAT_SHAPE + case 1: hb_barrier (); return u.aat.create_accelerator_data (num_glyphs); +#endif + default:return AAT::kern_accelerator_data_t (); + } + } + + struct accelerator_t + { + accelerator_t (hb_face_t *face) + { + hb_sanitize_context_t sc; + this->table = sc.reference_table (face); + this->accel_data = this->table->create_accelerator_data (face->get_num_glyphs ()); + } + ~accelerator_t () + { + this->table.destroy (); + } + + hb_blob_t *get_blob () const { return table.get_blob (); } + + bool apply (AAT::hb_aat_apply_context_t *c) const + { + return table->apply (c, accel_data); + } + + hb_blob_ptr_t table; + AAT::kern_accelerator_data_t accel_data; + AAT::hb_aat_scratch_t scratch; + }; + protected: union { HBUINT32 version32; @@ -353,6 +417,10 @@ struct kern DEFINE_SIZE_UNION (4, version32); }; +struct kern_accelerator_t : kern::accelerator_t { + kern_accelerator_t (hb_face_t *face) : kern::accelerator_t (face) {} +}; + } /* namespace OT */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-base-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-base-table.hh index 0e57a6c790994..04a79a93a893e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-base-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-base-table.hh @@ -46,6 +46,12 @@ struct BaseCoordFormat1 return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_y (coordinate) : font->em_scale_x (coordinate); } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + return_trace ((bool) c->serializer->embed (*this)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -67,6 +73,17 @@ struct BaseCoordFormat2 return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_y (coordinate) : font->em_scale_x (coordinate); } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + + return_trace (c->serializer->check_assign (out->referenceGlyph, + c->plan->glyph_map->get (referenceGlyph), + HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -86,7 +103,7 @@ struct BaseCoordFormat2 struct BaseCoordFormat3 { hb_position_t get_coord (hb_font_t *font, - const VariationStore &var_store, + const ItemVariationStore &var_store, hb_direction_t direction) const { const Device &device = this+deviceTable; @@ -96,6 +113,37 @@ struct BaseCoordFormat3 : font->em_scale_x (coordinate) + device.get_x_delta (font, var_store); } + void collect_variation_indices (hb_set_t& varidx_set /* OUT */) const + { + unsigned varidx = (this+deviceTable).get_variation_index (); + varidx_set.add (varidx); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + + if (!c->plan->pinned_at_default) + { + unsigned var_idx = (this+deviceTable).get_variation_index (); + if (var_idx != VarIdx::NO_VARIATION) + { + hb_pair_t *v; + if (!c->plan->base_variation_idx_map.has (var_idx, &v)) + return_trace (false); + + if (unlikely (!c->serializer->check_assign (out->coordinate, coordinate + hb_second (*v), + HB_SERIALIZE_ERROR_INT_OVERFLOW))) + return_trace (false); + } + } + return_trace (out->deviceTable.serialize_copy (c->serializer, deviceTable, + this, 0, + hb_serialize_context_t::Head, + &c->plan->base_variation_idx_map)); + } bool sanitize (hb_sanitize_context_t *c) const { @@ -120,25 +168,47 @@ struct BaseCoord bool has_data () const { return u.format; } hb_position_t get_coord (hb_font_t *font, - const VariationStore &var_store, + const ItemVariationStore &var_store, hb_direction_t direction) const { switch (u.format) { - case 1: return u.format1.get_coord (font, direction); - case 2: return u.format2.get_coord (font, direction); - case 3: return u.format3.get_coord (font, var_store, direction); + case 1: hb_barrier (); return u.format1.get_coord (font, direction); + case 2: hb_barrier (); return u.format2.get_coord (font, direction); + case 3: hb_barrier (); return u.format3.get_coord (font, var_store, direction); default:return 0; } } + void collect_variation_indices (hb_set_t& varidx_set /* OUT */) const + { + switch (u.format) { + case 3: hb_barrier (); u.format3.collect_variation_indices (varidx_set); return; + default:return; + } + } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: hb_barrier (); return_trace (c->dispatch (u.format1, std::forward (ds)...)); + case 2: hb_barrier (); return_trace (c->dispatch (u.format2, std::forward (ds)...)); + case 3: hb_barrier (); return_trace (c->dispatch (u.format3, std::forward (ds)...)); + default:return_trace (c->default_return_value ()); + } + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); if (unlikely (!u.format.sanitize (c))) return_trace (false); + hb_barrier (); switch (u.format) { - case 1: return_trace (u.format1.sanitize (c)); - case 2: return_trace (u.format2.sanitize (c)); - case 3: return_trace (u.format3.sanitize (c)); + case 1: hb_barrier (); return_trace (u.format1.sanitize (c)); + case 2: hb_barrier (); return_trace (u.format2.sanitize (c)); + case 3: hb_barrier (); return_trace (u.format3.sanitize (c)); default:return_trace (false); } } @@ -160,12 +230,37 @@ struct FeatMinMaxRecord bool has_data () const { return tag; } + hb_tag_t get_feature_tag () const { return tag; } + void get_min_max (const BaseCoord **min, const BaseCoord **max) const { if (likely (min)) *min = &(this+minCoord); if (likely (max)) *max = &(this+maxCoord); } + void collect_variation_indices (const hb_subset_plan_t* plan, + const void *base, + hb_set_t& varidx_set /* OUT */) const + { + if (!plan->layout_features.has (tag)) + return; + + (base+minCoord).collect_variation_indices (varidx_set); + (base+maxCoord).collect_variation_indices (varidx_set); + } + + bool subset (hb_subset_context_t *c, + const void *base) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + if (!(out->minCoord.serialize_subset (c, minCoord, base))) + return_trace (false); + + return_trace (out->maxCoord.serialize_subset (c, maxCoord, base)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); @@ -205,6 +300,39 @@ struct MinMax } } + void collect_variation_indices (const hb_subset_plan_t* plan, + hb_set_t& varidx_set /* OUT */) const + { + (this+minCoord).collect_variation_indices (varidx_set); + (this+maxCoord).collect_variation_indices (varidx_set); + for (const FeatMinMaxRecord& record : featMinMaxRecords) + record.collect_variation_indices (plan, this, varidx_set); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + if (!(out->minCoord.serialize_subset (c, minCoord, this)) || + !(out->maxCoord.serialize_subset (c, maxCoord, this))) + return_trace (false); + + unsigned len = 0; + for (const FeatMinMaxRecord& _ : featMinMaxRecords) + { + hb_tag_t feature_tag = _.get_feature_tag (); + if (!c->plan->layout_features.has (feature_tag)) + continue; + + if (!_.subset (c, this)) return false; + len++; + } + return_trace (c->serializer->check_assign (out->featMinMaxRecords.len, len, + HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -239,6 +367,26 @@ struct BaseValues return this+baseCoords[baseline_tag_index]; } + void collect_variation_indices (hb_set_t& varidx_set /* OUT */) const + { + for (const auto& _ : baseCoords) + (this+_).collect_variation_indices (varidx_set); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + out->defaultIndex = defaultIndex; + + for (const auto& _ : baseCoords) + if (!subset_offset_array (c, out->baseCoords, this) (_)) + return_trace (false); + + return_trace (bool (out->baseCoords)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -267,7 +415,22 @@ struct BaseLangSysRecord bool has_data () const { return baseLangSysTag; } - const MinMax &get_min_max () const { return this+minMax; } + const MinMax &get_min_max (const void* base) const { return base+minMax; } + + void collect_variation_indices (const void* base, + const hb_subset_plan_t* plan, + hb_set_t& varidx_set /* OUT */) const + { (base+minMax).collect_variation_indices (plan, varidx_set); } + + bool subset (hb_subset_context_t *c, + const void *base) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->minMax.serialize_subset (c, minMax, base)); + } bool sanitize (hb_sanitize_context_t *c, const void *base) const { @@ -290,14 +453,43 @@ struct BaseScript const MinMax &get_min_max (hb_tag_t language_tag) const { const BaseLangSysRecord& record = baseLangSysRecords.bsearch (language_tag); - return record.has_data () ? record.get_min_max () : this+defaultMinMax; + return record.has_data () ? record.get_min_max (this) : this+defaultMinMax; } const BaseCoord &get_base_coord (int baseline_tag_index) const { return (this+baseValues).get_base_coord (baseline_tag_index); } bool has_values () const { return baseValues; } - bool has_min_max () const { return defaultMinMax; /* TODO What if only per-language is present? */ } + bool has_min_max () const { return defaultMinMax || baseLangSysRecords; } + + void collect_variation_indices (const hb_subset_plan_t* plan, + hb_set_t& varidx_set /* OUT */) const + { + (this+baseValues).collect_variation_indices (varidx_set); + (this+defaultMinMax).collect_variation_indices (plan, varidx_set); + + for (const BaseLangSysRecord& _ : baseLangSysRecords) + _.collect_variation_indices (this, plan, varidx_set); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + if (baseValues && !out->baseValues.serialize_subset (c, baseValues, this)) + return_trace (false); + + if (defaultMinMax && !out->defaultMinMax.serialize_subset (c, defaultMinMax, this)) + return_trace (false); + + for (const auto& _ : baseLangSysRecords) + if (!_.subset (c, this)) return_trace (false); + + return_trace (c->serializer->check_assign (out->baseLangSysRecords.len, baseLangSysRecords.len, + HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } bool sanitize (hb_sanitize_context_t *c) const { @@ -331,9 +523,31 @@ struct BaseScriptRecord bool has_data () const { return baseScriptTag; } + hb_tag_t get_script_tag () const { return baseScriptTag; } + const BaseScript &get_base_script (const BaseScriptList *list) const { return list+baseScript; } + void collect_variation_indices (const hb_subset_plan_t* plan, + const void* list, + hb_set_t& varidx_set /* OUT */) const + { + if (!plan->layout_scripts.has (baseScriptTag)) + return; + + (list+baseScript).collect_variation_indices (plan, varidx_set); + } + + bool subset (hb_subset_context_t *c, + const void *base) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->baseScript.serialize_subset (c, baseScript, base)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); @@ -360,6 +574,33 @@ struct BaseScriptList return record->has_data () ? record->get_base_script (this) : Null (BaseScript); } + void collect_variation_indices (const hb_subset_plan_t* plan, + hb_set_t& varidx_set /* OUT */) const + { + for (const BaseScriptRecord& _ : baseScriptRecords) + _.collect_variation_indices (plan, this, varidx_set); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + unsigned len = 0; + for (const BaseScriptRecord& _ : baseScriptRecords) + { + hb_tag_t script_tag = _.get_script_tag (); + if (!c->plan->layout_scripts.has (script_tag)) + continue; + + if (!_.subset (c, this)) return false; + len++; + } + return_trace (c->serializer->check_assign (out->baseScriptRecords.len, len, + HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -421,6 +662,20 @@ struct Axis return true; } + void collect_variation_indices (const hb_subset_plan_t* plan, + hb_set_t& varidx_set /* OUT */) const + { (this+baseScriptList).collect_variation_indices (plan, varidx_set); } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + + out->baseTagList.serialize_copy (c->serializer, baseTagList, this); + return_trace (out->baseScriptList.serialize_subset (c, baseScriptList, this)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -452,8 +707,77 @@ struct BASE const Axis &get_axis (hb_direction_t direction) const { return HB_DIRECTION_IS_VERTICAL (direction) ? this+vAxis : this+hAxis; } - const VariationStore &get_var_store () const - { return version.to_int () < 0x00010001u ? Null (VariationStore) : this+varStore; } + bool has_var_store () const + { return version.to_int () >= 0x00010001u && varStore != 0; } + + const ItemVariationStore &get_var_store () const + { return version.to_int () < 0x00010001u ? Null (ItemVariationStore) : this+varStore; } + + void collect_variation_indices (const hb_subset_plan_t* plan, + hb_set_t& varidx_set /* OUT */) const + { + (this+hAxis).collect_variation_indices (plan, varidx_set); + (this+vAxis).collect_variation_indices (plan, varidx_set); + } + + bool subset_varstore (hb_subset_context_t *c, + BASE *out /* OUT */) const + { + TRACE_SUBSET (this); + if (!c->serializer->allocate_size> (Offset32To::static_size)) + return_trace (false); + if (!c->plan->normalized_coords) + return_trace (out->varStore.serialize_subset (c, varStore, this, c->plan->base_varstore_inner_maps.as_array ())); + + if (c->plan->all_axes_pinned) + return_trace (true); + + item_variations_t item_vars; + if (!item_vars.instantiate (this+varStore, c->plan, true, true, + c->plan->base_varstore_inner_maps.as_array ())) + return_trace (false); + + if (!out->varStore.serialize_serialize (c->serializer, + item_vars.has_long_word (), + c->plan->axis_tags, + item_vars.get_region_list (), + item_vars.get_vardata_encodings ())) + return_trace (false); + + const hb_map_t &varidx_map = item_vars.get_varidx_map (); + /* base_variation_idx_map in the plan is old_varidx->(varidx, delta) + * mapping, new varidx is generated for subsetting, we need to remap this + * after instancing */ + for (auto _ : c->plan->base_variation_idx_map.iter_ref ()) + { + uint32_t varidx = _.second.first; + uint32_t *new_varidx; + if (varidx_map.has (varidx, &new_varidx)) + _.second.first = *new_varidx; + else + _.second.first = HB_OT_LAYOUT_NO_VARIATIONS_INDEX; + } + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + out->version = version; + if (has_var_store () && !subset_varstore (c, out)) + return_trace (false); + + if (hAxis && !out->hAxis.serialize_subset (c, hAxis, this)) + return_trace (false); + + if (vAxis && !out->vAxis.serialize_subset (c, vAxis, this)) + return_trace (false); + + return_trace (true); + } bool get_baseline (hb_font_t *font, hb_tag_t baseline_tag, @@ -486,7 +810,7 @@ struct BASE &min_coord, &max_coord)) return false; - const VariationStore &var_store = get_var_store (); + const ItemVariationStore &var_store = get_var_store (); if (likely (min && min_coord)) *min = min_coord->get_coord (font, var_store, direction); if (likely (max && max_coord)) *max = max_coord->get_coord (font, var_store, direction); return true; @@ -496,6 +820,7 @@ struct BASE { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && + hb_barrier () && likely (version.major == 1) && hAxis.sanitize (c, this) && vAxis.sanitize (c, this) && @@ -508,7 +833,7 @@ struct BASE * of BASE table (may be NULL) */ Offset16TovAxis; /* Offset to vertical Axis table, from beginning * of BASE table (may be NULL) */ - Offset32To + Offset32To varStore; /* Offset to the table of Item Variation * Store--from beginning of BASE * header (may be NULL). Introduced diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh index 9216a9a0fe70f..7ee34185575d1 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh @@ -34,6 +34,7 @@ #include "hb-open-type.hh" #include "hb-set.hh" #include "hb-bimap.hh" +#include "hb-cache.hh" #include "OT/Layout/Common/Coverage.hh" #include "OT/Layout/types.hh" @@ -64,7 +65,7 @@ struct hb_collect_feature_substitutes_with_var_context_t const hb_hashmap_t *axes_location; hb_hashmap_t> *record_cond_idx_map; hb_hashmap_t *feature_substitutes_map; - bool& insert_catch_all_feature_variation_record; + hb_set_t& catch_all_record_feature_idxes; // not stored in subset_plan hb_set_t *feature_indices; @@ -142,6 +143,8 @@ struct hb_subset_layout_context_t : const hb_map_t *feature_index_map; const hb_hashmap_t *feature_substitutes_map; hb_hashmap_t> *feature_record_cond_idx_map; + const hb_set_t *catch_all_record_feature_idxes; + const hb_hashmap_t> *feature_idx_tag_map; unsigned cur_script_index; unsigned cur_feature_var_record_idx; @@ -164,6 +167,8 @@ struct hb_subset_layout_context_t : feature_index_map = &c_->plan->gsub_features; feature_substitutes_map = &c_->plan->gsub_feature_substitutes_map; feature_record_cond_idx_map = c_->plan->user_axes_location.is_empty () ? nullptr : &c_->plan->gsub_feature_record_cond_idx_map; + catch_all_record_feature_idxes = &c_->plan->gsub_old_features; + feature_idx_tag_map = &c_->plan->gsub_old_feature_idx_tag_map; } else { @@ -172,6 +177,8 @@ struct hb_subset_layout_context_t : feature_index_map = &c_->plan->gpos_features; feature_substitutes_map = &c_->plan->gpos_feature_substitutes_map; feature_record_cond_idx_map = c_->plan->user_axes_location.is_empty () ? nullptr : &c_->plan->gpos_feature_record_cond_idx_map; + catch_all_record_feature_idxes = &c_->plan->gpos_old_features; + feature_idx_tag_map = &c_->plan->gpos_old_feature_idx_tag_map; } } @@ -182,7 +189,7 @@ struct hb_subset_layout_context_t : unsigned lookup_index_count; }; -struct VariationStore; +struct ItemVariationStore; struct hb_collect_variation_indices_context_t : hb_dispatch_context_t { @@ -454,6 +461,7 @@ struct FeatureParamsSize { TRACE_SANITIZE (this); if (unlikely (!c->check_struct (this))) return_trace (false); + hb_barrier (); /* This subtable has some "history", if you will. Some earlier versions of * Adobe tools calculated the offset of the FeatureParams subtable from the @@ -639,8 +647,7 @@ struct FeatureParamsCharacterVariants return; unsigned last_name_id = (unsigned) firstParamUILabelNameID + (unsigned) numNamedParameters - 1; - if (last_name_id >= 256 && last_name_id <= 32767) - nameids_to_retain->add_range (firstParamUILabelNameID, last_name_id); + nameids_to_retain->add_range (firstParamUILabelNameID, last_name_id); } bool subset (hb_subset_context_t *c) const @@ -820,6 +827,7 @@ struct Feature TRACE_SANITIZE (this); if (unlikely (!(c->check_struct (this) && lookupIndex.sanitize (c)))) return_trace (false); + hb_barrier (); /* Some earlier versions of Adobe tools calculated the offset of the * FeatureParams subtable from the beginning of the FeatureList table! @@ -838,6 +846,7 @@ struct Feature unsigned int orig_offset = featureParams; if (unlikely (!featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE))) return_trace (false); + hb_barrier (); if (featureParams == 0 && closure && closure->tag == HB_TAG ('s','i','z','e') && @@ -900,7 +909,8 @@ struct Record { TRACE_SANITIZE (this); const Record_sanitize_closure_t closure = {tag, base}; - return_trace (c->check_struct (this) && offset.sanitize (c, base, &closure)); + return_trace (c->check_struct (this) && + offset.sanitize (c, base, &closure)); } Tag tag; /* 4-byte Tag identifier */ @@ -1371,10 +1381,20 @@ struct Lookup if (lookupFlag & LookupFlag::UseMarkFilteringSet) { - if (unlikely (!c->serializer->extend (out))) return_trace (false); const HBUINT16 &markFilteringSet = StructAfter (subTable); - HBUINT16 &outMarkFilteringSet = StructAfter (out->subTable); - outMarkFilteringSet = markFilteringSet; + hb_codepoint_t *idx; + if (!c->plan->used_mark_sets_map.has (markFilteringSet, &idx)) + { + unsigned new_flag = lookupFlag; + new_flag &= ~LookupFlag::UseMarkFilteringSet; + out->lookupFlag = new_flag; + } + else + { + if (unlikely (!c->serializer->extend (out))) return_trace (false); + HBUINT16 &outMarkFilteringSet = StructAfter (out->subTable); + outMarkFilteringSet = *idx; + } } // Always keep the lookup even if it's empty. The rest of layout subsetting depends on lookup @@ -1391,6 +1411,7 @@ struct Lookup { TRACE_SANITIZE (this); if (!(c->check_struct (this) && subTable.sanitize (c))) return_trace (false); + hb_barrier (); unsigned subtables = get_subtable_count (); if (unlikely (!c->visit_subtables (subtables))) return_trace (false); @@ -1406,6 +1427,8 @@ struct Lookup if (unlikely (get_type () == TSubTable::Extension && !c->get_edit_count ())) { + hb_barrier (); + /* The spec says all subtables of an Extension lookup should * have the same type, which shall not be the Extension type * itself (but we already checked for that). @@ -1827,7 +1850,7 @@ struct ClassDefFormat2_4 hb_sorted_vector_t glyph_and_klass; hb_set_t orig_klasses; - if (glyph_set.get_population () * hb_bit_storage ((unsigned) rangeRecord.len) / 2 + if (glyph_set.get_population () * hb_bit_storage ((unsigned) rangeRecord.len) < get_population ()) { for (hb_codepoint_t g : glyph_set) @@ -1908,7 +1931,7 @@ struct ClassDefFormat2_4 bool intersects (const hb_set_t *glyphs) const { - if (rangeRecord.len > glyphs->get_population () * hb_bit_storage ((unsigned) rangeRecord.len) / 2) + if (rangeRecord.len > glyphs->get_population () * hb_bit_storage ((unsigned) rangeRecord.len)) { for (auto g : *glyphs) if (get_class (g)) @@ -1977,7 +2000,7 @@ struct ClassDefFormat2_4 } unsigned count = rangeRecord.len; - if (count > glyphs->get_population () * hb_bit_storage (count) * 8) + if (count > glyphs->get_population () * hb_bit_storage (count)) { for (auto g : *glyphs) { @@ -2045,24 +2068,33 @@ struct ClassDef unsigned int get_class (hb_codepoint_t glyph_id) const { switch (u.format) { - case 1: return u.format1.get_class (glyph_id); - case 2: return u.format2.get_class (glyph_id); + case 1: hb_barrier (); return u.format1.get_class (glyph_id); + case 2: hb_barrier (); return u.format2.get_class (glyph_id); #ifndef HB_NO_BEYOND_64K - case 3: return u.format3.get_class (glyph_id); - case 4: return u.format4.get_class (glyph_id); + case 3: hb_barrier (); return u.format3.get_class (glyph_id); + case 4: hb_barrier (); return u.format4.get_class (glyph_id); #endif default:return 0; } } + unsigned int get_class (hb_codepoint_t glyph_id, + hb_ot_lookup_cache_t *cache) const + { + unsigned klass; + if (cache && cache->get (glyph_id, &klass)) return klass; + klass = get_class (glyph_id); + if (cache) cache->set (glyph_id, klass); + return klass; + } unsigned get_population () const { switch (u.format) { - case 1: return u.format1.get_population (); - case 2: return u.format2.get_population (); + case 1: hb_barrier (); return u.format1.get_population (); + case 2: hb_barrier (); return u.format2.get_population (); #ifndef HB_NO_BEYOND_64K - case 3: return u.format3.get_population (); - case 4: return u.format4.get_population (); + case 3: hb_barrier (); return u.format3.get_population (); + case 4: hb_barrier (); return u.format4.get_population (); #endif default:return NOT_COVERED; } @@ -2124,11 +2156,11 @@ struct ClassDef switch (u.format) { - case 1: return_trace (u.format1.serialize (c, it)); - case 2: return_trace (u.format2.serialize (c, it)); + case 1: hb_barrier (); return_trace (u.format1.serialize (c, it)); + case 2: hb_barrier (); return_trace (u.format2.serialize (c, it)); #ifndef HB_NO_BEYOND_64K - case 3: return_trace (u.format3.serialize (c, it)); - case 4: return_trace (u.format4.serialize (c, it)); + case 3: hb_barrier (); return_trace (u.format3.serialize (c, it)); + case 4: hb_barrier (); return_trace (u.format4.serialize (c, it)); #endif default:return_trace (false); } @@ -2142,11 +2174,11 @@ struct ClassDef { TRACE_SUBSET (this); switch (u.format) { - case 1: return_trace (u.format1.subset (c, klass_map, keep_empty_table, use_class_zero, glyph_filter)); - case 2: return_trace (u.format2.subset (c, klass_map, keep_empty_table, use_class_zero, glyph_filter)); + case 1: hb_barrier (); return_trace (u.format1.subset (c, klass_map, keep_empty_table, use_class_zero, glyph_filter)); + case 2: hb_barrier (); return_trace (u.format2.subset (c, klass_map, keep_empty_table, use_class_zero, glyph_filter)); #ifndef HB_NO_BEYOND_64K - case 3: return_trace (u.format3.subset (c, klass_map, keep_empty_table, use_class_zero, glyph_filter)); - case 4: return_trace (u.format4.subset (c, klass_map, keep_empty_table, use_class_zero, glyph_filter)); + case 3: hb_barrier (); return_trace (u.format3.subset (c, klass_map, keep_empty_table, use_class_zero, glyph_filter)); + case 4: hb_barrier (); return_trace (u.format4.subset (c, klass_map, keep_empty_table, use_class_zero, glyph_filter)); #endif default:return_trace (false); } @@ -2156,12 +2188,13 @@ struct ClassDef { TRACE_SANITIZE (this); if (!u.format.sanitize (c)) return_trace (false); + hb_barrier (); switch (u.format) { - case 1: return_trace (u.format1.sanitize (c)); - case 2: return_trace (u.format2.sanitize (c)); + case 1: hb_barrier (); return_trace (u.format1.sanitize (c)); + case 2: hb_barrier (); return_trace (u.format2.sanitize (c)); #ifndef HB_NO_BEYOND_64K - case 3: return_trace (u.format3.sanitize (c)); - case 4: return_trace (u.format4.sanitize (c)); + case 3: hb_barrier (); return_trace (u.format3.sanitize (c)); + case 4: hb_barrier (); return_trace (u.format4.sanitize (c)); #endif default:return_trace (true); } @@ -2170,11 +2203,11 @@ struct ClassDef unsigned cost () const { switch (u.format) { - case 1: return u.format1.cost (); - case 2: return u.format2.cost (); + case 1: hb_barrier (); return u.format1.cost (); + case 2: hb_barrier (); return u.format2.cost (); #ifndef HB_NO_BEYOND_64K - case 3: return u.format3.cost (); - case 4: return u.format4.cost (); + case 3: hb_barrier (); return u.format3.cost (); + case 4: hb_barrier (); return u.format4.cost (); #endif default:return 0u; } @@ -2186,11 +2219,11 @@ struct ClassDef bool collect_coverage (set_t *glyphs) const { switch (u.format) { - case 1: return u.format1.collect_coverage (glyphs); - case 2: return u.format2.collect_coverage (glyphs); + case 1: hb_barrier (); return u.format1.collect_coverage (glyphs); + case 2: hb_barrier (); return u.format2.collect_coverage (glyphs); #ifndef HB_NO_BEYOND_64K - case 3: return u.format3.collect_coverage (glyphs); - case 4: return u.format4.collect_coverage (glyphs); + case 3: hb_barrier (); return u.format3.collect_coverage (glyphs); + case 4: hb_barrier (); return u.format4.collect_coverage (glyphs); #endif default:return false; } @@ -2202,11 +2235,11 @@ struct ClassDef bool collect_class (set_t *glyphs, unsigned int klass) const { switch (u.format) { - case 1: return u.format1.collect_class (glyphs, klass); - case 2: return u.format2.collect_class (glyphs, klass); + case 1: hb_barrier (); return u.format1.collect_class (glyphs, klass); + case 2: hb_barrier (); return u.format2.collect_class (glyphs, klass); #ifndef HB_NO_BEYOND_64K - case 3: return u.format3.collect_class (glyphs, klass); - case 4: return u.format4.collect_class (glyphs, klass); + case 3: hb_barrier (); return u.format3.collect_class (glyphs, klass); + case 4: hb_barrier (); return u.format4.collect_class (glyphs, klass); #endif default:return false; } @@ -2215,11 +2248,11 @@ struct ClassDef bool intersects (const hb_set_t *glyphs) const { switch (u.format) { - case 1: return u.format1.intersects (glyphs); - case 2: return u.format2.intersects (glyphs); + case 1: hb_barrier (); return u.format1.intersects (glyphs); + case 2: hb_barrier (); return u.format2.intersects (glyphs); #ifndef HB_NO_BEYOND_64K - case 3: return u.format3.intersects (glyphs); - case 4: return u.format4.intersects (glyphs); + case 3: hb_barrier (); return u.format3.intersects (glyphs); + case 4: hb_barrier (); return u.format4.intersects (glyphs); #endif default:return false; } @@ -2227,11 +2260,11 @@ struct ClassDef bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const { switch (u.format) { - case 1: return u.format1.intersects_class (glyphs, klass); - case 2: return u.format2.intersects_class (glyphs, klass); + case 1: hb_barrier (); return u.format1.intersects_class (glyphs, klass); + case 2: hb_barrier (); return u.format2.intersects_class (glyphs, klass); #ifndef HB_NO_BEYOND_64K - case 3: return u.format3.intersects_class (glyphs, klass); - case 4: return u.format4.intersects_class (glyphs, klass); + case 3: hb_barrier (); return u.format3.intersects_class (glyphs, klass); + case 4: hb_barrier (); return u.format4.intersects_class (glyphs, klass); #endif default:return false; } @@ -2240,11 +2273,11 @@ struct ClassDef void intersected_class_glyphs (const hb_set_t *glyphs, unsigned klass, hb_set_t *intersect_glyphs) const { switch (u.format) { - case 1: return u.format1.intersected_class_glyphs (glyphs, klass, intersect_glyphs); - case 2: return u.format2.intersected_class_glyphs (glyphs, klass, intersect_glyphs); + case 1: hb_barrier (); return u.format1.intersected_class_glyphs (glyphs, klass, intersect_glyphs); + case 2: hb_barrier (); return u.format2.intersected_class_glyphs (glyphs, klass, intersect_glyphs); #ifndef HB_NO_BEYOND_64K - case 3: return u.format3.intersected_class_glyphs (glyphs, klass, intersect_glyphs); - case 4: return u.format4.intersected_class_glyphs (glyphs, klass, intersect_glyphs); + case 3: hb_barrier (); return u.format3.intersected_class_glyphs (glyphs, klass, intersect_glyphs); + case 4: hb_barrier (); return u.format4.intersected_class_glyphs (glyphs, klass, intersect_glyphs); #endif default:return; } @@ -2253,11 +2286,11 @@ struct ClassDef void intersected_classes (const hb_set_t *glyphs, hb_set_t *intersect_classes) const { switch (u.format) { - case 1: return u.format1.intersected_classes (glyphs, intersect_classes); - case 2: return u.format2.intersected_classes (glyphs, intersect_classes); + case 1: hb_barrier (); return u.format1.intersected_classes (glyphs, intersect_classes); + case 2: hb_barrier (); return u.format2.intersected_classes (glyphs, intersect_classes); #ifndef HB_NO_BEYOND_64K - case 3: return u.format3.intersected_classes (glyphs, intersect_classes); - case 4: return u.format4.intersected_classes (glyphs, intersect_classes); + case 3: hb_barrier (); return u.format3.intersected_classes (glyphs, intersect_classes); + case 4: hb_barrier (); return u.format4.intersected_classes (glyphs, intersect_classes); #endif default:return; } @@ -2447,6 +2480,8 @@ struct VarRegionAxis int peak = peakCoord.to_int (); if (peak == 0 || coord == peak) return 1.f; + else if (coord == 0) // Faster + return 0.f; int start = startCoord.to_int (), end = endCoord.to_int (); @@ -2470,8 +2505,6 @@ struct VarRegionAxis { TRACE_SANITIZE (this); return_trace (c->check_struct (this)); - /* TODO Handle invalid start/peak/end configs, so we don't - * have to do that at runtime. */ } bool serialize (hb_serialize_context_t *c) const @@ -2487,12 +2520,41 @@ struct VarRegionAxis public: DEFINE_SIZE_STATIC (6); }; +struct SparseVarRegionAxis +{ + float evaluate (const int *coords, unsigned int coord_len) const + { + unsigned i = axisIndex; + int coord = i < coord_len ? coords[i] : 0; + return axis.evaluate (coord); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + bool serialize (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (this)); + } + + public: + HBUINT16 axisIndex; + VarRegionAxis axis; + public: + DEFINE_SIZE_STATIC (8); +}; -#define REGION_CACHE_ITEM_CACHE_INVALID 2.f +#define REGION_CACHE_ITEM_CACHE_INVALID INT_MIN +#define REGION_CACHE_ITEM_MULTIPLIER (float (1 << ((sizeof (int) * 8) - 2))) +#define REGION_CACHE_ITEM_DIVISOR (1.f / float (1 << ((sizeof (int) * 8) - 2))) struct VarRegionList { - using cache_t = float; + using cache_t = hb_atomic_t; float evaluate (unsigned int region_index, const int *coords, unsigned int coord_len, @@ -2501,12 +2563,12 @@ struct VarRegionList if (unlikely (region_index >= regionCount)) return 0.; - float *cached_value = nullptr; + cache_t *cached_value = nullptr; if (cache) { cached_value = &(cache[region_index]); - if (likely (*cached_value != REGION_CACHE_ITEM_CACHE_INVALID)) - return *cached_value; + if (*cached_value != REGION_CACHE_ITEM_CACHE_INVALID) + return *cached_value * REGION_CACHE_ITEM_DIVISOR; } const VarRegionAxis *axes = axesZ.arrayZ + (region_index * axisCount); @@ -2527,14 +2589,16 @@ struct VarRegionList } if (cache) - *cached_value = v; + *cached_value = v * REGION_CACHE_ITEM_MULTIPLIER; return v; } bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && axesZ.sanitize (c, axisCount * regionCount)); + return_trace (c->check_struct (this) && + hb_barrier () && + axesZ.sanitize (c, axisCount * regionCount)); } bool serialize (hb_serialize_context_t *c, @@ -2615,7 +2679,7 @@ struct VarRegionList float max_val = axis_region->endCoord.to_float (); if (def_val != 0.f) - axis_tuples.set (*axis_tag, Triple (min_val, def_val, max_val)); + axis_tuples.set (*axis_tag, Triple ((double) min_val, (double) def_val, (double) max_val)); axis_region++; } return !axis_tuples.in_error (); @@ -2649,6 +2713,65 @@ struct VarRegionList DEFINE_SIZE_ARRAY (4, axesZ); }; +struct SparseVariationRegion : Array16Of +{ + float evaluate (const int *coords, unsigned int coord_len) const + { + float v = 1.f; + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + { + float factor = arrayZ[i].evaluate (coords, coord_len); + if (factor == 0.f) + return 0.; + v *= factor; + } + return v; + } +}; + +struct SparseVarRegionList +{ + using cache_t = hb_atomic_t; + + float evaluate (unsigned int region_index, + const int *coords, unsigned int coord_len, + cache_t *cache = nullptr) const + { + if (unlikely (region_index >= regions.len)) + return 0.; + + cache_t *cached_value = nullptr; + if (cache) + { + cached_value = &(cache[region_index]); + if (*cached_value != REGION_CACHE_ITEM_CACHE_INVALID) + return *cached_value * REGION_CACHE_ITEM_DIVISOR; + } + + const SparseVariationRegion ®ion = this+regions[region_index]; + + float v = region.evaluate (coords, coord_len); + + if (cache) + *cached_value = v * REGION_CACHE_ITEM_MULTIPLIER; + return v; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (regions.sanitize (c, this)); + } + + public: + Array16Of> + regions; + public: + DEFINE_SIZE_ARRAY (2, regions); +}; + + struct VarData { unsigned int get_item_count () const @@ -2728,6 +2851,7 @@ struct VarData TRACE_SANITIZE (this); return_trace (c->check_struct (this) && regionIndices.sanitize (c) && + hb_barrier () && wordCount () <= regionIndices.len && c->check_range (get_delta_bytes (), itemCount, @@ -2739,8 +2863,13 @@ struct VarData const hb_vector_t*>& rows) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (this))) return_trace (false); unsigned row_count = rows.length; + if (!row_count) { + // Nothing to serialize, will be empty. + return false; + } + + if (unlikely (!c->extend_min (this))) return_trace (false); itemCount = row_count; int min_threshold = has_long ? -65536 : -128; @@ -3009,7 +3138,53 @@ struct VarData DEFINE_SIZE_ARRAY (6, regionIndices); }; -struct VariationStore +struct MultiVarData +{ + unsigned int get_size () const + { return min_size + - regionIndices.min_size + regionIndices.get_size () + + StructAfter (regionIndices).get_size (); + } + + void get_delta (unsigned int inner, + const int *coords, unsigned int coord_count, + const SparseVarRegionList ®ions, + hb_array_t out, + SparseVarRegionList::cache_t *cache = nullptr) const + { + auto &deltaSets = StructAfter (regionIndices); + + auto values_iter = deltaSets.fetcher (inner); + unsigned regionCount = regionIndices.len; + for (unsigned regionIndex = 0; regionIndex < regionCount; regionIndex++) + { + float scalar = regions.evaluate (regionIndices.arrayZ[regionIndex], + coords, coord_count, + cache); + values_iter.add_to (out, scalar); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (format.sanitize (c) && + hb_barrier () && + format == 1 && + regionIndices.sanitize (c) && + hb_barrier () && + StructAfter (regionIndices).sanitize (c)); + } + + protected: + HBUINT8 format; // 1 + Array16Of regionIndices; + TupleList deltaSetsX; + public: + DEFINE_SIZE_MIN (8); +}; + +struct ItemVariationStore { friend struct item_variations_t; using cache_t = VarRegionList::cache_t; @@ -3019,10 +3194,10 @@ struct VariationStore #ifdef HB_NO_VAR return nullptr; #endif - auto &r = this+regions; - unsigned count = r.regionCount; + unsigned count = (this+regions).regionCount; + if (!count) return nullptr; - float *cache = (float *) hb_malloc (sizeof (float) * count); + cache_t *cache = (cache_t *) hb_malloc (sizeof (float) * count); if (unlikely (!cache)) return nullptr; for (unsigned i = 0; i < count; i++) @@ -3061,7 +3236,7 @@ struct VariationStore return get_delta (outer, inner, coords, coord_count, cache); } float get_delta (unsigned int index, - hb_array_t coords, + hb_array_t coords, VarRegionList::cache_t *cache = nullptr) const { return get_delta (index, @@ -3077,6 +3252,7 @@ struct VariationStore TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && format == 1 && regions.sanitize (c, this) && dataSets.sanitize (c, this)); @@ -3113,7 +3289,7 @@ struct VariationStore } bool serialize (hb_serialize_context_t *c, - const VariationStore *src, + const ItemVariationStore *src, const hb_array_t &inner_maps) { TRACE_SERIALIZE (this); @@ -3169,7 +3345,7 @@ struct VariationStore return_trace (true); } - VariationStore *copy (hb_serialize_context_t *c) const + ItemVariationStore *copy (hb_serialize_context_t *c) const { TRACE_SERIALIZE (this); auto *out = c->start_embed (this); @@ -3180,6 +3356,8 @@ struct VariationStore for (unsigned i = 0; i < count; i++) { hb_inc_bimap_t *map = inner_maps.push (); + if (!c->propagate_error(inner_maps)) + return_trace(nullptr); auto &data = this+dataSets[i]; unsigned itemCount = data.get_item_count (); @@ -3199,7 +3377,7 @@ struct VariationStore return_trace (false); #endif - VariationStore *varstore_prime = c->serializer->start_embed (); + ItemVariationStore *varstore_prime = c->serializer->start_embed (); if (unlikely (!varstore_prime)) return_trace (false); varstore_prime->serialize (c->serializer, this, inner_maps); @@ -3265,8 +3443,370 @@ struct VariationStore DEFINE_SIZE_ARRAY_SIZED (8, dataSets); }; +struct MultiItemVariationStore +{ + using cache_t = SparseVarRegionList::cache_t; + + cache_t *create_cache (hb_array_t static_cache = hb_array_t ()) const + { +#ifdef HB_NO_VAR + return nullptr; +#endif + auto &r = this+regions; + unsigned count = r.regions.len; + + cache_t *cache; + if (count <= static_cache.length) + cache = static_cache.arrayZ; + else + { + cache = (cache_t *) hb_malloc (sizeof (float) * count); + if (unlikely (!cache)) return nullptr; + } + + for (unsigned i = 0; i < count; i++) + cache[i] = REGION_CACHE_ITEM_CACHE_INVALID; + + return cache; + } + + static void destroy_cache (cache_t *cache, + hb_array_t static_cache = hb_array_t ()) + { + if (cache != static_cache.arrayZ) + hb_free (cache); + } + + private: + void get_delta (unsigned int outer, unsigned int inner, + const int *coords, unsigned int coord_count, + hb_array_t out, + VarRegionList::cache_t *cache = nullptr) const + { +#ifdef HB_NO_VAR + return; +#endif + + if (unlikely (outer >= dataSets.len)) + return; + + return (this+dataSets[outer]).get_delta (inner, + coords, coord_count, + this+regions, + out, + cache); + } + + public: + void get_delta (unsigned int index, + const int *coords, unsigned int coord_count, + hb_array_t out, + VarRegionList::cache_t *cache = nullptr) const + { + unsigned int outer = index >> 16; + unsigned int inner = index & 0xFFFF; + get_delta (outer, inner, coords, coord_count, out, cache); + } + void get_delta (unsigned int index, + hb_array_t coords, + hb_array_t out, + VarRegionList::cache_t *cache = nullptr) const + { + return get_delta (index, + coords.arrayZ, coords.length, + out, + cache); + } + + bool sanitize (hb_sanitize_context_t *c) const + { +#ifdef HB_NO_VAR + return true; +#endif + + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + hb_barrier () && + format == 1 && + regions.sanitize (c, this) && + dataSets.sanitize (c, this)); + } + + protected: + HBUINT16 format; // 1 + Offset32To regions; + Array16OfOffset32To dataSets; + public: + DEFINE_SIZE_ARRAY_SIZED (8, dataSets); +}; + #undef REGION_CACHE_ITEM_CACHE_INVALID +template +struct DeltaSetIndexMapFormat01 +{ + friend struct DeltaSetIndexMap; + + unsigned get_size () const + { return min_size + mapCount * get_width (); } + + private: + DeltaSetIndexMapFormat01* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (this)); + } + + template + bool serialize (hb_serialize_context_t *c, const T &plan) + { + unsigned int width = plan.get_width (); + unsigned int inner_bit_count = plan.get_inner_bit_count (); + const hb_array_t output_map = plan.get_output_map (); + + TRACE_SERIALIZE (this); + if (unlikely (output_map.length && ((((inner_bit_count-1)&~0xF)!=0) || (((width-1)&~0x3)!=0)))) + return_trace (false); + if (unlikely (!c->extend_min (this))) return_trace (false); + + entryFormat = ((width-1)<<4)|(inner_bit_count-1); + mapCount = output_map.length; + HBUINT8 *p = c->allocate_size (width * output_map.length); + if (unlikely (!p)) return_trace (false); + for (unsigned int i = 0; i < output_map.length; i++) + { + unsigned int v = output_map.arrayZ[i]; + if (v) + { + unsigned int outer = v >> 16; + unsigned int inner = v & 0xFFFF; + unsigned int u = (outer << inner_bit_count) | inner; + for (unsigned int w = width; w > 0;) + { + p[--w] = u; + u >>= 8; + } + } + p += width; + } + return_trace (true); + } + + uint32_t map (unsigned int v) const /* Returns 16.16 outer.inner. */ + { + /* If count is zero, pass value unchanged. This takes + * care of direct mapping for advance map. */ + if (!mapCount) + return v; + + if (v >= mapCount) + v = mapCount - 1; + + unsigned int u = 0; + { /* Fetch it. */ + unsigned int w = get_width (); + const HBUINT8 *p = mapDataZ.arrayZ + w * v; + for (; w; w--) + u = (u << 8) + *p++; + } + + { /* Repack it. */ + unsigned int n = get_inner_bit_count (); + unsigned int outer = u >> n; + unsigned int inner = u & ((1 << n) - 1); + u = (outer<<16) | inner; + } + + return u; + } + + unsigned get_map_count () const { return mapCount; } + unsigned get_width () const { return ((entryFormat >> 4) & 3) + 1; } + unsigned get_inner_bit_count () const { return (entryFormat & 0xF) + 1; } + + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + hb_barrier () && + c->check_range (mapDataZ.arrayZ, + mapCount, + get_width ())); + } + + protected: + HBUINT8 format; /* Format identifier--format = 0 */ + HBUINT8 entryFormat; /* A packed field that describes the compressed + * representation of delta-set indices. */ + MapCountT mapCount; /* The number of mapping entries. */ + UnsizedArrayOf + mapDataZ; /* The delta-set index mapping data. */ + + public: + DEFINE_SIZE_ARRAY (2+MapCountT::static_size, mapDataZ); +}; + +struct DeltaSetIndexMap +{ + template + bool serialize (hb_serialize_context_t *c, const T &plan) + { + TRACE_SERIALIZE (this); + unsigned length = plan.get_output_map ().length; + u.format = length <= 0xFFFF ? 0 : 1; + switch (u.format) { + case 0: hb_barrier (); return_trace (u.format0.serialize (c, plan)); + case 1: hb_barrier (); return_trace (u.format1.serialize (c, plan)); + default:return_trace (false); + } + } + + uint32_t map (unsigned v) const + { + switch (u.format) { + case 0: hb_barrier (); return (u.format0.map (v)); + case 1: hb_barrier (); return (u.format1.map (v)); + default:return v; + } + } + + unsigned get_map_count () const + { + switch (u.format) { + case 0: hb_barrier (); return u.format0.get_map_count (); + case 1: hb_barrier (); return u.format1.get_map_count (); + default:return 0; + } + } + + unsigned get_width () const + { + switch (u.format) { + case 0: hb_barrier (); return u.format0.get_width (); + case 1: hb_barrier (); return u.format1.get_width (); + default:return 0; + } + } + + unsigned get_inner_bit_count () const + { + switch (u.format) { + case 0: hb_barrier (); return u.format0.get_inner_bit_count (); + case 1: hb_barrier (); return u.format1.get_inner_bit_count (); + default:return 0; + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + hb_barrier (); + switch (u.format) { + case 0: hb_barrier (); return_trace (u.format0.sanitize (c)); + case 1: hb_barrier (); return_trace (u.format1.sanitize (c)); + default:return_trace (true); + } + } + + DeltaSetIndexMap* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + switch (u.format) { + case 0: hb_barrier (); return_trace (reinterpret_cast (u.format0.copy (c))); + case 1: hb_barrier (); return_trace (reinterpret_cast (u.format1.copy (c))); + default:return_trace (nullptr); + } + } + + protected: + union { + HBUINT8 format; /* Format identifier */ + DeltaSetIndexMapFormat01 format0; + DeltaSetIndexMapFormat01 format1; + } u; + public: + DEFINE_SIZE_UNION (1, format); +}; + + +struct ItemVarStoreInstancer +{ + ItemVarStoreInstancer (const ItemVariationStore *varStore_, + const DeltaSetIndexMap *varIdxMap, + hb_array_t coords, + VarRegionList::cache_t *cache = nullptr) : + varStore (varStore_), varIdxMap (varIdxMap), coords (coords), cache (cache) + { + if (!varStore) + varStore = &Null(ItemVariationStore); + } + + operator bool () const { return varStore && bool (coords); } + + float operator[] (uint32_t varIdx) const + { return (*this) (varIdx); } + + float operator() (uint32_t varIdx, unsigned short offset = 0) const + { + if (!coords || varIdx == VarIdx::NO_VARIATION) + return 0.f; + + varIdx += offset; + if (varIdxMap) + varIdx = varIdxMap->map (varIdx); + return varStore->get_delta (varIdx, coords, cache); + } + + const ItemVariationStore *varStore; + const DeltaSetIndexMap *varIdxMap; + hb_array_t coords; + VarRegionList::cache_t *cache; +}; + +struct MultiItemVarStoreInstancer +{ + MultiItemVarStoreInstancer (const MultiItemVariationStore *varStore, + const DeltaSetIndexMap *varIdxMap, + hb_array_t coords, + SparseVarRegionList::cache_t *cache = nullptr) : + varStore (varStore), varIdxMap (varIdxMap), coords (coords), cache (cache) + { + if (!varStore) + varStore = &Null(MultiItemVariationStore); + } + + operator bool () const { return varStore && bool (coords); } + + float operator[] (uint32_t varIdx) const + { + float v = 0; + (*this) (hb_array (&v, 1), varIdx); + return v; + } + + void operator() (hb_array_t out, uint32_t varIdx, unsigned short offset = 0) const + { + if (coords && varIdx != VarIdx::NO_VARIATION) + { + varIdx += offset; + if (varIdxMap) + varIdx = varIdxMap->map (varIdx); + varStore->get_delta (varIdx, coords, out, cache); + } + else + for (unsigned i = 0; i < out.length; i++) + out.arrayZ[i] = 0.f; + } + + const MultiItemVariationStore *varStore; + const DeltaSetIndexMap *varIdxMap; + hb_array_t coords; + SparseVarRegionList::cache_t *cache; +}; + + /* * Feature Variations */ @@ -3278,7 +3818,16 @@ enum Cond_with_Var_flag_t DROP_RECORD_WITH_VAR = 3, }; -struct ConditionFormat1 +struct Condition; + +template +static bool +_hb_recurse_condition_evaluate (const struct Condition &condition, + const int *coords, + unsigned int coord_len, + Instancer *instancer); + +struct ConditionAxisRange { friend struct Condition; @@ -3298,19 +3847,19 @@ struct ConditionFormat1 return_trace (false); const hb_hashmap_t& normalized_axes_location = c->plan->axes_location; - Triple axis_limit{-1.f, 0.f, 1.f}; + Triple axis_limit{-1.0, 0.0, 1.0}; Triple *normalized_limit; if (normalized_axes_location.has (*axis_tag, &normalized_limit)) axis_limit = *normalized_limit; const hb_hashmap_t& axes_triple_distances = c->plan->axes_triple_distances; - TripleDistances axis_triple_distances{1.f, 1.f}; + TripleDistances axis_triple_distances{1.0, 1.0}; TripleDistances *triple_dists; if (axes_triple_distances.has (*axis_tag, &triple_dists)) axis_triple_distances = *triple_dists; - float normalized_min = renormalizeValue (filterRangeMinValue.to_float (), axis_limit, axis_triple_distances, false); - float normalized_max = renormalizeValue (filterRangeMaxValue.to_float (), axis_limit, axis_triple_distances, false); + float normalized_min = renormalizeValue ((double) filterRangeMinValue.to_float (), axis_limit, axis_triple_distances, false); + float normalized_max = renormalizeValue ((double) filterRangeMaxValue.to_float (), axis_limit, axis_triple_distances, false); out->filterRangeMinValue.set_float (normalized_min); out->filterRangeMaxValue.set_float (normalized_max); @@ -3328,10 +3877,14 @@ struct ConditionFormat1 hb_tag_t axis_tag = c->axes_index_tag_map->get (axisIndex); - Triple axis_range (-1.f, 0.f, 1.f); + Triple axis_range (-1.0, 0.0, 1.0); Triple *axis_limit; + bool axis_set_by_user = false; if (c->axes_location->has (axis_tag, &axis_limit)) + { axis_range = *axis_limit; + axis_set_by_user = true; + } float axis_min_val = axis_range.minimum; float axis_default_val = axis_range.middle; @@ -3350,26 +3903,26 @@ struct ConditionFormat1 return DROP_RECORD_WITH_VAR; //condition met and axis pinned, drop the condition - if (c->axes_location->has (axis_tag) && - c->axes_location->get (axis_tag).is_point ()) + if (axis_set_by_user && axis_range.is_point ()) return DROP_COND_WITH_VAR; if (filter_max_val != axis_max_val || filter_min_val != axis_min_val) { // add axisIndex->value into the hashmap so we can check if the record is // unique with variations - int16_t int_filter_max_val = filterRangeMaxValue.to_int (); - int16_t int_filter_min_val = filterRangeMinValue.to_int (); + uint16_t int_filter_max_val = (uint16_t) filterRangeMaxValue.to_int (); + uint16_t int_filter_min_val = (uint16_t) filterRangeMinValue.to_int (); hb_codepoint_t val = (int_filter_max_val << 16) + int_filter_min_val; condition_map->set (axisIndex, val); return KEEP_COND_WITH_VAR; } - return KEEP_RECORD_WITH_VAR; } - bool evaluate (const int *coords, unsigned int coord_len) const + template + bool evaluate (const int *coords, unsigned int coord_len, + Instancer *instancer HB_UNUSED) const { int coord = axisIndex < coord_len ? coords[axisIndex] : 0; return filterRangeMinValue.to_int () <= coord && coord <= filterRangeMaxValue.to_int (); @@ -3390,12 +3943,199 @@ struct ConditionFormat1 DEFINE_SIZE_STATIC (8); }; +struct ConditionValue +{ + friend struct Condition; + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + // TODO(subset) + return_trace (false); + } + + private: + template + bool evaluate (const int *coords, unsigned int coord_len, + Instancer *instancer) const + { + signed value = defaultValue; + value += (*instancer)[varIdx]; + return value > 0; + } + + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l, + bool insert_catch_all) const + { + TRACE_SUBSET (this); + // TODO(subset) + return_trace (false); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + HBINT16 defaultValue; /* Value at default instance. */ + VarIdx varIdx; /* Variation index */ + public: + DEFINE_SIZE_STATIC (8); +}; + +struct ConditionAnd +{ + friend struct Condition; + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + // TODO(subset) + return_trace (false); + } + + private: + template + bool evaluate (const int *coords, unsigned int coord_len, + Instancer *instancer) const + { + unsigned int count = conditions.len; + for (unsigned int i = 0; i < count; i++) + if (!_hb_recurse_condition_evaluate (this+conditions.arrayZ[i], + coords, coord_len, + instancer)) + return false; + return true; + } + + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l, + bool insert_catch_all) const + { + TRACE_SUBSET (this); + // TODO(subset) + return_trace (false); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (conditions.sanitize (c, this)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 3 */ + Array8OfOffset24To conditions; + public: + DEFINE_SIZE_ARRAY (3, conditions); +}; + +struct ConditionOr +{ + friend struct Condition; + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + // TODO(subset) + return_trace (false); + } + + private: + template + bool evaluate (const int *coords, unsigned int coord_len, + Instancer *instancer) const + { + unsigned int count = conditions.len; + for (unsigned int i = 0; i < count; i++) + if (_hb_recurse_condition_evaluate (this+conditions.arrayZ[i], + coords, coord_len, + instancer)) + return true; + return false; + } + + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l, + bool insert_catch_all) const + { + TRACE_SUBSET (this); + // TODO(subset) + return_trace (false); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (conditions.sanitize (c, this)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 4 */ + Array8OfOffset24To conditions; + public: + DEFINE_SIZE_ARRAY (3, conditions); +}; + +struct ConditionNegate +{ + friend struct Condition; + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + // TODO(subset) + return_trace (false); + } + + private: + template + bool evaluate (const int *coords, unsigned int coord_len, + Instancer *instancer) const + { + return !_hb_recurse_condition_evaluate (this+condition, + coords, coord_len, + instancer); + } + + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l, + bool insert_catch_all) const + { + TRACE_SUBSET (this); + // TODO(subset) + return_trace (false); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (condition.sanitize (c, this)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 5 */ + Offset24To condition; + public: + DEFINE_SIZE_STATIC (5); +}; + struct Condition { - bool evaluate (const int *coords, unsigned int coord_len) const + template + bool evaluate (const int *coords, unsigned int coord_len, + Instancer *instancer) const { switch (u.format) { - case 1: return u.format1.evaluate (coords, coord_len); + case 1: hb_barrier (); return u.format1.evaluate (coords, coord_len, instancer); + case 2: hb_barrier (); return u.format2.evaluate (coords, coord_len, instancer); + case 3: hb_barrier (); return u.format3.evaluate (coords, coord_len, instancer); + case 4: hb_barrier (); return u.format4.evaluate (coords, coord_len, instancer); + case 5: hb_barrier (); return u.format5.evaluate (coords, coord_len, instancer); default:return false; } } @@ -3404,7 +4144,8 @@ struct Condition hb_map_t *condition_map /* OUT */) const { switch (u.format) { - case 1: return u.format1.keep_with_variations (c, condition_map); + case 1: hb_barrier (); return u.format1.keep_with_variations (c, condition_map); + // TODO(subset) default: c->apply = false; return KEEP_COND_WITH_VAR; } } @@ -3415,7 +4156,11 @@ struct Condition if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); TRACE_DISPATCH (this, u.format); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); + case 1: hb_barrier (); return_trace (c->dispatch (u.format1, std::forward (ds)...)); + case 2: hb_barrier (); return_trace (c->dispatch (u.format2, std::forward (ds)...)); + case 3: hb_barrier (); return_trace (c->dispatch (u.format3, std::forward (ds)...)); + case 4: hb_barrier (); return_trace (c->dispatch (u.format4, std::forward (ds)...)); + case 5: hb_barrier (); return_trace (c->dispatch (u.format5, std::forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -3424,8 +4169,13 @@ struct Condition { TRACE_SANITIZE (this); if (!u.format.sanitize (c)) return_trace (false); + hb_barrier (); switch (u.format) { - case 1: return_trace (u.format1.sanitize (c)); + case 1: hb_barrier (); return_trace (u.format1.sanitize (c)); + case 2: hb_barrier (); return_trace (u.format2.sanitize (c)); + case 3: hb_barrier (); return_trace (u.format3.sanitize (c)); + case 4: hb_barrier (); return_trace (u.format4.sanitize (c)); + case 5: hb_barrier (); return_trace (u.format5.sanitize (c)); default:return_trace (true); } } @@ -3433,19 +4183,51 @@ struct Condition protected: union { HBUINT16 format; /* Format identifier */ - ConditionFormat1 format1; + ConditionAxisRange format1; + ConditionValue format2; + ConditionAnd format3; + ConditionOr format4; + ConditionNegate format5; } u; public: DEFINE_SIZE_UNION (2, format); }; +template +bool +_hb_recurse_condition_evaluate (const struct Condition &condition, + const int *coords, + unsigned int coord_len, + Instancer *instancer) +{ + return condition.evaluate (coords, coord_len, instancer); +} + +struct ConditionList +{ + const Condition& operator[] (unsigned i) const + { return this+conditions[i]; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (conditions.sanitize (c, this)); + } + + protected: + Array32OfOffset32To conditions; + public: + DEFINE_SIZE_ARRAY (4, conditions); +}; + struct ConditionSet { - bool evaluate (const int *coords, unsigned int coord_len) const + bool evaluate (const int *coords, unsigned int coord_len, + ItemVarStoreInstancer *instancer) const { unsigned int count = conditions.len; for (unsigned int i = 0; i < count; i++) - if (!(this+conditions.arrayZ[i]).evaluate (coords, coord_len)) + if (!(this+conditions.arrayZ[i]).evaluate (coords, coord_len, instancer)) return false; return true; } @@ -3497,12 +4279,15 @@ struct ConditionSet } bool subset (hb_subset_context_t *c, - hb_subset_layout_context_t *l) const + hb_subset_layout_context_t *l, + bool insert_catch_all) const { TRACE_SUBSET (this); auto *out = c->serializer->start_embed (this); if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + if (insert_catch_all) return_trace (true); + hb_set_t *retained_cond_set = nullptr; if (l->feature_record_cond_idx_map != nullptr) retained_cond_set = l->feature_record_cond_idx_map->get (l->cur_feature_var_record_idx); @@ -3548,27 +4333,51 @@ struct FeatureTableSubstitutionRecord } void collect_feature_substitutes_with_variations (hb_hashmap_t *feature_substitutes_map, + hb_set_t& catch_all_record_feature_idxes, const hb_set_t *feature_indices, const void *base) const { if (feature_indices->has (featureIndex)) + { feature_substitutes_map->set (featureIndex, &(base+feature)); + catch_all_record_feature_idxes.add (featureIndex); + } + } + + bool serialize (hb_subset_layout_context_t *c, + unsigned feature_index, + const Feature *f, const Tag *tag) + { + TRACE_SERIALIZE (this); + hb_serialize_context_t *s = c->subset_context->serializer; + if (unlikely (!s->extend_min (this))) return_trace (false); + + uint32_t *new_feature_idx; + if (!c->feature_index_map->has (feature_index, &new_feature_idx)) + return_trace (false); + + if (!s->check_assign (featureIndex, *new_feature_idx, HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + + s->push (); + bool ret = f->subset (c->subset_context, c, tag); + if (ret) s->add_link (feature, s->pop_pack ()); + else s->pop_discard (); + + return_trace (ret); } bool subset (hb_subset_layout_context_t *c, const void *base) const { TRACE_SUBSET (this); - if (!c->feature_index_map->has (featureIndex) || - c->feature_substitutes_map->has (featureIndex)) { - // Feature that is being substituted is not being retained, so we don't - // need this. + uint32_t *new_feature_index; + if (!c->feature_index_map->has (featureIndex, &new_feature_index)) return_trace (false); - } auto *out = c->subset_context->serializer->embed (this); if (unlikely (!out)) return_trace (false); - out->featureIndex = c->feature_index_map->get (featureIndex); + out->featureIndex = *new_feature_index; return_trace (out->feature.serialize_subset (c->subset_context, feature, base, c)); } @@ -3600,16 +4409,10 @@ struct FeatureTableSubstitution } void collect_lookups (const hb_set_t *feature_indexes, - const hb_hashmap_t *feature_substitutes_map, hb_set_t *lookup_indexes /* OUT */) const { + hb_iter (substitutions) | hb_filter (feature_indexes, &FeatureTableSubstitutionRecord::featureIndex) - | hb_filter ([feature_substitutes_map] (const FeatureTableSubstitutionRecord& record) - { - if (feature_substitutes_map == nullptr) return true; - return !feature_substitutes_map->has (record.featureIndex); - }) | hb_apply ([this, lookup_indexes] (const FeatureTableSubstitutionRecord& r) { r.collect_lookups (this, lookup_indexes); }) ; @@ -3634,11 +4437,14 @@ struct FeatureTableSubstitution void collect_feature_substitutes_with_variations (hb_collect_feature_substitutes_with_var_context_t *c) const { for (const FeatureTableSubstitutionRecord& record : substitutions) - record.collect_feature_substitutes_with_variations (c->feature_substitutes_map, c->feature_indices, this); + record.collect_feature_substitutes_with_variations (c->feature_substitutes_map, + c->catch_all_record_feature_idxes, + c->feature_indices, this); } bool subset (hb_subset_context_t *c, - hb_subset_layout_context_t *l) const + hb_subset_layout_context_t *l, + bool insert_catch_all) const { TRACE_SUBSET (this); auto *out = c->serializer->start_embed (*this); @@ -3647,6 +4453,22 @@ struct FeatureTableSubstitution out->version.major = version.major; out->version.minor = version.minor; + if (insert_catch_all) + { + for (unsigned feature_index : *(l->catch_all_record_feature_idxes)) + { + hb_pair_t *p; + if (!l->feature_idx_tag_map->has (feature_index, &p)) + return_trace (false); + auto *o = out->substitutions.serialize_append (c->serializer); + if (!o->serialize (l, feature_index, + reinterpret_cast (p->first), + reinterpret_cast (p->second))) + return_trace (false); + } + return_trace (true); + } + + substitutions.iter () | hb_apply (subset_record_array (l, &(out->substitutions), this)) ; @@ -3658,6 +4480,7 @@ struct FeatureTableSubstitution { TRACE_SANITIZE (this); return_trace (version.sanitize (c) && + hb_barrier () && likely (version.major == 1) && substitutions.sanitize (c, this)); } @@ -3676,10 +4499,9 @@ struct FeatureVariationRecord void collect_lookups (const void *base, const hb_set_t *feature_indexes, - const hb_hashmap_t *feature_substitutes_map, hb_set_t *lookup_indexes /* OUT */) const { - return (base+substitutions).collect_lookups (feature_indexes, feature_substitutes_map, lookup_indexes); + return (base+substitutions).collect_lookups (feature_indexes, lookup_indexes); } void closure_features (const void *base, @@ -3705,14 +4527,15 @@ struct FeatureVariationRecord } } - bool subset (hb_subset_layout_context_t *c, const void *base) const + bool subset (hb_subset_layout_context_t *c, const void *base, + bool insert_catch_all = false) const { TRACE_SUBSET (this); auto *out = c->subset_context->serializer->embed (this); if (unlikely (!out)) return_trace (false); - out->conditions.serialize_subset (c->subset_context, conditions, base, c); - out->substitutions.serialize_subset (c->subset_context, substitutions, base, c); + out->conditions.serialize_subset (c->subset_context, conditions, base, c, insert_catch_all); + out->substitutions.serialize_subset (c->subset_context, substitutions, base, c, insert_catch_all); return_trace (true); } @@ -3738,13 +4561,14 @@ struct FeatureVariations static constexpr unsigned NOT_FOUND_INDEX = 0xFFFFFFFFu; bool find_index (const int *coords, unsigned int coord_len, - unsigned int *index) const + unsigned int *index, + ItemVarStoreInstancer *instancer) const { unsigned int count = varRecords.len; for (unsigned int i = 0; i < count; i++) { const FeatureVariationRecord &record = varRecords.arrayZ[i]; - if ((this+record.conditions).evaluate (coords, coord_len)) + if ((this+record.conditions).evaluate (coords, coord_len, instancer)) { *index = i; return true; @@ -3771,9 +4595,8 @@ struct FeatureVariations if (c->universal) break; } - if (c->variation_applied && !c->universal && - !c->record_cond_idx_map->is_empty ()) - c->insert_catch_all_feature_variation_record = true; + if (c->universal || c->record_cond_idx_map->is_empty ()) + c->catch_all_record_feature_idxes.reset (); } FeatureVariations* copy (hb_serialize_context_t *c) const @@ -3783,11 +4606,17 @@ struct FeatureVariations } void collect_lookups (const hb_set_t *feature_indexes, - const hb_hashmap_t *feature_substitutes_map, + const hb_hashmap_t> *feature_record_cond_idx_map, hb_set_t *lookup_indexes /* OUT */) const { - for (const FeatureVariationRecord& r : varRecords) - r.collect_lookups (this, feature_indexes, feature_substitutes_map, lookup_indexes); + unsigned count = varRecords.len; + for (unsigned int i = 0; i < count; i++) + { + if (feature_record_cond_idx_map && + !feature_record_cond_idx_map->has (i)) + continue; + varRecords[i].collect_lookups (this, feature_indexes, lookup_indexes); + } } void closure_features (const hb_map_t *lookup_indexes, @@ -3832,6 +4661,13 @@ struct FeatureVariations l->cur_feature_var_record_idx = i; subset_record_array (l, &(out->varRecords), this) (varRecords[i]); } + + if (out->varRecords.len && !l->catch_all_record_feature_idxes->is_empty ()) + { + bool insert_catch_all_record = true; + subset_record_array (l, &(out->varRecords), this, insert_catch_all_record) (varRecords[0]); + } + return_trace (bool (out->varRecords)); } @@ -3839,6 +4675,7 @@ struct FeatureVariations { TRACE_SANITIZE (this); return_trace (version.sanitize (c) && + hb_barrier () && likely (version.major == 1) && varRecords.sanitize (c, this)); } @@ -3945,14 +4782,14 @@ struct VariationDevice private: hb_position_t get_x_delta (hb_font_t *font, - const VariationStore &store, - VariationStore::cache_t *store_cache = nullptr) const - { return font->em_scalef_x (get_delta (font, store, store_cache)); } + const ItemVariationStore &store, + ItemVariationStore::cache_t *store_cache = nullptr) const + { return !font->num_coords ? 0 : font->em_scalef_x (get_delta (font, store, store_cache)); } hb_position_t get_y_delta (hb_font_t *font, - const VariationStore &store, - VariationStore::cache_t *store_cache = nullptr) const - { return font->em_scalef_y (get_delta (font, store, store_cache)); } + const ItemVariationStore &store, + ItemVariationStore::cache_t *store_cache = nullptr) const + { return !font->num_coords ? 0 : font->em_scalef_y (get_delta (font, store, store_cache)); } VariationDevice* copy (hb_serialize_context_t *c, const hb_hashmap_t> *layout_variation_idx_delta_map) const @@ -3985,14 +4822,14 @@ struct VariationDevice private: float get_delta (hb_font_t *font, - const VariationStore &store, - VariationStore::cache_t *store_cache = nullptr) const + const ItemVariationStore &store, + ItemVariationStore::cache_t *store_cache = nullptr) const { - return store.get_delta (varIdx, font->coords, font->num_coords, (VariationStore::cache_t *) store_cache); + return store.get_delta (varIdx, font->coords, font->num_coords, (ItemVariationStore::cache_t *) store_cache); } protected: - VarIdx varIdx; + VarIdx varIdx; /* Variation index */ HBUINT16 deltaFormat; /* Format identifier for this table: 0x0x8000 */ public: DEFINE_SIZE_STATIC (6); @@ -4012,8 +4849,8 @@ struct DeviceHeader struct Device { hb_position_t get_x_delta (hb_font_t *font, - const VariationStore &store=Null (VariationStore), - VariationStore::cache_t *store_cache = nullptr) const + const ItemVariationStore &store=Null (ItemVariationStore), + ItemVariationStore::cache_t *store_cache = nullptr) const { switch (u.b.format) { @@ -4030,8 +4867,8 @@ struct Device } } hb_position_t get_y_delta (hb_font_t *font, - const VariationStore &store=Null (VariationStore), - VariationStore::cache_t *store_cache = nullptr) const + const ItemVariationStore &store=Null (ItemVariationStore), + ItemVariationStore::cache_t *store_cache = nullptr) const { switch (u.b.format) { diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsubgpos.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsubgpos.hh index d5b5a488a263a..072e5cdba265f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsubgpos.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsubgpos.hh @@ -406,6 +406,7 @@ struct hb_ot_apply_context_t : void set_ignore_zwnj (bool ignore_zwnj_) { ignore_zwnj = ignore_zwnj_; } void set_ignore_zwj (bool ignore_zwj_) { ignore_zwj = ignore_zwj_; } + void set_ignore_hidden (bool ignore_hidden_) { ignore_hidden = ignore_hidden_; } void set_lookup_props (unsigned int lookup_props_) { lookup_props = lookup_props_; } void set_mask (hb_mask_t mask_) { mask = mask_; } void set_per_syllable (bool per_syllable_) { per_syllable = per_syllable_; } @@ -451,9 +452,10 @@ struct hb_ot_apply_context_t : if (!c->check_glyph_property (&info, lookup_props)) return SKIP_YES; - if (unlikely (_hb_glyph_info_is_default_ignorable_and_not_hidden (&info) && + if (unlikely (_hb_glyph_info_is_default_ignorable (&info) && (ignore_zwnj || !_hb_glyph_info_is_zwnj (&info)) && - (ignore_zwj || !_hb_glyph_info_is_zwj (&info)))) + (ignore_zwj || !_hb_glyph_info_is_zwj (&info)) && + (ignore_hidden || !_hb_glyph_info_is_hidden (&info)))) return SKIP_MAYBE; return SKIP_NO; @@ -464,6 +466,7 @@ struct hb_ot_apply_context_t : hb_mask_t mask = -1; bool ignore_zwnj = false; bool ignore_zwj = false; + bool ignore_hidden = false; bool per_syllable = false; uint8_t syllable = 0; match_func_t match_func = nullptr; @@ -486,6 +489,8 @@ struct hb_ot_apply_context_t : matcher.set_ignore_zwnj (c->table_index == 1 || (context_match && c->auto_zwnj)); /* Ignore ZWJ if we are matching context, or asked to. */ matcher.set_ignore_zwj (context_match || c->auto_zwj); + /* Ignore hidden glyphs (like CGJ) during GPOS. */ + matcher.set_ignore_hidden (c->table_index == 1); matcher.set_mask (context_match ? -1 : c->lookup_mask); /* Per syllable matching is only for GSUB. */ matcher.set_per_syllable (c->table_index == 0 && c->per_syllable); @@ -708,8 +713,9 @@ struct hb_ot_apply_context_t : recurse_func_t recurse_func = nullptr; const GDEF &gdef; const GDEF::accelerator_t &gdef_accel; - const VariationStore &var_store; - VariationStore::cache_t *var_store_cache; + const hb_ot_layout_lookup_accelerator_t *lookup_accel = nullptr; + const ItemVariationStore &var_store; + ItemVariationStore::cache_t *var_store_cache; hb_set_digest_t digest; hb_direction_t direction; @@ -723,7 +729,6 @@ struct hb_ot_apply_context_t : bool auto_zwj = true; bool per_syllable = false; bool random = false; - uint32_t random_state = 1; unsigned new_syllables = (unsigned) -1; signed last_base = -1; // GPOS uses @@ -732,7 +737,8 @@ struct hb_ot_apply_context_t : hb_ot_apply_context_t (unsigned int table_index_, hb_font_t *font_, hb_buffer_t *buffer_, - hb_blob_t *table_blob_) : + hb_blob_t *table_blob_, + ItemVariationStore::cache_t *var_store_cache_ = nullptr) : table_index (table_index_), font (font_), face (font->face), buffer (buffer_), sanitizer (table_blob_), @@ -751,23 +757,12 @@ struct hb_ot_apply_context_t : #endif ), var_store (gdef.get_var_store ()), - var_store_cache ( -#ifndef HB_NO_VAR - table_index == 1 && font->num_coords ? var_store.create_cache () : nullptr -#else - nullptr -#endif - ), - digest (buffer_->digest ()), + var_store_cache (var_store_cache_), direction (buffer_->props.direction), has_glyph_classes (gdef.has_glyph_classes ()) - { init_iters (); } - - ~hb_ot_apply_context_t () { -#ifndef HB_NO_VAR - VariationStore::destroy_cache (var_store_cache); -#endif + init_iters (); + buffer->collect_codepoints (digest); } void init_iters () @@ -788,8 +783,8 @@ struct hb_ot_apply_context_t : uint32_t random_number () { /* http://www.cplusplus.com/reference/random/minstd_rand/ */ - random_state = random_state * 48271 % 2147483647; - return random_state; + buffer->random_state = buffer->random_state * 48271 % 2147483647; + return buffer->random_state; } bool match_properties_mark (hb_codepoint_t glyph, @@ -895,6 +890,13 @@ struct hb_ot_apply_context_t : } }; +enum class hb_ot_lookup_cache_op_t +{ + CREATE, + ENTER, + LEAVE, + DESTROY, +}; struct hb_accelerate_subtables_context_t : hb_dispatch_context_t @@ -919,19 +921,23 @@ struct hb_accelerate_subtables_context_t : } template - static inline auto cache_func_ (const T *obj, hb_ot_apply_context_t *c, bool enter, hb_priority<1>) HB_RETURN (bool, obj->cache_func (c, enter) ) - template - static inline bool cache_func_ (const T *obj, hb_ot_apply_context_t *c, bool enter, hb_priority<0>) { return false; } + static inline auto cache_func_ (void *p, + hb_ot_lookup_cache_op_t op, + hb_priority<1>) HB_RETURN (void *, T::cache_func (p, op) ) + template + static inline void * cache_func_ (void *p, + hb_ot_lookup_cache_op_t op HB_UNUSED, + hb_priority<0>) { return (void *) false; } template - static inline bool cache_func_to (const void *obj, hb_ot_apply_context_t *c, bool enter) + static inline void * cache_func_to (void *p, + hb_ot_lookup_cache_op_t op) { - const Type *typed_obj = (const Type *) obj; - return cache_func_ (typed_obj, c, enter, hb_prioritize); + return cache_func_ (p, op, hb_prioritize); } #endif typedef bool (*hb_apply_func_t) (const void *obj, hb_ot_apply_context_t *c); - typedef bool (*hb_cache_func_t) (const void *obj, hb_ot_apply_context_t *c, bool enter); + typedef void * (*hb_cache_func_t) (void *p, hb_ot_lookup_cache_op_t op); struct hb_applicable_t { @@ -968,11 +974,11 @@ struct hb_accelerate_subtables_context_t : } bool cache_enter (hb_ot_apply_context_t *c) const { - return cache_func (obj, c, true); + return (bool) cache_func (c, hb_ot_lookup_cache_op_t::ENTER); } void cache_leave (hb_ot_apply_context_t *c) const { - cache_func (obj, c, false); + cache_func (c, hb_ot_lookup_cache_op_t::LEAVE); } #endif @@ -1255,7 +1261,7 @@ static bool match_input (hb_ot_apply_context_t *c, match_func_t match_func, const void *match_data, unsigned int *end_position, - unsigned int match_positions[HB_MAX_CONTEXT_LENGTH], + unsigned int *match_positions, unsigned int *p_total_component_count = nullptr) { TRACE_APPLY (nullptr); @@ -1379,7 +1385,7 @@ static bool match_input (hb_ot_apply_context_t *c, } static inline bool ligate_input (hb_ot_apply_context_t *c, unsigned int count, /* Including the first glyph */ - const unsigned int match_positions[HB_MAX_CONTEXT_LENGTH], /* Including the first glyph */ + const unsigned int *match_positions, /* Including the first glyph */ unsigned int match_end, hb_codepoint_t lig_glyph, unsigned int total_component_count) @@ -1458,6 +1464,7 @@ static inline bool ligate_input (hb_ot_apply_context_t *c, unsigned int this_comp = _hb_glyph_info_get_lig_comp (&buffer->cur()); if (this_comp == 0) this_comp = last_num_components; + assert (components_so_far >= last_num_components); unsigned int new_lig_comp = components_so_far - last_num_components + hb_min (this_comp, last_num_components); _hb_glyph_info_set_lig_props_for_mark (&buffer->cur(), lig_id, new_lig_comp); @@ -1483,6 +1490,7 @@ static inline bool ligate_input (hb_ot_apply_context_t *c, unsigned this_comp = _hb_glyph_info_get_lig_comp (&buffer->info[i]); if (!this_comp) break; + assert (components_so_far >= last_num_components); unsigned new_lig_comp = components_so_far - last_num_components + hb_min (this_comp, last_num_components); _hb_glyph_info_set_lig_props_for_mark (&buffer->info[i], lig_id, new_lig_comp); @@ -1538,6 +1546,7 @@ static bool match_lookahead (hb_ot_apply_context_t *c, TRACE_APPLY (nullptr); hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_context; + assert (start_index >= 1); skippy_iter.reset (start_index - 1); skippy_iter.set_match_func (match_func, match_data); skippy_iter.set_glyph_data (lookahead); @@ -1687,7 +1696,7 @@ static inline void recurse_lookups (context_t *c, static inline void apply_lookup (hb_ot_apply_context_t *c, unsigned int count, /* Including the first glyph */ - unsigned int match_positions[HB_MAX_CONTEXT_LENGTH], /* Including the first glyph */ + unsigned int *match_positions, /* Including the first glyph */ unsigned int lookupCount, const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */ unsigned int match_end) @@ -1695,6 +1704,9 @@ static inline void apply_lookup (hb_ot_apply_context_t *c, hb_buffer_t *buffer = c->buffer; int end; + unsigned int *match_positions_input = match_positions; + unsigned int match_positions_count = count; + /* All positions are distance from beginning of *output* buffer. * Adjust. */ { @@ -1798,6 +1810,27 @@ static inline void apply_lookup (hb_ot_apply_context_t *c, { if (unlikely (delta + count > HB_MAX_CONTEXT_LENGTH)) break; + if (unlikely (delta + count > match_positions_count)) + { + unsigned new_match_positions_count = hb_max (delta + count, hb_max(match_positions_count, 4u) * 1.5); + if (match_positions == match_positions_input) + { + match_positions = (unsigned int *) hb_malloc (new_match_positions_count * sizeof (match_positions[0])); + if (unlikely (!match_positions)) + break; + memcpy (match_positions, match_positions_input, count * sizeof (match_positions[0])); + match_positions_count = new_match_positions_count; + } + else + { + unsigned int *new_match_positions = (unsigned int *) hb_realloc (match_positions, new_match_positions_count * sizeof (match_positions[0])); + if (unlikely (!new_match_positions)) + break; + match_positions = new_match_positions; + match_positions_count = new_match_positions_count; + } + } + } else { @@ -1821,6 +1854,10 @@ static inline void apply_lookup (hb_ot_apply_context_t *c, match_positions[next] += delta; } + if (match_positions != match_positions_input) + hb_free (match_positions); + + assert (end >= 0); (void) buffer->move_to (end); } @@ -1921,8 +1958,18 @@ static bool context_apply_lookup (hb_ot_apply_context_t *c, const LookupRecord lookupRecord[], const ContextApplyLookupContext &lookup_context) { + if (unlikely (inputCount > HB_MAX_CONTEXT_LENGTH)) return false; + unsigned match_positions_stack[4]; + unsigned *match_positions = match_positions_stack; + if (unlikely (inputCount > ARRAY_LENGTH (match_positions_stack))) + { + match_positions = (unsigned *) hb_malloc (hb_max (inputCount, 1u) * sizeof (match_positions[0])); + if (unlikely (!match_positions)) + return false; + } + unsigned match_end = 0; - unsigned match_positions[HB_MAX_CONTEXT_LENGTH]; + bool ret = false; if (match_input (c, inputCount, input, lookup_context.funcs.match, lookup_context.match_data, @@ -1933,13 +1980,18 @@ static bool context_apply_lookup (hb_ot_apply_context_t *c, inputCount, match_positions, lookupCount, lookupRecord, match_end); - return true; + ret = true; } else { c->buffer->unsafe_to_concat (c->buffer->idx, match_end); - return false; + ret = false; } + + if (unlikely (match_positions != match_positions_stack)) + hb_free (match_positions); + + return ret; } template @@ -2051,6 +2103,7 @@ struct Rule { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && c->check_range (inputZ.arrayZ, inputZ.item_size * (inputCount ? inputCount - 1 : 0) + LookupRecord::static_size * lookupCount)); @@ -2572,25 +2625,35 @@ struct ContextFormat2_5 unsigned c = (this+classDef).cost () * ruleSet.len; return c >= 4 ? c : 0; } - bool cache_func (hb_ot_apply_context_t *c, bool enter) const + static void * cache_func (void *p, hb_ot_lookup_cache_op_t op) { - if (enter) - { - if (!HB_BUFFER_TRY_ALLOCATE_VAR (c->buffer, syllable)) - return false; - auto &info = c->buffer->info; - unsigned count = c->buffer->len; - for (unsigned i = 0; i < count; i++) - info[i].syllable() = 255; - c->new_syllables = 255; - return true; - } - else + switch (op) { - c->new_syllables = (unsigned) -1; - HB_BUFFER_DEALLOCATE_VAR (c->buffer, syllable); - return true; + case hb_ot_lookup_cache_op_t::CREATE: + return (void *) true; + case hb_ot_lookup_cache_op_t::ENTER: + { + hb_ot_apply_context_t *c = (hb_ot_apply_context_t *) p; + if (!HB_BUFFER_TRY_ALLOCATE_VAR (c->buffer, syllable)) + return (void *) false; + auto &info = c->buffer->info; + unsigned count = c->buffer->len; + for (unsigned i = 0; i < count; i++) + info[i].syllable() = 255; + c->new_syllables = 255; + return (void *) true; + } + case hb_ot_lookup_cache_op_t::LEAVE: + { + hb_ot_apply_context_t *c = (hb_ot_apply_context_t *) p; + c->new_syllables = (unsigned) -1; + HB_BUFFER_DEALLOCATE_VAR (c->buffer, syllable); + return nullptr; + } + case hb_ot_lookup_cache_op_t::DESTROY: + return nullptr; } + return nullptr; } bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); } @@ -2599,7 +2662,7 @@ struct ContextFormat2_5 { TRACE_APPLY (this); unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); - if (likely (index == NOT_COVERED)) return_trace (false); + if (index == NOT_COVERED) return_trace (false); const ClassDef &class_def = this+classDef; @@ -2785,7 +2848,7 @@ struct ContextFormat3 { TRACE_APPLY (this); unsigned int index = (this+coverageZ[0]).get_coverage (c->buffer->cur().codepoint); - if (likely (index == NOT_COVERED)) return_trace (false); + if (index == NOT_COVERED) return_trace (false); const LookupRecord *lookupRecord = &StructAfter (coverageZ.as_array (glyphCount)); struct ContextApplyLookupContext lookup_context = { @@ -2826,6 +2889,7 @@ struct ContextFormat3 { TRACE_SANITIZE (this); if (unlikely (!c->check_struct (this))) return_trace (false); + hb_barrier (); unsigned int count = glyphCount; if (unlikely (!count)) return_trace (false); /* We want to access coverageZ[0] freely. */ if (unlikely (!c->check_array (coverageZ.arrayZ, count))) return_trace (false); @@ -2858,12 +2922,12 @@ struct Context if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); TRACE_DISPATCH (this, u.format); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); - case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); - case 3: return_trace (c->dispatch (u.format3, std::forward (ds)...)); + case 1: hb_barrier (); return_trace (c->dispatch (u.format1, std::forward (ds)...)); + case 2: hb_barrier (); return_trace (c->dispatch (u.format2, std::forward (ds)...)); + case 3: hb_barrier (); return_trace (c->dispatch (u.format3, std::forward (ds)...)); #ifndef HB_NO_BEYOND_64K - case 4: return_trace (c->dispatch (u.format4, std::forward (ds)...)); - case 5: return_trace (c->dispatch (u.format5, std::forward (ds)...)); + case 4: hb_barrier (); return_trace (c->dispatch (u.format4, std::forward (ds)...)); + case 5: hb_barrier (); return_trace (c->dispatch (u.format5, std::forward (ds)...)); #endif default:return_trace (c->default_return_value ()); } @@ -3017,9 +3081,20 @@ static bool chain_context_apply_lookup (hb_ot_apply_context_t *c, const LookupRecord lookupRecord[], const ChainContextApplyLookupContext &lookup_context) { + if (unlikely (inputCount > HB_MAX_CONTEXT_LENGTH)) return false; + unsigned match_positions_stack[4]; + unsigned *match_positions = match_positions_stack; + if (unlikely (inputCount > ARRAY_LENGTH (match_positions_stack))) + { + match_positions = (unsigned *) hb_malloc (hb_max (inputCount, 1u) * sizeof (match_positions[0])); + if (unlikely (!match_positions)) + return false; + } + + unsigned start_index = c->buffer->out_len; unsigned end_index = c->buffer->idx; unsigned match_end = 0; - unsigned match_positions[HB_MAX_CONTEXT_LENGTH]; + bool ret = true; if (!(match_input (c, inputCount, input, lookup_context.funcs.match[1], lookup_context.match_data[1], @@ -3030,17 +3105,18 @@ static bool chain_context_apply_lookup (hb_ot_apply_context_t *c, match_end, &end_index))) { c->buffer->unsafe_to_concat (c->buffer->idx, end_index); - return false; + ret = false; + goto done; } - unsigned start_index = c->buffer->out_len; if (!match_backtrack (c, backtrackCount, backtrack, lookup_context.funcs.match[0], lookup_context.match_data[0], &start_index)) { c->buffer->unsafe_to_concat_from_outbuffer (start_index, end_index); - return false; + ret = false; + goto done; } c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index); @@ -3048,7 +3124,12 @@ static bool chain_context_apply_lookup (hb_ot_apply_context_t *c, inputCount, match_positions, lookupCount, lookupRecord, match_end); - return true; + done: + + if (unlikely (match_positions != match_positions_stack)) + hb_free (match_positions); + + return ret; } template @@ -3219,10 +3300,13 @@ struct ChainRule TRACE_SANITIZE (this); /* Hyper-optimized sanitized because this is really hot. */ if (unlikely (!backtrack.len.sanitize (c))) return_trace (false); + hb_barrier (); const auto &input = StructAfter (backtrack); if (unlikely (!input.lenP1.sanitize (c))) return_trace (false); + hb_barrier (); const auto &lookahead = StructAfter (input); if (unlikely (!lookahead.len.sanitize (c))) return_trace (false); + hb_barrier (); const auto &lookup = StructAfter (lookahead); return_trace (likely (lookup.sanitize (c))); } @@ -3327,6 +3411,15 @@ struct ChainRuleSet * * Replicated from LigatureSet::apply(). */ + /* If the input skippy has non-auto joiners behavior (as in Indic shapers), + * skip this fast path, as we don't distinguish between input & lookahead + * matching in the fast path. + * + * https://github.com/harfbuzz/harfbuzz/issues/4813 + */ + if (!c->auto_zwnj || !c->auto_zwj) + goto slow; + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; skippy_iter.reset (c->buffer->idx); skippy_iter.set_match_func (match_always, nullptr); @@ -3366,10 +3459,10 @@ struct ChainRuleSet } matched = skippy_iter.next (); if (likely (matched && !skippy_iter.may_skip (c->buffer->info[skippy_iter.idx]))) - { + { second = &c->buffer->info[skippy_iter.idx]; unsafe_to2 = skippy_iter.idx + 1; - } + } auto match_input = lookup_context.funcs.match[1]; auto match_lookahead = lookup_context.funcs.match[2]; @@ -3569,7 +3662,7 @@ struct ChainContextFormat1_4 { TRACE_APPLY (this); unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); - if (likely (index == NOT_COVERED)) return_trace (false); + if (index == NOT_COVERED) return_trace (false); const ChainRuleSet &rule_set = this+ruleSet[index]; struct ChainContextApplyLookupContext lookup_context = { @@ -3780,28 +3873,37 @@ struct ChainContextFormat2_5 unsigned cache_cost () const { - unsigned c = (this+lookaheadClassDef).cost () * ruleSet.len; - return c >= 4 ? c : 0; + return (this+lookaheadClassDef).cost () * ruleSet.len; } - bool cache_func (hb_ot_apply_context_t *c, bool enter) const + static void * cache_func (void *p, hb_ot_lookup_cache_op_t op) { - if (enter) - { - if (!HB_BUFFER_TRY_ALLOCATE_VAR (c->buffer, syllable)) - return false; - auto &info = c->buffer->info; - unsigned count = c->buffer->len; - for (unsigned i = 0; i < count; i++) - info[i].syllable() = 255; - c->new_syllables = 255; - return true; - } - else + switch (op) { - c->new_syllables = (unsigned) -1; - HB_BUFFER_DEALLOCATE_VAR (c->buffer, syllable); - return true; + case hb_ot_lookup_cache_op_t::CREATE: + return (void *) true; + case hb_ot_lookup_cache_op_t::ENTER: + { + hb_ot_apply_context_t *c = (hb_ot_apply_context_t *) p; + if (!HB_BUFFER_TRY_ALLOCATE_VAR (c->buffer, syllable)) + return (void *) false; + auto &info = c->buffer->info; + unsigned count = c->buffer->len; + for (unsigned i = 0; i < count; i++) + info[i].syllable() = 255; + c->new_syllables = 255; + return (void *) true; + } + case hb_ot_lookup_cache_op_t::LEAVE: + { + hb_ot_apply_context_t *c = (hb_ot_apply_context_t *) p; + c->new_syllables = (unsigned) -1; + HB_BUFFER_DEALLOCATE_VAR (c->buffer, syllable); + return nullptr; + } + case hb_ot_lookup_cache_op_t::DESTROY: + return nullptr; } + return nullptr; } bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); } @@ -3810,7 +3912,7 @@ struct ChainContextFormat2_5 { TRACE_APPLY (this); unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); - if (likely (index == NOT_COVERED)) return_trace (false); + if (index == NOT_COVERED) return_trace (false); const ClassDef &backtrack_class_def = this+backtrackClassDef; const ClassDef &input_class_def = this+inputClassDef; @@ -4056,7 +4158,7 @@ struct ChainContextFormat3 const auto &input = StructAfter (backtrack); unsigned int index = (this+input[0]).get_coverage (c->buffer->cur().codepoint); - if (likely (index == NOT_COVERED)) return_trace (false); + if (index == NOT_COVERED) return_trace (false); const auto &lookahead = StructAfter (input); const auto &lookup = StructAfter (lookahead); @@ -4121,11 +4223,14 @@ struct ChainContextFormat3 { TRACE_SANITIZE (this); if (unlikely (!backtrack.sanitize (c, this))) return_trace (false); + hb_barrier (); const auto &input = StructAfter (backtrack); if (unlikely (!input.sanitize (c, this))) return_trace (false); + hb_barrier (); if (unlikely (!input.len)) return_trace (false); /* To be consistent with Context. */ const auto &lookahead = StructAfter (input); if (unlikely (!lookahead.sanitize (c, this))) return_trace (false); + hb_barrier (); const auto &lookup = StructAfter (lookahead); return_trace (likely (lookup.sanitize (c))); } @@ -4159,12 +4264,12 @@ struct ChainContext if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); TRACE_DISPATCH (this, u.format); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); - case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); - case 3: return_trace (c->dispatch (u.format3, std::forward (ds)...)); + case 1: hb_barrier (); return_trace (c->dispatch (u.format1, std::forward (ds)...)); + case 2: hb_barrier (); return_trace (c->dispatch (u.format2, std::forward (ds)...)); + case 3: hb_barrier (); return_trace (c->dispatch (u.format3, std::forward (ds)...)); #ifndef HB_NO_BEYOND_64K - case 4: return_trace (c->dispatch (u.format4, std::forward (ds)...)); - case 5: return_trace (c->dispatch (u.format5, std::forward (ds)...)); + case 4: hb_barrier (); return_trace (c->dispatch (u.format4, std::forward (ds)...)); + case 5: hb_barrier (); return_trace (c->dispatch (u.format5, std::forward (ds)...)); #endif default:return_trace (c->default_return_value ()); } @@ -4209,6 +4314,7 @@ struct ExtensionFormat1 { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && extensionLookupType != T::SubTable::Extension); } @@ -4247,7 +4353,7 @@ struct Extension unsigned int get_type () const { switch (u.format) { - case 1: return u.format1.get_type (); + case 1: hb_barrier (); return u.format1.get_type (); default:return 0; } } @@ -4255,7 +4361,7 @@ struct Extension const X& get_subtable () const { switch (u.format) { - case 1: return u.format1.template get_subtable (); + case 1: hb_barrier (); return u.format1.template get_subtable (); default:return Null (typename T::SubTable); } } @@ -4267,7 +4373,7 @@ struct Extension typename hb_subset_context_t::return_t dispatch (hb_subset_context_t *c, Ts&&... ds) const { switch (u.format) { - case 1: return u.format1.subset (c); + case 1: hb_barrier (); return u.format1.subset (c); default: return c->default_return_value (); } } @@ -4278,7 +4384,7 @@ struct Extension if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); TRACE_DISPATCH (this, u.format); switch (u.format) { - case 1: return_trace (u.format1.dispatch (c, std::forward (ds)...)); + case 1: hb_barrier (); return_trace (u.format1.dispatch (c, std::forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -4320,10 +4426,21 @@ struct hb_ot_layout_lookup_accelerator_t thiz->digest.init (); for (auto& subtable : hb_iter (thiz->subtables, count)) - thiz->digest.add (subtable.digest); + thiz->digest.union_ (subtable.digest); #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE + if (c_accelerate_subtables.cache_user_cost < 4) + c_accelerate_subtables.cache_user_idx = (unsigned) -1; + thiz->cache_user_idx = c_accelerate_subtables.cache_user_idx; + + if (thiz->cache_user_idx != (unsigned) -1) + { + thiz->cache = thiz->subtables[thiz->cache_user_idx].cache_func (nullptr, hb_ot_lookup_cache_op_t::CREATE); + if (!thiz->cache) + thiz->cache_user_idx = (unsigned) -1; + } + for (unsigned i = 0; i < count; i++) if (i != thiz->cache_user_idx) thiz->subtables[i].apply_cached_func = thiz->subtables[i].apply_func; @@ -4332,6 +4449,17 @@ struct hb_ot_layout_lookup_accelerator_t return thiz; } + void fini () + { +#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE + if (cache) + { + assert (cache_user_idx != (unsigned) -1); + subtables[cache_user_idx].cache_func (cache, hb_ot_lookup_cache_op_t::DESTROY); + } +#endif + } + bool may_have (hb_codepoint_t g) const { return digest.may_have (g); } @@ -4340,6 +4468,7 @@ struct hb_ot_layout_lookup_accelerator_t #endif bool apply (hb_ot_apply_context_t *c, unsigned subtables_count, bool use_cache) const { + c->lookup_accel = this; #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE if (use_cache) { @@ -4379,10 +4508,13 @@ struct hb_ot_layout_lookup_accelerator_t hb_set_digest_t digest; - private: #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE + public: + void *cache = nullptr; + private: unsigned cache_user_idx = (unsigned) -1; #endif + private: hb_accelerate_subtables_context_t::hb_applicable_t subtables[HB_VAR_ARRAY]; }; @@ -4472,13 +4604,6 @@ struct GSUBGPOSVersion1_2 if (!c->subset_context->serializer->extend_min (&out->featureVars)) return_trace (false); - // TODO(qxliu76): the current implementation doesn't correctly handle feature variations - // that are dropped by instancing when the associated conditions don't trigger. - // Since partial instancing isn't yet supported this isn't an issue yet but will - // need to be fixed for partial instancing. - - - // if all axes are pinned all feature vars are dropped. bool ret = !c->subset_context->plan->all_axes_pinned && out->featureVars.serialize_subset (c->subset_context, featureVars, this, c); @@ -4500,9 +4625,9 @@ struct GSUBGPOS unsigned int get_size () const { switch (u.version.major) { - case 1: return u.version1.get_size (); + case 1: hb_barrier (); return u.version1.get_size (); #ifndef HB_NO_BEYOND_64K - case 2: return u.version2.get_size (); + case 2: hb_barrier (); return u.version2.get_size (); #endif default: return u.version.static_size; } @@ -4513,10 +4638,11 @@ struct GSUBGPOS { TRACE_SANITIZE (this); if (unlikely (!u.version.sanitize (c))) return_trace (false); + hb_barrier (); switch (u.version.major) { - case 1: return_trace (u.version1.sanitize (c)); + case 1: hb_barrier (); return_trace (u.version1.sanitize (c)); #ifndef HB_NO_BEYOND_64K - case 2: return_trace (u.version2.sanitize (c)); + case 2: hb_barrier (); return_trace (u.version2.sanitize (c)); #endif default: return_trace (true); } @@ -4526,9 +4652,9 @@ struct GSUBGPOS bool subset (hb_subset_layout_context_t *c) const { switch (u.version.major) { - case 1: return u.version1.subset (c); + case 1: hb_barrier (); return u.version1.subset (c); #ifndef HB_NO_BEYOND_64K - case 2: return u.version2.subset (c); + case 2: hb_barrier (); return u.version2.subset (c); #endif default: return false; } @@ -4537,9 +4663,9 @@ struct GSUBGPOS const ScriptList &get_script_list () const { switch (u.version.major) { - case 1: return this+u.version1.scriptList; + case 1: hb_barrier (); return this+u.version1.scriptList; #ifndef HB_NO_BEYOND_64K - case 2: return this+u.version2.scriptList; + case 2: hb_barrier (); return this+u.version2.scriptList; #endif default: return Null (ScriptList); } @@ -4547,9 +4673,9 @@ struct GSUBGPOS const FeatureList &get_feature_list () const { switch (u.version.major) { - case 1: return this+u.version1.featureList; + case 1: hb_barrier (); return this+u.version1.featureList; #ifndef HB_NO_BEYOND_64K - case 2: return this+u.version2.featureList; + case 2: hb_barrier (); return this+u.version2.featureList; #endif default: return Null (FeatureList); } @@ -4557,9 +4683,9 @@ struct GSUBGPOS unsigned int get_lookup_count () const { switch (u.version.major) { - case 1: return (this+u.version1.lookupList).len; + case 1: hb_barrier (); return (this+u.version1.lookupList).len; #ifndef HB_NO_BEYOND_64K - case 2: return (this+u.version2.lookupList).len; + case 2: hb_barrier (); return (this+u.version2.lookupList).len; #endif default: return 0; } @@ -4567,9 +4693,9 @@ struct GSUBGPOS const Lookup& get_lookup (unsigned int i) const { switch (u.version.major) { - case 1: return (this+u.version1.lookupList)[i]; + case 1: hb_barrier (); return (this+u.version1.lookupList)[i]; #ifndef HB_NO_BEYOND_64K - case 2: return (this+u.version2.lookupList)[i]; + case 2: hb_barrier (); return (this+u.version2.lookupList)[i]; #endif default: return Null (Lookup); } @@ -4577,9 +4703,9 @@ struct GSUBGPOS const FeatureVariations &get_feature_variations () const { switch (u.version.major) { - case 1: return (u.version.to_int () >= 0x00010001u ? this+u.version1.featureVars : Null (FeatureVariations)); + case 1: hb_barrier (); return (u.version.to_int () >= 0x00010001u && hb_barrier () ? this+u.version1.featureVars : Null (FeatureVariations)); #ifndef HB_NO_BEYOND_64K - case 2: return this+u.version2.featureVars; + case 2: hb_barrier (); return this+u.version2.featureVars; #endif default: return Null (FeatureVariations); } @@ -4613,13 +4739,14 @@ struct GSUBGPOS { return get_feature_list ().find_index (tag, index); } bool find_variations_index (const int *coords, unsigned int num_coords, - unsigned int *index) const + unsigned int *index, + ItemVarStoreInstancer *instancer) const { #ifdef HB_NO_VAR *index = FeatureVariations::NOT_FOUND_INDEX; return false; #endif - return get_feature_variations ().find_index (coords, num_coords, index); + return get_feature_variations ().find_index (coords, num_coords, index, instancer); } const Feature& get_feature_variation (unsigned int feature_index, unsigned int variations_index) const @@ -4638,11 +4765,11 @@ struct GSUBGPOS } void feature_variation_collect_lookups (const hb_set_t *feature_indexes, - const hb_hashmap_t *feature_substitutes_map, + const hb_hashmap_t> *feature_record_cond_idx_map, hb_set_t *lookup_indexes /* OUT */) const { #ifndef HB_NO_VAR - get_feature_variations ().collect_lookups (feature_indexes, feature_substitutes_map, lookup_indexes); + get_feature_variations ().collect_lookups (feature_indexes, feature_record_cond_idx_map, lookup_indexes); #endif } @@ -4761,7 +4888,7 @@ struct GSUBGPOS this->lookup_count = table->get_lookup_count (); - this->accels = (hb_atomic_ptr_t *) hb_calloc (this->lookup_count, sizeof (*accels)); + this->accels = (hb_atomic_t *) hb_calloc (this->lookup_count, sizeof (*accels)); if (unlikely (!this->accels)) { this->lookup_count = 0; @@ -4772,7 +4899,12 @@ struct GSUBGPOS ~accelerator_t () { for (unsigned int i = 0; i < this->lookup_count; i++) - hb_free (this->accels[i]); + { + auto *accel = this->accels[i].get_relaxed (); + if (accel) + accel->fini (); + hb_free (accel); + } hb_free (this->accels); this->table.destroy (); } @@ -4793,6 +4925,7 @@ struct GSUBGPOS if (unlikely (!accels[lookup_index].cmpexch (nullptr, accel))) { + accel->fini (); hb_free (accel); goto retry; } @@ -4803,7 +4936,7 @@ struct GSUBGPOS hb_blob_ptr_t table; unsigned int lookup_count; - hb_atomic_ptr_t *accels; + hb_atomic_t *accels; }; protected: diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-jstf-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-jstf-table.hh index 948c88269251e..823520f748340 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-jstf-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-jstf-table.hh @@ -214,6 +214,7 @@ struct JSTF { TRACE_SANITIZE (this); return_trace (version.sanitize (c) && + hb_barrier () && likely (version.major == 1) && scriptList.sanitize (c, this)); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.cc index 49465acb75e21..afe5296320196 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.cc @@ -87,7 +87,7 @@ using OT::Layout::GPOS; bool hb_ot_layout_has_kerning (hb_face_t *face) { - return face->table.kern->has_data (); + return face->table.kern->table->has_data (); } /** @@ -103,7 +103,7 @@ hb_ot_layout_has_kerning (hb_face_t *face) bool hb_ot_layout_has_machine_kerning (hb_face_t *face) { - return face->table.kern->has_state_machine (); + return face->table.kern->table->has_state_machine (); } /** @@ -123,7 +123,7 @@ hb_ot_layout_has_machine_kerning (hb_face_t *face) bool hb_ot_layout_has_cross_kerning (hb_face_t *face) { - return face->table.kern->has_cross_stream (); + return face->table.kern->table->has_cross_stream (); } void @@ -131,13 +131,15 @@ hb_ot_layout_kern (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) { - hb_blob_t *blob = font->face->table.kern.get_blob (); - const AAT::kern& kern = *blob->as (); + auto &accel = *font->face->table.kern; + hb_blob_t *blob = accel.get_blob (); AAT::hb_aat_apply_context_t c (plan, font, buffer, blob); if (!buffer->message (font, "start table kern")) return; - kern.apply (&c); + c.buffer_glyph_set = accel.scratch.create_buffer_glyph_set (); + accel.apply (&c); + accel.scratch.destroy_buffer_glyph_set (c.buffer_glyph_set); (void) buffer->message (font, "end table kern"); } #endif @@ -246,6 +248,18 @@ OT::GDEF::is_blocklisted (hb_blob_t *blob, /* sha1sum: c26e41d567ed821bed997e937bc0c41435689e85 Padauk.ttf * "Padauk Regular" "Version 2.5", see https://crbug.com/681813 */ case HB_CODEPOINT_ENCODE3 (1004, 59092, 14836): + /* 88d2006ca084f04af2df1954ed714a8c71e8400f Courier New.ttf from macOS 15 */ + case HB_CODEPOINT_ENCODE3 (588, 5078, 14418): + /* 608e3ebb6dd1aee521cff08eb07d500a2c59df68 Courier New Bold.ttf from macOS 15 */ + case HB_CODEPOINT_ENCODE3 (588, 5078, 14238): + /* d13221044ff054efd78f1cd8631b853c3ce85676 cour.ttf from Windows 10 */ + case HB_CODEPOINT_ENCODE3 (894, 17162, 33960): + /* 68ed4a22d8067fcf1622ac6f6e2f4d3a2e3ec394 courbd.ttf from Windows 10 */ + case HB_CODEPOINT_ENCODE3 (894, 17154, 34472): + /* 4cdb0259c96b7fd7c103821bb8f08f7cc6b211d7 cour.ttf from Windows 8.1 */ + case HB_CODEPOINT_ENCODE3 (816, 7868, 17052): + /* 920483d8a8ed37f7f0afdabbe7f679aece7c75d8 courbd.ttf from Windows 8.1 */ + case HB_CODEPOINT_ENCODE3 (816, 7868, 17138): return true; } return false; @@ -1443,8 +1457,12 @@ hb_ot_layout_table_find_feature_variations (hb_face_t *face, unsigned int *variations_index /* out */) { const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + const OT::GDEF &gdef = *face->table.GDEF->table; - return g.find_variations_index (coords, num_coords, variations_index); + auto instancer = OT::ItemVarStoreInstancer(&gdef.get_var_store(), nullptr, + hb_array (coords, num_coords)); + + return g.find_variations_index (coords, num_coords, variations_index, &instancer); } @@ -1907,9 +1925,10 @@ apply_forward (OT::hb_ot_apply_context_t *c, while (buffer->idx < buffer->len && buffer->successful) { bool applied = false; - if (accel.digest.may_have (buffer->cur().codepoint) && - (buffer->cur().mask & c->lookup_mask) && - c->check_glyph_property (&buffer->cur(), c->lookup_props)) + auto &cur = buffer->cur(); + if (accel.digest.may_have (cur.codepoint) && + (cur.mask & c->lookup_mask) && + c->check_glyph_property (&cur, c->lookup_props)) { applied = accel.apply (c, subtable_count, use_cache); } @@ -1935,9 +1954,10 @@ apply_backward (OT::hb_ot_apply_context_t *c, hb_buffer_t *buffer = c->buffer; do { - if (accel.digest.may_have (buffer->cur().codepoint) && - (buffer->cur().mask & c->lookup_mask) && - c->check_glyph_property (&buffer->cur(), c->lookup_props)) + auto &cur = buffer->cur(); + if (accel.digest.may_have (cur.codepoint) && + (cur.mask & c->lookup_mask) && + c->check_glyph_property (&cur, c->lookup_props)) ret |= accel.apply (c, subtable_count, false); /* The reverse lookup doesn't "advance" cursor (for good reason). */ @@ -1995,7 +2015,11 @@ inline void hb_ot_map_t::apply (const Proxy &proxy, { const unsigned int table_index = proxy.table_index; unsigned int i = 0; - OT::hb_ot_apply_context_t c (table_index, font, buffer, proxy.accel.get_blob ()); + + auto *font_data = font->data.ot.get (); + auto *var_store_cache = font_data == HB_SHAPER_DATA_SUCCEEDED ? nullptr : (OT::ItemVariationStore::cache_t *) font_data; + + OT::hb_ot_apply_context_t c (table_index, font, buffer, proxy.accel.get_blob (), var_store_cache); c.set_recurse_func (Proxy::Lookup::template dispatch_recurse_func); for (unsigned int stage_index = 0; stage_index < stages[table_index].length; stage_index++) @@ -2017,7 +2041,7 @@ inline void hb_ot_map_t::apply (const Proxy &proxy, * (plus some past glyphs). * * Only try applying the lookup if there is any overlap. */ - if (accel->digest.may_have (c.digest)) + if (accel->digest.may_intersect (c.digest)) { c.set_lookup_index (lookup_index); c.set_lookup_mask (lookup.mask, false); @@ -2043,7 +2067,7 @@ inline void hb_ot_map_t::apply (const Proxy &proxy, if (stage->pause_func (plan, font, buffer)) { /* Refresh working buffer digest since buffer changed. */ - c.digest = buffer->digest (); + buffer->collect_codepoints (c.digest); } } } @@ -2127,7 +2151,7 @@ hb_ot_layout_get_font_extents (hb_font_t *font, hb_tag_t language_tag, hb_font_extents_t *extents) { - hb_position_t min, max; + hb_position_t min = 0, max = 0; if (font->face->table.BASE->get_min_max (font, direction, script_tag, language_tag, HB_TAG_NONE, &min, &max)) { @@ -2608,7 +2632,8 @@ struct hb_get_glyph_alternates_dispatch_t : * @alternate_glyphs: (out caller-allocates) (array length=alternate_count): A glyphs buffer. * Alternate glyphs associated with the glyph id. * - * Fetches alternates of a glyph from a given GSUB lookup index. + * Fetches alternates of a glyph from a given GSUB lookup index. Note that for one-to-one GSUB + * glyph substitutions, this function fetches the substituted glyph. * * Return value: Total number of alternates found in the specific lookup index for the given glyph id. * diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.hh index 2d9a184a89339..a68c8421e4c84 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.hh @@ -173,12 +173,12 @@ _hb_next_syllable (hb_buffer_t *buffer, unsigned int start) /* Design: * unicode_props() is a two-byte number. The low byte includes: - * - General_Category: 5 bits. + * - Modified General_Category: 5 bits. * - A bit each for: * * Is it Default_Ignorable(); we have a modified Default_Ignorable(). * * Whether it's one of the four Mongolian Free Variation Selectors, * CGJ, or other characters that are hidden but should not be ignored - * like most other Default_Ignorable()s do during matching. + * like most other Default_Ignorable()s do during GSUB matching. * * Whether it's a grapheme continuation. * * The high-byte has different meanings, switched by the Gen-Cat: @@ -186,17 +186,24 @@ _hb_next_syllable (hb_buffer_t *buffer, unsigned int start) * - For Cf: whether it's ZWJ, ZWNJ, or something else. * - For Ws: index of which space character this is, if space fallback * is needed, ie. we don't set this by default, only if asked to. + * + * Above I said "modified" General_Category. This is because we need to + * remember Variation Selectors, and we don't have bits left. So we + * change their Gen_Cat from Mn to Cf, and use a bit of the high byte to + * remember them. */ enum hb_unicode_props_flags_t { UPROPS_MASK_GEN_CAT = 0x001Fu, UPROPS_MASK_IGNORABLE = 0x0020u, - UPROPS_MASK_HIDDEN = 0x0040u, /* MONGOLIAN FREE VARIATION SELECTOR 1..4, or TAG characters */ + UPROPS_MASK_HIDDEN = 0x0040u, /* MONGOLIAN FREE VARIATION SELECTOR 1..4, or TAG characters, or CGJ sometimes */ UPROPS_MASK_CONTINUATION=0x0080u, /* If GEN_CAT=FORMAT, top byte masks: */ UPROPS_MASK_Cf_ZWJ = 0x0100u, - UPROPS_MASK_Cf_ZWNJ = 0x0200u + UPROPS_MASK_Cf_ZWNJ = 0x0200u, + UPROPS_MASK_Cf_VS = 0x0400u, + UPROPS_MASK_Cf_AAT_DELETED = 0x0800u }; HB_MARK_AS_FLAG_T (hb_unicode_props_flags_t); @@ -229,7 +236,7 @@ _hb_glyph_info_set_unicode_props (hb_glyph_info_t *info, hb_buffer_t *buffer) /* TAG characters need similar treatment. Fixes: * https://github.com/harfbuzz/harfbuzz/issues/463 */ else if (unlikely (hb_in_range (u, 0xE0020u, 0xE007Fu))) props |= UPROPS_MASK_HIDDEN; - /* COMBINING GRAPHEME JOINER should not be skipped; at least some times. + /* COMBINING GRAPHEME JOINER should not be skipped during GSUB either. * https://github.com/harfbuzz/harfbuzz/issues/554 */ else if (unlikely (u == 0x034Fu)) { @@ -302,6 +309,27 @@ _hb_glyph_info_get_unicode_space_fallback_type (const hb_glyph_info_t *info) (hb_unicode_funcs_t::space_t) (info->unicode_props()>>8) : hb_unicode_funcs_t::NOT_SPACE; } +static inline bool +_hb_glyph_info_is_variation_selector (const hb_glyph_info_t *info) +{ + return _hb_glyph_info_get_general_category (info) == + HB_UNICODE_GENERAL_CATEGORY_FORMAT && + (info->unicode_props() & UPROPS_MASK_Cf_VS); +} +static inline void +_hb_glyph_info_set_variation_selector (hb_glyph_info_t *info, bool customize) +{ + if (customize) + { + _hb_glyph_info_set_general_category (info, HB_UNICODE_GENERAL_CATEGORY_FORMAT); + info->unicode_props() |= UPROPS_MASK_Cf_VS; + } + else + { + // Reset to their original condition + _hb_glyph_info_set_general_category (info, HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK); + } +} static inline bool _hb_glyph_info_substituted (const hb_glyph_info_t *info); @@ -311,12 +339,20 @@ _hb_glyph_info_is_default_ignorable (const hb_glyph_info_t *info) return (info->unicode_props() & UPROPS_MASK_IGNORABLE) && !_hb_glyph_info_substituted (info); } +static inline void +_hb_glyph_info_set_default_ignorable (hb_glyph_info_t *info) +{ + info->unicode_props() |= UPROPS_MASK_IGNORABLE; +} +static inline void +_hb_glyph_info_clear_default_ignorable (hb_glyph_info_t *info) +{ + info->unicode_props() &= ~ UPROPS_MASK_IGNORABLE; +} static inline bool -_hb_glyph_info_is_default_ignorable_and_not_hidden (const hb_glyph_info_t *info) +_hb_glyph_info_is_hidden (const hb_glyph_info_t *info) { - return ((info->unicode_props() & (UPROPS_MASK_IGNORABLE|UPROPS_MASK_HIDDEN)) - == UPROPS_MASK_IGNORABLE) && - !_hb_glyph_info_substituted (info); + return info->unicode_props() & UPROPS_MASK_HIDDEN; } static inline void _hb_glyph_info_unhide (hb_glyph_info_t *info) @@ -330,7 +366,7 @@ _hb_glyph_info_set_continuation (hb_glyph_info_t *info) info->unicode_props() |= UPROPS_MASK_CONTINUATION; } static inline void -_hb_glyph_info_reset_continuation (hb_glyph_info_t *info) +_hb_glyph_info_clear_continuation (hb_glyph_info_t *info) { info->unicode_props() &= ~ UPROPS_MASK_CONTINUATION; } @@ -351,6 +387,8 @@ _hb_grapheme_group_func (const hb_glyph_info_t& a HB_UNUSED, static inline void _hb_ot_layout_reverse_graphemes (hb_buffer_t *buffer) { + // MONOTONE_GRAPHEMES was already applied and is taken care of by _hb_grapheme_group_func. + // So we just check for MONOTONE_CHARACTERS here. buffer->reverse_groups (_hb_grapheme_group_func, buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS); } @@ -383,6 +421,18 @@ _hb_glyph_info_flip_joiners (hb_glyph_info_t *info) return; info->unicode_props() ^= UPROPS_MASK_Cf_ZWNJ | UPROPS_MASK_Cf_ZWJ; } +static inline bool +_hb_glyph_info_is_aat_deleted (const hb_glyph_info_t *info) +{ + return _hb_glyph_info_is_unicode_format (info) && (info->unicode_props() & UPROPS_MASK_Cf_AAT_DELETED); +} +static inline void +_hb_glyph_info_set_aat_deleted (hb_glyph_info_t *info) +{ + _hb_glyph_info_set_general_category (info, HB_UNICODE_GENERAL_CATEGORY_FORMAT); + info->unicode_props() |= UPROPS_MASK_Cf_AAT_DELETED; + info->unicode_props() |= UPROPS_MASK_HIDDEN; +} /* lig_props: aka lig_id / lig_comp * @@ -603,8 +653,7 @@ _hb_buffer_assert_gsubgpos_vars (hb_buffer_t *buffer) } /* Make sure no one directly touches our props... */ -#undef unicode_props0 -#undef unicode_props1 +#undef unicode_props #undef lig_props #undef glyph_props diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-map.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-map.cc index 261ce43049c67..a0cdfddfa632f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-map.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-map.cc @@ -390,5 +390,19 @@ hb_ot_map_builder_t::compile (hb_ot_map_t &m, } } +unsigned int hb_ot_map_t::get_feature_tags (unsigned int start_offset, unsigned int *tag_count, hb_tag_t *tags) const +{ + if (tag_count) + { + auto sub_features = features.as_array ().sub_array (start_offset, tag_count); + if (tags) + { + for (unsigned int i = 0; i < sub_features.length; i++) + tags[i] = sub_features[i].tag; + } + } + + return features.length; +} #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-map.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-map.hh index 110361552c028..68af9792439fd 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-map.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-map.hh @@ -166,6 +166,9 @@ struct hb_ot_map_t const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const; HB_INTERNAL void substitute (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const; HB_INTERNAL void position (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const; + HB_INTERNAL unsigned int get_feature_tags (unsigned int start_offset, + unsigned int *tag_count, /* IN/OUT */ + hb_tag_t *tags /* OUT */) const; public: hb_tag_t chosen_script[2]; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-math-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-math-table.hh index 63bcd132cf436..b33e01fd6c9ec 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-math-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-math-table.hh @@ -333,6 +333,7 @@ struct MathKern { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && c->check_array (mathValueRecordsZ.arrayZ, 2 * heightCount + 1) && sanitize_math_value_records (c)); } @@ -343,27 +344,20 @@ struct MathKern const MathValueRecord* kernValue = mathValueRecordsZ.arrayZ + heightCount; int sign = font->y_scale < 0 ? -1 : +1; - /* The description of the MathKern table is a ambiguous, but interpreting - * "between the two heights found at those indexes" for 0 < i < len as - * - * correctionHeight[i-1] < correction_height <= correctionHeight[i] - * - * makes the result consistent with the limit cases and we can just use the - * binary search algorithm of std::upper_bound: + /* According to OpenType spec (v1.9), except for the boundary cases, the index + * chosen for kern value should be i such that + * correctionHeight[i-1] <= correction_height < correctionHeight[i] + * We can use the binary search algorithm of std::upper_bound(). Or, we can + * use the internal hb_bsearch_impl. */ - unsigned int i = 0; - unsigned int count = heightCount; - while (count > 0) - { - unsigned int half = count / 2; - hb_position_t height = correctionHeight[i + half].get_y_value (font, this); - if (sign * height < sign * correction_height) - { - i += half + 1; - count -= half + 1; - } else - count = half; - } + unsigned int pos; + auto cmp = +[](const void* key, const void* p, + int sign, hb_font_t* font, const MathKern* mathKern) -> int { + return sign * *(hb_position_t*)key - sign * ((MathValueRecord*)p)->get_y_value(font, mathKern); + }; + unsigned int i = hb_bsearch_impl(&pos, correction_height, correctionHeight, + heightCount, MathValueRecord::static_size, + cmp, sign, font, this) ? pos + 1 : pos; return kernValue[i].get_x_value (font, this); } @@ -984,6 +978,7 @@ struct MathVariants return_trace (c->check_struct (this) && vertGlyphCoverage.sanitize (c, this) && horizGlyphCoverage.sanitize (c, this) && + hb_barrier () && c->check_array (glyphConstruction.arrayZ, vertGlyphCount + horizGlyphCount) && sanitize_offsets (c)); } @@ -1103,11 +1098,30 @@ struct MATH TRACE_SANITIZE (this); return_trace (version.sanitize (c) && likely (version.major == 1) && + hb_barrier () && mathConstants.sanitize (c, this) && mathGlyphInfo.sanitize (c, this) && mathVariants.sanitize (c, this)); } + // https://github.com/harfbuzz/harfbuzz/issues/4653 + HB_INTERNAL bool is_bad_cambria (hb_font_t *font) const + { +#ifndef HB_NO_MATH + switch HB_CODEPOINT_ENCODE3 (font->face->table.MATH.get_blob ()->length, + get_constant (HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT, font), + get_constant (HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT, font)) + { + /* sha1sum:ab4a4fe054d23061f3c039493d6f665cfda2ecf5 cambria.ttc + * sha1sum:086855301bff644f9d8827b88491fcf73a6d4cb9 cambria.ttc + * sha1sum:b1e5a3feaca2ea3dfcf79ccb377de749ecf60343 cambria.ttc */ + case HB_CODEPOINT_ENCODE3 (25722, 2500, 3000): + return true; + } +#endif + return false; + } + hb_position_t get_constant (hb_ot_math_constant_t constant, hb_font_t *font) const { return (this+mathConstants).get_value (constant, font); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-math.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-math.cc index 191149964f93f..ab1f50c8eb804 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-math.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-math.cc @@ -87,6 +87,20 @@ hb_position_t hb_ot_math_get_constant (hb_font_t *font, hb_ot_math_constant_t constant) { + /* https://github.com/harfbuzz/harfbuzz/issues/4653 + * Cambria Math has incorrect value for displayOperatorMinHeight, and + * apparently Microsoft implementation swaps displayOperatorMinHeight and + * delimitedSubFormulaMinHeight, so we do the same if we detect Cambria Math + * with the swapped values. */ + if ((constant == HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT || + constant == HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT) && + font->face->table.MATH->is_bad_cambria (font)) + { + if (constant == HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT) + constant = HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT; + else + constant = HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT; + } return font->face->table.MATH->get_constant(constant, font); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-maxp-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-maxp-table.hh index f7f2e860e4a9f..f6eeda40ed672 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-maxp-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-maxp-table.hh @@ -85,7 +85,7 @@ struct maxp TRACE_SANITIZE (this); if (unlikely (!c->check_struct (this))) return_trace (false); - + hb_barrier (); if (version.major == 1) { const maxpV1Tail &v1 = StructAfter (*this); @@ -103,6 +103,7 @@ struct maxp maxp_prime->numGlyphs = hb_min (c->plan->num_output_glyphs (), 0xFFFFu); if (maxp_prime->version.major == 1) { + hb_barrier (); const maxpV1Tail *src_v1 = &StructAfter (*this); maxpV1Tail *dest_v1 = c->serializer->embed (src_v1); if (unlikely (!dest_v1)) return_trace (false); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-meta-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-meta-table.hh index 1f011882807a0..20c62b83edbe3 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-meta-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-meta-table.hh @@ -51,6 +51,7 @@ struct DataMap { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && + hb_barrier () && dataZ.sanitize (c, base, dataLength))); } @@ -101,6 +102,7 @@ struct meta { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && + hb_barrier () && version == 1 && dataMaps.sanitize (c, this))); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-os2-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-os2-table.hh index 30ae335d703f6..24e9c9703531a 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-os2-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-os2-table.hh @@ -209,6 +209,23 @@ struct OS2 return ret; } + static unsigned calc_avg_char_width (const hb_hashmap_t>& hmtx_map) + { + unsigned num = 0; + unsigned total_width = 0; + for (const auto& _ : hmtx_map.values_ref ()) + { + unsigned width = _.first; + if (width) + { + total_width += width; + num++; + } + } + + return num ? (unsigned) roundf ((double) total_width / (double) num) : 0; + } + bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); @@ -239,17 +256,23 @@ struct OS2 if (os2_prime->version >= 2) { + hb_barrier (); auto *table = & const_cast (os2_prime->v2 ()); HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_X_HEIGHT, sxHeight); HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_CAP_HEIGHT, sCapHeight); } + + unsigned avg_char_width = calc_avg_char_width (c->plan->hmtx_map); + if (!c->serializer->check_assign (os2_prime->xAvgCharWidth, avg_char_width, + HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); } #endif Triple *axis_range; if (c->plan->user_axes_location.has (HB_TAG ('w','g','h','t'), &axis_range)) { - unsigned weight_class = static_cast (roundf (hb_clamp (axis_range->middle, 1.0f, 1000.0f))); + unsigned weight_class = static_cast (roundf (hb_clamp (axis_range->middle, 1.0, 1000.0))); if (os2_prime->usWeightClass != weight_class) os2_prime->usWeightClass = weight_class; } @@ -261,12 +284,12 @@ struct OS2 os2_prime->usWidthClass = width_class; } + os2_prime->usFirstCharIndex = hb_min (0xFFFFu, c->plan->os2_info.min_cmap_codepoint); + os2_prime->usLastCharIndex = hb_min (0xFFFFu, c->plan->os2_info.max_cmap_codepoint); + if (c->plan->flags & HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES) return_trace (true); - os2_prime->usFirstCharIndex = hb_min (0xFFFFu, c->plan->unicodes.get_min ()); - os2_prime->usLastCharIndex = hb_min (0xFFFFu, c->plan->unicodes.get_max ()); - _update_unicode_ranges (&c->plan->unicodes, os2_prime->ulUnicodeRange); return_trace (true); @@ -334,6 +357,7 @@ struct OS2 { TRACE_SANITIZE (this); if (unlikely (!c->check_struct (this))) return_trace (false); + hb_barrier (); if (unlikely (version >= 1 && !v1X.sanitize (c))) return_trace (false); if (unlikely (version >= 2 && !v2X.sanitize (c))) return_trace (false); if (unlikely (version >= 5 && !v5X.sanitize (c))) return_trace (false); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-post-table-v2subset.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-post-table-v2subset.hh index d44233610a695..67d1b6aa713a7 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-post-table-v2subset.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-post-table-v2subset.hh @@ -84,9 +84,9 @@ HB_INTERNAL bool postV2Tail::subset (hb_subset_context_t *c) const old_gid_new_index_map.alloc (num_glyphs); glyph_name_to_new_index.alloc (num_glyphs); - for (hb_codepoint_t new_gid = 0; new_gid < num_glyphs; new_gid++) + for (auto _ : c->plan->new_to_old_gid_list) { - hb_codepoint_t old_gid = reverse_glyph_map.get (new_gid); + hb_codepoint_t old_gid = _.second; unsigned old_index = glyphNameIndex[old_gid]; unsigned new_index; @@ -125,13 +125,22 @@ HB_INTERNAL bool postV2Tail::subset (hb_subset_context_t *c) const old_gid_new_index_map.set (old_gid, new_index); } + if (old_gid_new_index_map.in_error()) + return_trace (false); + auto index_iter = + hb_range (num_glyphs) - | hb_map (reverse_glyph_map) - | hb_map_retains_sorting ([&](hb_codepoint_t old_gid) + | hb_map_retains_sorting ([&](hb_codepoint_t new_gid) { - unsigned new_index = old_gid_new_index_map.get (old_gid); - return hb_pair_t (old_gid, new_index); + hb_codepoint_t *old_gid; + /* use 0 for retain-gid holes, which refers to the name .notdef, + * as the glyphNameIndex entry for that glyph ID."*/ + unsigned new_index = 0; + if (reverse_glyph_map.has (new_gid, &old_gid)) { + new_index = old_gid_new_index_map.get (*old_gid); + return hb_pair_t (*old_gid, new_index); + } + return hb_pair_t (new_gid, new_index); }) ; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-post-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-post-table.hh index d41c86f8bd317..2c4c6c0466368 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-post-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-post-table.hh @@ -116,13 +116,16 @@ struct post Triple *axis_range; if (c->plan->user_axes_location.has (HB_TAG ('s','l','n','t'), &axis_range)) { - float italic_angle = hb_max (-90.f, hb_min (axis_range->middle, 90.f)); + float italic_angle = hb_max (-90.0, hb_min (axis_range->middle, 90.0)); if (post_prime->italicAngle.to_float () != italic_angle) post_prime->italicAngle.set_float (italic_angle); } if (glyph_names && version.major == 2) + { + hb_barrier (); return_trace (v2X.subset (c)); + } return_trace (true); } @@ -138,6 +141,7 @@ struct post version = table->version.to_int (); if (version != 0x00020000) return; + hb_barrier (); const postV2Tail &v2 = table->v2X; @@ -217,10 +221,16 @@ struct post unsigned int get_glyph_count () const { if (version == 0x00010000) + { + hb_barrier (); return format1_names_length; + } if (version == 0x00020000) + { + hb_barrier (); return glyphNameIndex->len; + } return 0; } @@ -245,13 +255,18 @@ struct post { if (version == 0x00010000) { + hb_barrier (); if (glyph >= format1_names_length) return hb_bytes_t (); return format1_names (glyph); } - if (version != 0x00020000 || glyph >= glyphNameIndex->len) + if (version != 0x00020000) + return hb_bytes_t (); + hb_barrier (); + + if (glyph >= glyphNameIndex->len) return hb_bytes_t (); unsigned int index = glyphNameIndex->arrayZ[glyph]; @@ -275,7 +290,7 @@ struct post const Array16Of *glyphNameIndex = nullptr; hb_vector_t index_to_offset; const uint8_t *pool = nullptr; - hb_atomic_ptr_t gids_sorted_by_name; + mutable hb_atomic_t gids_sorted_by_name; }; bool has_data () const { return version.to_int (); } @@ -284,8 +299,9 @@ struct post { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && (version.to_int () == 0x00010000 || - (version.to_int () == 0x00020000 && v2X.sanitize (c)) || + (version.to_int () == 0x00020000 && hb_barrier () && v2X.sanitize (c)) || version.to_int () == 0x00030000)); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-fallback.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-fallback.cc index b74d3104a7e56..2401e18f0e64a 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-fallback.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-fallback.cc @@ -427,8 +427,13 @@ position_cluster (const hb_ot_shape_plan_t *plan, /* Find mark glyphs */ unsigned int j; for (j = i + 1; j < end; j++) + { + if (_hb_glyph_info_is_hidden (&info[j]) || + _hb_glyph_info_is_default_ignorable (&info[j])) + continue; if (!_hb_glyph_info_is_unicode_mark (&info[j])) break; + } position_around_base (plan, font, buffer, i, j, adjust_offsets_when_zeroing); @@ -455,7 +460,9 @@ _hb_ot_shape_fallback_mark_position (const hb_ot_shape_plan_t *plan, unsigned int count = buffer->len; hb_glyph_info_t *info = buffer->info; for (unsigned int i = 1; i < count; i++) - if (likely (!_hb_glyph_info_is_unicode_mark (&info[i]))) { + if (likely (!_hb_glyph_info_is_unicode_mark (&info[i]) && + !_hb_glyph_info_is_hidden (&info[i]) && + !_hb_glyph_info_is_default_ignorable (&info[i]))) { position_cluster (plan, font, buffer, start, i, adjust_offsets_when_zeroing); start = i; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.cc index b1acc7e7db3c3..cb941bc7fbd22 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.cc @@ -74,23 +74,6 @@ * Indic shaper may want to disallow recomposing of two matras. */ -static bool -decompose_unicode (const hb_ot_shape_normalize_context_t *c, - hb_codepoint_t ab, - hb_codepoint_t *a, - hb_codepoint_t *b) -{ - return (bool) c->unicode->decompose (ab, a, b); -} - -static bool -compose_unicode (const hb_ot_shape_normalize_context_t *c, - hb_codepoint_t a, - hb_codepoint_t b, - hb_codepoint_t *ab) -{ - return (bool) c->unicode->compose (a, b, ab); -} static inline void set_glyph (hb_glyph_info_t &info, hb_font_t *font) @@ -170,7 +153,7 @@ decompose_current_character (const hb_ot_shape_normalize_context_t *c, bool shor hb_codepoint_t u = buffer->cur().codepoint; hb_codepoint_t glyph = 0; - if (shortest && c->font->get_nominal_glyph (u, &glyph, c->not_found)) + if (shortest && c->font->get_nominal_glyph (u, &glyph, buffer->not_found)) { next_char (buffer, glyph); return; @@ -182,7 +165,7 @@ decompose_current_character (const hb_ot_shape_normalize_context_t *c, bool shor return; } - if (!shortest && c->font->get_nominal_glyph (u, &glyph, c->not_found)) + if (!shortest && c->font->get_nominal_glyph (u, &glyph, buffer->not_found)) { next_char (buffer, glyph); return; @@ -237,6 +220,12 @@ handle_variation_selector_cluster (const hb_ot_shape_normalize_context_t *c, /* Just pass on the two characters separately, let GSUB do its magic. */ set_glyph (buffer->cur(), font); (void) buffer->next_glyph (); + + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_VARIATION_SELECTOR_FALLBACK; + _hb_glyph_info_set_variation_selector (&buffer->cur(), true); + if (buffer->not_found_variation_selector != HB_CODEPOINT_INVALID) + _hb_glyph_info_clear_default_ignorable (&buffer->cur()); + set_glyph (buffer->cur(), font); (void) buffer->next_glyph (); } @@ -307,15 +296,15 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, mode = HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS; } - const hb_ot_shape_normalize_context_t c = { + hb_ot_shape_normalize_context_t c = { plan, buffer, font, buffer->unicode, - buffer->not_found, - plan->shaper->decompose ? plan->shaper->decompose : decompose_unicode, - plan->shaper->compose ? plan->shaper->compose : compose_unicode + plan->shaper->decompose ? plan->shaper->decompose : hb_ot_shape_normalize_context_t::decompose_unicode, + plan->shaper->compose ? plan->shaper->compose : hb_ot_shape_normalize_context_t::compose_unicode }; + c.override_decompose_and_compose (plan->shaper->decompose, plan->shaper->compose); bool always_short_circuit = mode == HB_OT_SHAPE_NORMALIZATION_MODE_NONE; bool might_short_circuit = always_short_circuit || diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.hh index afd62dd40726a..9f17bdbb24326 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.hh @@ -28,6 +28,7 @@ #define HB_OT_SHAPE_NORMALIZE_HH #include "hb.hh" +#include "hb-unicode.hh" /* buffer var allocations, used during the normalization process */ @@ -52,11 +53,42 @@ HB_INTERNAL void _hb_ot_shape_normalize (const hb_ot_shape_plan_t *shaper, struct hb_ot_shape_normalize_context_t { + static bool + decompose_unicode (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b) + { + return (bool) c->unicode->decompose (ab, a, b); + } + + static bool + compose_unicode (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab) + { + return (bool) c->unicode->compose (a, b, ab); + } + + void + override_decompose_and_compose (bool (*decompose) (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b), + bool (*compose) (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab)) + { + this->decompose = decompose ? decompose : decompose_unicode; + this->compose = compose ? compose : compose_unicode; + } + const hb_ot_shape_plan_t *plan; hb_buffer_t *buffer; hb_font_t *font; hb_unicode_funcs_t *unicode; - const hb_codepoint_t not_found; bool (*decompose) (const hb_ot_shape_normalize_context_t *c, hb_codepoint_t ab, hb_codepoint_t *a, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.cc index f936ac45f37e1..477a7c7fc72ba 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.cc @@ -46,6 +46,9 @@ #include "hb-set.hh" #include "hb-aat-layout.hh" +#include "hb-ot-layout-gdef-table.hh" +#include "hb-ot-stat-table.hh" + static inline bool _hb_codepoint_is_regional_indicator (hb_codepoint_t u) @@ -82,10 +85,11 @@ hb_ot_shape_planner_t::hb_ot_shape_planner_t (hb_face_t *fac props (props), map (face, props) #ifndef HB_NO_AAT_SHAPE + , aat_map (face, props) , apply_morx (_hb_apply_morx (face, props)) #endif { - shaper = hb_ot_shaper_categorize (this); + shaper = hb_ot_shaper_categorize (props.script, props.direction, map.chosen_script[0]); script_zero_marks = shaper->zero_width_marks != HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE; script_fallback_mark_positioning = shaper->fallback_position; @@ -104,6 +108,10 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan, plan.props = props; plan.shaper = shaper; map.compile (plan.map, key); +#ifndef HB_NO_AAT_SHAPE + if (apply_morx) + aat_map.compile (plan.aat_map); +#endif #ifndef HB_NO_OT_SHAPE_FRACTIONS plan.frac_mask = plan.map.get_1_mask (HB_TAG ('f','r','a','c')); @@ -121,10 +129,6 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan, plan.kern_mask = plan.map.get_mask (kern_tag); plan.requested_kerning = !!plan.kern_mask; #endif -#ifndef HB_NO_AAT_SHAPE - plan.trak_mask = plan.map.get_mask (HB_TAG ('t','r','a','k')); - plan.requested_tracking = !!plan.trak_mask; -#endif bool has_gpos_kern = plan.map.get_feature_index (1, kern_tag) != HB_OT_LAYOUT_NO_FEATURE_INDEX; bool disable_gpos = plan.shaper->gpos_tag && @@ -155,7 +159,7 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan, #endif bool has_gpos = !disable_gpos && hb_ot_layout_has_positioning (face); if (false) - ; + {} #ifndef HB_NO_AAT_SHAPE /* Prefer GPOS over kerx if GSUB is present; * https://github.com/harfbuzz/harfbuzz/issues/3008 */ @@ -167,15 +171,16 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan, if (!plan.apply_kerx && (!has_gpos_kern || !plan.apply_gpos)) { + if (false) {} #ifndef HB_NO_AAT_SHAPE - if (has_kerx) + else if (has_kerx) plan.apply_kerx = true; - else #endif #ifndef HB_NO_OT_KERN - if (hb_ot_layout_has_kerning (face)) + else if (hb_ot_layout_has_kerning (face)) plan.apply_kern = true; #endif + else {} } plan.apply_fallback_kern = !(plan.apply_gpos || plan.apply_kerx || plan.apply_kern); @@ -207,8 +212,13 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan, if (plan.apply_morx) plan.adjust_mark_positioning_when_zeroing = false; - /* Currently we always apply trak. */ - plan.apply_trak = plan.requested_tracking && hb_aat_layout_has_tracking (face); + /* According to Ned, trak is applied by default for "modern fonts", as detected by presence of STAT table. */ +#ifndef HB_NO_STYLE + plan.apply_trak = hb_aat_layout_has_tracking (face) && face->table.STAT->has_data (); +#else + plan.apply_trak = false; +#endif + #endif } @@ -345,13 +355,6 @@ hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, /* Random! */ map->enable_feature (HB_TAG ('r','a','n','d'), F_RANDOM, HB_OT_MAP_MAX_VALUE); -#ifndef HB_NO_AAT_SHAPE - /* Tracking. We enable dummy feature here just to allow disabling - * AAT 'trak' table using features. - * https://github.com/harfbuzz/harfbuzz/issues/1303 */ - map->enable_feature (HB_TAG ('t','r','a','k'), F_HAS_FALLBACK); -#endif - map->enable_feature (HB_TAG ('H','a','r','f')); /* Considered required. */ map->enable_feature (HB_TAG ('H','A','R','F')); /* Considered discretionary. */ @@ -421,17 +424,26 @@ _hb_ot_shaper_face_data_destroy (hb_ot_face_data_t *data) * shaper font data */ -struct hb_ot_font_data_t {}; +struct hb_ot_font_data_t { + OT::ItemVariationStore::cache_t unused; // Just for alignment +}; hb_ot_font_data_t * -_hb_ot_shaper_font_data_create (hb_font_t *font HB_UNUSED) +_hb_ot_shaper_font_data_create (hb_font_t *font) { - return (hb_ot_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; + if (!font->num_coords) + return (hb_ot_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; + + const OT::ItemVariationStore &var_store = font->face->table.GDEF->table->get_var_store (); + auto *cache = (hb_ot_font_data_t *) var_store.create_cache (); + return cache ? cache : (hb_ot_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; } void -_hb_ot_shaper_font_data_destroy (hb_ot_font_data_t *data HB_UNUSED) +_hb_ot_shaper_font_data_destroy (hb_ot_font_data_t *data) { + if (data == HB_SHAPER_DATA_SUCCEEDED) return; + OT::ItemVariationStore::destroy_cache ((OT::ItemVariationStore::cache_t *) data); } @@ -567,7 +579,7 @@ hb_form_clusters (hb_buffer_t *buffer) if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII)) return; - if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) + if (HB_BUFFER_CLUSTER_LEVEL_IS_GRAPHEMES (buffer->cluster_level)) foreach_grapheme (buffer, start, end) buffer->merge_clusters (start, end); else @@ -625,7 +637,7 @@ hb_ensure_native_direction (hb_buffer_t *buffer) * Ogham fonts are supposed to be implemented BTT or not. Need to research that * first. */ if ((HB_DIRECTION_IS_HORIZONTAL (direction) && - direction != horiz_dir && horiz_dir != HB_DIRECTION_INVALID) || + direction != horiz_dir && HB_DIRECTION_IS_VALID (horiz_dir)) || (HB_DIRECTION_IS_VERTICAL (direction) && direction != HB_DIRECTION_TTB)) { @@ -836,6 +848,28 @@ hb_ot_zero_width_default_ignorables (const hb_buffer_t *buffer) pos[i].x_advance = pos[i].y_advance = pos[i].x_offset = pos[i].y_offset = 0; } +static void +hb_ot_deal_with_variation_selectors (hb_buffer_t *buffer) +{ + if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_VARIATION_SELECTOR_FALLBACK) || + buffer->not_found_variation_selector == HB_CODEPOINT_INVALID) + return; + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + + for (unsigned int i = 0; i < count; i++) + { + if (_hb_glyph_info_is_variation_selector (&info[i])) + { + info[i].codepoint = buffer->not_found_variation_selector; + pos[i].x_advance = pos[i].y_advance = pos[i].x_offset = pos[i].y_offset = 0; + _hb_glyph_info_set_variation_selector (&info[i], false); + } + } +} + static void hb_ot_hide_default_ignorables (hb_buffer_t *buffer, hb_font_t *font) @@ -965,6 +999,7 @@ hb_ot_substitute_post (const hb_ot_shape_context_t *c) hb_aat_layout_remove_deleted_glyphs (c->buffer); #endif + hb_ot_deal_with_variation_selectors (c->buffer); hb_ot_hide_default_ignorables (c->buffer, c->font); if (c->plan->shaper->postprocess_glyphs && @@ -1102,10 +1137,6 @@ hb_ot_position_plan (const hb_ot_shape_context_t *c) /* Finish off. Has to follow a certain order. */ hb_ot_layout_position_finish_advances (c->font, c->buffer); hb_ot_zero_width_default_ignorables (c->buffer); -#ifndef HB_NO_AAT_SHAPE - if (c->plan->apply_morx) - hb_aat_layout_zero_width_deleted_glyphs (c->buffer); -#endif hb_ot_layout_position_finish_offsets (c->font, c->buffer); /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ @@ -1253,6 +1284,36 @@ hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan, } +/** + * hb_ot_shape_plan_get_feature_tags: + * @shape_plan: A shaping plan + * @start_offset: The index of first feature to retrieve + * @tag_count: (inout): Input = the maximum number of features to return; + * Output = the actual number of features returned (may be zero) + * @tags: (out) (array length=tag_count): The array of enabled feature + * + * Fetches the list of OpenType feature tags enabled for a shaping plan, if possible. + * + * Return value: Total number of feature tagss. + * + * Since: 10.3.0 + */ +unsigned int +hb_ot_shape_plan_get_feature_tags (hb_shape_plan_t *shape_plan, + unsigned int start_offset, + unsigned int *tag_count, /* IN/OUT */ + hb_tag_t *tags /* OUT */) +{ +#ifndef HB_NO_OT_SHAPE + return shape_plan->ot.map.get_feature_tags (start_offset, tag_count, tags); +#else + if (tag_count) + *tag_count = 0; + return 0; +#endif +} + + /* TODO Move this to hb-ot-shape-normalize, make it do decompose, and make it public. */ static void add_char (hb_font_t *font, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.h b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.h index 545e2c3f1b4ce..1732831b1325c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.h @@ -48,6 +48,12 @@ hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan, hb_tag_t table_tag, hb_set_t *lookup_indexes /* OUT */); +HB_EXTERN unsigned int +hb_ot_shape_plan_get_feature_tags (hb_shape_plan_t *shape_plan, + unsigned int start_offset, + unsigned int *tag_count, /* IN/OUT */ + hb_tag_t *tags /* OUT */); + HB_END_DECLS #endif /* HB_OT_SHAPE_H */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.hh index 2fa121ced8137..068d7192d0d8f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.hh @@ -51,7 +51,8 @@ struct hb_ot_shape_plan_key_t bool equal (const hb_ot_shape_plan_key_t *other) { - return 0 == hb_memcmp (this, other, sizeof (*this)); + return variations_index[0] == other->variations_index[0] && + variations_index[1] == other->variations_index[1]; } }; @@ -65,6 +66,7 @@ struct hb_ot_shape_plan_t hb_segment_properties_t props; const struct hb_ot_shaper_t *shaper; hb_ot_map_t map; + hb_aat_map_t aat_map; const void *data; #ifndef HB_NO_OT_SHAPE_FRACTIONS hb_mask_t frac_mask, numr_mask, dnom_mask; @@ -79,22 +81,12 @@ struct hb_ot_shape_plan_t #else static constexpr hb_mask_t kern_mask = 0; #endif -#ifndef HB_NO_AAT_SHAPE - hb_mask_t trak_mask; -#else - static constexpr hb_mask_t trak_mask = 0; -#endif #ifndef HB_NO_OT_KERN bool requested_kerning : 1; #else static constexpr bool requested_kerning = false; #endif -#ifndef HB_NO_AAT_SHAPE - bool requested_tracking : 1; -#else - static constexpr bool requested_tracking = false; -#endif #ifndef HB_NO_OT_SHAPE_FRACTIONS bool has_frac : 1; #else @@ -152,6 +144,7 @@ struct hb_ot_shape_planner_t hb_segment_properties_t props; hb_ot_map_builder_t map; #ifndef HB_NO_AAT_SHAPE + hb_aat_map_builder_t aat_map; bool apply_morx : 1; #else static constexpr bool apply_morx = false; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic-fallback.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic-fallback.hh index 858a9872726b1..ff0b2acede1d9 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic-fallback.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic-fallback.hh @@ -355,6 +355,8 @@ arabic_fallback_plan_destroy (arabic_fallback_plan_t *fallback_plan) for (unsigned int i = 0; i < fallback_plan->num_lookups; i++) if (fallback_plan->lookup_array[i]) { + if (fallback_plan->accel_array[i]) + fallback_plan->accel_array[i]->fini (); hb_free (fallback_plan->accel_array[i]); if (fallback_plan->free_lookups) hb_free (fallback_plan->lookup_array[i]); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic-joining-list.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic-joining-list.hh index a5a7b84af4516..e38686e3ebb78 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic-joining-list.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic-joining-list.hh @@ -6,10 +6,10 @@ * * on files with these headers: * - * # ArabicShaping-15.1.0.txt - * # Date: 2023-01-05 - * # Scripts-15.1.0.txt - * # Date: 2023-07-28, 16:01:07 GMT + * # ArabicShaping-16.0.0.txt + * # Date: 2024-07-30 + * # Scripts-16.0.0.txt + * # Date: 2024-04-30, 21:48:40 GMT */ #ifndef HB_OT_SHAPER_ARABIC_JOINING_LIST_HH diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic-table.hh index 8c2504438d969..19bd72d421883 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic-table.hh @@ -6,10 +6,10 @@ * * on files with these headers: * - * # ArabicShaping-15.1.0.txt - * # Date: 2023-01-05 - * # Blocks-15.1.0.txt - * # Date: 2023-07-28, 15:47:20 GMT + * # ArabicShaping-16.0.0.txt + * # Date: 2024-07-30 + * # Blocks-16.0.0.txt + * # Date: 2024-02-02 * UnicodeData.txt does not have a header. */ @@ -136,7 +136,13 @@ static const uint8_t joining_table[] = /* 10D00 */ L,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, /* 10D20 */ D,D,R,D, -#define joining_offset_0x10f30u 1182 +#define joining_offset_0x10ec2u 1182 + + /* Arabic Extended-C */ + + /* 10EC0 */ R,D,D, + +#define joining_offset_0x10f30u 1185 /* Sogdian */ @@ -155,14 +161,14 @@ static const uint8_t joining_table[] = /* 10FA0 */ D,U,D,D,R,R,R,U,D,R,R,D,D,R,D,D, /* 10FC0 */ U,D,R,R,D,U,U,U,U,R,D,L, -#define joining_offset_0x110bdu 1338 +#define joining_offset_0x110bdu 1341 /* Kaithi */ /* 110A0 */ U,X,X, /* 110C0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,U, -#define joining_offset_0x1e900u 1355 +#define joining_offset_0x1e900u 1358 /* Adlam */ @@ -170,7 +176,7 @@ static const uint8_t joining_table[] = /* 1E920 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, /* 1E940 */ D,D,D,D,X,X,X,X,X,X,X,T, -}; /* Table items: 1431; occupancy: 57% */ +}; /* Table items: 1434; occupancy: 57% */ static unsigned int @@ -198,6 +204,7 @@ joining_type (hb_codepoint_t u) if (hb_in_range (u, 0x10AC0u, 0x10AEFu)) return joining_table[u - 0x10AC0u + joining_offset_0x10ac0u]; if (hb_in_range (u, 0x10B80u, 0x10BAFu)) return joining_table[u - 0x10B80u + joining_offset_0x10b80u]; if (hb_in_range (u, 0x10D00u, 0x10D23u)) return joining_table[u - 0x10D00u + joining_offset_0x10d00u]; + if (hb_in_range (u, 0x10EC2u, 0x10EC4u)) return joining_table[u - 0x10EC2u + joining_offset_0x10ec2u]; if (hb_in_range (u, 0x10F30u, 0x10FCBu)) return joining_table[u - 0x10F30u + joining_offset_0x10f30u]; break; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic.cc index 87d76e1a1013a..7379bb7f15be1 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic.cc @@ -233,10 +233,7 @@ collect_features_arabic (hb_ot_shape_planner_t *plan) map->enable_feature (HB_TAG('c','a','l','t'), F_MANUAL_ZWJ); /* https://github.com/harfbuzz/harfbuzz/issues/1573 */ if (!map->has_feature (HB_TAG('r','c','l','t'))) - { map->add_gsub_pause (nullptr); - map->enable_feature (HB_TAG('r','c','l','t'), F_MANUAL_ZWJ); - } map->enable_feature (HB_TAG('l','i','g','a'), F_MANUAL_ZWJ); map->enable_feature (HB_TAG('c','l','i','g'), F_MANUAL_ZWJ); @@ -263,7 +260,7 @@ struct arabic_shape_plan_t * mask_array[NONE] == 0. */ hb_mask_t mask_array[ARABIC_NUM_FEATURES + 1]; - hb_atomic_ptr_t fallback_plan; + mutable hb_atomic_t fallback_plan; unsigned int do_fallback : 1; unsigned int has_stch : 1; @@ -560,9 +557,9 @@ apply_stch (const hb_ot_shape_plan_t *plan HB_UNUSED, DEBUG_MSG (ARABIC, nullptr, "%s stretch at (%u,%u,%u)", step == MEASURE ? "measuring" : "cutting", context, start, end); - DEBUG_MSG (ARABIC, nullptr, "rest of word: count=%u width %d", start - context, w_total); - DEBUG_MSG (ARABIC, nullptr, "fixed tiles: count=%d width=%d", n_fixed, w_fixed); - DEBUG_MSG (ARABIC, nullptr, "repeating tiles: count=%d width=%d", n_repeating, w_repeating); + DEBUG_MSG (ARABIC, nullptr, "rest of word: count=%u width %" PRId32, start - context, w_total); + DEBUG_MSG (ARABIC, nullptr, "fixed tiles: count=%d width=%" PRId32, n_fixed, w_fixed); + DEBUG_MSG (ARABIC, nullptr, "repeating tiles: count=%d width=%" PRId32, n_repeating, w_repeating); /* Number of additional times to repeat each repeating tile. */ int n_copies = 0; @@ -602,7 +599,7 @@ apply_stch (const hb_ot_shape_plan_t *plan HB_UNUSED, if (info[k - 1].arabic_shaping_action() == STCH_REPEATING) repeat += n_copies; - DEBUG_MSG (ARABIC, nullptr, "appending %u copies of glyph %u; j=%u", + DEBUG_MSG (ARABIC, nullptr, "appending %u copies of glyph %" PRIu32 "; j=%u", repeat, info[k - 1].codepoint, j); pos[k - 1].x_advance = 0; for (unsigned int n = 0; n < repeat; n++) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-hangul.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-hangul.cc index 8b41e2219b13d..5c46ebbc281c9 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-hangul.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-hangul.cc @@ -298,8 +298,7 @@ preprocess_text_hangul (const hb_ot_shape_plan_t *plan HB_UNUSED, end = start + 2; if (unlikely (!buffer->successful)) break; - if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) - buffer->merge_out_clusters (start, end); + buffer->merge_out_clusters (start, end); continue; } } @@ -372,8 +371,7 @@ preprocess_text_hangul (const hb_ot_shape_plan_t *plan HB_UNUSED, if (i < end) info[i++].hangul_shaping_feature() = TJMO; - if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) - buffer->merge_out_clusters (start, end); + buffer->merge_out_clusters (start, end); continue; } else if ((!tindex && buffer->idx + 1 < count && isT (buffer->cur(+1).codepoint))) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-hebrew.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-hebrew.cc index bb9950d02d281..0ad1cea953252 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-hebrew.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-hebrew.cc @@ -78,7 +78,7 @@ compose_hebrew (const hb_ot_shape_normalize_context_t *c, return found; #endif - if (!found && !c->plan->has_gpos_mark) + if (!found && (c->plan && !c->plan->has_gpos_mark)) { /* Special-case Hebrew presentation forms that are excluded from * standard normalization, but wanted for old fonts. */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic-machine.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic-machine.hh index 6a6db44840eae..25e6d85ef8048 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic-machine.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic-machine.hh @@ -68,6 +68,7 @@ enum indic_syllable_type_t { #define indic_syllable_machine_ex_Ra 15u #define indic_syllable_machine_ex_Repha 14u #define indic_syllable_machine_ex_SM 8u +#define indic_syllable_machine_ex_SMPst 57u #define indic_syllable_machine_ex_Symbol 17u #define indic_syllable_machine_ex_V 2u #define indic_syllable_machine_ex_VD 9u @@ -76,251 +77,916 @@ enum indic_syllable_type_t { #define indic_syllable_machine_ex_ZWNJ 5u -#line 80 "hb-ot-shaper-indic-machine.hh" +#line 81 "hb-ot-shaper-indic-machine.hh" static const unsigned char _indic_syllable_machine_trans_keys[] = { - 8u, 8u, 4u, 13u, 5u, 13u, 5u, 13u, 13u, 13u, 4u, 13u, 4u, 13u, 4u, 13u, - 8u, 8u, 5u, 13u, 5u, 13u, 13u, 13u, 4u, 13u, 4u, 13u, 4u, 13u, 4u, 13u, - 8u, 8u, 5u, 13u, 5u, 13u, 13u, 13u, 4u, 13u, 4u, 13u, 4u, 13u, 8u, 8u, - 5u, 13u, 5u, 13u, 13u, 13u, 4u, 13u, 4u, 13u, 5u, 13u, 8u, 8u, 1u, 18u, - 3u, 16u, 3u, 16u, 4u, 16u, 1u, 15u, 5u, 9u, 5u, 9u, 9u, 9u, 5u, 9u, - 1u, 15u, 1u, 15u, 1u, 15u, 3u, 13u, 4u, 13u, 5u, 13u, 5u, 13u, 4u, 13u, - 5u, 9u, 3u, 9u, 5u, 9u, 3u, 16u, 3u, 16u, 3u, 16u, 3u, 16u, 4u, 16u, - 1u, 15u, 3u, 16u, 3u, 16u, 4u, 16u, 1u, 15u, 5u, 9u, 9u, 9u, 5u, 9u, - 1u, 15u, 1u, 15u, 3u, 13u, 4u, 13u, 5u, 13u, 5u, 13u, 4u, 13u, 5u, 9u, - 5u, 9u, 3u, 9u, 5u, 9u, 3u, 16u, 3u, 16u, 4u, 13u, 3u, 16u, 3u, 16u, - 4u, 16u, 1u, 15u, 3u, 16u, 1u, 15u, 5u, 9u, 9u, 9u, 5u, 9u, 1u, 15u, - 1u, 15u, 3u, 13u, 4u, 13u, 5u, 13u, 5u, 13u, 3u, 16u, 4u, 13u, 5u, 9u, - 5u, 9u, 3u, 9u, 5u, 9u, 3u, 16u, 4u, 13u, 4u, 13u, 3u, 16u, 3u, 16u, - 4u, 16u, 1u, 15u, 3u, 16u, 1u, 15u, 5u, 9u, 9u, 9u, 5u, 9u, 1u, 15u, - 1u, 15u, 3u, 13u, 4u, 13u, 5u, 13u, 5u, 13u, 3u, 16u, 4u, 13u, 5u, 9u, - 5u, 9u, 3u, 9u, 5u, 9u, 1u, 16u, 3u, 16u, 1u, 16u, 4u, 13u, 5u, 13u, - 5u, 13u, 9u, 9u, 5u, 9u, 1u, 15u, 3u, 9u, 5u, 9u, 5u, 9u, 9u, 9u, + 8u, 57u, 4u, 57u, 5u, 57u, 5u, 57u, 13u, 13u, 4u, 57u, 4u, 57u, 4u, 57u, + 8u, 57u, 5u, 57u, 5u, 57u, 13u, 13u, 4u, 57u, 4u, 57u, 4u, 57u, 4u, 57u, + 8u, 57u, 5u, 57u, 5u, 57u, 13u, 13u, 4u, 57u, 4u, 57u, 4u, 57u, 8u, 57u, + 5u, 57u, 5u, 57u, 13u, 13u, 4u, 57u, 4u, 57u, 5u, 57u, 8u, 57u, 1u, 57u, + 3u, 57u, 3u, 57u, 4u, 57u, 1u, 57u, 5u, 57u, 5u, 57u, 9u, 9u, 5u, 9u, + 1u, 57u, 1u, 57u, 1u, 57u, 3u, 57u, 4u, 57u, 5u, 57u, 5u, 57u, 4u, 57u, + 5u, 57u, 3u, 57u, 5u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 4u, 57u, + 1u, 57u, 3u, 57u, 3u, 57u, 4u, 57u, 1u, 57u, 5u, 57u, 9u, 9u, 5u, 9u, + 1u, 57u, 1u, 57u, 3u, 57u, 4u, 57u, 5u, 57u, 5u, 57u, 4u, 57u, 5u, 57u, + 5u, 57u, 3u, 57u, 5u, 57u, 3u, 57u, 3u, 57u, 4u, 57u, 3u, 57u, 3u, 57u, + 4u, 57u, 1u, 57u, 3u, 57u, 1u, 57u, 5u, 57u, 9u, 9u, 5u, 9u, 1u, 57u, + 1u, 57u, 3u, 57u, 4u, 57u, 5u, 57u, 5u, 57u, 3u, 57u, 4u, 57u, 5u, 57u, + 5u, 57u, 3u, 57u, 5u, 57u, 3u, 57u, 4u, 57u, 4u, 57u, 3u, 57u, 3u, 57u, + 4u, 57u, 1u, 57u, 3u, 57u, 1u, 57u, 5u, 57u, 9u, 9u, 5u, 9u, 1u, 57u, + 1u, 57u, 3u, 57u, 4u, 57u, 5u, 57u, 5u, 57u, 3u, 57u, 4u, 57u, 5u, 57u, + 5u, 57u, 3u, 57u, 5u, 57u, 1u, 57u, 3u, 57u, 1u, 57u, 4u, 57u, 5u, 57u, + 5u, 57u, 9u, 9u, 5u, 9u, 1u, 57u, 3u, 57u, 5u, 57u, 5u, 57u, 9u, 9u, 5u, 9u, 1u, 15u, 0 }; static const char _indic_syllable_machine_key_spans[] = { - 1, 10, 9, 9, 1, 10, 10, 10, - 1, 9, 9, 1, 10, 10, 10, 10, - 1, 9, 9, 1, 10, 10, 10, 1, - 9, 9, 1, 10, 10, 9, 1, 18, - 14, 14, 13, 15, 5, 5, 1, 5, - 15, 15, 15, 11, 10, 9, 9, 10, - 5, 7, 5, 14, 14, 14, 14, 13, - 15, 14, 14, 13, 15, 5, 1, 5, - 15, 15, 11, 10, 9, 9, 10, 5, - 5, 7, 5, 14, 14, 10, 14, 14, - 13, 15, 14, 15, 5, 1, 5, 15, - 15, 11, 10, 9, 9, 14, 10, 5, - 5, 7, 5, 14, 10, 10, 14, 14, - 13, 15, 14, 15, 5, 1, 5, 15, - 15, 11, 10, 9, 9, 14, 10, 5, - 5, 7, 5, 16, 14, 16, 10, 9, - 9, 1, 5, 15, 7, 5, 5, 1, + 50, 54, 53, 53, 1, 54, 54, 54, + 50, 53, 53, 1, 54, 54, 54, 54, + 50, 53, 53, 1, 54, 54, 54, 50, + 53, 53, 1, 54, 54, 53, 50, 57, + 55, 55, 54, 57, 53, 53, 1, 5, + 57, 57, 57, 55, 54, 53, 53, 54, + 53, 55, 53, 55, 55, 55, 55, 54, + 57, 55, 55, 54, 57, 53, 1, 5, + 57, 57, 55, 54, 53, 53, 54, 53, + 53, 55, 53, 55, 55, 54, 55, 55, + 54, 57, 55, 57, 53, 1, 5, 57, + 57, 55, 54, 53, 53, 55, 54, 53, + 53, 55, 53, 55, 54, 54, 55, 55, + 54, 57, 55, 57, 53, 1, 5, 57, + 57, 55, 54, 53, 53, 55, 54, 53, + 53, 55, 53, 57, 55, 57, 54, 53, + 53, 1, 5, 57, 55, 53, 53, 1, 5, 15 }; static const short _indic_syllable_machine_index_offsets[] = { - 0, 2, 13, 23, 33, 35, 46, 57, - 68, 70, 80, 90, 92, 103, 114, 125, - 136, 138, 148, 158, 160, 171, 182, 193, - 195, 205, 215, 217, 228, 239, 249, 251, - 270, 285, 300, 314, 330, 336, 342, 344, - 350, 366, 382, 398, 410, 421, 431, 441, - 452, 458, 466, 472, 487, 502, 517, 532, - 546, 562, 577, 592, 606, 622, 628, 630, - 636, 652, 668, 680, 691, 701, 711, 722, - 728, 734, 742, 748, 763, 778, 789, 804, - 819, 833, 849, 864, 880, 886, 888, 894, - 910, 926, 938, 949, 959, 969, 984, 995, - 1001, 1007, 1015, 1021, 1036, 1047, 1058, 1073, - 1088, 1102, 1118, 1133, 1149, 1155, 1157, 1163, - 1179, 1195, 1207, 1218, 1228, 1238, 1253, 1264, - 1270, 1276, 1284, 1290, 1307, 1322, 1339, 1350, - 1360, 1370, 1372, 1378, 1394, 1402, 1408, 1414, - 1416, 1422 + 0, 51, 106, 160, 214, 216, 271, 326, + 381, 432, 486, 540, 542, 597, 652, 707, + 762, 813, 867, 921, 923, 978, 1033, 1088, + 1139, 1193, 1247, 1249, 1304, 1359, 1413, 1464, + 1522, 1578, 1634, 1689, 1747, 1801, 1855, 1857, + 1863, 1921, 1979, 2037, 2093, 2148, 2202, 2256, + 2311, 2365, 2421, 2475, 2531, 2587, 2643, 2699, + 2754, 2812, 2868, 2924, 2979, 3037, 3091, 3093, + 3099, 3157, 3215, 3271, 3326, 3380, 3434, 3489, + 3543, 3597, 3653, 3707, 3763, 3819, 3874, 3930, + 3986, 4041, 4099, 4155, 4213, 4267, 4269, 4275, + 4333, 4391, 4447, 4502, 4556, 4610, 4666, 4721, + 4775, 4829, 4885, 4939, 4995, 5050, 5105, 5161, + 5217, 5272, 5330, 5386, 5444, 5498, 5500, 5506, + 5564, 5622, 5678, 5733, 5787, 5841, 5897, 5952, + 6006, 6060, 6116, 6170, 6228, 6284, 6342, 6397, + 6451, 6505, 6507, 6513, 6571, 6627, 6681, 6735, + 6737, 6743 }; static const unsigned char _indic_syllable_machine_indicies[] = { - 1, 0, 2, 3, 3, 4, 5, 0, - 0, 0, 0, 4, 0, 3, 3, 4, - 6, 0, 0, 0, 0, 4, 0, 3, - 3, 4, 5, 0, 0, 0, 0, 4, - 0, 4, 0, 7, 3, 3, 4, 5, - 0, 0, 0, 0, 4, 0, 2, 3, - 3, 4, 5, 0, 0, 0, 8, 4, - 0, 10, 11, 11, 12, 13, 9, 9, - 9, 9, 12, 9, 14, 9, 11, 11, - 12, 15, 9, 9, 9, 9, 12, 9, - 11, 11, 12, 13, 9, 9, 9, 9, - 12, 9, 12, 9, 16, 11, 11, 12, - 13, 9, 9, 9, 9, 12, 9, 10, - 11, 11, 12, 13, 9, 9, 9, 17, - 12, 9, 10, 11, 11, 12, 13, 9, - 9, 9, 18, 12, 9, 20, 21, 21, - 22, 23, 19, 19, 19, 24, 22, 19, - 25, 19, 21, 21, 22, 27, 26, 26, - 26, 26, 22, 26, 21, 21, 22, 23, - 19, 19, 19, 19, 22, 19, 22, 26, - 20, 21, 21, 22, 23, 19, 19, 19, - 19, 22, 19, 28, 21, 21, 22, 23, - 19, 19, 19, 19, 22, 19, 30, 31, - 31, 32, 33, 29, 29, 29, 34, 32, + 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 2, 3, 3, 4, 5, + 0, 0, 0, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 5, 0, 3, 3, 4, 6, 0, 0, + 0, 0, 4, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 6, 0, + 3, 3, 4, 5, 0, 0, 0, 0, + 4, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 0, 4, 0, + 7, 3, 3, 4, 5, 0, 0, 0, + 0, 4, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 5, 0, 2, + 3, 3, 4, 5, 0, 0, 0, 8, + 4, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 0, 10, 11, + 11, 12, 13, 9, 9, 9, 9, 12, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 13, 9, 14, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 14, 9, + 11, 11, 12, 15, 9, 9, 9, 9, + 12, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 15, 9, 11, 11, + 12, 13, 9, 9, 9, 9, 12, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 13, 9, 12, 9, 16, 11, + 11, 12, 13, 9, 9, 9, 9, 12, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 13, 9, 10, 11, 11, + 12, 13, 9, 9, 9, 17, 12, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 13, 9, 10, 11, 11, 12, + 13, 9, 9, 9, 18, 12, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 13, 9, 20, 21, 21, 22, 23, + 19, 19, 19, 24, 22, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 23, 19, 25, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 25, 19, 21, 21, 22, + 27, 26, 26, 26, 26, 22, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 27, 26, 21, 21, 22, 23, 19, + 19, 19, 19, 22, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 23, + 19, 22, 26, 20, 21, 21, 22, 23, + 19, 19, 19, 19, 22, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 23, 19, 28, 21, 21, 22, 23, 19, + 19, 19, 19, 22, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 23, + 19, 30, 31, 31, 32, 33, 29, 29, + 29, 34, 32, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 33, 29, + 35, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 35, 29, 31, 31, 32, 36, 29, - 29, 29, 29, 32, 29, 31, 31, 32, - 33, 29, 29, 29, 29, 32, 29, 32, + 29, 29, 29, 32, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 36, + 29, 31, 31, 32, 33, 29, 29, 29, + 29, 32, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 33, 29, 32, 29, 30, 31, 31, 32, 33, 29, 29, - 29, 29, 32, 29, 37, 31, 31, 32, - 33, 29, 29, 29, 29, 32, 29, 21, + 29, 29, 32, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 33, 29, + 37, 31, 31, 32, 33, 29, 29, 29, + 29, 32, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 33, 29, 21, 21, 22, 38, 0, 0, 0, 0, 22, - 0, 40, 39, 42, 43, 44, 45, 46, - 47, 22, 23, 48, 49, 49, 24, 22, - 50, 51, 52, 53, 54, 41, 56, 57, - 58, 59, 4, 5, 60, 55, 55, 8, - 4, 55, 55, 61, 55, 62, 57, 63, - 63, 4, 5, 60, 55, 55, 55, 4, - 55, 55, 61, 55, 57, 63, 63, 4, - 5, 60, 55, 55, 55, 4, 55, 55, - 61, 55, 42, 55, 55, 55, 64, 65, - 55, 1, 60, 55, 55, 55, 55, 55, - 42, 55, 66, 66, 55, 1, 60, 55, - 60, 55, 55, 67, 60, 55, 60, 55, - 60, 55, 55, 55, 60, 55, 42, 55, - 68, 55, 66, 66, 55, 1, 60, 55, - 55, 55, 55, 55, 42, 55, 42, 55, - 55, 55, 66, 66, 55, 1, 60, 55, - 55, 55, 55, 55, 42, 55, 42, 55, - 55, 55, 66, 65, 55, 1, 60, 55, - 55, 55, 55, 55, 42, 55, 69, 70, - 71, 71, 4, 5, 60, 55, 55, 55, - 4, 55, 70, 71, 71, 4, 5, 60, - 55, 55, 55, 4, 55, 71, 71, 4, - 5, 60, 55, 55, 55, 4, 55, 60, - 55, 55, 67, 60, 55, 55, 55, 4, - 55, 72, 73, 73, 4, 5, 60, 55, - 55, 55, 4, 55, 64, 74, 55, 1, - 60, 55, 64, 55, 66, 66, 55, 1, - 60, 55, 66, 74, 55, 1, 60, 55, - 56, 57, 63, 63, 4, 5, 60, 55, - 55, 55, 4, 55, 55, 61, 55, 56, - 57, 58, 63, 4, 5, 60, 55, 55, - 8, 4, 55, 55, 61, 55, 76, 77, - 78, 79, 12, 13, 80, 75, 75, 18, - 12, 75, 75, 81, 75, 82, 77, 83, - 79, 12, 13, 80, 75, 75, 75, 12, - 75, 75, 81, 75, 77, 83, 79, 12, - 13, 80, 75, 75, 75, 12, 75, 75, - 81, 75, 84, 75, 75, 75, 85, 86, - 75, 14, 80, 75, 75, 75, 75, 75, - 84, 75, 87, 77, 88, 89, 12, 13, - 80, 75, 75, 17, 12, 75, 75, 81, - 75, 90, 77, 83, 83, 12, 13, 80, - 75, 75, 75, 12, 75, 75, 81, 75, - 77, 83, 83, 12, 13, 80, 75, 75, - 75, 12, 75, 75, 81, 75, 84, 75, - 75, 75, 91, 86, 75, 14, 80, 75, - 75, 75, 75, 75, 84, 75, 80, 75, - 75, 92, 80, 75, 80, 75, 80, 75, - 75, 75, 80, 75, 84, 75, 93, 75, - 91, 91, 75, 14, 80, 75, 75, 75, - 75, 75, 84, 75, 84, 75, 75, 75, - 91, 91, 75, 14, 80, 75, 75, 75, - 75, 75, 84, 75, 94, 95, 96, 96, - 12, 13, 80, 75, 75, 75, 12, 75, - 95, 96, 96, 12, 13, 80, 75, 75, - 75, 12, 75, 96, 96, 12, 13, 80, - 75, 75, 75, 12, 75, 80, 75, 75, - 92, 80, 75, 75, 75, 12, 75, 97, - 98, 98, 12, 13, 80, 75, 75, 75, - 12, 75, 85, 99, 75, 14, 80, 75, - 91, 91, 75, 14, 80, 75, 85, 75, - 91, 91, 75, 14, 80, 75, 91, 99, - 75, 14, 80, 75, 87, 77, 83, 83, - 12, 13, 80, 75, 75, 75, 12, 75, - 75, 81, 75, 87, 77, 88, 83, 12, - 13, 80, 75, 75, 17, 12, 75, 75, - 81, 75, 10, 11, 11, 12, 13, 75, - 75, 75, 75, 12, 75, 76, 77, 83, - 79, 12, 13, 80, 75, 75, 75, 12, - 75, 75, 81, 75, 101, 45, 102, 102, - 22, 23, 48, 100, 100, 100, 22, 100, - 100, 52, 100, 45, 102, 102, 22, 23, - 48, 100, 100, 100, 22, 100, 100, 52, - 100, 103, 100, 100, 100, 104, 105, 100, - 25, 48, 100, 100, 100, 100, 100, 103, - 100, 44, 45, 106, 107, 22, 23, 48, - 100, 100, 24, 22, 100, 100, 52, 100, - 103, 100, 100, 100, 108, 105, 100, 25, - 48, 100, 100, 100, 100, 100, 103, 100, - 48, 100, 100, 109, 48, 100, 48, 100, - 48, 100, 100, 100, 48, 100, 103, 100, - 110, 100, 108, 108, 100, 25, 48, 100, - 100, 100, 100, 100, 103, 100, 103, 100, - 100, 100, 108, 108, 100, 25, 48, 100, - 100, 100, 100, 100, 103, 100, 111, 112, - 113, 113, 22, 23, 48, 100, 100, 100, - 22, 100, 112, 113, 113, 22, 23, 48, - 100, 100, 100, 22, 100, 113, 113, 22, - 23, 48, 100, 100, 100, 22, 100, 48, - 100, 100, 109, 48, 100, 100, 100, 22, - 100, 44, 45, 102, 102, 22, 23, 48, - 100, 100, 100, 22, 100, 100, 52, 100, - 114, 115, 115, 22, 23, 48, 100, 100, - 100, 22, 100, 104, 116, 100, 25, 48, - 100, 108, 108, 100, 25, 48, 100, 104, - 100, 108, 108, 100, 25, 48, 100, 108, - 116, 100, 25, 48, 100, 44, 45, 106, - 102, 22, 23, 48, 100, 100, 24, 22, - 100, 100, 52, 100, 20, 21, 21, 22, - 23, 117, 117, 117, 24, 22, 117, 20, - 21, 21, 22, 23, 117, 117, 117, 117, - 22, 117, 119, 120, 121, 122, 32, 33, - 123, 118, 118, 34, 32, 118, 118, 124, - 118, 125, 120, 122, 122, 32, 33, 123, - 118, 118, 118, 32, 118, 118, 124, 118, - 120, 122, 122, 32, 33, 123, 118, 118, - 118, 32, 118, 118, 124, 118, 126, 118, - 118, 118, 127, 128, 118, 35, 123, 118, - 118, 118, 118, 118, 126, 118, 119, 120, - 121, 49, 32, 33, 123, 118, 118, 34, - 32, 118, 118, 124, 118, 126, 118, 118, - 118, 129, 128, 118, 35, 123, 118, 118, - 118, 118, 118, 126, 118, 123, 118, 118, - 130, 123, 118, 123, 118, 123, 118, 118, - 118, 123, 118, 126, 118, 131, 118, 129, - 129, 118, 35, 123, 118, 118, 118, 118, - 118, 126, 118, 126, 118, 118, 118, 129, - 129, 118, 35, 123, 118, 118, 118, 118, - 118, 126, 118, 132, 133, 134, 134, 32, - 33, 123, 118, 118, 118, 32, 118, 133, - 134, 134, 32, 33, 123, 118, 118, 118, - 32, 118, 134, 134, 32, 33, 123, 118, - 118, 118, 32, 118, 123, 118, 118, 130, - 123, 118, 118, 118, 32, 118, 119, 120, - 122, 122, 32, 33, 123, 118, 118, 118, - 32, 118, 118, 124, 118, 135, 136, 136, - 32, 33, 123, 118, 118, 118, 32, 118, - 127, 137, 118, 35, 123, 118, 129, 129, - 118, 35, 123, 118, 127, 118, 129, 129, - 118, 35, 123, 118, 129, 137, 118, 35, - 123, 118, 42, 43, 44, 45, 106, 102, - 22, 23, 48, 49, 49, 24, 22, 100, - 42, 52, 100, 56, 138, 58, 59, 4, - 5, 60, 55, 55, 8, 4, 55, 55, - 61, 55, 42, 43, 44, 45, 139, 140, - 22, 141, 142, 55, 49, 24, 22, 55, - 42, 52, 55, 20, 143, 143, 22, 141, - 60, 55, 55, 24, 22, 55, 60, 55, - 55, 67, 60, 55, 55, 55, 22, 55, - 142, 55, 55, 144, 142, 55, 55, 55, - 22, 55, 142, 55, 142, 55, 55, 55, - 142, 55, 42, 55, 68, 20, 143, 143, - 22, 141, 60, 55, 55, 55, 22, 55, - 42, 55, 146, 145, 147, 147, 145, 40, - 148, 145, 147, 147, 145, 40, 148, 145, - 148, 145, 145, 149, 148, 145, 148, 145, - 148, 145, 145, 145, 148, 145, 42, 117, - 117, 117, 117, 117, 117, 117, 117, 49, - 117, 117, 117, 117, 42, 117, 0 + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 38, 0, 40, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 40, 39, + 42, 43, 44, 45, 46, 47, 22, 23, + 48, 49, 49, 24, 22, 50, 51, 52, + 53, 54, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 55, 41, 57, 58, 59, 60, 4, 5, + 61, 56, 56, 8, 4, 56, 56, 62, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 5, 56, 63, 58, 64, 64, 4, 5, + 61, 56, 56, 56, 4, 56, 56, 62, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 5, 56, 58, 64, 64, 4, 5, 61, + 56, 56, 56, 4, 56, 56, 62, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 5, + 56, 42, 56, 56, 56, 65, 66, 56, + 1, 61, 56, 56, 56, 56, 56, 42, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 1, 56, 67, 67, 56, 1, 61, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 1, + 56, 61, 56, 56, 68, 61, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 68, 56, 61, + 56, 61, 56, 56, 56, 61, 56, 42, + 56, 69, 56, 67, 67, 56, 1, 61, + 56, 56, 56, 56, 56, 42, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 1, + 56, 42, 56, 56, 56, 67, 67, 56, + 1, 61, 56, 56, 56, 56, 56, 42, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 1, 56, 42, 56, 56, 56, 67, + 66, 56, 1, 61, 56, 56, 56, 56, + 56, 42, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 1, 56, 70, 71, 72, + 72, 4, 5, 61, 56, 56, 56, 4, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 5, 56, 71, 72, 72, + 4, 5, 61, 56, 56, 56, 4, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 5, 56, 72, 72, 4, 5, + 61, 56, 56, 56, 4, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 5, 56, 61, 56, 56, 68, 61, 56, + 56, 56, 4, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 68, 56, + 73, 74, 74, 4, 5, 61, 56, 56, + 56, 4, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 5, 56, 65, + 75, 56, 1, 61, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 1, 56, 65, 56, 67, + 67, 56, 1, 61, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 1, 56, 67, 75, 56, + 1, 61, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 1, 56, 57, 58, 64, 64, 4, + 5, 61, 56, 56, 56, 4, 56, 56, + 62, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 5, 56, 57, 58, 59, 64, 4, + 5, 61, 56, 56, 8, 4, 56, 56, + 62, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 5, 56, 77, 78, 79, 80, 12, + 13, 81, 76, 76, 18, 12, 76, 76, + 82, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 13, 76, 83, 78, 84, 80, 12, + 13, 81, 76, 76, 76, 12, 76, 76, + 82, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 13, 76, 78, 84, 80, 12, 13, + 81, 76, 76, 76, 12, 76, 76, 82, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 13, 76, 85, 76, 76, 76, 86, 87, + 76, 14, 81, 76, 76, 76, 76, 76, + 85, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 14, 76, 88, 78, 89, 90, + 12, 13, 81, 76, 76, 17, 12, 76, + 76, 82, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 13, 76, 91, 78, 84, 84, + 12, 13, 81, 76, 76, 76, 12, 76, + 76, 82, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 13, 76, 78, 84, 84, 12, + 13, 81, 76, 76, 76, 12, 76, 76, + 82, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 13, 76, 85, 76, 76, 76, 92, + 87, 76, 14, 81, 76, 76, 76, 76, + 76, 85, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 14, 76, 81, 76, 76, + 93, 81, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 93, 76, 81, 76, 81, 76, 76, + 76, 81, 76, 85, 76, 94, 76, 92, + 92, 76, 14, 81, 76, 76, 76, 76, + 76, 85, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 14, 76, 85, 76, 76, + 76, 92, 92, 76, 14, 81, 76, 76, + 76, 76, 76, 85, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 14, 76, 95, + 96, 97, 97, 12, 13, 81, 76, 76, + 76, 12, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 13, 76, 96, + 97, 97, 12, 13, 81, 76, 76, 76, + 12, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 13, 76, 97, 97, + 12, 13, 81, 76, 76, 76, 12, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 13, 76, 81, 76, 76, 93, + 81, 76, 76, 76, 12, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 93, 76, 98, 99, 99, 12, 13, 81, + 76, 76, 76, 12, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 13, + 76, 86, 100, 76, 14, 81, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 14, 76, 92, + 92, 76, 14, 81, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 14, 76, 86, 76, 92, + 92, 76, 14, 81, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 14, 76, 92, 100, 76, + 14, 81, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 14, 76, 88, 78, 84, 84, 12, + 13, 81, 76, 76, 76, 12, 76, 76, + 82, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 13, 76, 88, 78, 89, 84, 12, + 13, 81, 76, 76, 17, 12, 76, 76, + 82, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 13, 76, 10, 11, 11, 12, 13, + 76, 76, 76, 76, 12, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 13, 76, 77, 78, 84, 80, 12, 13, + 81, 76, 76, 76, 12, 76, 76, 82, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 13, 76, 102, 45, 103, 103, 22, 23, + 48, 101, 101, 101, 22, 101, 101, 52, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 23, 101, 45, 103, 103, 22, 23, 48, + 101, 101, 101, 22, 101, 101, 52, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 23, + 101, 104, 101, 101, 101, 105, 106, 101, + 25, 48, 101, 101, 101, 101, 101, 104, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 25, 101, 44, 45, 107, 108, 22, + 23, 48, 101, 101, 24, 22, 101, 101, + 52, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 23, 101, 104, 101, 101, 101, 109, + 106, 101, 25, 48, 101, 101, 101, 101, + 101, 104, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 25, 101, 48, 101, 101, + 110, 48, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 110, 101, 48, 101, 48, 101, 101, + 101, 48, 101, 104, 101, 111, 101, 109, + 109, 101, 25, 48, 101, 101, 101, 101, + 101, 104, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 25, 101, 104, 101, 101, + 101, 109, 109, 101, 25, 48, 101, 101, + 101, 101, 101, 104, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 25, 101, 112, + 113, 114, 114, 22, 23, 48, 101, 101, + 101, 22, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 23, 101, 113, + 114, 114, 22, 23, 48, 101, 101, 101, + 22, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 23, 101, 114, 114, + 22, 23, 48, 101, 101, 101, 22, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 23, 101, 48, 26, 26, 110, + 48, 26, 26, 26, 22, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 110, 26, 44, 45, 103, 103, 22, 23, + 48, 101, 101, 101, 22, 101, 101, 52, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 23, 101, 115, 116, 116, 22, 23, 48, + 101, 101, 101, 22, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 23, + 101, 105, 117, 101, 25, 48, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 25, 101, 109, + 109, 101, 25, 48, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 25, 101, 105, 101, 109, + 109, 101, 25, 48, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 25, 101, 109, 117, 101, + 25, 48, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 25, 101, 44, 45, 107, 103, 22, + 23, 48, 101, 101, 24, 22, 101, 101, + 52, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 23, 101, 20, 21, 21, 22, 23, + 118, 118, 118, 24, 22, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, + 23, 118, 20, 21, 21, 22, 23, 118, + 118, 118, 118, 22, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 23, + 118, 120, 121, 122, 123, 32, 33, 124, + 119, 119, 34, 32, 119, 119, 125, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 33, + 119, 126, 121, 123, 123, 32, 33, 124, + 119, 119, 119, 32, 119, 119, 125, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 33, + 119, 121, 123, 123, 32, 33, 124, 119, + 119, 119, 32, 119, 119, 125, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 33, 119, + 127, 119, 119, 119, 128, 129, 119, 35, + 124, 119, 119, 119, 119, 119, 127, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 35, 119, 120, 121, 122, 49, 32, 33, + 124, 119, 119, 34, 32, 119, 119, 125, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 33, 119, 127, 119, 119, 119, 130, 129, + 119, 35, 124, 119, 119, 119, 119, 119, + 127, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 35, 119, 124, 119, 119, 131, + 124, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 131, 119, 124, 119, 124, 119, 119, 119, + 124, 119, 127, 119, 132, 119, 130, 130, + 119, 35, 124, 119, 119, 119, 119, 119, + 127, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 35, 119, 127, 119, 119, 119, + 130, 130, 119, 35, 124, 119, 119, 119, + 119, 119, 127, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 35, 119, 133, 134, + 135, 135, 32, 33, 124, 119, 119, 119, + 32, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 33, 119, 134, 135, + 135, 32, 33, 124, 119, 119, 119, 32, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 33, 119, 135, 135, 32, + 33, 124, 119, 119, 119, 32, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 33, 119, 124, 119, 119, 131, 124, + 119, 119, 119, 32, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 131, + 119, 120, 121, 123, 123, 32, 33, 124, + 119, 119, 119, 32, 119, 119, 125, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 33, + 119, 136, 137, 137, 32, 33, 124, 119, + 119, 119, 32, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 33, 119, + 128, 138, 119, 35, 124, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 35, 119, 130, 130, + 119, 35, 124, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 35, 119, 128, 119, 130, 130, + 119, 35, 124, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 35, 119, 130, 138, 119, 35, + 124, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 35, 119, 42, 43, 44, 45, 107, 103, + 22, 23, 48, 49, 49, 24, 22, 101, + 42, 52, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 23, 101, 57, 139, 59, 60, + 4, 5, 61, 56, 56, 8, 4, 56, + 56, 62, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 5, 56, 42, 43, 44, 45, + 140, 141, 22, 142, 143, 56, 49, 24, + 22, 56, 42, 52, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 142, 56, 20, 144, + 144, 22, 142, 61, 56, 56, 24, 22, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 142, 56, 61, 56, 56, + 68, 61, 56, 56, 56, 22, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 68, 56, 143, 56, 56, 145, 143, + 56, 56, 56, 22, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 145, + 56, 143, 56, 143, 56, 56, 56, 143, + 56, 42, 56, 69, 20, 144, 144, 22, + 142, 61, 56, 56, 56, 22, 56, 42, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 142, 56, 147, 146, 148, 148, 146, + 40, 149, 146, 146, 146, 146, 146, 146, + 146, 146, 146, 146, 146, 146, 146, 146, + 146, 146, 146, 146, 146, 146, 146, 146, + 146, 146, 146, 146, 146, 146, 146, 146, + 146, 146, 146, 146, 146, 146, 146, 146, + 146, 146, 146, 146, 146, 146, 146, 146, + 146, 40, 146, 148, 148, 146, 40, 149, + 146, 146, 146, 146, 146, 146, 146, 146, + 146, 146, 146, 146, 146, 146, 146, 146, + 146, 146, 146, 146, 146, 146, 146, 146, + 146, 146, 146, 146, 146, 146, 146, 146, + 146, 146, 146, 146, 146, 146, 146, 146, + 146, 146, 146, 146, 146, 146, 146, 40, + 146, 149, 146, 146, 150, 149, 146, 146, + 146, 146, 146, 146, 146, 146, 146, 146, + 146, 146, 146, 146, 146, 146, 146, 146, + 146, 146, 146, 146, 146, 146, 146, 146, + 146, 146, 146, 146, 146, 146, 146, 146, + 146, 146, 146, 146, 146, 146, 146, 146, + 146, 146, 146, 146, 146, 150, 146, 149, + 146, 149, 146, 146, 146, 149, 146, 42, + 118, 118, 118, 118, 118, 118, 118, 118, + 49, 118, 118, 118, 118, 42, 118, 0 }; static const unsigned char _indic_syllable_machine_trans_targs[] = { @@ -330,41 +996,41 @@ static const unsigned char _indic_syllable_machine_trans_targs[] = { 93, 84, 31, 19, 98, 31, 107, 24, 113, 116, 117, 108, 26, 122, 127, 31, 134, 31, 32, 53, 79, 81, 100, 101, - 85, 102, 123, 124, 94, 132, 137, 31, - 33, 35, 6, 52, 38, 47, 34, 1, - 36, 40, 0, 39, 41, 44, 45, 3, - 48, 5, 49, 31, 54, 56, 14, 77, - 62, 70, 55, 7, 57, 72, 64, 58, - 13, 76, 59, 8, 63, 65, 67, 68, - 10, 71, 12, 73, 31, 80, 20, 82, - 96, 87, 15, 99, 16, 86, 88, 90, - 91, 18, 95, 21, 97, 31, 31, 103, - 105, 22, 27, 109, 118, 104, 106, 120, - 111, 23, 110, 112, 114, 115, 25, 119, - 28, 121, 125, 126, 131, 128, 129, 29, - 130, 31, 133, 30, 135, 136 + 85, 102, 123, 124, 94, 132, 137, 92, + 31, 33, 35, 6, 52, 38, 47, 34, + 1, 36, 40, 0, 39, 41, 44, 45, + 3, 48, 5, 49, 31, 54, 56, 14, + 77, 62, 70, 55, 7, 57, 72, 64, + 58, 13, 76, 59, 8, 63, 65, 67, + 68, 10, 71, 12, 73, 31, 80, 20, + 82, 96, 87, 15, 99, 16, 86, 88, + 90, 91, 18, 95, 21, 97, 31, 31, + 103, 105, 22, 27, 109, 118, 104, 106, + 120, 111, 23, 110, 112, 114, 115, 25, + 119, 28, 121, 125, 126, 131, 128, 129, + 29, 130, 31, 133, 30, 135, 136 }; static const char _indic_syllable_machine_trans_actions[] = { 1, 0, 2, 0, 2, 0, 0, 2, 2, 3, 2, 0, 2, 0, 0, 0, - 2, 2, 2, 4, 2, 0, 5, 0, + 2, 2, 2, 4, 2, 0, 5, 5, 5, 0, 6, 0, 2, 7, 2, 0, 2, 0, 2, 0, 0, 2, 0, 8, 0, 11, 2, 2, 5, 0, 12, 12, 0, 2, 5, 2, 5, 2, 0, 13, - 2, 0, 0, 2, 0, 2, 2, 0, - 2, 2, 0, 0, 2, 2, 2, 0, - 0, 0, 2, 14, 2, 0, 0, 2, - 0, 2, 2, 0, 2, 2, 2, 2, + 14, 2, 0, 0, 2, 0, 2, 2, 0, 2, 2, 0, 0, 2, 2, 2, - 0, 0, 0, 2, 15, 5, 0, 5, - 2, 2, 0, 5, 0, 0, 2, 5, - 5, 0, 0, 0, 2, 16, 17, 2, - 0, 0, 0, 0, 2, 2, 2, 2, - 2, 0, 0, 2, 2, 2, 0, 0, - 0, 2, 0, 18, 18, 0, 0, 0, - 0, 19, 2, 0, 0, 0 + 0, 0, 0, 2, 15, 2, 0, 0, + 2, 0, 2, 2, 0, 2, 2, 2, + 2, 0, 2, 2, 0, 0, 2, 2, + 2, 0, 0, 0, 2, 16, 5, 0, + 5, 2, 2, 0, 5, 0, 0, 2, + 5, 5, 0, 0, 0, 2, 17, 18, + 2, 0, 0, 0, 0, 2, 2, 2, + 2, 2, 0, 0, 2, 2, 2, 0, + 0, 0, 2, 0, 19, 19, 0, 0, + 0, 0, 20, 2, 0, 0, 0 }; static const char _indic_syllable_machine_to_state_actions[] = { @@ -414,20 +1080,20 @@ static const short _indic_syllable_machine_eof_trans[] = { 10, 10, 10, 10, 10, 10, 10, 20, 20, 27, 20, 27, 20, 20, 30, 30, 30, 30, 30, 30, 30, 1, 40, 0, - 56, 56, 56, 56, 56, 56, 56, 56, - 56, 56, 56, 56, 56, 56, 56, 56, - 56, 56, 56, 56, 56, 76, 76, 76, - 76, 76, 76, 76, 76, 76, 76, 76, - 76, 76, 76, 76, 76, 76, 76, 76, - 76, 76, 76, 76, 76, 76, 76, 101, - 101, 101, 101, 101, 101, 101, 101, 101, - 101, 101, 101, 101, 101, 101, 101, 101, - 101, 101, 101, 101, 118, 118, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 101, 56, 56, 56, 56, - 56, 56, 56, 56, 146, 146, 146, 146, - 146, 118 + 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 102, + 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 27, 102, 102, 102, + 102, 102, 102, 102, 119, 119, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 102, 57, 57, 57, 57, + 57, 57, 57, 57, 147, 147, 147, 147, + 147, 119 }; static const int indic_syllable_machine_start = 31; @@ -441,7 +1107,7 @@ static const int indic_syllable_machine_en_main = 31; -#line 118 "hb-ot-shaper-indic-machine.rl" +#line 121 "hb-ot-shaper-indic-machine.rl" #define found_syllable(syllable_type) \ @@ -460,7 +1126,7 @@ find_syllables_indic (hb_buffer_t *buffer) int cs; hb_glyph_info_t *info = buffer->info; -#line 464 "hb-ot-shaper-indic-machine.hh" +#line 1130 "hb-ot-shaper-indic-machine.hh" { cs = indic_syllable_machine_start; ts = 0; @@ -468,7 +1134,7 @@ find_syllables_indic (hb_buffer_t *buffer) act = 0; } -#line 138 "hb-ot-shaper-indic-machine.rl" +#line 141 "hb-ot-shaper-indic-machine.rl" p = 0; @@ -476,7 +1142,7 @@ find_syllables_indic (hb_buffer_t *buffer) unsigned int syllable_serial = 1; -#line 480 "hb-ot-shaper-indic-machine.hh" +#line 1146 "hb-ot-shaper-indic-machine.hh" { int _slen; int _trans; @@ -490,7 +1156,7 @@ _resume: #line 1 "NONE" {ts = p;} break; -#line 494 "hb-ot-shaper-indic-machine.hh" +#line 1160 "hb-ot-shaper-indic-machine.hh" } _keys = _indic_syllable_machine_trans_keys + (cs<<1); @@ -513,51 +1179,51 @@ _eof_trans: {te = p+1;} break; case 11: -#line 114 "hb-ot-shaper-indic-machine.rl" +#line 117 "hb-ot-shaper-indic-machine.rl" {te = p+1;{ found_syllable (indic_non_indic_cluster); }} break; - case 13: -#line 109 "hb-ot-shaper-indic-machine.rl" + case 14: +#line 111 "hb-ot-shaper-indic-machine.rl" {te = p;p--;{ found_syllable (indic_consonant_syllable); }} break; - case 14: -#line 110 "hb-ot-shaper-indic-machine.rl" + case 15: +#line 112 "hb-ot-shaper-indic-machine.rl" {te = p;p--;{ found_syllable (indic_vowel_syllable); }} break; - case 17: -#line 111 "hb-ot-shaper-indic-machine.rl" + case 18: +#line 113 "hb-ot-shaper-indic-machine.rl" {te = p;p--;{ found_syllable (indic_standalone_cluster); }} break; - case 19: -#line 112 "hb-ot-shaper-indic-machine.rl" + case 20: +#line 114 "hb-ot-shaper-indic-machine.rl" {te = p;p--;{ found_syllable (indic_symbol_cluster); }} break; - case 15: -#line 113 "hb-ot-shaper-indic-machine.rl" + case 16: +#line 116 "hb-ot-shaper-indic-machine.rl" {te = p;p--;{ found_syllable (indic_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }} break; - case 16: -#line 114 "hb-ot-shaper-indic-machine.rl" + case 17: +#line 117 "hb-ot-shaper-indic-machine.rl" {te = p;p--;{ found_syllable (indic_non_indic_cluster); }} break; case 1: -#line 109 "hb-ot-shaper-indic-machine.rl" +#line 111 "hb-ot-shaper-indic-machine.rl" {{p = ((te))-1;}{ found_syllable (indic_consonant_syllable); }} break; case 3: -#line 110 "hb-ot-shaper-indic-machine.rl" +#line 112 "hb-ot-shaper-indic-machine.rl" {{p = ((te))-1;}{ found_syllable (indic_vowel_syllable); }} break; case 7: -#line 111 "hb-ot-shaper-indic-machine.rl" +#line 113 "hb-ot-shaper-indic-machine.rl" {{p = ((te))-1;}{ found_syllable (indic_standalone_cluster); }} break; case 8: -#line 112 "hb-ot-shaper-indic-machine.rl" +#line 114 "hb-ot-shaper-indic-machine.rl" {{p = ((te))-1;}{ found_syllable (indic_symbol_cluster); }} break; case 4: -#line 113 "hb-ot-shaper-indic-machine.rl" +#line 116 "hb-ot-shaper-indic-machine.rl" {{p = ((te))-1;}{ found_syllable (indic_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }} break; case 6: @@ -567,33 +1233,42 @@ _eof_trans: {{p = ((te))-1;} found_syllable (indic_consonant_syllable); } break; case 5: - {{p = ((te))-1;} found_syllable (indic_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; } + {{p = ((te))-1;} found_syllable (indic_non_indic_cluster); } break; case 6: + {{p = ((te))-1;} found_syllable (indic_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; } + break; + case 7: {{p = ((te))-1;} found_syllable (indic_non_indic_cluster); } break; } } break; - case 18: + case 19: #line 1 "NONE" {te = p+1;} -#line 109 "hb-ot-shaper-indic-machine.rl" +#line 111 "hb-ot-shaper-indic-machine.rl" {act = 1;} break; - case 5: + case 13: #line 1 "NONE" {te = p+1;} -#line 113 "hb-ot-shaper-indic-machine.rl" +#line 115 "hb-ot-shaper-indic-machine.rl" {act = 5;} break; - case 12: + case 5: #line 1 "NONE" {te = p+1;} -#line 114 "hb-ot-shaper-indic-machine.rl" +#line 116 "hb-ot-shaper-indic-machine.rl" {act = 6;} break; -#line 597 "hb-ot-shaper-indic-machine.hh" + case 12: +#line 1 "NONE" + {te = p+1;} +#line 117 "hb-ot-shaper-indic-machine.rl" + {act = 7;} + break; +#line 1272 "hb-ot-shaper-indic-machine.hh" } _again: @@ -602,7 +1277,7 @@ _again: #line 1 "NONE" {ts = 0;} break; -#line 606 "hb-ot-shaper-indic-machine.hh" +#line 1281 "hb-ot-shaper-indic-machine.hh" } if ( ++p != pe ) @@ -618,7 +1293,7 @@ _again: } -#line 146 "hb-ot-shaper-indic-machine.rl" +#line 149 "hb-ot-shaper-indic-machine.rl" } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic-table.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic-table.cc index d9899a633c189..b87c530853bf7 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic-table.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic-table.cc @@ -6,12 +6,12 @@ * * on files with these headers: * - * # IndicSyllabicCategory-15.1.0.txt - * # Date: 2023-01-05 - * # IndicPositionalCategory-15.1.0.txt - * # Date: 2023-01-05 - * # Blocks-15.1.0.txt - * # Date: 2023-07-28, 15:47:20 GMT + * # IndicSyllabicCategory-16.0.0.txt + * # Date: 2024-04-30, 21:48:21 GMT + * # IndicPositionalCategory-16.0.0.txt + * # Date: 2024-04-30, 21:48:21 GMT + * # Blocks-16.0.0.txt + * # Date: 2024-02-02 */ #include "hb.hh" @@ -48,6 +48,7 @@ #define OT_CM I_Cat(CM) #define OT_Symbol I_Cat(Symbol) #define OT_CS I_Cat(CS) +#define OT_SMPst I_Cat(SMPst) /* khmer */ #define OT_VAbv K_Cat(VAbv) #define OT_VBlw K_Cat(VBlw) @@ -89,12 +90,13 @@ static_assert (OT_VPst == M_Cat(VPst), ""); #define _OT_MW OT_MW /* 2 chars; MW */ #define _OT_MY OT_MY /* 3 chars; MY */ #define _OT_N OT_N /* 17 chars; N */ -#define _OT_GB OT_PLACEHOLDER /* 165 chars; PLACEHOLDER */ +#define _OT_GB OT_PLACEHOLDER /* 185 chars; PLACEHOLDER */ #define _OT_PT OT_PT /* 8 chars; PT */ #define _OT_R OT_Ra /* 14 chars; Ra */ #define _OT_Rf OT_Repha /* 1 chars; Repha */ #define _OT_Rt OT_Robatic /* 3 chars; Robatic */ -#define _OT_SM OT_SM /* 56 chars; SM */ +#define _OT_SM OT_SM /* 50 chars; SM */ +#define _OT_SP OT_SMPst /* 6 chars; SMPst */ #define _OT_S OT_Symbol /* 22 chars; Symbol */ #define _OT_V OT_V /* 172 chars; V */ #define _OT_VA OT_VAbv /* 18 chars; VAbv */ @@ -112,7 +114,7 @@ static_assert (OT_VPst == M_Cat(VPst), ""); #define _POS_A POS_AFTER_MAIN /* 3 chars; AFTER_MAIN */ #define _POS_AP POS_AFTER_POST /* 50 chars; AFTER_POST */ #define _POS_AS POS_AFTER_SUB /* 51 chars; AFTER_SUB */ -#define _POS_C POS_BASE_C /* 833 chars; BASE_C */ +#define _POS_C POS_BASE_C /* 853 chars; BASE_C */ #define _POS_BS POS_BEFORE_SUB /* 25 chars; BEFORE_SUB */ #define _POS_B POS_BELOW_C /* 13 chars; BELOW_C */ #define _POS_X POS_END /* 71 chars; END */ @@ -145,7 +147,7 @@ static const uint16_t indic_table[] = { /* Latin-1 Supplement */ - /* 00B0 */ _(X,X), _(X,X),_(SM,SM),_(SM,SM), _(X,X), _(X,X), _(X,X), _(X,X), + /* 00B0 */ _(X,X), _(X,X),_(SP,SM),_(SP,SM), _(X,X), _(X,X), _(X,X), _(X,X), /* 00B8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), /* 00C0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), /* 00C8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), @@ -398,9 +400,9 @@ static const uint16_t indic_table[] = { /* Superscripts and Subscripts */ - /* 2070 */ _(X,X), _(X,X), _(X,X), _(X,X),_(SM,SM), _(X,X), _(X,X), _(X,X), + /* 2070 */ _(X,X), _(X,X), _(X,X), _(X,X),_(SP,SM), _(X,X), _(X,X), _(X,X), /* 2078 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - /* 2080 */ _(X,X), _(X,X),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(X,X), _(X,X), + /* 2080 */ _(X,X), _(X,X),_(SP,SM),_(SP,SM),_(SP,SM), _(X,X), _(X,X), _(X,X), #define indic_offset_0x25f8u 1592 @@ -458,7 +460,16 @@ static const uint16_t indic_table[] = { /* 11338 */ _(X,X), _(X,X), _(X,X), _(N,X), _(N,X), _(X,X), _(X,X), _(X,X), -}; /* Table items: 1728; occupancy: 71% */ +#define indic_offset_0x116d0u 1728 + + + /* Myanmar Extended-C */ + + /* 116D0 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* 116D8 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* 116E0 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(X,X), _(X,X), _(X,X), _(X,X), + +}; /* Table items: 1752; occupancy: 71% */ uint16_t hb_indic_get_categories (hb_codepoint_t u) @@ -498,6 +509,7 @@ hb_indic_get_categories (hb_codepoint_t u) case 0x11u: if (hb_in_range (u, 0x11300u, 0x11307u)) return indic_table[u - 0x11300u + indic_offset_0x11300u]; if (hb_in_range (u, 0x11338u, 0x1133Fu)) return indic_table[u - 0x11338u + indic_offset_0x11338u]; + if (hb_in_range (u, 0x116D0u, 0x116E7u)) return indic_table[u - 0x116D0u + indic_offset_0x116d0u]; break; default: @@ -530,6 +542,7 @@ hb_indic_get_categories (hb_codepoint_t u) #undef _OT_Rf #undef _OT_Rt #undef _OT_SM +#undef _OT_SP #undef _OT_S #undef _OT_V #undef _OT_VA diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic.cc index 20007df8963b2..d78b5670f61fd 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic.cc @@ -301,7 +301,7 @@ struct indic_shape_plan_t #else static constexpr bool uniscribe_bug_compatible = false; #endif - mutable hb_atomic_int_t virama_glyph; + mutable hb_atomic_t virama_glyph; hb_indic_would_substitute_feature_t rphf; hb_indic_would_substitute_feature_t pref; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-myanmar-machine.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-myanmar-machine.hh index 3dcf61b938f37..64eb761b4ea95 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-myanmar-machine.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-myanmar-machine.hh @@ -68,6 +68,7 @@ enum myanmar_syllable_type_t { #define myanmar_syllable_machine_ex_PT 39u #define myanmar_syllable_machine_ex_Ra 15u #define myanmar_syllable_machine_ex_SM 8u +#define myanmar_syllable_machine_ex_SMPst 57u #define myanmar_syllable_machine_ex_VAbv 20u #define myanmar_syllable_machine_ex_VBlw 21u #define myanmar_syllable_machine_ex_VPre 22u @@ -77,35 +78,35 @@ enum myanmar_syllable_type_t { #define myanmar_syllable_machine_ex_ZWNJ 5u -#line 81 "hb-ot-shaper-myanmar-machine.hh" +#line 82 "hb-ot-shaper-myanmar-machine.hh" static const unsigned char _myanmar_syllable_machine_trans_keys[] = { - 1u, 41u, 3u, 41u, 5u, 39u, 5u, 8u, 3u, 41u, 3u, 39u, 3u, 39u, 5u, 39u, - 5u, 39u, 3u, 39u, 3u, 39u, 3u, 41u, 5u, 39u, 1u, 15u, 3u, 39u, 3u, 39u, - 3u, 40u, 3u, 39u, 3u, 41u, 3u, 41u, 3u, 39u, 3u, 41u, 3u, 41u, 3u, 41u, - 3u, 41u, 3u, 41u, 5u, 39u, 5u, 8u, 3u, 41u, 3u, 39u, 3u, 39u, 5u, 39u, - 5u, 39u, 3u, 39u, 3u, 39u, 3u, 41u, 5u, 39u, 1u, 15u, 3u, 41u, 3u, 39u, - 3u, 39u, 3u, 40u, 3u, 39u, 3u, 41u, 3u, 41u, 3u, 39u, 3u, 41u, 3u, 41u, - 3u, 41u, 3u, 41u, 3u, 41u, 3u, 41u, 3u, 41u, 1u, 41u, 1u, 15u, 0 + 1u, 57u, 3u, 57u, 5u, 57u, 5u, 57u, 3u, 57u, 5u, 57u, 3u, 57u, 3u, 57u, + 3u, 57u, 3u, 57u, 3u, 57u, 5u, 57u, 1u, 15u, 3u, 57u, 3u, 57u, 3u, 57u, + 3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, + 3u, 57u, 5u, 57u, 5u, 57u, 3u, 57u, 5u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, + 3u, 57u, 3u, 57u, 5u, 57u, 1u, 15u, 3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, + 3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, + 3u, 57u, 3u, 57u, 3u, 57u, 1u, 57u, 1u, 15u, 0 }; static const char _myanmar_syllable_machine_key_spans[] = { - 41, 39, 35, 4, 39, 37, 37, 35, - 35, 37, 37, 39, 35, 15, 37, 37, - 38, 37, 39, 39, 37, 39, 39, 39, - 39, 39, 35, 4, 39, 37, 37, 35, - 35, 37, 37, 39, 35, 15, 39, 37, - 37, 38, 37, 39, 39, 37, 39, 39, - 39, 39, 39, 39, 39, 41, 15 + 57, 55, 53, 53, 55, 53, 55, 55, + 55, 55, 55, 53, 15, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 53, 53, 55, 53, 55, 55, 55, + 55, 55, 53, 15, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 57, 15 }; static const short _myanmar_syllable_machine_index_offsets[] = { - 0, 42, 82, 118, 123, 163, 201, 239, - 275, 311, 349, 387, 427, 463, 479, 517, - 555, 594, 632, 672, 712, 750, 790, 830, - 870, 910, 950, 986, 991, 1031, 1069, 1107, - 1143, 1179, 1217, 1255, 1295, 1331, 1347, 1387, - 1425, 1463, 1502, 1540, 1580, 1620, 1658, 1698, - 1738, 1778, 1818, 1858, 1898, 1938, 1980 + 0, 58, 114, 168, 222, 278, 332, 388, + 444, 500, 556, 612, 666, 682, 738, 794, + 850, 906, 962, 1018, 1074, 1130, 1186, 1242, + 1298, 1354, 1408, 1462, 1518, 1572, 1628, 1684, + 1740, 1796, 1852, 1906, 1922, 1978, 2034, 2090, + 2146, 2202, 2258, 2314, 2370, 2426, 2482, 2538, + 2594, 2650, 2706, 2762, 2820 }; static const char _myanmar_syllable_machine_indicies[] = { @@ -114,273 +115,378 @@ static const char _myanmar_syllable_machine_indicies[] = { 0, 8, 0, 9, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 14, 15, 16, 17, 18, 19, - 20, 0, 22, 23, 24, 24, 21, 25, - 26, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 27, 28, 29, 30, 21, - 21, 21, 21, 21, 21, 21, 21, 31, - 21, 21, 32, 33, 34, 35, 36, 37, - 38, 21, 24, 24, 21, 25, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 30, 21, 21, 21, - 21, 21, 21, 21, 21, 39, 21, 21, - 21, 21, 21, 21, 36, 21, 24, 24, - 21, 25, 21, 22, 21, 24, 24, 21, - 25, 26, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 40, 21, 21, 30, - 21, 21, 21, 21, 21, 21, 21, 21, - 41, 21, 21, 42, 21, 21, 21, 36, - 21, 41, 21, 22, 21, 24, 24, 21, - 25, 26, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 30, - 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 36, - 21, 43, 21, 24, 24, 21, 25, 36, - 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 44, 21, - 21, 21, 21, 21, 21, 36, 21, 24, - 24, 21, 25, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 44, 21, 21, 21, 21, 21, - 21, 36, 21, 24, 24, 21, 25, 21, - 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 36, 21, 22, - 21, 24, 24, 21, 25, 26, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, - 40, 21, 21, 30, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 36, 21, 22, 21, 24, - 24, 21, 25, 26, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 40, 21, - 21, 30, 21, 21, 21, 21, 21, 21, - 21, 21, 41, 21, 21, 21, 21, 21, - 21, 36, 21, 22, 21, 24, 24, 21, - 25, 26, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 40, 21, 21, 30, - 21, 21, 21, 21, 21, 21, 21, 21, - 41, 21, 21, 21, 21, 21, 21, 36, - 21, 41, 21, 24, 24, 21, 25, 21, - 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 30, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 36, 21, 1, - 1, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 1, 21, 22, - 21, 24, 24, 21, 25, 26, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, - 27, 28, 21, 30, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 36, 21, 22, 21, 24, - 24, 21, 25, 26, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 28, - 21, 30, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, - 21, 36, 21, 22, 21, 24, 24, 21, - 25, 26, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 27, 28, 29, 30, - 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 36, - 45, 21, 22, 21, 24, 24, 21, 25, - 26, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 27, 28, 29, 30, 21, - 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 36, 21, - 22, 21, 24, 24, 21, 25, 26, 21, - 21, 21, 21, 21, 21, 21, 21, 21, - 21, 27, 28, 29, 30, 21, 21, 21, - 21, 21, 21, 21, 21, 31, 21, 21, - 32, 33, 34, 35, 36, 21, 38, 21, - 22, 21, 24, 24, 21, 25, 26, 21, - 21, 21, 21, 21, 21, 21, 21, 21, - 21, 27, 28, 29, 30, 21, 21, 21, - 21, 21, 21, 21, 21, 45, 21, 21, - 21, 21, 21, 21, 36, 21, 38, 21, - 22, 21, 24, 24, 21, 25, 26, 21, - 21, 21, 21, 21, 21, 21, 21, 21, - 21, 27, 28, 29, 30, 21, 21, 21, - 21, 21, 21, 21, 21, 45, 21, 21, - 21, 21, 21, 21, 36, 21, 22, 21, - 24, 24, 21, 25, 26, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 27, - 28, 29, 30, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 32, 21, - 34, 21, 36, 21, 38, 21, 22, 21, - 24, 24, 21, 25, 26, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 27, - 28, 29, 30, 21, 21, 21, 21, 21, - 21, 21, 21, 45, 21, 21, 32, 21, - 21, 21, 36, 21, 38, 21, 22, 21, - 24, 24, 21, 25, 26, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 27, - 28, 29, 30, 21, 21, 21, 21, 21, - 21, 21, 21, 46, 21, 21, 32, 33, - 34, 21, 36, 21, 38, 21, 22, 21, - 24, 24, 21, 25, 26, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 27, - 28, 29, 30, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 32, 33, - 34, 21, 36, 21, 38, 21, 22, 23, - 24, 24, 21, 25, 26, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 27, - 28, 29, 30, 21, 21, 21, 21, 21, - 21, 21, 21, 31, 21, 21, 32, 33, - 34, 35, 36, 21, 38, 21, 48, 48, + 20, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 21, 0, 23, 24, 25, 25, 22, 26, + 27, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 28, 29, 30, 31, 22, + 22, 22, 22, 22, 22, 22, 22, 32, + 22, 22, 33, 34, 35, 36, 37, 38, + 39, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 26, 22, 25, 25, 22, 26, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 31, 22, 22, 22, + 22, 22, 22, 22, 22, 40, 22, 22, + 22, 22, 22, 22, 37, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 26, 22, + 25, 25, 22, 26, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 37, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 26, 22, 41, 22, + 25, 25, 22, 26, 37, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 26, 22, 22, 22, 22, + 22, 22, 37, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 26, 22, 25, 25, + 22, 26, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 26, 22, 22, 22, 22, 22, 22, + 37, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 26, 22, 23, 22, 25, 25, + 22, 26, 27, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 42, 22, 22, + 31, 22, 22, 22, 22, 22, 22, 22, + 22, 43, 22, 22, 44, 22, 22, 22, + 37, 22, 43, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 26, 22, 23, 22, 25, 25, + 22, 26, 27, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 31, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 37, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 26, 22, 23, 22, 25, 25, + 22, 26, 27, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 42, 22, 22, + 31, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 37, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 26, 22, 23, 22, 25, 25, + 22, 26, 27, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 42, 22, 22, + 31, 22, 22, 22, 22, 22, 22, 22, + 22, 43, 22, 22, 22, 22, 22, 22, + 37, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 26, 22, 23, 22, 25, 25, + 22, 26, 27, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 42, 22, 22, + 31, 22, 22, 22, 22, 22, 22, 22, + 22, 43, 22, 22, 22, 22, 22, 22, + 37, 22, 43, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 26, 22, 25, 25, 22, 26, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 31, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 37, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 26, 22, 1, 1, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 1, 22, 23, 22, 25, 25, 22, 26, + 27, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 28, 29, 22, 31, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 37, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 26, 22, 23, 22, 25, 25, 22, 26, + 27, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 29, 22, 31, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 37, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 26, 22, 23, 22, 25, 25, 22, 26, + 27, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 28, 29, 30, 31, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 37, 45, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 26, 22, 23, 22, 25, 25, 22, 26, + 27, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 28, 29, 30, 31, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 37, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 26, 22, 23, 22, 25, 25, 22, 26, + 27, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 28, 29, 30, 31, 22, + 22, 22, 22, 22, 22, 22, 22, 32, + 22, 22, 33, 34, 35, 36, 37, 22, + 39, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 26, 22, 23, 22, 25, 25, 22, 26, + 27, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 28, 29, 30, 31, 22, + 22, 22, 22, 22, 22, 22, 22, 45, + 22, 22, 22, 22, 22, 22, 37, 22, + 39, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 26, 22, 23, 22, 25, 25, 22, 26, + 27, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 28, 29, 30, 31, 22, + 22, 22, 22, 22, 22, 22, 22, 45, + 22, 22, 22, 22, 22, 22, 37, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 26, 22, 23, 22, 25, 25, 22, 26, + 27, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 28, 29, 30, 31, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 33, 22, 35, 22, 37, 22, + 39, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 26, 22, 23, 22, 25, 25, 22, 26, + 27, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 28, 29, 30, 31, 22, + 22, 22, 22, 22, 22, 22, 22, 45, + 22, 22, 33, 22, 22, 22, 37, 22, + 39, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 26, 22, 23, 22, 25, 25, 22, 26, + 27, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 28, 29, 30, 31, 22, + 22, 22, 22, 22, 22, 22, 22, 46, + 22, 22, 33, 34, 35, 22, 37, 22, + 39, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 26, 22, 23, 22, 25, 25, 22, 26, + 27, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 28, 29, 30, 31, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 33, 34, 35, 22, 37, 22, + 39, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 26, 22, 23, 24, 25, 25, 22, 26, + 27, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 28, 29, 30, 31, 22, + 22, 22, 22, 22, 22, 22, 22, 32, + 22, 22, 33, 34, 35, 36, 37, 22, + 39, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 26, 22, 48, 48, 47, 5, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 12, 47, 47, 47, + 47, 47, 47, 47, 47, 49, 47, 47, + 47, 47, 47, 47, 18, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 5, 47, + 48, 48, 50, 5, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 18, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 5, 50, 51, 47, + 48, 48, 47, 5, 18, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 5, 47, 47, 47, 47, + 47, 47, 18, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 5, 47, 48, 48, 47, 5, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 5, 47, 47, 47, 47, 47, 47, + 18, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 5, 47, 2, 47, 48, 48, + 47, 5, 6, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 52, 47, 47, + 12, 47, 47, 47, 47, 47, 47, 47, + 47, 53, 47, 47, 54, 47, 47, 47, + 18, 47, 53, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 5, 47, 2, 47, 48, 48, + 47, 5, 6, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 12, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 18, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 5, 47, 2, 47, 48, 48, + 47, 5, 6, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 52, 47, 47, + 12, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 18, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 5, 47, 2, 47, 48, 48, + 47, 5, 6, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 52, 47, 47, + 12, 47, 47, 47, 47, 47, 47, 47, + 47, 53, 47, 47, 47, 47, 47, 47, + 18, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 5, 47, 2, 47, 48, 48, + 47, 5, 6, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 52, 47, 47, 12, 47, 47, 47, 47, 47, 47, 47, - 47, 49, 47, 47, 47, 47, 47, 47, - 18, 47, 48, 48, 47, 5, 47, 2, - 47, 48, 48, 47, 5, 6, 47, 47, + 47, 53, 47, 47, 47, 47, 47, 47, + 18, 47, 53, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 50, 47, 47, 12, 47, 47, 47, 47, - 47, 47, 47, 47, 51, 47, 47, 52, - 47, 47, 47, 18, 47, 51, 47, 2, - 47, 48, 48, 47, 5, 6, 47, 47, + 47, 47, 5, 47, 48, 48, 47, 5, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 12, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 12, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 18, 47, 53, 47, 48, - 48, 47, 5, 18, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 18, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 54, 47, 47, 47, 47, 47, - 47, 18, 47, 48, 48, 47, 5, 47, + 5, 47, 55, 55, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 55, 47, 2, 3, 48, 48, 47, 5, + 6, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 9, 10, 11, 12, 47, + 47, 47, 47, 47, 47, 47, 47, 13, + 47, 47, 14, 15, 16, 17, 18, 19, + 20, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 54, 47, - 47, 47, 47, 47, 47, 18, 47, 48, - 48, 47, 5, 47, 47, 47, 47, 47, + 5, 47, 2, 47, 48, 48, 47, 5, + 6, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 9, 10, 47, 12, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 18, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 18, 47, 2, 47, 48, 48, 47, - 5, 6, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 50, 47, 47, 12, + 5, 47, 2, 47, 48, 48, 47, 5, + 6, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 10, 47, 12, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 18, - 47, 2, 47, 48, 48, 47, 5, 6, + 47, 47, 47, 47, 47, 47, 18, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 50, 47, 47, 12, 47, 47, - 47, 47, 47, 47, 47, 47, 51, 47, - 47, 47, 47, 47, 47, 18, 47, 2, - 47, 48, 48, 47, 5, 6, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 50, 47, 47, 12, 47, 47, 47, 47, - 47, 47, 47, 47, 51, 47, 47, 47, - 47, 47, 47, 18, 47, 51, 47, 48, - 48, 47, 5, 47, 47, 47, 47, 47, + 5, 47, 2, 47, 48, 48, 47, 5, + 6, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 9, 10, 11, 12, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 12, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 18, 56, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 18, 47, 55, 55, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 55, 47, 2, 3, 48, 48, 47, - 5, 6, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 9, 10, 11, 12, + 5, 47, 2, 47, 48, 48, 47, 5, + 6, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 9, 10, 11, 12, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 13, 47, 47, 14, 15, 16, 17, 18, - 19, 20, 47, 2, 47, 48, 48, 47, - 5, 6, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 9, 10, 47, 12, + 47, 47, 47, 47, 47, 47, 18, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 18, - 47, 2, 47, 48, 48, 47, 5, 6, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 10, 47, 12, 47, 47, + 5, 47, 2, 47, 48, 48, 47, 5, + 6, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 9, 10, 11, 12, 47, + 47, 47, 47, 47, 47, 47, 47, 13, + 47, 47, 14, 15, 16, 17, 18, 47, + 20, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 18, 47, 2, - 47, 48, 48, 47, 5, 6, 47, 47, + 5, 47, 2, 47, 48, 48, 47, 5, + 6, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 9, 10, 11, 12, 47, + 47, 47, 47, 47, 47, 47, 47, 56, + 47, 47, 47, 47, 47, 47, 18, 47, + 20, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 9, 10, 11, 12, 47, 47, 47, 47, + 5, 47, 2, 47, 48, 48, 47, 5, + 6, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 9, 10, 11, 12, 47, + 47, 47, 47, 47, 47, 47, 47, 56, + 47, 47, 47, 47, 47, 47, 18, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 18, 56, 47, 2, 47, - 48, 48, 47, 5, 6, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 9, - 10, 11, 12, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 18, 47, 2, 47, 48, 48, - 47, 5, 6, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 9, 10, 11, - 12, 47, 47, 47, 47, 47, 47, 47, - 47, 13, 47, 47, 14, 15, 16, 17, - 18, 47, 20, 47, 2, 47, 48, 48, - 47, 5, 6, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 9, 10, 11, - 12, 47, 47, 47, 47, 47, 47, 47, - 47, 56, 47, 47, 47, 47, 47, 47, - 18, 47, 20, 47, 2, 47, 48, 48, - 47, 5, 6, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 9, 10, 11, - 12, 47, 47, 47, 47, 47, 47, 47, - 47, 56, 47, 47, 47, 47, 47, 47, - 18, 47, 2, 47, 48, 48, 47, 5, + 5, 47, 2, 47, 48, 48, 47, 5, 6, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 9, 10, 11, 12, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 14, 47, 16, 47, 18, 47, - 20, 47, 2, 47, 48, 48, 47, 5, + 20, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 5, 47, 2, 47, 48, 48, 47, 5, 6, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 9, 10, 11, 12, 47, 47, 47, 47, 47, 47, 47, 47, 56, 47, 47, 14, 47, 47, 47, 18, 47, - 20, 47, 2, 47, 48, 48, 47, 5, + 20, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 5, 47, 2, 47, 48, 48, 47, 5, 6, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 9, 10, 11, 12, 47, 47, 47, 47, 47, 47, 47, 47, 57, 47, 47, 14, 15, 16, 47, 18, 47, - 20, 47, 2, 47, 48, 48, 47, 5, + 20, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 5, 47, 2, 47, 48, 48, 47, 5, 6, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 9, 10, 11, 12, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 14, 15, 16, 47, 18, 47, - 20, 47, 2, 3, 48, 48, 47, 5, + 20, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 5, 47, 2, 3, 48, 48, 47, 5, 6, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 9, 10, 11, 12, 47, 47, 47, 47, 47, 47, 47, 47, 13, 47, 47, 14, 15, 16, 17, 18, 47, - 20, 47, 22, 23, 24, 24, 21, 25, - 26, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 27, 28, 29, 30, 21, - 21, 21, 21, 21, 21, 21, 21, 58, - 21, 21, 32, 33, 34, 35, 36, 37, - 38, 21, 22, 59, 24, 24, 21, 25, - 26, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 27, 28, 29, 30, 21, - 21, 21, 21, 21, 21, 21, 21, 31, - 21, 21, 32, 33, 34, 35, 36, 21, - 38, 21, 1, 1, 2, 3, 48, 48, + 20, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 5, 47, 23, 24, 25, 25, 22, 26, + 27, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 28, 29, 30, 31, 22, + 22, 22, 22, 22, 22, 22, 22, 58, + 22, 22, 33, 34, 35, 36, 37, 38, + 39, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 26, 22, 23, 59, 25, 25, 22, 26, + 27, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 28, 29, 30, 31, 22, + 22, 22, 22, 22, 22, 22, 22, 32, + 22, 22, 33, 34, 35, 36, 37, 22, + 39, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 26, 22, 1, 1, 2, 3, 48, 48, 47, 5, 6, 1, 1, 47, 47, 47, 1, 47, 47, 47, 47, 9, 10, 11, 12, 47, 47, 47, 47, 47, 47, 47, 47, 13, 47, 47, 14, 15, 16, 17, - 18, 19, 20, 47, 1, 1, 60, 60, + 18, 19, 20, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 5, 47, 1, 1, 60, 60, 60, 60, 60, 60, 60, 1, 1, 60, 60, 60, 1, 60, 0 }; static const char _myanmar_syllable_machine_trans_targs[] = { - 0, 1, 26, 37, 0, 27, 29, 51, - 54, 39, 40, 41, 28, 43, 44, 46, - 47, 48, 30, 50, 45, 0, 2, 13, - 0, 3, 5, 14, 15, 16, 4, 18, - 19, 21, 22, 23, 6, 25, 20, 12, - 9, 10, 11, 7, 8, 17, 24, 0, - 0, 36, 33, 34, 35, 31, 32, 38, - 42, 49, 52, 53, 0 + 0, 1, 25, 35, 0, 26, 30, 49, + 52, 37, 38, 39, 29, 41, 42, 44, + 45, 46, 27, 48, 43, 26, 0, 2, + 12, 0, 3, 7, 13, 14, 15, 6, + 17, 18, 20, 21, 22, 4, 24, 19, + 11, 5, 8, 9, 10, 16, 23, 0, + 0, 34, 0, 28, 31, 32, 33, 36, + 40, 47, 50, 51, 0 }; static const char _myanmar_syllable_machine_trans_actions[] = { - 3, 0, 0, 0, 4, 0, 0, 0, + 3, 0, 0, 0, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 5, 0, 0, - 6, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 6, 7, 0, + 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 7, - 8, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 9 + 0, 0, 0, 0, 0, 0, 0, 9, + 10, 0, 11, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 12 }; static const char _myanmar_syllable_machine_to_state_actions[] = { @@ -390,7 +496,7 @@ static const char _myanmar_syllable_machine_to_state_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0 }; static const char _myanmar_syllable_machine_from_state_actions[] = { @@ -400,17 +506,17 @@ static const char _myanmar_syllable_machine_from_state_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0 }; static const short _myanmar_syllable_machine_eof_trans[] = { - 0, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 48, 48, 48, 48, 48, 48, + 0, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, + 23, 48, 51, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 22, 22, 48, 61 + 48, 23, 23, 48, 61 }; static const int myanmar_syllable_machine_start = 0; @@ -424,7 +530,7 @@ static const int myanmar_syllable_machine_en_main = 0; -#line 117 "hb-ot-shaper-myanmar-machine.rl" +#line 118 "hb-ot-shaper-myanmar-machine.rl" #define found_syllable(syllable_type) \ @@ -443,7 +549,7 @@ find_syllables_myanmar (hb_buffer_t *buffer) int cs; hb_glyph_info_t *info = buffer->info; -#line 447 "hb-ot-shaper-myanmar-machine.hh" +#line 553 "hb-ot-shaper-myanmar-machine.hh" { cs = myanmar_syllable_machine_start; ts = 0; @@ -451,7 +557,7 @@ find_syllables_myanmar (hb_buffer_t *buffer) act = 0; } -#line 137 "hb-ot-shaper-myanmar-machine.rl" +#line 138 "hb-ot-shaper-myanmar-machine.rl" p = 0; @@ -459,7 +565,7 @@ find_syllables_myanmar (hb_buffer_t *buffer) unsigned int syllable_serial = 1; -#line 463 "hb-ot-shaper-myanmar-machine.hh" +#line 569 "hb-ot-shaper-myanmar-machine.hh" { int _slen; int _trans; @@ -473,7 +579,7 @@ _resume: #line 1 "NONE" {ts = p;} break; -#line 477 "hb-ot-shaper-myanmar-machine.hh" +#line 583 "hb-ot-shaper-myanmar-machine.hh" } _keys = _myanmar_syllable_machine_trans_keys + (cs<<1); @@ -491,35 +597,59 @@ _eof_trans: goto _again; switch ( _myanmar_syllable_machine_trans_actions[_trans] ) { - case 6: -#line 110 "hb-ot-shaper-myanmar-machine.rl" + case 8: +#line 111 "hb-ot-shaper-myanmar-machine.rl" {te = p+1;{ found_syllable (myanmar_consonant_syllable); }} break; case 4: -#line 111 "hb-ot-shaper-myanmar-machine.rl" +#line 112 "hb-ot-shaper-myanmar-machine.rl" {te = p+1;{ found_syllable (myanmar_non_myanmar_cluster); }} break; - case 8: -#line 112 "hb-ot-shaper-myanmar-machine.rl" + case 10: +#line 113 "hb-ot-shaper-myanmar-machine.rl" {te = p+1;{ found_syllable (myanmar_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }} break; case 3: -#line 113 "hb-ot-shaper-myanmar-machine.rl" +#line 114 "hb-ot-shaper-myanmar-machine.rl" {te = p+1;{ found_syllable (myanmar_non_myanmar_cluster); }} break; - case 5: -#line 110 "hb-ot-shaper-myanmar-machine.rl" - {te = p;p--;{ found_syllable (myanmar_consonant_syllable); }} - break; case 7: -#line 112 "hb-ot-shaper-myanmar-machine.rl" - {te = p;p--;{ found_syllable (myanmar_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }} +#line 111 "hb-ot-shaper-myanmar-machine.rl" + {te = p;p--;{ found_syllable (myanmar_consonant_syllable); }} break; case 9: #line 113 "hb-ot-shaper-myanmar-machine.rl" + {te = p;p--;{ found_syllable (myanmar_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }} + break; + case 12: +#line 114 "hb-ot-shaper-myanmar-machine.rl" {te = p;p--;{ found_syllable (myanmar_non_myanmar_cluster); }} break; -#line 523 "hb-ot-shaper-myanmar-machine.hh" + case 11: +#line 1 "NONE" + { switch( act ) { + case 2: + {{p = ((te))-1;} found_syllable (myanmar_non_myanmar_cluster); } + break; + case 3: + {{p = ((te))-1;} found_syllable (myanmar_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; } + break; + } + } + break; + case 6: +#line 1 "NONE" + {te = p+1;} +#line 112 "hb-ot-shaper-myanmar-machine.rl" + {act = 2;} + break; + case 5: +#line 1 "NONE" + {te = p+1;} +#line 113 "hb-ot-shaper-myanmar-machine.rl" + {act = 3;} + break; +#line 653 "hb-ot-shaper-myanmar-machine.hh" } _again: @@ -528,7 +658,7 @@ _again: #line 1 "NONE" {ts = 0;} break; -#line 532 "hb-ot-shaper-myanmar-machine.hh" +#line 662 "hb-ot-shaper-myanmar-machine.hh" } if ( ++p != pe ) @@ -544,7 +674,7 @@ _again: } -#line 145 "hb-ot-shaper-myanmar-machine.rl" +#line 146 "hb-ot-shaper-myanmar-machine.rl" } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-thai.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-thai.cc index 68642a5c1786b..a0ea464e7751a 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-thai.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-thai.cc @@ -360,7 +360,7 @@ preprocess_text_thai (const hb_ot_shape_plan_t *plan, { /* Since we decomposed, and NIKHAHIT is combining, merge clusters with the * previous cluster. */ - if (start && buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) + if (start) buffer->merge_out_clusters (start - 1, end); } } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-use-machine.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-use-machine.hh index 47b0e6bbdcc35..46f66f7d2858a 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-use-machine.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-use-machine.hh @@ -81,6 +81,7 @@ enum use_syllable_type_t { #define use_syllable_machine_ex_N 4u #define use_syllable_machine_ex_O 0u #define use_syllable_machine_ex_R 18u +#define use_syllable_machine_ex_RK 56u #define use_syllable_machine_ex_SB 51u #define use_syllable_machine_ex_SE 52u #define use_syllable_machine_ex_SMAbv 41u @@ -99,62 +100,62 @@ enum use_syllable_type_t { #define use_syllable_machine_ex_ZWNJ 14u -#line 103 "hb-ot-shaper-use-machine.hh" +#line 104 "hb-ot-shaper-use-machine.hh" static const unsigned char _use_syllable_machine_trans_keys[] = { - 49u, 51u, 0u, 53u, 11u, 53u, 11u, 53u, 1u, 53u, 14u, 48u, 14u, 47u, 14u, 47u, + 49u, 51u, 0u, 56u, 11u, 56u, 11u, 56u, 1u, 53u, 14u, 48u, 14u, 47u, 14u, 47u, 14u, 47u, 14u, 46u, 14u, 46u, 14u, 14u, 14u, 48u, 14u, 48u, 14u, 48u, 1u, 14u, 14u, 48u, 14u, 53u, 14u, 53u, 14u, 53u, 14u, 53u, 12u, 53u, 14u, 53u, 12u, 53u, - 12u, 53u, 12u, 53u, 11u, 53u, 1u, 14u, 1u, 48u, 14u, 42u, 14u, 42u, 11u, 53u, + 12u, 53u, 12u, 53u, 11u, 56u, 1u, 14u, 1u, 48u, 14u, 42u, 14u, 42u, 11u, 56u, 1u, 53u, 14u, 48u, 14u, 47u, 14u, 47u, 14u, 47u, 14u, 46u, 14u, 46u, 14u, 14u, 14u, 48u, 14u, 48u, 14u, 48u, 1u, 14u, 14u, 48u, 14u, 53u, 14u, 53u, 14u, 53u, - 14u, 53u, 12u, 53u, 14u, 53u, 12u, 53u, 12u, 53u, 12u, 53u, 11u, 53u, 1u, 14u, - 1u, 14u, 1u, 48u, 13u, 14u, 4u, 14u, 11u, 53u, 11u, 53u, 1u, 53u, 14u, 48u, - 14u, 47u, 14u, 47u, 14u, 47u, 14u, 46u, 14u, 46u, 14u, 14u, 14u, 48u, 14u, 48u, - 14u, 48u, 1u, 14u, 14u, 48u, 14u, 53u, 14u, 53u, 14u, 53u, 14u, 53u, 12u, 53u, - 14u, 53u, 12u, 53u, 12u, 53u, 12u, 53u, 11u, 53u, 1u, 14u, 1u, 14u, 1u, 48u, - 11u, 53u, 1u, 53u, 14u, 48u, 14u, 47u, 14u, 47u, 14u, 47u, 14u, 46u, 14u, 46u, - 14u, 14u, 14u, 48u, 14u, 48u, 14u, 48u, 1u, 14u, 14u, 48u, 14u, 53u, 14u, 53u, - 14u, 53u, 14u, 53u, 12u, 53u, 14u, 53u, 12u, 53u, 12u, 53u, 12u, 53u, 11u, 53u, - 1u, 14u, 1u, 48u, 4u, 14u, 13u, 14u, 1u, 53u, 14u, 42u, 14u, 42u, 1u, 5u, - 14u, 55u, 14u, 51u, 14u, 52u, 14u, 54u, 11u, 53u, 0 + 14u, 53u, 12u, 53u, 14u, 53u, 12u, 53u, 12u, 53u, 12u, 53u, 11u, 56u, 1u, 14u, + 1u, 14u, 1u, 48u, 14u, 14u, 13u, 14u, 4u, 14u, 11u, 56u, 11u, 56u, 1u, 53u, + 14u, 48u, 14u, 47u, 14u, 47u, 14u, 47u, 14u, 46u, 14u, 46u, 14u, 14u, 14u, 48u, + 14u, 48u, 14u, 48u, 1u, 14u, 14u, 48u, 14u, 53u, 14u, 53u, 14u, 53u, 14u, 53u, + 12u, 53u, 14u, 53u, 12u, 53u, 12u, 53u, 12u, 53u, 11u, 56u, 1u, 14u, 1u, 14u, + 1u, 48u, 14u, 14u, 11u, 56u, 1u, 53u, 14u, 48u, 14u, 47u, 14u, 47u, 14u, 47u, + 14u, 46u, 14u, 46u, 14u, 14u, 14u, 48u, 14u, 48u, 14u, 48u, 1u, 14u, 14u, 48u, + 14u, 53u, 14u, 53u, 14u, 53u, 14u, 53u, 12u, 53u, 14u, 53u, 12u, 53u, 12u, 53u, + 12u, 53u, 11u, 56u, 1u, 14u, 1u, 48u, 4u, 14u, 13u, 14u, 1u, 56u, 14u, 42u, + 14u, 42u, 1u, 5u, 14u, 55u, 14u, 51u, 14u, 52u, 14u, 54u, 11u, 56u, 0 }; static const char _use_syllable_machine_key_spans[] = { - 3, 54, 43, 43, 53, 35, 34, 34, + 3, 57, 46, 46, 53, 35, 34, 34, 34, 33, 33, 1, 35, 35, 35, 14, 35, 40, 40, 40, 40, 42, 40, 42, - 42, 42, 43, 14, 48, 29, 29, 43, + 42, 42, 46, 14, 48, 29, 29, 46, 53, 35, 34, 34, 34, 33, 33, 1, 35, 35, 35, 14, 35, 40, 40, 40, - 40, 42, 40, 42, 42, 42, 43, 14, - 14, 48, 2, 11, 43, 43, 53, 35, - 34, 34, 34, 33, 33, 1, 35, 35, - 35, 14, 35, 40, 40, 40, 40, 42, - 40, 42, 42, 42, 43, 14, 14, 48, - 43, 53, 35, 34, 34, 34, 33, 33, - 1, 35, 35, 35, 14, 35, 40, 40, - 40, 40, 42, 40, 42, 42, 42, 43, - 14, 48, 11, 2, 53, 29, 29, 5, - 42, 38, 39, 41, 43 + 40, 42, 40, 42, 42, 42, 46, 14, + 14, 48, 1, 2, 11, 46, 46, 53, + 35, 34, 34, 34, 33, 33, 1, 35, + 35, 35, 14, 35, 40, 40, 40, 40, + 42, 40, 42, 42, 42, 46, 14, 14, + 48, 1, 46, 53, 35, 34, 34, 34, + 33, 33, 1, 35, 35, 35, 14, 35, + 40, 40, 40, 40, 42, 40, 42, 42, + 42, 46, 14, 48, 11, 2, 56, 29, + 29, 5, 42, 38, 39, 41, 46 }; static const short _use_syllable_machine_index_offsets[] = { - 0, 4, 59, 103, 147, 201, 237, 272, - 307, 342, 376, 410, 412, 448, 484, 520, - 535, 571, 612, 653, 694, 735, 778, 819, - 862, 905, 948, 992, 1007, 1056, 1086, 1116, - 1160, 1214, 1250, 1285, 1320, 1355, 1389, 1423, - 1425, 1461, 1497, 1533, 1548, 1584, 1625, 1666, - 1707, 1748, 1791, 1832, 1875, 1918, 1961, 2005, - 2020, 2035, 2084, 2087, 2099, 2143, 2187, 2241, - 2277, 2312, 2347, 2382, 2416, 2450, 2452, 2488, - 2524, 2560, 2575, 2611, 2652, 2693, 2734, 2775, - 2818, 2859, 2902, 2945, 2988, 3032, 3047, 3062, - 3111, 3155, 3209, 3245, 3280, 3315, 3350, 3384, - 3418, 3420, 3456, 3492, 3528, 3543, 3579, 3620, - 3661, 3702, 3743, 3786, 3827, 3870, 3913, 3956, - 4000, 4015, 4064, 4076, 4079, 4133, 4163, 4193, - 4199, 4242, 4281, 4321, 4363 + 0, 4, 62, 109, 156, 210, 246, 281, + 316, 351, 385, 419, 421, 457, 493, 529, + 544, 580, 621, 662, 703, 744, 787, 828, + 871, 914, 957, 1004, 1019, 1068, 1098, 1128, + 1175, 1229, 1265, 1300, 1335, 1370, 1404, 1438, + 1440, 1476, 1512, 1548, 1563, 1599, 1640, 1681, + 1722, 1763, 1806, 1847, 1890, 1933, 1976, 2023, + 2038, 2053, 2102, 2104, 2107, 2119, 2166, 2213, + 2267, 2303, 2338, 2373, 2408, 2442, 2476, 2478, + 2514, 2550, 2586, 2601, 2637, 2678, 2719, 2760, + 2801, 2844, 2885, 2928, 2971, 3014, 3061, 3076, + 3091, 3140, 3142, 3189, 3243, 3279, 3314, 3349, + 3384, 3418, 3452, 3454, 3490, 3526, 3562, 3577, + 3613, 3654, 3695, 3736, 3777, 3820, 3861, 3904, + 3947, 3990, 4037, 4052, 4101, 4113, 4116, 4173, + 4203, 4233, 4239, 4282, 4321, 4361, 4403 }; static const unsigned char _use_syllable_machine_indicies[] = { @@ -165,571 +166,578 @@ static const unsigned char _use_syllable_machine_indicies[] = { 19, 20, 21, 8, 22, 23, 24, 25, 5, 26, 27, 28, 5, 29, 30, 31, 32, 33, 34, 35, 32, 1, 5, 36, - 5, 37, 5, 39, 40, 38, 41, 38, - 38, 38, 38, 38, 38, 38, 42, 43, - 44, 45, 46, 47, 48, 49, 50, 39, - 51, 52, 53, 54, 38, 55, 56, 57, - 38, 58, 59, 38, 60, 61, 62, 63, - 60, 38, 38, 38, 38, 64, 38, 39, - 40, 38, 41, 38, 38, 38, 38, 38, - 38, 38, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 39, 51, 52, 53, 54, - 38, 55, 56, 57, 38, 38, 38, 38, - 60, 61, 62, 63, 60, 38, 38, 38, - 38, 64, 38, 39, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, - 41, 38, 38, 38, 38, 38, 38, 38, - 38, 43, 44, 45, 46, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 55, - 56, 57, 38, 38, 38, 38, 38, 61, - 62, 63, 65, 38, 38, 38, 38, 43, - 38, 41, 38, 38, 38, 38, 38, 38, - 38, 38, 43, 44, 45, 46, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, - 55, 56, 57, 38, 38, 38, 38, 38, - 61, 62, 63, 65, 38, 41, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 44, - 45, 46, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 61, 62, 63, 38, - 41, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 45, 46, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 61, - 62, 63, 38, 41, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 46, - 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 61, 62, 63, 38, 41, 38, - 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 61, 62, 38, - 41, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, - 62, 38, 41, 38, 41, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 44, 45, - 46, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 55, 56, 57, 38, 38, - 38, 38, 38, 61, 62, 63, 65, 38, - 41, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 44, 45, 46, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, - 56, 57, 38, 38, 38, 38, 38, 61, - 62, 63, 65, 38, 41, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 44, 45, - 46, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 57, 38, 38, - 38, 38, 38, 61, 62, 63, 65, 38, - 66, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 41, 38, 41, - 38, 38, 38, 38, 38, 38, 38, 38, - 38, 44, 45, 46, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 61, 62, - 63, 65, 38, 41, 38, 38, 38, 38, - 38, 38, 38, 42, 43, 44, 45, 46, - 38, 38, 38, 38, 38, 38, 52, 53, - 54, 38, 55, 56, 57, 38, 38, 38, - 38, 38, 61, 62, 63, 65, 38, 38, - 38, 38, 43, 38, 41, 38, 38, 38, - 38, 38, 38, 38, 38, 43, 44, 45, - 46, 38, 38, 38, 38, 38, 38, 52, - 53, 54, 38, 55, 56, 57, 38, 38, - 38, 38, 38, 61, 62, 63, 65, 38, - 38, 38, 38, 43, 38, 41, 38, 38, - 38, 38, 38, 38, 38, 38, 43, 44, - 45, 46, 38, 38, 38, 38, 38, 38, - 38, 53, 54, 38, 55, 56, 57, 38, - 38, 38, 38, 38, 61, 62, 63, 65, - 38, 38, 38, 38, 43, 38, 41, 38, - 38, 38, 38, 38, 38, 38, 38, 43, - 44, 45, 46, 38, 38, 38, 38, 38, - 38, 38, 38, 54, 38, 55, 56, 57, - 38, 38, 38, 38, 38, 61, 62, 63, - 65, 38, 38, 38, 38, 43, 38, 67, - 38, 41, 38, 38, 38, 38, 38, 38, - 38, 42, 43, 44, 45, 46, 38, 48, - 49, 38, 38, 38, 52, 53, 54, 38, - 55, 56, 57, 38, 38, 38, 38, 38, - 61, 62, 63, 65, 38, 38, 38, 38, - 43, 38, 41, 38, 38, 38, 38, 38, - 38, 38, 38, 43, 44, 45, 46, 38, - 38, 38, 38, 38, 38, 38, 38, 38, - 38, 55, 56, 57, 38, 38, 38, 38, - 38, 61, 62, 63, 65, 38, 38, 38, - 38, 43, 38, 67, 38, 41, 38, 38, - 38, 38, 38, 38, 38, 42, 43, 44, - 45, 46, 38, 38, 49, 38, 38, 38, - 52, 53, 54, 38, 55, 56, 57, 38, - 38, 38, 38, 38, 61, 62, 63, 65, - 38, 38, 38, 38, 43, 38, 67, 38, - 41, 38, 38, 38, 38, 38, 38, 38, - 42, 43, 44, 45, 46, 38, 38, 38, - 38, 38, 38, 52, 53, 54, 38, 55, - 56, 57, 38, 38, 38, 38, 38, 61, - 62, 63, 65, 38, 38, 38, 38, 43, - 38, 67, 38, 41, 38, 38, 38, 38, - 38, 38, 38, 42, 43, 44, 45, 46, - 47, 48, 49, 38, 38, 38, 52, 53, - 54, 38, 55, 56, 57, 38, 38, 38, - 38, 38, 61, 62, 63, 65, 38, 38, - 38, 38, 43, 38, 39, 40, 38, 41, - 38, 38, 38, 38, 38, 38, 38, 42, + 5, 37, 5, 5, 38, 5, 40, 41, + 39, 42, 39, 39, 39, 39, 39, 39, + 39, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 40, 52, 53, 54, 55, 39, + 56, 57, 58, 39, 59, 60, 39, 61, + 62, 63, 64, 61, 39, 39, 39, 39, + 65, 39, 39, 64, 39, 40, 41, 39, + 42, 39, 39, 39, 39, 39, 39, 39, 43, 44, 45, 46, 47, 48, 49, 50, - 38, 51, 52, 53, 54, 38, 55, 56, - 57, 38, 38, 38, 38, 60, 61, 62, - 63, 60, 38, 38, 38, 38, 64, 38, - 39, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 41, 38, 39, - 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 41, 38, 38, 38, - 38, 38, 38, 38, 38, 43, 44, 45, - 46, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 55, 56, 57, 38, 38, - 38, 38, 38, 61, 62, 63, 65, 38, - 41, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 58, 59, 38, 41, 38, - 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 59, 38, 4, 69, 68, 70, - 68, 68, 68, 68, 68, 68, 68, 71, - 72, 73, 74, 75, 76, 77, 78, 79, - 4, 80, 81, 82, 83, 68, 84, 85, - 86, 68, 68, 68, 68, 87, 88, 89, - 90, 91, 68, 68, 68, 68, 92, 68, - 4, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 70, 68, 68, - 68, 68, 68, 68, 68, 68, 72, 73, - 74, 75, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 84, 85, 86, 68, - 68, 68, 68, 68, 88, 89, 90, 93, - 68, 68, 68, 68, 72, 68, 70, 68, - 68, 68, 68, 68, 68, 68, 68, 72, - 73, 74, 75, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 84, 85, 86, - 68, 68, 68, 68, 68, 88, 89, 90, - 93, 68, 70, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 73, 74, 75, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 88, 89, 90, 68, 70, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 74, 75, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 88, 89, 90, 68, - 70, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 75, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 88, - 89, 90, 68, 70, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 88, 89, 68, 70, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 89, 68, 70, - 68, 70, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 73, 74, 75, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 84, 85, 86, 68, 68, 68, 68, 68, - 88, 89, 90, 93, 68, 70, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 73, - 74, 75, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 85, 86, 68, - 68, 68, 68, 68, 88, 89, 90, 93, - 68, 70, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 73, 74, 75, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 86, 68, 68, 68, 68, 68, - 88, 89, 90, 93, 68, 95, 94, 94, - 94, 94, 94, 94, 94, 94, 94, 94, - 94, 94, 96, 94, 70, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 73, 74, - 75, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 88, 89, 90, 93, 68, - 70, 68, 68, 68, 68, 68, 68, 68, - 71, 72, 73, 74, 75, 68, 68, 68, - 68, 68, 68, 81, 82, 83, 68, 84, - 85, 86, 68, 68, 68, 68, 68, 88, - 89, 90, 93, 68, 68, 68, 68, 72, - 68, 70, 68, 68, 68, 68, 68, 68, - 68, 68, 72, 73, 74, 75, 68, 68, - 68, 68, 68, 68, 81, 82, 83, 68, - 84, 85, 86, 68, 68, 68, 68, 68, - 88, 89, 90, 93, 68, 68, 68, 68, - 72, 68, 70, 68, 68, 68, 68, 68, - 68, 68, 68, 72, 73, 74, 75, 68, - 68, 68, 68, 68, 68, 68, 82, 83, - 68, 84, 85, 86, 68, 68, 68, 68, - 68, 88, 89, 90, 93, 68, 68, 68, - 68, 72, 68, 70, 68, 68, 68, 68, - 68, 68, 68, 68, 72, 73, 74, 75, - 68, 68, 68, 68, 68, 68, 68, 68, - 83, 68, 84, 85, 86, 68, 68, 68, - 68, 68, 88, 89, 90, 93, 68, 68, - 68, 68, 72, 68, 97, 68, 70, 68, - 68, 68, 68, 68, 68, 68, 71, 72, - 73, 74, 75, 68, 77, 78, 68, 68, - 68, 81, 82, 83, 68, 84, 85, 86, - 68, 68, 68, 68, 68, 88, 89, 90, - 93, 68, 68, 68, 68, 72, 68, 70, - 68, 68, 68, 68, 68, 68, 68, 68, - 72, 73, 74, 75, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 84, 85, - 86, 68, 68, 68, 68, 68, 88, 89, - 90, 93, 68, 68, 68, 68, 72, 68, - 97, 68, 70, 68, 68, 68, 68, 68, - 68, 68, 71, 72, 73, 74, 75, 68, - 68, 78, 68, 68, 68, 81, 82, 83, - 68, 84, 85, 86, 68, 68, 68, 68, - 68, 88, 89, 90, 93, 68, 68, 68, - 68, 72, 68, 97, 68, 70, 68, 68, - 68, 68, 68, 68, 68, 71, 72, 73, - 74, 75, 68, 68, 68, 68, 68, 68, - 81, 82, 83, 68, 84, 85, 86, 68, - 68, 68, 68, 68, 88, 89, 90, 93, - 68, 68, 68, 68, 72, 68, 97, 68, - 70, 68, 68, 68, 68, 68, 68, 68, - 71, 72, 73, 74, 75, 76, 77, 78, - 68, 68, 68, 81, 82, 83, 68, 84, - 85, 86, 68, 68, 68, 68, 68, 88, - 89, 90, 93, 68, 68, 68, 68, 72, - 68, 4, 69, 68, 70, 68, 68, 68, - 68, 68, 68, 68, 71, 72, 73, 74, - 75, 76, 77, 78, 79, 68, 80, 81, - 82, 83, 68, 84, 85, 86, 68, 68, - 68, 68, 87, 88, 89, 90, 91, 68, - 68, 68, 68, 92, 68, 4, 98, 98, - 98, 98, 98, 98, 98, 98, 98, 98, - 98, 98, 99, 98, 4, 94, 94, 94, - 94, 94, 94, 94, 94, 94, 94, 94, - 94, 96, 94, 4, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 70, 68, 68, 68, 68, 68, 68, 68, - 68, 72, 73, 74, 75, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 84, - 85, 86, 68, 68, 68, 68, 68, 88, - 89, 90, 93, 68, 101, 102, 100, 6, - 103, 103, 103, 103, 103, 103, 103, 103, - 103, 104, 103, 105, 106, 68, 70, 68, - 68, 68, 68, 68, 68, 68, 107, 108, - 109, 110, 111, 112, 113, 114, 115, 105, - 116, 117, 118, 119, 68, 120, 121, 122, - 68, 58, 59, 68, 123, 124, 125, 126, - 127, 68, 68, 68, 68, 128, 68, 105, - 106, 68, 70, 68, 68, 68, 68, 68, - 68, 68, 107, 108, 109, 110, 111, 112, - 113, 114, 115, 105, 116, 117, 118, 119, - 68, 120, 121, 122, 68, 68, 68, 68, - 123, 124, 125, 126, 127, 68, 68, 68, - 68, 128, 68, 105, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 70, 68, 68, 68, 68, 68, 68, 68, - 68, 108, 109, 110, 111, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 120, - 121, 122, 68, 68, 68, 68, 68, 124, - 125, 126, 129, 68, 68, 68, 68, 108, - 68, 70, 68, 68, 68, 68, 68, 68, - 68, 68, 108, 109, 110, 111, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 120, 121, 122, 68, 68, 68, 68, 68, - 124, 125, 126, 129, 68, 70, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 109, - 110, 111, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 124, 125, 126, 68, - 70, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 110, 111, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 124, - 125, 126, 68, 70, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 111, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 124, 125, 126, 68, 70, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 124, 125, 68, - 70, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 125, 68, 70, 68, 70, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 109, 110, - 111, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 120, 121, 122, 68, 68, - 68, 68, 68, 124, 125, 126, 129, 68, - 70, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 109, 110, 111, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 121, 122, 68, 68, 68, 68, 68, 124, - 125, 126, 129, 68, 70, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 109, 110, - 111, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 122, 68, 68, - 68, 68, 68, 124, 125, 126, 129, 68, - 130, 94, 94, 94, 94, 94, 94, 94, - 94, 94, 94, 94, 94, 96, 94, 70, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 109, 110, 111, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 124, 125, - 126, 129, 68, 70, 68, 68, 68, 68, - 68, 68, 68, 107, 108, 109, 110, 111, - 68, 68, 68, 68, 68, 68, 117, 118, - 119, 68, 120, 121, 122, 68, 68, 68, - 68, 68, 124, 125, 126, 129, 68, 68, - 68, 68, 108, 68, 70, 68, 68, 68, - 68, 68, 68, 68, 68, 108, 109, 110, - 111, 68, 68, 68, 68, 68, 68, 117, - 118, 119, 68, 120, 121, 122, 68, 68, - 68, 68, 68, 124, 125, 126, 129, 68, - 68, 68, 68, 108, 68, 70, 68, 68, - 68, 68, 68, 68, 68, 68, 108, 109, - 110, 111, 68, 68, 68, 68, 68, 68, - 68, 118, 119, 68, 120, 121, 122, 68, - 68, 68, 68, 68, 124, 125, 126, 129, - 68, 68, 68, 68, 108, 68, 70, 68, - 68, 68, 68, 68, 68, 68, 68, 108, - 109, 110, 111, 68, 68, 68, 68, 68, - 68, 68, 68, 119, 68, 120, 121, 122, - 68, 68, 68, 68, 68, 124, 125, 126, - 129, 68, 68, 68, 68, 108, 68, 131, - 68, 70, 68, 68, 68, 68, 68, 68, - 68, 107, 108, 109, 110, 111, 68, 113, - 114, 68, 68, 68, 117, 118, 119, 68, - 120, 121, 122, 68, 68, 68, 68, 68, - 124, 125, 126, 129, 68, 68, 68, 68, - 108, 68, 70, 68, 68, 68, 68, 68, - 68, 68, 68, 108, 109, 110, 111, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 120, 121, 122, 68, 68, 68, 68, - 68, 124, 125, 126, 129, 68, 68, 68, - 68, 108, 68, 131, 68, 70, 68, 68, - 68, 68, 68, 68, 68, 107, 108, 109, - 110, 111, 68, 68, 114, 68, 68, 68, - 117, 118, 119, 68, 120, 121, 122, 68, - 68, 68, 68, 68, 124, 125, 126, 129, - 68, 68, 68, 68, 108, 68, 131, 68, - 70, 68, 68, 68, 68, 68, 68, 68, - 107, 108, 109, 110, 111, 68, 68, 68, - 68, 68, 68, 117, 118, 119, 68, 120, - 121, 122, 68, 68, 68, 68, 68, 124, - 125, 126, 129, 68, 68, 68, 68, 108, - 68, 131, 68, 70, 68, 68, 68, 68, - 68, 68, 68, 107, 108, 109, 110, 111, - 112, 113, 114, 68, 68, 68, 117, 118, - 119, 68, 120, 121, 122, 68, 68, 68, - 68, 68, 124, 125, 126, 129, 68, 68, - 68, 68, 108, 68, 105, 106, 68, 70, - 68, 68, 68, 68, 68, 68, 68, 107, - 108, 109, 110, 111, 112, 113, 114, 115, - 68, 116, 117, 118, 119, 68, 120, 121, - 122, 68, 68, 68, 68, 123, 124, 125, - 126, 127, 68, 68, 68, 68, 128, 68, - 105, 98, 98, 98, 98, 98, 98, 98, - 98, 98, 98, 98, 98, 99, 98, 105, - 94, 94, 94, 94, 94, 94, 94, 94, - 94, 94, 94, 94, 96, 94, 105, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 70, 68, 68, 68, 68, - 68, 68, 68, 68, 108, 109, 110, 111, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 120, 121, 122, 68, 68, 68, - 68, 68, 124, 125, 126, 129, 68, 8, - 9, 132, 11, 132, 132, 132, 132, 132, - 132, 132, 13, 14, 15, 16, 17, 18, - 19, 20, 21, 8, 22, 23, 24, 25, - 132, 26, 27, 28, 132, 132, 132, 132, - 32, 33, 34, 35, 32, 132, 132, 132, - 132, 37, 132, 8, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, - 11, 132, 132, 132, 132, 132, 132, 132, - 132, 14, 15, 16, 17, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 26, - 27, 28, 132, 132, 132, 132, 132, 33, - 34, 35, 133, 132, 132, 132, 132, 14, - 132, 11, 132, 132, 132, 132, 132, 132, - 132, 132, 14, 15, 16, 17, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, - 26, 27, 28, 132, 132, 132, 132, 132, - 33, 34, 35, 133, 132, 11, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 15, - 16, 17, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 33, 34, 35, 132, - 11, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 16, 17, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 33, - 34, 35, 132, 11, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 17, - 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 33, 34, 35, 132, 11, 132, - 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 33, 34, 132, - 11, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, - 34, 132, 11, 132, 11, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 15, 16, - 17, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 26, 27, 28, 132, 132, - 132, 132, 132, 33, 34, 35, 133, 132, - 11, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 15, 16, 17, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, - 27, 28, 132, 132, 132, 132, 132, 33, - 34, 35, 133, 132, 11, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 15, 16, - 17, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 28, 132, 132, - 132, 132, 132, 33, 34, 35, 133, 132, - 134, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 11, 132, 11, - 132, 132, 132, 132, 132, 132, 132, 132, - 132, 15, 16, 17, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 33, 34, - 35, 133, 132, 11, 132, 132, 132, 132, - 132, 132, 132, 13, 14, 15, 16, 17, - 132, 132, 132, 132, 132, 132, 23, 24, - 25, 132, 26, 27, 28, 132, 132, 132, - 132, 132, 33, 34, 35, 133, 132, 132, - 132, 132, 14, 132, 11, 132, 132, 132, - 132, 132, 132, 132, 132, 14, 15, 16, - 17, 132, 132, 132, 132, 132, 132, 23, - 24, 25, 132, 26, 27, 28, 132, 132, - 132, 132, 132, 33, 34, 35, 133, 132, - 132, 132, 132, 14, 132, 11, 132, 132, - 132, 132, 132, 132, 132, 132, 14, 15, - 16, 17, 132, 132, 132, 132, 132, 132, - 132, 24, 25, 132, 26, 27, 28, 132, - 132, 132, 132, 132, 33, 34, 35, 133, - 132, 132, 132, 132, 14, 132, 11, 132, - 132, 132, 132, 132, 132, 132, 132, 14, - 15, 16, 17, 132, 132, 132, 132, 132, - 132, 132, 132, 25, 132, 26, 27, 28, - 132, 132, 132, 132, 132, 33, 34, 35, - 133, 132, 132, 132, 132, 14, 132, 135, - 132, 11, 132, 132, 132, 132, 132, 132, - 132, 13, 14, 15, 16, 17, 132, 19, - 20, 132, 132, 132, 23, 24, 25, 132, - 26, 27, 28, 132, 132, 132, 132, 132, - 33, 34, 35, 133, 132, 132, 132, 132, - 14, 132, 11, 132, 132, 132, 132, 132, - 132, 132, 132, 14, 15, 16, 17, 132, - 132, 132, 132, 132, 132, 132, 132, 132, - 132, 26, 27, 28, 132, 132, 132, 132, - 132, 33, 34, 35, 133, 132, 132, 132, - 132, 14, 132, 135, 132, 11, 132, 132, - 132, 132, 132, 132, 132, 13, 14, 15, - 16, 17, 132, 132, 20, 132, 132, 132, - 23, 24, 25, 132, 26, 27, 28, 132, - 132, 132, 132, 132, 33, 34, 35, 133, - 132, 132, 132, 132, 14, 132, 135, 132, - 11, 132, 132, 132, 132, 132, 132, 132, - 13, 14, 15, 16, 17, 132, 132, 132, - 132, 132, 132, 23, 24, 25, 132, 26, - 27, 28, 132, 132, 132, 132, 132, 33, - 34, 35, 133, 132, 132, 132, 132, 14, - 132, 135, 132, 11, 132, 132, 132, 132, - 132, 132, 132, 13, 14, 15, 16, 17, - 18, 19, 20, 132, 132, 132, 23, 24, - 25, 132, 26, 27, 28, 132, 132, 132, - 132, 132, 33, 34, 35, 133, 132, 132, - 132, 132, 14, 132, 8, 9, 132, 11, - 132, 132, 132, 132, 132, 132, 132, 13, - 14, 15, 16, 17, 18, 19, 20, 21, - 132, 22, 23, 24, 25, 132, 26, 27, - 28, 132, 132, 132, 132, 32, 33, 34, - 35, 32, 132, 132, 132, 132, 37, 132, - 8, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 11, 132, 8, - 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 11, 132, 132, 132, - 132, 132, 132, 132, 132, 14, 15, 16, - 17, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 26, 27, 28, 132, 132, - 132, 132, 132, 33, 34, 35, 133, 132, - 136, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 11, 132, 10, 11, 132, 4, - 132, 132, 132, 4, 132, 132, 132, 132, - 132, 8, 9, 10, 11, 132, 132, 132, - 132, 132, 132, 132, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 8, 22, 23, - 24, 25, 132, 26, 27, 28, 132, 29, - 30, 132, 32, 33, 34, 35, 32, 132, - 132, 132, 132, 37, 132, 11, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, - 29, 30, 132, 11, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 30, - 132, 4, 137, 137, 137, 4, 137, 139, - 138, 138, 138, 138, 138, 138, 138, 138, - 138, 138, 138, 138, 138, 138, 138, 138, - 138, 138, 138, 138, 138, 138, 138, 138, - 138, 138, 138, 138, 138, 138, 138, 138, - 138, 138, 138, 140, 138, 141, 138, 141, - 142, 138, 139, 138, 138, 138, 138, 138, - 138, 138, 138, 138, 138, 138, 138, 138, - 138, 138, 138, 138, 138, 138, 138, 138, - 138, 138, 138, 138, 138, 138, 138, 138, - 138, 138, 138, 138, 138, 1, 140, 140, - 138, 139, 138, 138, 138, 138, 138, 138, - 138, 138, 138, 138, 138, 138, 138, 138, - 138, 138, 138, 138, 138, 138, 138, 138, - 138, 138, 138, 138, 138, 138, 138, 138, - 138, 138, 138, 138, 138, 140, 138, 141, - 138, 139, 138, 138, 138, 138, 138, 138, - 138, 138, 138, 138, 138, 138, 138, 138, - 138, 138, 138, 138, 138, 138, 138, 138, - 138, 138, 138, 138, 138, 138, 138, 138, - 138, 138, 138, 138, 138, 140, 138, 141, - 138, 141, 138, 39, 40, 38, 41, 38, - 38, 38, 38, 38, 38, 38, 42, 43, - 44, 45, 46, 47, 48, 49, 50, 39, - 51, 52, 53, 54, 38, 55, 56, 57, - 38, 58, 59, 38, 60, 61, 62, 63, - 60, 1, 38, 2, 38, 64, 38, 0 + 51, 40, 52, 53, 54, 55, 39, 56, + 57, 58, 39, 39, 39, 39, 61, 62, + 63, 64, 61, 39, 39, 39, 39, 65, + 39, 39, 64, 39, 40, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 39, 42, 39, 39, 39, 39, 39, 39, + 39, 39, 44, 45, 46, 47, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 56, 57, 58, 39, 39, 39, 39, 39, + 62, 63, 64, 66, 39, 39, 39, 39, + 44, 39, 42, 39, 39, 39, 39, 39, + 39, 39, 39, 44, 45, 46, 47, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 39, 56, 57, 58, 39, 39, 39, 39, + 39, 62, 63, 64, 66, 39, 42, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 45, 46, 47, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 62, 63, 64, + 39, 42, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 46, 47, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 62, 63, 64, 39, 42, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 47, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 62, 63, 64, 39, 42, + 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 62, 63, + 39, 42, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 39, 63, 39, 42, 39, 42, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 45, + 46, 47, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 56, 57, 58, 39, + 39, 39, 39, 39, 62, 63, 64, 66, + 39, 42, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 45, 46, 47, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 39, 57, 58, 39, 39, 39, 39, 39, + 62, 63, 64, 66, 39, 42, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 45, + 46, 47, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 58, 39, + 39, 39, 39, 39, 62, 63, 64, 66, + 39, 67, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 42, 39, + 42, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 45, 46, 47, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 62, + 63, 64, 66, 39, 42, 39, 39, 39, + 39, 39, 39, 39, 43, 44, 45, 46, + 47, 39, 39, 39, 39, 39, 39, 53, + 54, 55, 39, 56, 57, 58, 39, 39, + 39, 39, 39, 62, 63, 64, 66, 39, + 39, 39, 39, 44, 39, 42, 39, 39, + 39, 39, 39, 39, 39, 39, 44, 45, + 46, 47, 39, 39, 39, 39, 39, 39, + 53, 54, 55, 39, 56, 57, 58, 39, + 39, 39, 39, 39, 62, 63, 64, 66, + 39, 39, 39, 39, 44, 39, 42, 39, + 39, 39, 39, 39, 39, 39, 39, 44, + 45, 46, 47, 39, 39, 39, 39, 39, + 39, 39, 54, 55, 39, 56, 57, 58, + 39, 39, 39, 39, 39, 62, 63, 64, + 66, 39, 39, 39, 39, 44, 39, 42, + 39, 39, 39, 39, 39, 39, 39, 39, + 44, 45, 46, 47, 39, 39, 39, 39, + 39, 39, 39, 39, 55, 39, 56, 57, + 58, 39, 39, 39, 39, 39, 62, 63, + 64, 66, 39, 39, 39, 39, 44, 39, + 68, 39, 42, 39, 39, 39, 39, 39, + 39, 39, 43, 44, 45, 46, 47, 39, + 49, 50, 39, 39, 39, 53, 54, 55, + 39, 56, 57, 58, 39, 39, 39, 39, + 39, 62, 63, 64, 66, 39, 39, 39, + 39, 44, 39, 42, 39, 39, 39, 39, + 39, 39, 39, 39, 44, 45, 46, 47, + 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 56, 57, 58, 39, 39, 39, + 39, 39, 62, 63, 64, 66, 39, 39, + 39, 39, 44, 39, 68, 39, 42, 39, + 39, 39, 39, 39, 39, 39, 43, 44, + 45, 46, 47, 39, 39, 50, 39, 39, + 39, 53, 54, 55, 39, 56, 57, 58, + 39, 39, 39, 39, 39, 62, 63, 64, + 66, 39, 39, 39, 39, 44, 39, 68, + 39, 42, 39, 39, 39, 39, 39, 39, + 39, 43, 44, 45, 46, 47, 39, 39, + 39, 39, 39, 39, 53, 54, 55, 39, + 56, 57, 58, 39, 39, 39, 39, 39, + 62, 63, 64, 66, 39, 39, 39, 39, + 44, 39, 68, 39, 42, 39, 39, 39, + 39, 39, 39, 39, 43, 44, 45, 46, + 47, 48, 49, 50, 39, 39, 39, 53, + 54, 55, 39, 56, 57, 58, 39, 39, + 39, 39, 39, 62, 63, 64, 66, 39, + 39, 39, 39, 44, 39, 40, 41, 39, + 42, 39, 39, 39, 39, 39, 39, 39, + 43, 44, 45, 46, 47, 48, 49, 50, + 51, 39, 52, 53, 54, 55, 39, 56, + 57, 58, 39, 39, 39, 39, 61, 62, + 63, 64, 61, 39, 39, 39, 39, 65, + 39, 39, 64, 39, 40, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 39, 42, 39, 40, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 42, 39, 39, 39, 39, 39, 39, 39, + 39, 44, 45, 46, 47, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 56, + 57, 58, 39, 39, 39, 39, 39, 62, + 63, 64, 66, 39, 42, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 59, + 60, 39, 42, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 60, 39, + 4, 70, 69, 71, 69, 69, 69, 69, + 69, 69, 69, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 4, 81, 82, 83, + 84, 69, 85, 86, 87, 69, 69, 69, + 69, 88, 89, 90, 91, 92, 69, 69, + 69, 69, 93, 69, 69, 94, 69, 4, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 71, 69, 69, 69, + 69, 69, 69, 69, 69, 73, 74, 75, + 76, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 85, 86, 87, 69, 69, + 69, 69, 69, 89, 90, 91, 95, 69, + 69, 69, 69, 73, 69, 71, 69, 69, + 69, 69, 69, 69, 69, 69, 73, 74, + 75, 76, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 85, 86, 87, 69, + 69, 69, 69, 69, 89, 90, 91, 95, + 69, 71, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 74, 75, 76, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 89, 90, 91, 69, 71, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 75, + 76, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 89, 90, 91, 69, 71, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 76, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 89, 90, + 91, 69, 71, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 89, 90, 69, 71, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 90, 69, 71, 69, + 71, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 74, 75, 76, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 85, + 86, 87, 69, 69, 69, 69, 69, 89, + 90, 91, 95, 69, 71, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 74, 75, + 76, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 86, 87, 69, 69, + 69, 69, 69, 89, 90, 91, 95, 69, + 71, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 74, 75, 76, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 87, 69, 69, 69, 69, 69, 89, + 90, 91, 95, 69, 97, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, + 96, 98, 96, 71, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 74, 75, 76, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 89, 90, 91, 95, 69, 71, + 69, 69, 69, 69, 69, 69, 69, 72, + 73, 74, 75, 76, 69, 69, 69, 69, + 69, 69, 82, 83, 84, 69, 85, 86, + 87, 69, 69, 69, 69, 69, 89, 90, + 91, 95, 69, 69, 69, 69, 73, 69, + 71, 69, 69, 69, 69, 69, 69, 69, + 69, 73, 74, 75, 76, 69, 69, 69, + 69, 69, 69, 82, 83, 84, 69, 85, + 86, 87, 69, 69, 69, 69, 69, 89, + 90, 91, 95, 69, 69, 69, 69, 73, + 69, 71, 69, 69, 69, 69, 69, 69, + 69, 69, 73, 74, 75, 76, 69, 69, + 69, 69, 69, 69, 69, 83, 84, 69, + 85, 86, 87, 69, 69, 69, 69, 69, + 89, 90, 91, 95, 69, 69, 69, 69, + 73, 69, 71, 69, 69, 69, 69, 69, + 69, 69, 69, 73, 74, 75, 76, 69, + 69, 69, 69, 69, 69, 69, 69, 84, + 69, 85, 86, 87, 69, 69, 69, 69, + 69, 89, 90, 91, 95, 69, 69, 69, + 69, 73, 69, 99, 69, 71, 69, 69, + 69, 69, 69, 69, 69, 72, 73, 74, + 75, 76, 69, 78, 79, 69, 69, 69, + 82, 83, 84, 69, 85, 86, 87, 69, + 69, 69, 69, 69, 89, 90, 91, 95, + 69, 69, 69, 69, 73, 69, 71, 69, + 69, 69, 69, 69, 69, 69, 69, 73, + 74, 75, 76, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 85, 86, 87, + 69, 69, 69, 69, 69, 89, 90, 91, + 95, 69, 69, 69, 69, 73, 69, 99, + 69, 71, 69, 69, 69, 69, 69, 69, + 69, 72, 73, 74, 75, 76, 69, 69, + 79, 69, 69, 69, 82, 83, 84, 69, + 85, 86, 87, 69, 69, 69, 69, 69, + 89, 90, 91, 95, 69, 69, 69, 69, + 73, 69, 99, 69, 71, 69, 69, 69, + 69, 69, 69, 69, 72, 73, 74, 75, + 76, 69, 69, 69, 69, 69, 69, 82, + 83, 84, 69, 85, 86, 87, 69, 69, + 69, 69, 69, 89, 90, 91, 95, 69, + 69, 69, 69, 73, 69, 99, 69, 71, + 69, 69, 69, 69, 69, 69, 69, 72, + 73, 74, 75, 76, 77, 78, 79, 69, + 69, 69, 82, 83, 84, 69, 85, 86, + 87, 69, 69, 69, 69, 69, 89, 90, + 91, 95, 69, 69, 69, 69, 73, 69, + 4, 70, 69, 71, 69, 69, 69, 69, + 69, 69, 69, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 69, 81, 82, 83, + 84, 69, 85, 86, 87, 69, 69, 69, + 69, 88, 89, 90, 91, 92, 69, 69, + 69, 69, 93, 69, 69, 94, 69, 4, + 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 101, 100, 4, 96, + 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 98, 96, 4, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 71, 69, 69, 69, 69, 69, + 69, 69, 69, 73, 74, 75, 76, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 85, 86, 87, 69, 69, 69, 69, + 69, 89, 90, 91, 95, 69, 101, 100, + 103, 104, 102, 6, 105, 105, 105, 105, + 105, 105, 105, 105, 105, 106, 105, 107, + 108, 69, 71, 69, 69, 69, 69, 69, + 69, 69, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 107, 118, 119, 120, 121, + 69, 122, 123, 124, 69, 59, 60, 69, + 125, 126, 127, 128, 129, 69, 69, 69, + 69, 130, 69, 69, 131, 69, 107, 108, + 69, 71, 69, 69, 69, 69, 69, 69, + 69, 109, 110, 111, 112, 113, 114, 115, + 116, 117, 107, 118, 119, 120, 121, 69, + 122, 123, 124, 69, 69, 69, 69, 125, + 126, 127, 128, 129, 69, 69, 69, 69, + 130, 69, 69, 131, 69, 107, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 71, 69, 69, 69, 69, 69, + 69, 69, 69, 110, 111, 112, 113, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 122, 123, 124, 69, 69, 69, 69, + 69, 126, 127, 128, 132, 69, 69, 69, + 69, 110, 69, 71, 69, 69, 69, 69, + 69, 69, 69, 69, 110, 111, 112, 113, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 122, 123, 124, 69, 69, 69, + 69, 69, 126, 127, 128, 132, 69, 71, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 111, 112, 113, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 126, 127, + 128, 69, 71, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 112, 113, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 126, 127, 128, 69, 71, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 113, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 126, 127, 128, 69, + 71, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 126, + 127, 69, 71, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 127, 69, 71, 69, 71, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 111, 112, 113, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 122, 123, 124, + 69, 69, 69, 69, 69, 126, 127, 128, + 132, 69, 71, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 111, 112, 113, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 123, 124, 69, 69, 69, 69, + 69, 126, 127, 128, 132, 69, 71, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 111, 112, 113, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 124, + 69, 69, 69, 69, 69, 126, 127, 128, + 132, 69, 133, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 98, + 96, 71, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 111, 112, 113, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 126, 127, 128, 132, 69, 71, 69, 69, + 69, 69, 69, 69, 69, 109, 110, 111, + 112, 113, 69, 69, 69, 69, 69, 69, + 119, 120, 121, 69, 122, 123, 124, 69, + 69, 69, 69, 69, 126, 127, 128, 132, + 69, 69, 69, 69, 110, 69, 71, 69, + 69, 69, 69, 69, 69, 69, 69, 110, + 111, 112, 113, 69, 69, 69, 69, 69, + 69, 119, 120, 121, 69, 122, 123, 124, + 69, 69, 69, 69, 69, 126, 127, 128, + 132, 69, 69, 69, 69, 110, 69, 71, + 69, 69, 69, 69, 69, 69, 69, 69, + 110, 111, 112, 113, 69, 69, 69, 69, + 69, 69, 69, 120, 121, 69, 122, 123, + 124, 69, 69, 69, 69, 69, 126, 127, + 128, 132, 69, 69, 69, 69, 110, 69, + 71, 69, 69, 69, 69, 69, 69, 69, + 69, 110, 111, 112, 113, 69, 69, 69, + 69, 69, 69, 69, 69, 121, 69, 122, + 123, 124, 69, 69, 69, 69, 69, 126, + 127, 128, 132, 69, 69, 69, 69, 110, + 69, 134, 69, 71, 69, 69, 69, 69, + 69, 69, 69, 109, 110, 111, 112, 113, + 69, 115, 116, 69, 69, 69, 119, 120, + 121, 69, 122, 123, 124, 69, 69, 69, + 69, 69, 126, 127, 128, 132, 69, 69, + 69, 69, 110, 69, 71, 69, 69, 69, + 69, 69, 69, 69, 69, 110, 111, 112, + 113, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 122, 123, 124, 69, 69, + 69, 69, 69, 126, 127, 128, 132, 69, + 69, 69, 69, 110, 69, 134, 69, 71, + 69, 69, 69, 69, 69, 69, 69, 109, + 110, 111, 112, 113, 69, 69, 116, 69, + 69, 69, 119, 120, 121, 69, 122, 123, + 124, 69, 69, 69, 69, 69, 126, 127, + 128, 132, 69, 69, 69, 69, 110, 69, + 134, 69, 71, 69, 69, 69, 69, 69, + 69, 69, 109, 110, 111, 112, 113, 69, + 69, 69, 69, 69, 69, 119, 120, 121, + 69, 122, 123, 124, 69, 69, 69, 69, + 69, 126, 127, 128, 132, 69, 69, 69, + 69, 110, 69, 134, 69, 71, 69, 69, + 69, 69, 69, 69, 69, 109, 110, 111, + 112, 113, 114, 115, 116, 69, 69, 69, + 119, 120, 121, 69, 122, 123, 124, 69, + 69, 69, 69, 69, 126, 127, 128, 132, + 69, 69, 69, 69, 110, 69, 107, 108, + 69, 71, 69, 69, 69, 69, 69, 69, + 69, 109, 110, 111, 112, 113, 114, 115, + 116, 117, 69, 118, 119, 120, 121, 69, + 122, 123, 124, 69, 69, 69, 69, 125, + 126, 127, 128, 129, 69, 69, 69, 69, + 130, 69, 69, 131, 69, 107, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 101, 100, 107, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, + 96, 98, 96, 107, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 71, 69, 69, 69, 69, 69, 69, 69, + 69, 110, 111, 112, 113, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 122, + 123, 124, 69, 69, 69, 69, 69, 126, + 127, 128, 132, 69, 101, 100, 8, 9, + 135, 11, 135, 135, 135, 135, 135, 135, + 135, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 8, 22, 23, 24, 25, 135, + 26, 27, 28, 135, 135, 135, 135, 32, + 33, 34, 38, 32, 135, 135, 135, 135, + 37, 135, 135, 38, 135, 8, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 11, 135, 135, 135, 135, 135, + 135, 135, 135, 14, 15, 16, 17, 135, + 135, 135, 135, 135, 135, 135, 135, 135, + 135, 26, 27, 28, 135, 135, 135, 135, + 135, 33, 34, 38, 136, 135, 135, 135, + 135, 14, 135, 11, 135, 135, 135, 135, + 135, 135, 135, 135, 14, 15, 16, 17, + 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 26, 27, 28, 135, 135, 135, + 135, 135, 33, 34, 38, 136, 135, 11, + 135, 135, 135, 135, 135, 135, 135, 135, + 135, 15, 16, 17, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 33, 34, + 38, 135, 11, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 16, 17, 135, + 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, + 135, 33, 34, 38, 135, 11, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, + 135, 17, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 33, 34, 38, 135, + 11, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 33, + 34, 135, 11, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 34, 135, 11, 137, 11, 135, + 135, 135, 135, 135, 135, 135, 135, 135, + 15, 16, 17, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 26, 27, 28, + 135, 135, 135, 135, 135, 33, 34, 38, + 136, 135, 11, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 15, 16, 17, 135, + 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 27, 28, 135, 135, 135, 135, + 135, 33, 34, 38, 136, 135, 11, 135, + 135, 135, 135, 135, 135, 135, 135, 135, + 15, 16, 17, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 28, + 135, 135, 135, 135, 135, 33, 34, 38, + 136, 135, 138, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 11, + 135, 11, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 15, 16, 17, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, + 33, 34, 38, 136, 135, 11, 135, 135, + 135, 135, 135, 135, 135, 13, 14, 15, + 16, 17, 135, 135, 135, 135, 135, 135, + 23, 24, 25, 135, 26, 27, 28, 135, + 135, 135, 135, 135, 33, 34, 38, 136, + 135, 135, 135, 135, 14, 135, 11, 135, + 135, 135, 135, 135, 135, 135, 135, 14, + 15, 16, 17, 135, 135, 135, 135, 135, + 135, 23, 24, 25, 135, 26, 27, 28, + 135, 135, 135, 135, 135, 33, 34, 38, + 136, 135, 135, 135, 135, 14, 135, 11, + 135, 135, 135, 135, 135, 135, 135, 135, + 14, 15, 16, 17, 135, 135, 135, 135, + 135, 135, 135, 24, 25, 135, 26, 27, + 28, 135, 135, 135, 135, 135, 33, 34, + 38, 136, 135, 135, 135, 135, 14, 135, + 11, 135, 135, 135, 135, 135, 135, 135, + 135, 14, 15, 16, 17, 135, 135, 135, + 135, 135, 135, 135, 135, 25, 135, 26, + 27, 28, 135, 135, 135, 135, 135, 33, + 34, 38, 136, 135, 135, 135, 135, 14, + 135, 139, 135, 11, 135, 135, 135, 135, + 135, 135, 135, 13, 14, 15, 16, 17, + 135, 19, 20, 135, 135, 135, 23, 24, + 25, 135, 26, 27, 28, 135, 135, 135, + 135, 135, 33, 34, 38, 136, 135, 135, + 135, 135, 14, 135, 11, 135, 135, 135, + 135, 135, 135, 135, 135, 14, 15, 16, + 17, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 26, 27, 28, 135, 135, + 135, 135, 135, 33, 34, 38, 136, 135, + 135, 135, 135, 14, 135, 139, 135, 11, + 135, 135, 135, 135, 135, 135, 135, 13, + 14, 15, 16, 17, 135, 135, 20, 135, + 135, 135, 23, 24, 25, 135, 26, 27, + 28, 135, 135, 135, 135, 135, 33, 34, + 38, 136, 135, 135, 135, 135, 14, 135, + 139, 135, 11, 135, 135, 135, 135, 135, + 135, 135, 13, 14, 15, 16, 17, 135, + 135, 135, 135, 135, 135, 23, 24, 25, + 135, 26, 27, 28, 135, 135, 135, 135, + 135, 33, 34, 38, 136, 135, 135, 135, + 135, 14, 135, 139, 135, 11, 135, 135, + 135, 135, 135, 135, 135, 13, 14, 15, + 16, 17, 18, 19, 20, 135, 135, 135, + 23, 24, 25, 135, 26, 27, 28, 135, + 135, 135, 135, 135, 33, 34, 38, 136, + 135, 135, 135, 135, 14, 135, 8, 9, + 135, 11, 135, 135, 135, 135, 135, 135, + 135, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 135, 22, 23, 24, 25, 135, + 26, 27, 28, 135, 135, 135, 135, 32, + 33, 34, 38, 32, 135, 135, 135, 135, + 37, 135, 135, 38, 135, 8, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 11, 135, 8, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, + 135, 11, 135, 135, 135, 135, 135, 135, + 135, 135, 14, 15, 16, 17, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, + 26, 27, 28, 135, 135, 135, 135, 135, + 33, 34, 38, 136, 135, 140, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 11, + 135, 10, 11, 135, 4, 135, 135, 135, + 4, 135, 135, 135, 135, 135, 8, 9, + 10, 11, 135, 135, 135, 135, 135, 135, + 135, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 8, 22, 23, 24, 25, 135, + 26, 27, 28, 135, 29, 30, 135, 32, + 33, 34, 38, 32, 135, 135, 135, 135, + 37, 135, 135, 38, 135, 11, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, + 29, 30, 135, 11, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 30, + 135, 4, 141, 141, 141, 4, 141, 143, + 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 144, 142, 145, 142, 145, + 146, 142, 143, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 1, 144, 144, + 142, 143, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 144, 142, 145, + 142, 143, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 144, 142, 145, + 142, 145, 142, 40, 41, 39, 42, 39, + 39, 39, 39, 39, 39, 39, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 40, + 52, 53, 54, 55, 39, 56, 57, 58, + 39, 59, 60, 39, 61, 62, 63, 64, + 61, 1, 39, 2, 39, 65, 39, 39, + 64, 39, 0 }; static const char _use_syllable_machine_trans_targs[] = { - 1, 120, 0, 2, 31, 1, 58, 60, - 88, 89, 114, 1, 116, 102, 90, 91, - 92, 93, 106, 108, 109, 110, 111, 103, - 104, 105, 97, 98, 99, 117, 118, 119, - 112, 94, 95, 96, 124, 113, 1, 3, - 4, 1, 17, 5, 6, 7, 8, 21, - 23, 24, 25, 26, 18, 19, 20, 12, - 13, 14, 29, 30, 27, 9, 10, 11, - 28, 15, 16, 22, 1, 32, 1, 45, - 33, 34, 35, 36, 49, 51, 52, 53, - 54, 46, 47, 48, 40, 41, 42, 55, - 37, 38, 39, 56, 57, 43, 1, 44, - 1, 50, 1, 1, 1, 59, 1, 1, - 1, 61, 62, 75, 63, 64, 65, 66, - 79, 81, 82, 83, 84, 76, 77, 78, - 70, 71, 72, 85, 67, 68, 69, 86, - 87, 73, 74, 80, 1, 100, 101, 107, - 115, 1, 1, 1, 121, 122, 123 + 1, 122, 0, 2, 31, 1, 59, 61, + 90, 91, 116, 1, 118, 104, 92, 93, + 94, 95, 108, 110, 111, 112, 113, 105, + 106, 107, 99, 100, 101, 119, 120, 121, + 114, 96, 97, 98, 126, 115, 98, 1, + 3, 4, 1, 17, 5, 6, 7, 8, + 21, 23, 24, 25, 26, 18, 19, 20, + 12, 13, 14, 29, 30, 27, 9, 10, + 11, 28, 15, 16, 22, 1, 32, 1, + 45, 33, 34, 35, 36, 49, 51, 52, + 53, 54, 46, 47, 48, 40, 41, 42, + 55, 37, 38, 39, 56, 57, 58, 43, + 1, 44, 1, 50, 1, 1, 1, 60, + 1, 1, 1, 62, 63, 76, 64, 65, + 66, 67, 80, 82, 83, 84, 85, 77, + 78, 79, 71, 72, 73, 86, 68, 69, + 70, 87, 88, 89, 74, 75, 81, 1, + 102, 1, 103, 109, 117, 1, 1, 1, + 123, 124, 125 }; static const char _use_syllable_machine_trans_actions[] = { @@ -737,20 +745,21 @@ static const char _use_syllable_machine_trans_actions[] = { 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 6, 0, 7, 0, - 0, 8, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 6, 7, 0, 8, 9, + 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 9, 0, 10, 0, + 0, 0, 0, 0, 0, 11, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 11, 0, - 12, 0, 13, 14, 15, 0, 16, 17, - 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 13, 0, 14, 0, 15, 16, 17, 0, + 18, 19, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 19, 0, 0, 0, - 0, 20, 21, 22, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 21, + 0, 22, 0, 0, 0, 23, 24, 25, + 0, 0, 0 }; static const char _use_syllable_machine_to_state_actions[] = { @@ -769,7 +778,7 @@ static const char _use_syllable_machine_to_state_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0 }; static const char _use_syllable_machine_from_state_actions[] = { @@ -788,26 +797,26 @@ static const char _use_syllable_machine_from_state_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0 }; static const short _use_syllable_machine_eof_trans[] = { - 1, 0, 39, 39, 39, 39, 39, 39, - 39, 39, 39, 39, 39, 39, 39, 39, - 39, 39, 39, 39, 39, 39, 39, 39, - 39, 39, 39, 39, 39, 39, 39, 69, - 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 95, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 99, - 95, 69, 101, 104, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, - 69, 95, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 99, 95, 69, - 133, 133, 133, 133, 133, 133, 133, 133, - 133, 133, 133, 133, 133, 133, 133, 133, - 133, 133, 133, 133, 133, 133, 133, 133, - 133, 133, 133, 133, 133, 133, 133, 138, - 139, 139, 139, 139, 39 + 1, 0, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 70, + 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 97, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 101, + 97, 70, 101, 103, 106, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 97, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 101, 97, + 70, 101, 136, 136, 136, 136, 136, 136, + 136, 136, 138, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, + 136, 142, 143, 143, 143, 143, 40 }; static const int use_syllable_machine_start = 1; @@ -821,7 +830,7 @@ static const int use_syllable_machine_en_main = 1; -#line 184 "hb-ot-shaper-use-machine.rl" +#line 186 "hb-ot-shaper-use-machine.rl" #define found_syllable(syllable_type) \ @@ -920,7 +929,7 @@ find_syllables_use (hb_buffer_t *buffer) unsigned int act HB_UNUSED; int cs; -#line 924 "hb-ot-shaper-use-machine.hh" +#line 933 "hb-ot-shaper-use-machine.hh" { cs = use_syllable_machine_start; ts = 0; @@ -928,12 +937,12 @@ find_syllables_use (hb_buffer_t *buffer) act = 0; } -#line 284 "hb-ot-shaper-use-machine.rl" +#line 286 "hb-ot-shaper-use-machine.rl" unsigned int syllable_serial = 1; -#line 937 "hb-ot-shaper-use-machine.hh" +#line 946 "hb-ot-shaper-use-machine.hh" { int _slen; int _trans; @@ -947,7 +956,7 @@ _resume: #line 1 "NONE" {ts = p;} break; -#line 951 "hb-ot-shaper-use-machine.hh" +#line 960 "hb-ot-shaper-use-machine.hh" } _keys = _use_syllable_machine_trans_keys + (cs<<1); @@ -965,87 +974,111 @@ _eof_trans: goto _again; switch ( _use_syllable_machine_trans_actions[_trans] ) { - case 6: + case 7: #line 1 "NONE" {te = p+1;} break; - case 14: -#line 172 "hb-ot-shaper-use-machine.rl" + case 16: +#line 173 "hb-ot-shaper-use-machine.rl" {te = p+1;{ found_syllable (use_virama_terminated_cluster); }} break; - case 12: -#line 173 "hb-ot-shaper-use-machine.rl" + case 14: +#line 174 "hb-ot-shaper-use-machine.rl" {te = p+1;{ found_syllable (use_sakot_terminated_cluster); }} break; - case 10: -#line 174 "hb-ot-shaper-use-machine.rl" + case 12: +#line 175 "hb-ot-shaper-use-machine.rl" {te = p+1;{ found_syllable (use_standard_cluster); }} break; - case 18: -#line 175 "hb-ot-shaper-use-machine.rl" + case 20: +#line 176 "hb-ot-shaper-use-machine.rl" {te = p+1;{ found_syllable (use_number_joiner_terminated_cluster); }} break; - case 16: -#line 176 "hb-ot-shaper-use-machine.rl" + case 18: +#line 177 "hb-ot-shaper-use-machine.rl" {te = p+1;{ found_syllable (use_numeral_cluster); }} break; - case 8: -#line 177 "hb-ot-shaper-use-machine.rl" + case 10: +#line 178 "hb-ot-shaper-use-machine.rl" {te = p+1;{ found_syllable (use_symbol_cluster); }} break; - case 22: -#line 178 "hb-ot-shaper-use-machine.rl" + case 25: +#line 179 "hb-ot-shaper-use-machine.rl" {te = p+1;{ found_syllable (use_hieroglyph_cluster); }} break; case 5: -#line 179 "hb-ot-shaper-use-machine.rl" +#line 181 "hb-ot-shaper-use-machine.rl" {te = p+1;{ found_syllable (use_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }} break; case 4: -#line 180 "hb-ot-shaper-use-machine.rl" +#line 182 "hb-ot-shaper-use-machine.rl" {te = p+1;{ found_syllable (use_non_cluster); }} break; - case 13: -#line 172 "hb-ot-shaper-use-machine.rl" + case 15: +#line 173 "hb-ot-shaper-use-machine.rl" {te = p;p--;{ found_syllable (use_virama_terminated_cluster); }} break; - case 11: -#line 173 "hb-ot-shaper-use-machine.rl" + case 13: +#line 174 "hb-ot-shaper-use-machine.rl" {te = p;p--;{ found_syllable (use_sakot_terminated_cluster); }} break; - case 9: -#line 174 "hb-ot-shaper-use-machine.rl" + case 11: +#line 175 "hb-ot-shaper-use-machine.rl" {te = p;p--;{ found_syllable (use_standard_cluster); }} break; - case 17: -#line 175 "hb-ot-shaper-use-machine.rl" + case 19: +#line 176 "hb-ot-shaper-use-machine.rl" {te = p;p--;{ found_syllable (use_number_joiner_terminated_cluster); }} break; - case 15: -#line 176 "hb-ot-shaper-use-machine.rl" + case 17: +#line 177 "hb-ot-shaper-use-machine.rl" {te = p;p--;{ found_syllable (use_numeral_cluster); }} break; - case 7: -#line 177 "hb-ot-shaper-use-machine.rl" + case 9: +#line 178 "hb-ot-shaper-use-machine.rl" {te = p;p--;{ found_syllable (use_symbol_cluster); }} break; - case 21: -#line 178 "hb-ot-shaper-use-machine.rl" + case 24: +#line 179 "hb-ot-shaper-use-machine.rl" {te = p;p--;{ found_syllable (use_hieroglyph_cluster); }} break; - case 19: -#line 179 "hb-ot-shaper-use-machine.rl" + case 21: +#line 181 "hb-ot-shaper-use-machine.rl" {te = p;p--;{ found_syllable (use_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }} break; - case 20: -#line 180 "hb-ot-shaper-use-machine.rl" + case 23: +#line 182 "hb-ot-shaper-use-machine.rl" {te = p;p--;{ found_syllable (use_non_cluster); }} break; case 1: -#line 177 "hb-ot-shaper-use-machine.rl" +#line 178 "hb-ot-shaper-use-machine.rl" {{p = ((te))-1;}{ found_syllable (use_symbol_cluster); }} break; -#line 1049 "hb-ot-shaper-use-machine.hh" + case 22: +#line 1 "NONE" + { switch( act ) { + case 8: + {{p = ((te))-1;} found_syllable (use_non_cluster); } + break; + case 9: + {{p = ((te))-1;} found_syllable (use_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; } + break; + } + } + break; + case 6: +#line 1 "NONE" + {te = p+1;} +#line 180 "hb-ot-shaper-use-machine.rl" + {act = 8;} + break; + case 8: +#line 1 "NONE" + {te = p+1;} +#line 181 "hb-ot-shaper-use-machine.rl" + {act = 9;} + break; +#line 1082 "hb-ot-shaper-use-machine.hh" } _again: @@ -1054,7 +1087,7 @@ _again: #line 1 "NONE" {ts = 0;} break; -#line 1058 "hb-ot-shaper-use-machine.hh" +#line 1091 "hb-ot-shaper-use-machine.hh" } if ( ++p != pe ) @@ -1070,7 +1103,7 @@ _again: } -#line 289 "hb-ot-shaper-use-machine.rl" +#line 291 "hb-ot-shaper-use-machine.rl" } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-use-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-use-table.hh index 491b2f940e1da..d3c49949aa8c6 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-use-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-use-table.hh @@ -6,18 +6,18 @@ * * on files with these headers: * - * # IndicSyllabicCategory-15.1.0.txt - * # Date: 2023-01-05 - * # IndicPositionalCategory-15.1.0.txt - * # Date: 2023-01-05 - * # ArabicShaping-15.1.0.txt - * # Date: 2023-01-05 - * # DerivedCoreProperties-15.1.0.txt - * # Date: 2023-08-07, 15:21:24 GMT - * # Blocks-15.1.0.txt - * # Date: 2023-07-28, 15:47:20 GMT - * # Scripts-15.1.0.txt - * # Date: 2023-07-28, 16:01:07 GMT + * # IndicSyllabicCategory-16.0.0.txt + * # Date: 2024-04-30, 21:48:21 GMT + * # IndicPositionalCategory-16.0.0.txt + * # Date: 2024-04-30, 21:48:21 GMT + * # ArabicShaping-16.0.0.txt + * # Date: 2024-07-30 + * # DerivedCoreProperties-16.0.0.txt + * # Date: 2024-05-31, 18:09:32 GMT + * # Blocks-16.0.0.txt + * # Date: 2024-02-02 + * # Scripts-16.0.0.txt + * # Date: 2024-04-30, 21:48:40 GMT * # Override values For Indic_Syllabic_Category * # Not derivable * # Initial version based on Unicode 7.0 by Andrew Glass 2014-03-17 @@ -27,6 +27,7 @@ * # Updated for Unicode 14.0 by Andrew Glass 2021-09-25 * # Updated for Unicode 15.0 by Andrew Glass 2022-09-16 * # Updated for Unicode 15.1 by Andrew Glass 2023-09-14 + * # Updated for Unicode 16.0 by Andrew Glass 2024-09-11 * # Override values For Indic_Positional_Category * # Not derivable * # Initial version based on Unicode 7.0 by Andrew Glass 2014-03-17 @@ -38,6 +39,7 @@ * # Updated for Unicode 14.0 by Andrew Glass 2021-09-28 * # Updated for Unicode 15.0 by Andrew Glass 2022-09-16 * # Updated for Unicode 15.1 by Andrew Glass 2023-09-14 + * # Updated for Unicode 16.0 by Andrew Glass 2024-09-11 * UnicodeData.txt does not have a header. */ @@ -65,6 +67,7 @@ #define N USE(N) /* BASE_NUM */ #define O USE(O) /* OTHER */ #define R USE(R) /* REPHA */ +#define RK USE(RK) /* REORDERING_KILLER */ #define SB USE(SB) /* HIEROGLYPH_SEGMENT_BEGIN */ #define SE USE(SE) /* HIEROGLYPH_SEGMENT_END */ #define SUB USE(SUB) /* CONS_SUB */ @@ -99,16 +102,16 @@ #ifndef HB_OPTIMIZE_SIZE static const uint8_t -hb_use_u8[3187] = +hb_use_u8[3345] = { - 16, 50, 51, 51, 51, 52, 51, 83, 118, 131, 51, 57, 58, 179, 195, 61, + 16, 50, 51, 51, 51, 52, 51, 83, 118, 131, 57, 58, 59, 195, 211, 62, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 14, 0, 1, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 4, 2, 2, + 15, 0, 1, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 4, 2, 2, 5, 6, 2, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 2, 2, 17, 18, 19, 20, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 2, 33, 2, 2, 2, @@ -121,24 +124,26 @@ hb_use_u8[3187] = 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 47, 48, 2, 49, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 50, 51, 2, 2, 2, - 2, 2, 2, 2, 2, 52, 53, 2, 54, 2, 2, 55, 2, 2, 56, 57, - 58, 59, 60, 61, 62, 63, 64, 65, 2, 66, 67, 2, 68, 69, 70, 71, - 2, 72, 2, 73, 74, 75, 76, 2, 2, 77, 78, 79, 80, 2, 81, 82, - 2, 83, 83, 83, 83, 83, 83, 83, 83, 84, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 52, 53, 2, 54, 2, 2, 55, 56, 2, 57, 58, + 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 2, 70, 71, 72, 73, + 2, 74, 2, 75, 76, 77, 78, 2, 2, 79, 80, 81, 82, 2, 83, 84, + 2, 85, 85, 85, 85, 85, 85, 85, 85, 86, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 87, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 88, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 89, 90, 2, 2, 2, 91, 2, 2, 2, 92, + 93, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 94, 94, 94, 95, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 85, 86, 2, 2, 2, 2, 2, 2, 2, 87, - 88, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 89, 89, 89, 90, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 96, 97, 2, 2, 2, 2, 2, + 2, 2, 2, 98, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 91, 92, 2, 2, 2, 2, 2, - 2, 2, 2, 93, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 94, 2, 2, 95, 2, 2, 2, 96, 2, 2, 2, 2, 2, - 2, 2, 2, 97, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 98, 98, 99, 100, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, - 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, - 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 2, 2, 2, 99, 2, 2, 100, 2, 2, 2, 101, 2, 102, 2, 2, 2, + 2, 2, 2, 103, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 104, 104, 105, 106, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, + 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, + 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 0, 5, 0, 0, 0, 0, 0, 6, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -167,7 +172,7 @@ hb_use_u8[3187] = 0, 0, 0, 27, 31, 2, 9, 0, 0, 10, 29, 30, 2, 2, 2, 9, 2, 2, 2, 30, 2, 2, 0, 17, 45, 0, 0, 35, 47, 0, 0, 0, 9, 50, 51, 0, 0, 0, 0, 0, 0, 11, 29, 2, 2, 2, 2, 9, - 2, 2, 2, 2, 2, 2, 52, 53, 23, 23, 19, 31, 48, 33, 48, 34, + 2, 2, 2, 2, 2, 2, 52, 53, 23, 19, 20, 31, 48, 33, 48, 34, 54, 0, 0, 0, 35, 0, 0, 0, 30, 12, 29, 30, 2, 2, 2, 2, 2, 2, 2, 2, 9, 0, 2, 2, 2, 2, 30, 2, 2, 2, 2, 30, 0, 2, 2, 2, 9, 0, 55, 0, 35, 23, 22, 31, 31, 18, 48, 48, @@ -195,9 +200,9 @@ hb_use_u8[3187] = 0, 2, 2, 100, 101, 102, 103, 61, 63, 104, 16, 45, 22, 59, 21, 80, 48, 48, 76, 11, 11, 11, 105, 46, 40, 11, 106, 74, 2, 2, 2, 2, 2, 2, 2, 107, 22, 20, 20, 22, 48, 48, 22, 108, 2, 2, 2, 9, - 0, 0, 0, 0, 0, 0, 109, 110, 111, 111, 111, 0, 0, 0, 0, 0, - 0, 106, 74, 2, 2, 2, 2, 2, 2, 60, 61, 59, 25, 22, 112, 61, - 2, 2, 2, 2, 107, 22, 23, 45, 45, 102, 14, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 109, 110, 110, 110, 110, 0, 0, 0, 0, 0, + 0, 106, 74, 2, 2, 2, 2, 2, 2, 60, 61, 59, 25, 22, 111, 61, + 2, 2, 2, 2, 107, 22, 23, 45, 45, 102, 112, 0, 0, 0, 0, 0, 0, 2, 2, 61, 18, 48, 23, 113, 102, 102, 102, 114, 115, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 30, 2, 11, 46, 116, 116, 116, 11, 116, 116, 15, 116, 116, 116, 26, 0, 40, 0, 0, 0, 117, 51, 11, 5, 0, @@ -226,6 +231,7 @@ hb_use_u8[3187] = 146, 2, 2, 30, 2, 30, 2, 2, 2, 2, 2, 2, 0, 14, 37, 0, 147, 2, 2, 13, 37, 0, 30, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 2, 2, 9, 2, 2, 11, 41, 0, 0, 0, + 0, 2, 2, 2, 0, 27, 22, 22, 30, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 27, 38, 0, 2, 2, 2, 116, 116, 116, 116, 116, 148, 2, 9, 0, 0, 0, 0, 0, 2, 14, 14, 0, 0, 0, 0, 0, 9, 2, 2, 9, 2, 2, 2, 2, 30, 2, 9, 0, 30, 2, 0, @@ -244,39 +250,45 @@ hb_use_u8[3187] = 0, 11, 11, 30, 2, 2, 2, 9, 30, 9, 2, 30, 2, 2, 58, 17, 23, 16, 23, 47, 32, 33, 32, 34, 0, 0, 0, 0, 35, 0, 0, 0, 2, 2, 23, 0, 11, 11, 11, 46, 0, 11, 11, 46, 0, 0, 0, 0, - 0, 2, 2, 65, 25, 20, 20, 20, 22, 23, 126, 15, 17, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 0, 0, 163, 164, 0, 0, 0, 0, 0, 0, - 0, 18, 19, 20, 20, 66, 99, 25, 160, 11, 165, 9, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 2, 2, 65, 25, 20, 20, 0, 48, 48, 11, - 166, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 20, - 0, 23, 19, 20, 20, 21, 16, 82, 166, 38, 0, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 10, 167, 25, 20, 22, 22, 165, 9, 0, 0, - 0, 2, 2, 2, 2, 2, 9, 43, 136, 23, 22, 20, 76, 21, 22, 0, - 0, 2, 2, 2, 9, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 18, - 19, 20, 21, 22, 105, 166, 37, 0, 0, 2, 2, 2, 9, 30, 0, 2, - 2, 2, 2, 30, 9, 2, 2, 2, 2, 23, 23, 18, 32, 33, 12, 168, - 169, 170, 171, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 2, 2, - 2, 65, 25, 20, 20, 0, 22, 23, 29, 108, 0, 33, 0, 0, 0, 0, - 0, 52, 20, 22, 22, 22, 140, 2, 2, 2, 172, 173, 11, 15, 174, 72, - 175, 0, 0, 1, 147, 0, 0, 0, 0, 52, 20, 22, 16, 19, 20, 2, - 2, 2, 2, 158, 158, 158, 176, 176, 176, 176, 176, 176, 15, 177, 0, 30, - 0, 22, 20, 20, 31, 22, 22, 11, 166, 0, 61, 61, 61, 61, 61, 61, - 61, 66, 21, 82, 46, 0, 0, 0, 0, 2, 2, 2, 9, 2, 30, 2, - 2, 52, 22, 22, 31, 0, 38, 22, 27, 11, 159, 178, 174, 0, 0, 0, - 0, 2, 2, 2, 30, 9, 2, 2, 2, 2, 2, 2, 2, 2, 23, 23, - 47, 22, 35, 82, 68, 0, 0, 0, 0, 2, 179, 66, 47, 0, 0, 0, - 0, 11, 180, 2, 2, 2, 2, 2, 2, 2, 2, 23, 22, 20, 31, 0, - 48, 16, 143, 0, 0, 0, 0, 0, 0, 181, 181, 181, 181, 181, 181, 181, - 181, 182, 182, 182, 183, 184, 182, 181, 181, 185, 181, 181, 186, 187, 187, 187, - 187, 187, 187, 187, 0, 0, 0, 0, 0, 11, 11, 11, 46, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 9, 0, 58, 188, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, - 40, 116, 26, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, - 0, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 58, - 37, 0, 6, 120, 120, 120, 121, 0, 0, 11, 11, 11, 49, 2, 2, 2, - 0, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, - 46, 2, 2, 2, 2, 2, 2, 11, 11, 2, 2, 2, 2, 2, 2, 22, - 22, 2, 2, 44, 44, 44, 92, 0, 0, O, O, O, GB, B, B, O, + 0, 2, 2, 2, 2, 2, 30, 0, 9, 2, 2, 2, 30, 45, 59, 20, + 20, 31, 33, 32, 32, 25, 163, 29, 164, 165, 37, 0, 0, 0, 0, 0, + 0, 12, 26, 0, 0, 0, 0, 0, 0, 2, 2, 65, 25, 20, 20, 20, + 22, 23, 126, 15, 17, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, + 166, 167, 0, 0, 0, 0, 0, 0, 0, 18, 19, 20, 20, 66, 99, 25, + 160, 11, 168, 9, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, + 65, 25, 20, 20, 0, 48, 48, 11, 169, 37, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 2, 20, 0, 23, 19, 20, 20, 21, 16, 82, + 169, 38, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 10, 170, + 25, 20, 22, 22, 168, 9, 0, 0, 0, 2, 2, 2, 2, 2, 9, 43, + 136, 23, 22, 20, 76, 21, 22, 0, 0, 2, 2, 2, 9, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 2, 18, 19, 20, 21, 22, 105, 169, 37, 0, + 0, 2, 2, 2, 9, 30, 0, 2, 2, 2, 2, 30, 9, 2, 2, 2, + 2, 23, 23, 18, 32, 33, 12, 171, 165, 172, 173, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 0, 2, 2, 2, 65, 25, 20, 20, 0, 22, 23, + 29, 108, 0, 33, 0, 0, 0, 0, 0, 52, 20, 22, 22, 22, 140, 2, + 2, 2, 174, 175, 11, 15, 176, 61, 177, 0, 0, 1, 147, 0, 0, 0, + 0, 52, 20, 22, 16, 19, 20, 2, 2, 2, 2, 158, 158, 158, 178, 178, + 178, 178, 178, 178, 15, 179, 0, 30, 0, 22, 20, 20, 31, 22, 22, 11, + 169, 0, 61, 61, 61, 61, 61, 61, 61, 66, 21, 82, 46, 0, 0, 0, + 0, 2, 2, 2, 9, 2, 30, 2, 2, 52, 22, 22, 31, 0, 38, 22, + 27, 11, 159, 180, 181, 0, 0, 0, 0, 2, 2, 2, 30, 9, 2, 2, + 2, 2, 2, 2, 2, 2, 23, 23, 47, 22, 35, 82, 68, 0, 0, 0, + 0, 2, 182, 66, 47, 0, 0, 0, 0, 11, 183, 2, 2, 2, 2, 2, + 2, 2, 2, 23, 22, 20, 31, 0, 48, 16, 143, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 156, 0, 0, 184, 184, 184, 184, 184, 184, 184, + 184, 185, 185, 185, 186, 187, 185, 184, 184, 188, 184, 184, 189, 190, 190, 190, + 190, 190, 190, 190, 0, 0, 0, 0, 0, 184, 184, 184, 184, 184, 191, 0, + 0, 2, 2, 2, 2, 2, 2, 2, 22, 22, 22, 22, 22, 22, 192, 193, + 194, 11, 11, 11, 46, 0, 0, 0, 0, 29, 74, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 65, 47, 0, 2, 2, 2, 2, 2, 9, 0, + 58, 195, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 0, 0, 0, 40, 116, 26, 0, 0, 0, 0, 0, + 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 30, 2, 2, 2, 2, 2, 0, 58, 37, 0, 6, 120, 120, 120, 121, 0, + 0, 11, 11, 11, 49, 2, 2, 2, 0, 2, 2, 2, 2, 2, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 46, 2, 2, 2, 2, 2, 2, 11, + 11, 2, 2, 2, 2, 2, 2, 22, 22, 2, 2, 2, 2, 2, 2, 2, + 20, 2, 2, 44, 44, 44, 92, 0, 0, O, O, O, GB, B, B, O, SB, O, SE, GB, O, O, WJ,FMPst,FMPst, O, CGJ, B, O, B,VMAbv,VMAbv, VMAbv, O,VMAbv, B,CMBlw,CMBlw,CMBlw,VMAbv,VMPst, VAbv, VPst,CMBlw, B, VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, VAbv, VPst, VPst, VPst, H, VPre, VPst,VMBlw, O, O, @@ -290,20 +302,21 @@ hb_use_u8[3187] = FMAbv, FAbv,CMAbv,FMAbv,VMAbv,FMAbv, VAbv, IS,FMAbv, B,FMAbv, B, CGJ, WJ, CGJ, GB, CMAbv,CMAbv, B, GB, B, VAbv, SUB, FPst, FPst,VMBlw, FPst, FPst, FBlw,VMAbv,FMBlw, VAbv, VPre, B, MPre, MBlw, SUB, FAbv, FAbv, MAbv, SUB, Sk, VPst, VAbv,VMAbv,VMAbv, FAbv,CMAbv, - VPst, H, B, O,SMAbv,SMBlw,SMAbv,SMAbv,SMAbv, VPst, IS, VBlw, FAbv,VMPre,VMPre,FMAbv, + VPst, H, B, O,SMAbv,SMAbv,SMAbv, VPst, IS, RK, RK, VBlw, FAbv,VMPre,VMPre,FMAbv, CMBlw,VMBlw,VMBlw,VMAbv, CS, O,FMAbv, ZWNJ, CGJ, WJ, WJ, WJ, O,FMPst, O, SB, SE, O, H, MPst, VPst, H,VMAbv, VAbv,VMBlw, B, VBlw, FPst, VPst, FAbv,VMPst, B, CMAbv, VAbv, MBlw, MPst, MBlw, H, O, VBlw, MPst, MPre, MAbv, MBlw, O, B, FAbv, FAbv, FPst, VBlw, B, B, VPre, O,VMPst, IS, O,VMPst, VBlw, VPst,VMBlw,VMBlw,VMAbv, O, IS,VMBlw, B,VMPst,VMAbv,VMPst, CS, CS, B, N, N, O, HN, VPre, VBlw, VAbv, - IS,CMAbv, O, VPst, B, R, R,CMBlw, VAbv, VPre,VMAbv,VMAbv, H, VAbv,CMBlw,FMAbv, - B, CS, CS, H,CMBlw,VMPst, H,VMPst, VAbv,VMAbv, VPst, IS, R, MPst, R, MPst, - CMBlw, B,FMBlw, VBlw,VMAbv, R, MBlw, MBlw, GB, FBlw, FBlw,CMAbv, IS, VBlw, IS, GB, - VAbv, R,VMPst, G, G, J, J, J, SB, SE, J, HR, G, G, HM, HM, - HM, O, VBlw, + IS,CMAbv, O, VPst, B, R, R,CMBlw, VAbv, VPre,VMAbv,VMAbv, H, VAbv,CMBlw,VMPst, + O,VMAbv,CMBlw, IS, R,FMAbv, B, CS, CS, H,CMBlw,VMPst, H,VMPst, VAbv,VMAbv, + VPst, MPst, R, MPst,CMBlw, B,FMBlw, VBlw,VMAbv, CS, SUB, SUB, GB, FBlw, FBlw,CMAbv, + IS, VBlw, IS, R, MBlw, GB, VAbv, R,VMPst, G, G, J, J, J, SB, SE, + J, HR, G, G, HM, HM, HM, G, O, MPre, MPre, MPst,VMAbv, MBlw, VBlw, O, + VBlw, }; static const uint16_t -hb_use_u16[808] = +hb_use_u16[856] = { 0, 0, 1, 2, 0, 3, 0, 3, 0, 0, 4, 5, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, @@ -332,28 +345,31 @@ hb_use_u16[808] = 0, 0, 0, 0, 0, 0, 0,164, 0, 0, 0, 0, 0, 0, 0,165, 0, 0, 0, 0, 0, 0, 0,166,166,167, 34,168, 0, 0, 0, 0, 169,170, 10,171, 95, 0, 0, 0, 0, 0, 0, 0, 70, 10,172, 0, - 10,173,174, 0, 0, 0, 0, 0, 10, 10,175, 2, 0, 0, 0, 0, - 10, 10,176,173, 0, 0, 0, 0, 0, 0, 0, 10,177,178, 0, 10, - 179, 0, 0,180,181, 0, 0, 0,182, 10, 10,183,184,185,186,187, - 188, 10, 10,189,190, 0, 0, 0,191, 10,192,193,194, 10, 10,195, - 188, 10, 10,196,197,106,198,103, 10, 34,199,200,201, 0, 0, 0, - 202,203, 95, 10, 10,204,205, 2,206, 21, 22,207,208,209,210,211, - 10, 10, 10,212,213,214,215, 0,198, 10, 10,216,217, 2, 0, 0, - 10, 10,218,219,220,221, 0, 0, 10, 10, 10,222,223, 2, 0, 0, - 10, 10,224,225, 2, 0, 0, 0, 10,226,227,104,228, 0, 0, 0, - 10, 10,229,230, 0, 0, 0, 0,231,232, 10,233,234, 2, 0, 0, - 0, 0,235, 10, 10,236,237, 0,238, 10, 10,239,240,241, 10, 10, - 242,243, 0, 0, 0, 0, 0, 0, 22, 10,218,244, 8, 10, 71, 19, - 10,245, 74,246, 0, 0, 0, 0,247, 10, 10,248,249, 2,250, 10, - 251,252, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10,253, - 254, 49, 10,255,256, 2, 0, 0,257,257,257,257,257,257,257,257, - 257,257,257,258,259,260, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, - 10, 10, 10,261, 0, 0, 0, 0, 10, 10, 10, 10,262,263,264,264, - 265,266, 0, 0, 0, 0,267, 0, 10, 10, 10, 10, 10, 10, 10, 10, - 10, 10, 10, 10, 10,268, 0, 0, 10, 10, 10, 10, 10, 10,106, 71, - 95,269, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,270, - 10, 10, 71,271,272, 0, 0, 0, 0, 10,273, 0, 10, 10,274, 2, - 0, 0, 0, 0, 0, 10,275, 2, 10, 10, 10, 10,276, 2, 0, 0, + 10,173,174, 0, 0, 0, 0, 0, 10, 10,175, 2, 9, 10,176, 10, + 177, 0, 0, 0, 0, 0, 0, 0, 10, 10,178,173, 0, 0, 0, 0, + 0, 0, 0, 10,179,180, 0, 10,181, 0, 0,182,183, 0, 0, 0, + 184, 10, 10,185,186,187,188,189,190, 10, 10,191,192, 0, 0, 0, + 193, 10,194,195,196, 10, 10,197,190, 10, 10,198,199,106,200,103, + 10, 34,201,202,203, 0, 0, 0,204,205, 95, 10, 10,206,207, 2, + 208, 21, 22,209,210,211,212,213,214, 10, 10,215,216,217,218, 0, + 10, 10, 10,219,220,221,222, 0,200, 10, 10,223,224, 2, 0, 0, + 10, 10,225,226,227,228, 0, 0, 10, 10, 10,229,230, 2, 0, 0, + 10, 10,231,232, 2, 10,141, 0, 10,233,234,104,235, 0, 0, 0, + 10, 10,236,237, 0, 0, 0, 0,238,239, 10,240,241, 2, 0, 0, + 0, 0,242, 10, 10,243,244, 0,245, 10, 10,246,247,248, 10, 10, + 249,250, 0, 0, 0, 0, 0, 0, 22, 10,225,251, 8, 10, 71, 19, + 10,252, 74,253, 0, 0, 0, 0,254, 10, 10,255,256, 2,257, 10, + 258,259, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10,260, + 261, 49, 10,262,263,264, 0, 0,265,265,265,265,265,265,265,265, + 265,265,265,266,267,268,265,265,265,265,265,265,265,265,265,269, + 10,270,271, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, + 10, 10, 10,272, 0, 0, 0, 0, 0, 0, 0, 0,273, 10,274, 2, + 10, 10, 10, 10,275,276,277,277,278,279, 0, 0, 0, 0,280, 0, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,177, 0,281, + 10, 10, 10, 10, 10, 10,106, 71, 95,282, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,283, 10, 10, 71,284,285, 0, 0, 0, + 0, 10,286, 0, 10, 10,287, 2, 0, 0, 0, 0, 0, 10,288, 2, + 0, 0, 0, 0, 0, 10,289,106, 10, 10, 10, 10,290, 2, 0, 0, 130,130,130,130,130,130,130,130,163,163,163,163,163,163,163,163, 163,163,163,163,163,163,163,130, }; @@ -366,23 +382,23 @@ hb_use_b4 (const uint8_t* a, unsigned i) static inline uint_fast8_t hb_use_get_category (unsigned u) { - return u<921600u?hb_use_u8[2809+(((hb_use_u8[593+(((hb_use_u16[((hb_use_u8[113+(((hb_use_b4(hb_use_u8,u>>1>>3>>3>>5))<<5)+((u>>1>>3>>3)&31u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:O; + return u<921600u?hb_use_u8[2953+(((hb_use_u8[625+(((hb_use_u16[((hb_use_u8[113+(((hb_use_b4(hb_use_u8,u>>1>>3>>3>>5))<<5)+((u>>1>>3>>3)&31u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:O; } #else static const uint8_t -hb_use_u8[3483] = +hb_use_u8[3657] = { - 16, 50, 51, 51, 51, 52, 51, 83, 118, 131, 51, 57, 58, 179, 195, 61, + 16, 50, 51, 51, 51, 52, 51, 83, 118, 131, 57, 58, 59, 195, 211, 62, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 14, 0, 1, 1, 2, 1, 1, 3, 4, 5, 6, 7, 8, 9, 10, 1, + 15, 0, 1, 1, 2, 1, 1, 3, 4, 5, 6, 7, 8, 9, 10, 1, 11, 12, 1, 1, 1, 1, 1, 1, 13, 14, 15, 16, 17, 18, 19, 1, 1, 20, 1, 1, 1, 1, 21, 1, 22, 1, 1, 1, 1, 1, 23, 24, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -390,14 +406,15 @@ hb_use_u8[3483] = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 29, 30, 1, 1, 1, 1, 1, 31, 1, 1, 1, 1, 32, 33, 1, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 1, 48, 49, 50, - 51, 52, 52, 52, 52, 53, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 54, 55, 1, 1, 1, - 56, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 57, 58, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 59, 1, 1, - 1, 1, 60, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 61, 62, 1, 63, 1, 1, 1, 1, 64, 1, 1, 1, 1, 1, - 1, 65, 66, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, - 65, 0, 1, 2, 2, 0, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, + 51, 52, 52, 52, 52, 53, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 54, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 55, 1, 1, 1, 1, 1, 1, 1, 1, 56, 57, 1, 58, 1, + 59, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 60, 61, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 62, 1, 1, + 1, 1, 63, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 64, 65, 1, 66, 67, 1, 1, 1, 68, 1, 1, 1, 1, 1, + 1, 69, 70, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 0, 1, 2, 2, 0, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 0, 0, 9, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, @@ -414,23 +431,25 @@ hb_use_u8[3483] = 122, 0, 0, 0, 0, 0, 0, 56, 123, 124, 0, 0, 0, 0, 0, 0, 125, 0, 0, 0, 0, 0, 0, 0, 126, 0, 0, 0, 127, 128, 129, 0, 0, 130, 131, 132, 0, 0, 0, 51, 133, 0, 0, 0, 0, 134, 135, 0, - 0, 56, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 137, 0, - 0, 0, 101, 138, 101, 139, 140, 141, 0, 142, 143, 144, 145, 146, 147, 148, - 0, 149, 150, 151, 152, 146, 153, 154, 155, 156, 157, 158, 0, 159, 160, 161, - 162, 163, 164, 165, 166, 0, 0, 0, 0, 56, 167, 168, 169, 170, 171, 172, - 0, 0, 0, 0, 0, 56, 173, 174, 0, 56, 175, 176, 0, 56, 177, 67, - 0, 178, 179, 180, 0, 0, 0, 0, 0, 56, 181, 0, 0, 0, 0, 0, - 0, 182, 183, 184, 0, 0, 185, 186, 187, 188, 189, 190, 56, 191, 0, 0, - 0, 192, 193, 194, 195, 196, 197, 0, 0, 198, 199, 200, 201, 202, 67, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 203, 204, 205, 206, 0, 0, 0, 0, - 0, 207, 207, 207, 207, 207, 207, 207, 207, 207, 208, 209, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 67, 0, 56, 210, 0, 0, 0, 0, 0, - 0, 56, 56, 211, 212, 213, 0, 0, 214, 56, 56, 56, 56, 56, 56, 56, - 56, 56, 56, 56, 56, 56, 56, 215, 0, 56, 56, 56, 216, 217, 0, 0, - 0, 0, 0, 0, 218, 0, 0, 0, 0, 56, 219, 220, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 101, 221, 56, 222, 0, 0, 0, 0, 0, 0, 101, - 223, 56, 56, 224, 0, 0, 0, 0, 0, 225, 225, 225, 225, 225, 225, 225, - 225, 226, 226, 226, 226, 226, 226, 226, 227, 0, 0, 0, 0, 0, 0, 0, + 0, 56, 136, 7, 137, 138, 0, 0, 0, 0, 0, 0, 0, 56, 139, 0, + 0, 0, 101, 140, 101, 141, 142, 143, 0, 144, 145, 146, 147, 148, 149, 150, + 0, 151, 152, 153, 154, 148, 155, 156, 157, 158, 159, 160, 0, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 171, 172, 56, 173, 174, 175, 176, 177, 178, + 0, 0, 0, 0, 0, 56, 179, 180, 0, 56, 181, 182, 0, 56, 183, 184, + 185, 186, 187, 188, 0, 0, 0, 0, 0, 56, 189, 0, 0, 0, 0, 0, + 0, 190, 191, 192, 0, 0, 193, 194, 195, 196, 197, 198, 56, 199, 0, 0, + 0, 200, 201, 202, 203, 204, 205, 0, 0, 206, 207, 208, 209, 210, 67, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 211, 212, 213, 214, 0, 0, 0, 0, + 0, 215, 215, 215, 215, 215, 215, 215, 215, 215, 216, 217, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, 218, 219, 220, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 67, 0, 56, 221, 0, 0, 0, 0, 0, + 0, 0, 0, 222, 223, 0, 0, 0, 0, 56, 56, 224, 225, 226, 0, 0, + 227, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 228, + 229, 56, 56, 56, 230, 231, 0, 0, 0, 0, 0, 0, 232, 0, 0, 0, + 0, 56, 233, 234, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101, 235, 56, + 236, 0, 0, 0, 0, 0, 0, 101, 237, 0, 0, 0, 0, 0, 0, 101, + 238, 56, 56, 239, 0, 0, 0, 0, 0, 240, 240, 240, 240, 240, 240, 240, + 240, 241, 241, 241, 241, 241, 241, 241, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 0, 5, 0, 0, 0, 0, 0, 6, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, @@ -460,7 +479,7 @@ hb_use_u8[3483] = 0, 10, 29, 30, 2, 2, 2, 9, 2, 2, 2, 30, 2, 2, 0, 17, 45, 0, 0, 35, 47, 0, 0, 0, 9, 50, 51, 0, 0, 0, 0, 0, 0, 11, 29, 2, 2, 2, 2, 9, 2, 2, 2, 2, 2, 2, 52, 53, - 23, 23, 19, 31, 48, 33, 48, 34, 54, 0, 0, 0, 35, 0, 0, 0, + 23, 19, 20, 31, 48, 33, 48, 34, 54, 0, 0, 0, 35, 0, 0, 0, 30, 12, 29, 30, 2, 2, 2, 2, 2, 2, 2, 2, 9, 0, 2, 2, 2, 2, 30, 2, 2, 2, 2, 30, 0, 2, 2, 2, 9, 0, 55, 0, 35, 23, 22, 31, 31, 18, 48, 48, 25, 0, 23, 0, 0, 0, 0, 0, @@ -488,9 +507,9 @@ hb_use_u8[3483] = 63, 104, 16, 45, 22, 59, 21, 80, 48, 48, 76, 11, 11, 11, 105, 46, 40, 11, 106, 74, 2, 2, 2, 2, 2, 2, 2, 107, 22, 20, 20, 22, 48, 48, 22, 108, 2, 2, 2, 9, 0, 0, 0, 0, 0, 0, 109, 110, - 111, 111, 111, 0, 0, 0, 0, 0, 0, 106, 74, 2, 2, 2, 2, 2, - 2, 60, 61, 59, 25, 22, 112, 61, 2, 2, 2, 2, 107, 22, 23, 45, - 45, 102, 14, 0, 0, 0, 0, 0, 0, 2, 2, 61, 18, 48, 23, 113, + 110, 110, 110, 0, 0, 0, 0, 0, 0, 106, 74, 2, 2, 2, 2, 2, + 2, 60, 61, 59, 25, 22, 111, 61, 2, 2, 2, 2, 107, 22, 23, 45, + 45, 102, 112, 0, 0, 0, 0, 0, 0, 2, 2, 61, 18, 48, 23, 113, 102, 102, 102, 114, 115, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 30, 2, 11, 46, 116, 116, 116, 11, 116, 116, 15, 116, 116, 116, 26, 0, 40, 0, 0, 0, 117, 51, 11, 5, 0, 0, 0, 0, 0, 0, 0, 118, 0, @@ -518,7 +537,8 @@ hb_use_u8[3483] = 0, 128, 20, 27, 31, 0, 0, 145, 146, 2, 2, 30, 2, 30, 2, 2, 2, 2, 2, 2, 0, 14, 37, 0, 147, 2, 2, 13, 37, 0, 30, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 2, 2, - 9, 2, 2, 11, 41, 0, 0, 0, 0, 2, 2, 2, 2, 2, 27, 38, + 9, 2, 2, 11, 41, 0, 0, 0, 0, 2, 2, 2, 0, 27, 22, 22, + 30, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 27, 38, 0, 2, 2, 2, 116, 116, 116, 116, 116, 148, 2, 9, 0, 0, 0, 0, 0, 2, 14, 14, 0, 0, 0, 0, 0, 9, 2, 2, 9, 2, 2, 2, 2, 30, 2, 9, 0, 30, 2, 0, 0, 149, 150, 151, 2, 2, 2, 2, @@ -536,39 +556,45 @@ hb_use_u8[3483] = 10, 18, 19, 21, 22, 162, 31, 0, 0, 11, 11, 30, 2, 2, 2, 9, 30, 9, 2, 30, 2, 2, 58, 17, 23, 16, 23, 47, 32, 33, 32, 34, 0, 0, 0, 0, 35, 0, 0, 0, 2, 2, 23, 0, 11, 11, 11, 46, - 0, 11, 11, 46, 0, 0, 0, 0, 0, 2, 2, 65, 25, 20, 20, 20, - 22, 23, 126, 15, 17, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, - 163, 164, 0, 0, 0, 0, 0, 0, 0, 18, 19, 20, 20, 66, 99, 25, - 160, 11, 165, 9, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, - 65, 25, 20, 20, 0, 48, 48, 11, 166, 37, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 2, 2, 20, 0, 23, 19, 20, 20, 21, 16, 82, - 166, 38, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 10, 167, - 25, 20, 22, 22, 165, 9, 0, 0, 0, 2, 2, 2, 2, 2, 9, 43, - 136, 23, 22, 20, 76, 21, 22, 0, 0, 2, 2, 2, 9, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 2, 18, 19, 20, 21, 22, 105, 166, 37, 0, - 0, 2, 2, 2, 9, 30, 0, 2, 2, 2, 2, 30, 9, 2, 2, 2, - 2, 23, 23, 18, 32, 33, 12, 168, 169, 170, 171, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 0, 2, 2, 2, 65, 25, 20, 20, 0, 22, 23, - 29, 108, 0, 33, 0, 0, 0, 0, 0, 52, 20, 22, 22, 22, 140, 2, - 2, 2, 172, 173, 11, 15, 174, 72, 175, 0, 0, 1, 147, 0, 0, 0, - 0, 52, 20, 22, 16, 19, 20, 2, 2, 2, 2, 158, 158, 158, 176, 176, - 176, 176, 176, 176, 15, 177, 0, 30, 0, 22, 20, 20, 31, 22, 22, 11, - 166, 0, 61, 61, 61, 61, 61, 61, 61, 66, 21, 82, 46, 0, 0, 0, - 0, 2, 2, 2, 9, 2, 30, 2, 2, 52, 22, 22, 31, 0, 38, 22, - 27, 11, 159, 178, 174, 0, 0, 0, 0, 2, 2, 2, 30, 9, 2, 2, - 2, 2, 2, 2, 2, 2, 23, 23, 47, 22, 35, 82, 68, 0, 0, 0, - 0, 2, 179, 66, 47, 0, 0, 0, 0, 11, 180, 2, 2, 2, 2, 2, - 2, 2, 2, 23, 22, 20, 31, 0, 48, 16, 143, 0, 0, 0, 0, 0, - 0, 181, 181, 181, 181, 181, 181, 181, 181, 182, 182, 182, 183, 184, 182, 181, - 181, 185, 181, 181, 186, 187, 187, 187, 187, 187, 187, 187, 0, 0, 0, 0, - 0, 11, 11, 11, 46, 0, 0, 0, 0, 2, 2, 2, 2, 2, 9, 0, - 58, 188, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 0, 0, 0, 40, 116, 26, 0, 0, 0, 0, 0, - 0, 0, 0, 9, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 0, 58, 37, 0, 6, 120, 120, 120, 121, 0, - 0, 11, 11, 11, 49, 2, 2, 2, 0, 2, 2, 2, 2, 2, 0, 0, - 2, 2, 2, 2, 2, 2, 2, 2, 46, 2, 2, 2, 2, 2, 2, 11, - 11, 2, 2, 2, 2, 2, 2, 22, 22, 2, 2, 44, 44, 44, 92, 0, + 0, 11, 11, 46, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 30, 0, + 9, 2, 2, 2, 30, 45, 59, 20, 20, 31, 33, 32, 32, 25, 163, 29, + 164, 165, 37, 0, 0, 0, 0, 0, 0, 12, 26, 0, 0, 0, 0, 0, + 0, 2, 2, 65, 25, 20, 20, 20, 22, 23, 126, 15, 17, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 0, 0, 166, 167, 0, 0, 0, 0, 0, 0, + 0, 18, 19, 20, 20, 66, 99, 25, 160, 11, 168, 9, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 2, 2, 65, 25, 20, 20, 0, 48, 48, 11, + 169, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 20, + 0, 23, 19, 20, 20, 21, 16, 82, 169, 38, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 10, 170, 25, 20, 22, 22, 168, 9, 0, 0, + 0, 2, 2, 2, 2, 2, 9, 43, 136, 23, 22, 20, 76, 21, 22, 0, + 0, 2, 2, 2, 9, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 18, + 19, 20, 21, 22, 105, 169, 37, 0, 0, 2, 2, 2, 9, 30, 0, 2, + 2, 2, 2, 30, 9, 2, 2, 2, 2, 23, 23, 18, 32, 33, 12, 171, + 165, 172, 173, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 2, 2, + 2, 65, 25, 20, 20, 0, 22, 23, 29, 108, 0, 33, 0, 0, 0, 0, + 0, 52, 20, 22, 22, 22, 140, 2, 2, 2, 174, 175, 11, 15, 176, 61, + 177, 0, 0, 1, 147, 0, 0, 0, 0, 52, 20, 22, 16, 19, 20, 2, + 2, 2, 2, 158, 158, 158, 178, 178, 178, 178, 178, 178, 15, 179, 0, 30, + 0, 22, 20, 20, 31, 22, 22, 11, 169, 0, 61, 61, 61, 61, 61, 61, + 61, 66, 21, 82, 46, 0, 0, 0, 0, 2, 2, 2, 9, 2, 30, 2, + 2, 52, 22, 22, 31, 0, 38, 22, 27, 11, 159, 180, 181, 0, 0, 0, + 0, 2, 2, 2, 30, 9, 2, 2, 2, 2, 2, 2, 2, 2, 23, 23, + 47, 22, 35, 82, 68, 0, 0, 0, 0, 2, 182, 66, 47, 0, 0, 0, + 0, 11, 183, 2, 2, 2, 2, 2, 2, 2, 2, 23, 22, 20, 31, 0, + 48, 16, 143, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 156, 0, + 0, 184, 184, 184, 184, 184, 184, 184, 184, 185, 185, 185, 186, 187, 185, 184, + 184, 188, 184, 184, 189, 190, 190, 190, 190, 190, 190, 190, 0, 0, 0, 0, + 0, 184, 184, 184, 184, 184, 191, 0, 0, 2, 2, 2, 2, 2, 2, 2, + 22, 22, 22, 22, 22, 22, 192, 193, 194, 11, 11, 11, 46, 0, 0, 0, + 0, 29, 74, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 65, 47, + 0, 2, 2, 2, 2, 2, 9, 0, 58, 195, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, + 40, 116, 26, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 30, 2, 2, 2, 2, 2, 0, 58, + 37, 0, 6, 120, 120, 120, 121, 0, 0, 11, 11, 11, 49, 2, 2, 2, + 0, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, + 46, 2, 2, 2, 2, 2, 2, 11, 11, 2, 2, 2, 2, 2, 2, 22, + 22, 2, 2, 2, 2, 2, 2, 2, 20, 2, 2, 44, 44, 44, 92, 0, 0, O, O, O, GB, B, B, O, SB, O, SE, GB, O, O, WJ,FMPst, FMPst, O, CGJ, B, O, B,VMAbv,VMAbv,VMAbv, O,VMAbv, B,CMBlw,CMBlw,CMBlw,VMAbv, VMPst, VAbv, VPst,CMBlw, B, VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, VAbv, VPst, @@ -582,20 +608,21 @@ hb_use_u8[3483] = VMPst, VBlw, VPst, CGJ, CGJ, VPst,VMAbv,VMAbv,FMAbv, FAbv,CMAbv,FMAbv,VMAbv,FMAbv, VAbv, IS, FMAbv, B,FMAbv, B, CGJ, WJ, CGJ, GB,CMAbv,CMAbv, B, GB, B, VAbv, SUB, FPst, FPst,VMBlw, FPst, FPst, FBlw,VMAbv,FMBlw, VAbv, VPre, B, MPre, MBlw, SUB, FAbv, FAbv, MAbv, - SUB, Sk, VPst, VAbv,VMAbv,VMAbv, FAbv,CMAbv, VPst, H, B, O,SMAbv,SMBlw,SMAbv,SMAbv, - SMAbv, VPst, IS, VBlw, FAbv,VMPre,VMPre,FMAbv,CMBlw,VMBlw,VMBlw,VMAbv, CS, O,FMAbv, ZWNJ, + SUB, Sk, VPst, VAbv,VMAbv,VMAbv, FAbv,CMAbv, VPst, H, B, O,SMAbv,SMAbv,SMAbv, VPst, + IS, RK, RK, VBlw, FAbv,VMPre,VMPre,FMAbv,CMBlw,VMBlw,VMBlw,VMAbv, CS, O,FMAbv, ZWNJ, CGJ, WJ, WJ, WJ, O,FMPst, O, SB, SE, O, H, MPst, VPst, H,VMAbv, VAbv, VMBlw, B, VBlw, FPst, VPst, FAbv,VMPst, B,CMAbv, VAbv, MBlw, MPst, MBlw, H, O, VBlw, MPst, MPre, MAbv, MBlw, O, B, FAbv, FAbv, FPst, VBlw, B, B, VPre, O,VMPst, IS, O,VMPst, VBlw, VPst,VMBlw,VMBlw,VMAbv, O, IS,VMBlw, B,VMPst,VMAbv,VMPst, CS, CS, B, N, N, O, HN, VPre, VBlw, VAbv, IS,CMAbv, O, VPst, B, R, R,CMBlw, - VAbv, VPre,VMAbv,VMAbv, H, VAbv,CMBlw,FMAbv, B, CS, CS, H,CMBlw,VMPst, H,VMPst, - VAbv,VMAbv, VPst, IS, R, MPst, R, MPst,CMBlw, B,FMBlw, VBlw,VMAbv, R, MBlw, MBlw, - GB, FBlw, FBlw,CMAbv, IS, VBlw, IS, GB, VAbv, R,VMPst, G, G, J, J, J, - SB, SE, J, HR, G, G, HM, HM, HM, O, VBlw, + VAbv, VPre,VMAbv,VMAbv, H, VAbv,CMBlw,VMPst, O,VMAbv,CMBlw, IS, R,FMAbv, B, CS, + CS, H,CMBlw,VMPst, H,VMPst, VAbv,VMAbv, VPst, MPst, R, MPst,CMBlw, B,FMBlw, VBlw, + VMAbv, CS, SUB, SUB, GB, FBlw, FBlw,CMAbv, IS, VBlw, IS, R, MBlw, GB, VAbv, R, + VMPst, G, G, J, J, J, SB, SE, J, HR, G, G, HM, HM, HM, G, + O, MPre, MPre, MPst,VMAbv, MBlw, VBlw, O, VBlw, }; static const uint16_t -hb_use_u16[456] = +hb_use_u16[486] = { 0, 0, 1, 2, 0, 3, 4, 5, 0, 6, 7, 0, 8, 0, 9, 10, 11, 12, 10, 13, 14, 10, 10, 15, 16, 17, 18, 19, 20, 21, 22, 23, @@ -614,18 +641,20 @@ hb_use_u16[456] = 148,149,150, 10, 10,151,152, 2,153, 99,154,155,156, 2, 10,157, 10,158,159, 0,160,161,162, 2,163, 0, 0,164, 0,165, 0,166, 166,167, 34,168,169,170, 10,171, 95, 0,172, 0, 10,173,174, 0, - 175, 2,176,173,177,178,179, 0, 0,180,181, 0,182, 10, 10,183, - 184,185,186,187,188, 10, 10,189,190, 0,191, 10,192,193,194, 10, - 10,195, 10,196,197,106,198,103, 10, 34,199,200,201, 0,202,203, - 95, 10, 10,204,205, 2,206, 21, 22,207,208,209,210,211, 10,212, - 213,214,215, 0,198, 10, 10,216,217, 2,218,219,220,221, 10,222, - 223, 2,224,225, 10,226,227,104,228, 0,229,230,231,232, 10,233, - 234, 2,235, 10, 10,236,237, 0,238, 10, 10,239,240,241,242,243, - 22, 10,218,244, 8, 10, 71, 19, 10,245, 74,246,247, 10, 10,248, - 249, 2,250, 10,251,252, 10,253,254, 49, 10,255,256, 2,257,257, - 257,258,259,260, 10,261,262,263,264,264,265,266,267, 0, 10,268, - 106, 71, 95,269, 0,270, 71,271,272, 0,273, 0,274, 2,275, 2, - 276, 2,130,130,163,163,163,130, + 175, 2,176, 10,177, 0,178,173,179,180,181, 0, 0,182,183, 0, + 184, 10, 10,185,186,187,188,189,190, 10, 10,191,192, 0,193, 10, + 194,195,196, 10, 10,197, 10,198,199,106,200,103, 10, 34,201,202, + 203, 0,204,205, 95, 10, 10,206,207, 2,208, 21, 22,209,210,211, + 212,213,214, 10, 10,215,216,217,218, 0, 10,219,220,221,222, 0, + 200, 10, 10,223,224, 2,225,226,227,228, 10,229,230, 2,231,232, + 2, 10,141, 0, 10,233,234,104,235, 0,236,237,238,239, 10,240, + 241, 2,242, 10, 10,243,244, 0,245, 10, 10,246,247,248,249,250, + 22, 10,225,251, 8, 10, 71, 19, 10,252, 74,253,254, 10, 10,255, + 256, 2,257, 10,258,259, 10,260,261, 49, 10,262,263,264,265,265, + 265,266,267,268,265,269, 10,270,271, 2, 10,272,273, 10,274, 2, + 275,276,277,277,278,279,280, 0, 10,177, 0,281,106, 71, 95,282, + 0,283, 71,284,285, 0,286, 0,287, 2,288, 2,289,106,290, 2, + 130,130,163,163,163,130, }; static inline unsigned @@ -636,7 +665,7 @@ hb_use_b4 (const uint8_t* a, unsigned i) static inline uint_fast8_t hb_use_get_category (unsigned u) { - return u<921600u?hb_use_u8[3105+(((hb_use_u8[889+(((hb_use_u16[((hb_use_u8[353+(((hb_use_u8[113+(((hb_use_b4(hb_use_u8,u>>1>>3>>1>>3>>4))<<4)+((u>>1>>3>>1>>3)&15u))])<<3)+((u>>1>>3>>1)&7u))])<<1)+((u>>1>>3)&1u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:O; + return u<921600u?hb_use_u8[3265+(((hb_use_u8[937+(((hb_use_u16[((hb_use_u8[369+(((hb_use_u8[113+(((hb_use_b4(hb_use_u8,u>>1>>3>>1>>3>>4))<<4)+((u>>1>>3>>1>>3)&15u))])<<3)+((u>>1>>3>>1)&7u))])<<1)+((u>>1>>3)&1u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:O; } #endif @@ -656,6 +685,7 @@ hb_use_get_category (unsigned u) #undef N #undef O #undef R +#undef RK #undef SB #undef SE #undef SUB diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-vowel-constraints.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-vowel-constraints.cc index 35693254ec075..4a112e1c28973 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-vowel-constraints.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-vowel-constraints.cc @@ -10,8 +10,8 @@ * # Date: 2015-03-12, 21:17:00 GMT [AG] * # Date: 2019-11-08, 23:22:00 GMT [AG] * - * # Scripts-15.1.0.txt - * # Date: 2023-07-28, 16:01:07 GMT + * # Scripts-16.0.0.txt + * # Date: 2024-04-30, 21:48:40 GMT */ #include "hb.hh" @@ -24,7 +24,7 @@ static void _output_dotted_circle (hb_buffer_t *buffer) { (void) buffer->output_glyph (0x25CCu); - _hb_glyph_info_reset_continuation (&buffer->prev()); + _hb_glyph_info_clear_continuation (&buffer->prev()); } static void diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper.hh index 39a04ec2348e6..48cd5296ffd05 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper.hh @@ -174,9 +174,11 @@ HB_OT_SHAPERS_IMPLEMENT_SHAPERS static inline const hb_ot_shaper_t * -hb_ot_shaper_categorize (const hb_ot_shape_planner_t *planner) +hb_ot_shaper_categorize (hb_script_t script, + hb_direction_t direction, + hb_tag_t gsub_script) { - switch ((hb_tag_t) planner->props.script) + switch ((hb_tag_t) script) { default: return &_hb_ot_shaper_default; @@ -192,9 +194,8 @@ hb_ot_shaper_categorize (const hb_ot_shape_planner_t *planner) * This is because we do fallback shaping for Arabic script (and not others). * But note that Arabic shaping is applicable only to horizontal layout; for * vertical text, just use the generic shaper instead. */ - if ((planner->map.chosen_script[0] != HB_OT_TAG_DEFAULT_SCRIPT || - planner->props.script == HB_SCRIPT_ARABIC) && - HB_DIRECTION_IS_HORIZONTAL(planner->props.direction)) + if ((gsub_script != HB_OT_TAG_DEFAULT_SCRIPT || script == HB_SCRIPT_ARABIC) && + HB_DIRECTION_IS_HORIZONTAL (direction)) return &_hb_ot_shaper_arabic; else return &_hb_ot_shaper_default; @@ -235,10 +236,10 @@ hb_ot_shaper_categorize (const hb_ot_shape_planner_t *planner) * Otherwise, use the specific shaper. * * If it's indy3 tag, send to USE. */ - if (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T') || - planner->map.chosen_script[0] == HB_TAG ('l','a','t','n')) + if (gsub_script == HB_TAG ('D','F','L','T') || + gsub_script == HB_TAG ('l','a','t','n')) return &_hb_ot_shaper_default; - else if ((planner->map.chosen_script[0] & 0x000000FF) == '3') + else if ((gsub_script & 0x000000FF) == '3') return &_hb_ot_shaper_use; else return &_hb_ot_shaper_indic; @@ -254,9 +255,9 @@ hb_ot_shaper_categorize (const hb_ot_shape_planner_t *planner) * If designer designed for 'mymr' tag, also send to default * shaper. That's tag used from before Myanmar shaping spec * was developed. The shaping spec uses 'mym2' tag. */ - if (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T') || - planner->map.chosen_script[0] == HB_TAG ('l','a','t','n') || - planner->map.chosen_script[0] == HB_TAG ('m','y','m','r')) + if (gsub_script == HB_TAG ('D','F','L','T') || + gsub_script == HB_TAG ('l','a','t','n') || + gsub_script == HB_TAG ('m','y','m','r')) return &_hb_ot_shaper_default; else return &_hb_ot_shaper_myanmar; @@ -386,13 +387,22 @@ hb_ot_shaper_categorize (const hb_ot_shape_planner_t *planner) case HB_SCRIPT_KAWI: case HB_SCRIPT_NAG_MUNDARI: + /* Unicode-16.0 additions */ + case HB_SCRIPT_GARAY: + case HB_SCRIPT_GURUNG_KHEMA: + case HB_SCRIPT_KIRAT_RAI: + case HB_SCRIPT_OL_ONAL: + case HB_SCRIPT_SUNUWAR: + case HB_SCRIPT_TODHRI: + case HB_SCRIPT_TULU_TIGALARI: + /* If the designer designed the font for the 'DFLT' script, * (or we ended up arbitrarily pick 'latn'), use the default shaper. * Otherwise, use the specific shaper. * Note that for some simple scripts, there may not be *any* * GSUB/GPOS needed, so there may be no scripts found! */ - if (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T') || - planner->map.chosen_script[0] == HB_TAG ('l','a','t','n')) + if (gsub_script == HB_TAG ('D','F','L','T') || + gsub_script == HB_TAG ('l','a','t','n')) return &_hb_ot_shaper_default; else return &_hb_ot_shaper_use; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-stat-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-stat-table.hh index 7f6b52d797754..10165f57b7556 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-stat-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-stat-table.hh @@ -63,8 +63,9 @@ static bool axis_value_is_outside_axis_range (hb_tag_t axis_tag, float axis_valu if (!user_axes_location->has (axis_tag)) return false; + double axis_value_double = static_cast(axis_value); Triple axis_range = user_axes_location->get (axis_tag); - return (axis_value < axis_range.minimum || axis_value > axis_range.maximum); + return (axis_value_double < axis_range.minimum || axis_value_double > axis_range.maximum); } struct StatAxisRecord @@ -327,6 +328,7 @@ struct AxisValueFormat4 { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && + hb_barrier () && axisValues.sanitize (c, axisCount))); } @@ -348,15 +350,15 @@ struct AxisValueFormat4 struct AxisValue { - bool get_value (unsigned int axis_index) const + float get_value (unsigned int axis_index) const { switch (u.format) { - case 1: return u.format1.get_value (); - case 2: return u.format2.get_value (); - case 3: return u.format3.get_value (); - case 4: return u.format4.get_axis_record (axis_index).get_value (); - default:return 0; + case 1: hb_barrier (); return u.format1.get_value (); + case 2: hb_barrier (); return u.format2.get_value (); + case 3: hb_barrier (); return u.format3.get_value (); + case 4: hb_barrier (); return u.format4.get_axis_record (axis_index).get_value (); + default:return 0.f; } } @@ -364,9 +366,9 @@ struct AxisValue { switch (u.format) { - case 1: return u.format1.get_axis_index (); - case 2: return u.format2.get_axis_index (); - case 3: return u.format3.get_axis_index (); + case 1: hb_barrier (); return u.format1.get_axis_index (); + case 2: hb_barrier (); return u.format2.get_axis_index (); + case 3: hb_barrier (); return u.format3.get_axis_index (); /* case 4: Makes more sense for variable fonts which are handled by fvar in hb-style */ default:return -1; } @@ -376,10 +378,10 @@ struct AxisValue { switch (u.format) { - case 1: return u.format1.get_value_name_id (); - case 2: return u.format2.get_value_name_id (); - case 3: return u.format3.get_value_name_id (); - case 4: return u.format4.get_value_name_id (); + case 1: hb_barrier (); return u.format1.get_value_name_id (); + case 2: hb_barrier (); return u.format2.get_value_name_id (); + case 3: hb_barrier (); return u.format3.get_value_name_id (); + case 4: hb_barrier (); return u.format4.get_value_name_id (); default:return HB_OT_NAME_ID_INVALID; } } @@ -390,10 +392,10 @@ struct AxisValue if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); TRACE_DISPATCH (this, u.format); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); - case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); - case 3: return_trace (c->dispatch (u.format3, std::forward (ds)...)); - case 4: return_trace (c->dispatch (u.format4, std::forward (ds)...)); + case 1: hb_barrier (); return_trace (c->dispatch (u.format1, std::forward (ds)...)); + case 2: hb_barrier (); return_trace (c->dispatch (u.format2, std::forward (ds)...)); + case 3: hb_barrier (); return_trace (c->dispatch (u.format3, std::forward (ds)...)); + case 4: hb_barrier (); return_trace (c->dispatch (u.format4, std::forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -403,10 +405,10 @@ struct AxisValue { switch (u.format) { - case 1: return u.format1.keep_axis_value (axis_records, user_axes_location); - case 2: return u.format2.keep_axis_value (axis_records, user_axes_location); - case 3: return u.format3.keep_axis_value (axis_records, user_axes_location); - case 4: return u.format4.keep_axis_value (axis_records, user_axes_location); + case 1: hb_barrier (); return u.format1.keep_axis_value (axis_records, user_axes_location); + case 2: hb_barrier (); return u.format2.keep_axis_value (axis_records, user_axes_location); + case 3: hb_barrier (); return u.format3.keep_axis_value (axis_records, user_axes_location); + case 4: hb_barrier (); return u.format4.keep_axis_value (axis_records, user_axes_location); default:return false; } } @@ -416,13 +418,14 @@ struct AxisValue TRACE_SANITIZE (this); if (unlikely (!c->check_struct (this))) return_trace (false); + hb_barrier (); switch (u.format) { - case 1: return_trace (u.format1.sanitize (c)); - case 2: return_trace (u.format2.sanitize (c)); - case 3: return_trace (u.format3.sanitize (c)); - case 4: return_trace (u.format4.sanitize (c)); + case 1: hb_barrier (); return_trace (u.format1.sanitize (c)); + case 2: hb_barrier (); return_trace (u.format2.sanitize (c)); + case 3: hb_barrier (); return_trace (u.format3.sanitize (c)); + case 4: hb_barrier (); return_trace (u.format4.sanitize (c)); default:return_trace (true); } } @@ -483,7 +486,7 @@ struct STAT hb_array_t> axis_values = get_axis_value_offsets (); for (unsigned int i = 0; i < axis_values.length; i++) { - const AxisValue& axis_value = this+axis_values[i]; + const AxisValue& axis_value = this+offsetToAxisValueOffsets+axis_values[i]; if (axis_value.get_axis_index () == axis_index) { if (value) @@ -560,6 +563,7 @@ struct STAT { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && + hb_barrier () && version.major == 1 && version.minor > 0 && designAxesOffset.sanitize (c, this, designAxisCount) && diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-tag-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-tag-table.hh index 8ee42e638e021..50ddf5f696725 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-tag-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-tag-table.hh @@ -6,8 +6,8 @@ * * on files with these headers: * - * - * File-Date: 2023-08-02 + * + * File-Date: 2025-01-21 */ #ifndef HB_OT_TAG_TABLE_HH @@ -26,12 +26,12 @@ static const LangTag ot_languages2[] = { {HB_TAG('a','y',' ',' '), HB_TAG('A','Y','M',' ')}, /* Aymara [macrolanguage] */ {HB_TAG('a','z',' ',' '), HB_TAG('A','Z','E',' ')}, /* Azerbaijani [macrolanguage] */ {HB_TAG('b','a',' ',' '), HB_TAG('B','S','H',' ')}, /* Bashkir */ - {HB_TAG('b','e',' ',' '), HB_TAG('B','E','L',' ')}, /* Belarusian -> Belarussian */ + {HB_TAG('b','e',' ',' '), HB_TAG('B','E','L',' ')}, /* Belarusian */ {HB_TAG('b','g',' ',' '), HB_TAG('B','G','R',' ')}, /* Bulgarian */ {HB_TAG('b','i',' ',' '), HB_TAG('B','I','S',' ')}, /* Bislama */ {HB_TAG('b','i',' ',' '), HB_TAG('C','P','P',' ')}, /* Bislama -> Creoles */ {HB_TAG('b','m',' ',' '), HB_TAG('B','M','B',' ')}, /* Bambara (Bamanankan) */ - {HB_TAG('b','n',' ',' '), HB_TAG('B','E','N',' ')}, /* Bengali */ + {HB_TAG('b','n',' ',' '), HB_TAG('B','E','N',' ')}, /* Bangla */ {HB_TAG('b','o',' ',' '), HB_TAG('T','I','B',' ')}, /* Tibetan */ {HB_TAG('b','r',' ',' '), HB_TAG('B','R','E',' ')}, /* Breton */ {HB_TAG('b','s',' ',' '), HB_TAG('B','O','S',' ')}, /* Bosnian */ @@ -64,7 +64,8 @@ static const LangTag ot_languages2[] = { {HB_TAG('f','r',' ',' '), HB_TAG('F','R','A',' ')}, /* French */ {HB_TAG('f','y',' ',' '), HB_TAG('F','R','I',' ')}, /* Western Frisian -> Frisian */ {HB_TAG('g','a',' ',' '), HB_TAG('I','R','I',' ')}, /* Irish */ - {HB_TAG('g','d',' ',' '), HB_TAG('G','A','E',' ')}, /* Scottish Gaelic (Gaelic) */ + {HB_TAG('g','a',' ',' '), HB_TAG('I','R','T',' ')}, /* Irish -> Irish Traditional */ + {HB_TAG('g','d',' ',' '), HB_TAG('G','A','E',' ')}, /* Scottish Gaelic */ {HB_TAG('g','l',' ',' '), HB_TAG('G','A','L',' ')}, /* Galician */ {HB_TAG('g','n',' ',' '), HB_TAG('G','U','A',' ')}, /* Guarani [macrolanguage] */ {HB_TAG('g','u',' ',' '), HB_TAG('G','U','J',' ')}, /* Gujarati */ @@ -153,7 +154,7 @@ static const LangTag ot_languages2[] = { {HB_TAG('o','c',' ',' '), HB_TAG('O','C','I',' ')}, /* Occitan (post 1500) */ {HB_TAG('o','j',' ',' '), HB_TAG('O','J','B',' ')}, /* Ojibwa [macrolanguage] -> Ojibway */ {HB_TAG('o','m',' ',' '), HB_TAG('O','R','O',' ')}, /* Oromo [macrolanguage] */ - {HB_TAG('o','r',' ',' '), HB_TAG('O','R','I',' ')}, /* Odia (formerly Oriya) [macrolanguage] */ + {HB_TAG('o','r',' ',' '), HB_TAG('O','R','I',' ')}, /* Odia [macrolanguage] */ {HB_TAG('o','s',' ',' '), HB_TAG('O','S','S',' ')}, /* Ossetian */ {HB_TAG('p','a',' ',' '), HB_TAG('P','A','N',' ')}, /* Punjabi */ {HB_TAG('p','i',' ',' '), HB_TAG('P','A','L',' ')}, /* Pali */ @@ -166,7 +167,7 @@ static const LangTag ot_languages2[] = { {HB_TAG('r','o',' ',' '), HB_TAG('R','O','M',' ')}, /* Romanian */ {HB_TAG('r','u',' ',' '), HB_TAG('R','U','S',' ')}, /* Russian */ {HB_TAG('r','w',' ',' '), HB_TAG('R','U','A',' ')}, /* Kinyarwanda */ - {HB_TAG('s','a',' ',' '), HB_TAG('S','A','N',' ')}, /* Sanskrit */ + {HB_TAG('s','a',' ',' '), HB_TAG('S','A','N',' ')}, /* Sanskrit [macrolanguage] */ {HB_TAG('s','c',' ',' '), HB_TAG('S','R','D',' ')}, /* Sardinian [macrolanguage] */ {HB_TAG('s','d',' ',' '), HB_TAG('S','N','D',' ')}, /* Sindhi */ {HB_TAG('s','e',' ',' '), HB_TAG('N','S','M',' ')}, /* Northern Sami */ @@ -223,6 +224,7 @@ static const LangTag ot_languages2[] = { static const LangTag ot_languages3[] = { {HB_TAG('a','a','e',' '), HB_TAG('S','Q','I',' ')}, /* Arbëreshë Albanian -> Albanian */ {HB_TAG('a','a','o',' '), HB_TAG('A','R','A',' ')}, /* Algerian Saharan Arabic -> Arabic */ +/*{HB_TAG('a','a','q',' '), HB_TAG('A','A','Q',' ')},*/ /* Eastern Abnaki -> Eastern Abenaki */ {HB_TAG('a','a','t',' '), HB_TAG('S','Q','I',' ')}, /* Arvanitika Albanian -> Albanian */ {HB_TAG('a','b','a',' '), HB_TAG_NONE }, /* Abé != Abaza */ {HB_TAG('a','b','h',' '), HB_TAG('A','R','A',' ')}, /* Tajiki Arabic -> Arabic */ @@ -238,6 +240,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('a','c','r',' '), HB_TAG('M','Y','N',' ')}, /* Achi -> Mayan */ {HB_TAG('a','c','w',' '), HB_TAG('A','R','A',' ')}, /* Hijazi Arabic -> Arabic */ {HB_TAG('a','c','x',' '), HB_TAG('A','R','A',' ')}, /* Omani Arabic -> Arabic */ + {HB_TAG('a','c','y',' '), HB_TAG('A','C','Y',' ')}, /* Cypriot Arabic */ {HB_TAG('a','c','y',' '), HB_TAG('A','R','A',' ')}, /* Cypriot Arabic -> Arabic */ {HB_TAG('a','d','a',' '), HB_TAG('D','N','G',' ')}, /* Adangme -> Dangme */ {HB_TAG('a','d','f',' '), HB_TAG('A','R','A',' ')}, /* Dhofari Arabic -> Arabic */ @@ -288,6 +291,7 @@ static const LangTag ot_languages3[] = { /*{HB_TAG('a','s','t',' '), HB_TAG('A','S','T',' ')},*/ /* Asturian */ /*{HB_TAG('a','t','h',' '), HB_TAG('A','T','H',' ')},*/ /* Athapascan [collection] -> Athapaskan */ {HB_TAG('a','t','j',' '), HB_TAG('R','C','R',' ')}, /* Atikamekw -> R-Cree */ +/*{HB_TAG('a','t','s',' '), HB_TAG('A','T','S',' ')},*/ /* Gros Ventre (Atsina) */ {HB_TAG('a','t','v',' '), HB_TAG('A','L','T',' ')}, /* Northern Altai -> Altai */ {HB_TAG('a','u','j',' '), HB_TAG('B','B','R',' ')}, /* Awjilah -> Berber */ {HB_TAG('a','u','z',' '), HB_TAG('A','R','A',' ')}, /* Uzbeki Arabic -> Arabic */ @@ -326,6 +330,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('b','c','l',' '), HB_TAG('B','I','K',' ')}, /* Central Bikol -> Bikol */ {HB_TAG('b','c','q',' '), HB_TAG('B','C','H',' ')}, /* Bench */ {HB_TAG('b','c','r',' '), HB_TAG('A','T','H',' ')}, /* Babine -> Athapaskan */ +/*{HB_TAG('b','d','c',' '), HB_TAG('B','D','C',' ')},*/ /* Emberá-Baudó */ /*{HB_TAG('b','d','y',' '), HB_TAG('B','D','Y',' ')},*/ /* Bandjalang */ {HB_TAG('b','e','a',' '), HB_TAG('A','T','H',' ')}, /* Beaver -> Athapaskan */ {HB_TAG('b','e','b',' '), HB_TAG('B','T','I',' ')}, /* Bebele -> Beti */ @@ -421,6 +426,8 @@ static const LangTag ot_languages3[] = { {HB_TAG('c','a','f',' '), HB_TAG('A','T','H',' ')}, /* Southern Carrier -> Athapaskan */ {HB_TAG('c','a','k',' '), HB_TAG('C','A','K',' ')}, /* Kaqchikel */ {HB_TAG('c','a','k',' '), HB_TAG('M','Y','N',' ')}, /* Kaqchikel -> Mayan */ +/*{HB_TAG('c','a','y',' '), HB_TAG('C','A','Y',' ')},*/ /* Cayuga */ +/*{HB_TAG('c','b','g',' '), HB_TAG('C','B','G',' ')},*/ /* Chimila */ {HB_TAG('c','b','k',' '), HB_TAG('C','B','K',' ')}, /* Chavacano -> Zamboanga Chavacano */ {HB_TAG('c','b','k',' '), HB_TAG('C','P','P',' ')}, /* Chavacano -> Creoles */ {HB_TAG('c','b','l',' '), HB_TAG('Q','I','N',' ')}, /* Bualkhaw Chin -> Chin */ @@ -465,7 +472,9 @@ static const LangTag ot_languages3[] = { {HB_TAG('c','l','d',' '), HB_TAG('S','Y','R',' ')}, /* Chaldean Neo-Aramaic -> Syriac */ {HB_TAG('c','l','e',' '), HB_TAG('C','C','H','N')}, /* Lealao Chinantec -> Chinantec */ {HB_TAG('c','l','j',' '), HB_TAG('Q','I','N',' ')}, /* Laitu Chin -> Chin */ + {HB_TAG('c','l','s',' '), HB_TAG('S','A','N',' ')}, /* Classical Sanskrit -> Sanskrit */ {HB_TAG('c','l','t',' '), HB_TAG('Q','I','N',' ')}, /* Lautu Chin -> Chin */ +/*{HB_TAG('c','m','i',' '), HB_TAG('C','M','I',' ')},*/ /* Emberá-Chamí */ {HB_TAG('c','m','n',' '), HB_TAG('Z','H','S',' ')}, /* Mandarin Chinese -> Chinese, Simplified */ {HB_TAG('c','m','r',' '), HB_TAG('Q','I','N',' ')}, /* Mro-Khimi Chin -> Chin */ {HB_TAG('c','n','b',' '), HB_TAG('Q','I','N',' ')}, /* Chinbon Chin -> Chin */ @@ -479,6 +488,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('c','n','w',' '), HB_TAG('Q','I','N',' ')}, /* Ngawn Chin -> Chin */ {HB_TAG('c','o','a',' '), HB_TAG('M','L','Y',' ')}, /* Cocos Islands Malay -> Malay */ {HB_TAG('c','o','b',' '), HB_TAG('M','Y','N',' ')}, /* Chicomuceltec -> Mayan */ +/*{HB_TAG('c','o','o',' '), HB_TAG('C','O','O',' ')},*/ /* Comox */ /*{HB_TAG('c','o','p',' '), HB_TAG('C','O','P',' ')},*/ /* Coptic */ {HB_TAG('c','o','q',' '), HB_TAG('A','T','H',' ')}, /* Coquille -> Athapaskan */ {HB_TAG('c','p','a',' '), HB_TAG('C','C','H','N')}, /* Palantla Chinantec -> Chinantec */ @@ -528,6 +538,7 @@ static const LangTag ot_languages3[] = { /*{HB_TAG('c','t','g',' '), HB_TAG('C','T','G',' ')},*/ /* Chittagonian */ {HB_TAG('c','t','h',' '), HB_TAG('Q','I','N',' ')}, /* Thaiphum Chin -> Chin */ {HB_TAG('c','t','l',' '), HB_TAG('C','C','H','N')}, /* Tlacoatzintepec Chinantec -> Chinantec */ +/*{HB_TAG('c','t','o',' '), HB_TAG('C','T','O',' ')},*/ /* Emberá-Catío */ {HB_TAG('c','t','s',' '), HB_TAG('B','I','K',' ')}, /* Northern Catanduanes Bikol -> Bikol */ /*{HB_TAG('c','t','t',' '), HB_TAG('C','T','T',' ')},*/ /* Wayanad Chetti */ {HB_TAG('c','t','u',' '), HB_TAG('M','Y','N',' ')}, /* Chol -> Mayan */ @@ -551,7 +562,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('d','e','p',' '), HB_TAG('C','P','P',' ')}, /* Pidgin Delaware -> Creoles */ {HB_TAG('d','g','o',' '), HB_TAG('D','G','O',' ')}, /* Dogri (individual language) */ {HB_TAG('d','g','o',' '), HB_TAG('D','G','R',' ')}, /* Dogri (macrolanguage) */ - {HB_TAG('d','g','r',' '), HB_TAG('A','T','H',' ')}, /* Dogrib -> Athapaskan */ + {HB_TAG('d','g','r',' '), HB_TAG('A','T','H',' ')}, /* Tlicho -> Athapaskan */ {HB_TAG('d','h','d',' '), HB_TAG('M','A','W',' ')}, /* Dhundari -> Marwari */ /*{HB_TAG('d','h','g',' '), HB_TAG('D','H','G',' ')},*/ /* Dhangu */ {HB_TAG('d','h','v',' '), HB_TAG_NONE }, /* Dehu != Divehi (Dhivehi, Maldivian) (deprecated) */ @@ -590,6 +601,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('e','k','y',' '), HB_TAG('K','R','N',' ')}, /* Eastern Kayah -> Karen */ {HB_TAG('e','m','k',' '), HB_TAG('E','M','K',' ')}, /* Eastern Maninkakan */ {HB_TAG('e','m','k',' '), HB_TAG('M','N','K',' ')}, /* Eastern Maninkakan -> Maninka */ +/*{HB_TAG('e','m','p',' '), HB_TAG('E','M','P',' ')},*/ /* Northern Emberá */ {HB_TAG('e','m','y',' '), HB_TAG('M','Y','N',' ')}, /* Epigraphic Mayan -> Mayan */ {HB_TAG('e','n','b',' '), HB_TAG('K','A','L',' ')}, /* Markweeta -> Kalenjin */ {HB_TAG('e','n','f',' '), HB_TAG('F','N','E',' ')}, /* Forest Enets */ @@ -637,7 +649,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('g','a','a',' '), HB_TAG('G','A','D',' ')}, /* Ga */ {HB_TAG('g','a','c',' '), HB_TAG('C','P','P',' ')}, /* Mixed Great Andamanese -> Creoles */ {HB_TAG('g','a','d',' '), HB_TAG_NONE }, /* Gaddang != Ga */ - {HB_TAG('g','a','e',' '), HB_TAG_NONE }, /* Guarequena != Scottish Gaelic (Gaelic) */ + {HB_TAG('g','a','e',' '), HB_TAG_NONE }, /* Guarequena != Scottish Gaelic */ /*{HB_TAG('g','a','g',' '), HB_TAG('G','A','G',' ')},*/ /* Gagauz */ {HB_TAG('g','a','l',' '), HB_TAG_NONE }, /* Galolen != Galician */ {HB_TAG('g','a','n',' '), HB_TAG('Z','H','S',' ')}, /* Gan Chinese -> Chinese, Simplified */ @@ -654,6 +666,7 @@ static const LangTag ot_languages3[] = { /*{HB_TAG('g','e','z',' '), HB_TAG('G','E','Z',' ')},*/ /* Geez */ {HB_TAG('g','g','o',' '), HB_TAG('G','O','N',' ')}, /* Southern Gondi (retired code) -> Gondi */ {HB_TAG('g','h','a',' '), HB_TAG('B','B','R',' ')}, /* Ghadamès -> Berber */ + {HB_TAG('g','h','c',' '), HB_TAG('I','R','T',' ')}, /* Hiberno-Scottish Gaelic -> Irish Traditional */ {HB_TAG('g','h','k',' '), HB_TAG('K','R','N',' ')}, /* Geko Karen -> Karen */ {HB_TAG('g','h','o',' '), HB_TAG('B','B','R',' ')}, /* Ghomara -> Berber */ {HB_TAG('g','i','b',' '), HB_TAG('C','P','P',' ')}, /* Gibanawa -> Creoles */ @@ -732,6 +745,7 @@ static const LangTag ot_languages3[] = { /*{HB_TAG('h','n','d',' '), HB_TAG('H','N','D',' ')},*/ /* Southern Hindko -> Hindko */ {HB_TAG('h','n','e',' '), HB_TAG('C','H','H',' ')}, /* Chhattisgarhi -> Chattisgarhi */ {HB_TAG('h','n','j',' '), HB_TAG('H','M','N',' ')}, /* Hmong Njua -> Hmong */ + {HB_TAG('h','n','m',' '), HB_TAG('Z','H','S',' ')}, /* Hainanese -> Chinese, Simplified */ {HB_TAG('h','n','o',' '), HB_TAG('H','N','D',' ')}, /* Northern Hindko -> Hindko */ {HB_TAG('h','o','c',' '), HB_TAG('H','O',' ',' ')}, /* Ho */ {HB_TAG('h','o','i',' '), HB_TAG('A','T','H',' ')}, /* Holikachuk -> Athapaskan */ @@ -743,6 +757,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('h','s','n',' '), HB_TAG('Z','H','S',' ')}, /* Xiang Chinese -> Chinese, Simplified */ {HB_TAG('h','u','j',' '), HB_TAG('H','M','N',' ')}, /* Northern Guiyang Hmong -> Hmong */ {HB_TAG('h','u','p',' '), HB_TAG('A','T','H',' ')}, /* Hupa -> Athapaskan */ +/*{HB_TAG('h','u','r',' '), HB_TAG('H','U','R',' ')},*/ /* Halkomelem */ {HB_TAG('h','u','s',' '), HB_TAG('M','Y','N',' ')}, /* Huastec -> Mayan */ {HB_TAG('h','w','c',' '), HB_TAG('C','P','P',' ')}, /* Hawai'i Creole English -> Creoles */ {HB_TAG('h','y','w',' '), HB_TAG('H','Y','E',' ')}, /* Western Armenian -> Armenian */ @@ -780,6 +795,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('j','b','n',' '), HB_TAG('B','B','R',' ')}, /* Nafusi -> Berber */ /*{HB_TAG('j','b','o',' '), HB_TAG('J','B','O',' ')},*/ /* Lojban */ /*{HB_TAG('j','c','t',' '), HB_TAG('J','C','T',' ')},*/ /* Krymchak */ +/*{HB_TAG('j','d','t',' '), HB_TAG('J','D','T',' ')},*/ /* Judeo-Tat */ {HB_TAG('j','g','o',' '), HB_TAG('B','M','L',' ')}, /* Ngomba -> Bamileke */ {HB_TAG('j','i','i',' '), HB_TAG_NONE }, /* Jiiddu != Yiddish */ {HB_TAG('j','k','m',' '), HB_TAG('K','R','N',' ')}, /* Mobwa Karen -> Karen */ @@ -794,6 +810,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('k','a','m',' '), HB_TAG('K','M','B',' ')}, /* Kamba (Kenya) */ {HB_TAG('k','a','r',' '), HB_TAG('K','R','N',' ')}, /* Karen [collection] */ /*{HB_TAG('k','a','w',' '), HB_TAG('K','A','W',' ')},*/ /* Kawi (Old Javanese) */ +/*{HB_TAG('k','b','c',' '), HB_TAG('K','B','C',' ')},*/ /* Kadiwéu */ {HB_TAG('k','b','d',' '), HB_TAG('K','A','B',' ')}, /* Kabardian */ {HB_TAG('k','b','y',' '), HB_TAG('K','N','R',' ')}, /* Manga Kanuri -> Kanuri */ {HB_TAG('k','c','a',' '), HB_TAG('K','H','K',' ')}, /* Khanty -> Khanty-Kazim */ @@ -814,6 +831,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('k','f','x',' '), HB_TAG('K','U','L',' ')}, /* Kullu Pahari -> Kulvi */ {HB_TAG('k','f','y',' '), HB_TAG('K','M','N',' ')}, /* Kumaoni */ {HB_TAG('k','g','e',' '), HB_TAG_NONE }, /* Komering != Khutsuri Georgian */ +/*{HB_TAG('k','g','f',' '), HB_TAG('K','G','F',' ')},*/ /* Kube */ {HB_TAG('k','h','a',' '), HB_TAG('K','S','I',' ')}, /* Khasi */ {HB_TAG('k','h','b',' '), HB_TAG('X','B','D',' ')}, /* Lü */ {HB_TAG('k','h','k',' '), HB_TAG('M','N','G',' ')}, /* Halh Mongolian -> Mongolian */ @@ -829,6 +847,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('k','j','b',' '), HB_TAG('M','Y','N',' ')}, /* Q'anjob'al -> Mayan */ /*{HB_TAG('k','j','d',' '), HB_TAG('K','J','D',' ')},*/ /* Southern Kiwai */ {HB_TAG('k','j','h',' '), HB_TAG('K','H','A',' ')}, /* Khakas -> Khakass */ +/*{HB_TAG('k','j','j',' '), HB_TAG('K','J','J',' ')},*/ /* Khinalugh -> Khinalug */ {HB_TAG('k','j','p',' '), HB_TAG('K','J','P',' ')}, /* Pwo Eastern Karen -> Eastern Pwo Karen */ {HB_TAG('k','j','p',' '), HB_TAG('K','R','N',' ')}, /* Pwo Eastern Karen -> Karen */ {HB_TAG('k','j','t',' '), HB_TAG('K','R','N',' ')}, /* Phrae Pwo Karen -> Karen */ @@ -838,6 +857,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('k','l','m',' '), HB_TAG_NONE }, /* Migum != Kalmyk */ {HB_TAG('k','l','n',' '), HB_TAG('K','A','L',' ')}, /* Kalenjin [macrolanguage] */ {HB_TAG('k','m','b',' '), HB_TAG('M','B','N',' ')}, /* Kimbundu -> Mbundu */ +/*{HB_TAG('k','m','g',' '), HB_TAG('K','M','G',' ')},*/ /* Kâte */ {HB_TAG('k','m','n',' '), HB_TAG_NONE }, /* Awtuw != Kumaoni */ {HB_TAG('k','m','o',' '), HB_TAG_NONE }, /* Kwoma != Komo */ {HB_TAG('k','m','r',' '), HB_TAG('K','U','R',' ')}, /* Northern Kurdish -> Kurdish */ @@ -881,6 +901,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('k','s','i',' '), HB_TAG_NONE }, /* Krisa != Khasi */ {HB_TAG('k','s','m',' '), HB_TAG_NONE }, /* Kumba != Kildin Sami */ {HB_TAG('k','s','s',' '), HB_TAG('K','I','S',' ')}, /* Southern Kisi -> Kisii */ +/*{HB_TAG('k','s','u',' '), HB_TAG('K','S','U',' ')},*/ /* Khamyang */ {HB_TAG('k','s','w',' '), HB_TAG('K','S','W',' ')}, /* S’gaw Karen */ {HB_TAG('k','s','w',' '), HB_TAG('K','R','N',' ')}, /* S'gaw Karen -> Karen */ {HB_TAG('k','t','b',' '), HB_TAG('K','E','B',' ')}, /* Kambaata -> Kebena */ @@ -894,6 +915,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('k','u','y',' '), HB_TAG_NONE }, /* Kuuku-Ya'u != Kuy */ {HB_TAG('k','v','b',' '), HB_TAG('M','L','Y',' ')}, /* Kubu -> Malay */ {HB_TAG('k','v','l',' '), HB_TAG('K','R','N',' ')}, /* Kayaw -> Karen */ + {HB_TAG('k','v','q',' '), HB_TAG('K','V','Q',' ')}, /* Geba Karen */ {HB_TAG('k','v','q',' '), HB_TAG('K','R','N',' ')}, /* Geba Karen -> Karen */ {HB_TAG('k','v','r',' '), HB_TAG('M','L','Y',' ')}, /* Kerinci -> Malay */ {HB_TAG('k','v','t',' '), HB_TAG('K','R','N',' ')}, /* Lahta Karen -> Karen */ @@ -930,6 +952,7 @@ static const LangTag ot_languages3[] = { /*{HB_TAG('l','i','j',' '), HB_TAG('L','I','J',' ')},*/ /* Ligurian */ {HB_TAG('l','i','r',' '), HB_TAG('C','P','P',' ')}, /* Liberian English -> Creoles */ /*{HB_TAG('l','i','s',' '), HB_TAG('L','I','S',' ')},*/ /* Lisu */ +/*{HB_TAG('l','i','v',' '), HB_TAG('L','I','V',' ')},*/ /* Liv */ {HB_TAG('l','i','w',' '), HB_TAG('M','L','Y',' ')}, /* Col -> Malay */ {HB_TAG('l','i','y',' '), HB_TAG('B','A','D','0')}, /* Banda-Bambari -> Banda */ /*{HB_TAG('l','j','p',' '), HB_TAG('L','J','P',' ')},*/ /* Lampung Api -> Lampung */ @@ -959,9 +982,11 @@ static const LangTag ot_languages3[] = { {HB_TAG('l','t','o',' '), HB_TAG('L','U','H',' ')}, /* Tsotso -> Luyia */ {HB_TAG('l','t','s',' '), HB_TAG('L','U','H',' ')}, /* Tachoni -> Luyia */ /*{HB_TAG('l','u','a',' '), HB_TAG('L','U','A',' ')},*/ /* Luba-Lulua */ + {HB_TAG('l','u','h',' '), HB_TAG('Z','H','S',' ')}, /* Leizhou Chinese -> Chinese, Simplified */ /*{HB_TAG('l','u','o',' '), HB_TAG('L','U','O',' ')},*/ /* Luo (Kenya and Tanzania) */ {HB_TAG('l','u','s',' '), HB_TAG('M','I','Z',' ')}, /* Lushai -> Mizo */ {HB_TAG('l','u','s',' '), HB_TAG('Q','I','N',' ')}, /* Lushai -> Chin */ +/*{HB_TAG('l','u','t',' '), HB_TAG('L','U','T',' ')},*/ /* Lushootseed */ {HB_TAG('l','u','y',' '), HB_TAG('L','U','H',' ')}, /* Luyia [macrolanguage] */ {HB_TAG('l','u','z',' '), HB_TAG('L','R','C',' ')}, /* Southern Luri -> Luri */ {HB_TAG('l','v','i',' '), HB_TAG_NONE }, /* Lavi != Latvian */ @@ -995,12 +1020,14 @@ static const LangTag ot_languages3[] = { {HB_TAG('m','e','n',' '), HB_TAG('M','D','E',' ')}, /* Mende (Sierra Leone) */ {HB_TAG('m','e','o',' '), HB_TAG('M','L','Y',' ')}, /* Kedah Malay -> Malay */ /*{HB_TAG('m','e','r',' '), HB_TAG('M','E','R',' ')},*/ /* Meru */ +/*{HB_TAG('m','e','v',' '), HB_TAG('M','E','V',' ')},*/ /* Mano */ {HB_TAG('m','f','a',' '), HB_TAG('M','F','A',' ')}, /* Pattani Malay */ {HB_TAG('m','f','a',' '), HB_TAG('M','L','Y',' ')}, /* Pattani Malay -> Malay */ {HB_TAG('m','f','b',' '), HB_TAG('M','L','Y',' ')}, /* Bangka -> Malay */ {HB_TAG('m','f','e',' '), HB_TAG('M','F','E',' ')}, /* Morisyen */ {HB_TAG('m','f','e',' '), HB_TAG('C','P','P',' ')}, /* Morisyen -> Creoles */ {HB_TAG('m','f','p',' '), HB_TAG('C','P','P',' ')}, /* Makassar Malay -> Creoles */ + {HB_TAG('m','g','a',' '), HB_TAG('S','G','A',' ')}, /* Middle Irish (900-1200) -> Old Irish */ {HB_TAG('m','h','c',' '), HB_TAG('M','Y','N',' ')}, /* Mocho -> Mayan */ {HB_TAG('m','h','r',' '), HB_TAG('L','M','A',' ')}, /* Eastern Mari -> Low Mari */ {HB_TAG('m','h','v',' '), HB_TAG('A','R','K',' ')}, /* Arakanese (retired code) -> Rakhine */ @@ -1126,6 +1153,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('n','o','d',' '), HB_TAG('N','T','A',' ')}, /* Northern Thai -> Northern Tai */ /*{HB_TAG('n','o','e',' '), HB_TAG('N','O','E',' ')},*/ /* Nimadi */ /*{HB_TAG('n','o','g',' '), HB_TAG('N','O','G',' ')},*/ /* Nogai */ +/*{HB_TAG('n','o','p',' '), HB_TAG('N','O','P',' ')},*/ /* Numanggang */ /*{HB_TAG('n','o','v',' '), HB_TAG('N','O','V',' ')},*/ /* Novial */ {HB_TAG('n','p','i',' '), HB_TAG('N','E','P',' ')}, /* Nepali */ {HB_TAG('n','p','l',' '), HB_TAG('N','A','H',' ')}, /* Southeastern Puebla Nahuatl -> Nahuatl */ @@ -1136,6 +1164,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('n','s','u',' '), HB_TAG('N','A','H',' ')}, /* Sierra Negra Nahuatl -> Nahuatl */ {HB_TAG('n','t','o',' '), HB_TAG_NONE }, /* Ntomba != Esperanto */ {HB_TAG('n','u','e',' '), HB_TAG('B','A','D','0')}, /* Ngundu -> Banda */ +/*{HB_TAG('n','u','k',' '), HB_TAG('N','U','K',' ')},*/ /* Nuu-chah-nulth */ {HB_TAG('n','u','u',' '), HB_TAG('B','A','D','0')}, /* Ngbundu -> Banda */ {HB_TAG('n','u','z',' '), HB_TAG('N','A','H',' ')}, /* Tlamacazapa Nahuatl -> Nahuatl */ {HB_TAG('n','w','e',' '), HB_TAG('B','M','L',' ')}, /* Ngwe -> Bamileke */ @@ -1153,6 +1182,8 @@ static const LangTag ot_languages3[] = { {HB_TAG('o','k','i',' '), HB_TAG('K','A','L',' ')}, /* Okiek -> Kalenjin */ {HB_TAG('o','k','m',' '), HB_TAG('K','O','H',' ')}, /* Middle Korean (10th-16th cent.) -> Korean Old Hangul */ {HB_TAG('o','k','r',' '), HB_TAG('I','J','O',' ')}, /* Kirike -> Ijo */ +/*{HB_TAG('o','n','e',' '), HB_TAG('O','N','E',' ')},*/ /* Oneida */ +/*{HB_TAG('o','n','o',' '), HB_TAG('O','N','O',' ')},*/ /* Onondaga */ {HB_TAG('o','n','x',' '), HB_TAG('C','P','P',' ')}, /* Onin Based Pidgin -> Creoles */ {HB_TAG('o','o','r',' '), HB_TAG('C','P','P',' ')}, /* Oorlams -> Creoles */ {HB_TAG('o','r','c',' '), HB_TAG('O','R','O',' ')}, /* Orma -> Oromo */ @@ -1160,7 +1191,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('o','r','o',' '), HB_TAG_NONE }, /* Orokolo != Oromo */ {HB_TAG('o','r','r',' '), HB_TAG('I','J','O',' ')}, /* Oruma -> Ijo */ {HB_TAG('o','r','s',' '), HB_TAG('M','L','Y',' ')}, /* Orang Seletar -> Malay */ - {HB_TAG('o','r','y',' '), HB_TAG('O','R','I',' ')}, /* Odia (formerly Oriya) */ + {HB_TAG('o','r','y',' '), HB_TAG('O','R','I',' ')}, /* Odia */ {HB_TAG('o','t','w',' '), HB_TAG('O','J','B',' ')}, /* Ottawa -> Ojibway */ {HB_TAG('o','u','a',' '), HB_TAG('B','B','R',' ')}, /* Tagargrent -> Berber */ {HB_TAG('p','a','a',' '), HB_TAG_NONE }, /* Papuan [collection] != Palestinian Aramaic */ @@ -1193,7 +1224,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('p','i','s',' '), HB_TAG('C','P','P',' ')}, /* Pijin -> Creoles */ {HB_TAG('p','k','h',' '), HB_TAG('Q','I','N',' ')}, /* Pankhu -> Chin */ {HB_TAG('p','k','o',' '), HB_TAG('K','A','L',' ')}, /* Pökoot -> Kalenjin */ - {HB_TAG('p','l','g',' '), HB_TAG_NONE }, /* Pilagá != Palaung */ + {HB_TAG('p','l','g',' '), HB_TAG('P','L','G','0')}, /* Pilagá */ {HB_TAG('p','l','k',' '), HB_TAG_NONE }, /* Kohistani Shina != Polish */ {HB_TAG('p','l','l',' '), HB_TAG('P','L','G',' ')}, /* Shwe Palaung -> Palaung */ {HB_TAG('p','l','n',' '), HB_TAG('C','P','P',' ')}, /* Palenquero -> Creoles */ @@ -1353,6 +1384,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('s','d','h',' '), HB_TAG('K','U','R',' ')}, /* Southern Kurdish -> Kurdish */ {HB_TAG('s','d','n',' '), HB_TAG('S','R','D',' ')}, /* Gallurese Sardinian -> Sardinian */ {HB_TAG('s','d','s',' '), HB_TAG('B','B','R',' ')}, /* Sened -> Berber */ +/*{HB_TAG('s','e','e',' '), HB_TAG('S','E','E',' ')},*/ /* Seneca */ {HB_TAG('s','e','h',' '), HB_TAG('S','N','A',' ')}, /* Sena */ {HB_TAG('s','e','k',' '), HB_TAG('A','T','H',' ')}, /* Sekani -> Athapaskan */ /*{HB_TAG('s','e','l',' '), HB_TAG('S','E','L',' ')},*/ /* Selkup */ @@ -1374,9 +1406,13 @@ static const LangTag ot_languages3[] = { /*{HB_TAG('s','i','d',' '), HB_TAG('S','I','D',' ')},*/ /* Sidamo */ {HB_TAG('s','i','g',' '), HB_TAG_NONE }, /* Paasaal != Silte Gurage */ {HB_TAG('s','i','z',' '), HB_TAG('B','B','R',' ')}, /* Siwi -> Berber */ +/*{HB_TAG('s','j','a',' '), HB_TAG('S','J','A',' ')},*/ /* Epena */ + {HB_TAG('s','j','c',' '), HB_TAG('Z','H','S',' ')}, /* Shaojiang Chinese -> Chinese, Simplified */ {HB_TAG('s','j','d',' '), HB_TAG('K','S','M',' ')}, /* Kildin Sami */ +/*{HB_TAG('s','j','e',' '), HB_TAG('S','J','E',' ')},*/ /* Pite Sami */ {HB_TAG('s','j','o',' '), HB_TAG('S','I','B',' ')}, /* Xibe -> Sibe */ {HB_TAG('s','j','s',' '), HB_TAG('B','B','R',' ')}, /* Senhaja De Srair -> Berber */ +/*{HB_TAG('s','j','u',' '), HB_TAG('S','J','U',' ')},*/ /* Ume Sami */ {HB_TAG('s','k','g',' '), HB_TAG('M','L','G',' ')}, /* Sakalava Malagasy -> Malagasy */ {HB_TAG('s','k','r',' '), HB_TAG('S','R','K',' ')}, /* Saraiki */ {HB_TAG('s','k','s',' '), HB_TAG_NONE }, /* Maia != Skolt Sami */ @@ -1395,7 +1431,7 @@ static const LangTag ot_languages3[] = { /*{HB_TAG('s','n','k',' '), HB_TAG('S','N','K',' ')},*/ /* Soninke */ {HB_TAG('s','o','g',' '), HB_TAG_NONE }, /* Sogdian != Sodo Gurage */ /*{HB_TAG('s','o','p',' '), HB_TAG('S','O','P',' ')},*/ /* Songe */ - {HB_TAG('s','p','v',' '), HB_TAG('O','R','I',' ')}, /* Sambalpuri -> Odia (formerly Oriya) */ + {HB_TAG('s','p','v',' '), HB_TAG('O','R','I',' ')}, /* Sambalpuri -> Odia */ {HB_TAG('s','p','y',' '), HB_TAG('K','A','L',' ')}, /* Sabaot -> Kalenjin */ {HB_TAG('s','r','b',' '), HB_TAG_NONE }, /* Sora != Serbian */ {HB_TAG('s','r','c',' '), HB_TAG('S','R','D',' ')}, /* Logudorese Sardinian -> Sardinian */ @@ -1410,6 +1446,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('s','s','m',' '), HB_TAG_NONE }, /* Semnam != Southern Sami */ {HB_TAG('s','t','a',' '), HB_TAG('C','P','P',' ')}, /* Settla -> Creoles */ /*{HB_TAG('s','t','q',' '), HB_TAG('S','T','Q',' ')},*/ /* Saterfriesisch -> Saterland Frisian */ +/*{HB_TAG('s','t','r',' '), HB_TAG('S','T','R',' ')},*/ /* Straits Salish */ {HB_TAG('s','t','v',' '), HB_TAG('S','I','G',' ')}, /* Silt'e -> Silte Gurage */ /*{HB_TAG('s','u','k',' '), HB_TAG('S','U','K',' ')},*/ /* Sukuma */ {HB_TAG('s','u','q',' '), HB_TAG('S','U','R',' ')}, /* Suri */ @@ -1431,10 +1468,12 @@ static const LangTag ot_languages3[] = { {HB_TAG('t','a','a',' '), HB_TAG('A','T','H',' ')}, /* Lower Tanana -> Athapaskan */ /*{HB_TAG('t','a','b',' '), HB_TAG('T','A','B',' ')},*/ /* Tabassaran -> Tabasaran */ {HB_TAG('t','a','j',' '), HB_TAG_NONE }, /* Eastern Tamang != Tajiki */ + {HB_TAG('t','a','q',' '), HB_TAG('T','A','Q',' ')}, /* Tamasheq */ {HB_TAG('t','a','q',' '), HB_TAG('T','M','H',' ')}, /* Tamasheq -> Tamashek */ {HB_TAG('t','a','q',' '), HB_TAG('B','B','R',' ')}, /* Tamasheq -> Berber */ {HB_TAG('t','a','s',' '), HB_TAG('C','P','P',' ')}, /* Tay Boi -> Creoles */ {HB_TAG('t','a','u',' '), HB_TAG('A','T','H',' ')}, /* Upper Tanana -> Athapaskan */ +/*{HB_TAG('t','b','v',' '), HB_TAG('T','B','V',' ')},*/ /* Tobo */ {HB_TAG('t','c','b',' '), HB_TAG('A','T','H',' ')}, /* Tanacross -> Athapaskan */ {HB_TAG('t','c','e',' '), HB_TAG('A','T','H',' ')}, /* Southern Tutchone -> Athapaskan */ {HB_TAG('t','c','h',' '), HB_TAG('C','P','P',' ')}, /* Turks And Caicos Creole English -> Creoles */ @@ -1442,6 +1481,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('t','c','s',' '), HB_TAG('C','P','P',' ')}, /* Torres Strait Creole -> Creoles */ {HB_TAG('t','c','y',' '), HB_TAG('T','U','L',' ')}, /* Tulu */ {HB_TAG('t','c','z',' '), HB_TAG('Q','I','N',' ')}, /* Thado Chin -> Chin */ +/*{HB_TAG('t','d','c',' '), HB_TAG('T','D','C',' ')},*/ /* Emberá-Tadó */ /*{HB_TAG('t','d','d',' '), HB_TAG('T','D','D',' ')},*/ /* Tai Nüa -> Dehong Dai */ {HB_TAG('t','d','x',' '), HB_TAG('M','L','G',' ')}, /* Tandroy-Mahafaly Malagasy -> Malagasy */ {HB_TAG('t','e','c',' '), HB_TAG('K','A','L',' ')}, /* Terik -> Kalenjin */ @@ -1455,9 +1495,12 @@ static const LangTag ot_languages3[] = { {HB_TAG('t','g','r',' '), HB_TAG_NONE }, /* Tareng != Tigre */ {HB_TAG('t','g','x',' '), HB_TAG('A','T','H',' ')}, /* Tagish -> Athapaskan */ {HB_TAG('t','g','y',' '), HB_TAG_NONE }, /* Togoyo != Tigrinya */ +/*{HB_TAG('t','h','p',' '), HB_TAG('T','H','P',' ')},*/ /* Thompson */ {HB_TAG('t','h','t',' '), HB_TAG('A','T','H',' ')}, /* Tahltan -> Athapaskan */ + {HB_TAG('t','h','v',' '), HB_TAG('T','H','V',' ')}, /* Tahaggart Tamahaq */ {HB_TAG('t','h','v',' '), HB_TAG('T','M','H',' ')}, /* Tahaggart Tamahaq -> Tamashek */ {HB_TAG('t','h','v',' '), HB_TAG('B','B','R',' ')}, /* Tahaggart Tamahaq -> Berber */ + {HB_TAG('t','h','z',' '), HB_TAG('T','H','Z',' ')}, /* Tayart Tamajeq */ {HB_TAG('t','h','z',' '), HB_TAG('T','M','H',' ')}, /* Tayart Tamajeq -> Tamashek */ {HB_TAG('t','h','z',' '), HB_TAG('B','B','R',' ')}, /* Tayart Tamajeq -> Berber */ {HB_TAG('t','i','a',' '), HB_TAG('B','B','R',' ')}, /* Tidikelt Tamazight -> Berber */ @@ -1468,6 +1511,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('t','k','g',' '), HB_TAG('M','L','G',' ')}, /* Tesaka Malagasy -> Malagasy */ {HB_TAG('t','k','m',' '), HB_TAG_NONE }, /* Takelma != Turkmen */ /*{HB_TAG('t','l','i',' '), HB_TAG('T','L','I',' ')},*/ /* Tlingit */ +/*{HB_TAG('t','l','y',' '), HB_TAG('T','L','Y',' ')},*/ /* Talysh */ {HB_TAG('t','m','g',' '), HB_TAG('C','P','P',' ')}, /* Ternateño -> Creoles */ {HB_TAG('t','m','h',' '), HB_TAG('T','M','H',' ')}, /* Tamashek [macrolanguage] */ {HB_TAG('t','m','h',' '), HB_TAG('B','B','R',' ')}, /* Tamashek [macrolanguage] -> Berber */ @@ -1493,11 +1537,13 @@ static const LangTag ot_languages3[] = { /*{HB_TAG('t','s','j',' '), HB_TAG('T','S','J',' ')},*/ /* Tshangla */ {HB_TAG('t','t','c',' '), HB_TAG('M','Y','N',' ')}, /* Tektiteko -> Mayan */ {HB_TAG('t','t','m',' '), HB_TAG('A','T','H',' ')}, /* Northern Tutchone -> Athapaskan */ + {HB_TAG('t','t','q',' '), HB_TAG('T','T','Q',' ')}, /* Tawallammat Tamajaq */ {HB_TAG('t','t','q',' '), HB_TAG('T','M','H',' ')}, /* Tawallammat Tamajaq -> Tamashek */ {HB_TAG('t','t','q',' '), HB_TAG('B','B','R',' ')}, /* Tawallammat Tamajaq -> Berber */ {HB_TAG('t','u','a',' '), HB_TAG_NONE }, /* Wiarumus != Turoyo Aramaic */ {HB_TAG('t','u','l',' '), HB_TAG_NONE }, /* Tula != Tulu */ /*{HB_TAG('t','u','m',' '), HB_TAG('T','U','M',' ')},*/ /* Tumbuka */ +/*{HB_TAG('t','u','s',' '), HB_TAG('T','U','S',' ')},*/ /* Tuscarora */ {HB_TAG('t','u','u',' '), HB_TAG('A','T','H',' ')}, /* Tututni -> Athapaskan */ {HB_TAG('t','u','v',' '), HB_TAG_NONE }, /* Turkana != Tuvin */ {HB_TAG('t','u','y',' '), HB_TAG('K','A','L',' ')}, /* Tugen -> Kalenjin */ @@ -1514,6 +1560,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('t','z','o',' '), HB_TAG('T','Z','O',' ')}, /* Tzotzil */ {HB_TAG('t','z','o',' '), HB_TAG('M','Y','N',' ')}, /* Tzotzil -> Mayan */ {HB_TAG('u','b','l',' '), HB_TAG('B','I','K',' ')}, /* Buhi'non Bikol -> Bikol */ +/*{HB_TAG('u','d','i',' '), HB_TAG('U','D','I',' ')},*/ /* Udi */ /*{HB_TAG('u','d','m',' '), HB_TAG('U','D','M',' ')},*/ /* Udmurt */ {HB_TAG('u','k','i',' '), HB_TAG('K','U','I',' ')}, /* Kui (India) */ {HB_TAG('u','l','n',' '), HB_TAG('C','P','P',' ')}, /* Unserdeutsch -> Creoles */ @@ -1532,13 +1579,17 @@ static const LangTag ot_languages3[] = { {HB_TAG('v','k','t',' '), HB_TAG('M','L','Y',' ')}, /* Tenggarong Kutai Malay -> Malay */ {HB_TAG('v','l','s',' '), HB_TAG('F','L','E',' ')}, /* Vlaams -> Dutch (Flemish) */ {HB_TAG('v','m','w',' '), HB_TAG('M','A','K',' ')}, /* Makhuwa */ -/*{HB_TAG('v','r','o',' '), HB_TAG('V','R','O',' ')},*/ /* Võro */ + {HB_TAG('v','r','o',' '), HB_TAG('V','R','O',' ')}, /* Võro */ + {HB_TAG('v','r','o',' '), HB_TAG('E','T','I',' ')}, /* Võro -> Estonian */ + {HB_TAG('v','s','n',' '), HB_TAG('S','A','N',' ')}, /* Vedic Sanskrit -> Sanskrit */ {HB_TAG('w','a','g',' '), HB_TAG_NONE }, /* Wa'ema != Wagdi */ /*{HB_TAG('w','a','r',' '), HB_TAG('W','A','R',' ')},*/ /* Waray (Philippines) -> Waray-Waray */ +/*{HB_TAG('w','b','l',' '), HB_TAG('W','B','L',' ')},*/ /* Wakhi */ {HB_TAG('w','b','m',' '), HB_TAG('W','A',' ',' ')}, /* Wa */ {HB_TAG('w','b','r',' '), HB_TAG('W','A','G',' ')}, /* Wagdi */ {HB_TAG('w','b','r',' '), HB_TAG('R','A','J',' ')}, /* Wagdi -> Rajasthani */ /*{HB_TAG('w','c','i',' '), HB_TAG('W','C','I',' ')},*/ /* Waci Gbe */ +/*{HB_TAG('w','d','t',' '), HB_TAG('W','D','T',' ')},*/ /* Wendat */ {HB_TAG('w','e','a',' '), HB_TAG('K','R','N',' ')}, /* Wewaw -> Karen */ {HB_TAG('w','e','s',' '), HB_TAG('C','P','P',' ')}, /* Cameroon Pidgin -> Creoles */ {HB_TAG('w','e','u',' '), HB_TAG('Q','I','N',' ')}, /* Rawngtu Chin -> Chin */ @@ -1550,6 +1601,9 @@ static const LangTag ot_languages3[] = { {HB_TAG('w','s','g',' '), HB_TAG('G','O','N',' ')}, /* Adilabad Gondi -> Gondi */ /*{HB_TAG('w','t','m',' '), HB_TAG('W','T','M',' ')},*/ /* Mewati */ {HB_TAG('w','u','u',' '), HB_TAG('Z','H','S',' ')}, /* Wu Chinese -> Chinese, Simplified */ + {HB_TAG('w','y','a',' '), HB_TAG('W','D','T',' ')}, /* Wyandot (retired code) -> Wendat */ + {HB_TAG('w','y','a',' '), HB_TAG('W','Y','N',' ')}, /* Wyandot (retired code) */ +/*{HB_TAG('w','y','n',' '), HB_TAG('W','Y','N',' ')},*/ /* Wyandot */ {HB_TAG('x','a','l',' '), HB_TAG('K','L','M',' ')}, /* Kalmyk */ {HB_TAG('x','a','l',' '), HB_TAG('T','O','D',' ')}, /* Kalmyk -> Todo */ {HB_TAG('x','a','n',' '), HB_TAG('S','E','K',' ')}, /* Xamtanga -> Sekota */ @@ -1582,7 +1636,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('y','b','a',' '), HB_TAG_NONE }, /* Yala != Yoruba */ {HB_TAG('y','b','b',' '), HB_TAG('B','M','L',' ')}, /* Yemba -> Bamileke */ {HB_TAG('y','b','d',' '), HB_TAG('A','R','K',' ')}, /* Yangbye (retired code) -> Rakhine */ - {HB_TAG('y','c','r',' '), HB_TAG_NONE }, /* Yilan Creole != Y-Cree */ + {HB_TAG('y','c','r',' '), HB_TAG('C','P','P',' ')}, /* Yilan Creole -> Creoles */ {HB_TAG('y','d','d',' '), HB_TAG('J','I','I',' ')}, /* Eastern Yiddish -> Yiddish */ /*{HB_TAG('y','g','p',' '), HB_TAG('Y','G','P',' ')},*/ /* Gepo */ {HB_TAG('y','i','h',' '), HB_TAG('J','I','I',' ')}, /* Western Yiddish -> Yiddish */ @@ -1591,6 +1645,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('y','o','s',' '), HB_TAG('Q','I','N',' ')}, /* Yos (retired code) -> Chin */ {HB_TAG('y','u','a',' '), HB_TAG('M','Y','N',' ')}, /* Yucateco -> Mayan */ {HB_TAG('y','u','e',' '), HB_TAG('Z','H','H',' ')}, /* Yue Chinese -> Chinese, Traditional, Hong Kong SAR */ +/*{HB_TAG('y','u','f',' '), HB_TAG('Y','U','F',' ')},*/ /* Havasupai-Walapai-Yavapai */ /*{HB_TAG('y','w','q',' '), HB_TAG('Y','W','Q',' ')},*/ /* Wuding-Luquan Yi */ {HB_TAG('z','c','h',' '), HB_TAG('Z','H','A',' ')}, /* Central Hongshuihe Zhuang -> Zhuang */ {HB_TAG('z','d','j',' '), HB_TAG('C','M','R',' ')}, /* Ngazidja Comorian -> Comorian */ @@ -2335,6 +2390,26 @@ out: *count = i; return true; } + if (lang_matches (&lang_str[1], limit, "nm-hant-hk", 10)) + { + /* Hainanese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "nm-hant-mo", 10)) + { + /* Hainanese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } if (lang_matches (&lang_str[1], limit, "sn-hant-hk", 10)) { /* Xiang Chinese; Han (Traditional variant); Hong Kong */ @@ -2369,6 +2444,20 @@ out: *count = 1; return true; } + if (lang_matches (&lang_str[1], limit, "nm-hans", 7)) + { + /* Hainanese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "nm-hant", 7)) + { + /* Hainanese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } if (lang_matches (&lang_str[1], limit, "sn-hans", 7)) { /* Xiang Chinese; Han (Simplified variant) */ @@ -2413,6 +2502,36 @@ out: *count = 1; return true; } + if (0 == strncmp (&lang_str[1], "nm-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Hainanese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "nm-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Hainanese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "nm-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Hainanese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } if (0 == strncmp (&lang_str[1], "sn-", 3) && subtag_matches (lang_str, limit, "-hk", 3)) { @@ -2474,6 +2593,40 @@ out: } break; case 'l': + if (lang_matches (&lang_str[1], limit, "uh-hant-hk", 10)) + { + /* Leizhou Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "uh-hant-mo", 10)) + { + /* Leizhou Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "uh-hans", 7)) + { + /* Leizhou Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "uh-hant", 7)) + { + /* Leizhou Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } if (lang_matches (&lang_str[1], limit, "zh-hans", 7)) { /* Literary Chinese; Han (Simplified variant) */ @@ -2481,6 +2634,36 @@ out: *count = 1; return true; } + if (0 == strncmp (&lang_str[1], "uh-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Leizhou Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "uh-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Leizhou Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "uh-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Leizhou Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } break; case 'm': if (lang_matches (&lang_str[1], limit, "np-hant-hk", 10)) @@ -2652,6 +2835,72 @@ out: return true; } break; + case 's': + if (lang_matches (&lang_str[1], limit, "jc-hant-hk", 10)) + { + /* Shaojiang Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "jc-hant-mo", 10)) + { + /* Shaojiang Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "jc-hans", 7)) + { + /* Shaojiang Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "jc-hant", 7)) + { + /* Shaojiang Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "jc-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Shaojiang Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "jc-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Shaojiang Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "jc-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Shaojiang Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + break; case 'w': if (lang_matches (&lang_str[1], limit, "uu-hant-hk", 10)) { @@ -2816,9 +3065,10 @@ out: * @tag: A language tag. * * Converts @tag to a BCP 47 language tag if it is ambiguous (it corresponds to - * many language tags) and the best tag is not the alphabetically first, or if - * the best tag consists of multiple subtags, or if the best tag does not appear - * in #ot_languages. + * many language tags) and the best tag is not the first (sorted alphabetically, + * with two-letter tags having priority over all three-letter tags), or if the + * best tag consists of multiple subtags, or if the best tag does not appear in + * #ot_languages2 or #ot_languages3. * * Return value: The #hb_language_t corresponding to the BCP 47 language tag, * or #HB_LANGUAGE_INVALID if @tag is not ambiguous. @@ -2832,8 +3082,6 @@ hb_ot_ambiguous_tag_to_language (hb_tag_t tag) return hb_language_from_string ("alt", -1); /* Southern Altai */ case HB_TAG('A','P','P','H'): /* Phonetic transcription—Americanist conventions */ return hb_language_from_string ("und-fonnapa", -1); /* Undetermined; North American Phonetic Alphabet */ - case HB_TAG('A','R','A',' '): /* Arabic */ - return hb_language_from_string ("ar", -1); /* Arabic [macrolanguage] */ case HB_TAG('A','R','K',' '): /* Rakhine */ return hb_language_from_string ("rki", -1); /* Rakhine */ case HB_TAG('A','T','H',' '): /* Athapaskan */ @@ -2854,12 +3102,6 @@ hb_ot_ambiguous_tag_to_language (hb_tag_t tag) return hb_language_from_string ("din", -1); /* Dinka [macrolanguage] */ case HB_TAG('D','R','I',' '): /* Dari */ return hb_language_from_string ("prs", -1); /* Dari */ - case HB_TAG('D','Z','N',' '): /* Dzongkha */ - return hb_language_from_string ("dz", -1); /* Dzongkha */ - case HB_TAG('E','T','I',' '): /* Estonian */ - return hb_language_from_string ("et", -1); /* Estonian [macrolanguage] */ - case HB_TAG('F','A','R',' '): /* Persian */ - return hb_language_from_string ("fa", -1); /* Persian [macrolanguage] */ case HB_TAG('G','O','N',' '): /* Gondi */ return hb_language_from_string ("gon", -1); /* Gondi [macrolanguage] */ case HB_TAG('H','M','A',' '): /* High Mari */ @@ -2874,50 +3116,34 @@ hb_ot_ambiguous_tag_to_language (hb_tag_t tag) return hb_language_from_string ("iba", -1); /* Iban */ case HB_TAG('I','J','O',' '): /* Ijo */ return hb_language_from_string ("ijo", -1); /* Ijo [collection] */ - case HB_TAG('I','N','U',' '): /* Inuktitut */ - return hb_language_from_string ("iu", -1); /* Inuktitut [macrolanguage] */ - case HB_TAG('I','P','K',' '): /* Inupiat */ - return hb_language_from_string ("ik", -1); /* Inupiaq [macrolanguage] */ case HB_TAG('I','P','P','H'): /* Phonetic transcription—IPA conventions */ return hb_language_from_string ("und-fonipa", -1); /* Undetermined; International Phonetic Alphabet */ case HB_TAG('I','R','T',' '): /* Irish Traditional */ - return hb_language_from_string ("ga-Latg", -1); /* Irish; Latin (Gaelic variant) */ + return hb_language_from_string ("ghc", -1); /* Hiberno-Scottish Gaelic */ case HB_TAG('J','I','I',' '): /* Yiddish */ return hb_language_from_string ("yi", -1); /* Yiddish [macrolanguage] */ case HB_TAG('K','A','L',' '): /* Kalenjin */ return hb_language_from_string ("kln", -1); /* Kalenjin [macrolanguage] */ case HB_TAG('K','G','E',' '): /* Khutsuri Georgian */ return hb_language_from_string ("und-Geok", -1); /* Undetermined; Khutsuri (Asomtavruli and Nuskhuri) */ - case HB_TAG('K','N','R',' '): /* Kanuri */ - return hb_language_from_string ("kr", -1); /* Kanuri [macrolanguage] */ case HB_TAG('K','O','H',' '): /* Korean Old Hangul */ return hb_language_from_string ("okm", -1); /* Middle Korean (10th-16th cent.) */ case HB_TAG('K','O','K',' '): /* Konkani */ return hb_language_from_string ("kok", -1); /* Konkani [macrolanguage] */ - case HB_TAG('K','O','M',' '): /* Komi */ - return hb_language_from_string ("kv", -1); /* Komi [macrolanguage] */ case HB_TAG('K','P','L',' '): /* Kpelle */ return hb_language_from_string ("kpe", -1); /* Kpelle [macrolanguage] */ case HB_TAG('K','R','N',' '): /* Karen */ return hb_language_from_string ("kar", -1); /* Karen [collection] */ case HB_TAG('K','U','I',' '): /* Kui */ return hb_language_from_string ("uki", -1); /* Kui (India) */ - case HB_TAG('K','U','R',' '): /* Kurdish */ - return hb_language_from_string ("ku", -1); /* Kurdish [macrolanguage] */ case HB_TAG('L','M','A',' '): /* Low Mari */ return hb_language_from_string ("mhr", -1); /* Eastern Mari */ case HB_TAG('L','U','H',' '): /* Luyia */ return hb_language_from_string ("luy", -1); /* Luyia [macrolanguage] */ - case HB_TAG('L','V','I',' '): /* Latvian */ - return hb_language_from_string ("lv", -1); /* Latvian [macrolanguage] */ case HB_TAG('M','A','W',' '): /* Marwari */ return hb_language_from_string ("mwr", -1); /* Marwari [macrolanguage] */ - case HB_TAG('M','L','G',' '): /* Malagasy */ - return hb_language_from_string ("mg", -1); /* Malagasy [macrolanguage] */ case HB_TAG('M','L','Y',' '): /* Malay */ return hb_language_from_string ("ms", -1); /* Malay [macrolanguage] */ - case HB_TAG('M','N','G',' '): /* Mongolian */ - return hb_language_from_string ("mn", -1); /* Mongolian [macrolanguage] */ case HB_TAG('M','N','K',' '): /* Maninka */ return hb_language_from_string ("man", -1); /* Mandingo [macrolanguage] */ case HB_TAG('M','O','L',' '): /* Moldavian */ @@ -2928,26 +3154,16 @@ hb_ot_ambiguous_tag_to_language (hb_tag_t tag) return hb_language_from_string ("myn", -1); /* Mayan [collection] */ case HB_TAG('N','A','H',' '): /* Nahuatl */ return hb_language_from_string ("nah", -1); /* Nahuatl [collection] */ - case HB_TAG('N','E','P',' '): /* Nepali */ - return hb_language_from_string ("ne", -1); /* Nepali [macrolanguage] */ case HB_TAG('N','I','S',' '): /* Nisi */ return hb_language_from_string ("njz", -1); /* Nyishi */ case HB_TAG('N','O','R',' '): /* Norwegian */ return hb_language_from_string ("no", -1); /* Norwegian [macrolanguage] */ - case HB_TAG('O','J','B',' '): /* Ojibway */ - return hb_language_from_string ("oj", -1); /* Ojibwa [macrolanguage] */ - case HB_TAG('O','R','O',' '): /* Oromo */ - return hb_language_from_string ("om", -1); /* Oromo [macrolanguage] */ - case HB_TAG('P','A','S',' '): /* Pashto */ - return hb_language_from_string ("ps", -1); /* Pashto [macrolanguage] */ case HB_TAG('P','G','R',' '): /* Polytonic Greek */ return hb_language_from_string ("el-polyton", -1); /* Modern Greek (1453-); Polytonic Greek */ case HB_TAG('P','R','O',' '): /* Provençal / Old Provençal */ return hb_language_from_string ("pro", -1); /* Old Provençal (to 1500) */ case HB_TAG('Q','U','H',' '): /* Quechua (Bolivia) */ return hb_language_from_string ("quh", -1); /* South Bolivian Quechua */ - case HB_TAG('Q','U','Z',' '): /* Quechua */ - return hb_language_from_string ("qu", -1); /* Quechua [macrolanguage] */ case HB_TAG('Q','V','I',' '): /* Quechua (Ecuador) */ return hb_language_from_string ("qvi", -1); /* Imbabura Highland Quichua */ case HB_TAG('Q','W','H',' '): /* Quechua (Peru) */ @@ -2958,8 +3174,8 @@ hb_ot_ambiguous_tag_to_language (hb_tag_t tag) return hb_language_from_string ("ro", -1); /* Romanian */ case HB_TAG('R','O','Y',' '): /* Romany */ return hb_language_from_string ("rom", -1); /* Romany [macrolanguage] */ - case HB_TAG('S','Q','I',' '): /* Albanian */ - return hb_language_from_string ("sq", -1); /* Albanian [macrolanguage] */ + case HB_TAG('S','G','A',' '): /* Old Irish */ + return hb_language_from_string ("sga", -1); /* Old Irish (to 900) */ case HB_TAG('S','R','B',' '): /* Serbian */ return hb_language_from_string ("sr", -1); /* Serbian */ case HB_TAG('S','X','T',' '): /* Sutu */ @@ -2976,6 +3192,10 @@ hb_ot_ambiguous_tag_to_language (hb_tag_t tag) return hb_language_from_string ("tmh", -1); /* Tamashek [macrolanguage] */ case HB_TAG('T','O','D',' '): /* Todo */ return hb_language_from_string ("xwo", -1); /* Written Oirat */ + case HB_TAG('W','D','T',' '): /* Wendat */ + return hb_language_from_string ("wdt", -1); /* Wendat */ + case HB_TAG('W','Y','N',' '): /* Wyandot */ + return hb_language_from_string ("wyn", -1); /* Wyandot */ case HB_TAG('Z','H','H',' '): /* Chinese, Traditional, Hong Kong SAR */ return hb_language_from_string ("zh-HK", -1); /* Chinese [macrolanguage]; Hong Kong */ case HB_TAG('Z','H','S',' '): /* Chinese, Simplified */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-tag.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-tag.cc index 9f0ae3b4dc198..786a220b6ecf0 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-tag.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-tag.cc @@ -326,7 +326,7 @@ hb_ot_tags_from_language (const char *lang_str, hb_tag_t lang_tag = hb_tag_from_string (lang_str, first_len); - static hb_atomic_int_t last_tag_idx; /* Poor man's cache. */ + static hb_atomic_t last_tag_idx = 0; /* Poor man's cache. */ unsigned tag_idx = last_tag_idx; if (likely (tag_idx < ot_languages_len && ot_languages[tag_idx].language == lang_tag) || @@ -547,7 +547,7 @@ hb_ot_tag_to_language (hb_tag_t tag) buf[3] = '-'; str += 4; } - snprintf (str, 16, "x-hbot-%08x", tag); + snprintf (str, 16, "x-hbot-%08" PRIx32, tag); return hb_language_from_string (&*buf, -1); } } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-avar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-avar-table.hh index 0aaf68adb0b14..e26687c34ed18 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-avar-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-avar-table.hh @@ -57,7 +57,7 @@ struct avarV2Tail protected: Offset32To varIdxMap; /* Offset from the beginning of 'avar' table. */ - Offset32To varStore; /* Offset from the beginning of 'avar' table. */ + Offset32To varStore; /* Offset from the beginning of 'avar' table. */ public: DEFINE_SIZE_STATIC (8); @@ -80,7 +80,7 @@ struct AxisValueMap bool is_outside_axis_range (const Triple& axis_range) const { - float from_coord = coords[0].to_float (); + double from_coord = (double) coords[0].to_float (); return !axis_range.contains (from_coord); } @@ -100,8 +100,8 @@ struct AxisValueMap float from_coord = coords[0].to_float (); float to_coord = coords[1].to_float (); - from_coord = renormalizeValue (from_coord, unmapped_range, triple_distances); - to_coord = renormalizeValue (to_coord, axis_range, triple_distances); + from_coord = renormalizeValue ((double) from_coord, unmapped_range, triple_distances); + to_coord = renormalizeValue ((double) to_coord, axis_range, triple_distances); coords[0].set_float (from_coord); coords[1].set_float (to_coord); @@ -197,7 +197,7 @@ struct SegmentMaps : Array16Of unmapped_val.set_int (unmap (val.to_int ())); float unmapped_max = unmapped_val.to_float (); - return Triple{unmapped_min, unmapped_middle, unmapped_max}; + return Triple{(double) unmapped_min, (double) unmapped_middle, (double) unmapped_max}; } bool subset (hb_subset_context_t *c, hb_tag_t axis_tag) const @@ -230,7 +230,7 @@ struct SegmentMaps : Array16Of * duplicates here */ if (mapping.must_include ()) continue; - value_mappings.push (std::move (mapping)); + value_mappings.push (mapping); } AxisValueMap m; @@ -273,6 +273,7 @@ struct avar { TRACE_SANITIZE (this); if (!(version.sanitize (c) && + hb_barrier () && (version.major == 1 #ifndef HB_NO_AVAR2 || version.major == 2 @@ -293,6 +294,7 @@ struct avar #ifndef HB_NO_AVAR2 if (version.major < 2) return_trace (true); + hb_barrier (); const auto &v2 = * (const avarV2Tail *) map; if (unlikely (!v2.sanitize (c, this))) @@ -316,6 +318,7 @@ struct avar #ifndef HB_NO_AVAR2 if (version.major < 2) return; + hb_barrier (); for (; count < axisCount; count++) map = &StructAfter (*map); @@ -340,7 +343,7 @@ struct avar for (unsigned i = 0; i < coords_length; i++) coords[i] = out[i]; - OT::VariationStore::destroy_cache (var_store_cache); + OT::ItemVariationStore::destroy_cache (var_store_cache); #endif } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-common.hh index 271250f91c38d..72deddef213d8 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-common.hh @@ -28,215 +28,11 @@ #include "hb-ot-layout-common.hh" #include "hb-priority-queue.hh" +#include "hb-subset-instancer-iup.hh" namespace OT { -template -struct DeltaSetIndexMapFormat01 -{ - friend struct DeltaSetIndexMap; - - unsigned get_size () const - { return min_size + mapCount * get_width (); } - - private: - DeltaSetIndexMapFormat01* copy (hb_serialize_context_t *c) const - { - TRACE_SERIALIZE (this); - return_trace (c->embed (this)); - } - - template - bool serialize (hb_serialize_context_t *c, const T &plan) - { - unsigned int width = plan.get_width (); - unsigned int inner_bit_count = plan.get_inner_bit_count (); - const hb_array_t output_map = plan.get_output_map (); - - TRACE_SERIALIZE (this); - if (unlikely (output_map.length && ((((inner_bit_count-1)&~0xF)!=0) || (((width-1)&~0x3)!=0)))) - return_trace (false); - if (unlikely (!c->extend_min (this))) return_trace (false); - - entryFormat = ((width-1)<<4)|(inner_bit_count-1); - mapCount = output_map.length; - HBUINT8 *p = c->allocate_size (width * output_map.length); - if (unlikely (!p)) return_trace (false); - for (unsigned int i = 0; i < output_map.length; i++) - { - unsigned int v = output_map.arrayZ[i]; - if (v) - { - unsigned int outer = v >> 16; - unsigned int inner = v & 0xFFFF; - unsigned int u = (outer << inner_bit_count) | inner; - for (unsigned int w = width; w > 0;) - { - p[--w] = u; - u >>= 8; - } - } - p += width; - } - return_trace (true); - } - - uint32_t map (unsigned int v) const /* Returns 16.16 outer.inner. */ - { - /* If count is zero, pass value unchanged. This takes - * care of direct mapping for advance map. */ - if (!mapCount) - return v; - - if (v >= mapCount) - v = mapCount - 1; - - unsigned int u = 0; - { /* Fetch it. */ - unsigned int w = get_width (); - const HBUINT8 *p = mapDataZ.arrayZ + w * v; - for (; w; w--) - u = (u << 8) + *p++; - } - - { /* Repack it. */ - unsigned int n = get_inner_bit_count (); - unsigned int outer = u >> n; - unsigned int inner = u & ((1 << n) - 1); - u = (outer<<16) | inner; - } - - return u; - } - - unsigned get_map_count () const { return mapCount; } - unsigned get_width () const { return ((entryFormat >> 4) & 3) + 1; } - unsigned get_inner_bit_count () const { return (entryFormat & 0xF) + 1; } - - - bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && - c->check_range (mapDataZ.arrayZ, - mapCount, - get_width ())); - } - - protected: - HBUINT8 format; /* Format identifier--format = 0 */ - HBUINT8 entryFormat; /* A packed field that describes the compressed - * representation of delta-set indices. */ - MapCountT mapCount; /* The number of mapping entries. */ - UnsizedArrayOf - mapDataZ; /* The delta-set index mapping data. */ - - public: - DEFINE_SIZE_ARRAY (2+MapCountT::static_size, mapDataZ); -}; - -struct DeltaSetIndexMap -{ - template - bool serialize (hb_serialize_context_t *c, const T &plan) - { - TRACE_SERIALIZE (this); - unsigned length = plan.get_output_map ().length; - u.format = length <= 0xFFFF ? 0 : 1; - switch (u.format) { - case 0: return_trace (u.format0.serialize (c, plan)); - case 1: return_trace (u.format1.serialize (c, plan)); - default:return_trace (false); - } - } - - uint32_t map (unsigned v) const - { - switch (u.format) { - case 0: return (u.format0.map (v)); - case 1: return (u.format1.map (v)); - default:return v; - } - } - - unsigned get_map_count () const - { - switch (u.format) { - case 0: return u.format0.get_map_count (); - case 1: return u.format1.get_map_count (); - default:return 0; - } - } - - unsigned get_width () const - { - switch (u.format) { - case 0: return u.format0.get_width (); - case 1: return u.format1.get_width (); - default:return 0; - } - } - - unsigned get_inner_bit_count () const - { - switch (u.format) { - case 0: return u.format0.get_inner_bit_count (); - case 1: return u.format1.get_inner_bit_count (); - default:return 0; - } - } - - bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); - switch (u.format) { - case 0: return_trace (u.format0.sanitize (c)); - case 1: return_trace (u.format1.sanitize (c)); - default:return_trace (true); - } - } - - DeltaSetIndexMap* copy (hb_serialize_context_t *c) const - { - TRACE_SERIALIZE (this); - switch (u.format) { - case 0: return_trace (reinterpret_cast (u.format0.copy (c))); - case 1: return_trace (reinterpret_cast (u.format1.copy (c))); - default:return_trace (nullptr); - } - } - - protected: - union { - HBUINT8 format; /* Format identifier */ - DeltaSetIndexMapFormat01 format0; - DeltaSetIndexMapFormat01 format1; - } u; - public: - DEFINE_SIZE_UNION (1, format); -}; - - -struct VarStoreInstancer -{ - VarStoreInstancer (const VariationStore *varStore, - const DeltaSetIndexMap *varIdxMap, - hb_array_t coords) : - varStore (varStore), varIdxMap (varIdxMap), coords (coords) {} - - operator bool () const { return varStore && bool (coords); } - - /* according to the spec, if colr table has varStore but does not have - * varIdxMap, then an implicit identity mapping is used */ - float operator() (uint32_t varIdx, unsigned short offset = 0) const - { return coords ? varStore->get_delta (varIdxMap ? varIdxMap->map (VarIdx::add (varIdx, offset)) : varIdx + offset, coords) : 0; } - - const VariationStore *varStore; - const DeltaSetIndexMap *varIdxMap; - hb_array_t coords; -}; /* https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#tuplevariationheader */ struct TupleVariationHeader @@ -296,15 +92,15 @@ struct TupleVariationHeader start = hb_min (peak, 0.f); end = hb_max (peak, 0.f); } - axis_tuples.set (*axis_tag, Triple (start, peak, end)); + axis_tuples.set (*axis_tag, Triple ((double) start, (double) peak, (double) end)); } return true; } - float calculate_scalar (hb_array_t coords, unsigned int coord_count, - const hb_array_t shared_tuples, - const hb_vector_t> *shared_tuple_active_idx = nullptr) const + double calculate_scalar (hb_array_t coords, unsigned int coord_count, + const hb_array_t shared_tuples, + const hb_vector_t> *shared_tuple_active_idx = nullptr) const { const F2DOT14 *peak_tuple; @@ -318,13 +114,13 @@ struct TupleVariationHeader { unsigned int index = get_index (); if (unlikely ((index + 1) * coord_count > shared_tuples.length)) - return 0.f; + return 0.0; peak_tuple = shared_tuples.sub_array (coord_count * index, coord_count).arrayZ; if (shared_tuple_active_idx) { if (unlikely (index >= shared_tuple_active_idx->length)) - return 0.f; + return 0.0; auto _ = (*shared_tuple_active_idx).arrayZ[index]; if (_.second != -1) { @@ -349,7 +145,7 @@ struct TupleVariationHeader end_tuple = get_end_tuple (coord_count).arrayZ; } - float scalar = 1.f; + double scalar = 1.0; for (unsigned int i = start_idx; i < end_idx; i += step) { int peak = peak_tuple[i].to_int (); @@ -364,15 +160,15 @@ struct TupleVariationHeader int end = end_tuple[i].to_int (); if (unlikely (start > peak || peak > end || (start < 0 && end > 0 && peak))) continue; - if (v < start || v > end) return 0.f; + if (v < start || v > end) return 0.0; if (v < peak) - { if (peak != start) scalar *= (float) (v - start) / (peak - start); } + { if (peak != start) scalar *= (double) (v - start) / (peak - start); } else - { if (peak != end) scalar *= (float) (end - v) / (end - peak); } + { if (peak != end) scalar *= (double) (end - v) / (end - peak); } } - else if (!v || v < hb_min (0, peak) || v > hb_max (0, peak)) return 0.f; + else if (!v || v < hb_min (0, peak) || v > hb_max (0, peak)) return 0.0; else - scalar *= (float) v / peak; + scalar *= (double) v / peak; } return scalar; } @@ -425,15 +221,10 @@ struct TupleVariationHeader DEFINE_SIZE_MIN (4); }; -enum packed_delta_flag_t -{ - DELTAS_ARE_ZERO = 0x80, - DELTAS_ARE_WORDS = 0x40, - DELTA_RUN_COUNT_MASK = 0x3F -}; - struct tuple_delta_t { + static constexpr bool realloc_move = true; // Watch out when adding new members! + public: hb_hashmap_t axis_tuples; @@ -447,8 +238,8 @@ struct tuple_delta_t /* compiled data: header and deltas * compiled point data is saved in a hashmap within tuple_variations_t cause * some point sets might be reused by different tuple variations */ - hb_vector_t compiled_tuple_header; - hb_vector_t compiled_deltas; + hb_vector_t compiled_tuple_header; + hb_vector_t compiled_deltas; /* compiled peak coords, empty for non-gvar tuples */ hb_vector_t compiled_peak_coords; @@ -456,7 +247,7 @@ struct tuple_delta_t tuple_delta_t () = default; tuple_delta_t (const tuple_delta_t& o) = default; - friend void swap (tuple_delta_t& a, tuple_delta_t& b) + friend void swap (tuple_delta_t& a, tuple_delta_t& b) noexcept { hb_swap (a.axis_tuples, b.axis_tuples); hb_swap (a.indices, b.indices); @@ -467,10 +258,10 @@ struct tuple_delta_t hb_swap (a.compiled_peak_coords, b.compiled_peak_coords); } - tuple_delta_t (tuple_delta_t&& o) : tuple_delta_t () + tuple_delta_t (tuple_delta_t&& o) noexcept : tuple_delta_t () { hb_swap (*this, o); } - tuple_delta_t& operator = (tuple_delta_t&& o) + tuple_delta_t& operator = (tuple_delta_t&& o) noexcept { hb_swap (*this, o); return *this; @@ -514,14 +305,19 @@ struct tuple_delta_t return *this; unsigned num = indices.length; - for (unsigned i = 0; i < num; i++) - { - if (!indices.arrayZ[i]) continue; - - deltas_x[i] *= scalar; - if (deltas_y) + if (deltas_y) + for (unsigned i = 0; i < num; i++) + { + if (!indices.arrayZ[i]) continue; + deltas_x[i] *= scalar; deltas_y[i] *= scalar; - } + } + else + for (unsigned i = 0; i < num; i++) + { + if (!indices.arrayZ[i]) continue; + deltas_x[i] *= scalar; + } return *this; } @@ -536,18 +332,18 @@ struct tuple_delta_t return out; } - if ((tent->minimum < 0.f && tent->maximum > 0.f) || + if ((tent->minimum < 0.0 && tent->maximum > 0.0) || !(tent->minimum <= tent->middle && tent->middle <= tent->maximum)) return out; - if (tent->middle == 0.f) + if (tent->middle == 0.0) { out.push (*this); return out; } - result_t solutions = rebase_tent (*tent, axis_limit, axis_triple_distances); - for (auto t : solutions) + rebase_tent_result_t solutions = rebase_tent (*tent, axis_limit, axis_triple_distances); + for (auto &t : solutions) { tuple_delta_t new_var = *this; if (t.second == Triple ()) @@ -600,7 +396,9 @@ struct tuple_delta_t const hb_map_t& axes_old_index_tag_map, const hb_hashmap_t*, unsigned>* shared_tuples_idx_map) { - if (!compiled_deltas) return false; + /* compiled_deltas could be empty after iup delta optimization, we can skip + * compiling this tuple and return true */ + if (!compiled_deltas) return true; unsigned cur_axis_count = axes_index_map.get_population (); /* allocate enough memory: 1 peak + 2 intermediate coords + fixed header size */ @@ -714,37 +512,42 @@ struct tuple_delta_t } bool compile_deltas () + { return compile_deltas (indices, deltas_x, deltas_y, compiled_deltas); } + + static bool compile_deltas (hb_array_t point_indices, + hb_array_t x_deltas, + hb_array_t y_deltas, + hb_vector_t &compiled_deltas /* OUT */) { hb_vector_t rounded_deltas; - if (unlikely (!rounded_deltas.alloc (indices.length))) + if (unlikely (!rounded_deltas.alloc (point_indices.length))) return false; - for (unsigned i = 0; i < indices.length; i++) + for (unsigned i = 0; i < point_indices.length; i++) { - if (!indices[i]) continue; - int rounded_delta = (int) roundf (deltas_x[i]); + if (!point_indices[i]) continue; + int rounded_delta = (int) roundf (x_deltas.arrayZ[i]); rounded_deltas.push (rounded_delta); } - if (!rounded_deltas) return false; - /* allocate enough memories 3 * num_deltas */ - unsigned alloc_len = 3 * rounded_deltas.length; - if (deltas_y) + if (!rounded_deltas) return true; + /* allocate enough memories 5 * num_deltas */ + unsigned alloc_len = 5 * rounded_deltas.length; + if (y_deltas) alloc_len *= 2; if (unlikely (!compiled_deltas.resize (alloc_len))) return false; - unsigned i = 0; - unsigned encoded_len = encode_delta_run (i, compiled_deltas.as_array (), rounded_deltas); + unsigned encoded_len = compile_deltas (compiled_deltas, rounded_deltas); - if (deltas_y) + if (y_deltas) { - /* reuse the rounded_deltas vector, check that deltas_y have the same num of deltas as deltas_x */ + /* reuse the rounded_deltas vector, check that y_deltas have the same num of deltas as x_deltas */ unsigned j = 0; - for (unsigned idx = 0; idx < indices.length; idx++) + for (unsigned idx = 0; idx < point_indices.length; idx++) { - if (!indices[idx]) continue; - int rounded_delta = (int) roundf (deltas_y[idx]); + if (!point_indices[idx]) continue; + int rounded_delta = (int) roundf (y_deltas.arrayZ[idx]); if (j >= rounded_deltas.length) return false; @@ -752,174 +555,15 @@ struct tuple_delta_t } if (j != rounded_deltas.length) return false; - /* reset i because we reuse rounded_deltas for deltas_y */ - i = 0; - encoded_len += encode_delta_run (i, compiled_deltas.as_array ().sub_array (encoded_len), rounded_deltas); + encoded_len += compile_deltas (compiled_deltas.as_array ().sub_array (encoded_len), rounded_deltas); } return compiled_deltas.resize (encoded_len); } - unsigned encode_delta_run (unsigned& i, - hb_array_t encoded_bytes, - const hb_vector_t& deltas) const - { - unsigned num_deltas = deltas.length; - unsigned encoded_len = 0; - while (i < num_deltas) - { - int val = deltas[i]; - if (val == 0) - encoded_len += encode_delta_run_as_zeroes (i, encoded_bytes.sub_array (encoded_len), deltas); - else if (val >= -128 && val <= 127) - encoded_len += encode_delta_run_as_bytes (i, encoded_bytes.sub_array (encoded_len), deltas); - else - encoded_len += encode_delta_run_as_words (i, encoded_bytes.sub_array (encoded_len), deltas); - } - return encoded_len; - } - - unsigned encode_delta_run_as_zeroes (unsigned& i, - hb_array_t encoded_bytes, - const hb_vector_t& deltas) const - { - unsigned num_deltas = deltas.length; - unsigned run_length = 0; - auto it = encoded_bytes.iter (); - unsigned encoded_len = 0; - while (i < num_deltas && deltas[i] == 0) - { - i++; - run_length++; - } - - while (run_length >= 64) - { - *it++ = char (DELTAS_ARE_ZERO | 63); - run_length -= 64; - encoded_len++; - } - - if (run_length) - { - *it++ = char (DELTAS_ARE_ZERO | (run_length - 1)); - encoded_len++; - } - return encoded_len; - } - - unsigned encode_delta_run_as_bytes (unsigned &i, - hb_array_t encoded_bytes, - const hb_vector_t& deltas) const - { - unsigned start = i; - unsigned num_deltas = deltas.length; - while (i < num_deltas) - { - int val = deltas[i]; - if (val > 127 || val < -128) - break; - - /* from fonttools: if there're 2 or more zeros in a sequence, - * it is better to start a new run to save bytes. */ - if (val == 0 && i + 1 < num_deltas && deltas[i+1] == 0) - break; - - i++; - } - unsigned run_length = i - start; - - unsigned encoded_len = 0; - auto it = encoded_bytes.iter (); - - while (run_length >= 64) - { - *it++ = 63; - encoded_len++; - - for (unsigned j = 0; j < 64; j++) - { - *it++ = static_cast (deltas[start + j]); - encoded_len++; - } - - start += 64; - run_length -= 64; - } - - if (run_length) - { - *it++ = run_length - 1; - encoded_len++; - - while (start < i) - { - *it++ = static_cast (deltas[start++]); - encoded_len++; - } - } - - return encoded_len; - } - - unsigned encode_delta_run_as_words (unsigned &i, - hb_array_t encoded_bytes, - const hb_vector_t& deltas) const + static unsigned compile_deltas (hb_array_t encoded_bytes, + hb_array_t deltas) { - unsigned start = i; - unsigned num_deltas = deltas.length; - while (i < num_deltas) - { - int val = deltas[i]; - - /* start a new run for a single zero value*/ - if (val == 0) break; - - /* from fonttools: continue word-encoded run if there's only one - * single value in the range [-128, 127] because it is more compact. - * Only start a new run when there're 2 continuous such values. */ - if (val >= -128 && val <= 127 && - i + 1 < num_deltas && - deltas[i+1] >= -128 && deltas[i+1] <= 127) - break; - - i++; - } - - unsigned run_length = i - start; - auto it = encoded_bytes.iter (); - unsigned encoded_len = 0; - while (run_length >= 64) - { - *it++ = (DELTAS_ARE_WORDS | 63); - encoded_len++; - - for (unsigned j = 0; j < 64; j++) - { - int16_t delta_val = deltas[start + j]; - *it++ = static_cast (delta_val >> 8); - *it++ = static_cast (delta_val & 0xFF); - - encoded_len += 2; - } - - start += 64; - run_length -= 64; - } - - if (run_length) - { - *it++ = (DELTAS_ARE_WORDS | (run_length - 1)); - encoded_len++; - while (start < i) - { - int16_t delta_val = deltas[start++]; - *it++ = static_cast (delta_val >> 8); - *it++ = static_cast (delta_val & 0xFF); - - encoded_len += 2; - } - } - return encoded_len; + return TupleValues::compile (deltas, encoded_bytes); } bool calc_inferred_deltas (const contour_point_vector_t& orig_points) @@ -982,10 +626,14 @@ struct tuple_delta_t { i = next_index (i, start_point, end_point); if (i == next) break; - deltas_x.arrayZ[i] = infer_delta (orig_points.arrayZ[i].x, orig_points.arrayZ[prev].x, orig_points.arrayZ[next].x, - deltas_x.arrayZ[prev], deltas_x.arrayZ[next]); - deltas_y.arrayZ[i] = infer_delta (orig_points.arrayZ[i].y, orig_points.arrayZ[prev].y, orig_points.arrayZ[next].y, - deltas_y.arrayZ[prev], deltas_y.arrayZ[next]); + deltas_x.arrayZ[i] = infer_delta ((double) orig_points.arrayZ[i].x, + (double) orig_points.arrayZ[prev].x, + (double) orig_points.arrayZ[next].x, + (double) deltas_x.arrayZ[prev], (double) deltas_x.arrayZ[next]); + deltas_y.arrayZ[i] = infer_delta ((double) orig_points.arrayZ[i].y, + (double) orig_points.arrayZ[prev].y, + (double) orig_points.arrayZ[next].y, + (double) deltas_y.arrayZ[prev], (double) deltas_y.arrayZ[next]); inferred_idxes.add (i); if (--unref_count == 0) goto no_more_gaps; } @@ -1002,8 +650,8 @@ struct tuple_delta_t { if (!inferred_idxes.has (i)) { - deltas_x.arrayZ[i] = 0.f; - deltas_y.arrayZ[i] = 0.f; + deltas_x.arrayZ[i] = 0.0; + deltas_y.arrayZ[i] = 0.0; } indices[i] = true; } @@ -1011,16 +659,181 @@ struct tuple_delta_t return true; } - static float infer_delta (float target_val, float prev_val, float next_val, float prev_delta, float next_delta) + bool optimize (const contour_point_vector_t& contour_points, + bool is_composite, + double tolerance = 0.5 + 1e-10) + { + unsigned count = contour_points.length; + if (deltas_x.length != count || + deltas_y.length != count) + return false; + + hb_vector_t opt_indices; + hb_vector_t rounded_x_deltas, rounded_y_deltas; + + if (unlikely (!rounded_x_deltas.alloc (count) || + !rounded_y_deltas.alloc (count))) + return false; + + for (unsigned i = 0; i < count; i++) + { + int rounded_x_delta = (int) roundf (deltas_x.arrayZ[i]); + int rounded_y_delta = (int) roundf (deltas_y.arrayZ[i]); + rounded_x_deltas.push (rounded_x_delta); + rounded_y_deltas.push (rounded_y_delta); + } + + if (!iup_delta_optimize (contour_points, rounded_x_deltas, rounded_y_deltas, opt_indices, tolerance)) + return false; + + unsigned ref_count = 0; + for (bool ref_flag : opt_indices) + ref_count += ref_flag; + + if (ref_count == count) return true; + + hb_vector_t opt_deltas_x, opt_deltas_y; + bool is_comp_glyph_wo_deltas = (is_composite && ref_count == 0); + if (is_comp_glyph_wo_deltas) + { + if (unlikely (!opt_deltas_x.resize (count) || + !opt_deltas_y.resize (count))) + return false; + + opt_indices.arrayZ[0] = true; + for (unsigned i = 1; i < count; i++) + opt_indices.arrayZ[i] = false; + } + + hb_vector_t opt_point_data; + if (!compile_point_set (opt_indices, opt_point_data)) + return false; + hb_vector_t opt_deltas_data; + if (!compile_deltas (opt_indices, + is_comp_glyph_wo_deltas ? opt_deltas_x : deltas_x, + is_comp_glyph_wo_deltas ? opt_deltas_y : deltas_y, + opt_deltas_data)) + return false; + + hb_vector_t point_data; + if (!compile_point_set (indices, point_data)) + return false; + hb_vector_t deltas_data; + if (!compile_deltas (indices, deltas_x, deltas_y, deltas_data)) + return false; + + if (opt_point_data.length + opt_deltas_data.length < point_data.length + deltas_data.length) + { + indices.fini (); + indices = std::move (opt_indices); + + if (is_comp_glyph_wo_deltas) + { + deltas_x.fini (); + deltas_x = std::move (opt_deltas_x); + + deltas_y.fini (); + deltas_y = std::move (opt_deltas_y); + } + } + return !indices.in_error () && !deltas_x.in_error () && !deltas_y.in_error (); + } + + static bool compile_point_set (const hb_vector_t &point_indices, + hb_vector_t& compiled_points /* OUT */) + { + unsigned num_points = 0; + for (bool i : point_indices) + if (i) num_points++; + + /* when iup optimization is enabled, num of referenced points could be 0 */ + if (!num_points) return true; + + unsigned indices_length = point_indices.length; + /* If the points set consists of all points in the glyph, it's encoded with a + * single zero byte */ + if (num_points == indices_length) + return compiled_points.resize (1); + + /* allocate enough memories: 2 bytes for count + 3 bytes for each point */ + unsigned num_bytes = 2 + 3 *num_points; + if (unlikely (!compiled_points.resize (num_bytes, false))) + return false; + + unsigned pos = 0; + /* binary data starts with the total number of reference points */ + if (num_points < 0x80) + compiled_points.arrayZ[pos++] = num_points; + else + { + compiled_points.arrayZ[pos++] = ((num_points >> 8) | 0x80); + compiled_points.arrayZ[pos++] = num_points & 0xFF; + } + + const unsigned max_run_length = 0x7F; + unsigned i = 0; + unsigned last_value = 0; + unsigned num_encoded = 0; + while (i < indices_length && num_encoded < num_points) + { + unsigned run_length = 0; + unsigned header_pos = pos; + compiled_points.arrayZ[pos++] = 0; + + bool use_byte_encoding = false; + bool new_run = true; + while (i < indices_length && num_encoded < num_points && + run_length <= max_run_length) + { + // find out next referenced point index + while (i < indices_length && !point_indices[i]) + i++; + + if (i >= indices_length) break; + + unsigned cur_value = i; + unsigned delta = cur_value - last_value; + + if (new_run) + { + use_byte_encoding = (delta <= 0xFF); + new_run = false; + } + + if (use_byte_encoding && delta > 0xFF) + break; + + if (use_byte_encoding) + compiled_points.arrayZ[pos++] = delta; + else + { + compiled_points.arrayZ[pos++] = delta >> 8; + compiled_points.arrayZ[pos++] = delta & 0xFF; + } + i++; + last_value = cur_value; + run_length++; + num_encoded++; + } + + if (use_byte_encoding) + compiled_points.arrayZ[header_pos] = run_length - 1; + else + compiled_points.arrayZ[header_pos] = (run_length - 1) | 0x80; + } + return compiled_points.resize (pos, false); + } + + static double infer_delta (double target_val, double prev_val, double next_val, double prev_delta, double next_delta) { if (prev_val == next_val) - return (prev_delta == next_delta) ? prev_delta : 0.f; + return (prev_delta == next_delta) ? prev_delta : 0.0; else if (target_val <= hb_min (prev_val, next_val)) return (prev_val < next_val) ? prev_delta : next_delta; else if (target_val >= hb_max (prev_val, next_val)) return (prev_val > next_val) ? prev_delta : next_delta; - float r = (target_val - prev_val) / (next_val - prev_val); + double r = (target_val - prev_val) / (next_val - prev_val); return prev_delta + r * (next_delta - prev_delta); } @@ -1028,6 +841,7 @@ struct tuple_delta_t { return (i >= end) ? start : (i + 1); } }; +template struct TupleVariationData { bool sanitize (hb_sanitize_context_t *c) const @@ -1062,19 +876,22 @@ struct TupleVariationData private: /* referenced point set->compiled point data map */ - hb_hashmap_t*, hb_bytes_t> point_data_map; + hb_hashmap_t*, hb_vector_t> point_data_map; /* referenced point set-> count map, used in finding shared points */ hb_hashmap_t*, unsigned> point_set_count_map; /* empty for non-gvar tuples. - * shared_points_bytes is just a copy of some value in the point_data_map, + * shared_points_bytes is a pointer to some value in the point_data_map, * which will be freed during map destruction. Save it for serialization, so * no need to do find_shared_points () again */ - hb_bytes_t shared_points_bytes; + hb_vector_t *shared_points_bytes = nullptr; - /* total compiled byte size as TupleVariationData format, initialized to its - * min_size: 4 */ - unsigned compiled_byte_size = 4; + /* total compiled byte size as TupleVariationData format, initialized to 0 */ + unsigned compiled_byte_size = 0; + bool needs_padding = false; + + /* for gvar iup delta optimization: whether this is a composite glyph */ + bool is_composite = false; public: tuple_variations_t () = default; @@ -1082,21 +899,18 @@ struct TupleVariationData tuple_variations_t& operator=(const tuple_variations_t&) = delete; tuple_variations_t (tuple_variations_t&&) = default; tuple_variations_t& operator=(tuple_variations_t&&) = default; - ~tuple_variations_t () { fini (); } - void fini () - { - for (auto _ : point_data_map.values ()) - _.fini (); - - point_set_count_map.fini (); - tuple_vars.fini (); - } + ~tuple_variations_t () = default; explicit operator bool () const { return bool (tuple_vars); } unsigned get_var_count () const { - unsigned count = tuple_vars.length; - if (shared_points_bytes.length) + unsigned count = 0; + /* when iup delta opt is enabled, compiled_deltas could be empty and we + * should skip this tuple */ + for (auto& tuple: tuple_vars) + if (tuple.compiled_deltas) count++; + + if (shared_points_bytes && shared_points_bytes->length) count |= TupleVarCount::SharedPointNumbers; return count; } @@ -1110,26 +924,27 @@ struct TupleVariationData bool is_gvar, const hb_map_t *axes_old_index_tag_map, const hb_vector_t &shared_indices, - const hb_array_t shared_tuples) + const hb_array_t shared_tuples, + bool is_composite_glyph) { do { const HBUINT8 *p = iterator.get_serialized_data (); unsigned int length = iterator.current_tuple->get_data_size (); if (unlikely (!iterator.var_data_bytes.check_range (p, length))) - { fini (); return false; } + return false; hb_hashmap_t axis_tuples; if (!iterator.current_tuple->unpack_axis_tuples (iterator.get_axis_count (), shared_tuples, axes_old_index_tag_map, axis_tuples) || axis_tuples.is_empty ()) - { fini (); return false; } + return false; hb_vector_t private_indices; bool has_private_points = iterator.current_tuple->has_private_points (); const HBUINT8 *end = p + length; if (has_private_points && - !TupleVariationData::unpack_points (p, private_indices, end)) - { fini (); return false; } + !TupleVariationData::decompile_points (p, private_indices, end)) + return false; const hb_vector_t &indices = has_private_points ? private_indices : shared_indices; bool apply_to_all = (indices.length == 0); @@ -1138,43 +953,46 @@ struct TupleVariationData hb_vector_t deltas_x; if (unlikely (!deltas_x.resize (num_deltas, false) || - !TupleVariationData::unpack_deltas (p, deltas_x, end))) - { fini (); return false; } + !TupleVariationData::decompile_deltas (p, deltas_x, end))) + return false; hb_vector_t deltas_y; if (is_gvar) { if (unlikely (!deltas_y.resize (num_deltas, false) || - !TupleVariationData::unpack_deltas (p, deltas_y, end))) - { fini (); return false; } + !TupleVariationData::decompile_deltas (p, deltas_y, end))) + return false; } tuple_delta_t var; var.axis_tuples = std::move (axis_tuples); if (unlikely (!var.indices.resize (point_count) || !var.deltas_x.resize (point_count, false))) - { fini (); return false; } + return false; if (is_gvar && unlikely (!var.deltas_y.resize (point_count, false))) - { fini (); return false; } + return false; for (unsigned i = 0; i < num_deltas; i++) { unsigned idx = apply_to_all ? i : indices[i]; if (idx >= point_count) continue; var.indices[idx] = true; - var.deltas_x[idx] = static_cast (deltas_x[i]); + var.deltas_x[idx] = deltas_x[i]; if (is_gvar) - var.deltas_y[idx] = static_cast (deltas_y[i]); + var.deltas_y[idx] = deltas_y[i]; } tuple_vars.push (std::move (var)); } while (iterator.move_to_next ()); + + is_composite = is_composite_glyph; return true; } bool create_from_item_var_data (const VarData &var_data, const hb_vector_t>& regions, const hb_map_t& axes_old_index_tag_map, + unsigned& item_count, const hb_inc_bimap_t* inner_map = nullptr) { /* NULL offset, to keep original varidx valid, just return */ @@ -1184,7 +1002,8 @@ struct TupleVariationData unsigned num_regions = var_data.get_region_index_count (); if (!tuple_vars.alloc (num_regions)) return false; - unsigned item_count = inner_map ? inner_map->get_population () : var_data.get_item_count (); + item_count = inner_map ? inner_map->get_population () : var_data.get_item_count (); + if (!item_count) return true; unsigned row_size = var_data.get_row_size (); const HBUINT8 *delta_bytes = var_data.get_delta_bytes (); @@ -1237,7 +1056,7 @@ struct TupleVariationData Triple *axis_limit; if (!normalized_axes_location.has (axis_tag, &axis_limit)) return false; - TripleDistances axis_triple_distances{1.f, 1.f}; + TripleDistances axis_triple_distances{1.0, 1.0}; if (axes_triple_distances.has (axis_tag)) axis_triple_distances = axes_triple_distances.get (axis_tag); @@ -1250,7 +1069,7 @@ struct TupleVariationData unsigned new_len = new_vars.length + out.length; if (unlikely (!new_vars.alloc (new_len, false))) - { fini (); return false;} + return false; for (unsigned i = 0; i < out.length; i++) new_vars.push (std::move (out[i])); @@ -1261,8 +1080,9 @@ struct TupleVariationData return true; } - /* merge tuple variations with overlapping tents */ - void merge_tuple_variations () + /* merge tuple variations with overlapping tents, if iup delta optimization + * is enabled, add default deltas to contour_points */ + bool merge_tuple_variations (contour_point_vector_t* contour_points = nullptr) { hb_vector_t new_vars; hb_hashmap_t*, unsigned> m; @@ -1270,7 +1090,15 @@ struct TupleVariationData for (const tuple_delta_t& var : tuple_vars) { /* if all axes are pinned, drop the tuple variation */ - if (var.axis_tuples.is_empty ()) continue; + if (var.axis_tuples.is_empty ()) + { + /* if iup_delta_optimize is enabled, add deltas to contour coords */ + if (contour_points && !contour_points->add_deltas (var.deltas_x, + var.deltas_y, + var.indices)) + return false; + continue; + } unsigned *idx; if (m.has (&(var.axis_tuples), &idx)) @@ -1280,98 +1108,14 @@ struct TupleVariationData else { new_vars.push (var); - m.set (&(var.axis_tuples), i); + if (!m.set (&(var.axis_tuples), i)) + return false; i++; } } tuple_vars.fini (); tuple_vars = std::move (new_vars); - } - - hb_bytes_t compile_point_set (const hb_vector_t &point_indices) - { - unsigned num_points = 0; - for (bool i : point_indices) - if (i) num_points++; - - unsigned indices_length = point_indices.length; - /* If the points set consists of all points in the glyph, it's encoded with a - * single zero byte */ - if (num_points == indices_length) - { - char *p = (char *) hb_calloc (1, sizeof (char)); - if (unlikely (!p)) return hb_bytes_t (); - - return hb_bytes_t (p, 1); - } - - /* allocate enough memories: 2 bytes for count + 3 bytes for each point */ - unsigned num_bytes = 2 + 3 *num_points; - char *p = (char *) hb_calloc (num_bytes, sizeof (char)); - if (unlikely (!p)) return hb_bytes_t (); - - unsigned pos = 0; - /* binary data starts with the total number of reference points */ - if (num_points < 0x80) - p[pos++] = num_points; - else - { - p[pos++] = ((num_points >> 8) | 0x80); - p[pos++] = num_points & 0xFF; - } - - const unsigned max_run_length = 0x7F; - unsigned i = 0; - unsigned last_value = 0; - unsigned num_encoded = 0; - while (i < indices_length && num_encoded < num_points) - { - unsigned run_length = 0; - unsigned header_pos = pos; - p[pos++] = 0; - - bool use_byte_encoding = false; - bool new_run = true; - while (i < indices_length && num_encoded < num_points && - run_length <= max_run_length) - { - // find out next referenced point index - while (i < indices_length && !point_indices[i]) - i++; - - if (i >= indices_length) break; - - unsigned cur_value = i; - unsigned delta = cur_value - last_value; - - if (new_run) - { - use_byte_encoding = (delta <= 0xFF); - new_run = false; - } - - if (use_byte_encoding && delta > 0xFF) - break; - - if (use_byte_encoding) - p[pos++] = delta; - else - { - p[pos++] = delta >> 8; - p[pos++] = delta & 0xFF; - } - i++; - last_value = cur_value; - run_length++; - num_encoded++; - } - - if (use_byte_encoding) - p[header_pos] = run_length - 1; - else - p[header_pos] = (run_length - 1) | 0x80; - } - return hb_bytes_t (p, pos); + return true; } /* compile all point set and store byte data in a point_set->hb_bytes_t hashmap, @@ -1391,11 +1135,11 @@ struct TupleVariationData continue; } - hb_bytes_t compiled_data = compile_point_set (*points_set); - if (unlikely (compiled_data == hb_bytes_t ())) + hb_vector_t compiled_point_data; + if (!tuple_delta_t::compile_point_set (*points_set, compiled_point_data)) return false; - if (!point_data_map.set (points_set, compiled_data) || + if (!point_data_map.set (points_set, std::move (compiled_point_data)) || !point_set_count_map.set (points_set, 1)) return false; } @@ -1403,31 +1147,33 @@ struct TupleVariationData } /* find shared points set which saves most bytes */ - hb_bytes_t find_shared_points () + void find_shared_points () { unsigned max_saved_bytes = 0; - hb_bytes_t res{}; - for (const auto& _ : point_data_map.iter ()) + for (const auto& _ : point_data_map.iter_ref ()) { const hb_vector_t* points_set = _.first; unsigned data_length = _.second.length; + if (!data_length) continue; unsigned *count; if (unlikely (!point_set_count_map.has (points_set, &count) || *count <= 1)) - return hb_bytes_t (); + { + shared_points_bytes = nullptr; + return; + } unsigned saved_bytes = data_length * ((*count) -1); if (saved_bytes > max_saved_bytes) { max_saved_bytes = saved_bytes; - res = _.second; + shared_points_bytes = &(_.second); } } - return res; } - bool calc_inferred_deltas (contour_point_vector_t& contour_points) + bool calc_inferred_deltas (const contour_point_vector_t& contour_points) { for (tuple_delta_t& var : tuple_vars) if (!var.calc_inferred_deltas (contour_points)) @@ -1436,10 +1182,21 @@ struct TupleVariationData return true; } + bool iup_optimize (const contour_point_vector_t& contour_points) + { + for (tuple_delta_t& var : tuple_vars) + { + if (!var.optimize (contour_points, is_composite)) + return false; + } + return true; + } + public: bool instantiate (const hb_hashmap_t& normalized_axes_location, const hb_hashmap_t& axes_triple_distances, - contour_point_vector_t* contour_points = nullptr) + contour_point_vector_t* contour_points = nullptr, + bool optimize = false) { if (!tuple_vars) return true; if (!change_tuple_variations_axis_limits (normalized_axes_location, axes_triple_distances)) @@ -1449,41 +1206,70 @@ struct TupleVariationData if (!calc_inferred_deltas (*contour_points)) return false; - merge_tuple_variations (); + /* if iup delta opt is on, contour_points can't be null */ + if (optimize && !contour_points) + return false; + + if (!merge_tuple_variations (optimize ? contour_points : nullptr)) + return false; + + if (optimize && !iup_optimize (*contour_points)) return false; return !tuple_vars.in_error (); } bool compile_bytes (const hb_map_t& axes_index_map, const hb_map_t& axes_old_index_tag_map, bool use_shared_points, + bool is_gvar = false, const hb_hashmap_t*, unsigned>* shared_tuples_idx_map = nullptr) { + // return true for empty glyph + if (!tuple_vars) + return true; + // compile points set and store data in hashmap if (!compile_all_point_sets ()) return false; + /* total compiled byte size as TupleVariationData format, initialized to its + * min_size: 4 */ + compiled_byte_size += 4; + if (use_shared_points) { - shared_points_bytes = find_shared_points (); - compiled_byte_size += shared_points_bytes.length; + find_shared_points (); + if (shared_points_bytes) + compiled_byte_size += shared_points_bytes->length; } // compile delta and tuple var header for each tuple variation for (auto& tuple: tuple_vars) { const hb_vector_t* points_set = &(tuple.indices); - hb_bytes_t *points_data; + hb_vector_t *points_data; if (unlikely (!point_data_map.has (points_set, &points_data))) return false; + /* when iup optimization is enabled, num of referenced points could be 0 + * and thus the compiled points bytes is empty, we should skip compiling + * this tuple */ + if (!points_data->length) + continue; if (!tuple.compile_deltas ()) return false; - unsigned points_data_length = (*points_data != shared_points_bytes) ? points_data->length : 0; + unsigned points_data_length = (points_data != shared_points_bytes) ? points_data->length : 0; if (!tuple.compile_tuple_var_header (axes_index_map, points_data_length, axes_old_index_tag_map, shared_tuples_idx_map)) return false; compiled_byte_size += tuple.compiled_tuple_header.length + points_data_length + tuple.compiled_deltas.length; } + + if (is_gvar && (compiled_byte_size % 2)) + { + needs_padding = true; + compiled_byte_size += 1; + } + return true; } @@ -1502,25 +1288,31 @@ struct TupleVariationData bool serialize_var_data (hb_serialize_context_t *c, bool is_gvar) const { TRACE_SERIALIZE (this); - if (is_gvar) - shared_points_bytes.copy (c); + if (is_gvar && shared_points_bytes) + { + hb_ubytes_t s (shared_points_bytes->arrayZ, shared_points_bytes->length); + s.copy (c); + } for (const auto& tuple: tuple_vars) { const hb_vector_t* points_set = &(tuple.indices); - hb_bytes_t *point_data; + hb_vector_t *point_data; if (!point_data_map.has (points_set, &point_data)) return_trace (false); - if (!is_gvar || *point_data != shared_points_bytes) - point_data->copy (c); + if (!is_gvar || point_data != shared_points_bytes) + { + hb_ubytes_t s (point_data->arrayZ, point_data->length); + s.copy (c); + } tuple.compiled_deltas.as_array ().copy (c); if (c->in_error ()) return_trace (false); } /* padding for gvar */ - if (is_gvar && (compiled_byte_size % 2)) + if (is_gvar && needs_padding) { HBUINT8 pad; pad = 0; @@ -1551,7 +1343,7 @@ struct TupleVariationData { const HBUINT8 *base = &(table_base+var_data->data); const HBUINT8 *p = base; - if (!unpack_points (p, shared_indices, (const HBUINT8 *) (var_data_bytes.arrayZ + var_data_bytes.length))) return false; + if (!decompile_points (p, shared_indices, (const HBUINT8 *) (var_data_bytes.arrayZ + var_data_bytes.length))) return false; data_offset = p - base; } return true; @@ -1601,9 +1393,9 @@ struct TupleVariationData bool has_shared_point_numbers () const { return tupleVarCount.has_shared_point_numbers (); } - static bool unpack_points (const HBUINT8 *&p /* IN/OUT */, - hb_vector_t &points /* OUT */, - const HBUINT8 *end) + static bool decompile_points (const HBUINT8 *&p /* IN/OUT */, + hb_vector_t &points /* OUT */, + const HBUINT8 *end) { enum packed_point_flag_t { @@ -1653,43 +1445,13 @@ struct TupleVariationData return true; } - static bool unpack_deltas (const HBUINT8 *&p /* IN/OUT */, - hb_vector_t &deltas /* IN/OUT */, - const HBUINT8 *end) + template + static bool decompile_deltas (const HBUINT8 *&p /* IN/OUT */, + hb_vector_t &deltas /* IN/OUT */, + const HBUINT8 *end, + bool consume_all = false) { - unsigned i = 0; - unsigned count = deltas.length; - while (i < count) - { - if (unlikely (p + 1 > end)) return false; - unsigned control = *p++; - unsigned run_count = (control & DELTA_RUN_COUNT_MASK) + 1; - unsigned stop = i + run_count; - if (unlikely (stop > count)) return false; - if (control & DELTAS_ARE_ZERO) - { - for (; i < stop; i++) - deltas.arrayZ[i] = 0; - } - else if (control & DELTAS_ARE_WORDS) - { - if (unlikely (p + run_count * HBUINT16::static_size > end)) return false; - for (; i < stop; i++) - { - deltas.arrayZ[i] = * (const HBINT16 *) p; - p += HBUINT16::static_size; - } - } - else - { - if (unlikely (p + run_count > end)) return false; - for (; i < stop; i++) - { - deltas.arrayZ[i] = * (const HBINT8 *) p++; - } - } - } - return true; + return TupleValues::decompile (p, deltas, end, consume_all); } bool has_data () const { return tupleVarCount; } @@ -1700,13 +1462,15 @@ struct TupleVariationData const hb_map_t *axes_old_index_tag_map, const hb_vector_t &shared_indices, const hb_array_t shared_tuples, - tuple_variations_t& tuple_variations /* OUT */) const + tuple_variations_t& tuple_variations, /* OUT */ + bool is_composite_glyph = false) const { return tuple_variations.create_from_tuple_var_data (iterator, tupleVarCount, point_count, is_gvar, axes_old_index_tag_map, shared_indices, - shared_tuples); + shared_tuples, + is_composite_glyph); } bool serialize (hb_serialize_context_t *c, @@ -1758,15 +1522,16 @@ struct TupleVariationData * low 12 bits are the number of tuple variation tables * for this glyph. The number of tuple variation tables * can be any number between 1 and 4095. */ - Offset16To + OffsetTo data; /* Offset from the start of the base table * to the serialized data. */ /* TupleVariationHeader tupleVariationHeaders[] *//* Array of tuple variation headers. */ public: - DEFINE_SIZE_MIN (4); + DEFINE_SIZE_MIN (2 + OffType::static_size); }; -using tuple_variations_t = TupleVariationData::tuple_variations_t; +// TODO: Move tuple_variations_t to outside of TupleVariationData +using tuple_variations_t = TupleVariationData::tuple_variations_t; struct item_variations_t { using region_t = const hb_hashmap_t*; @@ -1775,6 +1540,14 @@ struct item_variations_t * have the same num of deltas (rows) */ hb_vector_t vars; + /* num of retained rows for each subtable, there're 2 cases when var_data is empty: + * 1. retained item_count is zero + * 2. regions is empty and item_count is non-zero. + * when converting to tuples, both will be dropped because the tuple is empty, + * however, we need to retain 2. as all-zero rows to keep original varidx + * valid, so we need a way to remember the num of rows for each subtable */ + hb_vector_t var_data_num_rows; + /* original region list, decompiled from item varstore, used when rebuilding * region list after instantiation */ hb_vector_t> orig_region_list; @@ -1812,7 +1585,7 @@ struct item_variations_t const hb_map_t& get_varidx_map () const { return varidx_map; } - bool instantiate (const VariationStore& varStore, + bool instantiate (const ItemVariationStore& varStore, const hb_subset_plan_t *plan, bool optimize=true, bool use_no_variation_idx=true, @@ -1826,7 +1599,7 @@ struct item_variations_t } /* keep below APIs public only for unit test: test-item-varstore */ - bool create_from_item_varstore (const VariationStore& varStore, + bool create_from_item_varstore (const ItemVariationStore& varStore, const hb_map_t& axes_old_index_tag_map, const hb_array_t inner_maps = hb_array_t ()) { @@ -1836,22 +1609,26 @@ struct item_variations_t unsigned num_var_data = varStore.get_sub_table_count (); if (inner_maps && inner_maps.length != num_var_data) return false; - if (!vars.alloc (num_var_data)) return false; + if (!vars.alloc (num_var_data) || + !var_data_num_rows.alloc (num_var_data)) return false; for (unsigned i = 0; i < num_var_data; i++) { if (inner_maps && !inner_maps.arrayZ[i].get_population ()) continue; tuple_variations_t var_data_tuples; + unsigned item_count = 0; if (!var_data_tuples.create_from_item_var_data (varStore.get_sub_table (i), orig_region_list, axes_old_index_tag_map, + item_count, inner_maps ? &(inner_maps.arrayZ[i]) : nullptr)) return false; + var_data_num_rows.push (item_count); vars.push (std::move (var_data_tuples)); } - return !vars.in_error (); + return !vars.in_error () && !var_data_num_rows.in_error () && vars.length == var_data_num_rows.length; } bool instantiate_tuple_vars (const hb_hashmap_t& normalized_axes_location, @@ -1904,7 +1681,9 @@ struct item_variations_t } } - if (!all_regions || !all_unique_regions) return false; + /* regions are empty means no variation data, return true */ + if (!all_regions || !all_unique_regions) return true; + if (!region_list.alloc (all_regions.get_population ())) return false; @@ -1969,16 +1748,13 @@ struct item_variations_t bool as_item_varstore (bool optimize=true, bool use_no_variation_idx=true) { - if (!region_list) return false; + /* return true if no variation data */ + if (!region_list) return true; unsigned num_cols = region_list.length; /* pre-alloc a 2D vector for all sub_table's VarData rows */ unsigned total_rows = 0; - for (unsigned major = 0; major < vars.length; major++) - { - const tuple_variations_t& tuples = vars[major]; - /* all tuples in each sub_table should have same num of deltas(num rows) */ - total_rows += tuples.tuple_vars[0].deltas_x.length; - } + for (unsigned major = 0; major < var_data_num_rows.length; major++) + total_rows += var_data_num_rows[major]; if (!delta_rows.resize (total_rows)) return false; /* init all rows to [0]*num_cols */ @@ -1998,7 +1774,7 @@ struct item_variations_t /* deltas are stored in tuples(column based), convert them back into items * (row based) delta */ const tuple_variations_t& tuples = vars[major]; - unsigned num_rows = tuples.tuple_vars[0].deltas_x.length; + unsigned num_rows = var_data_num_rows[major]; for (const tuple_delta_t& tuple: tuples.tuple_vars) { if (tuple.deltas_x.length != num_rows) @@ -2223,6 +1999,7 @@ struct item_variations_t } }; + } /* namespace OT */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-cvar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-cvar-table.hh index a44a8e99998b1..3dc4ebaebd821 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-cvar-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-cvar-table.hh @@ -45,11 +45,12 @@ struct cvar { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && - version.sanitize (c) && likely (version.major == 1) && + hb_barrier () && + likely (version.major == 1) && tupleVariationData.sanitize (c)); } - const TupleVariationData* get_tuple_var_data (void) const + const TupleVariationData<>* get_tuple_var_data (void) const { return &tupleVariationData; } bool decompile_tuple_variations (unsigned axis_count, @@ -57,12 +58,12 @@ struct cvar hb_blob_t *blob, bool is_gvar, const hb_map_t *axes_old_index_tag_map, - TupleVariationData::tuple_variations_t& tuple_variations /* OUT */) const + TupleVariationData<>::tuple_variations_t& tuple_variations /* OUT */) const { hb_vector_t shared_indices; - TupleVariationData::tuple_iterator_t iterator; + TupleVariationData<>::tuple_iterator_t iterator; hb_bytes_t var_data_bytes = blob->as_bytes ().sub_array (4); - if (!TupleVariationData::get_tuple_iterator (var_data_bytes, axis_count, this, + if (!TupleVariationData<>::get_tuple_iterator (var_data_bytes, axis_count, this, shared_indices, &iterator)) return false; @@ -76,16 +77,16 @@ struct cvar static bool calculate_cvt_deltas (unsigned axis_count, hb_array_t coords, unsigned num_cvt_item, - const TupleVariationData *tuple_var_data, + const TupleVariationData<> *tuple_var_data, const void *base, hb_vector_t& cvt_deltas /* OUT */) { if (!coords) return true; hb_vector_t shared_indices; - TupleVariationData::tuple_iterator_t iterator; + TupleVariationData<>::tuple_iterator_t iterator; unsigned var_data_length = tuple_var_data->get_size (axis_count); hb_bytes_t var_data_bytes = hb_bytes_t (reinterpret_cast (tuple_var_data), var_data_length); - if (!TupleVariationData::get_tuple_iterator (var_data_bytes, axis_count, base, + if (!TupleVariationData<>::get_tuple_iterator (var_data_bytes, axis_count, base, shared_indices, &iterator)) return true; /* isn't applied at all */ @@ -106,14 +107,14 @@ struct cvar bool has_private_points = iterator.current_tuple->has_private_points (); if (has_private_points && - !TupleVariationData::unpack_points (p, private_indices, end)) + !TupleVariationData<>::decompile_points (p, private_indices, end)) return false; const hb_vector_t &indices = has_private_points ? private_indices : shared_indices; bool apply_to_all = (indices.length == 0); unsigned num_deltas = apply_to_all ? num_cvt_item : indices.length; if (unlikely (!unpacked_deltas.resize (num_deltas, false))) return false; - if (unlikely (!TupleVariationData::unpack_deltas (p, unpacked_deltas, end))) return false; + if (unlikely (!TupleVariationData<>::decompile_deltas (p, unpacked_deltas, end))) return false; for (unsigned int i = 0; i < num_deltas; i++) { @@ -128,7 +129,7 @@ struct cvar } bool serialize (hb_serialize_context_t *c, - TupleVariationData::tuple_variations_t& tuple_variations) const + TupleVariationData<>::tuple_variations_t& tuple_variations) const { TRACE_SERIALIZE (this); if (!tuple_variations) return_trace (false); @@ -143,7 +144,7 @@ struct cvar if (c->plan->all_axes_pinned) return_trace (false); - OT::TupleVariationData::tuple_variations_t tuple_variations; + OT::TupleVariationData<>::tuple_variations_t tuple_variations; unsigned axis_count = c->plan->axes_old_index_tag_map.get_population (); const hb_tag_t cvt = HB_TAG('c','v','t',' '); @@ -168,7 +169,7 @@ struct cvar } static bool add_cvt_and_apply_deltas (hb_subset_plan_t *plan, - const TupleVariationData *tuple_var_data, + const TupleVariationData<> *tuple_var_data, const void *base) { const hb_tag_t cvt = HB_TAG('c','v','t',' '); @@ -208,7 +209,7 @@ struct cvar protected: FixedVersion<>version; /* Version of the CVT variation table * initially set to 0x00010000u */ - TupleVariationData tupleVariationData; /* TupleVariationDate for cvar table */ + TupleVariationData<> tupleVariationData; /* TupleVariationDate for cvar table */ public: DEFINE_SIZE_MIN (8); }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-fvar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-fvar-table.hh index d60c3dbcf4e47..f2725eaa2823e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-fvar-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-fvar-table.hh @@ -43,7 +43,7 @@ static bool axis_coord_pinned_or_within_axis_range (const hb_array_t(coords[axis_index].to_float ()); if (axis_limit.is_point ()) { if (axis_limit.minimum != axis_coord) @@ -131,6 +131,7 @@ struct InstanceRecord { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && c->check_array (coordinatesZ.arrayZ, axis_count)); } @@ -232,7 +233,10 @@ struct AxisRecord { float min, default_, max; get_coordinates (min, default_, max); - return TripleDistances (min, default_, max); + return TripleDistances ( + static_cast(min), + static_cast(default_), + static_cast(max)); } bool subset (hb_subset_context_t *c) const @@ -277,8 +281,10 @@ struct fvar { TRACE_SANITIZE (this); return_trace (version.sanitize (c) && + hb_barrier () && likely (version.major == 1) && c->check_struct (this) && + hb_barrier () && axisSize == 20 && /* Assumed in our code. */ instanceSize >= axisCount * 4 + 4 && get_axes ().sanitize (c) && diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-gvar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-gvar-table.hh index fd32ef03f30d6..9f9b5be3cbea9 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-gvar-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-gvar-table.hh @@ -28,6 +28,7 @@ #ifndef HB_OT_VAR_GVAR_TABLE_HH #define HB_OT_VAR_GVAR_TABLE_HH +#include "hb-decycler.hh" #include "hb-open-type.hh" #include "hb-ot-var-common.hh" @@ -36,15 +37,33 @@ * https://docs.microsoft.com/en-us/typography/opentype/spec/gvar */ #define HB_OT_TAG_gvar HB_TAG('g','v','a','r') +#define HB_OT_TAG_GVAR HB_TAG('G','V','A','R') -namespace OT { +struct hb_glyf_scratch_t +{ + // glyf + contour_point_vector_t all_points; + contour_point_vector_t comp_points; + hb_decycler_t decycler; + + // gvar + contour_point_vector_t orig_points; + hb_vector_t x_deltas; + hb_vector_t y_deltas; + contour_point_vector_t deltas; + hb_vector_t shared_indices; + hb_vector_t private_indices; +}; -struct GlyphVariationData : TupleVariationData -{}; +namespace OT { +template struct glyph_variations_t { - using tuple_variations_t = TupleVariationData::tuple_variations_t; + // TODO: Move tuple_variations_t to outside of TupleVariationData + using tuple_variations_t = typename TupleVariationData::tuple_variations_t; + using GlyphVariationData = TupleVariationData; + hb_vector_t glyph_variations; hb_vector_t compiled_shared_tuples; @@ -72,7 +91,7 @@ struct glyph_variations_t const hb_subset_plan_t *plan, const hb_hashmap_t& new_gid_var_data_map) { - if (unlikely (!glyph_variations.alloc (plan->new_to_old_gid_list.length, true))) + if (unlikely (!glyph_variations.alloc_exact (plan->new_to_old_gid_list.length))) return false; auto it = hb_iter (plan->new_to_old_gid_list); @@ -86,10 +105,11 @@ struct glyph_variations_t hb_bytes_t var_data = new_gid_var_data_map.get (new_gid); const GlyphVariationData* p = reinterpret_cast (var_data.arrayZ); - hb_vector_t shared_indices; - GlyphVariationData::tuple_iterator_t iterator; + typename GlyphVariationData::tuple_iterator_t iterator; tuple_variations_t tuple_vars; + hb_vector_t shared_indices; + /* in case variation data is empty, push an empty struct into the vector, * keep the vector in sync with the new_to_old_gid_list */ if (!var_data || ! p->has_data () || !all_contour_points->length || @@ -101,10 +121,14 @@ struct glyph_variations_t continue; } + bool is_composite_glyph = false; + is_composite_glyph = plan->composite_new_gids.has (new_gid); + if (!p->decompile_tuple_variations (all_contour_points->length, true /* is_gvar */, iterator, &(plan->axes_old_index_tag_map), shared_indices, shared_tuples, - tuple_vars /* OUT */)) + tuple_vars, /* OUT */ + is_composite_glyph)) return false; glyph_variations.push (std::move (tuple_vars)); } @@ -114,13 +138,15 @@ struct glyph_variations_t bool instantiate (const hb_subset_plan_t *plan) { unsigned count = plan->new_to_old_gid_list.length; + bool iup_optimize = false; + iup_optimize = plan->flags & HB_SUBSET_FLAGS_OPTIMIZE_IUP_DELTAS; for (unsigned i = 0; i < count; i++) { hb_codepoint_t new_gid = plan->new_to_old_gid_list[i].first; contour_point_vector_t *all_points; if (!plan->new_gid_contour_points_map.has (new_gid, &all_points)) return false; - if (!glyph_variations[i].instantiate (plan->axes_location, plan->axes_triple_distances, all_points)) + if (!glyph_variations[i].instantiate (plan->axes_location, plan->axes_triple_distances, all_points, iup_optimize)) return false; } return true; @@ -134,6 +160,7 @@ struct glyph_variations_t for (tuple_variations_t& vars: glyph_variations) if (!vars.compile_bytes (axes_index_map, axes_old_index_tag_map, true, /* use shared points*/ + true, &shared_tuples_idx_map)) return false; @@ -252,7 +279,7 @@ struct glyph_variations_t hb_codepoint_t last_gid = 0; unsigned idx = 0; - TupleVariationData* cur_glyph = c->start_embed (); + GlyphVariationData* cur_glyph = c->start_embed (); if (!cur_glyph) return_trace (false); for (auto &_ : it) { @@ -266,7 +293,7 @@ struct glyph_variations_t if (idx >= glyph_variations.length) return_trace (false); if (!cur_glyph->serialize (c, true, glyph_variations[idx])) return_trace (false); - TupleVariationData* next_glyph = c->start_embed (); + GlyphVariationData* next_glyph = c->start_embed (); glyph_offset += (char *) next_glyph - (char *) cur_glyph; if (long_offset) @@ -289,14 +316,21 @@ struct glyph_variations_t } }; -struct gvar +template +struct gvar_GVAR { - static constexpr hb_tag_t tableTag = HB_OT_TAG_gvar; + static constexpr hb_tag_t tableTag = TableTag; + + using GlyphVariationData = TupleVariationData; + + bool has_data () const { return version.to_int () != 0; } bool sanitize_shallow (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && (version.major == 1) && + return_trace (c->check_struct (this) && + hb_barrier () && + (version.major == 1) && sharedTuples.sanitize (c, this, axisCount * sharedTupleCount) && (is_long_offset () ? c->check_array (get_long_offset_array (), c->get_num_glyphs () + 1) : @@ -308,7 +342,7 @@ struct gvar { return sanitize_shallow (c); } bool decompile_glyph_variations (hb_subset_context_t *c, - glyph_variations_t& glyph_vars /* OUT */) const + glyph_variations_t& glyph_vars /* OUT */) const { hb_hashmap_t new_gid_var_data_map; auto it = hb_iter (c->plan->new_to_old_gid_list); @@ -335,13 +369,14 @@ struct gvar template bool serialize (hb_serialize_context_t *c, - const glyph_variations_t& glyph_vars, + const glyph_variations_t& glyph_vars, Iterator it, unsigned axis_count, - unsigned num_glyphs) const + unsigned num_glyphs, + bool force_long_offsets) const { TRACE_SERIALIZE (this); - gvar *out = c->allocate_min (); + gvar_GVAR *out = c->allocate_min (); if (unlikely (!out)) return_trace (false); out->version.major = 1; @@ -350,7 +385,10 @@ struct gvar out->glyphCountX = hb_min (0xFFFFu, num_glyphs); unsigned glyph_var_data_size = glyph_vars.compiled_byte_size (); - bool long_offset = glyph_var_data_size & ~0xFFFFu; + /* According to the spec: If the short format (Offset16) is used for offsets, + * the value stored is the offset divided by 2, so the maximum data size should + * be 2 * 0xFFFFu, which is 0x1FFFEu */ + bool long_offset = glyph_var_data_size > 0x1FFFEu || force_long_offsets; out->flags = long_offset ? 1 : 0; HBUINT8 *glyph_var_data_offsets = c->allocate_size ((long_offset ? 4 : 2) * (num_glyphs + 1), false); @@ -380,7 +418,7 @@ struct gvar bool instantiate (hb_subset_context_t *c) const { TRACE_SUBSET (this); - glyph_variations_t glyph_vars; + glyph_variations_t glyph_vars; if (!decompile_glyph_variations (c, glyph_vars)) return_trace (false); @@ -391,7 +429,12 @@ struct gvar unsigned axis_count = c->plan->axes_index_map.get_population (); unsigned num_glyphs = c->plan->num_output_glyphs (); auto it = hb_iter (c->plan->new_to_old_gid_list); - return_trace (serialize (c->serializer, glyph_vars, it, axis_count, num_glyphs)); + + bool force_long_offsets = false; +#ifdef HB_EXPERIMENTAL_API + force_long_offsets = c->plan->flags & HB_SUBSET_FLAGS_IFTB_REQUIREMENTS; +#endif + return_trace (serialize (c->serializer, glyph_vars, it, axis_count, num_glyphs, force_long_offsets)); } bool subset (hb_subset_context_t *c) const @@ -405,7 +448,7 @@ struct gvar unsigned glyph_count = version.to_int () ? c->plan->source->get_num_glyphs () : 0; - gvar *out = c->serializer->allocate_min (); + gvar_GVAR *out = c->serializer->allocate_min (); if (unlikely (!out)) return_trace (false); out->version.major = 1; @@ -426,7 +469,13 @@ struct gvar subset_data_size += get_glyph_var_data_bytes (c->source_blob, glyph_count, old_gid).length; } - bool long_offset = subset_data_size & ~0xFFFFu; + /* According to the spec: If the short format (Offset16) is used for offsets, + * the value stored is the offset divided by 2, so the maximum data size should + * be 2 * 0xFFFFu, which is 0x1FFFEu */ + bool long_offset = subset_data_size > 0x1FFFEu; +#ifdef HB_EXPERIMENTAL_API + long_offset = long_offset || (c->plan->flags & HB_SUBSET_FLAGS_IFTB_REQUIREMENTS); +#endif out->flags = long_offset ? 1 : 0; HBUINT8 *subset_offsets = c->serializer->allocate_size ((long_offset ? 4 : 2) * (num_glyphs + 1), false); @@ -444,6 +493,8 @@ struct gvar hb_memcpy (tuples, this+sharedTuples, shared_tuple_size); } + /* This ordering relative to the shared tuples array, which puts the glyphVariationData + last in the table, is required when HB_SUBSET_FLAGS_IFTB_REQUIREMENTS is set */ char *subset_data = c->serializer->allocate_size (subset_data_size, false); if (!subset_data) return_trace (false); out->dataZ = subset_data - (char *) out; @@ -521,7 +572,7 @@ struct gvar unsigned get_offset (unsigned glyph_count, unsigned i) const { if (unlikely (i > glyph_count)) return 0; - _hb_compiler_memory_r_barrier (); + hb_barrier (); return is_long_offset () ? get_long_offset_array ()[i] : get_short_offset_array ()[i] * 2; } @@ -531,15 +582,17 @@ struct gvar public: struct accelerator_t { + bool has_data () const { return table->has_data (); } + accelerator_t (hb_face_t *face) { - table = hb_sanitize_context_t ().reference_table (face); + table = hb_sanitize_context_t ().reference_table (face); /* If sanitize failed, set glyphCount to 0. */ glyphCount = table->version.to_int () ? face->get_num_glyphs () : 0; - /* For shared tuples that only have one axis active, shared the index of - * that axis as a cache. This will speed up caclulate_scalar() a lot - * for fonts with lots of axes and many "monovar" tuples. */ + /* For shared tuples that only have one or two axes active, shared the index + * of that axis as a cache. This will speed up caclulate_scalar() a lot + * for fonts with lots of axes and many "monovar" or "duovar" tuples. */ hb_array_t shared_tuples = (table+table->sharedTuples).as_array (table->sharedTupleCount * table->axisCount); unsigned count = table->sharedTupleCount; if (unlikely (!shared_tuple_active_idx.resize (count, false))) return; @@ -599,37 +652,42 @@ struct gvar public: bool apply_deltas_to_points (hb_codepoint_t glyph, - hb_array_t coords, + hb_array_t coords, const hb_array_t points, + hb_glyf_scratch_t &scratch, bool phantom_only = false) const { if (unlikely (glyph >= glyphCount)) return true; hb_bytes_t var_data_bytes = table->get_glyph_var_data_bytes (table.get_blob (), glyphCount, glyph); if (!var_data_bytes.as ()->has_data ()) return true; - hb_vector_t shared_indices; - GlyphVariationData::tuple_iterator_t iterator; + + auto &shared_indices = scratch.shared_indices; + shared_indices.clear (); + + typename GlyphVariationData::tuple_iterator_t iterator; if (!GlyphVariationData::get_tuple_iterator (var_data_bytes, table->axisCount, var_data_bytes.arrayZ, shared_indices, &iterator)) return true; /* so isn't applied at all */ /* Save original points for inferred delta calculation */ - contour_point_vector_t orig_points_vec; // Populated lazily + auto &orig_points_vec = scratch.orig_points; + orig_points_vec.clear (); // Populated lazily auto orig_points = orig_points_vec.as_array (); /* flag is used to indicate referenced point */ - contour_point_vector_t deltas_vec; // Populated lazily + auto &deltas_vec = scratch.deltas; + deltas_vec.clear (); // Populated lazily auto deltas = deltas_vec.as_array (); - hb_vector_t end_points; // Populated lazily - unsigned num_coords = table->axisCount; hb_array_t shared_tuples = (table+table->sharedTuples).as_array (table->sharedTupleCount * num_coords); - hb_vector_t private_indices; - hb_vector_t x_deltas; - hb_vector_t y_deltas; + auto &private_indices = scratch.private_indices; + auto &x_deltas = scratch.x_deltas; + auto &y_deltas = scratch.y_deltas; + unsigned count = points.length; bool flush = false; do @@ -654,16 +712,16 @@ struct gvar bool has_private_points = iterator.current_tuple->has_private_points (); if (has_private_points && - !GlyphVariationData::unpack_points (p, private_indices, end)) + !GlyphVariationData::decompile_points (p, private_indices, end)) return false; const hb_array_t &indices = has_private_points ? private_indices : shared_indices; bool apply_to_all = (indices.length == 0); unsigned int num_deltas = apply_to_all ? points.length : indices.length; if (unlikely (!x_deltas.resize (num_deltas, false))) return false; - if (unlikely (!GlyphVariationData::unpack_deltas (p, x_deltas, end))) return false; + if (unlikely (!GlyphVariationData::decompile_deltas (p, x_deltas, end))) return false; if (unlikely (!y_deltas.resize (num_deltas, false))) return false; - if (unlikely (!GlyphVariationData::unpack_deltas (p, y_deltas, end))) return false; + if (unlikely (!GlyphVariationData::decompile_deltas (p, y_deltas, end))) return false; if (!apply_to_all) { @@ -700,8 +758,8 @@ struct gvar if (phantom_only && pt_index < count - 4) continue; auto &delta = deltas.arrayZ[pt_index]; delta.flag = 1; /* this point is referenced, i.e., explicit deltas specified */ - delta.x += x_deltas.arrayZ[i] * scalar; - delta.y += y_deltas.arrayZ[i] * scalar; + delta.add_delta (x_deltas.arrayZ[i] * scalar, + y_deltas.arrayZ[i] * scalar); } } else @@ -712,10 +770,9 @@ struct gvar if (apply_to_all) for (unsigned int i = phantom_only ? count - 4 : 0; i < count; i++) { - unsigned int pt_index = i; - auto &delta = deltas.arrayZ[pt_index]; - delta.x += x_deltas.arrayZ[i] * scalar; - delta.y += y_deltas.arrayZ[i] * scalar; + auto &delta = deltas.arrayZ[i]; + delta.add_delta (x_deltas.arrayZ[i] * scalar, + y_deltas.arrayZ[i] * scalar); } else for (unsigned int i = 0; i < num_deltas; i++) @@ -725,8 +782,8 @@ struct gvar if (phantom_only && pt_index < count - 4) continue; auto &delta = deltas.arrayZ[pt_index]; delta.flag = 1; /* this point is referenced, i.e., explicit deltas specified */ - delta.x += x_deltas.arrayZ[i] * scalar; - delta.y += y_deltas.arrayZ[i] * scalar; + delta.add_delta (x_deltas.arrayZ[i] * scalar, + y_deltas.arrayZ[i] * scalar); } } else @@ -734,10 +791,9 @@ struct gvar if (apply_to_all) for (unsigned int i = phantom_only ? count - 4 : 0; i < count; i++) { - unsigned int pt_index = i; - auto &delta = deltas.arrayZ[pt_index]; - delta.x += x_deltas.arrayZ[i]; - delta.y += y_deltas.arrayZ[i]; + auto &delta = deltas.arrayZ[i]; + delta.add_delta (x_deltas.arrayZ[i], + y_deltas.arrayZ[i]); } else for (unsigned int i = 0; i < num_deltas; i++) @@ -747,8 +803,8 @@ struct gvar if (phantom_only && pt_index < count - 4) continue; auto &delta = deltas.arrayZ[pt_index]; delta.flag = 1; /* this point is referenced, i.e., explicit deltas specified */ - delta.x += x_deltas.arrayZ[i]; - delta.y += y_deltas.arrayZ[i]; + delta.add_delta (x_deltas.arrayZ[i], + y_deltas.arrayZ[i]); } } } @@ -756,17 +812,14 @@ struct gvar /* infer deltas for unreferenced points */ if (!apply_to_all && !phantom_only) { - if (!end_points) - { - for (unsigned i = 0; i < count; ++i) - if (points.arrayZ[i].is_end_point) - end_points.push (i); - if (unlikely (end_points.in_error ())) return false; - } - unsigned start_point = 0; - for (unsigned end_point : end_points) + unsigned end_point = 0; + while (true) { + while (end_point < count && !points.arrayZ[end_point].is_end_point) + end_point++; + if (unlikely (end_point == count)) break; + /* Check the number of unreferenced points in a contour. If no unref points or no ref points, nothing to do. */ unsigned unref_count = 0; for (unsigned i = start_point; i < end_point + 1; i++) @@ -809,7 +862,7 @@ struct gvar } } no_more_gaps: - start_point = end_point + 1; + start_point = end_point = end_point + 1; } } @@ -829,7 +882,7 @@ struct gvar unsigned int get_axis_count () const { return table->axisCount; } private: - hb_blob_ptr_t table; + hb_blob_ptr_t table; unsigned glyphCount; hb_vector_t> shared_tuple_active_idx; }; @@ -847,7 +900,7 @@ struct gvar NNOffset32To> sharedTuples; /* Offset from the start of this table to the shared tuple records. * Array of tuple records shared across all glyph variation data tables. */ - HBUINT16 glyphCountX; /* The number of glyphs in this font. This must match the number of + GidOffsetType glyphCountX; /* The number of glyphs in this font. This must match the number of * glyphs stored elsewhere in the font. */ HBUINT16 flags; /* Bit-field that gives the format of the offset array that follows. * If bit 0 is clear, the offsets are uint16; if bit 0 is set, the @@ -862,9 +915,15 @@ struct gvar DEFINE_SIZE_ARRAY (20, offsetZ); }; +using gvar = gvar_GVAR; +using GVAR = gvar_GVAR; + struct gvar_accelerator_t : gvar::accelerator_t { gvar_accelerator_t (hb_face_t *face) : gvar::accelerator_t (face) {} }; +struct GVAR_accelerator_t : GVAR::accelerator_t { + GVAR_accelerator_t (hb_face_t *face) : GVAR::accelerator_t (face) {} +}; } /* namespace OT */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-hvar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-hvar-table.hh index c1b40dcb49d95..40a85a62b788a 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-hvar-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-hvar-table.hh @@ -188,7 +188,7 @@ struct hvarvvar_subset_plan_t ~hvarvvar_subset_plan_t() { fini (); } void init (const hb_array_t &index_maps, - const VariationStore &_var_store, + const ItemVariationStore &_var_store, const hb_subset_plan_t *plan) { index_map_plans.resize (index_maps.length); @@ -263,7 +263,7 @@ struct hvarvvar_subset_plan_t hb_inc_bimap_t outer_map; hb_vector_t inner_maps; hb_vector_t index_map_plans; - const VariationStore *var_store; + const ItemVariationStore *var_store; protected: hb_vector_t inner_sets; @@ -288,6 +288,7 @@ struct HVARVVAR { TRACE_SANITIZE (this); return_trace (version.sanitize (c) && + hb_barrier () && likely (version.major == 1) && varStore.sanitize (c, this) && advMap.sanitize (c, this) && @@ -295,7 +296,7 @@ struct HVARVVAR rsbMap.sanitize (c, this)); } - const VariationStore& get_var_store () const + const ItemVariationStore& get_var_store () const { return this+varStore; } void listup_index_maps (hb_vector_t &index_maps) const @@ -383,7 +384,7 @@ struct HVARVVAR float get_advance_delta_unscaled (hb_codepoint_t glyph, const int *coords, unsigned int coord_count, - VariationStore::cache_t *store_cache = nullptr) const + ItemVariationStore::cache_t *store_cache = nullptr) const { uint32_t varidx = (this+advMap).map (glyph); return (this+varStore).get_delta (varidx, @@ -404,7 +405,7 @@ struct HVARVVAR public: FixedVersion<>version; /* Version of the metrics variation table * initially set to 0x00010000u */ - Offset32To + Offset32To varStore; /* Offset to item variation store table. */ Offset32To advMap; /* Offset to advance var-idx mapping. */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-mvar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-mvar-table.hh index ced04d89da3cd..c5a58d6f6bf93 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-mvar-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-mvar-table.hh @@ -56,7 +56,7 @@ struct VariationValueRecord public: Tag valueTag; /* Four-byte tag identifying a font-wide measure. */ - VarIdx varIdx; /* Outer/inner index into VariationStore item. */ + VarIdx varIdx; /* Outer/inner index into ItemVariationStore item. */ public: DEFINE_SIZE_STATIC (8); @@ -77,8 +77,10 @@ struct MVAR { TRACE_SANITIZE (this); return_trace (version.sanitize (c) && + hb_barrier () && likely (version.major == 1) && c->check_struct (this) && + hb_barrier () && valueRecordSize >= VariationValueRecord::static_size && varStore.sanitize (c, this) && c->check_range (valuesZ.arrayZ, @@ -104,7 +106,7 @@ struct MVAR out->valueRecordCount = valueRecordCount; item_variations_t item_vars; - const VariationStore& src_var_store = this+varStore; + const ItemVariationStore& src_var_store = this+varStore; if (!item_vars.instantiate (src_var_store, c->plan)) return_trace (false); @@ -157,7 +159,7 @@ protected: HBUINT16 valueRecordSize;/* The size in bytes of each value record — * must be greater than zero. */ HBUINT16 valueRecordCount;/* The number of value records — may be zero. */ - Offset16To + Offset16To varStore; /* Offset to item variation store table. */ UnsizedArrayOf valuesZ; /* Array of value records. The records must be diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-varc-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-varc-table.hh new file mode 100644 index 0000000000000..603aeb258cf1d --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-varc-table.hh @@ -0,0 +1,32 @@ +/* + * Copyright © 2024 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_VAR_VARC_TABLE_HH +#define HB_OT_VAR_VARC_TABLE_HH + +#include "OT/Var/VARC/VARC.hh" + +#endif /* HB_OT_VAR_VARC_TABLE_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-var.cc index 82bbbb0b1f229..8c695c41e24fe 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var.cc @@ -117,7 +117,7 @@ hb_ot_var_get_axes (hb_face_t *face, * in the specified face. * * Since: 1.4.2 - * Deprecated: 2.2.0 - use hb_ot_var_find_axis_info() instead + * Deprecated: 2.2.0: use hb_ot_var_find_axis_info() instead **/ hb_bool_t hb_ot_var_find_axis (hb_face_t *face, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-vorg-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-vorg-table.hh index dd3039924ef1f..b2300a327da27 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-vorg-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-vorg-table.hh @@ -117,6 +117,7 @@ struct VORG { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && version.major == 1 && vertYOrigins.sanitize (c)); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-outline.cc b/src/java.desktop/share/native/libharfbuzz/hb-outline.cc index a0a6ea156c356..85ffc793678d4 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-outline.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-outline.cc @@ -84,6 +84,12 @@ void hb_outline_t::replay (hb_draw_funcs_t *pen, void *pen_data) const } } +void hb_outline_t::slant (float slant_xy) +{ + for (auto &p : points) + p.x += slant_xy * p.y; +} + float hb_outline_t::control_area () const { float a = 0; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-outline.hh b/src/java.desktop/share/native/libharfbuzz/hb-outline.hh index c44625da2e17f..c5b68c05dd717 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-outline.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-outline.hh @@ -69,6 +69,7 @@ struct hb_outline_t HB_INTERNAL void replay (hb_draw_funcs_t *pen, void *pen_data) const; HB_INTERNAL float control_area () const; + HB_INTERNAL void slant (float slant_xy); HB_INTERNAL void embolden (float x_strength, float y_strength, float x_shift, float y_shift); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-paint-bounded.cc b/src/java.desktop/share/native/libharfbuzz/hb-paint-bounded.cc new file mode 100644 index 0000000000000..bc7e962bb463f --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-paint-bounded.cc @@ -0,0 +1,207 @@ +/* + * Copyright © 2022 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include "hb.hh" + +#ifndef HB_NO_PAINT + +#include "hb-paint-bounded.hh" + +#include "hb-machinery.hh" + + +/* + * This file implements boundedness computation of COLRv1 fonts as described in: + * + * https://learn.microsoft.com/en-us/typography/opentype/spec/colr#glyph-metrics-and-boundedness + */ + +static void +hb_paint_bounded_push_clip_glyph (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + hb_codepoint_t glyph, + hb_font_t *font, + void *user_data HB_UNUSED) +{ + hb_paint_bounded_context_t *c = (hb_paint_bounded_context_t *) paint_data; + + c->push_clip (); +} + +static void +hb_paint_bounded_push_clip_rectangle (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + float xmin, float ymin, float xmax, float ymax, + void *user_data) +{ + hb_paint_bounded_context_t *c = (hb_paint_bounded_context_t *) paint_data; + + c->push_clip (); +} + +static void +hb_paint_bounded_pop_clip (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + void *user_data HB_UNUSED) +{ + hb_paint_bounded_context_t *c = (hb_paint_bounded_context_t *) paint_data; + + c->pop_clip (); +} + +static void +hb_paint_bounded_push_group (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + void *user_data HB_UNUSED) +{ + hb_paint_bounded_context_t *c = (hb_paint_bounded_context_t *) paint_data; + + c->push_group (); +} + +static void +hb_paint_bounded_pop_group (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + hb_paint_composite_mode_t mode, + void *user_data HB_UNUSED) +{ + hb_paint_bounded_context_t *c = (hb_paint_bounded_context_t *) paint_data; + + c->pop_group (mode); +} + +static hb_bool_t +hb_paint_bounded_paint_image (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + hb_blob_t *blob HB_UNUSED, + unsigned int width HB_UNUSED, + unsigned int height HB_UNUSED, + hb_tag_t format HB_UNUSED, + float slant HB_UNUSED, + hb_glyph_extents_t *glyph_extents, + void *user_data HB_UNUSED) +{ + hb_paint_bounded_context_t *c = (hb_paint_bounded_context_t *) paint_data; + + c->push_clip (); + c->paint (); + c->pop_clip (); + + return true; +} + +static void +hb_paint_bounded_paint_color (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + hb_bool_t use_foreground HB_UNUSED, + hb_color_t color HB_UNUSED, + void *user_data HB_UNUSED) +{ + hb_paint_bounded_context_t *c = (hb_paint_bounded_context_t *) paint_data; + + c->paint (); +} + +static void +hb_paint_bounded_paint_linear_gradient (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + hb_color_line_t *color_line HB_UNUSED, + float x0 HB_UNUSED, float y0 HB_UNUSED, + float x1 HB_UNUSED, float y1 HB_UNUSED, + float x2 HB_UNUSED, float y2 HB_UNUSED, + void *user_data HB_UNUSED) +{ + hb_paint_bounded_context_t *c = (hb_paint_bounded_context_t *) paint_data; + + c->paint (); +} + +static void +hb_paint_bounded_paint_radial_gradient (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + hb_color_line_t *color_line HB_UNUSED, + float x0 HB_UNUSED, float y0 HB_UNUSED, float r0 HB_UNUSED, + float x1 HB_UNUSED, float y1 HB_UNUSED, float r1 HB_UNUSED, + void *user_data HB_UNUSED) +{ + hb_paint_bounded_context_t *c = (hb_paint_bounded_context_t *) paint_data; + + c->paint (); +} + +static void +hb_paint_bounded_paint_sweep_gradient (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + hb_color_line_t *color_line HB_UNUSED, + float cx HB_UNUSED, float cy HB_UNUSED, + float start_angle HB_UNUSED, + float end_angle HB_UNUSED, + void *user_data HB_UNUSED) +{ + hb_paint_bounded_context_t *c = (hb_paint_bounded_context_t *) paint_data; + + c->paint (); +} + +static inline void free_static_paint_bounded_funcs (); + +static struct hb_paint_bounded_funcs_lazy_loader_t : hb_paint_funcs_lazy_loader_t +{ + static hb_paint_funcs_t *create () + { + hb_paint_funcs_t *funcs = hb_paint_funcs_create (); + + hb_paint_funcs_set_push_clip_glyph_func (funcs, hb_paint_bounded_push_clip_glyph, nullptr, nullptr); + hb_paint_funcs_set_push_clip_rectangle_func (funcs, hb_paint_bounded_push_clip_rectangle, nullptr, nullptr); + hb_paint_funcs_set_pop_clip_func (funcs, hb_paint_bounded_pop_clip, nullptr, nullptr); + hb_paint_funcs_set_push_group_func (funcs, hb_paint_bounded_push_group, nullptr, nullptr); + hb_paint_funcs_set_pop_group_func (funcs, hb_paint_bounded_pop_group, nullptr, nullptr); + hb_paint_funcs_set_color_func (funcs, hb_paint_bounded_paint_color, nullptr, nullptr); + hb_paint_funcs_set_image_func (funcs, hb_paint_bounded_paint_image, nullptr, nullptr); + hb_paint_funcs_set_linear_gradient_func (funcs, hb_paint_bounded_paint_linear_gradient, nullptr, nullptr); + hb_paint_funcs_set_radial_gradient_func (funcs, hb_paint_bounded_paint_radial_gradient, nullptr, nullptr); + hb_paint_funcs_set_sweep_gradient_func (funcs, hb_paint_bounded_paint_sweep_gradient, nullptr, nullptr); + + hb_paint_funcs_make_immutable (funcs); + + hb_atexit (free_static_paint_bounded_funcs); + + return funcs; + } +} static_paint_bounded_funcs; + +static inline +void free_static_paint_bounded_funcs () +{ + static_paint_bounded_funcs.free_instance (); +} + +hb_paint_funcs_t * +hb_paint_bounded_get_funcs () +{ + return static_paint_bounded_funcs.get_unconst (); +} + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-paint-bounded.hh b/src/java.desktop/share/native/libharfbuzz/hb-paint-bounded.hh new file mode 100644 index 0000000000000..bdcdba76397f8 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-paint-bounded.hh @@ -0,0 +1,117 @@ +/* + * Copyright © 2022 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_PAINT_BOUNDED_HH +#define HB_PAINT_BOUNDED_HH + +#include "hb.hh" +#include "hb-paint.h" + +#include "hb-geometry.hh" + + +typedef struct hb_paint_bounded_context_t hb_paint_bounded_context_t; + +struct hb_paint_bounded_context_t +{ + void clear () + { + clips = 0; + bounded = true; + groups.clear (); + } + + hb_paint_bounded_context_t () + { + clear (); + } + + bool is_bounded () + { + return bounded; + } + + void push_clip () + { + clips++; + } + + void pop_clip () + { + if (clips == 0) return; + clips--; + } + + void push_group () + { + groups.push (bounded); + bounded = true; + } + + void pop_group (hb_paint_composite_mode_t mode) + { + const bool src_bounded = bounded; + bounded = groups.pop (); + bool &backdrop_bounded = bounded; + + // https://learn.microsoft.com/en-us/typography/opentype/spec/colr#format-32-paintcomposite + switch ((int) mode) + { + case HB_PAINT_COMPOSITE_MODE_CLEAR: + backdrop_bounded = true; + break; + case HB_PAINT_COMPOSITE_MODE_SRC: + case HB_PAINT_COMPOSITE_MODE_SRC_OUT: + backdrop_bounded = src_bounded; + break; + case HB_PAINT_COMPOSITE_MODE_DEST: + case HB_PAINT_COMPOSITE_MODE_DEST_OUT: + break; + case HB_PAINT_COMPOSITE_MODE_SRC_IN: + case HB_PAINT_COMPOSITE_MODE_DEST_IN: + backdrop_bounded = backdrop_bounded && src_bounded; + break; + default: + backdrop_bounded = backdrop_bounded || src_bounded; + break; + } + } + + void paint () + { + if (!clips) + bounded = false; + } + + protected: + bool bounded; // true if current drawing bounded + unsigned clips; // number of active clips + hb_vector_t groups; // true if group bounded +}; + +HB_INTERNAL hb_paint_funcs_t * +hb_paint_bounded_get_funcs (); + + +#endif /* HB_PAINT_BOUNDED_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.cc b/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.cc index 3bbe635172585..bc08c5a9e14a9 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.cc @@ -28,14 +28,13 @@ #include "hb-paint-extents.hh" -#include "hb-draw.h" +#include "hb-draw.hh" #include "hb-machinery.hh" /* - * This file implements bounds-extraction as well as boundedness - * computation of COLRv1 fonts as described in: + * This file implements bounds-extraction computation of COLRv1 fonts as described in: * * https://learn.microsoft.com/en-us/typography/opentype/spec/colr#glyph-metrics-and-boundedness */ @@ -63,93 +62,6 @@ hb_paint_extents_pop_transform (hb_paint_funcs_t *funcs HB_UNUSED, c->pop_transform (); } -static void -hb_draw_extents_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED, - void *data, - hb_draw_state_t *st, - float to_x, float to_y, - void *user_data HB_UNUSED) -{ - hb_extents_t *extents = (hb_extents_t *) data; - - extents->add_point (to_x, to_y); -} - -static void -hb_draw_extents_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED, - void *data, - hb_draw_state_t *st, - float to_x, float to_y, - void *user_data HB_UNUSED) -{ - hb_extents_t *extents = (hb_extents_t *) data; - - extents->add_point (to_x, to_y); -} - -static void -hb_draw_extents_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, - void *data, - hb_draw_state_t *st, - float control_x, float control_y, - float to_x, float to_y, - void *user_data HB_UNUSED) -{ - hb_extents_t *extents = (hb_extents_t *) data; - - extents->add_point (control_x, control_y); - extents->add_point (to_x, to_y); -} - -static void -hb_draw_extents_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, - void *data, - hb_draw_state_t *st, - float control1_x, float control1_y, - float control2_x, float control2_y, - float to_x, float to_y, - void *user_data HB_UNUSED) -{ - hb_extents_t *extents = (hb_extents_t *) data; - - extents->add_point (control1_x, control1_y); - extents->add_point (control2_x, control2_y); - extents->add_point (to_x, to_y); -} - -static inline void free_static_draw_extents_funcs (); - -static struct hb_draw_extents_funcs_lazy_loader_t : hb_draw_funcs_lazy_loader_t -{ - static hb_draw_funcs_t *create () - { - hb_draw_funcs_t *funcs = hb_draw_funcs_create (); - - hb_draw_funcs_set_move_to_func (funcs, hb_draw_extents_move_to, nullptr, nullptr); - hb_draw_funcs_set_line_to_func (funcs, hb_draw_extents_line_to, nullptr, nullptr); - hb_draw_funcs_set_quadratic_to_func (funcs, hb_draw_extents_quadratic_to, nullptr, nullptr); - hb_draw_funcs_set_cubic_to_func (funcs, hb_draw_extents_cubic_to, nullptr, nullptr); - - hb_draw_funcs_make_immutable (funcs); - - hb_atexit (free_static_draw_extents_funcs); - - return funcs; - } -} static_draw_extents_funcs; - -static inline -void free_static_draw_extents_funcs () -{ - static_draw_extents_funcs.free_instance (); -} - -static hb_draw_funcs_t * -hb_draw_extents_get_funcs () -{ - return static_draw_extents_funcs.get_unconst (); -} - static void hb_paint_extents_push_clip_glyph (hb_paint_funcs_t *funcs HB_UNUSED, void *paint_data, @@ -221,6 +133,9 @@ hb_paint_extents_paint_image (hb_paint_funcs_t *funcs HB_UNUSED, { hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; + if (!glyph_extents) + return false; // Happens with SVG images. + hb_extents_t extents = {(float) glyph_extents->x_bearing, (float) glyph_extents->y_bearing + glyph_extents->height, (float) glyph_extents->x_bearing + glyph_extents->width, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.hh b/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.hh index 518228c414b54..60374eaa2d2a6 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.hh @@ -28,181 +28,29 @@ #include "hb.hh" #include "hb-paint.h" +#include "hb-geometry.hh" -typedef struct hb_extents_t -{ - hb_extents_t () {} - hb_extents_t (float xmin, float ymin, float xmax, float ymax) : - xmin (xmin), ymin (ymin), xmax (xmax), ymax (ymax) {} - - bool is_empty () const { return xmin >= xmax || ymin >= ymax; } - bool is_void () const { return xmin > xmax; } - - void union_ (const hb_extents_t &o) - { - xmin = hb_min (xmin, o.xmin); - ymin = hb_min (ymin, o.ymin); - xmax = hb_max (xmax, o.xmax); - ymax = hb_max (ymax, o.ymax); - } - - void intersect (const hb_extents_t &o) - { - xmin = hb_max (xmin, o.xmin); - ymin = hb_max (ymin, o.ymin); - xmax = hb_min (xmax, o.xmax); - ymax = hb_min (ymax, o.ymax); - } - - void - add_point (float x, float y) - { - if (unlikely (is_void ())) - { - xmin = xmax = x; - ymin = ymax = y; - } - else - { - xmin = hb_min (xmin, x); - ymin = hb_min (ymin, y); - xmax = hb_max (xmax, x); - ymax = hb_max (ymax, y); - } - } - - float xmin = 0.f; - float ymin = 0.f; - float xmax = -1.f; - float ymax = -1.f; -} hb_extents_t; - -typedef struct hb_transform_t -{ - hb_transform_t () {} - hb_transform_t (float xx, float yx, - float xy, float yy, - float x0, float y0) : - xx (xx), yx (yx), xy (xy), yy (yy), x0 (x0), y0 (y0) {} - - void multiply (const hb_transform_t &o) - { - /* Copied from cairo, with "o" being "a" there and "this" being "b" there. */ - hb_transform_t r; - - r.xx = o.xx * xx + o.yx * xy; - r.yx = o.xx * yx + o.yx * yy; - - r.xy = o.xy * xx + o.yy * xy; - r.yy = o.xy * yx + o.yy * yy; - - r.x0 = o.x0 * xx + o.y0 * xy + x0; - r.y0 = o.x0 * yx + o.y0 * yy + y0; - - *this = r; - } - - void transform_distance (float &dx, float &dy) const - { - float new_x = xx * dx + xy * dy; - float new_y = yx * dx + yy * dy; - dx = new_x; - dy = new_y; - } - - void transform_point (float &x, float &y) const - { - transform_distance (x, y); - x += x0; - y += y0; - } - - void transform_extents (hb_extents_t &extents) const - { - float quad_x[4], quad_y[4]; - - quad_x[0] = extents.xmin; - quad_y[0] = extents.ymin; - quad_x[1] = extents.xmin; - quad_y[1] = extents.ymax; - quad_x[2] = extents.xmax; - quad_y[2] = extents.ymin; - quad_x[3] = extents.xmax; - quad_y[3] = extents.ymax; - - extents = hb_extents_t {}; - for (unsigned i = 0; i < 4; i++) - { - transform_point (quad_x[i], quad_y[i]); - extents.add_point (quad_x[i], quad_y[i]); - } - } - - float xx = 1.f; - float yx = 0.f; - float xy = 0.f; - float yy = 1.f; - float x0 = 0.f; - float y0 = 0.f; -} hb_transform_t; - -typedef struct hb_bounds_t -{ - enum status_t { - UNBOUNDED, - BOUNDED, - EMPTY, - }; - - hb_bounds_t (status_t status) : status (status) {} - hb_bounds_t (const hb_extents_t &extents) : - status (extents.is_empty () ? EMPTY : BOUNDED), extents (extents) {} - - void union_ (const hb_bounds_t &o) - { - if (o.status == UNBOUNDED) - status = UNBOUNDED; - else if (o.status == BOUNDED) - { - if (status == EMPTY) - *this = o; - else if (status == BOUNDED) - extents.union_ (o.extents); - } - } - - void intersect (const hb_bounds_t &o) - { - if (o.status == EMPTY) - status = EMPTY; - else if (o.status == BOUNDED) - { - if (status == UNBOUNDED) - *this = o; - else if (status == BOUNDED) - { - extents.intersect (o.extents); - if (extents.is_empty ()) - status = EMPTY; - } - } - } - - status_t status; - hb_extents_t extents; -} hb_bounds_t; typedef struct hb_paint_extents_context_t hb_paint_extents_context_t; struct hb_paint_extents_context_t { - hb_paint_extents_context_t () + void clear () { + transforms.clear (); + clips.clear (); + groups.clear (); + transforms.push (hb_transform_t{}); clips.push (hb_bounds_t{hb_bounds_t::UNBOUNDED}); groups.push (hb_bounds_t{hb_bounds_t::EMPTY}); } + hb_paint_extents_context_t () + { + clear (); + } + hb_extents_t get_extents () { return groups.tail().extents; @@ -231,7 +79,10 @@ struct hb_paint_extents_context_t const hb_transform_t &t = transforms.tail (); t.transform_extents (extents); - clips.push (hb_bounds_t {extents}); + auto bounds = hb_bounds_t {extents}; + bounds.intersect (clips.tail ()); + + clips.push (bounds); } void pop_clip () diff --git a/src/java.desktop/share/native/libharfbuzz/hb-paint.cc b/src/java.desktop/share/native/libharfbuzz/hb-paint.cc index 6f2a296744fc2..094597d6457d8 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-paint.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-paint.cc @@ -87,7 +87,7 @@ hb_paint_image_nil (hb_paint_funcs_t *funcs, void *paint_data, unsigned int width, unsigned int height, hb_tag_t format, - float slant_xy, + float slant_xy_deprecated, hb_glyph_extents_t *extents, void *user_data) { return false; } @@ -464,6 +464,42 @@ hb_paint_push_transform (hb_paint_funcs_t *funcs, void *paint_data, funcs->push_transform (paint_data, xx, yx, xy, yy, dx, dy); } +/** + * hb_paint_push_font_transform: + * @funcs: paint functions + * @paint_data: associated data passed by the caller + * @font: a font + * + * Push the transform reflecting the font's scale and slant + * settings onto the paint functions. + * + * Since: 11.0.0 + */ +void +hb_paint_push_font_transform (hb_paint_funcs_t *funcs, void *paint_data, + const hb_font_t *font) +{ + funcs->push_font_transform (paint_data, font); +} + +/** + * hb_paint_push_inverse_font_transform: + * @funcs: paint functions + * @paint_data: associated data passed by the caller + * @font: a font + * + * Push the inverse of the transform reflecting the font's + * scale and slant settings onto the paint functions. + * + * Since: 11.0.0 + */ +void +hb_paint_push_inverse_font_transform (hb_paint_funcs_t *funcs, void *paint_data, + const hb_font_t *font) +{ + funcs->push_inverse_font_transform (paint_data, font); +} + /** * hb_paint_pop_transform: * @funcs: paint functions @@ -579,7 +615,7 @@ hb_paint_color (hb_paint_funcs_t *funcs, void *paint_data, * @width: width of the raster image in pixels, or 0 * @height: height of the raster image in pixels, or 0 * @format: the image format as a tag - * @slant: the synthetic slant ratio to be applied to the image during rendering + * @slant: Deprecated. set to 0.0 * @extents: (nullable): the extents of the glyph * * Perform a "image" paint operation. @@ -592,10 +628,10 @@ hb_paint_image (hb_paint_funcs_t *funcs, void *paint_data, unsigned int width, unsigned int height, hb_tag_t format, - float slant, + HB_UNUSED float slant, hb_glyph_extents_t *extents) { - funcs->image (paint_data, image, width, height, format, slant, extents); + funcs->image (paint_data, image, width, height, format, 0.f, extents); } /** @@ -646,7 +682,7 @@ hb_paint_radial_gradient (hb_paint_funcs_t *funcs, void *paint_data, float x0, float y0, float r0, float x1, float y1, float r1) { - funcs->radial_gradient (paint_data, color_line, x0, y0, r0, y1, x1, r1); + funcs->radial_gradient (paint_data, color_line, x0, y0, r0, x1, y1, r1); } /** diff --git a/src/java.desktop/share/native/libharfbuzz/hb-paint.h b/src/java.desktop/share/native/libharfbuzz/hb-paint.h index 6215488e2e7bf..9827a02887b21 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-paint.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-paint.h @@ -146,7 +146,7 @@ typedef void (*hb_paint_pop_transform_func_t) (hb_paint_funcs_t *funcs, * * A virtual method for the #hb_paint_funcs_t to render a color glyph by glyph index. * - * Return value: %true if the glyph was painted, %false otherwise. + * Return value: `true` if the glyph was painted, `false` otherwise. * * Since: 8.2.0 */ @@ -167,8 +167,10 @@ typedef hb_bool_t (*hb_paint_color_glyph_func_t) (hb_paint_funcs_t *funcs, * A virtual method for the #hb_paint_funcs_t to clip * subsequent paint calls to the outline of a glyph. * - * The coordinates of the glyph outline are interpreted according - * to the current transform. + * The coordinates of the glyph outline are expected in the + * current @font scale (ie. the results of calling + * hb_font_draw_glyph() with @font). The outline is + * transformed by the current transform. * * This clip is applied in addition to the current clip, * and remains in effect until a matching call to @@ -281,7 +283,7 @@ typedef void (*hb_paint_color_func_t) (hb_paint_funcs_t *funcs, * @width: width of the raster image in pixels, or 0 * @height: height of the raster image in pixels, or 0 * @format: the image format as a tag - * @slant: the synthetic slant ratio to be applied to the image during rendering + * @slant: Deprecated. Always set to 0.0. * @extents: (nullable): glyph extents for desired rendering * @user_data: User data pointer passed to hb_paint_funcs_set_image_func() * @@ -956,6 +958,14 @@ hb_paint_push_transform (hb_paint_funcs_t *funcs, void *paint_data, float xy, float yy, float dx, float dy); +HB_EXTERN void +hb_paint_push_font_transform (hb_paint_funcs_t *funcs, void *paint_data, + const hb_font_t *font); + +HB_EXTERN void +hb_paint_push_inverse_font_transform (hb_paint_funcs_t *funcs, void *paint_data, + const hb_font_t *font); + HB_EXTERN void hb_paint_pop_transform (hb_paint_funcs_t *funcs, void *paint_data); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-paint.hh b/src/java.desktop/share/native/libharfbuzz/hb-paint.hh index fbf4a08cfffd5..2abadebab3cbb 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-paint.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-paint.hh @@ -157,27 +157,29 @@ struct hb_paint_funcs_t /* Internal specializations. */ - void push_root_transform (void *paint_data, + void push_font_transform (void *paint_data, const hb_font_t *font) { float upem = font->face->get_upem (); int xscale = font->x_scale, yscale = font->y_scale; - float slant = font->slant_xy; push_transform (paint_data, - xscale/upem, 0, slant * yscale/upem, yscale/upem, 0, 0); + xscale/upem, 0, + 0, yscale/upem, + 0, 0); } - void push_inverse_root_transform (void *paint_data, - hb_font_t *font) + void push_inverse_font_transform (void *paint_data, + const hb_font_t *font) { float upem = font->face->get_upem (); int xscale = font->x_scale ? font->x_scale : upem; int yscale = font->y_scale ? font->y_scale : upem; - float slant = font->slant_xy; push_transform (paint_data, - upem/xscale, 0, -slant * upem/xscale, upem/yscale, 0, 0); + upem/xscale, 0, + 0, upem/yscale, + 0, 0); } HB_NODISCARD diff --git a/src/java.desktop/share/native/libharfbuzz/hb-priority-queue.hh b/src/java.desktop/share/native/libharfbuzz/hb-priority-queue.hh index 2c8ccbfb68856..274d5df4c541c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-priority-queue.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-priority-queue.hh @@ -55,6 +55,9 @@ struct hb_priority_queue_t bool in_error () const { return heap.in_error (); } + bool alloc (unsigned size) + { return heap.alloc (size); } + #ifndef HB_OPTIMIZE_SIZE HB_ALWAYS_INLINE #endif @@ -160,7 +163,7 @@ struct hb_priority_queue_t goto repeat; } - void swap (unsigned a, unsigned b) + void swap (unsigned a, unsigned b) noexcept { assert (a < heap.length); assert (b < heap.length); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-repacker.hh b/src/java.desktop/share/native/libharfbuzz/hb-repacker.hh index e9cd376ad3721..cb4fdeead2e1e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-repacker.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-repacker.hh @@ -238,6 +238,54 @@ bool _try_isolating_subgraphs (const hb_vector_t& over return true; } +static inline +bool _resolve_shared_overflow(const hb_vector_t& overflows, + int overflow_index, + graph_t& sorted_graph) +{ + const graph::overflow_record_t& r = overflows[overflow_index]; + + // Find all of the parents in overflowing links that link to this + // same child node. We will then try duplicating the child node and + // re-assigning all of these parents to the duplicate. + hb_set_t parents; + parents.add(r.parent); + for (int i = overflow_index - 1; i >= 0; i--) { + const graph::overflow_record_t& r2 = overflows[i]; + if (r2.child == r.child) { + parents.add(r2.parent); + } + } + + unsigned result = sorted_graph.duplicate(&parents, r.child); + if (result == (unsigned) -1 && parents.get_population() > 2) { + // All links to the child are overflowing, so we can't include all + // in the duplication. Remove one parent from the duplication. + // Remove the lowest index parent, which will be the closest to the child. + parents.del(parents.get_min()); + result = sorted_graph.duplicate(&parents, r.child); + } + + if (result == (unsigned) -1) return result; + + if (parents.get_population() > 1) { + // If the duplicated node has more than one parent pre-emptively raise it's priority to the maximum. + // This will place it close to the parents. Node's with only one parent, don't need this as normal overflow + // resolution will raise priority if needed. + // + // Reasoning: most of the parents to this child are likely at the same layer in the graph. Duplicating + // the child will theoretically allow it to be placed closer to it's parents. However, due to the shortest + // distance sort by default it's placement will remain in the same layer, thus it will remain in roughly the + // same position (and distance from parents) as the original child node. The overflow resolution will attempt + // to move nodes closer, but only for non-shared nodes. Since this node is shared, it will simply be given + // further duplication which defeats the attempt to duplicate with multiple parents. To fix this we + // pre-emptively raise priority now which allows the duplicated node to pack into the same layer as it's parents. + sorted_graph.vertices_[result].give_max_priority(); + } + + return result; +} + static inline bool _process_overflows (const hb_vector_t& overflows, hb_set_t& priority_bumped_parents, @@ -254,7 +302,7 @@ bool _process_overflows (const hb_vector_t& overflows, { // The child object is shared, we may be able to eliminate the overflow // by duplicating it. - if (sorted_graph.duplicate (r.parent, r.child) == (unsigned) -1) continue; + if (!_resolve_shared_overflow(overflows, i, sorted_graph)) continue; return true; } @@ -289,9 +337,10 @@ bool _process_overflows (const hb_vector_t& overflows, inline bool hb_resolve_graph_overflows (hb_tag_t table_tag, unsigned max_rounds , - bool recalculate_extensions, + bool always_recalculate_extensions, graph_t& sorted_graph /* IN/OUT */) { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Repacking %c%c%c%c.", HB_UNTAG(table_tag)); sorted_graph.sort_shortest_distance (); if (sorted_graph.in_error ()) { @@ -303,12 +352,12 @@ hb_resolve_graph_overflows (hb_tag_t table_tag, if (!will_overflow) return true; + bool is_gsub_or_gpos = (table_tag == HB_OT_TAG_GPOS || table_tag == HB_OT_TAG_GSUB); graph::gsubgpos_graph_context_t ext_context (table_tag, sorted_graph); - if ((table_tag == HB_OT_TAG_GPOS - || table_tag == HB_OT_TAG_GSUB) - && will_overflow) + if (is_gsub_or_gpos && will_overflow) { - if (recalculate_extensions) + DEBUG_MSG (SUBSET_REPACK, nullptr, "Applying GSUB/GPOS repacking specializations."); + if (always_recalculate_extensions) { DEBUG_MSG (SUBSET_REPACK, nullptr, "Splitting subtables if needed."); if (!_presplit_subtables_if_needed (ext_context)) { @@ -364,6 +413,13 @@ hb_resolve_graph_overflows (hb_tag_t table_tag, if (graph::will_overflow (sorted_graph)) { + if (is_gsub_or_gpos && !always_recalculate_extensions) { + // If this a GSUB/GPOS table and we didn't try to extension promotion and table splitting then + // as a last ditch effort, re-run the repacker with it enabled. + DEBUG_MSG (SUBSET_REPACK, nullptr, "Failed to find a resolution. Re-running with extension promotion and table splitting enabled."); + return hb_resolve_graph_overflows (table_tag, max_rounds, true, sorted_graph); + } + DEBUG_MSG (SUBSET_REPACK, nullptr, "Offset overflow resolution failed."); return false; } @@ -388,7 +444,7 @@ template inline hb_blob_t* hb_resolve_overflows (const T& packed, hb_tag_t table_tag, - unsigned max_rounds = 20, + unsigned max_rounds = 32, bool recalculate_extensions = false) { graph_t sorted_graph (packed); if (sorted_graph.in_error ()) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-sanitize.hh b/src/java.desktop/share/native/libharfbuzz/hb-sanitize.hh index 3a6ec4fed9c00..1f8ba32bae204 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-sanitize.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-sanitize.hh @@ -72,8 +72,8 @@ * * === The sanitize() contract === * - * The sanitize() method of each object type shall return true if it's safe to - * call other methods of the object, and %false otherwise. + * The sanitize() method of each object type shall return `true` if it's safe to + * call other methods of the object, and `false` otherwise. * * Note that what sanitize() checks for might align with what the specification * describes as valid table data, but does not have to be. In particular, we @@ -134,7 +134,10 @@ struct hb_sanitize_context_t : const char *get_name () { return "SANITIZE"; } template bool may_dispatch (const T *obj HB_UNUSED, const F *format) - { return format->sanitize (this); } + { + return format->sanitize (this) && + hb_barrier (); + } static return_t default_return_value () { return true; } static return_t no_dispatch_return_value () { return false; } bool stop_sublookup_iteration (const return_t r) const { return !r; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-script-list.h b/src/java.desktop/share/native/libharfbuzz/hb-script-list.h new file mode 100644 index 0000000000000..f811dbc340805 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-script-list.h @@ -0,0 +1,484 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_SCRIPT_LIST_H +#define HB_SCRIPT_LIST_H + +/* This file belongs to the middle of hb-common.h. + * The reason it has been surgically extracted is because + * FreeType imports types and enums from hb-common.h, + * and since this enum is large and growing, we want to + * make it easy to just copy the file over to FreeType. + * https://github.com/harfbuzz/harfbuzz/issues/5271 + */ + +/* Dummy lines to make our checks happy. */ +#if 0 +#include "hb-common.h" +HB_BEGIN_DECLS +HB_END_DECLS +#endif + + +/** + * hb_script_t: + * @HB_SCRIPT_COMMON: `Zyyy` + * @HB_SCRIPT_INHERITED: `Zinh` + * @HB_SCRIPT_UNKNOWN: `Zzzz` + * @HB_SCRIPT_ARABIC: `Arab` + * @HB_SCRIPT_ARMENIAN: `Armn` + * @HB_SCRIPT_BENGALI: `Beng` + * @HB_SCRIPT_CYRILLIC: `Cyrl` + * @HB_SCRIPT_DEVANAGARI: `Deva` + * @HB_SCRIPT_GEORGIAN: `Geor` + * @HB_SCRIPT_GREEK: `Grek` + * @HB_SCRIPT_GUJARATI: `Gujr` + * @HB_SCRIPT_GURMUKHI: `Guru` + * @HB_SCRIPT_HANGUL: `Hang` + * @HB_SCRIPT_HAN: `Hani` + * @HB_SCRIPT_HEBREW: `Hebr` + * @HB_SCRIPT_HIRAGANA: `Hira` + * @HB_SCRIPT_KANNADA: `Knda` + * @HB_SCRIPT_KATAKANA: `Kana` + * @HB_SCRIPT_LAO: `Laoo` + * @HB_SCRIPT_LATIN: `Latn` + * @HB_SCRIPT_MALAYALAM: `Mlym` + * @HB_SCRIPT_ORIYA: `Orya` + * @HB_SCRIPT_TAMIL: `Taml` + * @HB_SCRIPT_TELUGU: `Telu` + * @HB_SCRIPT_THAI: `Thai` + * @HB_SCRIPT_TIBETAN: `Tibt` + * @HB_SCRIPT_BOPOMOFO: `Bopo` + * @HB_SCRIPT_BRAILLE: `Brai` + * @HB_SCRIPT_CANADIAN_SYLLABICS: `Cans` + * @HB_SCRIPT_CHEROKEE: `Cher` + * @HB_SCRIPT_ETHIOPIC: `Ethi` + * @HB_SCRIPT_KHMER: `Khmr` + * @HB_SCRIPT_MONGOLIAN: `Mong` + * @HB_SCRIPT_MYANMAR: `Mymr` + * @HB_SCRIPT_OGHAM: `Ogam` + * @HB_SCRIPT_RUNIC: `Runr` + * @HB_SCRIPT_SINHALA: `Sinh` + * @HB_SCRIPT_SYRIAC: `Syrc` + * @HB_SCRIPT_THAANA: `Thaa` + * @HB_SCRIPT_YI: `Yiii` + * @HB_SCRIPT_DESERET: `Dsrt` + * @HB_SCRIPT_GOTHIC: `Goth` + * @HB_SCRIPT_OLD_ITALIC: `Ital` + * @HB_SCRIPT_BUHID: `Buhd` + * @HB_SCRIPT_HANUNOO: `Hano` + * @HB_SCRIPT_TAGALOG: `Tglg` + * @HB_SCRIPT_TAGBANWA: `Tagb` + * @HB_SCRIPT_CYPRIOT: `Cprt` + * @HB_SCRIPT_LIMBU: `Limb` + * @HB_SCRIPT_LINEAR_B: `Linb` + * @HB_SCRIPT_OSMANYA: `Osma` + * @HB_SCRIPT_SHAVIAN: `Shaw` + * @HB_SCRIPT_TAI_LE: `Tale` + * @HB_SCRIPT_UGARITIC: `Ugar` + * @HB_SCRIPT_BUGINESE: `Bugi` + * @HB_SCRIPT_COPTIC: `Copt` + * @HB_SCRIPT_GLAGOLITIC: `Glag` + * @HB_SCRIPT_KHAROSHTHI: `Khar` + * @HB_SCRIPT_NEW_TAI_LUE: `Talu` + * @HB_SCRIPT_OLD_PERSIAN: `Xpeo` + * @HB_SCRIPT_SYLOTI_NAGRI: `Sylo` + * @HB_SCRIPT_TIFINAGH: `Tfng` + * @HB_SCRIPT_BALINESE: `Bali` + * @HB_SCRIPT_CUNEIFORM: `Xsux` + * @HB_SCRIPT_NKO: `Nkoo` + * @HB_SCRIPT_PHAGS_PA: `Phag` + * @HB_SCRIPT_PHOENICIAN: `Phnx` + * @HB_SCRIPT_CARIAN: `Cari` + * @HB_SCRIPT_CHAM: `Cham` + * @HB_SCRIPT_KAYAH_LI: `Kali` + * @HB_SCRIPT_LEPCHA: `Lepc` + * @HB_SCRIPT_LYCIAN: `Lyci` + * @HB_SCRIPT_LYDIAN: `Lydi` + * @HB_SCRIPT_OL_CHIKI: `Olck` + * @HB_SCRIPT_REJANG: `Rjng` + * @HB_SCRIPT_SAURASHTRA: `Saur` + * @HB_SCRIPT_SUNDANESE: `Sund` + * @HB_SCRIPT_VAI: `Vaii` + * @HB_SCRIPT_AVESTAN: `Avst` + * @HB_SCRIPT_BAMUM: `Bamu` + * @HB_SCRIPT_EGYPTIAN_HIEROGLYPHS: `Egyp` + * @HB_SCRIPT_IMPERIAL_ARAMAIC: `Armi` + * @HB_SCRIPT_INSCRIPTIONAL_PAHLAVI: `Phli` + * @HB_SCRIPT_INSCRIPTIONAL_PARTHIAN: `Prti` + * @HB_SCRIPT_JAVANESE: `Java` + * @HB_SCRIPT_KAITHI: `Kthi` + * @HB_SCRIPT_LISU: `Lisu` + * @HB_SCRIPT_MEETEI_MAYEK: `Mtei` + * @HB_SCRIPT_OLD_SOUTH_ARABIAN: `Sarb` + * @HB_SCRIPT_OLD_TURKIC: `Orkh` + * @HB_SCRIPT_SAMARITAN: `Samr` + * @HB_SCRIPT_TAI_THAM: `Lana` + * @HB_SCRIPT_TAI_VIET: `Tavt` + * @HB_SCRIPT_BATAK: `Batk` + * @HB_SCRIPT_BRAHMI: `Brah` + * @HB_SCRIPT_MANDAIC: `Mand` + * @HB_SCRIPT_CHAKMA: `Cakm` + * @HB_SCRIPT_MEROITIC_CURSIVE: `Merc` + * @HB_SCRIPT_MEROITIC_HIEROGLYPHS: `Mero` + * @HB_SCRIPT_MIAO: `Plrd` + * @HB_SCRIPT_SHARADA: `Shrd` + * @HB_SCRIPT_SORA_SOMPENG: `Sora` + * @HB_SCRIPT_TAKRI: `Takr` + * @HB_SCRIPT_BASSA_VAH: `Bass`, Since: 0.9.30 + * @HB_SCRIPT_CAUCASIAN_ALBANIAN: `Aghb`, Since: 0.9.30 + * @HB_SCRIPT_DUPLOYAN: `Dupl`, Since: 0.9.30 + * @HB_SCRIPT_ELBASAN: `Elba`, Since: 0.9.30 + * @HB_SCRIPT_GRANTHA: `Gran`, Since: 0.9.30 + * @HB_SCRIPT_KHOJKI: `Khoj`, Since: 0.9.30 + * @HB_SCRIPT_KHUDAWADI: `Sind`, Since: 0.9.30 + * @HB_SCRIPT_LINEAR_A: `Lina`, Since: 0.9.30 + * @HB_SCRIPT_MAHAJANI: `Mahj`, Since: 0.9.30 + * @HB_SCRIPT_MANICHAEAN: `Mani`, Since: 0.9.30 + * @HB_SCRIPT_MENDE_KIKAKUI: `Mend`, Since: 0.9.30 + * @HB_SCRIPT_MODI: `Modi`, Since: 0.9.30 + * @HB_SCRIPT_MRO: `Mroo`, Since: 0.9.30 + * @HB_SCRIPT_NABATAEAN: `Nbat`, Since: 0.9.30 + * @HB_SCRIPT_OLD_NORTH_ARABIAN: `Narb`, Since: 0.9.30 + * @HB_SCRIPT_OLD_PERMIC: `Perm`, Since: 0.9.30 + * @HB_SCRIPT_PAHAWH_HMONG: `Hmng`, Since: 0.9.30 + * @HB_SCRIPT_PALMYRENE: `Palm`, Since: 0.9.30 + * @HB_SCRIPT_PAU_CIN_HAU: `Pauc`, Since: 0.9.30 + * @HB_SCRIPT_PSALTER_PAHLAVI: `Phlp`, Since: 0.9.30 + * @HB_SCRIPT_SIDDHAM: `Sidd`, Since: 0.9.30 + * @HB_SCRIPT_TIRHUTA: `Tirh`, Since: 0.9.30 + * @HB_SCRIPT_WARANG_CITI: `Wara`, Since: 0.9.30 + * @HB_SCRIPT_AHOM: `Ahom`, Since: 0.9.30 + * @HB_SCRIPT_ANATOLIAN_HIEROGLYPHS: `Hluw`, Since: 0.9.30 + * @HB_SCRIPT_HATRAN: `Hatr`, Since: 0.9.30 + * @HB_SCRIPT_MULTANI: `Mult`, Since: 0.9.30 + * @HB_SCRIPT_OLD_HUNGARIAN: `Hung`, Since: 0.9.30 + * @HB_SCRIPT_SIGNWRITING: `Sgnw`, Since: 0.9.30 + * @HB_SCRIPT_ADLAM: `Adlm`, Since: 1.3.0 + * @HB_SCRIPT_BHAIKSUKI: `Bhks`, Since: 1.3.0 + * @HB_SCRIPT_MARCHEN: `Marc`, Since: 1.3.0 + * @HB_SCRIPT_OSAGE: `Osge`, Since: 1.3.0 + * @HB_SCRIPT_TANGUT: `Tang`, Since: 1.3.0 + * @HB_SCRIPT_NEWA: `Newa`, Since: 1.3.0 + * @HB_SCRIPT_MASARAM_GONDI: `Gonm`, Since: 1.6.0 + * @HB_SCRIPT_NUSHU: `Nshu`, Since: 1.6.0 + * @HB_SCRIPT_SOYOMBO: `Soyo`, Since: 1.6.0 + * @HB_SCRIPT_ZANABAZAR_SQUARE: `Zanb`, Since: 1.6.0 + * @HB_SCRIPT_DOGRA: `Dogr`, Since: 1.8.0 + * @HB_SCRIPT_GUNJALA_GONDI: `Gong`, Since: 1.8.0 + * @HB_SCRIPT_HANIFI_ROHINGYA: `Rohg`, Since: 1.8.0 + * @HB_SCRIPT_MAKASAR: `Maka`, Since: 1.8.0 + * @HB_SCRIPT_MEDEFAIDRIN: `Medf`, Since: 1.8.0 + * @HB_SCRIPT_OLD_SOGDIAN: `Sogo`, Since: 1.8.0 + * @HB_SCRIPT_SOGDIAN: `Sogd`, Since: 1.8.0 + * @HB_SCRIPT_ELYMAIC: `Elym`, Since: 2.4.0 + * @HB_SCRIPT_NANDINAGARI: `Nand`, Since: 2.4.0 + * @HB_SCRIPT_NYIAKENG_PUACHUE_HMONG: `Hmnp`, Since: 2.4.0 + * @HB_SCRIPT_WANCHO: `Wcho`, Since: 2.4.0 + * @HB_SCRIPT_CHORASMIAN: `Chrs`, Since: 2.6.7 + * @HB_SCRIPT_DIVES_AKURU: `Diak`, Since: 2.6.7 + * @HB_SCRIPT_KHITAN_SMALL_SCRIPT: `Kits`, Since: 2.6.7 + * @HB_SCRIPT_YEZIDI: `Yezi`, Since: 2.6.7 + * @HB_SCRIPT_CYPRO_MINOAN: `Cpmn`, Since: 3.0.0 + * @HB_SCRIPT_OLD_UYGHUR: `Ougr`, Since: 3.0.0 + * @HB_SCRIPT_TANGSA: `Tnsa`, Since: 3.0.0 + * @HB_SCRIPT_TOTO: `Toto`, Since: 3.0.0 + * @HB_SCRIPT_VITHKUQI: `Vith`, Since: 3.0.0 + * @HB_SCRIPT_MATH: `Zmth`, Since: 3.4.0 + * @HB_SCRIPT_KAWI: `Kawi`, Since: 5.2.0 + * @HB_SCRIPT_NAG_MUNDARI: `Nagm`, Since: 5.2.0 + * @HB_SCRIPT_GARAY: `Gara`, Since: 10.0.0 + * @HB_SCRIPT_GURUNG_KHEMA: `Gukh`, Since: 10.0.0 + * @HB_SCRIPT_KIRAT_RAI: `Krai`, Since: 10.0.0 + * @HB_SCRIPT_OL_ONAL: `Onao`, Since: 10.0.0 + * @HB_SCRIPT_SUNUWAR: `Sunu`, Since: 10.0.0 + * @HB_SCRIPT_TODHRI: `Todr`, Since: 10.0.0 + * @HB_SCRIPT_TULU_TIGALARI: `Tutg`, Since: 10.0.0 + * @HB_SCRIPT_INVALID: No script set + * + * Data type for scripts. Each #hb_script_t's value is an #hb_tag_t corresponding + * to the four-letter values defined by [ISO 15924](https://unicode.org/iso15924/). + * + * See also the Script (sc) property of the Unicode Character Database. + * + **/ + +/* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */ +typedef enum +{ + HB_SCRIPT_COMMON = HB_TAG ('Z','y','y','y'), /*1.1*/ + HB_SCRIPT_INHERITED = HB_TAG ('Z','i','n','h'), /*1.1*/ + HB_SCRIPT_UNKNOWN = HB_TAG ('Z','z','z','z'), /*5.0*/ + + HB_SCRIPT_ARABIC = HB_TAG ('A','r','a','b'), /*1.1*/ + HB_SCRIPT_ARMENIAN = HB_TAG ('A','r','m','n'), /*1.1*/ + HB_SCRIPT_BENGALI = HB_TAG ('B','e','n','g'), /*1.1*/ + HB_SCRIPT_CYRILLIC = HB_TAG ('C','y','r','l'), /*1.1*/ + HB_SCRIPT_DEVANAGARI = HB_TAG ('D','e','v','a'), /*1.1*/ + HB_SCRIPT_GEORGIAN = HB_TAG ('G','e','o','r'), /*1.1*/ + HB_SCRIPT_GREEK = HB_TAG ('G','r','e','k'), /*1.1*/ + HB_SCRIPT_GUJARATI = HB_TAG ('G','u','j','r'), /*1.1*/ + HB_SCRIPT_GURMUKHI = HB_TAG ('G','u','r','u'), /*1.1*/ + HB_SCRIPT_HANGUL = HB_TAG ('H','a','n','g'), /*1.1*/ + HB_SCRIPT_HAN = HB_TAG ('H','a','n','i'), /*1.1*/ + HB_SCRIPT_HEBREW = HB_TAG ('H','e','b','r'), /*1.1*/ + HB_SCRIPT_HIRAGANA = HB_TAG ('H','i','r','a'), /*1.1*/ + HB_SCRIPT_KANNADA = HB_TAG ('K','n','d','a'), /*1.1*/ + HB_SCRIPT_KATAKANA = HB_TAG ('K','a','n','a'), /*1.1*/ + HB_SCRIPT_LAO = HB_TAG ('L','a','o','o'), /*1.1*/ + HB_SCRIPT_LATIN = HB_TAG ('L','a','t','n'), /*1.1*/ + HB_SCRIPT_MALAYALAM = HB_TAG ('M','l','y','m'), /*1.1*/ + HB_SCRIPT_ORIYA = HB_TAG ('O','r','y','a'), /*1.1*/ + HB_SCRIPT_TAMIL = HB_TAG ('T','a','m','l'), /*1.1*/ + HB_SCRIPT_TELUGU = HB_TAG ('T','e','l','u'), /*1.1*/ + HB_SCRIPT_THAI = HB_TAG ('T','h','a','i'), /*1.1*/ + + HB_SCRIPT_TIBETAN = HB_TAG ('T','i','b','t'), /*2.0*/ + + HB_SCRIPT_BOPOMOFO = HB_TAG ('B','o','p','o'), /*3.0*/ + HB_SCRIPT_BRAILLE = HB_TAG ('B','r','a','i'), /*3.0*/ + HB_SCRIPT_CANADIAN_SYLLABICS = HB_TAG ('C','a','n','s'), /*3.0*/ + HB_SCRIPT_CHEROKEE = HB_TAG ('C','h','e','r'), /*3.0*/ + HB_SCRIPT_ETHIOPIC = HB_TAG ('E','t','h','i'), /*3.0*/ + HB_SCRIPT_KHMER = HB_TAG ('K','h','m','r'), /*3.0*/ + HB_SCRIPT_MONGOLIAN = HB_TAG ('M','o','n','g'), /*3.0*/ + HB_SCRIPT_MYANMAR = HB_TAG ('M','y','m','r'), /*3.0*/ + HB_SCRIPT_OGHAM = HB_TAG ('O','g','a','m'), /*3.0*/ + HB_SCRIPT_RUNIC = HB_TAG ('R','u','n','r'), /*3.0*/ + HB_SCRIPT_SINHALA = HB_TAG ('S','i','n','h'), /*3.0*/ + HB_SCRIPT_SYRIAC = HB_TAG ('S','y','r','c'), /*3.0*/ + HB_SCRIPT_THAANA = HB_TAG ('T','h','a','a'), /*3.0*/ + HB_SCRIPT_YI = HB_TAG ('Y','i','i','i'), /*3.0*/ + + HB_SCRIPT_DESERET = HB_TAG ('D','s','r','t'), /*3.1*/ + HB_SCRIPT_GOTHIC = HB_TAG ('G','o','t','h'), /*3.1*/ + HB_SCRIPT_OLD_ITALIC = HB_TAG ('I','t','a','l'), /*3.1*/ + + HB_SCRIPT_BUHID = HB_TAG ('B','u','h','d'), /*3.2*/ + HB_SCRIPT_HANUNOO = HB_TAG ('H','a','n','o'), /*3.2*/ + HB_SCRIPT_TAGALOG = HB_TAG ('T','g','l','g'), /*3.2*/ + HB_SCRIPT_TAGBANWA = HB_TAG ('T','a','g','b'), /*3.2*/ + + HB_SCRIPT_CYPRIOT = HB_TAG ('C','p','r','t'), /*4.0*/ + HB_SCRIPT_LIMBU = HB_TAG ('L','i','m','b'), /*4.0*/ + HB_SCRIPT_LINEAR_B = HB_TAG ('L','i','n','b'), /*4.0*/ + HB_SCRIPT_OSMANYA = HB_TAG ('O','s','m','a'), /*4.0*/ + HB_SCRIPT_SHAVIAN = HB_TAG ('S','h','a','w'), /*4.0*/ + HB_SCRIPT_TAI_LE = HB_TAG ('T','a','l','e'), /*4.0*/ + HB_SCRIPT_UGARITIC = HB_TAG ('U','g','a','r'), /*4.0*/ + + HB_SCRIPT_BUGINESE = HB_TAG ('B','u','g','i'), /*4.1*/ + HB_SCRIPT_COPTIC = HB_TAG ('C','o','p','t'), /*4.1*/ + HB_SCRIPT_GLAGOLITIC = HB_TAG ('G','l','a','g'), /*4.1*/ + HB_SCRIPT_KHAROSHTHI = HB_TAG ('K','h','a','r'), /*4.1*/ + HB_SCRIPT_NEW_TAI_LUE = HB_TAG ('T','a','l','u'), /*4.1*/ + HB_SCRIPT_OLD_PERSIAN = HB_TAG ('X','p','e','o'), /*4.1*/ + HB_SCRIPT_SYLOTI_NAGRI = HB_TAG ('S','y','l','o'), /*4.1*/ + HB_SCRIPT_TIFINAGH = HB_TAG ('T','f','n','g'), /*4.1*/ + + HB_SCRIPT_BALINESE = HB_TAG ('B','a','l','i'), /*5.0*/ + HB_SCRIPT_CUNEIFORM = HB_TAG ('X','s','u','x'), /*5.0*/ + HB_SCRIPT_NKO = HB_TAG ('N','k','o','o'), /*5.0*/ + HB_SCRIPT_PHAGS_PA = HB_TAG ('P','h','a','g'), /*5.0*/ + HB_SCRIPT_PHOENICIAN = HB_TAG ('P','h','n','x'), /*5.0*/ + + HB_SCRIPT_CARIAN = HB_TAG ('C','a','r','i'), /*5.1*/ + HB_SCRIPT_CHAM = HB_TAG ('C','h','a','m'), /*5.1*/ + HB_SCRIPT_KAYAH_LI = HB_TAG ('K','a','l','i'), /*5.1*/ + HB_SCRIPT_LEPCHA = HB_TAG ('L','e','p','c'), /*5.1*/ + HB_SCRIPT_LYCIAN = HB_TAG ('L','y','c','i'), /*5.1*/ + HB_SCRIPT_LYDIAN = HB_TAG ('L','y','d','i'), /*5.1*/ + HB_SCRIPT_OL_CHIKI = HB_TAG ('O','l','c','k'), /*5.1*/ + HB_SCRIPT_REJANG = HB_TAG ('R','j','n','g'), /*5.1*/ + HB_SCRIPT_SAURASHTRA = HB_TAG ('S','a','u','r'), /*5.1*/ + HB_SCRIPT_SUNDANESE = HB_TAG ('S','u','n','d'), /*5.1*/ + HB_SCRIPT_VAI = HB_TAG ('V','a','i','i'), /*5.1*/ + + HB_SCRIPT_AVESTAN = HB_TAG ('A','v','s','t'), /*5.2*/ + HB_SCRIPT_BAMUM = HB_TAG ('B','a','m','u'), /*5.2*/ + HB_SCRIPT_EGYPTIAN_HIEROGLYPHS = HB_TAG ('E','g','y','p'), /*5.2*/ + HB_SCRIPT_IMPERIAL_ARAMAIC = HB_TAG ('A','r','m','i'), /*5.2*/ + HB_SCRIPT_INSCRIPTIONAL_PAHLAVI = HB_TAG ('P','h','l','i'), /*5.2*/ + HB_SCRIPT_INSCRIPTIONAL_PARTHIAN = HB_TAG ('P','r','t','i'), /*5.2*/ + HB_SCRIPT_JAVANESE = HB_TAG ('J','a','v','a'), /*5.2*/ + HB_SCRIPT_KAITHI = HB_TAG ('K','t','h','i'), /*5.2*/ + HB_SCRIPT_LISU = HB_TAG ('L','i','s','u'), /*5.2*/ + HB_SCRIPT_MEETEI_MAYEK = HB_TAG ('M','t','e','i'), /*5.2*/ + HB_SCRIPT_OLD_SOUTH_ARABIAN = HB_TAG ('S','a','r','b'), /*5.2*/ + HB_SCRIPT_OLD_TURKIC = HB_TAG ('O','r','k','h'), /*5.2*/ + HB_SCRIPT_SAMARITAN = HB_TAG ('S','a','m','r'), /*5.2*/ + HB_SCRIPT_TAI_THAM = HB_TAG ('L','a','n','a'), /*5.2*/ + HB_SCRIPT_TAI_VIET = HB_TAG ('T','a','v','t'), /*5.2*/ + + HB_SCRIPT_BATAK = HB_TAG ('B','a','t','k'), /*6.0*/ + HB_SCRIPT_BRAHMI = HB_TAG ('B','r','a','h'), /*6.0*/ + HB_SCRIPT_MANDAIC = HB_TAG ('M','a','n','d'), /*6.0*/ + + HB_SCRIPT_CHAKMA = HB_TAG ('C','a','k','m'), /*6.1*/ + HB_SCRIPT_MEROITIC_CURSIVE = HB_TAG ('M','e','r','c'), /*6.1*/ + HB_SCRIPT_MEROITIC_HIEROGLYPHS = HB_TAG ('M','e','r','o'), /*6.1*/ + HB_SCRIPT_MIAO = HB_TAG ('P','l','r','d'), /*6.1*/ + HB_SCRIPT_SHARADA = HB_TAG ('S','h','r','d'), /*6.1*/ + HB_SCRIPT_SORA_SOMPENG = HB_TAG ('S','o','r','a'), /*6.1*/ + HB_SCRIPT_TAKRI = HB_TAG ('T','a','k','r'), /*6.1*/ + + /* + * Since: 0.9.30 + */ + HB_SCRIPT_BASSA_VAH = HB_TAG ('B','a','s','s'), /*7.0*/ + HB_SCRIPT_CAUCASIAN_ALBANIAN = HB_TAG ('A','g','h','b'), /*7.0*/ + HB_SCRIPT_DUPLOYAN = HB_TAG ('D','u','p','l'), /*7.0*/ + HB_SCRIPT_ELBASAN = HB_TAG ('E','l','b','a'), /*7.0*/ + HB_SCRIPT_GRANTHA = HB_TAG ('G','r','a','n'), /*7.0*/ + HB_SCRIPT_KHOJKI = HB_TAG ('K','h','o','j'), /*7.0*/ + HB_SCRIPT_KHUDAWADI = HB_TAG ('S','i','n','d'), /*7.0*/ + HB_SCRIPT_LINEAR_A = HB_TAG ('L','i','n','a'), /*7.0*/ + HB_SCRIPT_MAHAJANI = HB_TAG ('M','a','h','j'), /*7.0*/ + HB_SCRIPT_MANICHAEAN = HB_TAG ('M','a','n','i'), /*7.0*/ + HB_SCRIPT_MENDE_KIKAKUI = HB_TAG ('M','e','n','d'), /*7.0*/ + HB_SCRIPT_MODI = HB_TAG ('M','o','d','i'), /*7.0*/ + HB_SCRIPT_MRO = HB_TAG ('M','r','o','o'), /*7.0*/ + HB_SCRIPT_NABATAEAN = HB_TAG ('N','b','a','t'), /*7.0*/ + HB_SCRIPT_OLD_NORTH_ARABIAN = HB_TAG ('N','a','r','b'), /*7.0*/ + HB_SCRIPT_OLD_PERMIC = HB_TAG ('P','e','r','m'), /*7.0*/ + HB_SCRIPT_PAHAWH_HMONG = HB_TAG ('H','m','n','g'), /*7.0*/ + HB_SCRIPT_PALMYRENE = HB_TAG ('P','a','l','m'), /*7.0*/ + HB_SCRIPT_PAU_CIN_HAU = HB_TAG ('P','a','u','c'), /*7.0*/ + HB_SCRIPT_PSALTER_PAHLAVI = HB_TAG ('P','h','l','p'), /*7.0*/ + HB_SCRIPT_SIDDHAM = HB_TAG ('S','i','d','d'), /*7.0*/ + HB_SCRIPT_TIRHUTA = HB_TAG ('T','i','r','h'), /*7.0*/ + HB_SCRIPT_WARANG_CITI = HB_TAG ('W','a','r','a'), /*7.0*/ + + HB_SCRIPT_AHOM = HB_TAG ('A','h','o','m'), /*8.0*/ + HB_SCRIPT_ANATOLIAN_HIEROGLYPHS = HB_TAG ('H','l','u','w'), /*8.0*/ + HB_SCRIPT_HATRAN = HB_TAG ('H','a','t','r'), /*8.0*/ + HB_SCRIPT_MULTANI = HB_TAG ('M','u','l','t'), /*8.0*/ + HB_SCRIPT_OLD_HUNGARIAN = HB_TAG ('H','u','n','g'), /*8.0*/ + HB_SCRIPT_SIGNWRITING = HB_TAG ('S','g','n','w'), /*8.0*/ + + /* + * Since 1.3.0 + */ + HB_SCRIPT_ADLAM = HB_TAG ('A','d','l','m'), /*9.0*/ + HB_SCRIPT_BHAIKSUKI = HB_TAG ('B','h','k','s'), /*9.0*/ + HB_SCRIPT_MARCHEN = HB_TAG ('M','a','r','c'), /*9.0*/ + HB_SCRIPT_OSAGE = HB_TAG ('O','s','g','e'), /*9.0*/ + HB_SCRIPT_TANGUT = HB_TAG ('T','a','n','g'), /*9.0*/ + HB_SCRIPT_NEWA = HB_TAG ('N','e','w','a'), /*9.0*/ + + /* + * Since 1.6.0 + */ + HB_SCRIPT_MASARAM_GONDI = HB_TAG ('G','o','n','m'), /*10.0*/ + HB_SCRIPT_NUSHU = HB_TAG ('N','s','h','u'), /*10.0*/ + HB_SCRIPT_SOYOMBO = HB_TAG ('S','o','y','o'), /*10.0*/ + HB_SCRIPT_ZANABAZAR_SQUARE = HB_TAG ('Z','a','n','b'), /*10.0*/ + + /* + * Since 1.8.0 + */ + HB_SCRIPT_DOGRA = HB_TAG ('D','o','g','r'), /*11.0*/ + HB_SCRIPT_GUNJALA_GONDI = HB_TAG ('G','o','n','g'), /*11.0*/ + HB_SCRIPT_HANIFI_ROHINGYA = HB_TAG ('R','o','h','g'), /*11.0*/ + HB_SCRIPT_MAKASAR = HB_TAG ('M','a','k','a'), /*11.0*/ + HB_SCRIPT_MEDEFAIDRIN = HB_TAG ('M','e','d','f'), /*11.0*/ + HB_SCRIPT_OLD_SOGDIAN = HB_TAG ('S','o','g','o'), /*11.0*/ + HB_SCRIPT_SOGDIAN = HB_TAG ('S','o','g','d'), /*11.0*/ + + /* + * Since 2.4.0 + */ + HB_SCRIPT_ELYMAIC = HB_TAG ('E','l','y','m'), /*12.0*/ + HB_SCRIPT_NANDINAGARI = HB_TAG ('N','a','n','d'), /*12.0*/ + HB_SCRIPT_NYIAKENG_PUACHUE_HMONG = HB_TAG ('H','m','n','p'), /*12.0*/ + HB_SCRIPT_WANCHO = HB_TAG ('W','c','h','o'), /*12.0*/ + + /* + * Since 2.6.7 + */ + HB_SCRIPT_CHORASMIAN = HB_TAG ('C','h','r','s'), /*13.0*/ + HB_SCRIPT_DIVES_AKURU = HB_TAG ('D','i','a','k'), /*13.0*/ + HB_SCRIPT_KHITAN_SMALL_SCRIPT = HB_TAG ('K','i','t','s'), /*13.0*/ + HB_SCRIPT_YEZIDI = HB_TAG ('Y','e','z','i'), /*13.0*/ + + /* + * Since 3.0.0 + */ + HB_SCRIPT_CYPRO_MINOAN = HB_TAG ('C','p','m','n'), /*14.0*/ + HB_SCRIPT_OLD_UYGHUR = HB_TAG ('O','u','g','r'), /*14.0*/ + HB_SCRIPT_TANGSA = HB_TAG ('T','n','s','a'), /*14.0*/ + HB_SCRIPT_TOTO = HB_TAG ('T','o','t','o'), /*14.0*/ + HB_SCRIPT_VITHKUQI = HB_TAG ('V','i','t','h'), /*14.0*/ + + /* + * Since 3.4.0 + */ + HB_SCRIPT_MATH = HB_TAG ('Z','m','t','h'), + + /* + * Since 5.2.0 + */ + HB_SCRIPT_KAWI = HB_TAG ('K','a','w','i'), /*15.0*/ + HB_SCRIPT_NAG_MUNDARI = HB_TAG ('N','a','g','m'), /*15.0*/ + + /* + * Since 10.0.0 + */ + HB_SCRIPT_GARAY = HB_TAG ('G','a','r','a'), /*16.0*/ + HB_SCRIPT_GURUNG_KHEMA = HB_TAG ('G','u','k','h'), /*16.0*/ + HB_SCRIPT_KIRAT_RAI = HB_TAG ('K','r','a','i'), /*16.0*/ + HB_SCRIPT_OL_ONAL = HB_TAG ('O','n','a','o'), /*16.0*/ + HB_SCRIPT_SUNUWAR = HB_TAG ('S','u','n','u'), /*16.0*/ + HB_SCRIPT_TODHRI = HB_TAG ('T','o','d','r'), /*16.0*/ + HB_SCRIPT_TULU_TIGALARI = HB_TAG ('T','u','t','g'), /*16.0*/ + + /* No script set. */ + HB_SCRIPT_INVALID = HB_TAG_NONE, + + /*< private >*/ + + /* Dummy values to ensure any hb_tag_t value can be passed/stored as hb_script_t + * without risking undefined behavior. We have two, for historical reasons. + * HB_TAG_MAX used to be unsigned, but that was invalid Ansi C, so was changed + * to _HB_SCRIPT_MAX_VALUE to be equal to HB_TAG_MAX_SIGNED as well. + * + * See this thread for technicalities: + * + * https://lists.freedesktop.org/archives/harfbuzz/2014-March/004150.html + */ + _HB_SCRIPT_MAX_VALUE = HB_TAG_MAX_SIGNED, /*< skip >*/ + _HB_SCRIPT_MAX_VALUE_SIGNED = HB_TAG_MAX_SIGNED /*< skip >*/ + +} hb_script_t; + + +#endif /* HB_SCRIPT_LIST_H */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-serialize.hh b/src/java.desktop/share/native/libharfbuzz/hb-serialize.hh index 675eb03cadf28..704d0ffd12ba2 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-serialize.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-serialize.hh @@ -36,9 +36,7 @@ #include "hb-map.hh" #include "hb-pool.hh" -#ifdef HB_EXPERIMENTAL_API -#include "hb-subset-repacker.h" -#endif +#include "hb-subset-serialize.h" /* * Serialize @@ -75,23 +73,41 @@ struct hb_serialize_context_t object_t () = default; -#ifdef HB_EXPERIMENTAL_API - object_t (const hb_object_t &o) + object_t (const hb_subset_serialize_object_t &o) { head = o.head; tail = o.tail; next = nullptr; - real_links.alloc (o.num_real_links, true); + real_links.alloc_exact (o.num_real_links); for (unsigned i = 0 ; i < o.num_real_links; i++) real_links.push (o.real_links[i]); - virtual_links.alloc (o.num_virtual_links, true); + virtual_links.alloc_exact (o.num_virtual_links); for (unsigned i = 0; i < o.num_virtual_links; i++) virtual_links.push (o.virtual_links[i]); } -#endif - friend void swap (object_t& a, object_t& b) + bool add_virtual_link (objidx_t objidx) + { + if (!objidx) + return false; + + auto& link = *virtual_links.push (); + if (virtual_links.in_error ()) + return false; + + link.objidx = objidx; + // Remaining fields were previously zero'd by push(): + // link.width = 0; + // link.is_signed = 0; + // link.whence = 0; + // link.position = 0; + // link.bias = 0; + + return true; + } + + friend void swap (object_t& a, object_t& b) noexcept { hb_swap (a.head, b.head); hb_swap (a.tail, b.tail); @@ -128,8 +144,7 @@ struct hb_serialize_context_t link_t () = default; -#ifdef HB_EXPERIMENTAL_API - link_t (const hb_link_t &o) + link_t (const hb_subset_serialize_link_t &o) { width = o.width; is_signed = 0; @@ -138,7 +153,6 @@ struct hb_serialize_context_t bias = 0; objidx = o.objidx; } -#endif HB_INTERNAL static int cmp (const void* a, const void* b) { @@ -156,9 +170,9 @@ struct hb_serialize_context_t object_t *next; auto all_links () const HB_AUTO_RETURN - (( hb_concat (this->real_links, this->virtual_links) )); + (( hb_concat (real_links, virtual_links) )); auto all_links_writer () HB_AUTO_RETURN - (( hb_concat (this->real_links.writer (), this->virtual_links.writer ()) )); + (( hb_concat (real_links.writer (), virtual_links.writer ()) )); }; struct snapshot_t @@ -380,6 +394,7 @@ struct hb_serialize_context_t { merge_virtual_links (obj, objidx); obj->fini (); + object_pool.release (obj); return objidx; } } @@ -443,9 +458,11 @@ struct hb_serialize_context_t while (packed.length > 1 && packed.tail ()->head < tail) { - packed_map.del (packed.tail ()); - assert (!packed.tail ()->next); - packed.tail ()->fini (); + object_t *obj = packed.tail (); + packed_map.del (obj); + assert (!obj->next); + obj->fini (); + object_pool.release (obj); packed.pop (); } if (packed.length > 1) @@ -469,16 +486,40 @@ struct hb_serialize_context_t assert (current); - auto& link = *current->virtual_links.push (); - if (current->virtual_links.in_error ()) + if (!current->add_virtual_link(objidx)) err (HB_SERIALIZE_ERROR_OTHER); + } - link.width = 0; - link.objidx = objidx; - link.is_signed = 0; - link.whence = 0; - link.position = 0; - link.bias = 0; + objidx_t last_added_child_index() const { + if (unlikely (in_error ())) return (objidx_t) -1; + + assert (current); + if (!bool(current->real_links)) { + return (objidx_t) -1; + } + + return current->real_links[current->real_links.length - 1].objidx; + } + + // For the current object ensure that the sub-table bytes for child objidx are always placed + // after the subtable bytes for any other existing children. This only ensures that the + // repacker will not move the target subtable before the other children + // (by adding virtual links). It is up to the caller to ensure the initial serialization + // order is correct. + void repack_last(objidx_t objidx) { + if (unlikely (in_error ())) return; + + if (!objidx) + return; + + assert (current); + for (auto& l : current->real_links) { + if (l.objidx == objidx) { + continue; + } + + packed[l.objidx]->add_virtual_link(objidx); + } } template diff --git a/src/java.desktop/share/native/libharfbuzz/hb-set-digest.hh b/src/java.desktop/share/native/libharfbuzz/hb-set-digest.hh index c1e107b4c2980..f0416d5fa1d6e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-set-digest.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-set-digest.hh @@ -31,11 +31,10 @@ #include "hb-machinery.hh" /* - * The set-digests here implement various "filters" that support - * "approximate member query". Conceptually these are like Bloom - * Filter and Quotient Filter, however, much smaller, faster, and - * designed to fit the requirements of our uses for glyph coverage - * queries. + * The set-digests implement "filters" that support "approximate + * member query". Conceptually these are like Bloom Filter and + * Quotient Filter, however, much smaller, faster, and designed + * to fit the requirements of our uses for glyph coverage queries. * * Our filters are highly accurate if the lookup covers fairly local * set of glyphs, but fully flooded and ineffective if coverage is @@ -56,7 +55,7 @@ * - For each glyph, if it doesn't match the subtable digest, * skip it. * - * The main filter we use is a combination of three bits-pattern + * The main filter we use is a combination of four bits-pattern * filters. A bits-pattern filter checks a number of bits (5 or 6) * of the input number (glyph-id in this case) and checks whether * its pattern is amongst the patterns of any of the accepted values. @@ -64,43 +63,60 @@ * check is done using four bitwise operations only. */ -template -struct hb_set_digest_bits_pattern_t +static constexpr unsigned hb_set_digest_shifts[] = {4, 0, 6}; + +struct hb_set_digest_t { + // No science in these. Intuition and testing only. + using mask_t = uint64_t; + + static constexpr unsigned n = ARRAY_LENGTH_CONST (hb_set_digest_shifts); static constexpr unsigned mask_bytes = sizeof (mask_t); static constexpr unsigned mask_bits = sizeof (mask_t) * 8; - static constexpr unsigned num_bits = 0 - + (mask_bytes >= 1 ? 3 : 0) - + (mask_bytes >= 2 ? 1 : 0) - + (mask_bytes >= 4 ? 1 : 0) - + (mask_bytes >= 8 ? 1 : 0) - + (mask_bytes >= 16? 1 : 0) - + 0; + static constexpr hb_codepoint_t mb1 = mask_bits - 1; + static constexpr mask_t one = 1; + static constexpr mask_t all = (mask_t) -1; - static_assert ((shift < sizeof (hb_codepoint_t) * 8), ""); - static_assert ((shift + num_bits <= sizeof (hb_codepoint_t) * 8), ""); + void init () + { for (unsigned i = 0; i < n; i++) masks[i] = 0; } - void init () { mask = 0; } + void clear () { init (); } - void add (const hb_set_digest_bits_pattern_t &o) { mask |= o.mask; } + static hb_set_digest_t full () + { + hb_set_digest_t d; + for (unsigned i = 0; i < n; i++) d.masks[i] = all; + return d; + } - void add (hb_codepoint_t g) { mask |= mask_for (g); } + void union_ (const hb_set_digest_t &o) + { for (unsigned i = 0; i < n; i++) masks[i] |= o.masks[i]; } bool add_range (hb_codepoint_t a, hb_codepoint_t b) { - if (mask == (mask_t) -1) return false; - if ((b >> shift) - (a >> shift) >= mask_bits - 1) - { - mask = (mask_t) -1; - return false; - } - else + bool ret; + + ret = false; + for (unsigned i = 0; i < n; i++) + if (masks[i] != all) + ret = true; + if (!ret) return false; + + ret = false; + for (unsigned i = 0; i < n; i++) { - mask_t ma = mask_for (a); - mask_t mb = mask_for (b); - mask |= mb + (mb - ma) - (mb < ma); - return true; + mask_t shift = hb_set_digest_shifts[i]; + if ((b >> shift) - (a >> shift) >= mb1) + masks[i] = all; + else + { + mask_t ma = one << ((a >> shift) & mb1); + mask_t mb = one << ((b >> shift) & mb1); + masks[i] |= mb + (mb - ma) - (mb < ma); + ret = true; + } } + return ret; } template @@ -123,95 +139,37 @@ struct hb_set_digest_bits_pattern_t template bool add_sorted_array (const hb_sorted_array_t& arr) { return add_sorted_array (&arr, arr.len ()); } - bool may_have (const hb_set_digest_bits_pattern_t &o) const - { return mask & o.mask; } - - bool may_have (hb_codepoint_t g) const - { return mask & mask_for (g); } - - private: - - static mask_t mask_for (hb_codepoint_t g) - { return ((mask_t) 1) << ((g >> shift) & (mask_bits - 1)); } - mask_t mask; -}; - -template -struct hb_set_digest_combiner_t -{ - void init () - { - head.init (); - tail.init (); - } + bool operator [] (hb_codepoint_t g) const + { return may_have (g); } - void add (const hb_set_digest_combiner_t &o) - { - head.add (o.head); - tail.add (o.tail); - } void add (hb_codepoint_t g) { - head.add (g); - tail.add (g); - } - - bool add_range (hb_codepoint_t a, hb_codepoint_t b) - { - return (int) head.add_range (a, b) | (int) tail.add_range (a, b); - } - template - void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) - { - head.add_array (array, count, stride); - tail.add_array (array, count, stride); - } - template - void add_array (const hb_array_t& arr) { add_array (&arr, arr.len ()); } - template - bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) - { - return head.add_sorted_array (array, count, stride) && - tail.add_sorted_array (array, count, stride); + for (unsigned i = 0; i < n; i++) + masks[i] |= one << ((g >> hb_set_digest_shifts[i]) & mb1); } - template - bool add_sorted_array (const hb_sorted_array_t& arr) { return add_sorted_array (&arr, arr.len ()); } - bool may_have (const hb_set_digest_combiner_t &o) const + HB_ALWAYS_INLINE + bool may_have (hb_codepoint_t g) const { - return head.may_have (o.head) && tail.may_have (o.tail); + for (unsigned i = 0; i < n; i++) + if (!(masks[i] & (one << ((g >> hb_set_digest_shifts[i]) & mb1)))) + return false; + return true; } - bool may_have (hb_codepoint_t g) const + bool may_intersect (const hb_set_digest_t &o) const { - return head.may_have (g) && tail.may_have (g); + for (unsigned i = 0; i < n; i++) + if (!(masks[i] & o.masks[i])) + return false; + return true; } private: - head_t head; - tail_t tail; -}; - -/* - * hb_set_digest_t - * - * This is a combination of digests that performs "best". - * There is not much science to this: it's a result of intuition - * and testing. - */ -using hb_set_digest_t = - hb_set_digest_combiner_t - < - hb_set_digest_bits_pattern_t, - hb_set_digest_combiner_t - < - hb_set_digest_bits_pattern_t, - hb_set_digest_bits_pattern_t - > - > -; + mask_t masks[n] = {}; +}; #endif /* HB_SET_DIGEST_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-set.hh b/src/java.desktop/share/native/libharfbuzz/hb-set.hh index 4aa682616a2d8..c5df9b8aec47a 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-set.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-set.hh @@ -30,11 +30,14 @@ #include "hb.hh" #include "hb-bit-set-invertible.hh" +#include "hb-bit-vector.hh" // Just to include template struct hb_sparseset_t { + static constexpr bool realloc_move = true; + hb_object_header_t header; impl_t s; @@ -42,10 +45,10 @@ struct hb_sparseset_t ~hb_sparseset_t () { fini (); } hb_sparseset_t (const hb_sparseset_t& other) : hb_sparseset_t () { set (other); } - hb_sparseset_t (hb_sparseset_t&& other) : hb_sparseset_t () { s = std::move (other.s); } + hb_sparseset_t (hb_sparseset_t&& other) noexcept : hb_sparseset_t () { s = std::move (other.s); } hb_sparseset_t& operator = (const hb_sparseset_t& other) { set (other); return *this; } - hb_sparseset_t& operator = (hb_sparseset_t&& other) { s = std::move (other.s); return *this; } - friend void swap (hb_sparseset_t& a, hb_sparseset_t& b) { hb_swap (a.s, b.s); } + hb_sparseset_t& operator = (hb_sparseset_t&& other) noexcept { s = std::move (other.s); return *this; } + friend void swap (hb_sparseset_t& a, hb_sparseset_t& b) noexcept { hb_swap (a.s, b.s); } hb_sparseset_t (std::initializer_list lst) : hb_sparseset_t () { @@ -84,7 +87,7 @@ struct hb_sparseset_t uint32_t hash () const { return s.hash (); } void add (hb_codepoint_t g) { s.add (g); } - bool add_range (hb_codepoint_t a, hb_codepoint_t b) { return s.add_range (a, b); } + bool add_range (hb_codepoint_t first, hb_codepoint_t last) { return s.add_range (first, last); } template void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) @@ -104,6 +107,7 @@ struct hb_sparseset_t void del_range (hb_codepoint_t a, hb_codepoint_t b) { s.del_range (a, b); } bool get (hb_codepoint_t g) const { return s.get (g); } + bool may_have (hb_codepoint_t g) const { return get (g); } /* Has interface. */ bool operator [] (hb_codepoint_t k) const { return get (k); } @@ -118,6 +122,9 @@ struct hb_sparseset_t hb_sparseset_t& operator << (const hb_codepoint_pair_t& range) { add_range (range.first, range.second); return *this; } + bool may_intersect (const hb_sparseset_t &other) const + { return s.may_intersect (other.s); } + bool intersects (hb_codepoint_t first, hb_codepoint_t last) const { return s.intersects (first, last); } @@ -164,7 +171,7 @@ struct hb_set_t : hb_sparseset_t ~hb_set_t () = default; hb_set_t () : sparseset () {}; hb_set_t (const hb_set_t &o) : sparseset ((sparseset &) o) {}; - hb_set_t (hb_set_t&& o) : sparseset (std::move ((sparseset &) o)) {} + hb_set_t (hb_set_t&& o) noexcept : sparseset (std::move ((sparseset &) o)) {} hb_set_t& operator = (const hb_set_t&) = default; hb_set_t& operator = (hb_set_t&&) = default; hb_set_t (std::initializer_list lst) : sparseset (lst) {} diff --git a/src/java.desktop/share/native/libharfbuzz/hb-shape-plan.cc b/src/java.desktop/share/native/libharfbuzz/hb-shape-plan.cc index 970aaa0b40ba9..19188f8006a0d 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-shape-plan.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-shape-plan.cc @@ -233,7 +233,7 @@ hb_shape_plan_create2 (hb_face_t *face, num_coords, shaper_list); - if (unlikely (props->direction == HB_DIRECTION_INVALID)) + if (unlikely (!HB_DIRECTION_IS_VALID (props->direction))) return hb_shape_plan_get_empty (); hb_shape_plan_t *shape_plan; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-shape.cc b/src/java.desktop/share/native/libharfbuzz/hb-shape.cc index 2f7d335ddceca..d14f577be0043 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-shape.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-shape.cc @@ -91,8 +91,10 @@ void free_static_shaper_list () * * Retrieves the list of shapers supported by HarfBuzz. * - * Return value: (transfer none) (array zero-terminated=1): an array of - * constant strings + * Return value: (transfer none) (array zero-terminated=1): a + * `NULL`-terminated array of supported shapers constant string. + * The returned array is owned by HarfBuzz and should not be + * modified or freed. * * Since: 0.9.2 **/ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-shape.h b/src/java.desktop/share/native/libharfbuzz/hb-shape.h index bf7eafd2b4fe3..294105d3cb0a8 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-shape.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-shape.h @@ -53,6 +53,7 @@ hb_shape_full (hb_font_t *font, unsigned int num_features, const char * const *shaper_list); +#ifdef HB_EXPERIMENTAL_API HB_EXTERN hb_bool_t hb_shape_justify (hb_font_t *font, hb_buffer_t *buffer, @@ -64,6 +65,7 @@ hb_shape_justify (hb_font_t *font, float *advance, /* IN/OUT */ hb_tag_t *var_tag, /* OUT */ float *var_value /* OUT */); +#endif HB_EXTERN const char ** hb_shape_list_shapers (void); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-static.cc b/src/java.desktop/share/native/libharfbuzz/hb-static.cc index abdfda9a75692..4e70e4136510e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-static.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-static.cc @@ -41,6 +41,7 @@ #include "hb-ot-maxp-table.hh" #ifndef HB_NO_VISIBILITY + #include "hb-ot-name-language-static.hh" uint64_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (uint64_t) - 1) / sizeof (uint64_t)] = {}; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-style.cc b/src/java.desktop/share/native/libharfbuzz/hb-style.cc index 20d4696b92626..7378a8895f4f0 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-style.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-style.cc @@ -61,8 +61,8 @@ _hb_ratio_to_angle (float r) * @style_tag: a style tag. * * Searches variation axes of a #hb_font_t object for a specific axis first, - * if not set, then tries to get default style values from different - * tables of the font. + * if not set, first tries to get default style values in `STAT` table + * then tries to polyfill from different tables of the font. * * Returns: Corresponding axis or default value to a style tag. * diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.hh index 4213f7e71c5c1..e7bddaef72337 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.hh @@ -115,7 +115,7 @@ struct str_encoder_t encode_byte (OpCode_BCD); // Based on: - // https://github.com/fonttools/fonttools/blob/97ed3a61cde03e17b8be36f866192fbd56f1d1a7/Lib/fontTools/misc/psCharStrings.py#L265-L294 + // https://github.com/fonttools/fonttools/blob/0738c41dfbcbc213ab9263f486ef0cccc6eb5ce5/Lib/fontTools/misc/psCharStrings.py#L267-L316 char buf[16]; /* FontTools has the following comment: @@ -133,6 +133,10 @@ struct str_encoder_t (void) hb_uselocale (((void) freelocale (clocale), oldlocale)); char *s = buf; + size_t len; + char *comma = strchr (s, ','); + if (comma) // Comma for some European locales in case no uselocale available. + *comma = '.'; if (s[0] == '0' && s[1] == '.') s++; else if (s[0] == '-' && s[1] == '0' && s[2] == '.') @@ -140,6 +144,45 @@ struct str_encoder_t s[1] = '-'; s++; } + else if ((len = strlen (s)) > 3 && !strcmp (s + len - 3, "000")) + { + unsigned exponent = len - 3; + char *s2 = s + exponent - 1; + while (*s2 == '0' && exponent > 1) + { + s2--; + exponent++; + } + snprintf (s2 + 1, sizeof (buf) - (s2 + 1 - buf), "E%u", exponent); + } + else + { + char *dot = strchr (s, '.'); + char *e = strchr (s, 'E'); + if (dot && e) + { + memmove (dot, dot + 1, e - (dot + 1)); + int exponent = atoi (e + 1); + int new_exponent = exponent - (e - (dot + 1)); + if (new_exponent == 1) + { + e[-1] = '0'; + e[0] = '\0'; + } + else + snprintf (e - 1, sizeof (buf) - (e - 1 - buf), "E%d", new_exponent); + } + } + if ((s[0] == '.' && s[1] == '0') || (s[0] == '-' && s[1] == '.' && s[2] == '0')) + { + int sign = s[0] == '-'; + char *s2 = s + sign + 1; + while (*s2 == '0') + s2++; + len = strlen (s2); + memmove (s + sign, s2, len); + snprintf (s + sign + len, sizeof (buf) - (s + sign + len - buf), "E-%u", (unsigned) (strlen (s + sign) - 1)); + } hb_vector_t nibbles; while (*s) { @@ -155,20 +198,22 @@ struct str_encoder_t { s++; nibbles.push (0x0C); // E- - continue; + } else { + if (c2 == '+') + s++; + nibbles.push (0x0B); // E } - if (c2 == '+') + if (*s == '0') s++; - nibbles.push (0x0B); // E continue; } - case '.': case ',': // Comma for some European locales in case no uselocale available. + case '.': nibbles.push (0x0A); // . continue; case '-': - nibbles.push (0x0E); // . + nibbles.push (0x0E); // - continue; } @@ -525,7 +570,7 @@ struct cff_subset_accelerator_t parsed_cs_str_vec_t parsed_charstrings; parsed_cs_str_vec_t parsed_global_subrs; hb_vector_t parsed_local_subrs; - mutable hb_atomic_ptr_t glyph_to_sid_map; + mutable hb_atomic_t glyph_to_sid_map; private: hb_blob_t* original_blob; @@ -1083,7 +1128,7 @@ struct subr_subsetter_t if (opstr.op == OpCode_callsubr || opstr.op == OpCode_callgsubr) size += 3; } - if (!buff.alloc (buff.length + size, true)) + if (!buff.alloc_exact (buff.length + size)) return false; for (auto &opstr : str.values) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff1.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff1.cc index 780bf34efaf66..4938c048d6d46 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff1.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff1.cc @@ -45,7 +45,7 @@ struct remap_sid_t void alloc (unsigned size) { map.alloc (size); - vector.alloc (size, true); + vector.alloc_exact (size); } bool in_error () const @@ -620,6 +620,12 @@ struct cff1_subset_plan drop_hints = plan->flags & HB_SUBSET_FLAGS_NO_HINTING; desubroutinize = plan->flags & HB_SUBSET_FLAGS_DESUBROUTINIZE; + #ifdef HB_EXPERIMENTAL_API + min_charstrings_off_size = (plan->flags & HB_SUBSET_FLAGS_IFTB_REQUIREMENTS) ? 4 : 0; + #else + min_charstrings_off_size = 0; + #endif + subset_charset = !acc.is_predef_charset (); if (!subset_charset) /* check whether the subset renumbers any glyph IDs */ @@ -778,13 +784,43 @@ struct cff1_subset_plan unsigned int topDictModSIDs[name_dict_values_t::ValCount]; bool desubroutinize = false; + + unsigned min_charstrings_off_size = 0; }; } // namespace OT +static bool _serialize_cff1_charstrings (hb_serialize_context_t *c, + struct OT::cff1_subset_plan &plan, + const OT::cff1::accelerator_subset_t &acc) +{ + c->push (); + + unsigned data_size = 0; + unsigned total_size = CFF1CharStrings::total_size (plan.subset_charstrings, &data_size, plan.min_charstrings_off_size); + if (unlikely (!c->start_zerocopy (total_size))) + return false; + + auto *cs = c->start_embed (); + if (unlikely (!cs->serialize (c, plan.subset_charstrings, &data_size, plan.min_charstrings_off_size))) { + c->pop_discard (); + return false; + } + + plan.info.char_strings_link = c->pop_pack (false); + return true; +} + bool OT::cff1::accelerator_subset_t::serialize (hb_serialize_context_t *c, struct OT::cff1_subset_plan &plan) const { + /* push charstrings onto the object stack first which will ensure it packs as the last + object in the table. Keeping the chastrings last satisfies the requirements for patching + via IFTB. If this ordering needs to be changed in the future, charstrings should be left + at the end whenever HB_SUBSET_FLAGS_ITFB_REQUIREMENTS is enabled. */ + if (!_serialize_cff1_charstrings(c, plan, *this)) + return false; + /* private dicts & local subrs */ for (int i = (int) privateDicts.length; --i >= 0 ;) { @@ -823,25 +859,6 @@ OT::cff1::accelerator_subset_t::serialize (hb_serialize_context_t *c, if (!is_CID ()) plan.info.privateDictInfo = plan.fontdicts_mod[0].privateDictInfo; - /* CharStrings */ - { - c->push (); - - unsigned data_size = 0; - unsigned total_size = CFF1CharStrings::total_size (plan.subset_charstrings, &data_size); - if (unlikely (!c->start_zerocopy (total_size))) - return false; - - auto *cs = c->start_embed (); - if (likely (cs->serialize (c, plan.subset_charstrings, &data_size))) - plan.info.char_strings_link = c->pop_pack (false); - else - { - c->pop_discard (); - return false; - } - } - /* FDArray (FD Index) */ if (fdArray != &Null (CFF1FDArray)) { diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff2.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff2.cc index 1f9bc209e88c2..1074d2403865d 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff2.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff2.cc @@ -248,7 +248,7 @@ struct cff2_subr_subsetter_t : subr_subsetter_t normalized_coords) : c (c), varStore (varStore), normalized_coords (normalized_coords) {} @@ -284,7 +284,7 @@ struct cff2_private_blend_encoder_param_t unsigned ivs = 0; unsigned region_count = 0; hb_vector_t scalars; - const CFF2VariationStore *varStore = nullptr; + const CFF2ItemVariationStore *varStore = nullptr; hb_array_t normalized_coords; }; @@ -378,7 +378,7 @@ struct cff2_private_dict_blend_opset_t : dict_opset_t struct cff2_private_dict_op_serializer_t : op_serializer_t { cff2_private_dict_op_serializer_t (bool desubroutinize_, bool drop_hints_, bool pinned_, - const CFF::CFF2VariationStore* varStore_, + const CFF::CFF2ItemVariationStore* varStore_, hb_array_t normalized_coords_) : desubroutinize (desubroutinize_), drop_hints (drop_hints_), pinned (pinned_), varStore (varStore_), normalized_coords (normalized_coords_) {} @@ -416,7 +416,7 @@ struct cff2_private_dict_op_serializer_t : op_serializer_t const bool desubroutinize; const bool drop_hints; const bool pinned; - const CFF::CFF2VariationStore* varStore; + const CFF::CFF2ItemVariationStore* varStore; hb_array_t normalized_coords; }; @@ -439,6 +439,12 @@ struct cff2_subset_plan desubroutinize = plan->flags & HB_SUBSET_FLAGS_DESUBROUTINIZE || pinned; // For instancing we need this path + #ifdef HB_EXPERIMENTAL_API + min_charstrings_off_size = (plan->flags & HB_SUBSET_FLAGS_IFTB_REQUIREMENTS) ? 4 : 0; + #else + min_charstrings_off_size = 0; + #endif + if (desubroutinize) { /* Flatten global & local subrs */ @@ -510,14 +516,45 @@ struct cff2_subset_plan bool drop_hints = false; bool desubroutinize = false; + + unsigned min_charstrings_off_size = 0; }; } // namespace OT +static bool _serialize_cff2_charstrings (hb_serialize_context_t *c, + cff2_subset_plan &plan, + const OT::cff2::accelerator_subset_t &acc) +{ + c->push (); + + unsigned data_size = 0; + unsigned total_size = CFF2CharStrings::total_size (plan.subset_charstrings, &data_size, plan.min_charstrings_off_size); + if (unlikely (!c->start_zerocopy (total_size))) + return false; + + auto *cs = c->start_embed (); + if (unlikely (!cs->serialize (c, plan.subset_charstrings, &data_size, plan.min_charstrings_off_size))) + { + c->pop_discard (); + return false; + } + + plan.info.char_strings_link = c->pop_pack (false); + return true; +} + bool OT::cff2::accelerator_subset_t::serialize (hb_serialize_context_t *c, struct cff2_subset_plan &plan, hb_array_t normalized_coords) const { + /* push charstrings onto the object stack first which will ensure it packs as the last + object in the table. Keeping the chastrings last satisfies the requirements for patching + via IFTB. If this ordering needs to be changed in the future, charstrings should be left + at the end whenever HB_SUBSET_FLAGS_ITFB_REQUIREMENTS is enabled. */ + if (!_serialize_cff2_charstrings(c, plan, *this)) + return false; + /* private dicts & local subrs */ hb_vector_t private_dict_infos; if (unlikely (!private_dict_infos.resize (plan.subset_fdcount))) return false; @@ -556,25 +593,6 @@ OT::cff2::accelerator_subset_t::serialize (hb_serialize_context_t *c, } } - /* CharStrings */ - { - c->push (); - - unsigned data_size = 0; - unsigned total_size = CFF2CharStrings::total_size (plan.subset_charstrings, &data_size); - if (unlikely (!c->start_zerocopy (total_size))) - return false; - - auto *cs = c->start_embed (); - if (likely (cs->serialize (c, plan.subset_charstrings, &data_size))) - plan.info.char_strings_link = c->pop_pack (false); - else - { - c->pop_discard (); - return false; - } - } - /* FDSelect */ if (fdSelect != &Null (CFF2FDSelect)) { @@ -610,10 +628,10 @@ OT::cff2::accelerator_subset_t::serialize (hb_serialize_context_t *c, } /* variation store */ - if (varStore != &Null (CFF2VariationStore) && + if (varStore != &Null (CFF2ItemVariationStore) && !plan.pinned) { - auto *dest = c->push (); + auto *dest = c->push (); if (unlikely (!dest->serialize (c, varStore))) { c->pop_discard (); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-input.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset-input.cc index 8c67f7f23f4d2..5de29081cc1b3 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-input.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-input.cc @@ -24,6 +24,7 @@ * Google Author(s): Garret Rieger, Rod Sheeter, Behdad Esfahbod */ +#include "hb-subset-instancer-solver.hh" #include "hb-subset.hh" #include "hb-set.hh" #include "hb-utf.hh" @@ -50,7 +51,6 @@ hb_subset_input_t::hb_subset_input_t () HB_TAG ('k', 'e', 'r', 'n'), // Copied from fontTools: - HB_TAG ('B', 'A', 'S', 'E'), HB_TAG ('J', 'S', 'T', 'F'), HB_TAG ('D', 'S', 'I', 'G'), HB_TAG ('E', 'B', 'D', 'T'), @@ -123,6 +123,12 @@ hb_subset_input_t::hb_subset_input_t () //justify HB_TAG ('j', 'a', 'l', 't'), // HarfBuzz doesn't use; others might + //East Asian spacing + HB_TAG ('c', 'h', 'w', 's'), + HB_TAG ('v', 'c', 'h', 'w'), + HB_TAG ('h', 'a', 'l', 't'), + HB_TAG ('v', 'h', 'a', 'l'), + //private HB_TAG ('H', 'a', 'r', 'f'), HB_TAG ('H', 'A', 'R', 'F'), @@ -406,11 +412,52 @@ hb_subset_input_keep_everything (hb_subset_input_t *input) hb_subset_input_set_flags (input, HB_SUBSET_FLAGS_NOTDEF_OUTLINE | HB_SUBSET_FLAGS_GLYPH_NAMES | + HB_SUBSET_FLAGS_NAME_LEGACY | HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES | HB_SUBSET_FLAGS_PASSTHROUGH_UNRECOGNIZED); } #ifndef HB_NO_VAR +/** + * hb_subset_input_pin_all_axes_to_default: (skip) + * @input: a #hb_subset_input_t object. + * @face: a #hb_face_t object. + * + * Pin all axes to default locations in the given subset input object. + * + * All axes in a font must be pinned. Additionally, `CFF2` table, if present, + * will be de-subroutinized. + * + * Return value: `true` if success, `false` otherwise + * + * Since: 8.3.1 + **/ +HB_EXTERN hb_bool_t +hb_subset_input_pin_all_axes_to_default (hb_subset_input_t *input, + hb_face_t *face) +{ + unsigned axis_count = hb_ot_var_get_axis_count (face); + if (!axis_count) return false; + + hb_ot_var_axis_info_t *axis_infos = (hb_ot_var_axis_info_t *) hb_calloc (axis_count, sizeof (hb_ot_var_axis_info_t)); + if (unlikely (!axis_infos)) return false; + + (void) hb_ot_var_get_axis_infos (face, 0, &axis_count, axis_infos); + + for (unsigned i = 0; i < axis_count; i++) + { + hb_tag_t axis_tag = axis_infos[i].tag; + double default_val = (double) axis_infos[i].default_value; + if (!input->axes_location.set (axis_tag, Triple (default_val, default_val, default_val))) + { + hb_free (axis_infos); + return false; + } + } + hb_free (axis_infos); + return true; +} + /** * hb_subset_input_pin_axis_to_default: (skip) * @input: a #hb_subset_input_t object. @@ -435,7 +482,7 @@ hb_subset_input_pin_axis_to_default (hb_subset_input_t *input, if (!hb_ot_var_find_axis_info (face, axis_tag, &axis_info)) return false; - float default_val = axis_info.default_value; + double default_val = (double) axis_info.default_value; return input->axes_location.set (axis_tag, Triple (default_val, default_val, default_val)); } @@ -465,37 +512,32 @@ hb_subset_input_pin_axis_location (hb_subset_input_t *input, if (!hb_ot_var_find_axis_info (face, axis_tag, &axis_info)) return false; - float val = hb_clamp(axis_value, axis_info.min_value, axis_info.max_value); + double val = hb_clamp((double) axis_value, (double) axis_info.min_value, (double) axis_info.max_value); return input->axes_location.set (axis_tag, Triple (val, val, val)); } -#ifdef HB_EXPERIMENTAL_API /** * hb_subset_input_set_axis_range: (skip) * @input: a #hb_subset_input_t object. * @face: a #hb_face_t object. * @axis_tag: Tag of the axis - * @axis_min_value: Minimum value of the axis variation range to set - * @axis_max_value: Maximum value of the axis variation range to set - * @axis_def_value: Default value of the axis variation range to set, in case of - * null, it'll be determined automatically + * @axis_min_value: Minimum value of the axis variation range to set, if NaN the existing min will be used. + * @axis_max_value: Maximum value of the axis variation range to set if NaN the existing max will be used. + * @axis_def_value: Default value of the axis variation range to set, if NaN the existing default will be used. * * Restricting the range of variation on an axis in the given subset input object. * New min/default/max values will be clamped if they're not within the fvar axis range. - * If the new default value is null: - * If the fvar axis default value is within the new range, then new default - * value is the same as original default value. + * * If the fvar axis default value is not within the new range, the new default * value will be changed to the new min or max value, whichever is closer to the fvar * axis default. * * Note: input min value can not be bigger than input max value. If the input * default value is not within the new min/max range, it'll be clamped. - * Note: currently it supports gvar and cvar tables only. * * Return value: `true` if success, `false` otherwise * - * XSince: EXPERIMENTAL + * Since: 8.5.0 **/ HB_EXTERN hb_bool_t hb_subset_input_set_axis_range (hb_subset_input_t *input, @@ -503,22 +545,195 @@ hb_subset_input_set_axis_range (hb_subset_input_t *input, hb_tag_t axis_tag, float axis_min_value, float axis_max_value, - float *axis_def_value /* IN, maybe NULL */) + float axis_def_value) { - if (axis_min_value > axis_max_value) - return false; - hb_ot_var_axis_info_t axis_info; if (!hb_ot_var_find_axis_info (face, axis_tag, &axis_info)) return false; - float new_min_val = hb_clamp(axis_min_value, axis_info.min_value, axis_info.max_value); - float new_max_val = hb_clamp(axis_max_value, axis_info.min_value, axis_info.max_value); - float new_default_val = axis_def_value ? *axis_def_value : axis_info.default_value; - new_default_val = hb_clamp(new_default_val, new_min_val, new_max_val); - return input->axes_location.set (axis_tag, Triple (new_min_val, new_default_val, new_max_val)); + float min = !std::isnan(axis_min_value) ? axis_min_value : axis_info.min_value; + float max = !std::isnan(axis_max_value) ? axis_max_value : axis_info.max_value; + float def = !std::isnan(axis_def_value) ? axis_def_value : axis_info.default_value; + + if (min > max) + return false; + + float new_min_val = hb_clamp(min, axis_info.min_value, axis_info.max_value); + float new_max_val = hb_clamp(max, axis_info.min_value, axis_info.max_value); + float new_default_val = hb_clamp(def, new_min_val, new_max_val); + return input->axes_location.set (axis_tag, Triple ((double) new_min_val, (double) new_default_val, (double) new_max_val)); +} + +/** + * hb_subset_input_get_axis_range: (skip) + * @input: a #hb_subset_input_t object. + * @axis_tag: Tag of the axis + * @axis_min_value: Set to the previously configured minimum value of the axis variation range. + * @axis_max_value: Set to the previously configured maximum value of the axis variation range. + * @axis_def_value: Set to the previously configured default value of the axis variation range. + * + * Gets the axis range assigned by previous calls to hb_subset_input_set_axis_range. + * + * Return value: `true` if a range has been set for this axis tag, `false` otherwise. + * + * Since: 8.5.0 + **/ +HB_EXTERN hb_bool_t +hb_subset_input_get_axis_range (hb_subset_input_t *input, + hb_tag_t axis_tag, + float *axis_min_value, + float *axis_max_value, + float *axis_def_value) + +{ + Triple* triple; + if (!input->axes_location.has(axis_tag, &triple)) { + return false; + } + + *axis_min_value = triple->minimum; + *axis_def_value = triple->middle; + *axis_max_value = triple->maximum; + return true; +} + +/** + * hb_subset_axis_range_from_string: + * @str: a string to parse + * @len: length of @str, or -1 if str is NULL terminated + * @axis_min_value: (out): the axis min value to initialize with the parsed value + * @axis_max_value: (out): the axis max value to initialize with the parsed value + * @axis_def_value: (out): the axis default value to initialize with the parse + * value + * + * Parses a string into a subset axis range(min, def, max). + * Axis positions string is in the format of min:def:max or min:max + * When parsing axis positions, empty values as meaning the existing value for that part + * E.g: :300:500 + * Specifies min = existing, def = 300, max = 500 + * In the output axis_range, if a value should be set to it's default value, + * then it will be set to NaN + * + * Return value: + * `true` if @str is successfully parsed, `false` otherwise + * + * Since: 10.2.0 + */ +HB_EXTERN hb_bool_t +hb_subset_axis_range_from_string (const char *str, int len, + float *axis_min_value, + float *axis_max_value, + float *axis_def_value) +{ + if (len < 0) + len = strlen (str); + + const char *end = str + len; + const char* part = strpbrk (str, ":"); + if (!part) + { + // Single value. + if (strcmp (str, "drop") == 0) + { + *axis_min_value = NAN; + *axis_def_value = NAN; + *axis_max_value = NAN; + return true; + } + + double v; + if (!hb_parse_double (&str, end, &v)) return false; + + *axis_min_value = v; + *axis_def_value = v; + *axis_max_value = v; + return true; + } + + float values[3]; + int count = 0; + for (int i = 0; i < 3; i++) { + count++; + if (!*str || part == str) + { + values[i] = NAN; + + if (part == NULL) break; + str = part + 1; + part = strpbrk (str, ":"); + continue; + } + + double v; + if (!hb_parse_double (&str, part, &v)) return false; + values[i] = v; + + if (part == NULL) break; + str = part + 1; + part = strpbrk (str, ":"); + } + + if (count == 2) + { + *axis_min_value = values[0]; + *axis_def_value = NAN; + *axis_max_value = values[1]; + return true; + } + else if (count == 3) + { + *axis_min_value = values[0]; + *axis_def_value = values[1]; + *axis_max_value = values[2]; + return true; + } + return false; +} + +/** + * hb_subset_axis_range_to_string: + * @input: a #hb_subset_input_t object. + * @axis_tag: an axis to convert + * @buf: (array length=size) (out caller-allocates): output string + * @size: the allocated size of @buf + * + * Converts an axis range into a `NULL`-terminated string in the format + * understood by hb_subset_axis_range_from_string(). The client in responsible for + * allocating big enough size for @buf, 128 bytes is more than enough. + * + * Since: 10.2.0 + */ +HB_EXTERN void +hb_subset_axis_range_to_string (hb_subset_input_t *input, + hb_tag_t axis_tag, + char *buf, unsigned size) +{ + if (unlikely (!size)) return; + Triple* triple; + if (!input->axes_location.has(axis_tag, &triple)) { + return; + } + + char s[128]; + unsigned len = 0; + + hb_locale_t clocale HB_UNUSED; + hb_locale_t oldlocale HB_UNUSED; + oldlocale = hb_uselocale (clocale = newlocale (LC_ALL_MASK, "C", NULL)); + len += hb_max (0, snprintf (s, ARRAY_LENGTH (s) - len, "%g", (double) triple->minimum)); + s[len++] = ':'; + + len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) triple->middle)); + s[len++] = ':'; + + len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) triple->maximum)); + (void) hb_uselocale (((void) freelocale (clocale), oldlocale)); + + assert (len < ARRAY_LENGTH (s)); + len = hb_min (len, size - 1); + hb_memcpy (buf, s, len); + buf[len] = '\0'; } -#endif #endif /** @@ -653,7 +868,7 @@ hb_subset_input_override_name_table (hb_subset_input_t *input, src = hb_utf8_t::next (src, src_end, &unicode, replacement); if (unicode >= 0x0080u) { - printf ("Non-ascii character detected, ignored...This API supports acsii characters only for mac platform\n"); + // Non-ascii character detected, ignored... return false; } } @@ -667,5 +882,4 @@ hb_subset_input_override_name_table (hb_subset_input_t *input, input->name_table_overrides.set (hb_ot_name_record_ids_t (platform_id, encoding_id, language_id, name_id), name_bytes); return true; } - #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-iup.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-iup.hh new file mode 100644 index 0000000000000..01987bd258dd5 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-iup.hh @@ -0,0 +1,37 @@ +/* + * Copyright © 2024 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_SUBSET_INSTANCER_IUP_HH +#define HB_SUBSET_INSTANCER_IUP_HH + +#include "hb-subset-plan.hh" +/* given contour points and deltas, optimize a set of referenced points within error + * tolerance. Returns optimized referenced point indices */ +HB_INTERNAL bool iup_delta_optimize (const contour_point_vector_t& contour_points, + const hb_vector_t& x_deltas, + const hb_vector_t& y_deltas, + hb_vector_t& opt_indices, /* OUT */ + double tolerance = 0.0); + +#endif /* HB_SUBSET_INSTANCER_IUP_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-solver.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-solver.cc index 8ec36e3e80dcc..c67fee421c204 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-solver.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-solver.cc @@ -32,17 +32,17 @@ * This should be safe. */ -constexpr static float EPSILON = 1.f / (1 << 14); -constexpr static float MAX_F2DOT14 = float (0x7FFF) / (1 << 14); +constexpr static double EPSILON = 1.0 / (1 << 14); +constexpr static double MAX_F2DOT14 = double (0x7FFF) / (1 << 14); static inline Triple _reverse_negate(const Triple &v) { return {-v.maximum, -v.middle, -v.minimum}; } -static inline float supportScalar (float coord, const Triple &tent) +static inline double supportScalar (double coord, const Triple &tent) { /* Copied from VarRegionAxis::evaluate() */ - float start = tent.minimum, peak = tent.middle, end = tent.maximum; + double start = tent.minimum, peak = tent.middle, end = tent.maximum; if (unlikely (start > peak || peak > end)) return 1.; @@ -62,20 +62,20 @@ static inline float supportScalar (float coord, const Triple &tent) return (end - coord) / (end - peak); } -static inline result_t +static inline rebase_tent_result_t _solve (Triple tent, Triple axisLimit, bool negative = false) { - float axisMin = axisLimit.minimum; - float axisDef = axisLimit.middle; - float axisMax = axisLimit.maximum; - float lower = tent.minimum; - float peak = tent.middle; - float upper = tent.maximum; + double axisMin = axisLimit.minimum; + double axisDef = axisLimit.middle; + double axisMax = axisLimit.maximum; + double lower = tent.minimum; + double peak = tent.middle; + double upper = tent.maximum; // Mirror the problem such that axisDef <= peak if (axisDef > peak) { - result_t vec = _solve (_reverse_negate (tent), + rebase_tent_result_t vec = _solve (_reverse_negate (tent), _reverse_negate (axisLimit), !negative); @@ -98,7 +98,7 @@ _solve (Triple tent, Triple axisLimit, bool negative = false) * axisMin axisDef axisMax lower upper */ if (axisMax <= lower && axisMax < peak) - return result_t{}; // No overlap + return rebase_tent_result_t{}; // No overlap /* case 2: Only the peak and outermost bound fall outside the new limit; * we keep the deltaset, update peak and outermost bound and scale deltas @@ -130,10 +130,10 @@ _solve (Triple tent, Triple axisLimit, bool negative = false) */ if (axisMax < peak) { - float mult = supportScalar (axisMax, tent); + double mult = supportScalar (axisMax, tent); tent = Triple{lower, axisMax, axisMax}; - result_t vec = _solve (tent, axisLimit); + rebase_tent_result_t vec = _solve (tent, axisLimit); for (auto &p : vec) p = hb_pair (p.first * mult, p.second); @@ -143,13 +143,13 @@ _solve (Triple tent, Triple axisLimit, bool negative = false) // lower <= axisDef <= peak <= axisMax - float gain = supportScalar (axisDef, tent); - result_t out {hb_pair (gain, Triple{})}; + double gain = supportScalar (axisDef, tent); + rebase_tent_result_t out {hb_pair (gain, Triple{})}; // First, the positive side // outGain is the scalar of axisMax at the tent. - float outGain = supportScalar (axisMax, tent); + double outGain = supportScalar (axisMax, tent); /* Case 3a: Gain is more than outGain. The tent down-slope crosses * the axis into negative. We have to split it into multiples. @@ -168,13 +168,15 @@ _solve (Triple tent, Triple axisLimit, bool negative = false) * | * crossing */ - if (gain > outGain) + if (gain >= outGain) { + // Note that this is the branch taken if both gain and outGain are 0. + // Crossing point on the axis. - float crossing = peak + (1 - gain) * (upper - peak); + double crossing = peak + (1 - gain) * (upper - peak); - Triple loc{axisDef, peak, crossing}; - float scalar = 1.f; + Triple loc{hb_max (lower, axisDef), peak, crossing}; + double scalar = 1.0; // The part before the crossing point. out.push (hb_pair (scalar - gain, loc)); @@ -189,7 +191,7 @@ _solve (Triple tent, Triple axisLimit, bool negative = false) if (upper >= axisMax) { Triple loc {crossing, axisMax, axisMax}; - float scalar = outGain; + double scalar = outGain; out.push (hb_pair (scalar - gain, loc)); } @@ -219,11 +221,11 @@ _solve (Triple tent, Triple axisLimit, bool negative = false) // Downslope. Triple loc1 {crossing, upper, axisMax}; - float scalar1 = 0.f; + double scalar1 = 0.0; // Eternity justify. Triple loc2 {upper, axisMax, axisMax}; - float scalar2 = 0.f; + double scalar2 = 0.0; out.push (hb_pair (scalar1 - gain, loc1)); out.push (hb_pair (scalar2 - gain, loc2)); @@ -252,9 +254,12 @@ _solve (Triple tent, Triple axisLimit, bool negative = false) * | | newUpper * axisDef axisMax */ - float newUpper = peak + (1 - gain) * (upper - peak); - assert (axisMax <= newUpper); // Because outGain >= gain - if (newUpper <= axisDef + (axisMax - axisDef) * 2) + double newUpper = peak + (1 - gain) * (upper - peak); + assert (axisMax <= newUpper); // Because outGain > gain + /* Disabled because ots doesn't like us: + * https://github.com/fonttools/fonttools/issues/3350 */ + + if (false && (newUpper <= axisDef + (axisMax - axisDef) * 2)) { upper = newUpper; if (!negative && axisDef + (axisMax - axisDef) * MAX_F2DOT14 < upper) @@ -265,7 +270,7 @@ _solve (Triple tent, Triple axisLimit, bool negative = false) } Triple loc {hb_max (axisDef, lower), peak, upper}; - float scalar = 1.f; + double scalar = 1.0; out.push (hb_pair (scalar - gain, loc)); } @@ -289,10 +294,10 @@ _solve (Triple tent, Triple axisLimit, bool negative = false) else { Triple loc1 {hb_max (axisDef, lower), peak, axisMax}; - float scalar1 = 1.f; + double scalar1 = 1.0; Triple loc2 {peak, axisMax, axisMax}; - float scalar2 = outGain; + double scalar2 = outGain; out.push (hb_pair (scalar1 - gain, loc1)); // Don't add a dirac delta! @@ -320,7 +325,7 @@ _solve (Triple tent, Triple axisLimit, bool negative = false) if (lower <= axisMin) { Triple loc {axisMin, axisMin, axisDef}; - float scalar = supportScalar (axisMin, tent); + double scalar = supportScalar (axisMin, tent); out.push (hb_pair (scalar - gain, loc)); } @@ -348,11 +353,11 @@ _solve (Triple tent, Triple axisLimit, bool negative = false) // Downslope. Triple loc1 {axisMin, lower, axisDef}; - float scalar1 = 0.f; + double scalar1 = 0.0; // Eternity justify. Triple loc2 {axisMin, axisMin, lower}; - float scalar2 = 0.f; + double scalar2 = 0.0; out.push (hb_pair (scalar1 - gain, loc1)); out.push (hb_pair (scalar2 - gain, loc2)); @@ -364,19 +369,19 @@ _solve (Triple tent, Triple axisLimit, bool negative = false) static inline TripleDistances _reverse_triple_distances (const TripleDistances &v) { return TripleDistances (v.positive, v.negative); } -float renormalizeValue (float v, const Triple &triple, - const TripleDistances &triple_distances, bool extrapolate) +double renormalizeValue (double v, const Triple &triple, + const TripleDistances &triple_distances, bool extrapolate) { - float lower = triple.minimum, def = triple.middle, upper = triple.maximum; + double lower = triple.minimum, def = triple.middle, upper = triple.maximum; assert (lower <= def && def <= upper); if (!extrapolate) - v = hb_max (hb_min (v, upper), lower); + v = hb_clamp (v, lower, upper); if (v == def) - return 0.f; + return 0.0; - if (def < 0.f) + if (def < 0.0) return -renormalizeValue (-v, _reverse_negate (triple), _reverse_triple_distances (triple_distances), extrapolate); @@ -385,14 +390,14 @@ float renormalizeValue (float v, const Triple &triple, return (v - def) / (upper - def); /* v < def */ - if (lower >= 0.f) + if (lower >= 0.0) return (v - def) / (def - lower); /* lower < 0 and v < default */ - float total_distance = triple_distances.negative * (-lower) + triple_distances.positive * def; + double total_distance = triple_distances.negative * (-lower) + triple_distances.positive * def; - float v_distance; - if (v >= 0.f) + double v_distance; + if (v >= 0.0) v_distance = (def - v) * triple_distances.positive; else v_distance = (-v) * triple_distances.negative + triple_distances.positive * def; @@ -400,18 +405,18 @@ float renormalizeValue (float v, const Triple &triple, return (-v_distance) /total_distance; } -result_t +rebase_tent_result_t rebase_tent (Triple tent, Triple axisLimit, TripleDistances axis_triple_distances) { - assert (-1.f <= axisLimit.minimum && axisLimit.minimum <= axisLimit.middle && axisLimit.middle <= axisLimit.maximum && axisLimit.maximum <= +1.f); - assert (-2.f <= tent.minimum && tent.minimum <= tent.middle && tent.middle <= tent.maximum && tent.maximum <= +2.f); - assert (tent.middle != 0.f); + assert (-1.0 <= axisLimit.minimum && axisLimit.minimum <= axisLimit.middle && axisLimit.middle <= axisLimit.maximum && axisLimit.maximum <= +1.0); + assert (-2.0 <= tent.minimum && tent.minimum <= tent.middle && tent.middle <= tent.maximum && tent.maximum <= +2.0); + assert (tent.middle != 0.0); - result_t sols = _solve (tent, axisLimit); + rebase_tent_result_t sols = _solve (tent, axisLimit); - auto n = [&axisLimit, &axis_triple_distances] (float v) { return renormalizeValue (v, axisLimit, axis_triple_distances); }; + auto n = [&axisLimit, &axis_triple_distances] (double v) { return renormalizeValue (v, axisLimit, axis_triple_distances); }; - result_t out; + rebase_tent_result_t out; for (auto &p : sols) { if (!p.first) continue; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-solver.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-solver.hh index e8ca1dc4e6360..9764dcc1725b0 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-solver.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-solver.hh @@ -30,24 +30,24 @@ /* pre-normalized distances */ struct TripleDistances { - TripleDistances (): negative (1.f), positive (1.f) {} - TripleDistances (float neg_, float pos_): negative (neg_), positive (pos_) {} - TripleDistances (float min, float default_, float max) + TripleDistances (): negative (1.0), positive (1.0) {} + TripleDistances (double neg_, double pos_): negative (neg_), positive (pos_) {} + TripleDistances (double min, double default_, double max) { negative = default_ - min; positive = max - default_; } - float negative; - float positive; + double negative; + double positive; }; struct Triple { Triple () : - minimum (0.f), middle (0.f), maximum (0.f) {} + minimum (0.0), middle (0.0), maximum (0.0) {} - Triple (float minimum_, float middle_, float maximum_) : + Triple (double minimum_, double middle_, double maximum_) : minimum (minimum_), middle (middle_), maximum (maximum_) {} bool operator == (const Triple &o) const @@ -63,7 +63,7 @@ struct Triple { bool is_point () const { return minimum == middle && middle == maximum; } - bool contains (float point) const + bool contains (double point) const { return minimum <= point && point <= maximum; } /* from hb_array_t hash ()*/ @@ -82,18 +82,18 @@ struct Triple { } - float minimum; - float middle; - float maximum; + double minimum; + double middle; + double maximum; }; -using result_item_t = hb_pair_t; -using result_t = hb_vector_t; +using rebase_tent_result_item_t = hb_pair_t; +using rebase_tent_result_t = hb_vector_t; /* renormalize a normalized value v to the range of an axis, * considering the prenormalized distances as well as the new axis limits. * Ported from fonttools */ -HB_INTERNAL float renormalizeValue (float v, const Triple &triple, +HB_INTERNAL double renormalizeValue (double v, const Triple &triple, const TripleDistances &triple_distances, bool extrapolate = true); /* Given a tuple (lower,peak,upper) "tent" and new axis limits @@ -107,6 +107,8 @@ HB_INTERNAL float renormalizeValue (float v, const Triple &triple, * If tent value is Triple{}, that is a special deltaset that should * be always-enabled (called "gain"). */ -HB_INTERNAL result_t rebase_tent (Triple tent, Triple axisLimit, TripleDistances axis_triple_distances); +HB_INTERNAL rebase_tent_result_t rebase_tent (Triple tent, + Triple axisLimit, + TripleDistances axis_triple_distances); #endif /* HB_SUBSET_INSTANCER_SOLVER_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan-member-list.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan-member-list.hh index 8bc1fcb56820e..ade8278c40fdd 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan-member-list.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan-member-list.hh @@ -70,6 +70,9 @@ HB_SUBSET_PLAN_MEMBER (hb_set_t, _glyphset_colred) HB_SUBSET_PLAN_MEMBER (hb_map_t, gsub_lookups) HB_SUBSET_PLAN_MEMBER (hb_map_t, gpos_lookups) +//use_mark_sets mapping: old->new +HB_SUBSET_PLAN_MEMBER (hb_map_t, used_mark_sets_map) + //active langsys we'd like to retain HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(>), gsub_langsys) HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(>), gpos_langsys) @@ -87,9 +90,24 @@ HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(>), gpo HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(), gsub_feature_substitutes_map) HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(), gpos_feature_substitutes_map) +// old feature_indexes set, used to reinstate the old features +HB_SUBSET_PLAN_MEMBER (hb_set_t, gsub_old_features) +HB_SUBSET_PLAN_MEMBER (hb_set_t, gpos_old_features) + +//feature_index->pair of (address of old feature, feature tag), used for inserting a catch all record +//if necessary +HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E()>), gsub_old_feature_idx_tag_map) +HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E()>), gpos_old_feature_idx_tag_map) + //active layers/palettes we'd like to retain HB_SUBSET_PLAN_MEMBER (hb_map_t, colrv1_layers) HB_SUBSET_PLAN_MEMBER (hb_map_t, colr_palettes) +//colrv1 varstore retained varidx mapping +HB_SUBSET_PLAN_MEMBER (hb_vector_t, colrv1_varstore_inner_maps) +//colrv1 retained varidx -> (new varidx, delta) mapping +HB_SUBSET_PLAN_MEMBER (mutable hb_hashmap_t E()>), colrv1_variation_idx_delta_map) +//colrv1 retained new delta set index -> new varidx mapping +HB_SUBSET_PLAN_MEMBER (hb_map_t, colrv1_new_deltaset_idx_varidx_map) //Old layout item variation index -> (New varidx, delta) mapping HB_SUBSET_PLAN_MEMBER (mutable hb_hashmap_t E()>), layout_variation_idx_delta_map) @@ -128,6 +146,15 @@ HB_SUBSET_PLAN_MEMBER (mutable hb_vector_t, bounds_height_vec) //map: new_gid -> contour points vector HB_SUBSET_PLAN_MEMBER (mutable hb_hashmap_t E(), new_gid_contour_points_map) +//new gids set for composite glyphs +HB_SUBSET_PLAN_MEMBER (hb_set_t, composite_new_gids) + +//Old BASE item variation index -> (New varidx, 0) mapping +HB_SUBSET_PLAN_MEMBER (mutable hb_hashmap_t E()>), base_variation_idx_map) + +//BASE table varstore retained varidx mapping +HB_SUBSET_PLAN_MEMBER (hb_vector_t, base_varstore_inner_maps) + #ifdef HB_EXPERIMENTAL_API // name table overrides map: hb_ot_name_record_ids_t-> name string new value or // None to indicate should remove diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.cc index b9cc4fd0bcd79..3ba726d925e6a 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.cc @@ -29,26 +29,21 @@ #include "hb-map.hh" #include "hb-multimap.hh" #include "hb-set.hh" +#include "hb-subset.h" +#include "hb-unicode.h" #include "hb-ot-cmap-table.hh" #include "hb-ot-glyf-table.hh" -#include "hb-ot-layout-gdef-table.hh" -#include "hb-ot-layout-gpos-table.hh" -#include "hb-ot-layout-gsub-table.hh" +#include "hb-ot-layout-base-table.hh" #include "hb-ot-cff1-table.hh" #include "hb-ot-cff2-table.hh" #include "OT/Color/COLR/COLR.hh" #include "OT/Color/COLR/colrv1-closure.hh" #include "OT/Color/CPAL/CPAL.hh" #include "hb-ot-var-fvar-table.hh" -#include "hb-ot-var-avar-table.hh" #include "hb-ot-stat-table.hh" #include "hb-ot-math-table.hh" -using OT::Layout::GSUB; -using OT::Layout::GPOS; - - hb_subset_accelerator_t::~hb_subset_accelerator_t () { if (cmap_cache && destroy_cmap_cache) @@ -62,7 +57,6 @@ hb_subset_accelerator_t::~hb_subset_accelerator_t () } -typedef hb_hashmap_t> script_langsys_map; #ifndef HB_NO_SUBSET_CFF static inline bool _add_cff_seac_components (const OT::cff1::accelerator_subset_t &cff, @@ -97,331 +91,14 @@ _remap_palette_indexes (const hb_set_t *palette_indexes, } } -static void -_remap_indexes (const hb_set_t *indexes, - hb_map_t *mapping /* OUT */) +void +remap_indexes (const hb_set_t *indexes, + hb_map_t *mapping /* OUT */) { for (auto _ : + hb_enumerate (indexes->iter ())) mapping->set (_.second, _.first); - -} - -#ifndef HB_NO_SUBSET_LAYOUT - -/* - * Removes all tags from 'tags' that are not in filter. Additionally eliminates any duplicates. - * Returns true if anything was removed (not including duplicates). - */ -static bool _filter_tag_list(hb_vector_t* tags, /* IN/OUT */ - const hb_set_t* filter) -{ - hb_vector_t out; - out.alloc (tags->get_size() + 1); // +1 is to allocate room for the null terminator. - - bool removed = false; - hb_set_t visited; - - for (hb_tag_t tag : *tags) - { - if (!tag) continue; - if (visited.has (tag)) continue; - - if (!filter->has (tag)) - { - removed = true; - continue; - } - - visited.add (tag); - out.push (tag); - } - - // The collect function needs a null element to signal end of the array. - out.push (HB_TAG_NONE); - - hb_swap (out, *tags); - return removed; -} - -template -static void _collect_layout_indices (hb_subset_plan_t *plan, - const T& table, - hb_set_t *lookup_indices, /* OUT */ - hb_set_t *feature_indices, /* OUT */ - hb_hashmap_t> *feature_record_cond_idx_map, /* OUT */ - hb_hashmap_t *feature_substitutes_map, /* OUT */ - bool& insert_catch_all_feature_variation_record) -{ - unsigned num_features = table.get_feature_count (); - hb_vector_t features; - if (!plan->check_success (features.resize (num_features))) return; - table.get_feature_tags (0, &num_features, features.arrayZ); - bool retain_all_features = !_filter_tag_list (&features, &plan->layout_features); - - unsigned num_scripts = table.get_script_count (); - hb_vector_t scripts; - if (!plan->check_success (scripts.resize (num_scripts))) return; - table.get_script_tags (0, &num_scripts, scripts.arrayZ); - bool retain_all_scripts = !_filter_tag_list (&scripts, &plan->layout_scripts); - - if (!plan->check_success (!features.in_error ()) || !features - || !plan->check_success (!scripts.in_error ()) || !scripts) - return; - - hb_ot_layout_collect_features (plan->source, - T::tableTag, - retain_all_scripts ? nullptr : scripts.arrayZ, - nullptr, - retain_all_features ? nullptr : features.arrayZ, - feature_indices); - -#ifndef HB_NO_VAR - // collect feature substitutes with variations - if (!plan->user_axes_location.is_empty ()) - { - hb_hashmap_t, unsigned> conditionset_map; - OT::hb_collect_feature_substitutes_with_var_context_t c = - { - &plan->axes_old_index_tag_map, - &plan->axes_location, - feature_record_cond_idx_map, - feature_substitutes_map, - insert_catch_all_feature_variation_record, - feature_indices, - false, - false, - false, - 0, - &conditionset_map - }; - table.collect_feature_substitutes_with_variations (&c); - } -#endif - - for (unsigned feature_index : *feature_indices) - { - const OT::Feature* f = &(table.get_feature (feature_index)); - const OT::Feature **p = nullptr; - if (feature_substitutes_map->has (feature_index, &p)) - f = *p; - - f->add_lookup_indexes_to (lookup_indices); - } - - // If all axes are pinned then all feature variations will be dropped so there's no need - // to collect lookups from them. - if (!plan->all_axes_pinned) - { - // TODO(qxliu76): this collection doesn't work correctly for feature variations that are dropped - // but not applied. The collection will collect and retain the lookup indices - // associated with those dropped but not activated rules. Since partial instancing - // isn't yet supported this isn't an issue yet but will need to be fixed for - // partial instancing. - table.feature_variation_collect_lookups (feature_indices, feature_substitutes_map, lookup_indices); - } -} - - -static inline void -_GSUBGPOS_find_duplicate_features (const OT::GSUBGPOS &g, - const hb_map_t *lookup_indices, - const hb_set_t *feature_indices, - const hb_hashmap_t *feature_substitutes_map, - hb_map_t *duplicate_feature_map /* OUT */) -{ - if (feature_indices->is_empty ()) return; - hb_hashmap_t> unique_features; - //find out duplicate features after subset - for (unsigned i : feature_indices->iter ()) - { - hb_tag_t t = g.get_feature_tag (i); - if (t == HB_MAP_VALUE_INVALID) continue; - if (!unique_features.has (t)) - { - if (unlikely (!unique_features.set (t, hb::unique_ptr {hb_set_create ()}))) - return; - if (unique_features.has (t)) - unique_features.get (t)->add (i); - duplicate_feature_map->set (i, i); - continue; - } - - bool found = false; - - hb_set_t* same_tag_features = unique_features.get (t); - for (unsigned other_f_index : same_tag_features->iter ()) - { - const OT::Feature* f = &(g.get_feature (i)); - const OT::Feature **p = nullptr; - if (feature_substitutes_map->has (i, &p)) - f = *p; - - const OT::Feature* other_f = &(g.get_feature (other_f_index)); - if (feature_substitutes_map->has (other_f_index, &p)) - other_f = *p; - - auto f_iter = - + hb_iter (f->lookupIndex) - | hb_filter (lookup_indices) - ; - - auto other_f_iter = - + hb_iter (other_f->lookupIndex) - | hb_filter (lookup_indices) - ; - - bool is_equal = true; - for (; f_iter && other_f_iter; f_iter++, other_f_iter++) - { - unsigned a = *f_iter; - unsigned b = *other_f_iter; - if (a != b) { is_equal = false; break; } - } - - if (is_equal == false || f_iter || other_f_iter) continue; - - found = true; - duplicate_feature_map->set (i, other_f_index); - break; - } - - if (found == false) - { - same_tag_features->add (i); - duplicate_feature_map->set (i, i); - } - } } -template -static inline void -_closure_glyphs_lookups_features (hb_subset_plan_t *plan, - hb_set_t *gids_to_retain, - hb_map_t *lookups, - hb_map_t *features, - script_langsys_map *langsys_map, - hb_hashmap_t> *feature_record_cond_idx_map, - hb_hashmap_t *feature_substitutes_map, - bool& insert_catch_all_feature_variation_record) -{ - hb_blob_ptr_t table = plan->source_table (); - hb_tag_t table_tag = table->tableTag; - hb_set_t lookup_indices, feature_indices; - _collect_layout_indices (plan, - *table, - &lookup_indices, - &feature_indices, - feature_record_cond_idx_map, - feature_substitutes_map, - insert_catch_all_feature_variation_record); - - if (table_tag == HB_OT_TAG_GSUB && !(plan->flags & HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE)) - hb_ot_layout_lookups_substitute_closure (plan->source, - &lookup_indices, - gids_to_retain); - table->closure_lookups (plan->source, - gids_to_retain, - &lookup_indices); - _remap_indexes (&lookup_indices, lookups); - - // prune features - table->prune_features (lookups, - plan->user_axes_location.is_empty () ? nullptr : feature_record_cond_idx_map, - feature_substitutes_map, - &feature_indices); - hb_map_t duplicate_feature_map; - _GSUBGPOS_find_duplicate_features (*table, lookups, &feature_indices, feature_substitutes_map, &duplicate_feature_map); - - feature_indices.clear (); - table->prune_langsys (&duplicate_feature_map, &plan->layout_scripts, langsys_map, &feature_indices); - _remap_indexes (&feature_indices, features); - - table.destroy (); -} - -#endif - -#ifndef HB_NO_VAR -static inline void -_generate_varstore_inner_maps (const hb_set_t& varidx_set, - unsigned subtable_count, - hb_vector_t &inner_maps /* OUT */) -{ - if (varidx_set.is_empty () || subtable_count == 0) return; - - if (unlikely (!inner_maps.resize (subtable_count))) return; - for (unsigned idx : varidx_set) - { - uint16_t major = idx >> 16; - uint16_t minor = idx & 0xFFFF; - - if (major >= subtable_count) - continue; - inner_maps[major].add (minor); - } -} - -static inline hb_font_t* -_get_hb_font_with_variations (const hb_subset_plan_t *plan) -{ - hb_font_t *font = hb_font_create (plan->source); - - hb_vector_t vars; - if (!vars.alloc (plan->user_axes_location.get_population ())) { - hb_font_destroy (font); - return nullptr; - } - - for (auto _ : plan->user_axes_location) - { - hb_variation_t var; - var.tag = _.first; - var.value = _.second.middle; - vars.push (var); - } - -#ifndef HB_NO_VAR - hb_font_set_variations (font, vars.arrayZ, plan->user_axes_location.get_population ()); -#endif - return font; -} - -static inline void -_collect_layout_variation_indices (hb_subset_plan_t* plan) -{ - hb_blob_ptr_t gdef = plan->source_table (); - hb_blob_ptr_t gpos = plan->source_table (); - - if (!gdef->has_data ()) - { - gdef.destroy (); - gpos.destroy (); - return; - } - - hb_set_t varidx_set; - OT::hb_collect_variation_indices_context_t c (&varidx_set, - &plan->_glyphset_gsub, - &plan->gpos_lookups); - gdef->collect_variation_indices (&c); - - if (hb_ot_layout_has_positioning (plan->source)) - gpos->collect_variation_indices (&c); - - gdef->remap_layout_variation_indices (&varidx_set, - plan->normalized_coords, - !plan->pinned_at_default, - plan->all_axes_pinned, - &plan->layout_variation_idx_delta_map); - - unsigned subtable_count = gdef->has_var_store () ? gdef->get_var_store ().get_sub_table_count () : 0; - _generate_varstore_inner_maps (varidx_set, subtable_count, plan->gdef_varstore_inner_maps); - - gdef.destroy (); - gpos.destroy (); -} -#endif - static inline void _cmap_closure (hb_face_t *face, const hb_set_t *unicodes, @@ -431,12 +108,10 @@ _cmap_closure (hb_face_t *face, cmap.table->closure_glyphs (unicodes, glyphset); } -static void _colr_closure (hb_face_t *face, - hb_map_t *layers_map, - hb_map_t *palettes_map, +static void _colr_closure (hb_subset_plan_t* plan, hb_set_t *glyphs_colred) { - OT::COLR::accelerator_t colr (face); + OT::COLR::accelerator_t colr (plan->source); if (!colr.is_valid ()) return; hb_set_t palette_indices, layer_indices; @@ -448,11 +123,45 @@ static void _colr_closure (hb_face_t *face, glyphs_colred->union_ (glyphset_colrv0); //closure for COLRv1 - colr.closure_forV1 (glyphs_colred, &layer_indices, &palette_indices); + hb_set_t variation_indices, delta_set_indices; + colr.closure_forV1 (glyphs_colred, &layer_indices, &palette_indices, &variation_indices, &delta_set_indices); colr.closure_V0palette_indices (glyphs_colred, &palette_indices); - _remap_indexes (&layer_indices, layers_map); - _remap_palette_indexes (&palette_indices, palettes_map); + remap_indexes (&layer_indices, &plan->colrv1_layers); + _remap_palette_indexes (&palette_indices, &plan->colr_palettes); + +#ifndef HB_NO_VAR + if (!colr.has_var_store () || !variation_indices) return; + + const OT::ItemVariationStore &var_store = colr.get_var_store (); + // generated inner_maps is used by ItemVariationStore serialize(), which is subset only + unsigned subtable_count = var_store.get_sub_table_count (); + generate_varstore_inner_maps (variation_indices, subtable_count, plan->colrv1_varstore_inner_maps); + + /* colr variation indices mapping during planning phase: + * generate colrv1_variation_idx_delta_map. When delta set index map is not + * included, it's a mapping from varIdx-> (new varIdx,delta). Otherwise, it's + * a mapping from old delta set idx-> (new delta set idx, delta). Mapping + * delta set indices is the same as gid mapping. + * Besides, we need to generate a delta set idx-> new var_idx map for updating + * delta set index map if exists. This map will be updated again after + * instancing. */ + if (!plan->all_axes_pinned) + { + remap_variation_indices (var_store, + variation_indices, + plan->normalized_coords, + false, /* no need to calculate delta for COLR during planning */ + plan->all_axes_pinned, + plan->colrv1_variation_idx_delta_map); + + if (colr.has_delta_set_index_map ()) + remap_colrv1_delta_set_index_indices (colr.get_delta_set_index_map (), + delta_set_indices, + plan->colrv1_variation_idx_delta_map, + plan->colrv1_new_deltaset_idx_varidx_map); + } +#endif } static inline void @@ -465,7 +174,6 @@ _math_closure (hb_subset_plan_t *plan, math.destroy (); } - static inline void _remove_invalid_gids (hb_set_t *glyphs, unsigned int num_glyphs) @@ -473,14 +181,76 @@ _remove_invalid_gids (hb_set_t *glyphs, glyphs->del_range (num_glyphs, HB_SET_VALUE_INVALID); } +template +static void +_fill_unicode_and_glyph_map(hb_subset_plan_t *plan, + I unicode_iterator, + F unicode_to_gid_for_iterator, + G unicode_to_gid_general) +{ + for (hb_codepoint_t cp : unicode_iterator) + { + hb_codepoint_t gid = unicode_to_gid_for_iterator(cp); + if (!GID_ALWAYS_EXISTS && gid == HB_MAP_VALUE_INVALID) + { + DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", cp); + continue; + } + + plan->codepoint_to_glyph->set (cp, gid); + plan->unicode_to_new_gid_list.push (hb_pair (cp, gid)); + } +} + +template +static void +_fill_unicode_and_glyph_map(hb_subset_plan_t *plan, + I unicode_iterator, + F unicode_to_gid_for_iterator) +{ + _fill_unicode_and_glyph_map(plan, unicode_iterator, unicode_to_gid_for_iterator, unicode_to_gid_for_iterator); +} + +/* + * Finds additional unicode codepoints which are reachable from the input unicode set. + * Currently this adds in mirrored variants (needed for bidi) of any input unicodes. + */ +static hb_set_t +_unicode_closure (const hb_set_t* unicodes, bool bidi_closure) { + // TODO: we may want to also consider pulling in reachable unicode composition and decompositions. + // see: https://github.com/harfbuzz/harfbuzz/issues/2283 + hb_set_t out = *unicodes; + if (!bidi_closure) return out; + + if (out.is_inverted()) { + // don't closure inverted sets, they are asking to specifically exclude certain codepoints. + // otherwise everything is already included. + return out; + } + + auto unicode_funcs = hb_unicode_funcs_get_default (); + for (hb_codepoint_t cp : *unicodes) { + hb_codepoint_t mirror = hb_unicode_mirroring(unicode_funcs, cp); + if (unlikely (mirror != cp)) { + out.add(mirror); + } + } + + return out; +} + static void -_populate_unicodes_to_retain (const hb_set_t *unicodes, +_populate_unicodes_to_retain (const hb_set_t *unicodes_in, const hb_set_t *glyphs, hb_subset_plan_t *plan) { + hb_set_t unicodes = _unicode_closure(unicodes_in, + !(plan->flags & HB_SUBSET_FLAGS_NO_BIDI_CLOSURE)); + OT::cmap::accelerator_t cmap (plan->source); unsigned size_threshold = plan->source->get_num_glyphs (); - if (glyphs->is_empty () && unicodes->get_population () < size_threshold) + + if (glyphs->is_empty () && unicodes.get_population () < size_threshold) { const hb_map_t* unicode_to_gid = nullptr; @@ -490,37 +260,23 @@ _populate_unicodes_to_retain (const hb_set_t *unicodes, // This is approach to collection is faster, but can only be used if glyphs // are not being explicitly added to the subset and the input unicodes set is // not excessively large (eg. an inverted set). - plan->unicode_to_new_gid_list.alloc (unicodes->get_population ()); + plan->unicode_to_new_gid_list.alloc (unicodes.get_population ()); if (!unicode_to_gid) { - for (hb_codepoint_t cp : *unicodes) - { + _fill_unicode_and_glyph_map(plan, unicodes.iter(), [&] (hb_codepoint_t cp) { hb_codepoint_t gid; - if (!cmap.get_nominal_glyph (cp, &gid)) - { - DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", cp); - continue; + if (!cmap.get_nominal_glyph (cp, &gid)) { + return HB_MAP_VALUE_INVALID; } - - plan->codepoint_to_glyph->set (cp, gid); - plan->unicode_to_new_gid_list.push (hb_pair (cp, gid)); - } + return gid; + }); } else { // Use in memory unicode to gid map it's faster then looking up from // the map. This code is mostly duplicated from above to avoid doing // conditionals on the presence of the unicode_to_gid map each // iteration. - for (hb_codepoint_t cp : *unicodes) - { - hb_codepoint_t gid = unicode_to_gid->get (cp); - if (gid == HB_MAP_VALUE_INVALID) - { - DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", cp); - continue; - } - - plan->codepoint_to_glyph->set (cp, gid); - plan->unicode_to_new_gid_list.push (hb_pair (cp, gid)); - } + _fill_unicode_and_glyph_map(plan, unicodes.iter(), [&] (hb_codepoint_t cp) { + return unicode_to_gid->get (cp); + }); } } else @@ -535,7 +291,7 @@ _populate_unicodes_to_retain (const hb_set_t *unicodes, if (!plan->accelerator) { cmap.collect_mapping (&cmap_unicodes_storage, &unicode_glyphid_map_storage); - plan->unicode_to_new_gid_list.alloc (hb_min(unicodes->get_population () + plan->unicode_to_new_gid_list.alloc (hb_min(unicodes.get_population () + glyphs->get_population (), cmap_unicodes->get_population ())); } else { @@ -544,48 +300,52 @@ _populate_unicodes_to_retain (const hb_set_t *unicodes, } if (plan->accelerator && - unicodes->get_population () < cmap_unicodes->get_population () && + unicodes.get_population () < cmap_unicodes->get_population () && glyphs->get_population () < cmap_unicodes->get_population ()) { - plan->codepoint_to_glyph->alloc (unicodes->get_population () + glyphs->get_population ()); + plan->codepoint_to_glyph->alloc (unicodes.get_population () + glyphs->get_population ()); auto &gid_to_unicodes = plan->accelerator->gid_to_unicodes; + for (hb_codepoint_t gid : *glyphs) { auto unicodes = gid_to_unicodes.get (gid); - - for (hb_codepoint_t cp : unicodes) - { - plan->codepoint_to_glyph->set (cp, gid); - plan->unicode_to_new_gid_list.push (hb_pair (cp, gid)); - } + _fill_unicode_and_glyph_map(plan, unicodes, [&] (hb_codepoint_t cp) { + return gid; + }, + [&] (hb_codepoint_t cp) { + return unicode_glyphid_map->get(cp); + }); } - for (hb_codepoint_t cp : *unicodes) - { - /* Don't double-add entry. */ + + _fill_unicode_and_glyph_map(plan, unicodes.iter(), [&] (hb_codepoint_t cp) { + /* Don't double-add entry. */ if (plan->codepoint_to_glyph->has (cp)) - continue; + return HB_MAP_VALUE_INVALID; - hb_codepoint_t *gid; - if (!unicode_glyphid_map->has(cp, &gid)) - continue; + return unicode_glyphid_map->get(cp); + }, + [&] (hb_codepoint_t cp) { + return unicode_glyphid_map->get(cp); + }); - plan->codepoint_to_glyph->set (cp, *gid); - plan->unicode_to_new_gid_list.push (hb_pair (cp, *gid)); - } plan->unicode_to_new_gid_list.qsort (); } else { plan->codepoint_to_glyph->alloc (cmap_unicodes->get_population ()); - for (hb_codepoint_t cp : *cmap_unicodes) + hb_codepoint_t first = HB_SET_VALUE_INVALID, last = HB_SET_VALUE_INVALID; + for (; cmap_unicodes->next_range (&first, &last); ) { - hb_codepoint_t gid = (*unicode_glyphid_map)[cp]; - if (!unicodes->has (cp) && !glyphs->has (gid)) - continue; - - plan->codepoint_to_glyph->set (cp, gid); - plan->unicode_to_new_gid_list.push (hb_pair (cp, gid)); + _fill_unicode_and_glyph_map(plan, hb_range(first, last + 1), [&] (hb_codepoint_t cp) { + hb_codepoint_t gid = (*unicode_glyphid_map)[cp]; + if (!unicodes.has (cp) && !glyphs->has (gid)) + return HB_MAP_VALUE_INVALID; + return gid; + }, + [&] (hb_codepoint_t cp) { + return unicode_glyphid_map->get(cp); + }); } } @@ -608,11 +368,22 @@ _populate_unicodes_to_retain (const hb_set_t *unicodes, plan->unicodes.add_sorted_array (&arr.arrayZ->first, arr.length, sizeof (*arr.arrayZ)); plan->_glyphset_gsub.add_array (&arr.arrayZ->second, arr.length, sizeof (*arr.arrayZ)); } -} -#ifndef HB_COMPOSITE_OPERATIONS_PER_GLYPH -#define HB_COMPOSITE_OPERATIONS_PER_GLYPH 64 -#endif + // Variation selectors don't have glyphs associated with them in the cmap so they will have been filtered out above + // but should still be retained. Add them back here. + + // However, the min and max codepoints for OS/2 should be calculated without considering variation selectors, + // so record those first. + plan->os2_info.min_cmap_codepoint = plan->unicodes.get_min(); + plan->os2_info.max_cmap_codepoint = plan->unicodes.get_max(); + + hb_set_t variation_selectors_to_retain; + cmap.collect_variation_selectors(&variation_selectors_to_retain); + + variation_selectors_to_retain.iter() + | hb_filter(unicodes) + | hb_sink(&plan->unicodes) + ; +} static unsigned _glyf_add_gid_and_children (const OT::glyf_accelerator_t &glyf, @@ -639,18 +410,6 @@ _glyf_add_gid_and_children (const OT::glyf_accelerator_t &glyf, operation_count, depth); -#ifndef HB_NO_VAR_COMPOSITES - for (auto &item : glyph.get_var_composite_iterator ()) - { - operation_count = - _glyf_add_gid_and_children (glyf, - item.get_gid (), - gids_to_retain, - operation_count, - depth); - } -#endif - return operation_count; } @@ -671,18 +430,7 @@ _nameid_closure (hb_subset_plan_t* plan, #endif #ifndef HB_NO_SUBSET_LAYOUT - if (!drop_tables->has (HB_OT_TAG_GPOS)) - { - hb_blob_ptr_t gpos = plan->source_table (); - gpos->collect_name_ids (&plan->gpos_features, &plan->name_ids); - gpos.destroy (); - } - if (!drop_tables->has (HB_OT_TAG_GSUB)) - { - hb_blob_ptr_t gsub = plan->source_table (); - gsub->collect_name_ids (&plan->gsub_features, &plan->name_ids); - gsub.destroy (); - } + layout_nameid_closure(plan, drop_tables); #endif } @@ -704,29 +452,9 @@ _populate_gids_to_retain (hb_subset_plan_t* plan, _cmap_closure (plan->source, &plan->unicodes, &plan->_glyphset_gsub); #ifndef HB_NO_SUBSET_LAYOUT - if (!drop_tables->has (HB_OT_TAG_GSUB)) - // closure all glyphs/lookups/features needed for GSUB substitutions. - _closure_glyphs_lookups_features ( - plan, - &plan->_glyphset_gsub, - &plan->gsub_lookups, - &plan->gsub_features, - &plan->gsub_langsys, - &plan->gsub_feature_record_cond_idx_map, - &plan->gsub_feature_substitutes_map, - plan->gsub_insert_catch_all_feature_variation_rec); - - if (!drop_tables->has (HB_OT_TAG_GPOS)) - _closure_glyphs_lookups_features ( - plan, - &plan->_glyphset_gsub, - &plan->gpos_lookups, - &plan->gpos_features, - &plan->gpos_langsys, - &plan->gpos_feature_record_cond_idx_map, - &plan->gpos_feature_substitutes_map, - plan->gpos_insert_catch_all_feature_variation_rec); + layout_populate_gids_to_retain(plan, drop_tables); #endif + _remove_invalid_gids (&plan->_glyphset_gsub, plan->source->get_num_glyphs ()); plan->_glyphset_mathed = plan->_glyphset_gsub; @@ -739,19 +467,21 @@ _populate_gids_to_retain (hb_subset_plan_t* plan, hb_set_t cur_glyphset = plan->_glyphset_mathed; if (!drop_tables->has (HB_OT_TAG_COLR)) { - _colr_closure (plan->source, &plan->colrv1_layers, &plan->colr_palettes, &cur_glyphset); + _colr_closure (plan, &cur_glyphset); _remove_invalid_gids (&cur_glyphset, plan->source->get_num_glyphs ()); } plan->_glyphset_colred = cur_glyphset; + // XXX TODO VARC closure / subset + _nameid_closure (plan, drop_tables); /* Populate a full set of glyphs to retain by adding all referenced * composite glyphs. */ if (glyf.has_data ()) for (hb_codepoint_t gid : cur_glyphset) _glyf_add_gid_and_children (glyf, gid, &plan->_glyphset, - cur_glyphset.get_population () * HB_COMPOSITE_OPERATIONS_PER_GLYPH); + cur_glyphset.get_population () * HB_MAX_COMPOSITE_OPERATIONS_PER_GLYPH); else plan->_glyphset.union_ (cur_glyphset); #ifndef HB_NO_SUBSET_CFF @@ -769,8 +499,10 @@ _populate_gids_to_retain (hb_subset_plan_t* plan, _remove_invalid_gids (&plan->_glyphset, plan->source->get_num_glyphs ()); #ifndef HB_NO_VAR +#ifndef HB_NO_SUBSET_LAYOUT if (!drop_tables->has (HB_OT_TAG_GDEF)) - _collect_layout_variation_indices (plan); + collect_layout_variation_indices (plan); +#endif #endif } @@ -871,9 +603,11 @@ _create_old_gid_to_new_gid_map (const hb_face_t *face, *num_glyphs = max_glyph + 1; } + reverse_glyph_map->alloc (reverse_glyph_map->get_population () + new_to_old_gid_list->length); + hb_iter (new_to_old_gid_list) | hb_sink (reverse_glyph_map) ; + glyph_map->alloc (glyph_map->get_population () + new_to_old_gid_list->length); + hb_iter (new_to_old_gid_list) | hb_map (&hb_codepoint_pair_t::reverse) | hb_sink (glyph_map) @@ -882,188 +616,6 @@ _create_old_gid_to_new_gid_map (const hb_face_t *face, return true; } -#ifndef HB_NO_VAR -static void -_normalize_axes_location (hb_face_t *face, hb_subset_plan_t *plan) -{ - if (plan->user_axes_location.is_empty ()) - return; - - hb_array_t axes = face->table.fvar->get_axes (); - plan->normalized_coords.resize (axes.length); - - bool has_avar = face->table.avar->has_data (); - const OT::SegmentMaps *seg_maps = nullptr; - unsigned avar_axis_count = 0; - if (has_avar) - { - seg_maps = face->table.avar->get_segment_maps (); - avar_axis_count = face->table.avar->get_axis_count(); - } - - bool axis_not_pinned = false; - unsigned old_axis_idx = 0, new_axis_idx = 0; - for (const auto& axis : axes) - { - hb_tag_t axis_tag = axis.get_axis_tag (); - plan->axes_old_index_tag_map.set (old_axis_idx, axis_tag); - - if (!plan->user_axes_location.has (axis_tag) || - !plan->user_axes_location.get (axis_tag).is_point ()) - { - axis_not_pinned = true; - plan->axes_index_map.set (old_axis_idx, new_axis_idx); - plan->axis_tags.push (axis_tag); - new_axis_idx++; - } - - Triple *axis_range; - if (plan->user_axes_location.has (axis_tag, &axis_range)) - { - plan->axes_triple_distances.set (axis_tag, axis.get_triple_distances ()); - - int normalized_min = axis.normalize_axis_value (axis_range->minimum); - int normalized_default = axis.normalize_axis_value (axis_range->middle); - int normalized_max = axis.normalize_axis_value (axis_range->maximum); - - if (has_avar && old_axis_idx < avar_axis_count) - { - normalized_min = seg_maps->map (normalized_min); - normalized_default = seg_maps->map (normalized_default); - normalized_max = seg_maps->map (normalized_max); - } - plan->axes_location.set (axis_tag, Triple (static_cast (normalized_min / 16384.f), - static_cast (normalized_default / 16384.f), - static_cast (normalized_max / 16384.f))); - - if (normalized_default != 0) - plan->pinned_at_default = false; - - plan->normalized_coords[old_axis_idx] = normalized_default; - } - - old_axis_idx++; - - if (has_avar && old_axis_idx < avar_axis_count) - seg_maps = &StructAfter (*seg_maps); - } - plan->all_axes_pinned = !axis_not_pinned; -} - -static void -_update_instance_metrics_map_from_cff2 (hb_subset_plan_t *plan) -{ - if (!plan->normalized_coords) return; - OT::cff2::accelerator_t cff2 (plan->source); - if (!cff2.is_valid ()) return; - - hb_font_t *font = nullptr; - if (unlikely (!plan->check_success (font = _get_hb_font_with_variations (plan)))) - { - hb_font_destroy (font); - return; - } - - hb_glyph_extents_t extents = {0x7FFF, -0x7FFF}; - OT::hmtx_accelerator_t _hmtx (plan->source); - float *hvar_store_cache = nullptr; - if (_hmtx.has_data () && _hmtx.var_table.get_length ()) - hvar_store_cache = _hmtx.var_table->get_var_store ().create_cache (); - - OT::vmtx_accelerator_t _vmtx (plan->source); - float *vvar_store_cache = nullptr; - if (_vmtx.has_data () && _vmtx.var_table.get_length ()) - vvar_store_cache = _vmtx.var_table->get_var_store ().create_cache (); - - for (auto p : *plan->glyph_map) - { - hb_codepoint_t old_gid = p.first; - hb_codepoint_t new_gid = p.second; - if (!cff2.get_extents (font, old_gid, &extents)) continue; - bool has_bounds_info = true; - if (extents.x_bearing == 0 && extents.width == 0 && - extents.height == 0 && extents.y_bearing == 0) - has_bounds_info = false; - - if (has_bounds_info) - { - plan->head_maxp_info.xMin = hb_min (plan->head_maxp_info.xMin, extents.x_bearing); - plan->head_maxp_info.xMax = hb_max (plan->head_maxp_info.xMax, extents.x_bearing + extents.width); - plan->head_maxp_info.yMax = hb_max (plan->head_maxp_info.yMax, extents.y_bearing); - plan->head_maxp_info.yMin = hb_min (plan->head_maxp_info.yMin, extents.y_bearing + extents.height); - } - - if (_hmtx.has_data ()) - { - int hori_aw = _hmtx.get_advance_without_var_unscaled (old_gid); - if (_hmtx.var_table.get_length ()) - hori_aw += (int) roundf (_hmtx.var_table->get_advance_delta_unscaled (old_gid, font->coords, font->num_coords, - hvar_store_cache)); - int lsb = extents.x_bearing; - if (!has_bounds_info) - { - if (!_hmtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb)) - continue; - } - plan->hmtx_map.set (new_gid, hb_pair ((unsigned) hori_aw, lsb)); - plan->bounds_width_vec[new_gid] = extents.width; - } - - if (_vmtx.has_data ()) - { - int vert_aw = _vmtx.get_advance_without_var_unscaled (old_gid); - if (_vmtx.var_table.get_length ()) - vert_aw += (int) roundf (_vmtx.var_table->get_advance_delta_unscaled (old_gid, font->coords, font->num_coords, - vvar_store_cache)); - - int tsb = extents.y_bearing; - if (!has_bounds_info) - { - if (!_vmtx.get_leading_bearing_without_var_unscaled (old_gid, &tsb)) - continue; - } - plan->vmtx_map.set (new_gid, hb_pair ((unsigned) vert_aw, tsb)); - plan->bounds_height_vec[new_gid] = extents.height; - } - } - hb_font_destroy (font); - if (hvar_store_cache) - _hmtx.var_table->get_var_store ().destroy_cache (hvar_store_cache); - if (vvar_store_cache) - _vmtx.var_table->get_var_store ().destroy_cache (vvar_store_cache); -} - -static bool -_get_instance_glyphs_contour_points (hb_subset_plan_t *plan) -{ - /* contour_points vector only needed for updating gvar table (infer delta) - * during partial instancing */ - if (plan->user_axes_location.is_empty () || plan->all_axes_pinned) - return true; - - OT::glyf_accelerator_t glyf (plan->source); - - for (auto &_ : plan->new_to_old_gid_list) - { - hb_codepoint_t new_gid = _.first; - contour_point_vector_t all_points; - if (new_gid == 0 && !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) - { - if (unlikely (!plan->new_gid_contour_points_map.set (new_gid, all_points))) - return false; - continue; - } - - hb_codepoint_t old_gid = _.second; - if (unlikely (!glyf.glyph_for_gid (old_gid).get_all_points_without_var (plan->source, all_points))) - return false; - if (unlikely (!plan->new_gid_contour_points_map.set (new_gid, all_points))) - return false; - } - return true; -} -#endif - hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face, const hb_subset_input_t *input) { @@ -1093,6 +645,7 @@ hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face, user_axes_location = input->axes_location; all_axes_pinned = false; pinned_at_default = true; + has_gdef_varstore = false; #ifdef HB_EXPERIMENTAL_API for (auto _ : input->name_table_overrides) @@ -1112,6 +665,10 @@ hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face, attach_accelerator_data = input->attach_accelerator_data; force_long_loca = input->force_long_loca; +#ifdef HB_EXPERIMENTAL_API + force_long_loca = force_long_loca || (flags & HB_SUBSET_FLAGS_IFTB_REQUIREMENTS); +#endif + if (accel) accelerator = (hb_subset_accelerator_t*) accel; @@ -1119,7 +676,7 @@ hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face, return; #ifndef HB_NO_VAR - _normalize_axes_location (face, this); + normalize_axes_location (face, this); #endif _populate_unicodes_to_retain (input->sets.unicodes, input->sets.glyphs, this); @@ -1160,12 +717,24 @@ hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face, for (auto &v : bounds_height_vec) v = 0xFFFFFFFF; +#ifndef HB_NO_SUBSET_LAYOUT + if (!drop_tables.has (HB_OT_TAG_GDEF)) + remap_used_mark_sets (this, used_mark_sets_map); +#endif + +#ifndef HB_NO_VAR +#ifndef HB_NO_BASE + if (!drop_tables.has (HB_OT_TAG_BASE)) + collect_base_variation_indices (this); +#endif +#endif + if (unlikely (in_error ())) return; #ifndef HB_NO_VAR - _update_instance_metrics_map_from_cff2 (this); - if (!check_success (_get_instance_glyphs_contour_points (this))) + update_instance_metrics_map_from_cff2 (this); + if (!check_success (get_instance_glyphs_contour_points (this))) return; #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.hh index 40a6ff112af93..8a6574365ed5f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.hh @@ -41,6 +41,13 @@ namespace OT { struct Feature; } +struct os2_info_t { + hb_codepoint_t min_cmap_codepoint; + hb_codepoint_t max_cmap_codepoint; +}; + +typedef struct os2_info_t os2_info_t; + struct head_maxp_info_t { head_maxp_info_t () @@ -78,10 +85,16 @@ struct contour_point_t y = x * matrix[1] + y * matrix[3]; x = x_; } + + void add_delta (float delta_x, float delta_y) + { + x += delta_x; + y += delta_y; + } + HB_ALWAYS_INLINE void translate (const contour_point_t &p) { x += p.x; y += p.y; } - float x; float y; uint8_t flag; @@ -90,14 +103,20 @@ struct contour_point_t struct contour_point_vector_t : hb_vector_t { - void extend (const hb_array_t &a) + bool add_deltas (hb_array_t deltas_x, + hb_array_t deltas_y, + hb_array_t indices) { - unsigned int old_len = length; - if (unlikely (!resize (old_len + a.length, false))) - return; - auto arrayZ = this->arrayZ + old_len; - unsigned count = a.length; - hb_memcpy (arrayZ, a.arrayZ, count * sizeof (arrayZ[0])); + if (indices.length != deltas_x.length || + indices.length != deltas_y.length) + return false; + + for (unsigned i = 0; i < indices.length; i++) + { + if (!indices.arrayZ[i]) continue; + arrayZ[i].add_delta (deltas_x.arrayZ[i], deltas_y.arrayZ[i]); + } + return true; } }; @@ -147,6 +166,9 @@ struct hb_subset_plan_t bool gsub_insert_catch_all_feature_variation_rec; bool gpos_insert_catch_all_feature_variation_rec; + // whether GDEF ItemVariationStore is retained + mutable bool has_gdef_varstore; + #define HB_SUBSET_PLAN_MEMBER(Type, Name) Type Name; #include "hb-subset-plan-member-list.hh" #undef HB_SUBSET_PLAN_MEMBER @@ -154,6 +176,8 @@ struct hb_subset_plan_t //recalculated head/maxp table info after instancing mutable head_maxp_info_t head_maxp_info; + os2_info_t os2_info; + const hb_subset_accelerator_t* accelerator; hb_subset_accelerator_t* inprogress_accelerator; @@ -272,5 +296,75 @@ struct hb_subset_plan_t } }; +// hb-subset-plan implementation is split into multiple files to keep +// compile times more reasonable: +// - hb-subset-plan.cc +// - hb-subset-plan-layout.cc +// +// The functions below are those needed to connect the split files +// above together. +HB_INTERNAL void +remap_indexes (const hb_set_t *indexes, + hb_map_t *mapping /* OUT */); + + +#ifndef HB_NO_VAR +template +HB_INTERNAL void +remap_variation_indices (const ItemVarStore &var_store, + const hb_set_t &variation_indices, + const hb_vector_t& normalized_coords, + bool calculate_delta, /* not pinned at default */ + bool no_variations, /* all axes pinned */ + hb_hashmap_t> &variation_idx_delta_map /* OUT */); + + +template +HB_INTERNAL void +remap_colrv1_delta_set_index_indices (const DeltaSetIndexMap &index_map, + const hb_set_t &delta_set_idxes, + hb_hashmap_t> &variation_idx_delta_map, /* IN/OUT */ + hb_map_t &new_deltaset_idx_varidx_map /* OUT */); + + +HB_INTERNAL void +generate_varstore_inner_maps (const hb_set_t& varidx_set, + unsigned subtable_count, + hb_vector_t &inner_maps /* OUT */); + +HB_INTERNAL void +normalize_axes_location (hb_face_t *face, hb_subset_plan_t *plan); + +HB_INTERNAL void +update_instance_metrics_map_from_cff2 (hb_subset_plan_t *plan); + +HB_INTERNAL bool +get_instance_glyphs_contour_points (hb_subset_plan_t *plan); + +#ifndef HB_NO_BASE +HB_INTERNAL void +collect_base_variation_indices (hb_subset_plan_t* plan); +#endif +#endif + +#ifndef HB_NO_SUBSET_LAYOUT +typedef hb_hashmap_t> script_langsys_map; + +HB_INTERNAL void +remap_used_mark_sets (hb_subset_plan_t *plan, + hb_map_t& used_mark_sets_map); + +HB_INTERNAL void +layout_nameid_closure (hb_subset_plan_t* plan, + hb_set_t* drop_tables); + +HB_INTERNAL void +layout_populate_gids_to_retain (hb_subset_plan_t* plan, + hb_set_t* drop_tables); + +HB_INTERNAL void +collect_layout_variation_indices (hb_subset_plan_t* plan); +#endif + #endif /* HB_SUBSET_PLAN_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-serialize.h b/src/java.desktop/share/native/libharfbuzz/hb-subset-serialize.h new file mode 100644 index 0000000000000..9035d4ced7a1e --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-serialize.h @@ -0,0 +1,83 @@ +/* + * Copyright © 2022 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + */ + +#ifndef HB_SUBSET_SERIALIZE_H +#define HB_SUBSET_SERIALIZE_H + +#include "hb.h" + +HB_BEGIN_DECLS + +/** + * hb_subset_serialize_link_t: + * @width: offsetSize in bytes + * @position: position of the offset field in bytes from + * beginning of subtable + * @objidx: index of subtable + * + * Represents a link between two objects in the object graph + * to be serialized. + * + * Since: 10.2.0 + */ +typedef struct hb_subset_serialize_link_t { + unsigned int width; + unsigned int position; + unsigned int objidx; +} hb_subset_serialize_link_t; + +/** + * hb_subset_serialize_object_t: + * @head: start of object data + * @tail: end of object data + * @num_real_links: number of offset field in the object + * @real_links: array of offset info + * @num_virtual_links: number of objects that must be packed + * after current object in the final + * serialized order + * @virtual_links: array of virtual link info + * + * Represents an object in the object graph to be serialized. + * + * Since: 10.2.0 + */ +typedef struct hb_subset_serialize_object_t { + char *head; + char *tail; + unsigned int num_real_links; + hb_subset_serialize_link_t *real_links; + unsigned int num_virtual_links; + hb_subset_serialize_link_t *virtual_links; +} hb_subset_serialize_object_t; + +HB_EXTERN hb_blob_t * +hb_subset_serialize_or_fail (hb_tag_t table_tag, + hb_subset_serialize_object_t *hb_objects, + unsigned num_hb_objs); + + +HB_END_DECLS + +#endif /* HB_SUBSET_SERIALIZE_H */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset.cc index c125a435b2b7b..079c94eddfee3 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset.cc @@ -48,6 +48,7 @@ #include "hb-ot-cff2-table.hh" #include "hb-ot-vorg-table.hh" #include "hb-ot-name-table.hh" +#include "hb-ot-layout-base-table.hh" #include "hb-ot-layout-gsub-table.hh" #include "hb-ot-layout-gpos-table.hh" #include "hb-ot-var-avar-table.hh" @@ -294,8 +295,8 @@ _try_subset (const TableType *table, DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c ran out of room; reallocating to %u bytes.", HB_UNTAG (c->table_tag), buf_size); - if (unlikely (buf_size > c->source_blob->length * 16 || - !buf->alloc (buf_size, true))) + if (unlikely (buf_size > c->source_blob->length * 256 || + !buf->alloc_exact (buf_size))) { DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c failed to reallocate %u bytes.", HB_UNTAG (c->table_tag), buf_size); @@ -460,9 +461,10 @@ _dependencies_satisfied (hb_subset_plan_t *plan, hb_tag_t tag, case HB_OT_TAG_hmtx: case HB_OT_TAG_vmtx: case HB_OT_TAG_maxp: + case HB_OT_TAG_OS2: return !plan->normalized_coords || !pending_subset_tags.has (HB_OT_TAG_glyf); case HB_OT_TAG_GPOS: - return !plan->normalized_coords || plan->all_axes_pinned || !pending_subset_tags.has (HB_OT_TAG_GDEF); + return plan->all_axes_pinned || !pending_subset_tags.has (HB_OT_TAG_GDEF); default: return true; } @@ -502,6 +504,7 @@ _subset_table (hb_subset_plan_t *plan, case HB_OT_TAG_CBLC: return _subset (plan, buf); case HB_OT_TAG_CBDT: return true; /* skip CBDT, handled by CBLC */ case HB_OT_TAG_MATH: return _subset (plan, buf); + case HB_OT_TAG_BASE: return _subset (plan, buf); #ifndef HB_NO_SUBSET_CFF case HB_OT_TAG_CFF1: return _subset (plan, buf); @@ -547,6 +550,7 @@ _subset_table (hb_subset_plan_t *plan, } #endif return _passthrough (plan, tag); + default: if (plan->flags & HB_SUBSET_FLAGS_PASSTHROUGH_UNRECOGNIZED) return _passthrough (plan, tag); @@ -590,14 +594,20 @@ static void _attach_accelerator_data (hb_subset_plan_t* plan, * @input: input to use for the subsetting. * * Subsets a font according to provided input. Returns nullptr - * if the subset operation fails. + * if the subset operation fails or the face has no glyphs. * * Since: 2.9.0 **/ hb_face_t * hb_subset_or_fail (hb_face_t *source, const hb_subset_input_t *input) { - if (unlikely (!input || !source)) return hb_face_get_empty (); + if (unlikely (!input || !source)) return nullptr; + + if (unlikely (!source->get_num_glyphs ())) + { + DEBUG_MSG (SUBSET, nullptr, "No glyphs in source font."); + return nullptr; + } hb_subset_plan_t *plan = hb_subset_plan_create_or_fail (source, input); if (unlikely (!plan)) { @@ -698,3 +708,107 @@ hb_subset_plan_execute_or_fail (hb_subset_plan_t *plan) end: return success ? hb_face_reference (plan->dest) : nullptr; } + + +#ifdef HB_EXPERIMENTAL_API + +#include "hb-ot-cff1-table.hh" + +template +static hb_blob_t* get_charstrings_data(accel_t& accel, hb_codepoint_t glyph_index) { + if (!accel.is_valid()) { + return hb_blob_get_empty (); + } + + hb_ubytes_t bytes = (*accel.charStrings)[glyph_index]; + if (!bytes) { + return hb_blob_get_empty (); + } + + hb_blob_t* cff_blob = accel.get_blob(); + uint32_t length; + const char* cff_data = hb_blob_get_data(cff_blob, &length) ; + + long int offset = (const char*) bytes.arrayZ - cff_data; + if (offset < 0 || offset > INT32_MAX) { + return hb_blob_get_empty (); + } + + return hb_blob_create_sub_blob(cff_blob, (uint32_t) offset, bytes.length); +} + +template +static hb_blob_t* get_charstrings_index(accel_t& accel) { + if (!accel.is_valid()) { + return hb_blob_get_empty (); + } + + const char* charstrings_start = (const char*) accel.charStrings; + unsigned charstrings_length = accel.charStrings->get_size(); + + hb_blob_t* cff_blob = accel.get_blob(); + uint32_t length; + const char* cff_data = hb_blob_get_data(cff_blob, &length) ; + + long int offset = charstrings_start - cff_data; + if (offset < 0 || offset > INT32_MAX) { + return hb_blob_get_empty (); + } + + return hb_blob_create_sub_blob(cff_blob, (uint32_t) offset, charstrings_length); +} + +/** + * hb_subset_cff_get_charstring_data: + * @face: A face object + * @glyph_index: Glyph index to get data for. + * + * Returns the raw outline data from the CFF/CFF2 table associated with the given glyph index. + * + * XSince: EXPERIMENTAL + **/ +HB_EXTERN hb_blob_t* +hb_subset_cff_get_charstring_data(hb_face_t* face, hb_codepoint_t glyph_index) { + return get_charstrings_data(*face->table.cff1, glyph_index); +} + +/** + * hb_subset_cff_get_charstrings_index: + * @face: A face object + * + * Returns the raw CFF CharStrings INDEX from the CFF table. + * + * XSince: EXPERIMENTAL + **/ +HB_EXTERN hb_blob_t* +hb_subset_cff_get_charstrings_index (hb_face_t* face) { + return get_charstrings_index (*face->table.cff1); +} + +/** + * hb_subset_cff2_get_charstring_data: + * @face: A face object + * @glyph_index: Glyph index to get data for. + * + * Returns the raw outline data from the CFF/CFF2 table associated with the given glyph index. + * + * XSince: EXPERIMENTAL + **/ +HB_EXTERN hb_blob_t* +hb_subset_cff2_get_charstring_data(hb_face_t* face, hb_codepoint_t glyph_index) { + return get_charstrings_data(*face->table.cff2, glyph_index); +} + +/** + * hb_subset_cff2_get_charstrings_index: + * @face: A face object + * + * Returns the raw CFF2 CharStrings INDEX from the CFF2 table. + * + * XSince: EXPERIMENTAL + **/ +HB_EXTERN hb_blob_t* +hb_subset_cff2_get_charstrings_index (hb_face_t* face) { + return get_charstrings_index (*face->table.cff2); +} +#endif \ No newline at end of file diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset.h b/src/java.desktop/share/native/libharfbuzz/hb-subset.h index 8ea24da41b94f..3eccd25738ec8 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset.h @@ -71,8 +71,15 @@ typedef struct hb_subset_plan_t hb_subset_plan_t; * in the final subset. * @HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES: If set then the unicode ranges in * OS/2 will not be recalculated. - * @HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE: If set don't perform glyph closure on layout + * @HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE: If set do not perform glyph closure on layout * substitution rules (GSUB). Since: 7.2.0. + * @HB_SUBSET_FLAGS_OPTIMIZE_IUP_DELTAS: If set perform IUP delta optimization on the + * remaining gvar table's deltas. Since: 8.5.0 + * @HB_SUBSET_FLAGS_NO_BIDI_CLOSURE: If set do not pull mirrored versions of input + * codepoints into the subset. Since: 11.1.0 + * @HB_SUBSET_FLAGS_IFTB_REQUIREMENTS: If set enforce requirements on the output subset + * to allow it to be used with incremental font transfer IFTB patches. Primarily, + * this forces all outline data to use long (32 bit) offsets. Since: EXPERIMENTAL * * List of boolean properties that can be configured on the subset input. * @@ -90,6 +97,11 @@ typedef enum { /*< flags >*/ HB_SUBSET_FLAGS_GLYPH_NAMES = 0x00000080u, HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES = 0x00000100u, HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE = 0x00000200u, + HB_SUBSET_FLAGS_OPTIMIZE_IUP_DELTAS = 0x00000400u, + HB_SUBSET_FLAGS_NO_BIDI_CLOSURE = 0x00000800u, +#ifdef HB_EXPERIMENTAL_API + HB_SUBSET_FLAGS_IFTB_REQUIREMENTS = 0x00001000u, +#endif } hb_subset_flags_t; /** @@ -164,6 +176,10 @@ HB_EXTERN void hb_subset_input_set_flags (hb_subset_input_t *input, unsigned value); +HB_EXTERN hb_bool_t +hb_subset_input_pin_all_axes_to_default (hb_subset_input_t *input, + hb_face_t *face); + HB_EXTERN hb_bool_t hb_subset_input_pin_axis_to_default (hb_subset_input_t *input, hb_face_t *face, @@ -175,15 +191,34 @@ hb_subset_input_pin_axis_location (hb_subset_input_t *input, hb_tag_t axis_tag, float axis_value); -#ifdef HB_EXPERIMENTAL_API +HB_EXTERN hb_bool_t +hb_subset_input_get_axis_range (hb_subset_input_t *input, + hb_tag_t axis_tag, + float *axis_min_value, + float *axis_max_value, + float *axis_def_value); + HB_EXTERN hb_bool_t hb_subset_input_set_axis_range (hb_subset_input_t *input, hb_face_t *face, hb_tag_t axis_tag, float axis_min_value, float axis_max_value, - float *axis_def_value); + float axis_def_value); +HB_EXTERN hb_bool_t +hb_subset_axis_range_from_string (const char *str, int len, + float *axis_min_value, + float *axis_max_value, + float *axis_def_value); + +HB_EXTERN void +hb_subset_axis_range_to_string (hb_subset_input_t *input, + hb_tag_t axis_tag, + char *buf, + unsigned size); + +#ifdef HB_EXPERIMENTAL_API HB_EXTERN hb_bool_t hb_subset_input_override_name_table (hb_subset_input_t *input, hb_ot_name_id_t name_id, @@ -193,6 +228,22 @@ hb_subset_input_override_name_table (hb_subset_input_t *input, const char *name_str, int str_len); + +/* +* Raw outline data access +*/ + +HB_EXTERN hb_blob_t* +hb_subset_cff_get_charstring_data (hb_face_t* face, hb_codepoint_t glyph_index); + +HB_EXTERN hb_blob_t* +hb_subset_cff_get_charstrings_index (hb_face_t* face); + +HB_EXTERN hb_blob_t* +hb_subset_cff2_get_charstring_data (hb_face_t* face, hb_codepoint_t glyph_index); + +HB_EXTERN hb_blob_t* +hb_subset_cff2_get_charstrings_index (hb_face_t* face); #endif HB_EXTERN hb_face_t * diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ucd-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ucd-table.hh index 8d3807a80f0cd..8731a0bcf8d72 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ucd-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ucd-table.hh @@ -4,7 +4,7 @@ * * ./gen-ucd-table.py ucd.nounihan.grouped.xml * - * on file with this description: Unicode 15.1.0 + * on file with this description: Unicode 16.0.0 */ #ifndef HB_UCD_TABLE_HH @@ -13,7 +13,7 @@ #include "hb.hh" static const hb_script_t -_hb_ucd_sc_map[165] = +_hb_ucd_sc_map[172] = { HB_SCRIPT_COMMON, HB_SCRIPT_INHERITED, HB_SCRIPT_UNKNOWN, HB_SCRIPT_ARABIC, @@ -97,7 +97,10 @@ _hb_ucd_sc_map[165] = HB_SCRIPT_OLD_UYGHUR, HB_SCRIPT_TANGSA, HB_SCRIPT_TOTO, HB_SCRIPT_VITHKUQI, HB_SCRIPT_MATH, HB_SCRIPT_KAWI, - HB_SCRIPT_NAG_MUNDARI, + HB_SCRIPT_NAG_MUNDARI, HB_SCRIPT_GARAY, + HB_SCRIPT_GURUNG_KHEMA, HB_SCRIPT_KIRAT_RAI, + HB_SCRIPT_OL_ONAL, HB_SCRIPT_SUNUWAR, + HB_SCRIPT_TODHRI, HB_SCRIPT_TULU_TIGALARI, }; static const uint16_t _hb_ucd_dm1_p0_map[825] = @@ -868,7 +871,7 @@ _hb_ucd_dm2_u32_map[638] = HB_CODEPOINT_ENCODE3_11_7_14 (0x04E9u, 0x0308u, 0x04EBu), }; static const uint64_t -_hb_ucd_dm2_u64_map[388] = +_hb_ucd_dm2_u64_map[408] = { HB_CODEPOINT_ENCODE3 (0x05D0u, 0x05B7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D0u, 0x05B8u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D0u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D1u, 0x05BCu, 0x0000u), @@ -1051,13 +1054,23 @@ _hb_ucd_dm2_u64_map[388] = HB_CODEPOINT_ENCODE3 (0x30F0u, 0x3099u, 0x30F8u), HB_CODEPOINT_ENCODE3 (0x30F1u, 0x3099u, 0x30F9u), HB_CODEPOINT_ENCODE3 (0x30F2u, 0x3099u, 0x30FAu), HB_CODEPOINT_ENCODE3 (0x30FDu, 0x3099u, 0x30FEu), HB_CODEPOINT_ENCODE3 (0xFB49u, 0x05C1u, 0x0000u), HB_CODEPOINT_ENCODE3 (0xFB49u, 0x05C2u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x105D2u, 0x0307u, 0x105C9u), HB_CODEPOINT_ENCODE3 (0x105DAu, 0x0307u, 0x105E4u), HB_CODEPOINT_ENCODE3 (0x11099u, 0x110BAu, 0x1109Au),HB_CODEPOINT_ENCODE3 (0x1109Bu, 0x110BAu, 0x1109Cu), HB_CODEPOINT_ENCODE3 (0x110A5u, 0x110BAu, 0x110ABu),HB_CODEPOINT_ENCODE3 (0x11131u, 0x11127u, 0x1112Eu), HB_CODEPOINT_ENCODE3 (0x11132u, 0x11127u, 0x1112Fu),HB_CODEPOINT_ENCODE3 (0x11347u, 0x1133Eu, 0x1134Bu), - HB_CODEPOINT_ENCODE3 (0x11347u, 0x11357u, 0x1134Cu),HB_CODEPOINT_ENCODE3 (0x114B9u, 0x114B0u, 0x114BCu), - HB_CODEPOINT_ENCODE3 (0x114B9u, 0x114BAu, 0x114BBu),HB_CODEPOINT_ENCODE3 (0x114B9u, 0x114BDu, 0x114BEu), - HB_CODEPOINT_ENCODE3 (0x115B8u, 0x115AFu, 0x115BAu),HB_CODEPOINT_ENCODE3 (0x115B9u, 0x115AFu, 0x115BBu), - HB_CODEPOINT_ENCODE3 (0x11935u, 0x11930u, 0x11938u), HB_CODEPOINT_ENCODE3 (0x1D157u, 0x1D165u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x11347u, 0x11357u, 0x1134Cu),HB_CODEPOINT_ENCODE3 (0x11382u, 0x113C9u, 0x11383u), + HB_CODEPOINT_ENCODE3 (0x11384u, 0x113BBu, 0x11385u),HB_CODEPOINT_ENCODE3 (0x1138Bu, 0x113C2u, 0x1138Eu), + HB_CODEPOINT_ENCODE3 (0x11390u, 0x113C9u, 0x11391u),HB_CODEPOINT_ENCODE3 (0x113C2u, 0x113B8u, 0x113C7u), + HB_CODEPOINT_ENCODE3 (0x113C2u, 0x113C2u, 0x113C5u),HB_CODEPOINT_ENCODE3 (0x113C2u, 0x113C9u, 0x113C8u), + HB_CODEPOINT_ENCODE3 (0x114B9u, 0x114B0u, 0x114BCu),HB_CODEPOINT_ENCODE3 (0x114B9u, 0x114BAu, 0x114BBu), + HB_CODEPOINT_ENCODE3 (0x114B9u, 0x114BDu, 0x114BEu),HB_CODEPOINT_ENCODE3 (0x115B8u, 0x115AFu, 0x115BAu), + HB_CODEPOINT_ENCODE3 (0x115B9u, 0x115AFu, 0x115BBu),HB_CODEPOINT_ENCODE3 (0x11935u, 0x11930u, 0x11938u), + HB_CODEPOINT_ENCODE3 (0x1611Eu, 0x1611Eu, 0x16121u),HB_CODEPOINT_ENCODE3 (0x1611Eu, 0x1611Fu, 0x16123u), + HB_CODEPOINT_ENCODE3 (0x1611Eu, 0x16120u, 0x16125u),HB_CODEPOINT_ENCODE3 (0x1611Eu, 0x16129u, 0x16122u), + HB_CODEPOINT_ENCODE3 (0x16121u, 0x1611Fu, 0x16126u),HB_CODEPOINT_ENCODE3 (0x16121u, 0x16120u, 0x16128u), + HB_CODEPOINT_ENCODE3 (0x16122u, 0x1611Fu, 0x16127u),HB_CODEPOINT_ENCODE3 (0x16129u, 0x1611Fu, 0x16124u), + HB_CODEPOINT_ENCODE3 (0x16D63u, 0x16D67u, 0x16D69u),HB_CODEPOINT_ENCODE3 (0x16D67u, 0x16D67u, 0x16D68u), + HB_CODEPOINT_ENCODE3 (0x16D69u, 0x16D67u, 0x16D6Au), HB_CODEPOINT_ENCODE3 (0x1D157u, 0x1D165u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D158u, 0x1D165u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D16Eu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D16Fu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D170u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D171u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D172u, 0x0000u), @@ -1069,90 +1082,59 @@ _hb_ucd_dm2_u64_map[388] = #ifndef HB_OPTIMIZE_SIZE static const uint8_t -_hb_ucd_u8[17884] = +_hb_ucd_u8[17612] = { - 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 9, 10, 7, 7, 7, 7, 11, 12, 13, 13, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 22, 22, 22, 22, 24, 7, 7, - 25, 26, 22, 22, 22, 27, 28, 29, 22, 30, 31, 32, 33, 34, 35, 36, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 37, 7, 38, 39, 7, 40, 7, 7, 7, 41, 22, 42, - 7, 7, 43, 7, 44, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 45, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 46, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 47, + 0, 1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 5, 5, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 5, 17, 15, 18, 19, 20, 21, 22, 23, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 24, 25, 26, 5, 27, 28, + 5, 29, 30, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 31, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 32, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 33, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 34, 35, 36, 37, 38, 39, 34, 34, 34, 40, 41, 42, 43, - 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, - 60, 61, 62, 63, 64, 64, 65, 66, 67, 68, 69, 70, 71, 69, 72, 73, - 69, 69, 64, 74, 64, 64, 75, 76, 77, 78, 79, 80, 81, 82, 69, 83, - 84, 85, 86, 87, 88, 89, 69, 69, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 90, 34, 34, 34, 34, - 91, 34, 34, 34, 34, 34, 34, 34, 34, 92, 34, 34, 93, 94, 95, 96, - 97, 98, 99,100,101,102,103,104, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,105, - 106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106, - 107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107, - 107,107, 34, 34,108,109,110,111, 34, 34,112,113,114,115,116,117, - 118,119,120,121,122,123,124,125,126,127,128,129, 34, 34,130,131, - 132,133,134,135,136,137,138,139,140,141,142,122,143,144,145,146, - 147,148,149,150,151,152,153,122,154,155,122,156,157,158,159,122, - 160,161,162,163,164,165,166,122,167,168,169,170,122,171,172,173, - 34, 34, 34, 34, 34, 34, 34,174,175, 34,176,122,122,122,122,122, - 122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,177, - 34, 34, 34, 34, 34, 34, 34, 34,178,122,122,122,122,122,122,122, - 122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122, - 122,122,122,122,122,122,122,122, 34, 34, 34, 34,179,122,122,122, - 34, 34, 34, 34,180,181,182,183,122,122,122,122,184,185,186,187, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,188, - 34, 34, 34, 34, 34, 34, 34, 34, 34,189,190,122,122,122,122,122, - 122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,191, - 34, 34,192, 34, 34,193,122,122,122,122,122,122,122,122,122,122, - 122,122,122,122,122,122,122,122,194,195,122,122,122,122,122,122, - 122,122,122,122,122,122,122,122,122,122,122,122,122,122,196,197, - 69,198,199,200,201,202,203,122,204,205,206,207,208,209,210,211, - 69, 69, 69, 69,212,213,122,122,122,122,122,122,122,122,214,122, - 215,216,217,122,122,218,122,122,122,219,122,122,122,122,122,220, - 34,221,222,122,122,122,122,122,223,224,225,122,226,227,122,122, - 228,229,230,231,232,122, 69,233, 69, 69, 69, 69, 69,234,235,236, - 237,238, 69, 69,239,240, 69,241,122,122,122,122,122,122,122,122, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,242, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,243, 34, - 244, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,245, 34, 34, - 34, 34, 34, 34, 34, 34, 34,246, 34, 34, 34, 34,247,122,122,122, - 34, 34, 34, 34,248,122,122,122,122,122,122,122,122,122,122,122, - 34, 34, 34, 34, 34, 34,249, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34,250,122,122,122,122,122,122,122,122, - 251,122,252,253,122,122,122,122,122,122,122,122,122,122,122,122, - 107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,254, - 107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,255, + 16, 17, 18, 19, 20, 17, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 33, 41, 42, 43, 44, 45, + 46, 47, 48, 39, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 49, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 50, 17, 17, 17, 51, 17, 52, 53, 54, 55, 56, 57, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 58, 59, 59, 59, 59, 59, 59, 59, 59, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 17, 61, 62, 17, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 17, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 17, 17, 17, 97, 98, 99,100,100,100,100,100,100,100,100,100,101, + 17, 17, 17, 17,102, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17,103, 17, 17,104,100,100,100,100,100,100,100,100,100, + 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, + 100,105,100,100,100,100,100,100, 17, 17,106,107,100,108,109,110, + 17, 17, 17, 17, 17, 17, 17,111, 17, 17, 17, 17,112,113,100,100, + 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,114, + 17,115,116,100,100,100,100,100,100,100,100,100,117,100,100,100, + 100,100,100,100,100,100,100,100,100,100,100,100,118, 39,119,120, + 121,122,123,124,125,126,127,128, 39, 39,129,100,100,100,100,130, + 131,132,133,100,134,135,100,136,137,138,100,100,139,140,141,100, + 142,143,144,145, 39, 39,146,147,148, 39,149,150,100,100,100,100, + 17, 17, 17, 17, 17, 17,151, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17,152,153, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,154, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,155, 17, 17,156,100, + 100,100,100,100,100,100,100,100, 17, 17,157,100,100,100,100,100, + 17, 17, 17,158, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17,159,100,100,100,100,100,100,100,100,100,100,100,100, + 160,161,100,100,100,100,100,100,100,100,100,100,100,100,100,100, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,162, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,163, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 4, 5, 6, 2, 7, 7, 7, 7, 7, 2, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, @@ -1189,7 +1171,7 @@ _hb_ucd_u8[17884] = 43, 43, 40, 21, 2, 81, 57, 20, 36, 36, 36, 43, 43, 75, 43, 43, 43, 43, 75, 43, 75, 43, 43, 44, 2, 2, 2, 2, 2, 2, 2, 64, 36, 36, 36, 36, 70, 43, 44, 64, 36, 36, 36, 36, 36, 61, 44, 44, - 36, 36, 36, 36, 82, 36, 36, 61, 65, 44, 44, 44, 43, 43, 43, 43, + 36, 36, 36, 36, 82, 36, 36, 61, 65, 44, 44, 57, 43, 43, 43, 43, 36, 36, 36, 36, 83, 43, 43, 43, 43, 84, 43, 43, 43, 43, 43, 43, 43, 85, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 85, 71, 86, 87, 43, 43, 43, 85, 86, 87, 86, 70, 43, 43, 43, 36, 36, 36, 36, @@ -1262,13 +1244,13 @@ _hb_ucd_u8[17884] = 85, 85, 87, 43, 43, 43, 85, 86, 86, 87, 43, 43, 43, 43, 80, 57, 2, 2, 2, 88, 2, 2, 2, 44, 43, 43, 43, 43, 43, 43, 43,109, 43, 43, 43, 43, 43, 43, 43, 80, 43, 43, 98, 36, 36, 36, 36, 36, - 36, 36, 85, 43, 43, 85, 85, 86, 86, 85, 98, 36, 36, 36, 61, 44, - 97, 67, 67, 67, 67, 50, 43, 43, 43, 43, 67, 67, 67, 67, 21, 64, + 36, 36, 85, 43, 43, 85, 85, 86, 86, 85, 98, 36, 36, 36, 61, 2, + 97, 67, 67, 67, 67, 50, 43, 43, 43, 43, 67, 67, 67, 67, 21, 2, 43, 98, 36, 36, 36, 36, 36, 36, 94, 43, 43, 86, 43, 87, 43, 36, 36, 36, 36, 85, 43, 86, 87, 87, 43, 86, 44, 44, 44, 44, 2, 2, 36, 36, 86, 86, 86, 86, 43, 43, 43, 43, 86, 43, 44, 93, 2, 2, 7, 7, 7, 7, 7, 44, 62, 36, 36, 36, 36, 36, 40, 40, 40, 2, - 16, 16, 16, 16,110, 44, 44, 44, 11, 11, 11, 11, 11, 47, 48, 11, + 16, 16, 16, 16, 34,110, 44, 44, 11, 11, 11, 11, 11, 47, 48, 11, 2, 2, 2, 2, 44, 44, 44, 44, 43, 60, 43, 43, 43, 43, 43, 43, 85, 43, 43, 43, 71, 36, 70, 36, 36, 36, 71, 94, 43, 61, 44, 44, 16, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 45, 16, 16, @@ -1296,33 +1278,33 @@ _hb_ucd_u8[17884] = 67, 67, 67, 67, 4, 4, 67, 67, 8, 67, 67, 67,145,146, 67, 67, 67, 67, 67, 67, 67, 67,144, 67, 67, 67, 67, 67, 67, 26, 8, 8, 8, 8, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 8, 8, - 8, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 92, 44, 44, 44, 44, - 67, 67, 67, 67, 67, 92, 44, 44, 27, 27, 27, 27, 27, 27, 67, 67, - 67, 67, 67, 67, 67, 27, 27, 27, 67, 67, 67, 26, 67, 67, 67, 67, - 26, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 8, 8, 8, 8, - 67, 67, 67, 67, 67, 67, 67, 26, 67, 67, 67, 67, 4, 4, 4, 4, - 4, 4, 4, 27, 27, 27, 27, 27, 27, 27, 67, 67, 67, 67, 67, 67, - 8, 8,129,147, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4, - 8,129,148,148,148,148,148,148,148,148,148,148,147, 8, 8, 8, - 8, 8, 8, 8, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8, - 8, 8,144, 26, 8, 8,144, 67, 67, 67, 44, 67, 67, 67, 67, 67, - 67, 67, 67, 55, 67, 67, 67, 67, 32, 11, 32, 34, 34, 34, 34, 11, - 32, 32, 34, 16, 16, 16, 40, 11, 32, 32,140, 67, 67,138, 34,149, - 43, 32, 44, 44, 93, 2, 99, 2, 16, 16, 16,150, 44, 44,150, 44, - 36, 36, 36, 36, 44, 44, 44, 52, 64, 44, 44, 44, 44, 44, 44, 57, - 36, 36, 36, 61, 44, 44, 44, 44, 36, 36, 36, 61, 36, 36, 36, 61, - 2,121,121, 2,125,126,121, 2, 2, 2, 2, 6, 2,108,121, 2, - 121, 4, 4, 4, 4, 2, 2, 88, 2, 2, 2, 2, 2,120, 2, 2, - 108,151, 2, 2, 2, 2, 2, 2, 67, 2,152,148,148,148,153, 44, - 67, 67, 67, 67, 67, 55, 67, 67, 67, 67, 44, 44, 44, 44, 44, 44, - 67, 67, 67, 44, 44, 44, 44, 44, 1, 2,154,155, 4, 4, 4, 4, - 4, 67, 4, 4, 4, 4,156,157,158,105,105,105,105, 43, 43, 86, - 159, 40, 40, 67,105,160, 63, 67, 36, 36, 36, 61, 57,161,162, 69, - 36, 36, 36, 36, 36, 63, 40, 69, 44, 44, 62, 36, 36, 36, 36, 36, - 67, 27, 27, 67, 67, 67, 67, 67, 67, 67, 44, 44, 44, 44, 44, 55, - 67, 67, 67, 67, 67, 67, 67, 92, 27, 27, 27, 27, 27, 67, 67, 67, - 67, 67, 67, 67, 27, 27, 27, 27,163, 27, 27, 27, 27, 27, 27, 27, - 36, 36, 83, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,164, 2, + 8, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 92, 44, 44, + 27, 27, 27, 27, 27, 27, 67, 67, 67, 67, 67, 67, 67, 27, 27, 27, + 67, 67, 67, 26, 67, 67, 67, 67, 26, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 8, 8, 8, 8, 67, 67, 67, 67, 67, 67, 67, 26, + 67, 67, 67, 67, 4, 4, 4, 4, 4, 4, 4, 27, 27, 27, 27, 27, + 27, 27, 67, 67, 67, 67, 67, 67, 8, 8,129,147, 8, 8, 8, 8, + 8, 8, 8, 4, 4, 4, 4, 4, 8,129,148,148,148,148,148,148, + 148,148,148,148,147, 8, 8, 8, 8, 8, 8, 8, 4, 4, 8, 8, + 8, 8, 8, 8, 8, 8, 4, 8, 8, 8,144, 26, 8, 8,144, 67, + 67, 67, 44, 67, 67, 67, 67, 67, 67, 67, 67, 55, 67, 67, 67, 67, + 32, 11, 32, 34, 34, 34, 34, 11, 32, 32, 34, 16, 16, 16, 40, 11, + 32, 32,140, 67, 67,138, 34,149, 43, 32, 44, 44, 93, 2, 99, 2, + 16, 16, 16,150, 44, 44,150, 44, 36, 36, 36, 36, 44, 44, 44, 52, + 64, 44, 44, 44, 44, 44, 44, 57, 36, 36, 36, 61, 44, 44, 44, 44, + 36, 36, 36, 61, 36, 36, 36, 61, 2,121,121, 2,125,126,121, 2, + 2, 2, 2, 6, 2,108,121, 2,121, 4, 4, 4, 4, 2, 2, 88, + 2, 2, 2, 2, 2,120, 2, 2,108,151, 2, 2, 2, 2, 2, 2, + 67, 2,152,148,148,148,153, 44, 67, 67, 67, 67, 67, 55, 67, 67, + 67, 67, 44, 44, 44, 44, 44, 44, 67, 67, 67, 44, 44, 44, 44, 44, + 1, 2,154,155, 4, 4, 4, 4, 4, 67, 4, 4, 4, 4,156,157, + 158,105,105,105,105, 43, 43, 86,159, 40, 40, 67,105,160, 63, 67, + 36, 36, 36, 61, 57,161,162, 69, 36, 36, 36, 36, 36, 63, 40, 69, + 44, 44, 62, 36, 36, 36, 36, 36, 67, 27, 27, 67, 67, 67, 67, 67, + 67, 67, 67, 44, 44, 44, 44, 55, 67, 67, 67, 67, 67, 67, 67, 92, + 27, 27, 27, 27, 27, 67, 67, 67, 67, 67, 67, 67, 27, 27, 27, 27, + 163, 27, 27, 27, 27, 27, 27, 27, 36, 36, 83, 36, 36, 36, 36, 36, + 67, 67, 67, 92, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36,164, 2, 7, 7, 7, 7, 7, 36, 44, 44, 32, 32, 32, 32, 32, 32, 32, 70, 51,165, 43, 43, 43, 43, 43, 88, 32, 32, 32, 32, 32, 32, 40, 43, 36, 36, 36,105,105,105,105,105, 43, 2, 2, 2, 44, 44, 44, 44, @@ -1330,7 +1312,7 @@ _hb_ucd_u8[17884] = 16, 32, 32, 32, 32, 32, 32, 32, 45, 16, 16, 16, 34, 34, 34, 32, 32, 32, 32, 32, 42,166, 34, 35, 32, 32, 16, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 11, 11, 32, 11, 11, 32, 32, 32, 32, 32, 32, - 32, 32, 11, 11, 34,110, 44, 44, 32,150,150, 32, 32, 44, 44, 44, + 32, 32, 11, 11, 34, 34, 32, 44, 32,150,150, 32, 32, 32, 47, 44, 44, 40,167, 35, 40, 35, 36, 36, 36, 71, 36, 71, 36, 70, 36, 36, 36, 94, 87, 85, 67, 67, 80, 44, 27, 27, 27, 67,168, 44, 44, 44, 36, 36, 2, 2, 44, 44, 44, 44, 86, 36, 36, 36, 36, 36, 36, 36, @@ -1391,8 +1373,10 @@ _hb_ucd_u8[17884] = 36, 61, 44, 44, 27, 27, 27, 27, 36, 44, 44, 44, 93, 2, 64, 44, 44, 44, 44, 44,179, 27, 27, 27, 11, 47, 44, 44, 44, 44, 44, 44, 16,110, 44, 44, 44, 27, 27, 27, 36, 36, 43, 43, 44, 44, 44, 44, - 27, 27, 27, 27, 27, 27, 27,100, 36, 36, 36, 36, 36, 57,184, 44, - 36, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 57, 43, + 7, 7, 7, 7, 7, 36, 36, 69, 11, 11, 11, 44, 57, 43, 43,159, + 16, 16, 16, 44, 44, 44, 44, 8, 27, 27, 27, 27, 27, 27, 27,100, + 36, 36, 36, 36, 36, 57,184, 44, 36, 44, 44, 44, 44, 44, 44, 44, + 44, 36, 61, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 43, 43, 27, 27, 27, 95, 44, 44, 44, 44,180, 27, 30, 2, 2, 44, 44, 44, 36, 43, 43, 2, 2, 44, 44, 44, 36, 36,183, 27, 27, 27, 44, 44, 87, 98, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, @@ -1410,14 +1394,18 @@ _hb_ucd_u8[17884] = 86, 87, 43, 43, 43, 80, 44, 44, 43, 86, 62, 36, 36, 36, 61, 62, 61, 36, 62, 36, 36, 57, 71, 86, 85, 86, 90, 89, 90, 89, 86, 44, 61, 44, 44, 89, 44, 44, 62, 36, 36, 86, 44, 43, 43, 43, 80, 44, - 43, 43, 80, 44, 44, 44, 44, 44, 36, 36, 94, 86, 43, 43, 43, 43, - 86, 43, 85, 71, 36, 63, 2, 2, 7, 7, 7, 7, 7, 2, 93, 71, - 86, 87, 43, 43, 85, 85, 86, 87, 85, 43, 36, 72, 44, 44, 44, 44, - 36, 36, 36, 36, 36, 36, 36, 94, 86, 43, 43, 44, 86, 86, 43, 87, - 60, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 43, 44, - 86, 87, 43, 43, 43, 85, 87, 87, 60, 2, 61, 44, 44, 44, 44, 44, - 2, 2, 2, 2, 2, 2, 64, 44, 36, 36, 36, 36, 36, 70, 87, 86, - 43, 43, 43, 87, 63, 44, 44, 44, 86, 43, 43, 87, 43, 43, 44, 44, + 43, 43, 80, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 62, 44, 61, + 36, 36, 36, 62, 86, 87, 43, 43, 80, 90, 89, 89, 86, 90, 86, 85, + 71, 71, 2, 93, 64, 44, 44, 44, 57, 80, 44, 44, 44, 44, 44, 44, + 36, 36, 94, 86, 43, 43, 43, 43, 86, 43, 85, 71, 36, 63, 2, 2, + 7, 7, 7, 7, 7, 2, 93, 71, 86, 87, 43, 43, 85, 85, 86, 87, + 85, 43, 36, 72, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 36, 94, + 86, 43, 43, 44, 86, 86, 43, 87, 60, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 36, 36, 43, 44, 86, 87, 43, 43, 43, 85, 87, 87, + 60, 2, 61, 44, 44, 44, 44, 44, 2, 2, 2, 2, 2, 2, 64, 44, + 36, 36, 36, 36, 36, 70, 87, 86, 43, 43, 43, 87, 63, 44, 44, 44, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 44, 44, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 61, 57, 87, 86, 43, 43, 87, 43, 43, 44, 44, 7, 7, 7, 7, 7, 27, 2, 97, 43, 43, 43, 43, 87, 60, 44, 44, 27,100, 44, 44, 44, 44, 44, 62, 36, 36, 36, 61, 62, 44, 36, 36, 36, 36, 62, 61, 36, 36, 36, 36, 86, 86, 86, 89, 90, 57, 85, 71, @@ -1427,49 +1415,52 @@ _hb_ucd_u8[17884] = 2, 2, 2, 59, 44, 44, 44, 44, 70, 43, 43, 85, 87, 43, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 85, 43, 2, 72, 2, 2, 64, 44, 44, 44, 44, 44, 44, 2, 2, 2, 2, 2, 44, 44, 44, - 43, 43, 43, 80, 43, 43, 43, 87, 63, 2, 2, 44, 44, 44, 44, 44, - 2, 36, 36, 36, 36, 36, 36, 36, 44, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 89, 43, 43, 43, 85, 43, 87, 80, 44, 44, 44, 44, - 36, 36, 36, 61, 36, 62, 36, 36, 70, 43, 43, 80, 44, 80, 43, 57, - 43, 43, 43, 70, 44, 44, 44, 44, 36, 36, 36, 62, 61, 36, 36, 36, - 36, 36, 36, 36, 36, 86, 86, 90, 43, 89, 87, 87, 61, 44, 44, 44, - 36, 70, 85,107, 64, 44, 44, 44, 43, 94, 36, 36, 36, 36, 36, 36, - 36, 36, 86, 43, 43, 80, 44, 86, 85, 60, 2, 2, 2, 2, 2, 2, + 63, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 80, 43, 43, 43, 87, + 63, 2, 2, 44, 44, 44, 44, 44, 2, 36, 36, 36, 36, 36, 36, 36, + 44, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 89, 43, 43, 43, + 85, 43, 87, 80, 44, 44, 44, 44, 36, 36, 36, 61, 36, 62, 36, 36, + 70, 43, 43, 80, 44, 80, 43, 57, 43, 43, 43, 70, 44, 44, 44, 44, + 36, 36, 36, 62, 61, 36, 36, 36, 36, 36, 36, 36, 36, 86, 86, 90, + 43, 89, 87, 87, 61, 44, 44, 44, 36, 70, 85,107, 64, 44, 44, 44, + 43, 94, 36, 36, 36, 36, 36, 36, 36, 36, 86, 43, 43, 80, 44, 86, + 85, 60, 2, 2, 2, 2, 2, 2, 7, 7, 7, 7, 7, 80, 44, 44, 27, 27, 91, 67, 67, 67, 56, 20,168, 67, 67, 67, 67, 67, 67, 67, 67, 44, 44, 44, 44, 44, 44, 93,105,105,105,105,105,105,105,181, 2, 2, 64, 44, 44, 44, 44, 44, 63, 64, 44, 44, 44, 44, 44, 44, 65, 65, 65, 65, 65, 65, 65, 65, 71, 36, 36, 70, 43, 43, 43, 43, - 43, 43, 43, 44, 44, 44, 44, 44, 43, 43, 60, 44, 44, 44, 44, 44, + 43, 43, 43, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 36, 43, + 43, 43, 43, 43, 43, 86, 87, 43, 43, 43, 60, 44, 44, 44, 44, 44, 43, 43, 43, 60, 2, 2, 67, 67, 40, 40, 97, 44, 44, 44, 44, 44, 7, 7, 7, 7, 7,179, 27, 27, 27, 62, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 44, 44, 62, 36, 27, 27, 27, 30, 2, 64, 44, 44, + 36, 36, 36, 36, 44, 44, 62, 36, 40, 69, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 83,164, 2, 27, 27, 27, 30, 2, 64, 44, 44, 36, 36, 36, 36, 36, 61, 44, 57, 94, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 44, 44, 44, 57, 43, 74, 40, 40, 40, 40, 40, 40, 40, 88, 80, 44, 44, 44, 44, 44, - 86, 44, 44, 44, 44, 44, 44, 44, 40, 40, 52, 40, 40, 40, 52, 81, - 36, 61, 44, 44, 44, 44, 44, 44, 44, 61, 44, 44, 44, 44, 44, 44, - 36, 61, 62, 44, 44, 44, 44, 44, 44, 44, 36, 36, 44, 44, 44, 44, - 36, 36, 36, 36, 36, 44, 50, 60, 65, 65, 44, 44, 44, 44, 44, 44, - 43, 43, 43, 43, 43, 43, 43, 44, 43, 43, 43, 80, 44, 44, 44, 44, - 67, 67, 67, 92, 55, 67, 67, 67, 67, 67,186, 87, 43, 67,186, 86, - 86,187, 65, 65, 65, 84, 43, 43, 43, 76, 50, 43, 43, 43, 67, 67, - 67, 67, 67, 67, 67, 43, 43, 67, 67, 43, 76, 44, 44, 44, 44, 44, - 27, 27, 44, 44, 44, 44, 44, 44, 11, 11, 11, 11, 11, 16, 16, 16, - 16, 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 16, - 16, 16,110, 16, 16, 16, 16, 16, 11, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 47, 11, 44, 47, 48, 47, 48, 11, 47, 11, - 11, 11, 11, 16, 16,150,150, 16, 16, 16,150, 16, 16, 16, 16, 16, - 16, 16, 11, 48, 11, 47, 48, 11, 11, 11, 47, 11, 11, 11, 47, 16, - 16, 16, 16, 16, 11, 48, 11, 47, 11, 11, 47, 47, 44, 11, 11, 11, - 47, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 11, 11, - 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 44, 11, 11, 11, 11, - 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, - 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, - 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, - 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 32, 44, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 43, 43, 43, 76, 67, 50, 43, 43, + 86, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 62, + 40, 40, 52, 40, 40, 40, 52, 81, 36, 61, 44, 44, 44, 44, 44, 44, + 44, 61, 44, 44, 44, 44, 44, 44, 36, 61, 62, 44, 44, 44, 44, 44, + 44, 44, 36, 36, 44, 44, 44, 44, 36, 36, 36, 36, 36, 44, 50, 60, + 65, 65, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43, 43, 44, + 43, 43, 43, 80, 44, 44, 44, 44, 67, 67, 67, 92, 55, 67, 67, 67, + 67, 67,186, 87, 43, 67,186, 86, 86,187, 65, 65, 65, 84, 43, 43, + 43, 76, 50, 43, 43, 43, 67, 67, 67, 67, 67, 67, 67, 43, 43, 67, + 67, 43, 76, 44, 44, 44, 44, 44, 27, 27, 44, 44, 44, 44, 44, 44, + 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 16, 16, 16,110, 16, 16, 16, 16, 16, + 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 47, 11, + 44, 47, 48, 47, 48, 11, 47, 11, 11, 11, 11, 16, 16,150,150, 16, + 16, 16,150, 16, 16, 16, 16, 16, 16, 16, 11, 48, 11, 47, 48, 11, + 11, 11, 47, 11, 11, 11, 47, 16, 16, 16, 16, 16, 11, 48, 11, 47, + 11, 11, 47, 47, 44, 11, 11, 11, 47, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, + 16, 16, 16, 44, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, + 11, 11, 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, + 16, 16, 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, + 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, + 16, 33, 16, 16, 16, 32, 44, 7, 43, 43, 43, 76, 67, 50, 43, 43, 43, 43, 43, 43, 43, 43, 76, 67, 67, 67, 50, 67, 67, 67, 67, 67, 67, 67, 76, 21, 2, 2, 44, 44, 44, 44, 44, 44, 44, 57, 43, 43, 16, 16, 16, 16, 16, 39, 16, 16, 16, 16, 16, 16, 16, 16, 16,110, @@ -1479,22 +1470,23 @@ _hb_ucd_u8[17884] = 43, 43, 43, 74, 40, 40, 40, 44, 7, 7, 7, 7, 7, 44, 44, 77, 36, 36, 36, 36, 36, 36, 36, 80, 36, 36, 36, 36, 36, 36, 43, 43, 7, 7, 7, 7, 7, 44, 44, 96, 36, 36, 36, 36, 36, 83, 43, 43, - 36, 36, 36, 61, 36, 36, 62, 61, 36, 36, 61,179, 27, 27, 27, 27, - 16, 16, 43, 43, 43, 74, 44, 44, 27, 27, 27, 27, 27, 27,163, 27, - 188, 27,100, 44, 44, 44, 44, 44, 27, 27, 27, 27, 27, 27, 27,163, - 27, 27, 27, 27, 27, 27, 27, 44, 36, 36, 62, 36, 36, 36, 36, 36, - 62, 61, 61, 62, 62, 36, 36, 36, 36, 61, 36, 36, 62, 62, 44, 44, - 44, 61, 44, 62, 62, 62, 62, 36, 62, 61, 61, 62, 62, 62, 62, 62, - 62, 61, 61, 62, 36, 61, 36, 36, 36, 61, 36, 36, 62, 36, 61, 61, - 36, 36, 36, 36, 36, 62, 36, 36, 62, 36, 62, 36, 36, 62, 36, 36, - 8, 44, 44, 44, 44, 44, 44, 44, 67, 67, 67, 67, 67, 67, 44, 44, - 55, 67, 67, 67, 67, 67, 67, 67, 27, 27, 27, 27, 27, 27, 91, 67, - 67, 67, 67, 67, 67, 67, 67, 44, 44, 44, 44, 67, 67, 67, 67, 67, - 67, 92, 44, 44, 44, 44, 44, 44, 67, 67, 67, 67, 92, 44, 44, 44, - 67, 44, 44, 44, 44, 44, 44, 44, 67, 67, 67, 67, 67, 25, 41, 41, - 67, 67, 67, 67, 44, 44, 67, 67, 67, 67, 67, 92, 44, 55, 67, 67, - 67, 67, 67, 67, 44, 44, 44, 44, 67, 67, 67, 67, 67, 67, 67, 55, - 67, 67, 67, 44, 44, 44, 44, 67, 67, 92, 67, 67, 67, 67, 67, 67, + 188, 7, 7, 7, 7,189, 44, 93, 36, 36, 36, 61, 36, 36, 62, 61, + 36, 36, 61,179, 27, 27, 27, 27, 16, 16, 43, 43, 43, 74, 44, 44, + 27, 27, 27, 27, 27, 27,163, 27,190, 27,100, 44, 44, 44, 44, 44, + 27, 27, 27, 27, 27, 27, 27,163, 27, 27, 27, 27, 27, 27, 27, 44, + 36, 36, 62, 36, 36, 36, 36, 36, 62, 61, 61, 62, 62, 36, 36, 36, + 36, 61, 36, 36, 62, 62, 44, 44, 44, 61, 44, 62, 62, 62, 62, 36, + 62, 61, 61, 62, 62, 62, 62, 62, 62, 61, 61, 62, 36, 61, 36, 36, + 36, 61, 36, 36, 62, 36, 61, 61, 36, 36, 36, 36, 36, 62, 36, 36, + 62, 36, 62, 36, 36, 62, 36, 36, 8, 44, 44, 44, 44, 44, 44, 44, + 67, 67, 67, 67, 67, 67, 44, 44, 55, 67, 67, 67, 67, 67, 67, 67, + 27, 27, 27, 27, 27, 27, 91, 67, 67, 67, 67, 67, 67, 67, 67, 44, + 44, 44, 44, 67, 67, 67, 67, 67, 67, 92, 44, 44, 44, 44, 44, 44, + 67, 67, 67, 67, 92, 44, 44, 44, 67, 44, 44, 44, 44, 44, 44, 44, + 67, 67, 67, 67, 67, 25, 41, 41, 67, 67, 67, 67, 44, 44, 67, 67, + 67, 67, 67, 92, 44, 55, 67, 67, 67, 67, 67, 67, 44, 44, 44, 44, + 67, 67, 67, 67, 67, 44, 44, 55, 67, 67, 67, 92, 44, 44, 44, 67, + 67, 67, 67, 67, 67, 67, 92, 55, 67, 92, 67, 67, 67, 67, 67, 67, 79, 44, 44, 44, 44, 44, 44, 44,171,171,171,171,171,171,171, 44, 171,171,171,171,171,171,171, 0, 0, 0, 29, 21, 21, 21, 23, 21, 22, 18, 21, 25, 21, 17, 13, 13, 25, 25, 25, 21, 21, 9, 9, 9, @@ -1520,366 +1512,350 @@ _hb_ucd_u8[17884] = 6, 21, 11, 21, 24, 9, 6, 9, 23, 26, 6, 10, 4, 4, 3, 3, 7, 25, 17, 16, 16, 22, 16, 16, 25, 17, 25, 2, 25, 24, 2, 15, 12, 15, 14, 2, 21, 14, 7, 15, 12, 17, 21, 1, 26, 10, 10, 1, - 23, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 10, 11, 12, - 13, 0, 14, 0, 0, 0, 0, 0, 15, 0, 16, 0, 0, 0, 0, 0, + 7, 13, 13, 2, 23, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 0, 10, 11, 12, 13, 0, 14, 0, 0, 0, 0, 0, 15, 0, 16, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, - 0, 21, 22, 23, 0, 0, 0, 24, 25, 26, 27, 28, 29, 30, 31, 32, - 33, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 20, 0, 21, 22, 23, 0, 0, 0, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 35, 0, 36, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 36, 0, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 39, 0, 0, 0, 0, - 0, 0, 40, 41, 42, 0, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 3, 0, 0, 0, 4, 5, - 6, 7, 0, 8, 9, 10, 0, 11, 12, 13, 14, 15, 16, 17, 16, 18, - 16, 19, 16, 19, 16, 19, 0, 19, 16, 20, 16, 19, 21, 19, 0, 22, - 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 32, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 34, 0, 0, 35, - 0, 0, 36, 0, 37, 0, 0, 0, 38, 39, 40, 41, 42, 43, 44, 45, - 46, 0, 0, 47, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, - 0, 0, 0, 50, 0, 51, 0, 52, 53, 0, 54, 0, 0, 0, 0, 0, - 0, 55, 56, 57, 0, 0, 0, 0, 58, 0, 0, 59, 60, 61, 62, 63, - 0, 0, 64, 65, 0, 0, 0, 66, 0, 0, 0, 0, 67, 0, 0, 0, - 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, - 0, 0, 0, 70, 0, 71, 0, 0, 72, 0, 0, 73, 0, 0, 0, 0, - 0, 0, 0, 0, 74, 0, 0, 0, 0, 0, 75, 76, 0, 77, 78, 0, - 0, 79, 80, 0, 81, 62, 0, 82, 83, 0, 0, 84, 85, 86, 0, 0, - 0, 87, 0, 88, 0, 0, 51, 89, 51, 0, 90, 0, 91, 0, 0, 0, - 80, 0, 0, 0, 92, 93, 0, 94, 95, 96, 97, 0, 0, 0, 0, 0, - 51, 0, 0, 0, 0, 98, 99, 0, 0, 0, 0, 0, 0,100, 0, 0, - 0, 0, 0,101,102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,103, - 0, 0,104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,105,106, 0, - 0,107, 0, 0, 0, 0, 0, 0,108, 0,109, 0,102, 0, 0, 0, - 0, 0,110,111, 0, 0, 0, 0, 0, 0, 0,112, 0, 0, 0, 0, - 0, 0, 0,113, 0,114, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, - 5, 6, 7, 0, 8, 0, 0, 0, 0, 9, 10, 11, 12, 0, 0, 0, - 0, 13, 0, 0, 14, 15, 0, 16, 0, 17, 18, 0, 0, 19, 0, 20, - 21, 0, 0, 0, 0, 0, 22, 23, 0, 24, 25, 0, 0, 26, 0, 0, - 0, 27, 0, 0, 28, 29, 30, 31, 0, 0, 0, 32, 33, 34, 0, 0, - 33, 0, 0, 35, 33, 0, 0, 0, 33, 36, 0, 0, 0, 0, 0, 37, - 38, 0, 0, 0, 0, 0, 0, 39, 40, 0, 0, 0, 0, 0, 0, 41, - 42, 0, 0, 0, 0, 43, 0, 44, 0, 0, 0, 45, 46, 0, 0, 0, - 47, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 50, 0, 0, - 0, 51, 0, 52, 0, 53, 0, 0, 0, 0, 54, 0, 0, 0, 0, 55, - 0, 56, 0, 0, 0, 0, 57, 58, 0, 0, 0, 59, 60, 0, 0, 0, - 0, 0, 0, 61, 52, 0, 62, 63, 0, 0, 64, 0, 0, 0, 65, 66, - 0, 0, 0, 67, 0, 68, 69, 70, 71, 72, 1, 73, 0, 74, 75, 76, - 0, 0, 77, 78, 0, 0, 0, 79, 0, 0, 1, 1, 0, 0, 80, 0, - 0, 81, 0, 0, 0, 0, 77, 82, 0, 83, 0, 0, 0, 0, 0, 78, - 84, 0, 85, 0, 52, 0, 1, 78, 0, 0, 86, 0, 0, 87, 0, 0, - 0, 0, 0, 88, 57, 0, 0, 0, 0, 0, 0, 89, 90, 0, 0, 84, - 0, 0, 33, 0, 0, 91, 0, 0, 0, 0, 92, 0, 0, 0, 0, 49, - 0, 0, 93, 0, 0, 0, 0, 94, 95, 0, 0, 96, 0, 0, 97, 0, - 0, 0, 98, 0, 0, 0, 99, 0, 0, 0, 0,100,101, 93, 0, 0, - 102, 0, 0, 0, 84, 0, 0,103, 0, 0, 0,104,105, 0, 0,106, - 107, 0, 0, 0, 0, 0, 0,108, 0, 0,109, 0, 0, 0, 0,110, - 33, 0,111,112,113, 35, 0, 0,114, 0, 0, 0,115, 0, 0, 0, - 0, 0, 0,116, 0, 0,117, 0, 0, 0, 0,118, 88, 0, 0, 0, - 0, 0, 57, 0, 0, 0, 0, 52,119, 0, 0, 0, 0,120, 0, 0, - 121, 0, 0, 0, 0,119, 0, 0,122, 0, 0, 0, 0, 0, 0,123, - 0, 0, 0,124, 0, 0, 0,125, 0,126, 0, 0, 0, 0,127,128, - 129, 0,130, 0,131, 0, 0, 0,132,133,134, 0, 77, 0, 0, 0, - 0, 0, 35, 0, 0, 0,135, 0, 0, 0,136, 0, 0,137, 0, 0, - 138, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, - 5, 6, 7, 4, 4, 8, 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, - 18, 1, 1, 1, 19, 1, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, - 25, 26, 27, 28, 29, 30, 0, 0, 1, 1, 31, 0, 0, 0, 32, 33, - 34, 35, 1, 36, 37, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, - 42, 0, 0, 0, 43, 36, 44, 45, 21, 45, 46, 0, 0, 0, 19, 1, - 21, 0, 0, 47, 0, 38, 48, 1, 1, 49, 49, 50, 0, 0, 51, 0, - 0, 0, 52, 1, 0, 0, 38, 14, 4, 1, 1, 1, 53, 21, 43, 52, - 54, 21, 35, 1, 0, 0, 0, 55, 0, 0, 0, 56, 57, 58, 0, 0, - 0, 0, 0, 59, 0, 60, 0, 0, 0, 0, 61, 62, 0, 0, 63, 0, - 0, 0, 64, 0, 0, 0, 65, 0, 0, 0, 66, 0, 0, 0, 67, 0, - 0, 0, 68, 0, 0, 69, 70, 0, 71, 72, 73, 74, 75, 76, 0, 0, - 0, 77, 0, 0, 0, 78, 79, 0, 0, 0, 0, 47, 0, 0, 0, 49, - 0, 80, 0, 0, 0, 62, 0, 0, 63, 0, 0, 81, 0, 0, 82, 0, - 0, 0, 83, 0, 0, 19, 84, 0, 62, 0, 0, 0, 0, 49, 1, 85, - 1, 52, 15, 86, 36, 10, 21, 87, 0, 55, 0, 0, 0, 0, 19, 10, - 1, 0, 0, 0, 0, 0, 88, 0, 0, 89, 0, 0, 88, 0, 0, 0, - 0, 78, 0, 0, 87, 9, 12, 4, 90, 8, 91, 47, 0, 58, 50, 0, - 21, 1, 21, 92, 93, 1, 1, 1, 1, 94, 95, 96, 97, 1, 98, 58, - 81, 99,100, 4, 58, 0, 0, 0, 0, 0, 0, 19, 50, 0, 0, 0, - 0, 0, 0, 61, 0, 0,101,102, 0, 0,103, 0, 0, 1, 1, 50, - 0, 0, 0, 38, 0, 63, 0, 0, 0, 0, 0, 62, 0, 0,104, 68, - 61, 0, 0, 0, 78, 0, 0, 0,105,106, 58, 38, 81, 0, 0, 0, - 0, 0, 0,107, 1, 14, 4, 12, 84, 0, 0, 0, 0, 38, 87, 0, - 0, 0, 0,108, 0, 0,109, 61, 0,110, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 40, + 0, 0, 0, 0, 0, 0, 41, 42, 43, 0, 44, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 3, 0, + 0, 0, 4, 5, 6, 7, 0, 8, 9, 10, 0, 11, 12, 13, 14, 15, + 16, 17, 16, 18, 16, 19, 16, 19, 16, 19, 0, 19, 16, 20, 16, 19, + 21, 19, 0, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 32, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, + 34, 0, 0, 35, 0, 0, 36, 0, 37, 0, 0, 0, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 0, 0, 47, 0, 0, 0, 48, 0, 0, 0, 49, + 0, 0, 0, 0, 0, 0, 0, 50, 0, 51, 0, 52, 53, 0, 54, 0, + 0, 0, 0, 0, 0, 55, 56, 57, 0, 0, 0, 0, 58, 0, 0, 59, + 60, 61, 62, 63, 0, 0, 64, 65, 0, 0, 0, 66, 0, 0, 0, 0, + 67, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 69, 0, 0, 0, 70, 0, 71, 0, 0, 72, 0, 0, 73, + 0, 0, 0, 0, 0, 0, 0, 0, 74, 75, 0, 0, 0, 0, 76, 77, + 0, 78, 79, 0, 0, 80, 81, 0, 82, 62, 0, 83, 84, 0, 0, 85, + 86, 87, 0, 88, 0, 89, 0, 90, 0, 0, 51, 91, 51, 0, 92, 0, + 93, 0, 0, 0, 81, 0, 0, 0, 94, 95, 0, 96, 97, 98, 99, 0, + 0, 0, 0, 0, 51, 0, 0, 0, 0,100,101, 0, 0, 0, 0, 0, + 0,102, 0, 0, 0, 0, 0, 0,103, 0, 0, 0, 0, 0, 0,104, + 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,106, 0, 0,107, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,108,109, 0, 0,110, 0, 0, + 0, 0, 0, 0,111, 0,112, 0,105, 0, 0, 0, 0, 0,113,114, + 0, 0, 0, 0, 0, 0, 0,115, 0, 0, 0,116, 0, 0, 0,117, + 0,118, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 0, + 8, 0, 0, 0, 0, 9, 10, 11, 12, 0, 0, 0, 0, 13, 0, 0, + 14, 15, 0, 16, 0, 17, 18, 0, 0, 19, 0, 20, 21, 0, 0, 0, + 0, 0, 22, 23, 0, 24, 25, 0, 0, 26, 0, 0, 0, 27, 0, 0, + 28, 29, 30, 31, 0, 0, 0, 32, 33, 34, 0, 0, 33, 0, 0, 35, + 33, 0, 0, 0, 33, 36, 0, 0, 0, 0, 0, 37, 38, 0, 0, 0, + 0, 0, 0, 39, 40, 0, 0, 0, 0, 0, 0, 41, 42, 0, 0, 0, + 0, 43, 0, 44, 0, 0, 0, 45, 46, 0, 0, 0, 47, 0, 0, 0, + 0, 0, 0, 48, 49, 0, 0, 0, 0, 50, 0, 0, 0, 51, 0, 52, + 0, 53, 0, 0, 0, 0, 54, 0, 0, 0, 0, 55, 0, 56, 0, 0, + 0, 0, 57, 58, 0, 0, 0, 59, 60, 0, 0, 0, 0, 0, 0, 61, + 52, 0, 62, 63, 0, 0, 64, 0, 0, 0, 65, 66, 0, 0, 0, 67, + 0, 68, 69, 70, 71, 72, 1, 73, 0, 74, 75, 76, 0, 0, 77, 78, + 0, 0, 0, 79, 0, 0, 1, 1, 0, 0, 80, 0, 0, 81, 0, 0, + 0, 0, 77, 82, 0, 83, 0, 0, 0, 0, 0, 78, 84, 0, 85, 0, + 52, 0, 1, 78, 0, 0, 86, 0, 0, 87, 0, 0, 0, 0, 0, 88, + 57, 0, 0, 0, 0, 0, 0, 89, 90, 0, 0, 84, 0, 0, 33, 0, + 0, 91, 0, 0, 0, 0, 92, 0, 0, 0, 0, 49, 0, 0, 93, 0, + 0, 0, 0, 94, 95, 0, 0, 96, 0, 0, 97, 0, 0, 0, 98, 0, + 0, 0, 99, 0, 0, 0,100, 0, 0, 0, 0,101,102, 93, 0, 0, + 103, 0, 0, 0, 84, 0, 0,104, 0, 0, 0,105,106, 0, 0,107, + 108, 0, 0, 0, 0, 0, 0,109, 0, 0,110, 0, 0, 0, 0,111, + 33, 0,112,113,114, 57, 0, 0,115, 35, 0, 0,116, 0, 0, 0, + 117, 0, 0, 0, 0, 0, 0,118, 0, 0,119, 0, 0, 0, 0,120, + 88, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 52,121, 0, 0, 0, + 0,122, 0, 0,123, 0, 0, 0, 0,121, 0, 0,124, 0, 0, 0, + 0, 0, 79, 0, 0, 0, 0,125, 0, 0, 0,126, 0, 0, 0,127, + 0,128, 0, 0, 0, 0,129,130,131, 0,132, 0,133, 0, 0, 0, + 134,135,136, 0, 77, 0, 0, 0, 0, 0, 35, 0, 0, 0,137, 0, + 0, 0,138, 0, 0, 0,139, 0, 0,140, 0, 0,141, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 4, + 4, 8, 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, 18, 1, 1, 1, + 19, 1, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, 25, 26, 27, 28, + 29, 30, 0, 0, 1, 1, 31, 0, 0, 0, 32, 33, 34, 35, 1, 36, + 37, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, 42, 0, 0, 0, + 43, 36, 44, 45, 21, 45, 46, 0, 0, 0, 19, 1, 21, 0, 0, 47, + 0, 38, 48, 1, 1, 49, 49, 50, 0, 0, 51, 0, 0, 19, 52, 1, + 0, 0, 38, 14, 4, 1, 1, 1, 53, 21, 43, 52, 54, 21, 35, 1, + 0, 0, 0, 55, 0, 0, 0, 56, 57, 58, 0, 0, 0, 0, 0, 59, + 0, 60, 0, 0, 0, 0, 61, 62, 0, 0, 63, 0, 0, 0, 64, 0, + 0, 0, 65, 0, 0, 0, 66, 0, 0, 0, 67, 0, 0, 0, 68, 0, + 0, 69, 70, 0, 71, 72, 73, 74, 75, 76, 0, 0, 0, 77, 0, 0, + 0, 78, 79, 0, 0, 0, 0, 47, 0, 0, 0, 49, 0, 80, 0, 0, + 0, 62, 0, 0, 63, 0, 0, 81, 0, 0, 82, 0, 0, 0, 83, 0, + 0, 19, 84, 0, 62, 0, 0, 0, 0, 49, 1, 85, 1, 52, 15, 86, + 36, 10, 21, 87, 0, 55, 0, 0, 0, 0, 19, 10, 1, 0, 0, 0, + 0, 0, 88, 0, 0, 89, 0, 0, 88, 0, 0, 0, 0, 78, 0, 0, + 87, 9, 12, 4, 90, 8, 91, 47, 0, 58, 50, 0, 21, 1, 21, 92, + 93, 1, 1, 1, 1, 94, 95, 96, 97, 1, 98, 58, 81, 99,100, 4, + 58, 0, 0, 0, 0, 0, 0, 19, 50, 0, 0, 0, 0, 0, 0, 61, + 0, 0,101,102, 0, 0,103, 0, 0, 1, 1, 50, 0, 0, 0, 38, + 0, 63, 0, 0, 0, 0, 0, 62, 0, 0,104, 68, 61, 0, 0, 0, + 78, 0, 0, 0,105,106, 58, 38, 81, 0, 0, 0, 0, 0, 0,107, + 1, 14, 4, 12, 84, 0, 0, 0, 0, 38, 87, 0, 0, 0, 0,108, + 0, 0,109, 61, 0,110, 0, 0, 0, 1, 0, 0, 0, 0, 49, 50, 0, 0, 19, 58, 0, 0, 0, 51, 0,111, 14, 52,112, 41, 0, 0, 62, 0, 0, 61, 0, 0,113, 0, 87, 0, 0, 0, 61, 62, 0, 0, 62, 0, 89, 0, 0,113, 0, 0, 0, 0,114, 0, 0, 0, 78, 55, - 0, 38, 1, 58, 1, 58, 0, 0, 63, 89, 0, 0,115, 0, 0, 0, - 55, 0, 0, 0, 0,115, 0, 0, 0, 0, 61, 0, 0, 0, 0, 79, - 0, 61, 0, 0, 0, 0, 56, 0, 89, 80, 0, 0, 79, 0, 0, 0, - 8, 91, 0, 0, 1, 87, 0, 0,116, 0, 0, 0, 0, 0, 0,117, - 0,118,119,120,121, 0,104, 4,122, 49, 23, 0, 0, 0, 38, 50, - 38, 58, 0, 0, 1, 87, 1, 1, 1, 1, 39, 1, 48,105, 87, 0, - 0, 0, 0, 1, 0, 0, 0,123, 4,122, 0, 0, 0, 1,124, 0, - 0, 0, 0, 0,230,230,230,230,230,232,220,220,220,220,232,216, - 220,220,220,220,220,202,202,220,220,220,220,202,202,220,220,220, - 1, 1, 1, 1, 1,220,220,220,220,230,230,230,230,240,230,220, - 220,220,230,230,230,220,220, 0,230,230,230,220,220,220,220,230, - 232,220,220,230,233,234,234,233,234,234,233,230, 0, 0, 0,230, - 0,220,230,230,230,230,220,230,230,230,222,220,230,230,220,220, - 230,222,228,230, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, - 21, 22, 0, 23, 0, 24, 25, 0,230,220, 0, 18, 30, 31, 32, 0, - 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, 34,230,230,220,220,230, - 220,230,230,220, 35, 0, 0, 0, 0, 0,230,230,230, 0, 0,230, - 230, 0,220,230,230,220, 0, 0, 0, 36, 0, 0,230,220,230,230, - 220,220,230,220,220,230,220,230,220,230,230, 0, 0,220, 0, 0, - 230,230, 0,230, 0,230,230,230,230,230, 0, 0, 0,220,220,220, - 230,220,220,220,230,230, 0,220, 27, 28, 29,230, 7, 0, 0, 0, - 0, 9, 0, 0, 0,230,220,230,230, 0, 0, 0, 0, 0,230, 0, - 0, 84, 91, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 9, 0, - 103,103, 9, 0,107,107,107,107,118,118, 9, 0,122,122,122,122, - 220,220, 0, 0, 0,220, 0,220, 0,216, 0, 0, 0,129,130, 0, - 132, 0, 0, 0, 0, 0,130,130,130,130, 0, 0,130, 0,230,230, - 9, 0,230,230, 0, 0,220, 0, 0, 0, 0, 7, 0, 9, 9, 0, - 9, 9, 0, 0, 0,230, 0, 0, 0,228, 0, 0, 0,222,230,220, - 220, 0, 0, 0,230, 0, 0,220,230,220, 0,220,230,230,230, 0, - 0, 0, 9, 9, 0, 0, 7, 0,230, 0, 1, 1, 1, 0, 0, 0, - 230,234,214,220,202,230,230,230,230,230,232,228,228,220,218,230, - 233,220,230,220,230,230, 1, 1, 1, 1, 1,230, 0, 1, 1,230, - 220,230, 1, 1, 0, 0,218,228,232,222,224,224, 0, 8, 8, 0, - 0, 0, 0,220,230, 0,230,230,220, 0, 0,230, 0, 0, 26, 0, - 0,220, 0,230,230, 1,220, 0, 0,230,220, 0, 0, 0,220,220, - 0, 0,230,220, 0, 9, 7, 0, 0, 7, 9, 0, 0, 0, 9, 7, - 6, 6, 0, 0, 0, 0, 1, 0, 0,216,216, 1, 1, 1, 0, 0, - 0,226,216,216,216,216,216, 0,220,220,220, 0,232,232,220,230, - 230,230, 7, 0, 16, 17, 17, 17, 17, 17, 17, 33, 17, 17, 17, 19, - 17, 17, 17, 17, 20,101, 17,113,129,169, 17, 27, 28, 17, 17, 17, + 0, 38, 1, 58, 1, 58, 0, 0, 0, 0, 0, 88, 63, 89, 0, 0, + 115, 0, 0, 0, 55, 0, 0, 0, 0,115, 0, 0, 0, 0, 61, 0, + 0, 0, 0, 79, 0, 61, 0, 0, 0, 0, 56, 0, 89, 80, 0, 0, + 79, 0, 0, 0, 8, 91, 0, 0, 1, 87, 0, 0,116, 0, 0, 0, + 0, 0, 0,117, 0,118,119,120,121, 0,104, 4,122, 49, 23, 0, + 0, 0, 38, 50, 38, 58, 0, 0, 1, 87, 1, 1, 1, 1, 39, 1, + 48,105, 87, 0, 0, 0, 0, 1, 0, 0, 0,123, 0, 0, 0,112, + 4,122, 0, 0, 0, 1,124, 0, 0, 0, 0, 0,230,230,230,230, + 230,232,220,220,220,220,232,216,220,220,220,220,220,202,202,220, + 220,220,220,202,202,220,220,220, 1, 1, 1, 1, 1,220,220,220, + 220,230,230,230,230,240,230,220,220,220,230,230,230,220,220, 0, + 230,230,230,220,220,220,220,230,232,220,220,230,233,234,234,233, + 234,234,233,230, 0, 0, 0,230, 0,220,230,230,230,230,220,230, + 230,230,222,220,230,230,220,220,230,222,228,230, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 0, 23, 0, 24, 25, 0, + 230,220, 0, 18, 30, 31, 32, 0, 0, 0, 0, 27, 28, 29, 30, 31, + 32, 33, 34,230,230,220,220,230,220,230,230,220, 35, 0, 0, 0, + 0, 0,230,230,230, 0, 0,230,230, 0,220,230,230,220, 0, 0, + 0, 36, 0, 0,230,220,230,230,220,220,230,220,220,230,220,230, + 220,230,230, 0, 0,220, 0, 0,230,230, 0,230, 0,230,230,230, + 230,230, 0, 0, 0,220,220,220,230,220,220,220,230,230, 0,220, + 27, 28, 29,230, 7, 0, 0, 0, 0, 9, 0, 0, 0,230,220,230, + 230, 0, 0, 0, 0, 0,230, 0, 0, 84, 91, 0, 0, 0, 0, 9, + 9, 0, 0, 0, 0, 0, 9, 0,103,103, 9, 0,107,107,107,107, + 118,118, 9, 0,122,122,122,122,220,220, 0, 0, 0,220, 0,220, + 0,216, 0, 0, 0,129,130, 0,132, 0, 0, 0, 0, 0,130,130, + 130,130, 0, 0,130, 0,230,230, 9, 0,230,230, 0, 0,220, 0, + 0, 0, 0, 7, 0, 9, 9, 0, 9, 9, 0, 0, 0,230, 0, 0, + 0,228, 0, 0, 0,222,230,220,220, 0, 0, 0,230, 0, 0,220, + 230,220, 0,220,230,230,230, 0, 0, 0, 9, 9, 0, 0, 7, 0, + 230, 0, 1, 1, 1, 0, 0, 0,230,234,214,220,202,230,230,230, + 230,230,232,228,228,220,218,230,233,220,230,220,230,230, 1, 1, + 1, 1, 1,230, 0, 1, 1,230,220,230, 1, 1, 0, 0,218,228, + 232,222,224,224, 0, 8, 8, 0, 0, 0, 0,220,230, 0,230,230, + 220, 0, 0,230, 0, 0, 26, 0, 0,220, 0,230,230, 1,220, 0, + 0,230,220, 0, 0, 0,220,220, 0, 0,230,220, 0, 9, 7, 0, + 0, 7, 9, 0, 0, 0, 9, 7, 6, 6, 0, 0, 0, 0, 1, 0, + 0,216,216, 1, 1, 1, 0, 0, 0,226,216,216,216,216,216, 0, + 220,220,220, 0,232,232,220,230,230,230, 7, 0, 16, 17, 17, 17, + 17, 17, 17, 33, 17, 17, 17, 19, 17, 17, 17, 17, 20,101, 17,113, + 129,169, 17, 27, 28, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17,237, 0, 1, 2, 2, 0, 3, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 5, 0, 0, 0, 0, 6, 7, 8, 9, 0, 0, 0, 10, 11, 12, 13, - 14, 15, 16, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, - 0, 0, 21, 22, 0, 0, 0, 0, 23, 24, 25, 26, 0, 27, 0, 28, - 29, 30, 31, 32, 0, 0, 0, 0, 0, 0, 0, 33, 34, 35, 36, 0, - 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 39, - 0, 0, 0, 0, 1, 2, 40, 41, 0, 1, 2, 2, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, - 0, 0, 3, 4, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 0, 0, - 0, 0, 7, 1, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 0, 0, - 0, 0, 10, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 11, 12, - 0, 13, 0, 14, 15, 16, 0, 0, 0, 0, 0, 1, 17, 18, 0, 19, - 7, 1, 0, 0, 0, 20, 20, 7, 20, 20, 20, 20, 20, 20, 20, 8, - 21, 0, 22, 0, 7, 23, 24, 0, 20, 20, 25, 0, 0, 0, 26, 27, - 1, 7, 20, 20, 20, 20, 20, 1, 28, 29, 30, 31, 0, 0, 20, 0, - 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 20, 20, - 20, 1, 0, 0, 8, 21, 32, 4, 0, 10, 0, 33, 7, 20, 20, 20, - 0, 0, 0, 0, 8, 34, 34, 35, 36, 34, 37, 0, 38, 1, 20, 20, - 0, 0, 39, 0, 1, 1, 0, 8, 21, 1, 20, 0, 0, 0, 1, 0, - 0, 40, 1, 1, 0, 0, 8, 21, 0, 1, 0, 1, 0, 1, 0, 0, - 0, 0, 26, 34, 34, 34, 34, 34, 34, 34, 34, 34, 21, 7, 20, 41, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 21, 0, 42, 43, 44, 0, 45, - 0, 8, 21, 0, 0, 0, 0, 0, 0, 0, 0, 46, 7, 1, 10, 1, - 0, 0, 0, 1, 20, 20, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 26, 34, 9, 0, 0, 20, 20, 1, 20, 20, 0, 0, 0, 0, 0, - 0, 0, 26, 21, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 3, 47, 48, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, - 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 9, 10, 11, 11, 11, 11, 12, 13, 13, 13, 13, 14, 15, 16, 17, 18, - 19, 20, 21, 13, 22, 13, 13, 13, 13, 23, 24, 24, 25, 26, 13, 13, - 13, 27, 28, 29, 13, 30, 31, 32, 33, 34, 35, 36, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 37, 7, 38, 39, 7, 40, 7, 7, 7, 41, 13, 42, 7, 7, 43, 7, - 44, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,237, 0, 1, 2, 2, + 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 6, 7, 8, + 9, 0, 0, 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 21, 22, 0, 0, 0, 0, + 23, 24, 25, 26, 0, 27, 0, 28, 29, 30, 31, 32, 0, 0, 0, 0, + 0, 0, 0, 33, 34, 35, 36, 0, 0, 0, 0, 0, 37, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 38, 39, 0, 0, 0, 0, 1, 2, 40, 41, + 0, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 3, 4, 0, 0, 5, 0, + 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, + 0, 0, 8, 9, 0, 0, 0, 0, 0, 0, 10, 0, 0, 10, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 10, + 0, 0, 0, 0, 0, 0, 11, 12, 0, 13, 0, 14, 15, 16, 0, 0, + 0, 0, 0, 1, 17, 18, 0, 19, 7, 1, 0, 0, 0, 20, 20, 7, + 20, 20, 20, 20, 20, 20, 20, 8, 21, 0, 22, 0, 7, 23, 24, 0, + 20, 20, 25, 0, 0, 0, 26, 27, 1, 7, 20, 20, 20, 20, 20, 1, + 28, 29, 30, 31, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 10, 0, + 0, 0, 0, 0, 0, 0, 20, 20, 20, 1, 0, 0, 8, 21, 32, 4, + 0, 10, 0, 33, 7, 20, 20, 20, 0, 0, 0, 0, 8, 34, 34, 35, + 36, 34, 37, 0, 38, 1, 20, 20, 0, 0, 39, 0, 1, 1, 0, 8, + 21, 1, 20, 0, 0, 0, 1, 0, 0, 40, 1, 1, 0, 0, 8, 21, + 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 26, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 21, 7, 20, 41, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 21, 0, 42, 43, 44, 0, 45, 0, 8, 21, 0, 0, 0, 0, 0, + 0, 0, 0, 46, 7, 1, 10, 1, 0, 0, 0, 1, 20, 20, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 34, 9, 0, 0, 20, 20, + 1, 20, 20, 0, 0, 0, 0, 0, 0, 0, 26, 21, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 47, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 13, 13, 13, 13, 13, 14, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 15, 16, 17, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 20, 20, 20, 20, 20, 20, + 20, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 20, 33, + 34, 35, 34, 34, 36, 37, 20, 20, 20, 20, 20, 20, 38, 20, 39, 40, + 41, 41, 41, 41, 41, 42, 43, 44, 20, 20, 20, 20, 20, 20, 20, 45, + 46, 20, 20, 47, 20, 20, 20, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 20, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 45, 0, 0, 1, - 2, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 32, 33, 34, 35, 36, 37, 37, 37, 37, 37, 38, 39, 40, 41, 42, - 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 2, 2, 53, 54, 55, 56, - 57, 58, 59, 59, 59, 59, 60, 59, 59, 59, 59, 59, 59, 59, 61, 61, - 59, 59, 59, 59, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, - 74, 75, 76, 77, 78, 59, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, - 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, - 70, 70, 70, 70, 70, 70, 70, 70, 70, 79, 70, 70, 70, 70, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 81, 82, 82, 83, 84, 85, 86, 87, 88, - 89, 90, 91, 92, 93, 94, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 95, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, - 70, 70, 97, 98, 99,100,101,101,102,103,104,105,106,107,108,109, - 110,111, 96,112,113,114,115,116,117,118,119,119,120,121,122,123, - 124,125,126,127,128,129,130,131,132, 96,133,134,135,136,137,138, - 139,140,141,142,143, 96,144,145, 96,146,147,148,149, 96,150,151, - 152,153,154,155,156, 96,157,158,159,160, 96,161,162,163,164,164, - 164,164,164,164,164,165,166,164,167, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,168,169,169, - 169,169,169,169,169,169,170, 96, 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96,171,171,171,171,172, 96, 96, 96,173,173, - 173,173,174,175,176,177, 96, 96, 96, 96,178,179,180,181,182,182, - 182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182, - 182,182,182,182,182,182,182,182,182,182,182,182,182,183,182,182, - 182,182,182,182,184,184,184,185,186, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,187,188,189, - 190,191,191,192, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96,193,194, 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,195,196, 59,197, - 198,199,200,201,202, 96,203,204,205, 59, 59,206, 59,207,208,208, - 208,208,208,209, 96, 96, 96, 96, 96, 96, 96, 96,210, 96,211,212, - 213, 96, 96,214, 96, 96, 96,215, 96, 96, 96, 96, 96,216,217,218, - 219, 96, 96, 96, 96, 96,220,221,222, 96,223,224, 96, 96,225,226, - 59,227,228, 96, 59, 59, 59, 59, 59, 59, 59,229,230,231,232,233, - 59, 59,234,235, 59,236, 96, 96, 96, 96, 96, 96, 96, 96, 70, 70, - 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,237, 70, 70, 70, 70, - 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,238, 70,239, 70, - 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, - 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,240, 70, 70, 70, 70, - 70, 70, 70, 70, 70,241, 70, 70, 70, 70,242, 96, 96, 96, 70, 70, - 70, 70,243, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 70, 70, - 70, 70, 70, 70,244, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, - 70, 70, 70, 70, 70,245, 96, 96, 96, 96, 96, 96, 96, 96,246, 96, - 247,248, 0, 1, 2, 2, 0, 1, 2, 2, 2, 3, 4, 5, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 0, 0, 0, 0, 0, 0, - 19, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 0, 19, 0, - 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 0, 0, 0, 0, 0, - 26, 26, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, - 9, 9, 0, 9, 9, 9, 2, 2, 9, 9, 9, 9, 0, 9, 2, 2, - 2, 2, 9, 0, 9, 0, 9, 9, 9, 2, 9, 2, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 9, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 1, 1, 6, 2, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 2, 4, 4, 4, 2, 2, 4, 4, 4, 2, 14, - 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 2, 2, - 2, 2, 2, 2, 2, 2, 14, 14, 14, 2, 2, 2, 2, 14, 14, 14, - 14, 14, 14, 2, 2, 2, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, - 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 0, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, 3, - 3, 3, 3, 3, 3, 3, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, - 37, 37, 37, 37, 2, 37, 37, 37, 37, 2, 2, 37, 37, 37, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, 2, 2, 2, 2, 2, 2, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 2, 2, 64, 64, 64, 90, 90, - 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 2, 2, 90, 90, - 90, 90, 90, 90, 90, 2, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 2, 2, 95, 2, 37, 37, 37, 2, 2, 2, 2, 2, 3, 3, - 3, 3, 3, 3, 3, 2, 3, 3, 2, 2, 2, 2, 2, 2, 3, 3, - 0, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7, 7, 7, 1, - 1, 1, 1, 7, 7, 7, 7, 7, 7, 7, 0, 0, 7, 7, 5, 5, - 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 5, 5, 2, - 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, - 5, 5, 5, 5, 5, 5, 5, 2, 5, 2, 2, 2, 5, 5, 5, 5, - 2, 2, 5, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, 2, 2, 2, - 2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 5, 5, 2, 5, 5, 5, - 5, 5, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 11, - 11, 11, 2, 11, 11, 11, 11, 11, 11, 2, 2, 2, 2, 11, 11, 2, - 2, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, - 11, 11, 11, 11, 11, 11, 11, 2, 11, 11, 2, 11, 11, 2, 11, 11, - 2, 2, 11, 2, 11, 11, 11, 2, 2, 11, 11, 11, 2, 2, 2, 11, - 2, 2, 2, 2, 2, 2, 2, 11, 11, 11, 11, 2, 11, 2, 2, 2, - 2, 2, 2, 2, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, 2, 10, - 10, 10, 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, - 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, - 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 2, 10, 10, 10, 10, 10, - 2, 2, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, 2, 2, 10, 2, - 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 2, 2, 10, 10, 10, 10, - 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 2, 21, - 21, 21, 2, 21, 21, 21, 21, 21, 21, 21, 21, 2, 2, 21, 21, 2, - 2, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, - 21, 21, 21, 21, 21, 21, 21, 2, 21, 21, 2, 21, 21, 21, 21, 21, - 2, 2, 21, 21, 21, 21, 21, 2, 2, 21, 21, 21, 2, 2, 2, 2, - 2, 2, 2, 21, 21, 21, 2, 2, 2, 2, 21, 21, 2, 21, 21, 21, - 21, 21, 2, 2, 21, 21, 2, 2, 22, 22, 2, 22, 22, 22, 22, 22, - 22, 2, 2, 2, 22, 22, 22, 2, 22, 22, 22, 22, 2, 2, 2, 22, - 22, 2, 22, 2, 22, 22, 2, 2, 2, 22, 22, 2, 2, 2, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 2, 2, 2, 2, 22, 22, 22, 2, - 2, 2, 2, 2, 2, 22, 2, 2, 2, 2, 2, 2, 22, 22, 22, 22, - 22, 2, 2, 2, 2, 2, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, - 23, 23, 23, 2, 23, 23, 23, 2, 23, 23, 23, 23, 23, 23, 23, 23, - 2, 2, 23, 23, 23, 23, 23, 2, 23, 23, 23, 23, 2, 2, 2, 2, - 2, 2, 2, 23, 23, 2, 23, 23, 23, 2, 2, 23, 2, 2, 23, 23, - 23, 23, 2, 2, 23, 23, 2, 2, 2, 2, 2, 2, 2, 23, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 16, 16, 16, 2, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 16, 16, 16, 16, 16, - 2, 2, 16, 16, 16, 16, 16, 2, 16, 16, 16, 16, 2, 2, 2, 2, - 2, 2, 2, 16, 16, 2, 16, 16, 16, 16, 2, 2, 16, 16, 2, 16, - 16, 16, 2, 2, 2, 2, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 2, 20, 20, 20, 2, 20, 20, 20, 20, 20, 20, 2, 2, - 2, 2, 20, 20, 20, 20, 20, 20, 20, 20, 2, 2, 20, 20, 2, 36, - 36, 36, 2, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 2, 2, 2, 36, 36, 36, 36, 36, 36, 36, 36, - 2, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 36, 2, 2, 2, 2, - 36, 2, 2, 2, 2, 36, 36, 36, 36, 36, 36, 2, 36, 2, 2, 2, - 2, 2, 2, 2, 36, 36, 2, 2, 36, 36, 36, 2, 2, 2, 2, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 2, 2, 2, 2, 0, 24, 24, 24, 24, 2, 2, 2, 2, 2, 18, - 18, 2, 18, 2, 18, 18, 18, 18, 18, 2, 18, 18, 18, 18, 18, 18, - 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 2, 18, 2, 18, 18, 18, - 18, 18, 18, 18, 2, 2, 18, 18, 18, 18, 18, 2, 18, 2, 18, 18, - 18, 18, 18, 18, 18, 2, 18, 18, 2, 2, 18, 18, 18, 18, 25, 25, - 25, 25, 25, 25, 25, 25, 2, 25, 25, 25, 25, 25, 25, 25, 25, 25, - 25, 25, 25, 2, 2, 2, 25, 25, 25, 25, 25, 2, 25, 25, 25, 25, - 25, 25, 25, 0, 0, 0, 0, 25, 25, 2, 2, 2, 2, 2, 33, 33, - 33, 33, 33, 33, 33, 33, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 2, 8, 2, 2, 2, 2, 2, 8, 2, 2, 8, 8, - 8, 0, 8, 8, 8, 8, 12, 12, 12, 12, 12, 12, 12, 12, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 2, 30, 30, 30, 30, 2, 2, 30, 30, - 30, 30, 30, 30, 30, 2, 30, 30, 30, 2, 2, 30, 30, 30, 30, 30, - 30, 30, 30, 2, 2, 2, 30, 30, 2, 2, 2, 2, 2, 2, 29, 29, - 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 2, 2, 28, 28, - 28, 28, 28, 28, 28, 28, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 2, 2, 2, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 0, 0, 0, 35, 35, 35, 2, 2, 2, 2, 2, 2, 2, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 0, 0, 2, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 2, 2, 2, 2, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 2, 46, 46, 46, 2, 46, 46, 2, 2, 2, 2, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 2, 2, 31, 31, - 2, 2, 2, 2, 2, 2, 32, 32, 0, 0, 32, 0, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 2, 2, 2, 2, 2, 2, 32, 2, - 2, 2, 2, 2, 2, 2, 32, 32, 32, 2, 2, 2, 2, 2, 28, 28, - 28, 28, 28, 28, 2, 2, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 2, 48, 48, 48, 48, 2, 2, 2, 2, 48, 2, - 2, 2, 48, 48, 48, 48, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 2, 2, 52, 52, 52, 52, 52, 2, 2, 2, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 2, 2, 2, 2, 58, 58, - 2, 2, 2, 2, 2, 2, 58, 58, 58, 2, 2, 2, 58, 58, 54, 54, - 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 2, 2, 54, 54, 91, 91, - 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 2, 91, 91, - 91, 91, 91, 2, 2, 91, 91, 91, 2, 2, 2, 2, 2, 2, 91, 91, - 91, 91, 91, 91, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 2, 2, 2, 62, 62, - 62, 62, 62, 62, 62, 2, 76, 76, 76, 76, 76, 76, 76, 76, 93, 93, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 60, 13, 13, + 13, 61, 62, 13, 13, 13, 13, 63, 13, 13, 13, 13, 13, 13, 64, 65, + 20, 20, 66, 20, 13, 13, 13, 13, 67, 13, 13, 13, 68, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 19, 19, + 19, 19, 19, 19, 19, 0, 19, 0, 0, 0, 0, 0, 0, 0, 19, 19, + 19, 19, 19, 0, 0, 0, 0, 0, 26, 26, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 0, 9, 9, 9, 2, 2, + 9, 9, 9, 9, 0, 9, 2, 2, 2, 2, 9, 0, 9, 0, 9, 9, + 9, 2, 9, 2, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 2, 9, 9, 9, 9, 9, 9, 9, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 1, 1, 6, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, + 4, 2, 2, 4, 4, 4, 2, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 2, 2, 2, 2, 2, 2, 2, 2, 14, 14, + 14, 2, 2, 2, 2, 14, 14, 14, 14, 14, 14, 2, 2, 2, 3, 3, + 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 0, 0, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 2, 37, 37, 37, + 37, 2, 2, 37, 37, 37, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 2, 2, 2, 2, 2, 2, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 2, 2, 64, 64, 64, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 2, 2, 90, 90, 90, 90, 90, 90, 90, 2, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 2, 2, 95, 2, 37, 37, + 37, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, + 2, 2, 2, 2, 2, 3, 3, 3, 0, 3, 3, 3, 3, 3, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 1, 1, 1, 1, 7, 7, 7, 7, 7, + 7, 7, 0, 0, 7, 7, 5, 5, 5, 5, 2, 5, 5, 5, 5, 5, + 5, 5, 5, 2, 2, 5, 5, 2, 2, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, 2, + 5, 2, 2, 2, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, 5, 2, + 2, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 5, 2, 2, + 2, 2, 5, 5, 2, 5, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 2, 2, 11, 11, 11, 2, 11, 11, 11, 11, 11, + 11, 2, 2, 2, 2, 11, 11, 2, 2, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 2, 11, 11, 11, 11, 11, 11, 11, 2, + 11, 11, 2, 11, 11, 2, 11, 11, 2, 2, 11, 2, 11, 11, 11, 2, + 2, 11, 11, 11, 2, 2, 2, 11, 2, 2, 2, 2, 2, 2, 2, 11, + 11, 11, 11, 2, 11, 2, 2, 2, 2, 2, 2, 2, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 2, 2, 10, 10, 10, 2, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 2, 10, 10, 10, 2, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, 10, 10, 10, 10, 2, + 10, 10, 2, 10, 10, 10, 10, 10, 2, 2, 10, 10, 10, 10, 10, 10, + 2, 10, 10, 10, 2, 2, 10, 2, 2, 2, 2, 2, 2, 2, 10, 10, + 10, 10, 2, 2, 10, 10, 10, 10, 2, 2, 2, 2, 2, 2, 2, 10, + 10, 10, 10, 10, 10, 10, 2, 21, 21, 21, 2, 21, 21, 21, 21, 21, + 21, 21, 21, 2, 2, 21, 21, 2, 2, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 2, 21, 21, 21, 21, 21, 21, 21, 2, + 21, 21, 2, 21, 21, 21, 21, 21, 2, 2, 21, 21, 21, 21, 21, 2, + 2, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 21, 21, 21, 2, 2, + 2, 2, 21, 21, 2, 21, 21, 21, 21, 21, 2, 2, 21, 21, 2, 2, + 22, 22, 2, 22, 22, 22, 22, 22, 22, 2, 2, 2, 22, 22, 22, 2, + 22, 22, 22, 22, 2, 2, 2, 22, 22, 2, 22, 2, 22, 22, 2, 2, + 2, 22, 22, 2, 2, 2, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 2, 2, 2, 2, 22, 22, 22, 2, 2, 2, 2, 2, 2, 22, 2, 2, + 2, 2, 2, 2, 22, 22, 22, 22, 22, 2, 2, 2, 2, 2, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 2, 23, 23, 23, 2, + 23, 23, 23, 23, 23, 23, 23, 23, 2, 2, 23, 23, 23, 23, 23, 2, + 23, 23, 23, 23, 2, 2, 2, 2, 2, 2, 2, 23, 23, 2, 23, 23, + 23, 2, 2, 23, 2, 2, 23, 23, 23, 23, 2, 2, 23, 23, 2, 2, + 2, 2, 2, 2, 2, 23, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 2, 16, 16, 16, 2, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 2, 16, 16, 16, 16, 16, 2, 2, 16, 16, 16, 16, 16, 2, + 16, 16, 16, 16, 2, 2, 2, 2, 2, 2, 2, 16, 16, 2, 16, 16, + 16, 16, 2, 2, 16, 16, 2, 16, 16, 16, 2, 2, 2, 2, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 2, 20, 20, 20, 2, + 20, 20, 20, 20, 20, 20, 2, 2, 2, 2, 20, 20, 20, 20, 20, 20, + 20, 20, 2, 2, 20, 20, 2, 36, 36, 36, 2, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2, + 36, 36, 36, 36, 36, 36, 36, 36, 2, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 2, 36, 2, 2, 2, 2, 36, 2, 2, 2, 2, 36, 36, 36, + 36, 36, 36, 2, 36, 2, 2, 2, 2, 2, 2, 2, 36, 36, 2, 2, + 36, 36, 36, 2, 2, 2, 2, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 2, 2, 2, 2, 0, 24, 24, + 24, 24, 2, 2, 2, 2, 2, 18, 18, 2, 18, 2, 18, 18, 18, 18, + 18, 2, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 2, 18, 2, 18, 18, 18, 18, 18, 18, 18, 2, 2, 18, 18, + 18, 18, 18, 2, 18, 2, 18, 18, 18, 18, 18, 18, 18, 2, 18, 18, + 2, 2, 18, 18, 18, 18, 25, 25, 25, 25, 25, 25, 25, 25, 2, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 2, 2, 2, 25, 25, + 25, 25, 25, 2, 25, 25, 25, 25, 25, 25, 25, 0, 0, 0, 0, 25, + 25, 2, 2, 2, 2, 2, 33, 33, 33, 33, 33, 33, 33, 33, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 2, 8, 2, 2, + 2, 2, 2, 8, 2, 2, 8, 8, 8, 0, 8, 8, 8, 8, 12, 12, + 12, 12, 12, 12, 12, 12, 30, 30, 30, 30, 30, 30, 30, 30, 30, 2, + 30, 30, 30, 30, 2, 2, 30, 30, 30, 30, 30, 30, 30, 2, 30, 30, + 30, 2, 2, 30, 30, 30, 30, 30, 30, 30, 30, 2, 2, 2, 30, 30, + 2, 2, 2, 2, 2, 2, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 2, 2, 28, 28, 28, 28, 28, 28, 28, 28, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 2, 2, 2, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 0, 0, 0, 35, 35, 35, 2, + 2, 2, 2, 2, 2, 2, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 2, 2, 2, 2, 2, 2, 2, 2, 2, 45, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 0, 0, 2, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 2, 2, 2, 2, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 2, 46, 46, 46, 2, + 46, 46, 2, 2, 2, 2, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 2, 2, 31, 31, 2, 2, 2, 2, 2, 2, 32, 32, + 0, 0, 32, 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 2, 2, 2, 2, 2, 2, 32, 2, 2, 2, 2, 2, 2, 2, 32, 32, + 32, 2, 2, 2, 2, 2, 28, 28, 28, 28, 28, 28, 2, 2, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 2, 48, 48, + 48, 48, 2, 2, 2, 2, 48, 2, 2, 2, 48, 48, 48, 48, 52, 52, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 2, 2, 52, 52, + 52, 52, 52, 2, 2, 2, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 2, 2, 2, 2, 58, 58, 2, 2, 2, 2, 2, 2, 58, 58, + 58, 2, 2, 2, 58, 58, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 2, 2, 54, 54, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 2, 91, 91, 91, 91, 91, 2, 2, 91, 91, 91, + 2, 2, 2, 2, 2, 2, 91, 91, 91, 91, 91, 91, 2, 2, 1, 1, + 1, 1, 1, 1, 1, 2, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 2, 62, 62, 76, 76, 76, 76, 76, 76, 76, 76, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 2, 2, 2, 2, 2, 2, 2, 2, 93, 93, 93, 93, 70, 70, 70, 70, 70, 70, 70, 70, 2, 2, 2, 70, 70, 70, 70, 70, 70, 70, 2, 2, 2, 70, 70, 70, 73, 73, - 73, 73, 73, 73, 73, 73, 6, 2, 2, 2, 2, 2, 2, 2, 8, 8, + 73, 73, 73, 73, 73, 73, 6, 6, 6, 2, 2, 2, 2, 2, 8, 8, 8, 2, 2, 8, 8, 8, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 2, 2, 2, 2, 2, 19, 19, @@ -1896,31 +1872,30 @@ _hb_ucd_u8[17884] = 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 19, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 19, 0, 0, 0, 2, 2, 2, 2, 0, 0, - 0, 2, 2, 2, 2, 2, 27, 27, 27, 27, 27, 27, 27, 27, 0, 0, - 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 56, 56, - 56, 56, 56, 56, 56, 56, 55, 55, 55, 55, 2, 2, 2, 2, 2, 55, - 55, 55, 55, 55, 55, 55, 61, 61, 61, 61, 61, 61, 61, 61, 2, 2, - 2, 2, 2, 2, 2, 61, 61, 2, 2, 2, 2, 2, 2, 2, 0, 0, - 0, 0, 0, 0, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, 13, 13, - 13, 13, 13, 13, 2, 2, 0, 0, 0, 0, 0, 13, 0, 13, 0, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 1, 1, 1, 1, 12, 12, 13, 13, - 13, 13, 0, 0, 0, 0, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 1, - 1, 0, 0, 15, 15, 15, 0, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 0, 0, 17, 17, 17, 2, 2, - 2, 2, 2, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 12, - 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 2, 0, 0, - 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 12, 12, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 2, 27, 27, + 27, 27, 27, 27, 27, 27, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 0, 56, 56, 56, 56, 56, 56, 56, 56, 55, 55, + 55, 55, 2, 2, 2, 2, 2, 55, 55, 55, 55, 55, 55, 55, 61, 61, + 61, 61, 61, 61, 61, 61, 2, 2, 2, 2, 2, 2, 2, 61, 61, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 2, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 2, 2, 2, 2, 13, 13, 13, 13, 13, 13, 2, 2, 0, 0, + 0, 0, 0, 13, 0, 13, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 1, 1, 1, 1, 12, 12, 13, 13, 13, 13, 0, 0, 0, 0, 2, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 2, 2, 1, 1, 0, 0, 15, 15, 15, 0, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 0, 0, 17, 17, 17, 2, 2, 2, 2, 2, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 2, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, 0, 12, 12, 12, 12, 12, 12, 12, 0, 17, 17, 17, 17, 17, 17, 17, 0, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 2, 2, 2, 39, 39, 39, 39, 39, 39, 39, 2, 86, 86, 86, 86, 86, 86, 86, 86, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 2, 2, 2, 2, 79, 79, 79, 79, 79, 79, 79, 79, 0, 0, 19, 19, 19, 19, 19, 19, 0, 0, - 0, 19, 19, 19, 19, 19, 19, 19, 19, 2, 2, 2, 2, 2, 19, 19, - 2, 19, 2, 19, 19, 19, 19, 19, 2, 2, 2, 2, 2, 2, 2, 2, - 19, 19, 19, 19, 19, 19, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 65, 65, + 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 2, 2, 19, 19, + 2, 19, 2, 19, 19, 19, 2, 2, 19, 19, 19, 19, 19, 19, 60, 60, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 2, 2, 2, 65, 65, 65, 65, 65, 65, 65, 65, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 2, 2, 2, 2, 2, 2, 2, 2, 75, 75, 75, 75, 2, 2, 2, 2, 2, 2, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, @@ -1943,36 +1918,38 @@ _hb_ucd_u8[17884] = 2, 14, 14, 2, 14, 14, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 2, 2, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 3, 1, 1, - 1, 1, 1, 1, 6, 6, 0, 0, 0, 2, 0, 0, 0, 0, 3, 3, - 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 2, 2, 0, 2, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 17, 17, - 17, 17, 17, 17, 0, 0, 2, 2, 12, 12, 12, 12, 12, 12, 2, 2, - 12, 12, 12, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 2, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 2, 49, 49, 49, 2, 49, 49, 2, 49, 49, 49, - 49, 49, 49, 49, 2, 2, 49, 49, 49, 2, 2, 2, 2, 2, 0, 0, - 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, - 0, 0, 0, 2, 2, 2, 9, 2, 2, 2, 2, 2, 2, 2, 0, 0, - 0, 0, 0, 1, 2, 2, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 2, 2, 2, 67, 67, 67, 67, 67, 67, 67, 67, 67, 2, - 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, - 41, 2, 2, 2, 2, 2,118,118,118,118,118,118,118,118,118,118, - 118, 2, 2, 2, 2, 2, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 2, 53, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, - 59, 59, 2, 2, 2, 2, 59, 59, 59, 59, 59, 59, 2, 2, 40, 40, - 40, 40, 40, 40, 40, 40, 51, 51, 51, 51, 51, 51, 51, 51, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 2, 2, 50, 50, - 2, 2, 2, 2, 2, 2,135,135,135,135,135,135,135,135,135,135, - 135,135, 2, 2, 2, 2,106,106,106,106,106,106,106,106,104,104, - 104,104,104,104,104,104,104,104,104,104, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2,104,161,161,161,161,161,161,161,161,161,161, - 161, 2,161,161,161,161,161,161,161, 2,161,161, 2,161,161,161, - 2,161,161,161,161,161,161,161, 2,161,161, 2, 2, 2,110,110, - 110,110,110,110,110,110,110,110,110,110,110,110,110, 2,110,110, - 110,110,110,110, 2, 2, 19, 19, 19, 19, 19, 19, 2, 19, 19, 2, - 19, 19, 19, 19, 19, 19, 47, 47, 47, 47, 47, 47, 2, 2, 47, 2, + 1, 1, 1, 1, 6, 6, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 2, 2, 2, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, + 3, 3, 3, 2, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 17, 17, 17, 17, 17, 17, 17, 17, 0, 0, 2, 2, + 12, 12, 12, 12, 12, 12, 2, 2, 12, 12, 12, 2, 2, 2, 2, 0, + 0, 0, 0, 0, 2, 2, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 2, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 2, 49, 49, + 49, 2, 49, 49, 2, 49, 49, 49, 49, 49, 49, 49, 2, 2, 49, 49, + 49, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, + 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 9, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 1, 2, 2, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 2, 2, 2, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 2, 2, 2, 2, 2, 2, 2, 1, 0, + 0, 0, 0, 0, 0, 0, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 2, 2, 2, 2, 2, 2, 2, 2, 2, 42, 42, 42, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 2, 2, 2, 2, 2,118,118, + 118,118,118,118,118,118,118,118,118, 2, 2, 2, 2, 2, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 2, 53, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 2, 2, 2, 2, 59, 59, + 59, 59, 59, 59, 2, 2, 40, 40, 40, 40, 40, 40, 40, 40, 51, 51, + 51, 51, 51, 51, 51, 51, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 2, 2, 50, 50, 2, 2, 2, 2, 2, 2,135,135, + 135,135,135,135,135,135,135,135,135,135, 2, 2, 2, 2,106,106, + 106,106,106,106,106,106,104,104,104,104,104,104,104,104,104,104, + 104,104, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,104,161,161, + 161,161,161,161,161,161,161,161,161, 2,161,161,161,161,161,161, + 161, 2,161,161, 2,161,161,161, 2,161,161,161,161,161,161,161, + 2,161,161, 2, 2, 2,170,170,170,170,170,170,170,170,170,170, + 170,170, 2, 2, 2, 2,110,110,110,110,110,110,110,110,110,110, + 110,110,110,110,110, 2,110,110,110,110,110,110, 2, 2, 19, 19, + 19, 19, 19, 19, 2, 19, 19, 2, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 2, 2, 2, 2, 2, 47, 47, 47, 47, 47, 47, 2, 2, 47, 2, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 2, 47, 47, 2, 2, 2, 47, 2, 2, 47, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 2, 81,120,120, @@ -1998,122 +1975,135 @@ _hb_ucd_u8[17884] = 122,122,122,122,122,122, 89, 89, 89, 89, 89, 89, 89, 89, 89, 2, 2, 2, 2, 2, 2, 2,130,130,130,130,130,130,130,130,130,130, 130, 2, 2, 2, 2, 2, 2, 2,130,130,130,130,130,130,144,144, - 144,144,144,144,144,144,144,144, 2, 2, 2, 2, 2, 2,156,156, + 144,144,144,144,144,144,144,144, 2, 2, 2, 2, 2, 2,165,165, + 165,165,165,165,165,165,165,165,165,165,165,165, 2, 2, 2,165, + 165,165,165,165,165,165, 2, 2, 2, 2, 2, 2,165,165,156,156, 156,156,156,156,156,156,156,156, 2,156,156,156, 2, 2,156,156, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3,147,147, - 147,147,147,147,147,147,148,148,148,148,148,148,148,148,148,148, - 2, 2, 2, 2, 2, 2,158,158,158,158,158,158,158,158,158,158, - 2, 2, 2, 2, 2, 2,153,153,153,153,153,153,153,153,153,153, - 153,153, 2, 2, 2, 2,149,149,149,149,149,149,149,149,149,149, - 149,149,149,149,149, 2, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, - 94, 94, 94, 94, 2, 2, 2, 2, 94, 94, 94, 94, 94, 94, 2, 2, - 2, 2, 2, 2, 2, 94, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 85, 2, 2,101,101, - 101,101,101,101,101,101,101, 2, 2, 2, 2, 2, 2, 2,101,101, - 2, 2, 2, 2, 2, 2, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 2, 96, 96,111,111,111,111,111,111,111,111,111,111, - 111,111,111,111,111, 2,100,100,100,100,100,100,100,100, 2, 36, - 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2,108,108, - 108,108,108,108,108,108,108,108, 2,108,108,108,108,108,108,108, - 2, 2, 2, 2, 2, 2,129,129,129,129,129,129,129, 2,129, 2, - 129,129,129,129, 2,129,129,129,129,129,129,129,129,129,129,129, - 129,129,129,129, 2,129,129,129, 2, 2, 2, 2, 2, 2,109,109, - 109,109,109,109,109,109,109,109,109, 2, 2, 2, 2, 2,109,109, - 2, 2, 2, 2, 2, 2,107,107,107,107, 2,107,107,107,107,107, - 107,107,107, 2, 2,107,107, 2, 2,107,107,107,107,107,107,107, - 107,107,107,107,107,107,107, 2,107,107,107,107,107,107,107, 2, - 107,107, 2,107,107,107,107,107, 2, 1,107,107,107,107,107, 2, - 2,107,107,107, 2, 2,107, 2, 2, 2, 2, 2, 2,107, 2, 2, - 2, 2, 2,107,107,107,107,107,107,107, 2, 2,107,107,107,107, - 107,107,107, 2, 2, 2,137,137,137,137,137,137,137,137,137,137, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 2, + 2, 2, 3, 3, 3, 3,147,147,147,147,147,147,147,147,148,148, + 148,148,148,148,148,148,148,148, 2, 2, 2, 2, 2, 2,158,158, + 158,158,158,158,158,158,158,158, 2, 2, 2, 2, 2, 2,153,153, + 153,153,153,153,153,153,153,153,153,153, 2, 2, 2, 2,149,149, + 149,149,149,149,149,149,149,149,149,149,149,149,149, 2, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 2, 2, 2, 2, + 94, 94, 94, 94, 94, 94, 2, 2, 2, 2, 2, 2, 2, 94, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 85, 2, 2,101,101,101,101,101,101,101,101,101, 2, + 2, 2, 2, 2, 2, 2,101,101, 2, 2, 2, 2, 2, 2, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 2, 96, 96,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111, 2,100,100, + 100,100,100,100,100,100, 2, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 2, 2, 2,108,108,108,108,108,108,108,108,108,108, + 2,108,108,108,108,108,108,108, 2, 2, 2, 2, 2, 2,129,129, + 129,129,129,129,129, 2,129, 2,129,129,129,129, 2,129,129,129, + 129,129,129,129,129,129,129,129,129,129,129,129, 2,129,129,129, + 2, 2, 2, 2, 2, 2,109,109,109,109,109,109,109,109,109,109, + 109, 2, 2, 2, 2, 2,109,109, 2, 2, 2, 2, 2, 2,107,107, + 107,107, 2,107,107,107,107,107,107,107,107, 2, 2,107,107, 2, + 2,107,107,107,107,107,107,107,107,107,107,107,107,107,107, 2, + 107,107,107,107,107,107,107, 2,107,107, 2,107,107,107,107,107, + 2, 1,107,107,107,107,107, 2, 2,107,107,107, 2, 2,107, 2, + 2, 2, 2, 2, 2,107, 2, 2, 2, 2, 2,107,107,107,107,107, + 107,107, 2, 2,107,107,107,107,107,107,107, 2, 2, 2,171,171, + 171,171,171,171,171,171,171,171, 2,171, 2, 2,171, 2,171,171, + 171,171,171,171, 2,171,171, 2,171, 2, 2,171, 2,171,171,171, + 171, 2,171,171,171,171,171, 2, 2, 2, 2, 2, 2, 2, 2,171, + 171, 2, 2, 2, 2, 2,137,137,137,137,137,137,137,137,137,137, 137,137, 2,137,137,137,137,137, 2, 2, 2, 2, 2, 2,124,124, 124,124,124,124,124,124,124,124, 2, 2, 2, 2, 2, 2,123,123, 123,123,123,123,123,123,123,123,123,123,123,123, 2, 2,114,114, 114,114,114,114,114,114,114,114,114,114,114, 2, 2, 2,114,114, 2, 2, 2, 2, 2, 2, 32, 32, 32, 32, 32, 2, 2, 2,102,102, - 102,102,102,102,102,102,102,102, 2, 2, 2, 2, 2, 2,126,126, - 126,126,126,126,126,126,126,126,126, 2, 2,126,126,126,126,126, - 126,126, 2, 2, 2, 2,126,126,126,126,126,126,126, 2,142,142, - 142,142,142,142,142,142,142,142,142,142, 2, 2, 2, 2,125,125, - 125,125,125,125,125,125,125,125,125, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2,125,154,154,154,154,154,154,154, 2, 2,154, - 2, 2,154,154,154,154,154,154,154,154, 2,154,154, 2,154,154, - 154,154,154,154,154,154,154,154,154,154,154,154, 2,154,154, 2, - 2,154,154,154,154,154,154,154, 2, 2, 2, 2, 2, 2,150,150, - 150,150,150,150,150,150, 2, 2,150,150,150,150,150,150,150,150, - 150,150,150, 2, 2, 2,141,141,141,141,141,141,141,141,140,140, - 140,140,140,140,140,140,140,140,140, 2, 2, 2, 2, 2,121,121, - 121,121,121,121,121,121,121, 2, 2, 2, 2, 2, 2, 2, 7, 7, - 2, 2, 2, 2, 2, 2,133,133,133,133,133,133,133,133,133, 2, - 133,133,133,133,133,133,133,133,133,133,133,133,133, 2,133,133, - 133,133,133,133, 2, 2,133,133,133,133,133, 2, 2, 2,134,134, - 134,134,134,134,134,134, 2, 2,134,134,134,134,134,134, 2,134, - 134,134,134,134,134,134,134,134,134,134,134,134,134, 2,138,138, - 138,138,138,138,138, 2,138,138, 2,138,138,138,138,138,138,138, - 138,138,138,138,138,138, 2, 2,138, 2,138,138, 2,138,138,138, - 2, 2, 2, 2, 2, 2,143,143,143,143,143,143, 2,143,143, 2, - 143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143, - 143,143,143,143,143, 2,143,143, 2,143,143,143,143,143,143, 2, - 2, 2, 2, 2, 2, 2,143,143, 2, 2, 2, 2, 2, 2,145,145, - 145,145,145,145,145,145,145, 2, 2, 2, 2, 2, 2, 2,163,163, - 163,163,163,163,163,163,163, 2,163,163,163,163,163,163,163,163, - 163, 2, 2, 2,163,163,163,163, 2, 2, 2, 2, 2, 2, 86, 2, - 2, 2, 2, 2, 2, 2, 22, 22, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 22, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, - 2, 2, 2, 2, 2, 2, 63, 63, 63, 63, 63, 63, 63, 2, 63, 63, - 63, 63, 63, 2, 2, 2, 63, 63, 63, 63, 2, 2, 2, 2,157,157, - 157,157,157,157,157,157,157,157,157, 2, 2, 2, 2, 2, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 2, 2,127,127, - 127,127,127,127,127,127,127,127,127,127,127,127,127, 2, 79, 2, + 102,102,102,102,102,102,102,102, 2, 2, 2, 2, 2, 2, 33, 33, + 33, 33, 2, 2, 2, 2,126,126,126,126,126,126,126,126,126,126, + 126, 2, 2,126,126,126,126,126,126,126, 2, 2, 2, 2,126,126, + 126,126,126,126,126, 2,142,142,142,142,142,142,142,142,142,142, + 142,142, 2, 2, 2, 2,125,125,125,125,125,125,125,125,125,125, + 125, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,125,154,154, + 154,154,154,154,154, 2, 2,154, 2, 2,154,154,154,154,154,154, + 154,154, 2,154,154, 2,154,154,154,154,154,154,154,154,154,154, + 154,154,154,154, 2,154,154, 2, 2,154,154,154,154,154,154,154, + 2, 2, 2, 2, 2, 2,150,150,150,150,150,150,150,150, 2, 2, + 150,150,150,150,150,150,150,150,150,150,150, 2, 2, 2,141,141, + 141,141,141,141,141,141,140,140,140,140,140,140,140,140,140,140, + 140, 2, 2, 2, 2, 2,121,121,121,121,121,121,121,121,121, 2, + 2, 2, 2, 2, 2, 2, 7, 7, 2, 2, 2, 2, 2, 2,169,169, + 169,169,169,169,169,169,169,169, 2, 2, 2, 2, 2, 2,133,133, + 133,133,133,133,133,133,133, 2,133,133,133,133,133,133,133,133, + 133,133,133,133,133, 2,133,133,133,133,133,133, 2, 2,133,133, + 133,133,133, 2, 2, 2,134,134,134,134,134,134,134,134, 2, 2, + 134,134,134,134,134,134, 2,134,134,134,134,134,134,134,134,134, + 134,134,134,134,134, 2,138,138,138,138,138,138,138, 2,138,138, + 2,138,138,138,138,138,138,138,138,138,138,138,138,138, 2, 2, + 138, 2,138,138, 2,138,138,138, 2, 2, 2, 2, 2, 2,143,143, + 143,143,143,143, 2,143,143, 2,143,143,143,143,143,143,143,143, + 143,143,143,143,143,143,143,143,143,143,143,143,143, 2,143,143, + 2,143,143,143,143,143,143, 2, 2, 2, 2, 2, 2, 2,143,143, + 2, 2, 2, 2, 2, 2,145,145,145,145,145,145,145,145,145, 2, + 2, 2, 2, 2, 2, 2,163,163,163,163,163,163,163,163,163, 2, + 163,163,163,163,163,163,163,163,163, 2, 2, 2,163,163,163,163, + 163, 2, 2, 2, 2, 2, 86, 2, 2, 2, 2, 2, 2, 2, 22, 22, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 22, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 2, 2, 2, 2, 2, 2, 63, 63, + 63, 63, 63, 63, 63, 2, 63, 63, 63, 63, 63, 2, 2, 2, 63, 63, + 63, 63, 2, 2, 2, 2,157,157,157,157,157,157,157,157,157,157, + 157, 2, 2, 2, 2, 2, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 2, 2, 80, 80, 80, 2, 2, 2, 2, 2,127,127, + 127,127,127,127,127,127,127,127,127,127,127,127,127, 2,166,166, + 166,166,166,166,166,166,166,166, 2, 2, 2, 2, 2, 2, 79, 2, 2, 2, 2, 2, 2, 2,115,115,115,115,115,115,115,115,115,115, 115,115,115,115,115, 2,115,115, 2, 2, 2, 2,115,115,159,159, 159,159,159,159,159,159,159,159,159,159,159,159,159, 2,159,159, 2, 2, 2, 2, 2, 2,103,103,103,103,103,103,103,103,103,103, 103,103,103,103, 2, 2,119,119,119,119,119,119,119,119,119,119, 119,119,119,119, 2, 2,119,119, 2,119,119,119,119,119, 2, 2, - 2, 2, 2,119,119,119,146,146,146,146,146,146,146,146,146,146, + 2, 2, 2,119,119,119,167,167,167,167,167,167,167,167,167,167, + 2, 2, 2, 2, 2, 2,146,146,146,146,146,146,146,146,146,146, 146, 2, 2, 2, 2, 2, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 2, 2, 2, 2, 99, 2, 2, 2, 2, 2, 2, 2, 99,136,139, 13, 13,155, 2, 2, 2,136,136,136,136,136,136,136,136,155,155, - 155,155,155,155,155,155,155,155,155,155,155,155, 2, 2,136, 2, - 2, 2, 2, 2, 2, 2, 17, 17, 17, 17, 2, 17, 17, 17, 17, 17, - 17, 17, 2, 17, 17, 2, 17, 15, 15, 15, 15, 15, 15, 15, 17, 17, - 17, 2, 2, 2, 2, 2, 2, 2, 15, 2, 2, 2, 2, 2, 15, 15, - 15, 2, 2, 17, 2, 2, 2, 2, 2, 2, 17, 17, 17, 17,139,139, - 139,139,139,139,139,139,139,139,139,139, 2, 2, 2, 2,105,105, - 105,105,105,105,105,105,105,105,105, 2, 2, 2, 2, 2,105,105, - 105,105,105, 2, 2, 2,105, 2, 2, 2, 2, 2, 2, 2,105,105, - 2, 2,105,105,105,105, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, - 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 2, 2, - 0, 2, 2, 0, 0, 2, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, - 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, - 0, 2, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, - 0, 0, 0, 2, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, - 0, 0, 0, 0, 0, 0,131,131,131,131,131,131,131,131,131,131, - 131,131, 2, 2, 2, 2, 2, 2, 2,131,131,131,131,131, 2,131, - 131,131,131,131,131,131, 2, 2, 2, 2, 2, 19, 19, 19, 56, 56, - 56, 56, 56, 56, 56, 2, 56, 2, 2, 56, 56, 56, 56, 56, 56, 56, - 2, 56, 56, 2, 56, 56, 56, 56, 56, 2, 2, 2, 2, 2, 6, 6, - 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6,151,151, - 151,151,151,151,151,151,151,151,151,151,151, 2, 2, 2,151,151, - 151,151,151,151, 2, 2,151,151, 2, 2, 2, 2,151,151,160,160, - 160,160,160,160,160,160,160,160,160,160,160,160,160, 2,152,152, - 152,152,152,152,152,152,152,152, 2, 2, 2, 2, 2,152,164,164, - 164,164,164,164,164,164,164,164, 2, 2, 2, 2, 2, 2, 30, 30, - 30, 30, 2, 30, 30, 2,113,113,113,113,113,113,113,113,113,113, - 113,113,113, 2, 2,113,113,113,113,113,113,113,113, 2,132,132, - 132,132,132,132,132,132,132,132,132,132, 2, 2, 2, 2,132,132, - 2, 2, 2, 2,132,132, 3, 3, 3, 3, 2, 3, 3, 3, 2, 3, - 3, 2, 3, 2, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 2, 3, 3, 3, 3, 2, 3, 2, 3, 2, 2, 2, 2, 2, 2, - 3, 2, 2, 2, 2, 3, 2, 3, 2, 3, 2, 3, 3, 3, 2, 3, - 2, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 2, 3, 2, 3, 3, - 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 3, - 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 2, 2, 0, 0, 15, 0, + 155,155,155,155,155,155,155,155,155,155,155,155, 2, 2, 2, 2, + 2, 2, 2, 2, 2,155,136, 2, 2, 2, 2, 2, 2, 2, 17, 17, + 17, 17, 2, 17, 17, 17, 17, 17, 17, 17, 2, 17, 17, 2, 17, 15, + 15, 15, 15, 15, 15, 15, 17, 17, 17, 2, 2, 2, 2, 2, 2, 2, + 15, 2, 2, 2, 2, 2, 15, 15, 15, 2, 2, 17, 2, 2, 2, 2, + 2, 2, 17, 17, 17, 17,139,139,139,139,139,139,139,139,139,139, + 139,139, 2, 2, 2, 2,105,105,105,105,105,105,105,105,105,105, + 105, 2, 2, 2, 2, 2,105,105,105,105,105, 2, 2, 2,105, 2, + 2, 2, 2, 2, 2, 2,105,105, 2, 2,105,105,105,105, 1, 1, + 1, 1, 1, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 0, 0, 2, 2, 0, 2, 2, 0, 0, 2, 2, 0, + 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 2, 2, 2, + 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0,131,131, + 131,131,131,131,131,131,131,131,131,131, 2, 2, 2, 2, 2, 2, + 2,131,131,131,131,131, 2,131,131,131,131,131,131,131, 2, 2, + 2, 2, 2, 19, 19, 19, 56, 56, 56, 56, 56, 56, 56, 2, 56, 2, + 2, 56, 56, 56, 56, 56, 56, 56, 2, 56, 56, 2, 56, 56, 56, 56, + 56, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 6,151,151,151,151,151,151,151,151,151,151, + 151,151,151, 2, 2, 2,151,151,151,151,151,151, 2, 2,151,151, + 2, 2, 2, 2,151,151,160,160,160,160,160,160,160,160,160,160, + 160,160,160,160,160, 2,152,152,152,152,152,152,152,152,152,152, + 2, 2, 2, 2, 2,152,164,164,164,164,164,164,164,164,164,164, + 2, 2, 2, 2, 2, 2,168,168,168,168,168,168,168,168,168,168, + 168, 2, 2, 2, 2,168, 30, 30, 30, 30, 2, 30, 30, 2,113,113, + 113,113,113,113,113,113,113,113,113,113,113, 2, 2,113,113,113, + 113,113,113,113,113, 2,132,132,132,132,132,132,132,132,132,132, + 132,132, 2, 2, 2, 2,132,132, 2, 2, 2, 2,132,132, 3, 3, + 3, 3, 2, 3, 3, 3, 2, 3, 3, 2, 3, 2, 2, 3, 2, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 2, 3, + 2, 3, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 3, 2, 3, + 2, 3, 2, 3, 3, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, + 3, 3, 3, 2, 3, 2, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 2, 2, 2, 2, 2, 3, 3, 3, 2, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 15, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 2, 2, - 2, 0, 0, 0, 0, 0, 13, 2, 2, 2, 2, 2, 2, 2, 13, 13, + 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, + 0, 0, 0, 2, 2, 0, 13, 2, 2, 2, 2, 2, 2, 2, 13, 13, 13, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, 10, 9, 11, 12, 13, 9, 9, 9, 14, 9, 9, 15, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -2123,13 +2113,14 @@ _hb_ucd_u8[17884] = 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 16, 17, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 18, 19, 20, 9, 21, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 18, 9, 9, 9, 9, 9, 19, 20, 21, 9, 22, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 23, 9, + 9, 9, 9, 9, 24, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 25, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 22, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -2137,8 +2128,7 @@ _hb_ucd_u8[17884] = 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 23, 24, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -2175,23 +2165,29 @@ _hb_ucd_u8[17884] = 132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147, 148,149,150,151,152,153,154,155,156,157, 0, 0, 0,158,159,160, 161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,162,163, 0, 0, 0, 0, 0, 0, 0,164, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,162, 0,163, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,164,165, 0, 0, 0, 0, 0, 0, 0,166, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 167, 0, 0, 0,168,169, 0, 0,170, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,171, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,172, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,173, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,174, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,166, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,167, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0,169,170, 0, 0, 0, 0,171,172, 0, 0, 0,173,174,175,176, - 177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192, - 193,194,195,196,197,198,199,200,201,202,203,204,205,206, 0, 0, + 0,176,177, 0, 0, 0, 0,178,179, 0, 0, 0,180,181,182,183, + 184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199, + 200,201,202,203,204,205,206,207,208,209,210,211,212,213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, }; static const uint16_t -_hb_ucd_u16[9344] = +_hb_ucd_u16[10400] = { 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 10, 11, 12, 13, 13, 13, 14, 15, 13, 13, 16, 17, 18, 19, 20, 21, 22, 13, 23, @@ -2210,9 +2206,10 @@ _hb_ucd_u16[9344] = 136, 48, 48, 137, 138, 139, 140, 140, 141, 48, 142, 143, 144, 145, 140, 140, 146, 147, 148, 149, 150, 48, 151, 152, 153, 154, 32, 155, 156, 157, 140, 140, 48, 48, 158, 159, 160, 161, 162, 163, 164, 165, 9, 9, 166, 11, 11, 167, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 168, 169, 48, 48, - 168, 48, 48, 170, 171, 172, 48, 48, 48, 171, 48, 48, 48, 173, 174, 175, - 48, 176, 9, 9, 9, 9, 9, 177, 178, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 168, 169, 48, 48, 168, 48, 48, 170, 171, 172, 48, 48, + 48, 171, 48, 48, 48, 173, 174, 175, 48, 176, 9, 9, 9, 9, 9, 177, + 178, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 179, 48, 180, 181, 48, 48, 48, 48, 182, 183, 48, 184, 48, 185, 48, 186, 187, 188, 48, 48, 48, 189, 190, 191, 192, 193, 194, 192, 48, 48, 195, 48, 48, 196, 197, 48, 198, 48, 48, 48, 48, 199, @@ -2225,28 +2222,34 @@ _hb_ucd_u16[9344] = 241, 242, 241, 241, 242, 243, 241, 244, 245, 245, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 272, 273, 274, 275, 209, 276, 277, 209, 278, - 279, 279, 279, 279, 279, 279, 279, 279, 280, 209, 281, 209, 209, 209, 209, 282, - 209, 283, 279, 284, 209, 285, 286, 209, 209, 209, 287, 140, 288, 140, 271, 271, - 271, 289, 209, 209, 209, 209, 290, 271, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 291, 292, 209, 209, 293, 209, 209, 209, 209, 209, 209, 294, 209, - 209, 209, 209, 209, 209, 209, 295, 296, 271, 297, 209, 209, 298, 279, 299, 279, - 300, 301, 279, 279, 279, 302, 279, 303, 209, 209, 209, 279, 304, 209, 209, 305, - 209, 306, 209, 209, 209, 209, 209, 209, 9, 9, 9, 11, 11, 11, 307, 308, - 13, 13, 13, 13, 13, 13, 309, 310, 11, 11, 311, 48, 48, 48, 312, 313, - 48, 314, 315, 315, 315, 315, 32, 32, 316, 317, 318, 319, 320, 321, 140, 140, - 209, 322, 209, 209, 209, 209, 209, 323, 209, 209, 209, 209, 209, 324, 140, 209, - 325, 326, 327, 328, 136, 48, 48, 48, 48, 329, 178, 48, 48, 48, 48, 330, - 331, 48, 48, 136, 48, 48, 48, 48, 200, 332, 48, 48, 209, 209, 333, 48, - 209, 334, 335, 209, 336, 337, 209, 209, 335, 209, 209, 337, 209, 209, 209, 209, - 48, 48, 48, 48, 209, 209, 209, 209, 48, 338, 48, 48, 48, 48, 48, 48, - 151, 209, 209, 209, 287, 48, 48, 229, 339, 48, 340, 140, 13, 13, 341, 342, - 13, 343, 48, 48, 48, 48, 344, 345, 31, 346, 347, 348, 13, 13, 13, 349, - 350, 351, 352, 353, 354, 355, 140, 356, 357, 48, 358, 359, 48, 48, 48, 360, - 361, 48, 48, 362, 363, 192, 32, 364, 64, 48, 365, 48, 366, 367, 48, 151, - 76, 48, 48, 368, 369, 370, 371, 372, 48, 48, 373, 374, 375, 376, 48, 377, - 48, 48, 48, 378, 379, 380, 381, 382, 383, 384, 315, 11, 11, 385, 386, 11, - 11, 11, 11, 11, 48, 48, 387, 192, 48, 48, 388, 48, 389, 48, 48, 206, - 390, 390, 390, 390, 390, 390, 390, 390, 391, 391, 391, 391, 391, 391, 391, 391, + 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, + 280, 209, 281, 209, 209, 209, 209, 282, 209, 283, 279, 284, 209, 285, 286, 209, + 209, 209, 176, 140, 287, 140, 271, 271, 271, 288, 209, 209, 209, 209, 289, 271, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 290, 291, 209, 209, 292, + 209, 209, 209, 209, 209, 209, 293, 209, 209, 209, 209, 209, 209, 209, 209, 209, + 209, 209, 209, 209, 209, 209, 294, 295, 271, 296, 209, 209, 297, 279, 298, 279, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, + 279, 279, 279, 279, 279, 279, 279, 279, 299, 300, 279, 279, 279, 301, 279, 302, + 209, 209, 209, 279, 303, 209, 209, 304, 209, 305, 209, 209, 209, 209, 209, 209, + 9, 9, 9, 11, 11, 11, 306, 307, 13, 13, 13, 13, 13, 13, 308, 309, + 11, 11, 310, 48, 48, 48, 311, 312, 48, 313, 314, 314, 314, 314, 32, 32, + 315, 316, 317, 318, 319, 320, 140, 140, 209, 321, 209, 209, 209, 209, 209, 322, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 323, 140, 209, + 324, 325, 326, 327, 136, 48, 48, 48, 48, 328, 178, 48, 48, 48, 48, 329, + 330, 48, 48, 136, 48, 48, 48, 48, 200, 331, 48, 48, 209, 209, 332, 48, + 209, 333, 334, 209, 335, 336, 209, 209, 334, 209, 209, 336, 209, 209, 209, 209, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 209, 209, 209, 209, + 48, 337, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 151, 209, 209, 209, 338, 48, 48, 229, + 339, 48, 340, 140, 13, 13, 341, 342, 13, 343, 48, 48, 48, 48, 344, 345, + 31, 346, 347, 348, 13, 13, 13, 349, 350, 351, 352, 353, 354, 355, 140, 356, + 357, 48, 358, 359, 48, 48, 48, 360, 361, 48, 48, 362, 363, 192, 32, 364, + 64, 48, 365, 48, 366, 367, 48, 151, 76, 48, 48, 368, 369, 370, 371, 372, + 48, 48, 373, 374, 375, 376, 48, 377, 48, 48, 48, 378, 379, 380, 381, 382, + 383, 384, 314, 11, 11, 385, 386, 11, 11, 11, 11, 11, 48, 48, 387, 192, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 388, 48, 389, 48, 48, 206, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 48, 48, 48, 48, 48, 48, 204, 48, 48, 48, 48, 48, 48, 207, 140, 140, 392, 393, 394, 395, 396, 48, 48, 48, 48, 48, 48, 397, 398, 399, 48, 48, 48, 48, 48, 400, 209, 48, 48, 48, 48, 401, 48, 48, 402, 140, 140, 403, @@ -2257,108 +2260,204 @@ _hb_ucd_u16[9344] = 140, 140, 140, 140, 140, 140, 140, 140, 48, 151, 48, 48, 48, 100, 429, 430, 48, 48, 431, 48, 432, 48, 48, 433, 48, 434, 48, 48, 435, 436, 140, 140, 9, 9, 437, 11, 11, 48, 48, 48, 48, 204, 192, 9, 9, 438, 11, 439, - 48, 48, 440, 48, 48, 48, 441, 442, 442, 443, 444, 445, 140, 140, 140, 140, - 48, 48, 48, 314, 48, 199, 440, 140, 446, 27, 27, 447, 140, 140, 140, 140, + 48, 48, 440, 48, 48, 48, 441, 442, 442, 443, 444, 445, 48, 48, 48, 388, + 48, 48, 48, 313, 48, 199, 440, 140, 446, 27, 27, 447, 140, 140, 140, 140, 448, 48, 48, 449, 48, 450, 48, 451, 48, 200, 452, 140, 140, 140, 48, 453, 48, 454, 48, 455, 140, 140, 140, 140, 48, 48, 48, 456, 271, 457, 271, 271, 458, 459, 48, 460, 461, 462, 48, 463, 48, 464, 140, 140, 465, 48, 466, 467, 48, 48, 48, 468, 48, 469, 48, 470, 48, 471, 472, 140, 140, 140, 140, 140, 48, 48, 48, 48, 196, 140, 140, 140, 9, 9, 9, 473, 11, 11, 11, 474, - 48, 48, 475, 192, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 271, 476, - 48, 48, 477, 478, 140, 140, 140, 479, 48, 464, 480, 48, 62, 481, 140, 48, - 482, 140, 140, 48, 483, 140, 48, 314, 484, 48, 48, 485, 486, 457, 487, 488, - 222, 48, 48, 489, 490, 48, 196, 192, 491, 48, 492, 493, 494, 48, 48, 495, - 222, 48, 48, 496, 497, 498, 499, 500, 48, 97, 501, 502, 503, 140, 140, 140, - 504, 505, 506, 48, 48, 507, 508, 192, 509, 83, 84, 510, 511, 512, 513, 514, - 48, 48, 48, 515, 516, 517, 478, 140, 48, 48, 48, 518, 519, 192, 140, 140, - 48, 48, 520, 521, 522, 523, 140, 140, 48, 48, 48, 524, 525, 192, 526, 140, - 48, 48, 527, 528, 192, 140, 140, 140, 48, 173, 529, 530, 314, 140, 140, 140, - 48, 48, 501, 531, 140, 140, 140, 140, 140, 140, 9, 9, 11, 11, 148, 532, - 533, 534, 48, 535, 536, 192, 140, 140, 140, 140, 537, 48, 48, 538, 539, 140, - 540, 48, 48, 541, 542, 543, 48, 48, 544, 545, 546, 48, 48, 48, 48, 196, - 547, 140, 140, 140, 140, 140, 140, 140, 84, 48, 520, 548, 549, 148, 175, 550, - 48, 551, 552, 553, 140, 140, 140, 140, 554, 48, 48, 555, 556, 192, 557, 48, - 558, 559, 192, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 560, - 561, 115, 48, 562, 563, 192, 140, 140, 140, 140, 140, 100, 271, 564, 565, 566, - 48, 207, 140, 140, 140, 140, 140, 140, 272, 272, 272, 272, 272, 272, 567, 568, - 48, 48, 48, 48, 388, 140, 140, 140, 140, 48, 48, 48, 48, 48, 48, 569, - 48, 48, 48, 570, 571, 572, 140, 140, 48, 48, 48, 48, 314, 140, 140, 140, - 48, 48, 48, 196, 48, 200, 370, 48, 48, 48, 48, 200, 192, 48, 204, 573, - 48, 48, 48, 574, 575, 576, 577, 578, 48, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 9, 9, 11, 11, 271, 579, 140, 140, 140, 140, 140, 140, - 48, 48, 48, 48, 580, 581, 582, 582, 583, 584, 140, 140, 140, 140, 585, 586, - 48, 48, 48, 48, 48, 48, 48, 440, 48, 48, 48, 48, 48, 199, 140, 140, - 196, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 587, - 48, 48, 588, 589, 140, 590, 591, 48, 48, 48, 48, 48, 48, 48, 48, 206, - 48, 48, 48, 48, 48, 48, 71, 151, 196, 592, 593, 140, 140, 140, 140, 140, - 32, 32, 594, 32, 595, 209, 209, 209, 209, 209, 209, 209, 323, 140, 140, 140, - 209, 209, 209, 209, 209, 209, 209, 324, 209, 209, 596, 209, 209, 209, 597, 598, - 599, 209, 600, 209, 209, 209, 288, 140, 209, 209, 209, 209, 601, 140, 140, 140, - 140, 140, 140, 140, 271, 602, 271, 602, 209, 209, 209, 209, 209, 287, 271, 461, - 9, 603, 11, 604, 605, 606, 241, 9, 607, 608, 609, 610, 611, 9, 603, 11, - 612, 613, 11, 614, 615, 616, 617, 9, 618, 11, 9, 603, 11, 604, 605, 11, - 241, 9, 607, 617, 9, 618, 11, 9, 603, 11, 619, 9, 620, 621, 622, 623, - 11, 624, 9, 625, 626, 627, 628, 11, 629, 9, 630, 11, 631, 632, 632, 632, - 32, 32, 32, 633, 32, 32, 634, 635, 636, 637, 45, 140, 140, 140, 140, 140, - 638, 639, 640, 140, 140, 140, 140, 140, 641, 642, 643, 27, 27, 27, 644, 140, - 645, 140, 140, 140, 140, 140, 140, 140, 48, 48, 151, 646, 647, 140, 140, 140, - 140, 48, 648, 140, 48, 48, 649, 650, 140, 140, 140, 140, 140, 48, 651, 192, - 140, 140, 140, 140, 140, 140, 652, 200, 48, 48, 48, 48, 653, 595, 140, 140, - 9, 9, 607, 11, 654, 370, 140, 140, 140, 140, 140, 140, 140, 140, 140, 499, - 271, 271, 655, 656, 140, 140, 140, 140, 499, 271, 657, 658, 140, 140, 140, 140, - 659, 48, 660, 661, 662, 663, 664, 665, 666, 206, 667, 206, 140, 140, 140, 668, - 209, 209, 669, 209, 209, 209, 209, 209, 209, 323, 334, 670, 670, 670, 209, 324, - 671, 209, 209, 209, 209, 209, 209, 209, 209, 209, 672, 140, 140, 140, 673, 209, - 674, 209, 209, 669, 675, 676, 324, 140, 209, 209, 209, 209, 209, 209, 209, 677, - 209, 209, 209, 209, 209, 678, 426, 426, 209, 209, 209, 209, 209, 209, 209, 679, - 209, 209, 209, 209, 209, 176, 669, 427, 669, 209, 209, 209, 680, 176, 209, 209, - 680, 209, 672, 676, 140, 140, 140, 140, 209, 209, 209, 209, 209, 323, 672, 426, - 675, 209, 209, 681, 682, 669, 675, 675, 209, 683, 209, 209, 288, 140, 140, 192, - 48, 48, 48, 48, 48, 48, 140, 140, 48, 48, 48, 207, 48, 48, 48, 48, - 48, 204, 48, 48, 48, 48, 48, 48, 48, 48, 478, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 100, 48, 48, 48, 48, 48, 48, 204, 140, 140, - 48, 204, 140, 140, 140, 140, 140, 140, 48, 48, 48, 48, 71, 48, 48, 48, - 48, 48, 48, 140, 140, 140, 140, 140, 684, 140, 570, 570, 570, 570, 570, 570, + 48, 48, 475, 192, 476, 9, 477, 11, 478, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 271, 479, 48, 48, 480, 481, 482, 140, 140, 483, + 48, 464, 484, 48, 62, 485, 140, 48, 486, 140, 140, 48, 487, 140, 48, 313, + 488, 48, 48, 489, 490, 457, 491, 492, 222, 48, 48, 493, 494, 48, 196, 192, + 495, 48, 496, 497, 498, 48, 48, 499, 222, 48, 48, 500, 501, 502, 503, 504, + 48, 97, 505, 506, 507, 140, 140, 140, 508, 509, 510, 48, 48, 511, 512, 192, + 513, 83, 84, 514, 515, 516, 517, 518, 519, 48, 48, 520, 521, 522, 523, 140, + 48, 48, 48, 524, 525, 526, 481, 140, 48, 48, 48, 527, 528, 192, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 529, 530, 531, 532, 140, 140, + 48, 48, 48, 533, 534, 192, 535, 140, 48, 48, 536, 537, 192, 538, 539, 140, + 48, 540, 541, 542, 313, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 48, 48, 505, 543, 140, 140, 140, 140, 140, 140, 9, 9, 11, 11, 148, 544, + 545, 546, 48, 547, 548, 192, 140, 140, 140, 140, 549, 48, 48, 550, 551, 140, + 552, 48, 48, 553, 554, 555, 48, 48, 556, 557, 558, 48, 48, 48, 48, 196, + 559, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 560, 192, + 84, 48, 529, 561, 562, 148, 175, 563, 48, 564, 565, 566, 140, 140, 140, 140, + 567, 48, 48, 568, 569, 192, 570, 48, 571, 572, 192, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 573, + 574, 115, 48, 575, 576, 577, 140, 140, 140, 140, 140, 100, 271, 578, 579, 580, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 207, 140, 140, 140, 140, 140, 140, + 272, 272, 272, 272, 272, 272, 581, 582, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 388, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 48, 48, 48, 48, 583, + 48, 48, 48, 584, 585, 586, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 71, + 48, 48, 48, 48, 313, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 48, 587, 588, 192, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 48, 48, 48, 196, 48, 200, 370, 48, 48, 48, 48, 200, 192, 48, 204, 589, + 48, 48, 48, 590, 591, 592, 593, 594, 48, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 595, 48, 596, 192, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 9, 9, 11, 11, 271, 597, 140, 140, 140, 140, 140, 140, + 48, 48, 48, 48, 598, 599, 600, 600, 601, 602, 140, 140, 140, 140, 603, 604, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 440, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 199, 140, 605, + 196, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 606, + 48, 48, 607, 608, 140, 609, 610, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 206, + 48, 48, 48, 48, 48, 48, 71, 151, 196, 611, 612, 140, 140, 140, 140, 140, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 192, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 322, 140, 140, 140, 140, + 32, 32, 613, 32, 614, 209, 209, 209, 209, 209, 209, 209, 322, 140, 140, 140, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 323, + 209, 209, 615, 209, 209, 209, 616, 617, 618, 209, 619, 209, 209, 209, 287, 140, + 209, 209, 209, 209, 620, 140, 140, 140, 140, 140, 140, 140, 271, 621, 271, 621, + 209, 209, 209, 209, 209, 338, 271, 461, 140, 140, 140, 140, 140, 140, 140, 140, + 9, 622, 11, 623, 624, 625, 241, 9, 626, 627, 628, 629, 630, 9, 622, 11, + 631, 632, 11, 633, 634, 635, 636, 9, 637, 11, 9, 622, 11, 623, 624, 11, + 241, 9, 626, 636, 9, 637, 11, 9, 622, 11, 638, 9, 639, 640, 641, 642, + 11, 643, 9, 644, 645, 646, 647, 11, 648, 9, 649, 11, 650, 538, 538, 538, + 32, 32, 32, 651, 32, 32, 652, 653, 654, 655, 45, 140, 140, 140, 140, 140, + 656, 657, 658, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 659, 660, 661, 27, 27, 27, 662, 140, 663, 140, 140, 140, 140, 140, 140, 140, + 48, 48, 151, 664, 665, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 666, 140, 48, 48, 667, 668, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 669, 192, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 587, 670, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 671, 200, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 672, 614, 140, 140, + 9, 9, 626, 11, 673, 370, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 503, 271, 271, 674, 675, 140, 140, 140, 140, + 503, 271, 676, 677, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 678, 48, 679, 680, 681, 682, 683, 684, 685, 206, 686, 206, 140, 140, 140, 687, + 209, 209, 688, 209, 209, 209, 209, 209, 209, 322, 333, 689, 689, 689, 209, 323, + 690, 209, 209, 209, 209, 209, 209, 209, 209, 209, 691, 140, 140, 140, 692, 209, + 693, 209, 209, 688, 694, 695, 323, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 696, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 697, 426, 426, + 209, 209, 209, 209, 209, 209, 209, 698, 209, 209, 209, 209, 209, 176, 688, 427, + 688, 209, 209, 209, 699, 176, 209, 209, 699, 209, 691, 688, 695, 140, 140, 140, + 209, 209, 209, 209, 209, 322, 691, 426, 700, 209, 209, 209, 701, 702, 176, 694, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 703, 209, 209, 209, 209, 209, 192, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 140, 140, + 48, 48, 48, 207, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 204, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 481, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 100, 48, + 48, 48, 48, 48, 48, 204, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 48, 204, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 48, 48, 48, 48, 71, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 140, 140, 140, 140, 140, + 704, 140, 584, 584, 584, 584, 584, 584, 140, 140, 140, 140, 140, 140, 140, 140, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 140, - 391, 391, 391, 391, 391, 391, 391, 685, 391, 391, 391, 391, 391, 391, 391, 686, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 705, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 706, + 0, 1, 2, 3, 4, 4, 4, 4, 4, 4, 5, 6, 7, 8, 9, 10, + 11, 11, 12, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 57, 58, 59, 60, 60, 60, 60, 61, 62, 63, 64, 65, 66, 67, 68, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 7, 4, 4, 4, 4, 99, 100, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 110, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 112, 112, 112, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113, 114, 0, + 115, 116, 117, 118, 119, 120, 121, 122, 0, 123, 124, 125, 126, 126, 126, 127, + 128, 129, 130, 131, 132, 60, 133, 134, 135, 136, 0, 137, 138, 139, 0, 0, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 0, 126, 126, 126, 126, 126, 126, 126, 126, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 141, 142, 143, 143, 143, 143, 144, 11, 145, 146, 147, 4, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 166, 167, + 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, + 168, 168, 168, 168, 126, 126, 126, 126, 126, 169, 126, 170, 171, 172, 19, 173, + 19, 19, 19, 19, 174, 19, 175, 176, 177, 178, 19, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, 168, 168, 192, 193, 194, 195, 196, 197, + 198, 199, 200, 201, 202, 203, 204, 205, 206, 206, 206, 206, 207, 208, 209, 168, + 210, 211, 212, 213, 214, 168, 215, 216, 217, 218, 219, 220, 221, 222, 223, 168, + 224, 225, 226, 227, 228, 229, 230, 168, 168, 231, 232, 233, 234, 235, 236, 237, + 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, + 254, 255, 256, 257, 168, 168, 258, 259, 260, 261, 262, 263, 264, 265, 168, 168, + 266, 168, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 168, 168, 278, + 279, 280, 281, 168, 282, 283, 284, 168, 168, 168, 168, 285, 286, 287, 288, 289, + 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 291, 168, + 290, 292, 290, 290, 290, 293, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, + 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 294, 295, + 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, + 296, 297, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, + 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 298, + 299, 299, 299, 299, 299, 299, 299, 299, 299, 300, 168, 168, 168, 168, 168, 168, + 168, 168, 168, 168, 301, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, + 302, 302, 302, 302, 302, 302, 302, 302, 303, 304, 305, 306, 307, 308, 309, 168, + 168, 168, 168, 168, 168, 310, 168, 168, 168, 311, 312, 168, 313, 314, 315, 316, + 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, + 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 318, + 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 319, 319, 319, 319, + 319, 319, 319, 320, 321, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, + 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 322, + 323, 324, 324, 324, 325, 326, 327, 327, 327, 327, 327, 328, 168, 168, 168, 168, + 329, 330, 331, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, + 0, 0, 0, 332, 0, 0, 0, 0, 0, 0, 333, 168, 334, 335, 0, 336, + 0, 0, 0, 337, 338, 339, 340, 341, 189, 342, 168, 343, 0, 344, 168, 168, + 0, 345, 346, 347, 348, 349, 0, 0, 0, 0, 350, 0, 0, 0, 0, 351, + 352, 352, 352, 352, 352, 352, 352, 352, 352, 352, 353, 168, 168, 168, 168, 168, + 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 354, 168, 168, 168, + 355, 356, 357, 168, 358, 359, 168, 168, 168, 168, 360, 361, 168, 168, 168, 168, + 168, 168, 168, 362, 168, 168, 168, 363, 168, 168, 168, 168, 168, 168, 168, 364, + 365, 365, 365, 366, 367, 368, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, + 168, 369, 370, 168, 371, 168, 168, 168, 372, 373, 374, 375, 168, 168, 168, 168, + 376, 0, 377, 378, 0, 0, 379, 380, 381, 382, 168, 168, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 383, 0, 384, 0, 385, + 386, 387, 388, 389, 0, 0, 0, 0, 0, 390, 391, 392, 0, 0, 393, 332, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 394, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 395, 126, 126, 126, + 396, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 397, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 398, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 399, 168, 168, 168, 168, 168, 168, + 126, 126, 126, 126, 126, 126, 126, 126, 399, 168, 168, 168, 168, 168, 168, 168, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 400, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 401, 168, + 402, 0, 168, 168, 7, 7, 7, 403, 0, 1, 2, 3, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 3, 1, 2, 2, 3, 0, 0, 0, 0, 0, 4, 0, 4, 2, 2, 5, 2, 2, 2, 5, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 6, 0, 0, 0, 0, 7, 8, 0, 0, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 11, - 12, 13, 14, 14, 15, 14, 14, 14, 14, 14, 14, 14, 16, 17, 14, 14, - 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, - 19, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, - 18, 18, 18, 18, 18, 18, 20, 21, 21, 21, 22, 20, 21, 21, 21, 21, - 21, 23, 24, 25, 25, 25, 25, 25, 25, 26, 25, 25, 25, 27, 28, 26, - 29, 30, 31, 32, 31, 31, 31, 31, 33, 34, 35, 31, 31, 31, 36, 31, - 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 29, 31, 31, 31, 31, - 37, 38, 37, 37, 37, 37, 37, 37, 37, 39, 31, 31, 31, 31, 31, 31, - 40, 40, 40, 40, 40, 40, 41, 26, 42, 42, 42, 42, 42, 42, 42, 43, - 44, 44, 44, 44, 44, 45, 44, 46, 47, 47, 47, 48, 37, 49, 31, 31, - 31, 50, 51, 31, 31, 31, 31, 31, 31, 31, 31, 31, 52, 31, 31, 31, - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54, 53, 55, 53, 53, 53, - 56, 57, 58, 59, 59, 60, 61, 62, 57, 63, 64, 65, 66, 59, 59, 67, - 68, 69, 70, 71, 71, 72, 73, 74, 69, 75, 76, 77, 78, 71, 79, 26, - 80, 81, 82, 83, 83, 84, 85, 86, 81, 87, 88, 26, 89, 83, 90, 91, - 92, 93, 94, 95, 95, 96, 97, 98, 93, 99, 100, 101, 102, 95, 95, 26, - 103, 104, 105, 106, 107, 104, 108, 109, 104, 105, 110, 26, 111, 108, 108, 112, - 113, 114, 115, 113, 113, 115, 113, 116, 114, 117, 118, 119, 120, 113, 121, 113, - 122, 123, 124, 122, 122, 124, 125, 126, 123, 127, 128, 128, 129, 122, 130, 26, - 131, 132, 133, 131, 131, 131, 131, 131, 132, 133, 134, 131, 135, 131, 131, 131, - 136, 137, 138, 139, 137, 137, 140, 141, 138, 142, 143, 137, 144, 137, 145, 26, - 146, 147, 147, 147, 147, 147, 147, 148, 147, 147, 147, 149, 26, 26, 26, 26, - 150, 151, 152, 152, 153, 152, 152, 154, 155, 156, 152, 157, 26, 26, 26, 26, - 158, 158, 158, 158, 158, 158, 158, 158, 158, 159, 158, 158, 158, 160, 159, 158, - 158, 158, 158, 159, 158, 158, 158, 161, 158, 161, 162, 163, 26, 26, 26, 26, - 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, + 0, 0, 0, 0, 7, 8, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 10, 11, 12, 13, 14, 14, 15, 14, 14, 14, + 14, 14, 14, 14, 16, 17, 14, 14, 18, 18, 18, 18, 18, 18, 18, 18, + 19, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 20, 21, + 21, 21, 22, 20, 21, 21, 21, 21, 21, 23, 24, 25, 25, 25, 25, 25, + 25, 26, 25, 25, 25, 27, 28, 26, 29, 30, 31, 32, 31, 31, 31, 31, + 33, 34, 35, 31, 31, 31, 36, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 29, 31, 31, 31, 31, 37, 38, 37, 37, 37, 37, 37, 37, + 37, 39, 31, 31, 31, 31, 31, 31, 40, 40, 40, 40, 40, 40, 41, 26, + 42, 42, 42, 42, 42, 42, 42, 43, 44, 44, 44, 44, 44, 45, 44, 46, + 47, 47, 47, 48, 37, 49, 31, 31, 31, 50, 51, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 52, 31, 31, 31, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 54, 53, 55, 53, 53, 53, 56, 57, 58, 59, 59, 60, 61, 62, + 57, 63, 64, 65, 66, 59, 59, 67, 68, 69, 70, 71, 71, 72, 73, 74, + 69, 75, 76, 77, 78, 71, 79, 26, 80, 81, 82, 83, 83, 84, 85, 86, + 81, 87, 88, 26, 89, 83, 90, 91, 92, 93, 94, 95, 95, 96, 97, 98, + 93, 99, 100, 101, 102, 95, 95, 26, 103, 104, 105, 106, 107, 104, 108, 109, + 104, 105, 110, 26, 111, 108, 108, 112, 113, 114, 115, 113, 113, 115, 113, 116, + 114, 117, 118, 119, 120, 113, 121, 113, 122, 123, 124, 122, 122, 124, 125, 126, + 123, 127, 128, 128, 129, 122, 130, 26, 131, 132, 133, 131, 131, 131, 131, 131, + 132, 133, 134, 131, 135, 131, 131, 131, 136, 137, 138, 139, 137, 137, 140, 141, + 138, 142, 143, 137, 144, 137, 145, 26, 146, 147, 147, 147, 147, 147, 147, 148, + 147, 147, 147, 149, 26, 26, 26, 26, 150, 151, 152, 152, 153, 152, 152, 154, + 155, 156, 152, 157, 26, 26, 26, 26, 158, 158, 158, 158, 158, 158, 158, 158, + 158, 159, 158, 158, 158, 160, 159, 158, 158, 158, 158, 159, 158, 158, 158, 161, + 158, 161, 162, 163, 26, 26, 26, 26, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, 166, 167, 165, 165, 165, 165, 165, 168, - 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, - 170, 170, 170, 170, 170, 170, 170, 170, 170, 171, 172, 171, 170, 170, 170, 170, - 170, 171, 170, 170, 170, 170, 171, 172, 171, 170, 172, 170, 170, 170, 170, 170, - 170, 170, 171, 170, 170, 170, 170, 170, 170, 170, 170, 173, 170, 170, 170, 174, - 170, 170, 170, 175, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 177, 177, - 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, + 169, 169, 169, 169, 169, 169, 169, 169, 170, 170, 170, 170, 170, 170, 170, 170, + 170, 171, 172, 171, 170, 170, 170, 170, 170, 171, 170, 170, 170, 170, 171, 172, + 171, 170, 172, 170, 170, 170, 170, 170, 170, 170, 171, 170, 170, 170, 170, 170, + 170, 170, 170, 173, 170, 170, 170, 174, 170, 170, 170, 175, 176, 176, 176, 176, + 176, 176, 176, 176, 176, 176, 177, 177, 178, 178, 178, 178, 178, 178, 178, 178, 179, 179, 179, 180, 181, 181, 181, 181, 181, 181, 181, 181, 181, 182, 181, 183, 184, 184, 185, 186, 187, 187, 188, 26, 189, 189, 190, 26, 191, 192, 193, 26, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 195, 194, 196, 194, 196, @@ -2368,208 +2467,164 @@ _hb_ucd_u16[9344] = 210, 210, 210, 210, 210, 211, 210, 210, 210, 212, 210, 213, 194, 194, 194, 194, 214, 214, 214, 215, 216, 216, 216, 216, 216, 216, 216, 217, 216, 216, 216, 218, 216, 219, 216, 219, 216, 220, 9, 9, 9, 221, 26, 26, 26, 26, 26, 26, - 222, 222, 222, 222, 222, 222, 222, 222, 222, 223, 222, 222, 222, 222, 222, 224, - 225, 225, 225, 225, 225, 225, 225, 225, 226, 226, 226, 226, 226, 226, 227, 228, - 229, 229, 229, 229, 229, 229, 229, 230, 229, 231, 232, 232, 232, 232, 232, 232, - 18, 233, 165, 165, 165, 165, 165, 234, 225, 26, 235, 9, 236, 237, 238, 239, - 2, 2, 2, 2, 240, 241, 2, 2, 2, 2, 2, 242, 243, 244, 2, 245, - 2, 2, 2, 2, 2, 2, 2, 246, 9, 9, 9, 9, 9, 9, 9, 9, - 14, 14, 247, 247, 14, 14, 14, 14, 247, 247, 14, 248, 14, 14, 14, 247, - 14, 14, 14, 14, 14, 14, 249, 14, 249, 14, 250, 251, 14, 14, 252, 253, - 0, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 256, 257, - 0, 258, 2, 259, 0, 0, 0, 0, 260, 26, 9, 9, 9, 9, 261, 26, - 0, 0, 0, 0, 262, 263, 4, 0, 0, 264, 0, 0, 2, 2, 2, 2, - 2, 265, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 258, 26, 26, 26, 0, 266, 26, 26, 0, 0, 0, 0, - 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, 0, - 0, 0, 269, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 2, 2, 2, 2, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 271, 272, - 165, 165, 165, 165, 166, 167, 273, 273, 273, 273, 273, 273, 273, 274, 275, 274, - 170, 170, 172, 26, 172, 172, 172, 172, 172, 172, 172, 172, 18, 18, 18, 18, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 276, 26, 26, 26, 26, + 222, 222, 222, 222, 222, 222, 222, 222, 222, 223, 222, 222, 222, 222, 222, 222, + 224, 224, 224, 224, 224, 224, 224, 224, 225, 225, 225, 225, 225, 225, 226, 227, + 228, 228, 228, 228, 228, 228, 228, 229, 228, 230, 231, 231, 231, 231, 231, 231, + 18, 232, 165, 165, 165, 165, 165, 233, 224, 26, 234, 9, 235, 236, 237, 238, + 2, 2, 2, 2, 239, 240, 2, 2, 2, 2, 2, 241, 242, 243, 2, 244, + 2, 2, 2, 2, 2, 2, 2, 245, 14, 14, 246, 246, 14, 14, 14, 14, + 246, 246, 14, 247, 14, 14, 14, 246, 14, 14, 14, 14, 14, 14, 248, 14, + 248, 14, 249, 250, 14, 14, 251, 252, 0, 253, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 254, 0, 255, 256, 0, 257, 2, 258, 0, 0, 0, 0, + 259, 26, 9, 9, 9, 9, 260, 26, 0, 0, 0, 0, 261, 262, 4, 0, + 0, 263, 0, 0, 2, 2, 2, 2, 2, 264, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 265, 26, 26, 0, 266, 26, 26, 0, 0, 0, 0, + 267, 267, 267, 267, 267, 267, 267, 267, 0, 0, 0, 0, 0, 0, 268, 0, + 0, 0, 269, 0, 0, 0, 0, 0, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 2, 2, 2, 2, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 271, 272, 165, 165, 165, 165, 166, 167, 273, 273, + 273, 273, 273, 273, 273, 274, 275, 274, 170, 170, 172, 26, 172, 172, 172, 172, + 172, 172, 172, 172, 18, 18, 18, 18, 0, 0, 0, 276, 26, 26, 26, 26, 277, 277, 277, 278, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 279, 26, - 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 280, 26, 26, 26, 0, 0, 281, 0, 0, 0, 282, 283, 0, 284, 285, 286, 286, 286, 286, 286, 286, 286, 286, 286, 287, 288, 289, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 291, - 292, 293, 293, 293, 293, 293, 294, 169, 169, 169, 169, 169, 169, 169, 169, 169, - 169, 295, 0, 0, 293, 293, 293, 293, 0, 0, 0, 0, 296, 297, 290, 290, - 169, 169, 169, 295, 0, 0, 0, 0, 0, 0, 0, 0, 169, 169, 169, 298, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 290, 290, 290, 290, 290, 299, + 292, 293, 293, 293, 293, 293, 294, 169, 169, 295, 0, 0, 293, 293, 293, 293, + 0, 0, 0, 0, 276, 296, 290, 290, 169, 169, 169, 295, 0, 0, 0, 0, + 0, 0, 0, 0, 169, 169, 169, 297, 0, 0, 290, 290, 290, 290, 290, 298, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 0, 0, 0, 0, 0, - 277, 277, 277, 277, 277, 277, 277, 277, 0, 0, 0, 0, 0, 0, 0, 0, - 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, - 300, 301, 300, 300, 300, 300, 300, 300, 302, 26, 303, 303, 303, 303, 303, 303, - 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, - 304, 304, 304, 304, 304, 305, 26, 26, 18, 18, 18, 18, 18, 18, 18, 18, - 18, 18, 18, 18, 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, 26, - 0, 0, 0, 0, 307, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 308, 2, 2, 2, 2, 2, 2, 2, 309, 310, 311, 26, 26, 312, 2, - 313, 313, 313, 313, 313, 314, 0, 315, 316, 316, 316, 316, 316, 316, 316, 26, - 317, 317, 317, 317, 317, 317, 317, 317, 318, 319, 317, 320, 53, 53, 53, 53, - 321, 321, 321, 321, 321, 322, 323, 323, 323, 323, 324, 325, 169, 169, 169, 326, - 327, 327, 327, 327, 327, 327, 327, 327, 327, 328, 327, 329, 164, 164, 164, 330, - 331, 331, 331, 331, 331, 331, 332, 26, 331, 333, 331, 334, 164, 164, 164, 164, - 335, 335, 335, 335, 335, 335, 335, 335, 336, 26, 26, 337, 338, 338, 339, 26, - 340, 340, 340, 26, 172, 172, 2, 2, 2, 2, 2, 341, 342, 343, 176, 176, - 176, 176, 176, 176, 176, 176, 176, 176, 338, 338, 338, 338, 338, 344, 338, 345, - 169, 169, 169, 169, 346, 26, 169, 169, 295, 347, 169, 169, 169, 169, 169, 346, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 280, 277, 277, - 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 348, 26, 26, 26, 26, - 349, 26, 350, 351, 25, 25, 352, 353, 354, 25, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, 355, 26, 356, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 357, 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 358, 31, 31, 31, 31, 31, 31, 359, 26, 26, 26, 26, 31, 31, - 9, 9, 0, 315, 9, 360, 0, 0, 0, 0, 361, 0, 258, 296, 362, 31, - 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 363, - 364, 0, 0, 0, 1, 2, 2, 3, 1, 2, 2, 3, 365, 290, 289, 290, - 290, 290, 290, 366, 169, 169, 169, 295, 367, 367, 367, 368, 258, 258, 26, 369, - 370, 371, 370, 370, 372, 370, 370, 373, 370, 374, 370, 374, 26, 26, 26, 26, - 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 375, - 376, 0, 0, 0, 0, 0, 377, 0, 14, 14, 14, 14, 14, 14, 14, 14, - 14, 253, 0, 378, 379, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 380, - 381, 381, 381, 382, 383, 383, 383, 383, 383, 383, 384, 26, 385, 0, 0, 296, - 386, 386, 386, 386, 387, 388, 389, 389, 389, 390, 391, 391, 391, 391, 391, 392, - 393, 393, 393, 394, 395, 395, 395, 395, 396, 395, 397, 26, 26, 26, 26, 26, - 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 399, 399, 399, 399, 399, 399, - 400, 400, 400, 401, 400, 402, 403, 403, 403, 403, 404, 403, 403, 403, 403, 404, - 405, 405, 405, 405, 405, 26, 406, 406, 406, 406, 406, 406, 407, 408, 409, 410, - 409, 410, 411, 409, 412, 409, 412, 413, 26, 26, 26, 26, 26, 26, 26, 26, - 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, - 414, 414, 414, 414, 414, 414, 415, 26, 414, 414, 416, 26, 414, 26, 26, 26, - 417, 2, 2, 2, 2, 2, 418, 309, 26, 26, 26, 26, 26, 26, 26, 26, - 419, 420, 421, 421, 421, 421, 422, 423, 424, 424, 425, 424, 426, 426, 426, 426, - 427, 427, 427, 428, 429, 427, 26, 26, 26, 26, 26, 26, 430, 430, 431, 432, - 433, 433, 433, 434, 435, 435, 435, 436, 26, 26, 26, 26, 26, 26, 26, 26, - 437, 437, 437, 437, 438, 438, 438, 439, 438, 438, 440, 438, 438, 438, 438, 438, - 441, 442, 443, 444, 445, 445, 446, 447, 445, 448, 445, 448, 449, 449, 449, 449, - 450, 450, 450, 450, 26, 26, 26, 26, 451, 451, 451, 451, 452, 453, 452, 26, - 454, 454, 454, 454, 454, 454, 455, 456, 457, 457, 458, 457, 459, 459, 460, 459, - 461, 461, 462, 463, 26, 464, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 465, 465, 465, 465, 465, 465, 465, 465, 465, 466, 26, 26, 26, 26, 26, 26, - 467, 467, 467, 467, 467, 467, 468, 26, 467, 467, 467, 467, 467, 467, 468, 469, - 470, 470, 470, 470, 470, 26, 470, 471, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 31, 31, 31, 50, - 472, 472, 472, 472, 472, 473, 474, 26, 26, 26, 26, 26, 26, 26, 26, 475, - 476, 476, 476, 476, 476, 26, 477, 477, 477, 477, 477, 478, 26, 26, 479, 479, - 479, 480, 26, 26, 26, 26, 481, 481, 481, 482, 26, 26, 483, 483, 484, 26, - 485, 485, 485, 485, 485, 485, 485, 485, 485, 486, 487, 485, 485, 485, 486, 488, - 489, 489, 489, 489, 489, 489, 489, 489, 490, 491, 492, 492, 492, 493, 492, 494, - 495, 495, 495, 495, 495, 495, 496, 495, 495, 26, 497, 497, 497, 497, 498, 26, - 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 500, 137, 501, 26, - 502, 502, 503, 502, 502, 502, 502, 502, 504, 26, 26, 26, 26, 26, 26, 26, - 505, 506, 507, 508, 507, 509, 510, 510, 510, 510, 510, 510, 510, 511, 510, 512, - 513, 514, 515, 516, 516, 517, 518, 519, 514, 520, 521, 522, 523, 524, 524, 26, - 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 526, 527, 26, 26, 26, - 528, 528, 528, 528, 528, 528, 528, 528, 528, 26, 528, 529, 26, 26, 26, 26, - 530, 530, 530, 530, 530, 530, 531, 530, 530, 530, 530, 531, 26, 26, 26, 26, - 532, 532, 532, 532, 532, 532, 532, 532, 533, 26, 532, 534, 198, 535, 26, 26, - 536, 536, 536, 536, 536, 536, 536, 537, 536, 537, 26, 26, 26, 26, 26, 26, - 538, 538, 538, 539, 538, 540, 538, 538, 541, 26, 26, 26, 26, 26, 26, 26, - 542, 542, 542, 542, 542, 542, 542, 543, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 544, 544, 544, 544, 544, 544, 544, 544, 544, 544, 545, 546, - 547, 548, 549, 550, 550, 550, 551, 552, 547, 26, 550, 553, 26, 26, 26, 26, - 26, 26, 26, 26, 554, 555, 554, 554, 554, 554, 554, 555, 556, 26, 26, 26, - 557, 557, 557, 557, 557, 557, 557, 557, 557, 26, 558, 558, 558, 558, 558, 558, - 558, 558, 558, 558, 559, 26, 178, 178, 560, 560, 560, 560, 560, 560, 560, 561, - 53, 562, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 563, 564, 563, 563, 563, 563, 565, 563, 566, 26, 563, 563, 563, 567, 568, 568, - 568, 568, 569, 568, 568, 570, 571, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 572, 573, 574, 574, 574, 574, 572, 575, 574, 26, 574, 576, 577, 578, 579, 579, - 579, 580, 581, 582, 579, 583, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 584, 584, 584, 585, - 586, 586, 587, 586, 586, 586, 586, 588, 586, 586, 586, 589, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 590, 26, 108, 108, 108, 108, 108, 108, 591, 592, - 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, - 593, 593, 593, 594, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 595, 596, 26, - 593, 593, 593, 593, 593, 593, 593, 593, 597, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 599, 26, - 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, - 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 601, 26, 26, 26, 26, 26, - 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, - 602, 602, 602, 602, 602, 602, 602, 602, 603, 26, 26, 26, 26, 26, 26, 26, - 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, - 306, 306, 306, 306, 306, 306, 306, 604, 605, 605, 605, 606, 605, 607, 608, 608, - 608, 608, 608, 608, 608, 608, 608, 609, 608, 610, 611, 611, 611, 612, 612, 26, - 613, 613, 613, 613, 613, 613, 613, 613, 614, 26, 613, 615, 615, 613, 613, 616, - 613, 613, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 617, 617, 617, 617, 617, 617, 617, 617, - 617, 617, 617, 618, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 619, 619, 619, 619, 619, 619, 619, 619, 619, 620, 619, 619, 619, 619, 619, 619, - 619, 621, 619, 619, 26, 26, 26, 26, 26, 26, 26, 26, 622, 26, 348, 26, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 26, - 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, - 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 625, 26, 26, 26, 26, 26, - 623, 626, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 627, 628, - 629, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, - 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, - 286, 286, 286, 286, 630, 26, 631, 26, 26, 26, 632, 26, 633, 26, 634, 634, - 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, - 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, 635, - 636, 636, 636, 636, 636, 636, 636, 636, 636, 636, 636, 636, 636, 637, 636, 638, - 636, 639, 636, 640, 296, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 9, 9, 9, 9, 9, 641, 9, 9, 221, 26, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 296, 26, 26, 26, 26, 26, 26, 26, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 276, 26, - 0, 0, 0, 0, 258, 364, 0, 0, 0, 0, 0, 0, 642, 643, 0, 644, - 645, 646, 0, 0, 0, 647, 0, 0, 0, 0, 0, 0, 0, 266, 26, 26, - 14, 14, 14, 14, 14, 14, 14, 14, 247, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 296, 26, 0, 0, 296, 26, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 258, 26, 0, 0, 0, 260, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 648, 649, 0, 650, 651, 0, 0, 0, 0, 0, 0, 0, - 269, 652, 255, 255, 0, 0, 0, 653, 654, 655, 656, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, 0, 0, 0, 0, 0, 0, - 657, 657, 657, 657, 657, 657, 657, 657, 657, 657, 657, 657, 657, 657, 657, 657, - 657, 658, 26, 659, 660, 657, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 2, 2, 2, 349, 661, 309, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 662, 270, 270, 663, 664, 665, 18, 18, 18, 18, 18, 18, 18, 666, 26, 26, - 26, 667, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 668, 668, 668, 668, 668, 669, 668, 670, 668, 671, 26, 26, 26, 26, 26, 26, - 26, 26, 672, 672, 672, 673, 26, 26, 674, 674, 674, 674, 674, 674, 674, 675, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 676, 676, 676, 676, 676, 677, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 172, 678, 170, 172, - 679, 679, 679, 679, 679, 679, 679, 679, 679, 679, 679, 679, 679, 679, 679, 679, - 679, 679, 679, 679, 679, 679, 679, 679, 680, 679, 681, 26, 26, 26, 26, 26, - 682, 682, 682, 682, 682, 682, 682, 682, 682, 683, 682, 684, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 364, 0, - 0, 0, 0, 0, 0, 0, 378, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 364, 0, 0, 0, 0, 0, 0, 276, 26, 26, 26, 26, 26, 26, 26, 26, - 685, 31, 31, 31, 686, 687, 688, 689, 690, 691, 686, 692, 686, 688, 688, 693, - 31, 694, 31, 695, 696, 694, 31, 695, 26, 26, 26, 26, 26, 26, 51, 26, - 0, 0, 0, 0, 0, 296, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 296, 26, 0, 258, 364, 0, 364, 0, 364, 0, 0, 0, 276, 26, - 0, 0, 0, 0, 0, 276, 26, 26, 26, 26, 26, 26, 697, 0, 0, 0, - 698, 26, 0, 0, 0, 0, 0, 296, 0, 260, 315, 26, 276, 26, 26, 26, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 699, 0, 378, 0, 378, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 258, 700, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 315, 0, 296, 260, 26, - 0, 296, 0, 0, 0, 0, 0, 0, 0, 26, 0, 315, 0, 0, 0, 0, - 0, 26, 0, 0, 0, 276, 315, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 296, 26, 0, 276, 0, 378, - 0, 260, 0, 0, 0, 0, 0, 269, 276, 697, 0, 296, 0, 260, 0, 260, - 0, 0, 361, 0, 0, 0, 0, 0, 0, 266, 26, 26, 26, 26, 0, 315, - 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 26, 26, 26, 26, - 277, 277, 277, 277, 277, 277, 277, 348, 277, 277, 277, 277, 277, 277, 277, 277, - 277, 277, 277, 280, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, - 277, 277, 277, 277, 348, 26, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, - 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 701, 26, 277, 277, - 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 280, 26, 26, 26, 26, - 277, 277, 277, 280, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 277, 277, 277, 277, 277, 277, 277, 277, 277, 702, 277, 277, 277, 277, 277, 277, - 277, 277, 277, 277, 277, 277, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 703, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, + 299, 299, 299, 299, 299, 299, 299, 299, 299, 300, 299, 299, 299, 299, 299, 299, + 301, 26, 302, 302, 302, 302, 302, 302, 303, 303, 303, 303, 303, 303, 303, 303, + 303, 303, 303, 303, 303, 304, 26, 26, 18, 18, 18, 18, 305, 305, 305, 305, + 305, 305, 305, 305, 305, 305, 305, 26, 0, 0, 0, 0, 306, 2, 2, 2, + 2, 307, 2, 2, 2, 2, 2, 2, 2, 308, 309, 258, 26, 26, 310, 2, + 311, 311, 311, 311, 311, 312, 0, 265, 313, 313, 313, 313, 313, 313, 313, 26, + 314, 314, 314, 314, 314, 314, 314, 314, 315, 316, 314, 317, 53, 53, 53, 53, + 318, 318, 318, 318, 318, 319, 320, 320, 320, 320, 321, 322, 169, 169, 169, 323, + 324, 324, 324, 324, 324, 324, 324, 324, 324, 325, 324, 326, 164, 164, 164, 327, + 328, 328, 328, 328, 328, 328, 329, 26, 328, 330, 328, 331, 164, 164, 164, 164, + 332, 332, 332, 332, 332, 332, 332, 332, 333, 26, 26, 334, 335, 335, 336, 26, + 337, 337, 337, 26, 172, 172, 2, 2, 2, 2, 2, 338, 339, 340, 176, 176, + 176, 176, 176, 176, 176, 176, 176, 176, 335, 335, 335, 335, 335, 341, 335, 342, + 169, 169, 169, 169, 343, 26, 169, 169, 295, 344, 169, 169, 169, 169, 169, 343, + 26, 26, 26, 26, 26, 26, 26, 26, 277, 277, 277, 277, 277, 280, 277, 277, + 277, 277, 277, 345, 26, 26, 26, 26, 346, 26, 347, 348, 25, 25, 349, 350, + 351, 25, 31, 31, 31, 31, 31, 31, 352, 26, 353, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 354, 31, 31, 355, 31, 31, 31, 31, 31, + 31, 356, 26, 26, 26, 26, 31, 31, 9, 9, 0, 265, 9, 357, 0, 0, + 0, 0, 358, 0, 257, 359, 360, 31, 31, 31, 31, 31, 31, 31, 31, 361, + 362, 0, 0, 0, 1, 2, 2, 3, 1, 2, 2, 3, 363, 290, 289, 290, + 290, 290, 290, 364, 169, 169, 169, 295, 365, 365, 365, 366, 257, 257, 26, 367, + 368, 369, 368, 368, 370, 368, 368, 371, 368, 372, 368, 372, 26, 26, 26, 26, + 368, 368, 368, 368, 368, 368, 368, 368, 368, 368, 368, 368, 368, 368, 368, 373, + 374, 0, 0, 0, 0, 0, 375, 0, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 252, 0, 376, 377, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 378, + 379, 379, 379, 380, 381, 381, 381, 381, 381, 381, 382, 26, 383, 0, 0, 359, + 384, 384, 384, 384, 385, 386, 387, 387, 387, 388, 389, 389, 389, 389, 389, 390, + 391, 391, 391, 392, 393, 393, 393, 393, 394, 393, 395, 26, 26, 26, 26, 26, + 396, 396, 396, 396, 396, 396, 396, 396, 396, 396, 397, 397, 397, 397, 397, 397, + 398, 398, 398, 399, 398, 400, 401, 401, 401, 401, 402, 401, 401, 401, 401, 402, + 403, 403, 403, 403, 403, 26, 404, 404, 404, 404, 404, 404, 405, 406, 407, 408, + 407, 408, 409, 407, 410, 407, 410, 411, 412, 412, 412, 412, 412, 412, 413, 26, + 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 415, 26, + 414, 414, 416, 26, 414, 26, 26, 26, 417, 2, 2, 2, 2, 2, 418, 419, + 420, 421, 422, 422, 422, 422, 423, 424, 425, 425, 426, 425, 427, 427, 427, 427, + 428, 428, 428, 429, 430, 428, 26, 26, 26, 26, 26, 26, 431, 431, 432, 433, + 434, 434, 434, 435, 436, 436, 436, 437, 438, 438, 438, 438, 439, 439, 439, 440, + 439, 439, 441, 439, 439, 439, 439, 439, 442, 443, 444, 445, 446, 446, 447, 448, + 446, 449, 446, 449, 450, 450, 450, 450, 451, 451, 451, 451, 26, 26, 26, 26, + 452, 452, 452, 452, 453, 454, 453, 26, 455, 455, 455, 455, 455, 455, 456, 457, + 458, 458, 459, 458, 460, 460, 461, 460, 462, 462, 463, 464, 26, 465, 26, 26, + 466, 466, 466, 466, 466, 466, 466, 466, 466, 467, 26, 26, 26, 26, 26, 26, + 468, 468, 468, 468, 468, 468, 469, 26, 468, 468, 468, 468, 468, 468, 469, 470, + 471, 471, 471, 471, 471, 26, 471, 472, 473, 473, 473, 473, 474, 475, 473, 473, + 474, 476, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 31, 31, 31, 50, + 477, 477, 477, 477, 477, 478, 479, 26, 480, 26, 26, 26, 26, 26, 26, 481, + 482, 482, 482, 482, 482, 26, 483, 483, 483, 483, 483, 484, 26, 26, 485, 485, + 485, 486, 26, 26, 26, 26, 487, 487, 487, 488, 26, 26, 489, 489, 490, 26, + 491, 491, 491, 491, 491, 491, 491, 491, 491, 492, 493, 491, 491, 491, 492, 494, + 495, 495, 495, 495, 495, 495, 495, 495, 496, 497, 498, 498, 498, 499, 498, 500, + 501, 501, 501, 501, 501, 501, 502, 501, 501, 26, 503, 503, 503, 503, 504, 26, + 505, 505, 505, 505, 505, 505, 505, 505, 505, 505, 505, 505, 506, 137, 507, 26, + 508, 508, 509, 508, 508, 508, 508, 508, 510, 26, 26, 26, 26, 26, 26, 26, + 511, 512, 513, 514, 513, 515, 516, 516, 516, 516, 516, 516, 516, 517, 516, 518, + 519, 520, 521, 522, 522, 523, 524, 525, 520, 526, 527, 528, 529, 530, 530, 26, + 531, 532, 531, 531, 531, 531, 533, 531, 534, 535, 533, 536, 537, 26, 26, 26, + 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 539, 540, 26, 26, 26, + 541, 541, 541, 541, 541, 541, 541, 541, 541, 26, 541, 542, 26, 26, 26, 26, + 543, 543, 543, 543, 543, 543, 544, 543, 543, 543, 543, 544, 26, 26, 26, 26, + 545, 545, 545, 545, 545, 545, 545, 545, 546, 26, 545, 547, 198, 548, 26, 26, + 549, 549, 549, 549, 549, 549, 549, 550, 549, 550, 164, 164, 551, 26, 26, 26, + 552, 552, 552, 553, 552, 554, 552, 552, 555, 26, 26, 26, 26, 26, 26, 26, + 556, 556, 556, 556, 556, 556, 556, 557, 26, 26, 26, 26, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 559, 560, 561, 562, 563, 564, 564, 564, 565, 566, + 561, 26, 564, 567, 26, 26, 26, 26, 26, 26, 26, 26, 568, 569, 568, 568, + 568, 568, 568, 569, 570, 26, 26, 26, 571, 571, 571, 571, 571, 571, 571, 571, + 571, 26, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 573, 26, 178, 178, + 574, 574, 574, 574, 574, 574, 574, 575, 53, 576, 26, 26, 26, 26, 26, 26, + 577, 577, 577, 577, 578, 26, 577, 578, 579, 580, 579, 579, 579, 579, 581, 579, + 582, 26, 579, 579, 579, 583, 584, 584, 584, 584, 585, 584, 584, 586, 587, 26, + 588, 589, 590, 590, 590, 590, 588, 591, 590, 26, 590, 592, 593, 594, 595, 595, + 595, 596, 597, 598, 595, 599, 26, 26, 26, 26, 26, 26, 600, 600, 600, 601, + 602, 602, 603, 602, 602, 602, 602, 604, 602, 602, 602, 605, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 606, 26, 108, 108, 108, 108, 108, 108, 607, 608, + 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 610, 26, 26, 26, 26, + 609, 609, 609, 609, 609, 611, 612, 26, 613, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 615, 26, + 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 617, 26, 616, 616, 616, 616, + 616, 616, 616, 616, 616, 616, 616, 618, 619, 619, 619, 619, 619, 619, 619, 619, + 620, 26, 26, 26, 26, 26, 26, 26, 621, 621, 621, 621, 621, 621, 621, 622, + 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 623, + 624, 624, 624, 625, 624, 626, 627, 627, 627, 627, 627, 627, 627, 627, 627, 628, + 627, 629, 630, 630, 630, 631, 631, 26, 632, 632, 632, 632, 632, 632, 632, 632, + 633, 26, 632, 634, 634, 632, 632, 635, 632, 632, 26, 26, 26, 26, 26, 26, + 636, 636, 636, 636, 636, 636, 636, 637, 638, 638, 638, 638, 638, 638, 638, 638, + 638, 638, 638, 639, 26, 26, 26, 26, 640, 640, 640, 640, 640, 640, 640, 640, + 640, 641, 640, 640, 640, 640, 640, 640, 640, 642, 640, 640, 26, 26, 26, 26, + 26, 26, 26, 26, 643, 26, 345, 26, 644, 644, 644, 644, 644, 644, 644, 644, + 644, 644, 644, 644, 644, 644, 644, 26, 645, 645, 645, 645, 645, 645, 645, 645, + 645, 645, 646, 26, 26, 26, 26, 647, 644, 648, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 649, 650, 651, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 652, 26, 653, 26, + 26, 26, 654, 26, 655, 26, 656, 656, 656, 656, 656, 656, 656, 656, 656, 656, + 656, 656, 656, 656, 656, 656, 656, 657, 658, 658, 658, 658, 658, 658, 658, 658, + 658, 658, 658, 658, 658, 659, 658, 660, 658, 661, 658, 662, 359, 26, 26, 26, + 0, 0, 0, 0, 0, 0, 0, 265, 0, 0, 0, 0, 0, 0, 359, 26, + 9, 9, 9, 9, 9, 663, 9, 9, 221, 26, 0, 0, 0, 0, 0, 0, + 359, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 276, 26, + 0, 0, 0, 0, 257, 362, 0, 0, 0, 0, 0, 0, 664, 665, 0, 666, + 667, 668, 0, 0, 0, 669, 0, 0, 0, 0, 0, 0, 0, 266, 26, 26, + 246, 26, 26, 26, 26, 26, 26, 26, 0, 0, 359, 26, 0, 0, 359, 26, + 0, 0, 257, 26, 0, 0, 0, 259, 0, 0, 254, 0, 0, 0, 0, 0, + 0, 0, 0, 254, 670, 671, 0, 672, 673, 0, 0, 0, 0, 0, 0, 0, + 269, 674, 254, 254, 0, 0, 0, 675, 676, 677, 678, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 276, 0, 0, 0, 0, 268, 0, 0, 0, 0, 0, 0, + 679, 679, 679, 679, 679, 679, 679, 679, 679, 680, 26, 681, 682, 679, 26, 26, + 2, 2, 2, 346, 683, 419, 26, 26, 684, 270, 270, 685, 686, 687, 18, 18, + 18, 18, 18, 18, 18, 688, 26, 26, 26, 689, 26, 26, 26, 26, 26, 26, + 690, 690, 690, 690, 690, 691, 690, 692, 690, 693, 26, 26, 26, 26, 26, 26, + 26, 26, 694, 694, 694, 695, 26, 26, 696, 696, 696, 696, 696, 696, 696, 697, + 26, 26, 698, 698, 698, 698, 698, 699, 26, 26, 700, 700, 700, 700, 700, 701, + 26, 26, 26, 26, 172, 702, 170, 172, 703, 703, 703, 703, 703, 703, 703, 703, + 704, 703, 705, 26, 26, 26, 26, 26, 706, 706, 706, 706, 706, 706, 706, 706, + 706, 707, 706, 708, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 362, 0, + 0, 0, 0, 0, 0, 0, 376, 26, 362, 0, 0, 0, 0, 0, 0, 276, + 709, 31, 31, 31, 710, 711, 712, 713, 714, 715, 710, 716, 710, 712, 712, 717, + 31, 718, 31, 719, 720, 718, 31, 719, 26, 26, 26, 26, 26, 26, 721, 26, + 0, 0, 0, 0, 0, 359, 0, 0, 0, 0, 359, 26, 0, 257, 362, 0, + 362, 0, 362, 0, 0, 0, 276, 26, 0, 0, 0, 0, 0, 276, 26, 26, + 26, 26, 26, 26, 722, 0, 0, 0, 723, 26, 0, 0, 0, 0, 0, 359, + 0, 259, 265, 26, 276, 26, 26, 26, 0, 0, 0, 724, 0, 376, 0, 376, + 0, 0, 0, 0, 0, 0, 257, 725, 0, 0, 0, 265, 0, 359, 259, 26, + 0, 359, 0, 0, 0, 0, 0, 0, 0, 26, 0, 265, 0, 0, 0, 0, + 0, 26, 0, 0, 0, 276, 0, 359, 265, 26, 26, 26, 26, 26, 26, 26, + 0, 0, 359, 26, 0, 276, 0, 376, 0, 726, 0, 0, 0, 0, 0, 0, + 257, 722, 0, 727, 0, 265, 0, 259, 0, 0, 358, 0, 0, 0, 0, 0, + 277, 277, 277, 277, 26, 26, 26, 26, 277, 277, 277, 277, 277, 277, 277, 345, + 277, 277, 277, 280, 277, 277, 277, 277, 277, 277, 277, 277, 345, 26, 277, 277, + 277, 277, 277, 277, 728, 26, 277, 277, 277, 277, 277, 280, 26, 26, 26, 26, + 277, 729, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 26, 26, + 730, 26, 26, 26, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 939, 940, 941, 942, 946, 948, 0, 962, 969, 970, 971, 976,1001,1002,1003,1008, 0,1033,1040,1041,1042,1043,1047, 0, 0,1080,1081,1082,1086,1110, 0, 0, @@ -2732,17 +2787,24 @@ _hb_ucd_u16[9344] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1602,1603,1934,1935,1574,1575, 1576,1577,1579,1580,1581,1583,1584, 0,1585,1587,1588,1589,1591, 0,1592, 0, 1593,1594, 0,1595,1596, 0,1598,1599,1600,1601,1604,1582,1578,1590,1597, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1936, 0,1937, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1938, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1939,1940, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1941,1942, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1944,1943, 0,1945, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1946,1947, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0,1948, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1949,1950, - 1951,1952,1953,1954,1955, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1956,1957,1958,1960,1959, - 1961, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1936, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1937, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1938, 0,1939, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1940, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1941,1942, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1943,1944, 0, 0, 0, + 0, 0, 0,1945, 0,1946, 0, 0, 0, 0, 0, 0, 0, 0,1947, 0, + 0,1948, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1950, 0,1949,1951, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1953,1952, 0,1954, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1955,1956, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1957, 0, 0, 0, 0, 0, 0, 0, + 0,1958,1961,1959,1965,1960,1962,1964,1963, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1967,1966,1968, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1969,1970, + 1971,1972,1973,1974,1975, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1976,1977,1978,1980,1979, + 1981, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 104, 107, 826, 114, 118, 119, 121, 123, 124, 127, 125, 34, 830, 130, 131, 132, 137, 827, 35, 133, 139, 829, 142, 143, 112, 144, 145, 924, 151, 152, 37, 157, 158, 159, 160, 38, 165, 166, 169, 171, 172, 173, 174, 176, 177, 178, 179, @@ -2799,12 +2861,12 @@ _hb_ucd_i16[196] = static inline uint_fast8_t _hb_ucd_gc (unsigned u) { - return u<1114110u?_hb_ucd_u8[6808+(((_hb_ucd_u8[1312+(((_hb_ucd_u16[((_hb_ucd_u8[544+(((_hb_ucd_u8[u>>1>>3>>3>>4])<<4)+((u>>1>>3>>3)&15u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:2; + return u<1114110u?_hb_ucd_u8[6472+(((_hb_ucd_u8[816+(((_hb_ucd_u16[((_hb_ucd_u8[272+(((_hb_ucd_u8[u>>1>>3>>4>>4])<<4)+((u>>1>>3>>4)&15u))])<<4)+((u>>1>>3)&15u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:2; } static inline uint_fast8_t _hb_ucd_ccc (unsigned u) { - return u<125259u?_hb_ucd_u8[8800+(((_hb_ucd_u8[8244+(((_hb_ucd_u8[7784+(((_hb_ucd_u8[7432+(((_hb_ucd_u8[7186+(u>>2>>2>>2>>3)])<<3)+((u>>2>>2>>2)&7u))])<<2)+((u>>2>>2)&3u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:0; + return u<125259u?_hb_ucd_u8[8504+(((_hb_ucd_u8[7936+(((_hb_ucd_u8[7460+(((_hb_ucd_u8[7100+(((_hb_ucd_u8[6854+(u>>2>>2>>2>>3)])<<3)+((u>>2>>2>>2)&7u))])<<2)+((u>>2>>2)&3u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:0; } static inline unsigned _hb_ucd_b4 (const uint8_t* a, unsigned i) @@ -2814,107 +2876,76 @@ _hb_ucd_b4 (const uint8_t* a, unsigned i) static inline int_fast16_t _hb_ucd_bmg (unsigned u) { - return u<65380u?_hb_ucd_i16[((_hb_ucd_u8[9548+(((_hb_ucd_u8[9428+(((_hb_ucd_b4(9300+_hb_ucd_u8,u>>2>>3>>3))<<3)+((u>>2>>3)&7u))])<<3)+((u>>2)&7u))])<<2)+((u)&3u)]:0; + return u<65380u?_hb_ucd_i16[((_hb_ucd_u8[9252+(((_hb_ucd_u8[9132+(((_hb_ucd_b4(9004+_hb_ucd_u8,u>>2>>3>>3))<<3)+((u>>2>>3)&7u))])<<3)+((u>>2)&7u))])<<2)+((u)&3u)]:0; } static inline uint_fast8_t _hb_ucd_sc (unsigned u) { - return u<918000u?_hb_ucd_u8[11070+(((_hb_ucd_u16[2048+(((_hb_ucd_u8[10334+(((_hb_ucd_u8[9884+(u>>3>>4>>4)])<<4)+((u>>3>>4)&15u))])<<4)+((u>>3)&15u))])<<3)+((u)&7u))]:2; + return u<918000u?_hb_ucd_u8[10486+(((_hb_ucd_u16[3744+(((_hb_ucd_u16[2624+(((_hb_ucd_u8[9588+(u>>3>>3>>4)])<<4)+((u>>3>>3)&15u))])<<3)+((u>>3)&7u))])<<3)+((u)&7u))]:2; } static inline uint_fast16_t _hb_ucd_dm (unsigned u) { - return u<195102u?_hb_ucd_u16[6032+(((_hb_ucd_u8[17084+(((_hb_ucd_u8[16702+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:0; + return u<195102u?_hb_ucd_u16[6976+(((_hb_ucd_u8[16716+(((_hb_ucd_u8[16334+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:0; } #elif !defined(HB_NO_UCD_UNASSIGNED) static const uint8_t -_hb_ucd_u8[14752] = +_hb_ucd_u8[17524] = { - 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 9, 10, 7, 7, 7, 7, 11, 12, 13, 13, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 22, 22, 22, 22, 24, 7, 7, - 25, 26, 22, 22, 22, 27, 28, 29, 22, 30, 31, 32, 33, 34, 35, 36, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 37, 7, 38, 39, 7, 40, 7, 7, 7, 41, 22, 42, - 7, 7, 43, 7, 44, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 45, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 46, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 47, + 0, 1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 5, 5, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 5, 17, 15, 18, 19, 20, 21, 22, 23, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 24, 25, 26, 5, 27, 28, + 5, 29, 30, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 31, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 32, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 33, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 34, 35, 36, 37, 38, 39, 34, 34, 34, 40, 41, 42, 43, - 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, - 60, 61, 62, 63, 64, 64, 65, 66, 67, 68, 69, 70, 71, 69, 72, 73, - 69, 69, 64, 74, 64, 64, 75, 76, 77, 78, 79, 80, 81, 82, 69, 83, - 84, 85, 86, 87, 88, 89, 69, 69, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 90, 34, 34, 34, 34, - 91, 34, 34, 34, 34, 34, 34, 34, 34, 92, 34, 34, 93, 94, 95, 96, - 97, 98, 99,100,101,102,103,104, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,105, - 106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106, - 107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107, - 107,107, 34, 34,108,109,110,111, 34, 34,112,113,114,115,116,117, - 118,119,120,121,122,123,124,125,126,127,128,129, 34, 34,130,131, - 132,133,134,135,136,137,138,139,140,141,142,122,143,144,145,146, - 147,148,149,150,151,152,153,122,154,155,122,156,157,158,159,122, - 160,161,162,163,164,165,166,122,167,168,169,170,122,171,172,173, - 34, 34, 34, 34, 34, 34, 34,174,175, 34,176,122,122,122,122,122, - 122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,177, - 34, 34, 34, 34, 34, 34, 34, 34,178,122,122,122,122,122,122,122, - 122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122, - 122,122,122,122,122,122,122,122, 34, 34, 34, 34,179,122,122,122, - 34, 34, 34, 34,180,181,182,183,122,122,122,122,184,185,186,187, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,188, - 34, 34, 34, 34, 34, 34, 34, 34, 34,189,190,122,122,122,122,122, - 122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,191, - 34, 34,192, 34, 34,193,122,122,122,122,122,122,122,122,122,122, - 122,122,122,122,122,122,122,122,194,195,122,122,122,122,122,122, - 122,122,122,122,122,122,122,122,122,122,122,122,122,122,196,197, - 69,198,199,200,201,202,203,122,204,205,206,207,208,209,210,211, - 69, 69, 69, 69,212,213,122,122,122,122,122,122,122,122,214,122, - 215,216,217,122,122,218,122,122,122,219,122,122,122,122,122,220, - 34,221,222,122,122,122,122,122,223,224,225,122,226,227,122,122, - 228,229,230,231,232,122, 69,233, 69, 69, 69, 69, 69,234,235,236, - 237,238, 69, 69,239,240, 69,241,122,122,122,122,122,122,122,122, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,242, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,243, 34, - 244, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,245, 34, 34, - 34, 34, 34, 34, 34, 34, 34,246, 34, 34, 34, 34,247,122,122,122, - 34, 34, 34, 34,248,122,122,122,122,122,122,122,122,122,122,122, - 34, 34, 34, 34, 34, 34,249, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34,250,122,122,122,122,122,122,122,122, - 251,122,252,253,122,122,122,122,122,122,122,122,122,122,122,122, - 107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,254, - 107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,255, + 16, 17, 18, 19, 20, 17, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 33, 41, 42, 43, 44, 45, + 46, 47, 48, 39, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 49, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 50, 17, 17, 17, 51, 17, 52, 53, 54, 55, 56, 57, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 58, 59, 59, 59, 59, 59, 59, 59, 59, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 17, 61, 62, 17, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 17, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 17, 17, 17, 97, 98, 99,100,100,100,100,100,100,100,100,100,101, + 17, 17, 17, 17,102, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17,103, 17, 17,104,100,100,100,100,100,100,100,100,100, + 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, + 100,105,100,100,100,100,100,100, 17, 17,106,107,100,108,109,110, + 17, 17, 17, 17, 17, 17, 17,111, 17, 17, 17, 17,112,113,100,100, + 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,114, + 17,115,116,100,100,100,100,100,100,100,100,100,117,100,100,100, + 100,100,100,100,100,100,100,100,100,100,100,100,118, 39,119,120, + 121,122,123,124,125,126,127,128, 39, 39,129,100,100,100,100,130, + 131,132,133,100,134,135,100,136,137,138,100,100,139,140,141,100, + 142,143,144,145, 39, 39,146,147,148, 39,149,150,100,100,100,100, + 17, 17, 17, 17, 17, 17,151, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17,152,153, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,154, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,155, 17, 17,156,100, + 100,100,100,100,100,100,100,100, 17, 17,157,100,100,100,100,100, + 17, 17, 17,158, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17,159,100,100,100,100,100,100,100,100,100,100,100,100, + 160,161,100,100,100,100,100,100,100,100,100,100,100,100,100,100, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,162, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,163, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 4, 5, 6, 2, 7, 7, 7, 7, 7, 2, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, @@ -2951,7 +2982,7 @@ _hb_ucd_u8[14752] = 43, 43, 40, 21, 2, 81, 57, 20, 36, 36, 36, 43, 43, 75, 43, 43, 43, 43, 75, 43, 75, 43, 43, 44, 2, 2, 2, 2, 2, 2, 2, 64, 36, 36, 36, 36, 70, 43, 44, 64, 36, 36, 36, 36, 36, 61, 44, 44, - 36, 36, 36, 36, 82, 36, 36, 61, 65, 44, 44, 44, 43, 43, 43, 43, + 36, 36, 36, 36, 82, 36, 36, 61, 65, 44, 44, 57, 43, 43, 43, 43, 36, 36, 36, 36, 83, 43, 43, 43, 43, 84, 43, 43, 43, 43, 43, 43, 43, 85, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 85, 71, 86, 87, 43, 43, 43, 85, 86, 87, 86, 70, 43, 43, 43, 36, 36, 36, 36, @@ -3024,13 +3055,13 @@ _hb_ucd_u8[14752] = 85, 85, 87, 43, 43, 43, 85, 86, 86, 87, 43, 43, 43, 43, 80, 57, 2, 2, 2, 88, 2, 2, 2, 44, 43, 43, 43, 43, 43, 43, 43,109, 43, 43, 43, 43, 43, 43, 43, 80, 43, 43, 98, 36, 36, 36, 36, 36, - 36, 36, 85, 43, 43, 85, 85, 86, 86, 85, 98, 36, 36, 36, 61, 44, - 97, 67, 67, 67, 67, 50, 43, 43, 43, 43, 67, 67, 67, 67, 21, 64, + 36, 36, 85, 43, 43, 85, 85, 86, 86, 85, 98, 36, 36, 36, 61, 2, + 97, 67, 67, 67, 67, 50, 43, 43, 43, 43, 67, 67, 67, 67, 21, 2, 43, 98, 36, 36, 36, 36, 36, 36, 94, 43, 43, 86, 43, 87, 43, 36, 36, 36, 36, 85, 43, 86, 87, 87, 43, 86, 44, 44, 44, 44, 2, 2, 36, 36, 86, 86, 86, 86, 43, 43, 43, 43, 86, 43, 44, 93, 2, 2, 7, 7, 7, 7, 7, 44, 62, 36, 36, 36, 36, 36, 40, 40, 40, 2, - 16, 16, 16, 16,110, 44, 44, 44, 11, 11, 11, 11, 11, 47, 48, 11, + 16, 16, 16, 16, 34,110, 44, 44, 11, 11, 11, 11, 11, 47, 48, 11, 2, 2, 2, 2, 44, 44, 44, 44, 43, 60, 43, 43, 43, 43, 43, 43, 85, 43, 43, 43, 71, 36, 70, 36, 36, 36, 71, 94, 43, 61, 44, 44, 16, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 45, 16, 16, @@ -3058,33 +3089,33 @@ _hb_ucd_u8[14752] = 67, 67, 67, 67, 4, 4, 67, 67, 8, 67, 67, 67,145,146, 67, 67, 67, 67, 67, 67, 67, 67,144, 67, 67, 67, 67, 67, 67, 26, 8, 8, 8, 8, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 8, 8, - 8, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 92, 44, 44, 44, 44, - 67, 67, 67, 67, 67, 92, 44, 44, 27, 27, 27, 27, 27, 27, 67, 67, - 67, 67, 67, 67, 67, 27, 27, 27, 67, 67, 67, 26, 67, 67, 67, 67, - 26, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 8, 8, 8, 8, - 67, 67, 67, 67, 67, 67, 67, 26, 67, 67, 67, 67, 4, 4, 4, 4, - 4, 4, 4, 27, 27, 27, 27, 27, 27, 27, 67, 67, 67, 67, 67, 67, - 8, 8,129,147, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4, - 8,129,148,148,148,148,148,148,148,148,148,148,147, 8, 8, 8, - 8, 8, 8, 8, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8, - 8, 8,144, 26, 8, 8,144, 67, 67, 67, 44, 67, 67, 67, 67, 67, - 67, 67, 67, 55, 67, 67, 67, 67, 32, 11, 32, 34, 34, 34, 34, 11, - 32, 32, 34, 16, 16, 16, 40, 11, 32, 32,140, 67, 67,138, 34,149, - 43, 32, 44, 44, 93, 2, 99, 2, 16, 16, 16,150, 44, 44,150, 44, - 36, 36, 36, 36, 44, 44, 44, 52, 64, 44, 44, 44, 44, 44, 44, 57, - 36, 36, 36, 61, 44, 44, 44, 44, 36, 36, 36, 61, 36, 36, 36, 61, - 2,121,121, 2,125,126,121, 2, 2, 2, 2, 6, 2,108,121, 2, - 121, 4, 4, 4, 4, 2, 2, 88, 2, 2, 2, 2, 2,120, 2, 2, - 108,151, 2, 2, 2, 2, 2, 2, 67, 2,152,148,148,148,153, 44, - 67, 67, 67, 67, 67, 55, 67, 67, 67, 67, 44, 44, 44, 44, 44, 44, - 67, 67, 67, 44, 44, 44, 44, 44, 1, 2,154,155, 4, 4, 4, 4, - 4, 67, 4, 4, 4, 4,156,157,158,105,105,105,105, 43, 43, 86, - 159, 40, 40, 67,105,160, 63, 67, 36, 36, 36, 61, 57,161,162, 69, - 36, 36, 36, 36, 36, 63, 40, 69, 44, 44, 62, 36, 36, 36, 36, 36, - 67, 27, 27, 67, 67, 67, 67, 67, 67, 67, 44, 44, 44, 44, 44, 55, - 67, 67, 67, 67, 67, 67, 67, 92, 27, 27, 27, 27, 27, 67, 67, 67, - 67, 67, 67, 67, 27, 27, 27, 27,163, 27, 27, 27, 27, 27, 27, 27, - 36, 36, 83, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,164, 2, + 8, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 92, 44, 44, + 27, 27, 27, 27, 27, 27, 67, 67, 67, 67, 67, 67, 67, 27, 27, 27, + 67, 67, 67, 26, 67, 67, 67, 67, 26, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 8, 8, 8, 8, 67, 67, 67, 67, 67, 67, 67, 26, + 67, 67, 67, 67, 4, 4, 4, 4, 4, 4, 4, 27, 27, 27, 27, 27, + 27, 27, 67, 67, 67, 67, 67, 67, 8, 8,129,147, 8, 8, 8, 8, + 8, 8, 8, 4, 4, 4, 4, 4, 8,129,148,148,148,148,148,148, + 148,148,148,148,147, 8, 8, 8, 8, 8, 8, 8, 4, 4, 8, 8, + 8, 8, 8, 8, 8, 8, 4, 8, 8, 8,144, 26, 8, 8,144, 67, + 67, 67, 44, 67, 67, 67, 67, 67, 67, 67, 67, 55, 67, 67, 67, 67, + 32, 11, 32, 34, 34, 34, 34, 11, 32, 32, 34, 16, 16, 16, 40, 11, + 32, 32,140, 67, 67,138, 34,149, 43, 32, 44, 44, 93, 2, 99, 2, + 16, 16, 16,150, 44, 44,150, 44, 36, 36, 36, 36, 44, 44, 44, 52, + 64, 44, 44, 44, 44, 44, 44, 57, 36, 36, 36, 61, 44, 44, 44, 44, + 36, 36, 36, 61, 36, 36, 36, 61, 2,121,121, 2,125,126,121, 2, + 2, 2, 2, 6, 2,108,121, 2,121, 4, 4, 4, 4, 2, 2, 88, + 2, 2, 2, 2, 2,120, 2, 2,108,151, 2, 2, 2, 2, 2, 2, + 67, 2,152,148,148,148,153, 44, 67, 67, 67, 67, 67, 55, 67, 67, + 67, 67, 44, 44, 44, 44, 44, 44, 67, 67, 67, 44, 44, 44, 44, 44, + 1, 2,154,155, 4, 4, 4, 4, 4, 67, 4, 4, 4, 4,156,157, + 158,105,105,105,105, 43, 43, 86,159, 40, 40, 67,105,160, 63, 67, + 36, 36, 36, 61, 57,161,162, 69, 36, 36, 36, 36, 36, 63, 40, 69, + 44, 44, 62, 36, 36, 36, 36, 36, 67, 27, 27, 67, 67, 67, 67, 67, + 67, 67, 67, 44, 44, 44, 44, 55, 67, 67, 67, 67, 67, 67, 67, 92, + 27, 27, 27, 27, 27, 67, 67, 67, 67, 67, 67, 67, 27, 27, 27, 27, + 163, 27, 27, 27, 27, 27, 27, 27, 36, 36, 83, 36, 36, 36, 36, 36, + 67, 67, 67, 92, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36,164, 2, 7, 7, 7, 7, 7, 36, 44, 44, 32, 32, 32, 32, 32, 32, 32, 70, 51,165, 43, 43, 43, 43, 43, 88, 32, 32, 32, 32, 32, 32, 40, 43, 36, 36, 36,105,105,105,105,105, 43, 2, 2, 2, 44, 44, 44, 44, @@ -3092,7 +3123,7 @@ _hb_ucd_u8[14752] = 16, 32, 32, 32, 32, 32, 32, 32, 45, 16, 16, 16, 34, 34, 34, 32, 32, 32, 32, 32, 42,166, 34, 35, 32, 32, 16, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 11, 11, 32, 11, 11, 32, 32, 32, 32, 32, 32, - 32, 32, 11, 11, 34,110, 44, 44, 32,150,150, 32, 32, 44, 44, 44, + 32, 32, 11, 11, 34, 34, 32, 44, 32,150,150, 32, 32, 32, 47, 44, 44, 40,167, 35, 40, 35, 36, 36, 36, 71, 36, 71, 36, 70, 36, 36, 36, 94, 87, 85, 67, 67, 80, 44, 27, 27, 27, 67,168, 44, 44, 44, 36, 36, 2, 2, 44, 44, 44, 44, 86, 36, 36, 36, 36, 36, 36, 36, @@ -3153,8 +3184,10 @@ _hb_ucd_u8[14752] = 36, 61, 44, 44, 27, 27, 27, 27, 36, 44, 44, 44, 93, 2, 64, 44, 44, 44, 44, 44,179, 27, 27, 27, 11, 47, 44, 44, 44, 44, 44, 44, 16,110, 44, 44, 44, 27, 27, 27, 36, 36, 43, 43, 44, 44, 44, 44, - 27, 27, 27, 27, 27, 27, 27,100, 36, 36, 36, 36, 36, 57,184, 44, - 36, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 57, 43, + 7, 7, 7, 7, 7, 36, 36, 69, 11, 11, 11, 44, 57, 43, 43,159, + 16, 16, 16, 44, 44, 44, 44, 8, 27, 27, 27, 27, 27, 27, 27,100, + 36, 36, 36, 36, 36, 57,184, 44, 36, 44, 44, 44, 44, 44, 44, 44, + 44, 36, 61, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 43, 43, 27, 27, 27, 95, 44, 44, 44, 44,180, 27, 30, 2, 2, 44, 44, 44, 36, 43, 43, 2, 2, 44, 44, 44, 36, 36,183, 27, 27, 27, 44, 44, 87, 98, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, @@ -3172,14 +3205,18 @@ _hb_ucd_u8[14752] = 86, 87, 43, 43, 43, 80, 44, 44, 43, 86, 62, 36, 36, 36, 61, 62, 61, 36, 62, 36, 36, 57, 71, 86, 85, 86, 90, 89, 90, 89, 86, 44, 61, 44, 44, 89, 44, 44, 62, 36, 36, 86, 44, 43, 43, 43, 80, 44, - 43, 43, 80, 44, 44, 44, 44, 44, 36, 36, 94, 86, 43, 43, 43, 43, - 86, 43, 85, 71, 36, 63, 2, 2, 7, 7, 7, 7, 7, 2, 93, 71, - 86, 87, 43, 43, 85, 85, 86, 87, 85, 43, 36, 72, 44, 44, 44, 44, - 36, 36, 36, 36, 36, 36, 36, 94, 86, 43, 43, 44, 86, 86, 43, 87, - 60, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 43, 44, - 86, 87, 43, 43, 43, 85, 87, 87, 60, 2, 61, 44, 44, 44, 44, 44, - 2, 2, 2, 2, 2, 2, 64, 44, 36, 36, 36, 36, 36, 70, 87, 86, - 43, 43, 43, 87, 63, 44, 44, 44, 86, 43, 43, 87, 43, 43, 44, 44, + 43, 43, 80, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 62, 44, 61, + 36, 36, 36, 62, 86, 87, 43, 43, 80, 90, 89, 89, 86, 90, 86, 85, + 71, 71, 2, 93, 64, 44, 44, 44, 57, 80, 44, 44, 44, 44, 44, 44, + 36, 36, 94, 86, 43, 43, 43, 43, 86, 43, 85, 71, 36, 63, 2, 2, + 7, 7, 7, 7, 7, 2, 93, 71, 86, 87, 43, 43, 85, 85, 86, 87, + 85, 43, 36, 72, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 36, 94, + 86, 43, 43, 44, 86, 86, 43, 87, 60, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 36, 36, 43, 44, 86, 87, 43, 43, 43, 85, 87, 87, + 60, 2, 61, 44, 44, 44, 44, 44, 2, 2, 2, 2, 2, 2, 64, 44, + 36, 36, 36, 36, 36, 70, 87, 86, 43, 43, 43, 87, 63, 44, 44, 44, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 44, 44, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 61, 57, 87, 86, 43, 43, 87, 43, 43, 44, 44, 7, 7, 7, 7, 7, 27, 2, 97, 43, 43, 43, 43, 87, 60, 44, 44, 27,100, 44, 44, 44, 44, 44, 62, 36, 36, 36, 61, 62, 44, 36, 36, 36, 36, 62, 61, 36, 36, 36, 36, 86, 86, 86, 89, 90, 57, 85, 71, @@ -3189,49 +3226,52 @@ _hb_ucd_u8[14752] = 2, 2, 2, 59, 44, 44, 44, 44, 70, 43, 43, 85, 87, 43, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 85, 43, 2, 72, 2, 2, 64, 44, 44, 44, 44, 44, 44, 2, 2, 2, 2, 2, 44, 44, 44, - 43, 43, 43, 80, 43, 43, 43, 87, 63, 2, 2, 44, 44, 44, 44, 44, - 2, 36, 36, 36, 36, 36, 36, 36, 44, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 89, 43, 43, 43, 85, 43, 87, 80, 44, 44, 44, 44, - 36, 36, 36, 61, 36, 62, 36, 36, 70, 43, 43, 80, 44, 80, 43, 57, - 43, 43, 43, 70, 44, 44, 44, 44, 36, 36, 36, 62, 61, 36, 36, 36, - 36, 36, 36, 36, 36, 86, 86, 90, 43, 89, 87, 87, 61, 44, 44, 44, - 36, 70, 85,107, 64, 44, 44, 44, 43, 94, 36, 36, 36, 36, 36, 36, - 36, 36, 86, 43, 43, 80, 44, 86, 85, 60, 2, 2, 2, 2, 2, 2, + 63, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 80, 43, 43, 43, 87, + 63, 2, 2, 44, 44, 44, 44, 44, 2, 36, 36, 36, 36, 36, 36, 36, + 44, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 89, 43, 43, 43, + 85, 43, 87, 80, 44, 44, 44, 44, 36, 36, 36, 61, 36, 62, 36, 36, + 70, 43, 43, 80, 44, 80, 43, 57, 43, 43, 43, 70, 44, 44, 44, 44, + 36, 36, 36, 62, 61, 36, 36, 36, 36, 36, 36, 36, 36, 86, 86, 90, + 43, 89, 87, 87, 61, 44, 44, 44, 36, 70, 85,107, 64, 44, 44, 44, + 43, 94, 36, 36, 36, 36, 36, 36, 36, 36, 86, 43, 43, 80, 44, 86, + 85, 60, 2, 2, 2, 2, 2, 2, 7, 7, 7, 7, 7, 80, 44, 44, 27, 27, 91, 67, 67, 67, 56, 20,168, 67, 67, 67, 67, 67, 67, 67, 67, 44, 44, 44, 44, 44, 44, 93,105,105,105,105,105,105,105,181, 2, 2, 64, 44, 44, 44, 44, 44, 63, 64, 44, 44, 44, 44, 44, 44, 65, 65, 65, 65, 65, 65, 65, 65, 71, 36, 36, 70, 43, 43, 43, 43, - 43, 43, 43, 44, 44, 44, 44, 44, 43, 43, 60, 44, 44, 44, 44, 44, + 43, 43, 43, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 36, 43, + 43, 43, 43, 43, 43, 86, 87, 43, 43, 43, 60, 44, 44, 44, 44, 44, 43, 43, 43, 60, 2, 2, 67, 67, 40, 40, 97, 44, 44, 44, 44, 44, 7, 7, 7, 7, 7,179, 27, 27, 27, 62, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 44, 44, 62, 36, 27, 27, 27, 30, 2, 64, 44, 44, + 36, 36, 36, 36, 44, 44, 62, 36, 40, 69, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 83,164, 2, 27, 27, 27, 30, 2, 64, 44, 44, 36, 36, 36, 36, 36, 61, 44, 57, 94, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 44, 44, 44, 57, 43, 74, 40, 40, 40, 40, 40, 40, 40, 88, 80, 44, 44, 44, 44, 44, - 86, 44, 44, 44, 44, 44, 44, 44, 40, 40, 52, 40, 40, 40, 52, 81, - 36, 61, 44, 44, 44, 44, 44, 44, 44, 61, 44, 44, 44, 44, 44, 44, - 36, 61, 62, 44, 44, 44, 44, 44, 44, 44, 36, 36, 44, 44, 44, 44, - 36, 36, 36, 36, 36, 44, 50, 60, 65, 65, 44, 44, 44, 44, 44, 44, - 43, 43, 43, 43, 43, 43, 43, 44, 43, 43, 43, 80, 44, 44, 44, 44, - 67, 67, 67, 92, 55, 67, 67, 67, 67, 67,186, 87, 43, 67,186, 86, - 86,187, 65, 65, 65, 84, 43, 43, 43, 76, 50, 43, 43, 43, 67, 67, - 67, 67, 67, 67, 67, 43, 43, 67, 67, 43, 76, 44, 44, 44, 44, 44, - 27, 27, 44, 44, 44, 44, 44, 44, 11, 11, 11, 11, 11, 16, 16, 16, - 16, 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 16, - 16, 16,110, 16, 16, 16, 16, 16, 11, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 47, 11, 44, 47, 48, 47, 48, 11, 47, 11, - 11, 11, 11, 16, 16,150,150, 16, 16, 16,150, 16, 16, 16, 16, 16, - 16, 16, 11, 48, 11, 47, 48, 11, 11, 11, 47, 11, 11, 11, 47, 16, - 16, 16, 16, 16, 11, 48, 11, 47, 11, 11, 47, 47, 44, 11, 11, 11, - 47, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 11, 11, - 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 44, 11, 11, 11, 11, - 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, - 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, - 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, - 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 32, 44, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 43, 43, 43, 76, 67, 50, 43, 43, + 86, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 62, + 40, 40, 52, 40, 40, 40, 52, 81, 36, 61, 44, 44, 44, 44, 44, 44, + 44, 61, 44, 44, 44, 44, 44, 44, 36, 61, 62, 44, 44, 44, 44, 44, + 44, 44, 36, 36, 44, 44, 44, 44, 36, 36, 36, 36, 36, 44, 50, 60, + 65, 65, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43, 43, 44, + 43, 43, 43, 80, 44, 44, 44, 44, 67, 67, 67, 92, 55, 67, 67, 67, + 67, 67,186, 87, 43, 67,186, 86, 86,187, 65, 65, 65, 84, 43, 43, + 43, 76, 50, 43, 43, 43, 67, 67, 67, 67, 67, 67, 67, 43, 43, 67, + 67, 43, 76, 44, 44, 44, 44, 44, 27, 27, 44, 44, 44, 44, 44, 44, + 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 16, 16, 16,110, 16, 16, 16, 16, 16, + 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 47, 11, + 44, 47, 48, 47, 48, 11, 47, 11, 11, 11, 11, 16, 16,150,150, 16, + 16, 16,150, 16, 16, 16, 16, 16, 16, 16, 11, 48, 11, 47, 48, 11, + 11, 11, 47, 11, 11, 11, 47, 16, 16, 16, 16, 16, 11, 48, 11, 47, + 11, 11, 47, 47, 44, 11, 11, 11, 47, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, + 16, 16, 16, 44, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, + 11, 11, 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, + 16, 16, 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, + 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, + 16, 33, 16, 16, 16, 32, 44, 7, 43, 43, 43, 76, 67, 50, 43, 43, 43, 43, 43, 43, 43, 43, 76, 67, 67, 67, 50, 67, 67, 67, 67, 67, 67, 67, 76, 21, 2, 2, 44, 44, 44, 44, 44, 44, 44, 57, 43, 43, 16, 16, 16, 16, 16, 39, 16, 16, 16, 16, 16, 16, 16, 16, 16,110, @@ -3241,22 +3281,23 @@ _hb_ucd_u8[14752] = 43, 43, 43, 74, 40, 40, 40, 44, 7, 7, 7, 7, 7, 44, 44, 77, 36, 36, 36, 36, 36, 36, 36, 80, 36, 36, 36, 36, 36, 36, 43, 43, 7, 7, 7, 7, 7, 44, 44, 96, 36, 36, 36, 36, 36, 83, 43, 43, - 36, 36, 36, 61, 36, 36, 62, 61, 36, 36, 61,179, 27, 27, 27, 27, - 16, 16, 43, 43, 43, 74, 44, 44, 27, 27, 27, 27, 27, 27,163, 27, - 188, 27,100, 44, 44, 44, 44, 44, 27, 27, 27, 27, 27, 27, 27,163, - 27, 27, 27, 27, 27, 27, 27, 44, 36, 36, 62, 36, 36, 36, 36, 36, - 62, 61, 61, 62, 62, 36, 36, 36, 36, 61, 36, 36, 62, 62, 44, 44, - 44, 61, 44, 62, 62, 62, 62, 36, 62, 61, 61, 62, 62, 62, 62, 62, - 62, 61, 61, 62, 36, 61, 36, 36, 36, 61, 36, 36, 62, 36, 61, 61, - 36, 36, 36, 36, 36, 62, 36, 36, 62, 36, 62, 36, 36, 62, 36, 36, - 8, 44, 44, 44, 44, 44, 44, 44, 67, 67, 67, 67, 67, 67, 44, 44, - 55, 67, 67, 67, 67, 67, 67, 67, 27, 27, 27, 27, 27, 27, 91, 67, - 67, 67, 67, 67, 67, 67, 67, 44, 44, 44, 44, 67, 67, 67, 67, 67, - 67, 92, 44, 44, 44, 44, 44, 44, 67, 67, 67, 67, 92, 44, 44, 44, - 67, 44, 44, 44, 44, 44, 44, 44, 67, 67, 67, 67, 67, 25, 41, 41, - 67, 67, 67, 67, 44, 44, 67, 67, 67, 67, 67, 92, 44, 55, 67, 67, - 67, 67, 67, 67, 44, 44, 44, 44, 67, 67, 67, 67, 67, 67, 67, 55, - 67, 67, 67, 44, 44, 44, 44, 67, 67, 92, 67, 67, 67, 67, 67, 67, + 188, 7, 7, 7, 7,189, 44, 93, 36, 36, 36, 61, 36, 36, 62, 61, + 36, 36, 61,179, 27, 27, 27, 27, 16, 16, 43, 43, 43, 74, 44, 44, + 27, 27, 27, 27, 27, 27,163, 27,190, 27,100, 44, 44, 44, 44, 44, + 27, 27, 27, 27, 27, 27, 27,163, 27, 27, 27, 27, 27, 27, 27, 44, + 36, 36, 62, 36, 36, 36, 36, 36, 62, 61, 61, 62, 62, 36, 36, 36, + 36, 61, 36, 36, 62, 62, 44, 44, 44, 61, 44, 62, 62, 62, 62, 36, + 62, 61, 61, 62, 62, 62, 62, 62, 62, 61, 61, 62, 36, 61, 36, 36, + 36, 61, 36, 36, 62, 36, 61, 61, 36, 36, 36, 36, 36, 62, 36, 36, + 62, 36, 62, 36, 36, 62, 36, 36, 8, 44, 44, 44, 44, 44, 44, 44, + 67, 67, 67, 67, 67, 67, 44, 44, 55, 67, 67, 67, 67, 67, 67, 67, + 27, 27, 27, 27, 27, 27, 91, 67, 67, 67, 67, 67, 67, 67, 67, 44, + 44, 44, 44, 67, 67, 67, 67, 67, 67, 92, 44, 44, 44, 44, 44, 44, + 67, 67, 67, 67, 92, 44, 44, 44, 67, 44, 44, 44, 44, 44, 44, 44, + 67, 67, 67, 67, 67, 25, 41, 41, 67, 67, 67, 67, 44, 44, 67, 67, + 67, 67, 67, 92, 44, 55, 67, 67, 67, 67, 67, 67, 44, 44, 44, 44, + 67, 67, 67, 67, 67, 44, 44, 55, 67, 67, 67, 92, 44, 44, 44, 67, + 67, 67, 67, 67, 67, 67, 92, 55, 67, 92, 67, 67, 67, 67, 67, 67, 79, 44, 44, 44, 44, 44, 44, 44,171,171,171,171,171,171,171, 44, 171,171,171,171,171,171,171, 0, 0, 0, 29, 21, 21, 21, 23, 21, 22, 18, 21, 25, 21, 17, 13, 13, 25, 25, 25, 21, 21, 9, 9, 9, @@ -3282,420 +3323,609 @@ _hb_ucd_u8[14752] = 6, 21, 11, 21, 24, 9, 6, 9, 23, 26, 6, 10, 4, 4, 3, 3, 7, 25, 17, 16, 16, 22, 16, 16, 25, 17, 25, 2, 25, 24, 2, 15, 12, 15, 14, 2, 21, 14, 7, 15, 12, 17, 21, 1, 26, 10, 10, 1, - 23, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 10, 11, 12, - 13, 0, 14, 0, 0, 0, 0, 0, 15, 0, 16, 0, 0, 0, 0, 0, + 7, 13, 13, 2, 23, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 0, 10, 11, 12, 13, 0, 14, 0, 0, 0, 0, 0, 15, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, - 0, 21, 22, 23, 0, 0, 0, 24, 25, 26, 27, 28, 29, 30, 31, 32, - 33, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 35, 0, 36, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 20, 0, 21, 22, 23, 0, 0, 0, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 36, 0, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 39, 0, 0, 0, 0, - 0, 0, 40, 41, 42, 0, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 3, 0, 0, 0, 4, 5, - 6, 7, 0, 8, 9, 10, 0, 11, 12, 13, 14, 15, 16, 17, 16, 18, - 16, 19, 16, 19, 16, 19, 0, 19, 16, 20, 16, 19, 21, 19, 0, 22, - 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 32, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 34, 0, 0, 35, - 0, 0, 36, 0, 37, 0, 0, 0, 38, 39, 40, 41, 42, 43, 44, 45, - 46, 0, 0, 47, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, - 0, 0, 0, 50, 0, 51, 0, 52, 53, 0, 54, 0, 0, 0, 0, 0, - 0, 55, 56, 57, 0, 0, 0, 0, 58, 0, 0, 59, 60, 61, 62, 63, - 0, 0, 64, 65, 0, 0, 0, 66, 0, 0, 0, 0, 67, 0, 0, 0, - 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, - 0, 0, 0, 70, 0, 71, 0, 0, 72, 0, 0, 73, 0, 0, 0, 0, - 0, 0, 0, 0, 74, 0, 0, 0, 0, 0, 75, 76, 0, 77, 78, 0, - 0, 79, 80, 0, 81, 62, 0, 82, 83, 0, 0, 84, 85, 86, 0, 0, - 0, 87, 0, 88, 0, 0, 51, 89, 51, 0, 90, 0, 91, 0, 0, 0, - 80, 0, 0, 0, 92, 93, 0, 94, 95, 96, 97, 0, 0, 0, 0, 0, - 51, 0, 0, 0, 0, 98, 99, 0, 0, 0, 0, 0, 0,100, 0, 0, - 0, 0, 0,101,102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,103, - 0, 0,104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,105,106, 0, - 0,107, 0, 0, 0, 0, 0, 0,108, 0,109, 0,102, 0, 0, 0, - 0, 0,110,111, 0, 0, 0, 0, 0, 0, 0,112, 0, 0, 0, 0, - 0, 0, 0,113, 0,114, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, - 5, 6, 7, 0, 8, 0, 0, 0, 0, 9, 10, 11, 12, 0, 0, 0, - 0, 13, 0, 0, 14, 15, 0, 16, 0, 17, 18, 0, 0, 19, 0, 20, - 21, 0, 0, 0, 0, 0, 22, 23, 0, 24, 25, 0, 0, 26, 0, 0, - 0, 27, 0, 0, 28, 29, 30, 31, 0, 0, 0, 32, 33, 34, 0, 0, - 33, 0, 0, 35, 33, 0, 0, 0, 33, 36, 0, 0, 0, 0, 0, 37, - 38, 0, 0, 0, 0, 0, 0, 39, 40, 0, 0, 0, 0, 0, 0, 41, - 42, 0, 0, 0, 0, 43, 0, 44, 0, 0, 0, 45, 46, 0, 0, 0, - 47, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 50, 0, 0, - 0, 51, 0, 52, 0, 53, 0, 0, 0, 0, 54, 0, 0, 0, 0, 55, - 0, 56, 0, 0, 0, 0, 57, 58, 0, 0, 0, 59, 60, 0, 0, 0, - 0, 0, 0, 61, 52, 0, 62, 63, 0, 0, 64, 0, 0, 0, 65, 66, - 0, 0, 0, 67, 0, 68, 69, 70, 71, 72, 1, 73, 0, 74, 75, 76, - 0, 0, 77, 78, 0, 0, 0, 79, 0, 0, 1, 1, 0, 0, 80, 0, - 0, 81, 0, 0, 0, 0, 77, 82, 0, 83, 0, 0, 0, 0, 0, 78, - 84, 0, 85, 0, 52, 0, 1, 78, 0, 0, 86, 0, 0, 87, 0, 0, - 0, 0, 0, 88, 57, 0, 0, 0, 0, 0, 0, 89, 90, 0, 0, 84, - 0, 0, 33, 0, 0, 91, 0, 0, 0, 0, 92, 0, 0, 0, 0, 49, - 0, 0, 93, 0, 0, 0, 0, 94, 95, 0, 0, 96, 0, 0, 97, 0, - 0, 0, 98, 0, 0, 0, 99, 0, 0, 0, 0,100,101, 93, 0, 0, - 102, 0, 0, 0, 84, 0, 0,103, 0, 0, 0,104,105, 0, 0,106, - 107, 0, 0, 0, 0, 0, 0,108, 0, 0,109, 0, 0, 0, 0,110, - 33, 0,111,112,113, 35, 0, 0,114, 0, 0, 0,115, 0, 0, 0, - 0, 0, 0,116, 0, 0,117, 0, 0, 0, 0,118, 88, 0, 0, 0, - 0, 0, 57, 0, 0, 0, 0, 52,119, 0, 0, 0, 0,120, 0, 0, - 121, 0, 0, 0, 0,119, 0, 0,122, 0, 0, 0, 0, 0, 0,123, - 0, 0, 0,124, 0, 0, 0,125, 0,126, 0, 0, 0, 0,127,128, - 129, 0,130, 0,131, 0, 0, 0,132,133,134, 0, 77, 0, 0, 0, - 0, 0, 35, 0, 0, 0,135, 0, 0, 0,136, 0, 0,137, 0, 0, - 138, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, - 5, 6, 7, 4, 4, 8, 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, - 18, 1, 1, 1, 19, 1, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, - 25, 26, 27, 28, 29, 30, 0, 0, 1, 1, 31, 0, 0, 0, 32, 33, - 34, 35, 1, 36, 37, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, - 42, 0, 0, 0, 43, 36, 44, 45, 21, 45, 46, 0, 0, 0, 19, 1, - 21, 0, 0, 47, 0, 38, 48, 1, 1, 49, 49, 50, 0, 0, 51, 0, - 0, 0, 52, 1, 0, 0, 38, 14, 4, 1, 1, 1, 53, 21, 43, 52, - 54, 21, 35, 1, 0, 0, 0, 55, 0, 0, 0, 56, 57, 58, 0, 0, - 0, 0, 0, 59, 0, 60, 0, 0, 0, 0, 61, 62, 0, 0, 63, 0, - 0, 0, 64, 0, 0, 0, 65, 0, 0, 0, 66, 0, 0, 0, 67, 0, - 0, 0, 68, 0, 0, 69, 70, 0, 71, 72, 73, 74, 75, 76, 0, 0, - 0, 77, 0, 0, 0, 78, 79, 0, 0, 0, 0, 47, 0, 0, 0, 49, - 0, 80, 0, 0, 0, 62, 0, 0, 63, 0, 0, 81, 0, 0, 82, 0, - 0, 0, 83, 0, 0, 19, 84, 0, 62, 0, 0, 0, 0, 49, 1, 85, - 1, 52, 15, 86, 36, 10, 21, 87, 0, 55, 0, 0, 0, 0, 19, 10, - 1, 0, 0, 0, 0, 0, 88, 0, 0, 89, 0, 0, 88, 0, 0, 0, - 0, 78, 0, 0, 87, 9, 12, 4, 90, 8, 91, 47, 0, 58, 50, 0, - 21, 1, 21, 92, 93, 1, 1, 1, 1, 94, 95, 96, 97, 1, 98, 58, - 81, 99,100, 4, 58, 0, 0, 0, 0, 0, 0, 19, 50, 0, 0, 0, - 0, 0, 0, 61, 0, 0,101,102, 0, 0,103, 0, 0, 1, 1, 50, - 0, 0, 0, 38, 0, 63, 0, 0, 0, 0, 0, 62, 0, 0,104, 68, - 61, 0, 0, 0, 78, 0, 0, 0,105,106, 58, 38, 81, 0, 0, 0, - 0, 0, 0,107, 1, 14, 4, 12, 84, 0, 0, 0, 0, 38, 87, 0, - 0, 0, 0,108, 0, 0,109, 61, 0,110, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 40, + 0, 0, 0, 0, 0, 0, 41, 42, 43, 0, 44, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 3, 0, + 0, 0, 4, 5, 6, 7, 0, 8, 9, 10, 0, 11, 12, 13, 14, 15, + 16, 17, 16, 18, 16, 19, 16, 19, 16, 19, 0, 19, 16, 20, 16, 19, + 21, 19, 0, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 32, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, + 34, 0, 0, 35, 0, 0, 36, 0, 37, 0, 0, 0, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 0, 0, 47, 0, 0, 0, 48, 0, 0, 0, 49, + 0, 0, 0, 0, 0, 0, 0, 50, 0, 51, 0, 52, 53, 0, 54, 0, + 0, 0, 0, 0, 0, 55, 56, 57, 0, 0, 0, 0, 58, 0, 0, 59, + 60, 61, 62, 63, 0, 0, 64, 65, 0, 0, 0, 66, 0, 0, 0, 0, + 67, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 69, 0, 0, 0, 70, 0, 71, 0, 0, 72, 0, 0, 73, + 0, 0, 0, 0, 0, 0, 0, 0, 74, 75, 0, 0, 0, 0, 76, 77, + 0, 78, 79, 0, 0, 80, 81, 0, 82, 62, 0, 83, 84, 0, 0, 85, + 86, 87, 0, 88, 0, 89, 0, 90, 0, 0, 51, 91, 51, 0, 92, 0, + 93, 0, 0, 0, 81, 0, 0, 0, 94, 95, 0, 96, 97, 98, 99, 0, + 0, 0, 0, 0, 51, 0, 0, 0, 0,100,101, 0, 0, 0, 0, 0, + 0,102, 0, 0, 0, 0, 0, 0,103, 0, 0, 0, 0, 0, 0,104, + 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,106, 0, 0,107, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,108,109, 0, 0,110, 0, 0, + 0, 0, 0, 0,111, 0,112, 0,105, 0, 0, 0, 0, 0,113,114, + 0, 0, 0, 0, 0, 0, 0,115, 0, 0, 0,116, 0, 0, 0,117, + 0,118, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 0, + 8, 0, 0, 0, 0, 9, 10, 11, 12, 0, 0, 0, 0, 13, 0, 0, + 14, 15, 0, 16, 0, 17, 18, 0, 0, 19, 0, 20, 21, 0, 0, 0, + 0, 0, 22, 23, 0, 24, 25, 0, 0, 26, 0, 0, 0, 27, 0, 0, + 28, 29, 30, 31, 0, 0, 0, 32, 33, 34, 0, 0, 33, 0, 0, 35, + 33, 0, 0, 0, 33, 36, 0, 0, 0, 0, 0, 37, 38, 0, 0, 0, + 0, 0, 0, 39, 40, 0, 0, 0, 0, 0, 0, 41, 42, 0, 0, 0, + 0, 43, 0, 44, 0, 0, 0, 45, 46, 0, 0, 0, 47, 0, 0, 0, + 0, 0, 0, 48, 49, 0, 0, 0, 0, 50, 0, 0, 0, 51, 0, 52, + 0, 53, 0, 0, 0, 0, 54, 0, 0, 0, 0, 55, 0, 56, 0, 0, + 0, 0, 57, 58, 0, 0, 0, 59, 60, 0, 0, 0, 0, 0, 0, 61, + 52, 0, 62, 63, 0, 0, 64, 0, 0, 0, 65, 66, 0, 0, 0, 67, + 0, 68, 69, 70, 71, 72, 1, 73, 0, 74, 75, 76, 0, 0, 77, 78, + 0, 0, 0, 79, 0, 0, 1, 1, 0, 0, 80, 0, 0, 81, 0, 0, + 0, 0, 77, 82, 0, 83, 0, 0, 0, 0, 0, 78, 84, 0, 85, 0, + 52, 0, 1, 78, 0, 0, 86, 0, 0, 87, 0, 0, 0, 0, 0, 88, + 57, 0, 0, 0, 0, 0, 0, 89, 90, 0, 0, 84, 0, 0, 33, 0, + 0, 91, 0, 0, 0, 0, 92, 0, 0, 0, 0, 49, 0, 0, 93, 0, + 0, 0, 0, 94, 95, 0, 0, 96, 0, 0, 97, 0, 0, 0, 98, 0, + 0, 0, 99, 0, 0, 0,100, 0, 0, 0, 0,101,102, 93, 0, 0, + 103, 0, 0, 0, 84, 0, 0,104, 0, 0, 0,105,106, 0, 0,107, + 108, 0, 0, 0, 0, 0, 0,109, 0, 0,110, 0, 0, 0, 0,111, + 33, 0,112,113,114, 57, 0, 0,115, 35, 0, 0,116, 0, 0, 0, + 117, 0, 0, 0, 0, 0, 0,118, 0, 0,119, 0, 0, 0, 0,120, + 88, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 52,121, 0, 0, 0, + 0,122, 0, 0,123, 0, 0, 0, 0,121, 0, 0,124, 0, 0, 0, + 0, 0, 79, 0, 0, 0, 0,125, 0, 0, 0,126, 0, 0, 0,127, + 0,128, 0, 0, 0, 0,129,130,131, 0,132, 0,133, 0, 0, 0, + 134,135,136, 0, 77, 0, 0, 0, 0, 0, 35, 0, 0, 0,137, 0, + 0, 0,138, 0, 0, 0,139, 0, 0,140, 0, 0,141, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 4, + 4, 8, 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, 18, 1, 1, 1, + 19, 1, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, 25, 26, 27, 28, + 29, 30, 0, 0, 1, 1, 31, 0, 0, 0, 32, 33, 34, 35, 1, 36, + 37, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, 42, 0, 0, 0, + 43, 36, 44, 45, 21, 45, 46, 0, 0, 0, 19, 1, 21, 0, 0, 47, + 0, 38, 48, 1, 1, 49, 49, 50, 0, 0, 51, 0, 0, 19, 52, 1, + 0, 0, 38, 14, 4, 1, 1, 1, 53, 21, 43, 52, 54, 21, 35, 1, + 0, 0, 0, 55, 0, 0, 0, 56, 57, 58, 0, 0, 0, 0, 0, 59, + 0, 60, 0, 0, 0, 0, 61, 62, 0, 0, 63, 0, 0, 0, 64, 0, + 0, 0, 65, 0, 0, 0, 66, 0, 0, 0, 67, 0, 0, 0, 68, 0, + 0, 69, 70, 0, 71, 72, 73, 74, 75, 76, 0, 0, 0, 77, 0, 0, + 0, 78, 79, 0, 0, 0, 0, 47, 0, 0, 0, 49, 0, 80, 0, 0, + 0, 62, 0, 0, 63, 0, 0, 81, 0, 0, 82, 0, 0, 0, 83, 0, + 0, 19, 84, 0, 62, 0, 0, 0, 0, 49, 1, 85, 1, 52, 15, 86, + 36, 10, 21, 87, 0, 55, 0, 0, 0, 0, 19, 10, 1, 0, 0, 0, + 0, 0, 88, 0, 0, 89, 0, 0, 88, 0, 0, 0, 0, 78, 0, 0, + 87, 9, 12, 4, 90, 8, 91, 47, 0, 58, 50, 0, 21, 1, 21, 92, + 93, 1, 1, 1, 1, 94, 95, 96, 97, 1, 98, 58, 81, 99,100, 4, + 58, 0, 0, 0, 0, 0, 0, 19, 50, 0, 0, 0, 0, 0, 0, 61, + 0, 0,101,102, 0, 0,103, 0, 0, 1, 1, 50, 0, 0, 0, 38, + 0, 63, 0, 0, 0, 0, 0, 62, 0, 0,104, 68, 61, 0, 0, 0, + 78, 0, 0, 0,105,106, 58, 38, 81, 0, 0, 0, 0, 0, 0,107, + 1, 14, 4, 12, 84, 0, 0, 0, 0, 38, 87, 0, 0, 0, 0,108, + 0, 0,109, 61, 0,110, 0, 0, 0, 1, 0, 0, 0, 0, 49, 50, 0, 0, 19, 58, 0, 0, 0, 51, 0,111, 14, 52,112, 41, 0, 0, 62, 0, 0, 61, 0, 0,113, 0, 87, 0, 0, 0, 61, 62, 0, 0, 62, 0, 89, 0, 0,113, 0, 0, 0, 0,114, 0, 0, 0, 78, 55, - 0, 38, 1, 58, 1, 58, 0, 0, 63, 89, 0, 0,115, 0, 0, 0, - 55, 0, 0, 0, 0,115, 0, 0, 0, 0, 61, 0, 0, 0, 0, 79, - 0, 61, 0, 0, 0, 0, 56, 0, 89, 80, 0, 0, 79, 0, 0, 0, - 8, 91, 0, 0, 1, 87, 0, 0,116, 0, 0, 0, 0, 0, 0,117, - 0,118,119,120,121, 0,104, 4,122, 49, 23, 0, 0, 0, 38, 50, - 38, 58, 0, 0, 1, 87, 1, 1, 1, 1, 39, 1, 48,105, 87, 0, - 0, 0, 0, 1, 0, 0, 0,123, 4,122, 0, 0, 0, 1,124, 0, - 0, 0, 0, 0,230,230,230,230,230,232,220,220,220,220,232,216, - 220,220,220,220,220,202,202,220,220,220,220,202,202,220,220,220, - 1, 1, 1, 1, 1,220,220,220,220,230,230,230,230,240,230,220, - 220,220,230,230,230,220,220, 0,230,230,230,220,220,220,220,230, - 232,220,220,230,233,234,234,233,234,234,233,230, 0, 0, 0,230, - 0,220,230,230,230,230,220,230,230,230,222,220,230,230,220,220, - 230,222,228,230, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, - 21, 22, 0, 23, 0, 24, 25, 0,230,220, 0, 18, 30, 31, 32, 0, - 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, 34,230,230,220,220,230, - 220,230,230,220, 35, 0, 0, 0, 0, 0,230,230,230, 0, 0,230, - 230, 0,220,230,230,220, 0, 0, 0, 36, 0, 0,230,220,230,230, - 220,220,230,220,220,230,220,230,220,230,230, 0, 0,220, 0, 0, - 230,230, 0,230, 0,230,230,230,230,230, 0, 0, 0,220,220,220, - 230,220,220,220,230,230, 0,220, 27, 28, 29,230, 7, 0, 0, 0, - 0, 9, 0, 0, 0,230,220,230,230, 0, 0, 0, 0, 0,230, 0, - 0, 84, 91, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 9, 0, - 103,103, 9, 0,107,107,107,107,118,118, 9, 0,122,122,122,122, - 220,220, 0, 0, 0,220, 0,220, 0,216, 0, 0, 0,129,130, 0, - 132, 0, 0, 0, 0, 0,130,130,130,130, 0, 0,130, 0,230,230, - 9, 0,230,230, 0, 0,220, 0, 0, 0, 0, 7, 0, 9, 9, 0, - 9, 9, 0, 0, 0,230, 0, 0, 0,228, 0, 0, 0,222,230,220, - 220, 0, 0, 0,230, 0, 0,220,230,220, 0,220,230,230,230, 0, - 0, 0, 9, 9, 0, 0, 7, 0,230, 0, 1, 1, 1, 0, 0, 0, - 230,234,214,220,202,230,230,230,230,230,232,228,228,220,218,230, - 233,220,230,220,230,230, 1, 1, 1, 1, 1,230, 0, 1, 1,230, - 220,230, 1, 1, 0, 0,218,228,232,222,224,224, 0, 8, 8, 0, - 0, 0, 0,220,230, 0,230,230,220, 0, 0,230, 0, 0, 26, 0, - 0,220, 0,230,230, 1,220, 0, 0,230,220, 0, 0, 0,220,220, - 0, 0,230,220, 0, 9, 7, 0, 0, 7, 9, 0, 0, 0, 9, 7, - 6, 6, 0, 0, 0, 0, 1, 0, 0,216,216, 1, 1, 1, 0, 0, - 0,226,216,216,216,216,216, 0,220,220,220, 0,232,232,220,230, - 230,230, 7, 0, 16, 17, 17, 33, 17, 49, 17, 17, 84, 97,135,145, - 26, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 0, 38, 1, 58, 1, 58, 0, 0, 0, 0, 0, 88, 63, 89, 0, 0, + 115, 0, 0, 0, 55, 0, 0, 0, 0,115, 0, 0, 0, 0, 61, 0, + 0, 0, 0, 79, 0, 61, 0, 0, 0, 0, 56, 0, 89, 80, 0, 0, + 79, 0, 0, 0, 8, 91, 0, 0, 1, 87, 0, 0,116, 0, 0, 0, + 0, 0, 0,117, 0,118,119,120,121, 0,104, 4,122, 49, 23, 0, + 0, 0, 38, 50, 38, 58, 0, 0, 1, 87, 1, 1, 1, 1, 39, 1, + 48,105, 87, 0, 0, 0, 0, 1, 0, 0, 0,123, 0, 0, 0,112, + 4,122, 0, 0, 0, 1,124, 0, 0, 0, 0, 0,230,230,230,230, + 230,232,220,220,220,220,232,216,220,220,220,220,220,202,202,220, + 220,220,220,202,202,220,220,220, 1, 1, 1, 1, 1,220,220,220, + 220,230,230,230,230,240,230,220,220,220,230,230,230,220,220, 0, + 230,230,230,220,220,220,220,230,232,220,220,230,233,234,234,233, + 234,234,233,230, 0, 0, 0,230, 0,220,230,230,230,230,220,230, + 230,230,222,220,230,230,220,220,230,222,228,230, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 0, 23, 0, 24, 25, 0, + 230,220, 0, 18, 30, 31, 32, 0, 0, 0, 0, 27, 28, 29, 30, 31, + 32, 33, 34,230,230,220,220,230,220,230,230,220, 35, 0, 0, 0, + 0, 0,230,230,230, 0, 0,230,230, 0,220,230,230,220, 0, 0, + 0, 36, 0, 0,230,220,230,230,220,220,230,220,220,230,220,230, + 220,230,230, 0, 0,220, 0, 0,230,230, 0,230, 0,230,230,230, + 230,230, 0, 0, 0,220,220,220,230,220,220,220,230,230, 0,220, + 27, 28, 29,230, 7, 0, 0, 0, 0, 9, 0, 0, 0,230,220,230, + 230, 0, 0, 0, 0, 0,230, 0, 0, 84, 91, 0, 0, 0, 0, 9, + 9, 0, 0, 0, 0, 0, 9, 0,103,103, 9, 0,107,107,107,107, + 118,118, 9, 0,122,122,122,122,220,220, 0, 0, 0,220, 0,220, + 0,216, 0, 0, 0,129,130, 0,132, 0, 0, 0, 0, 0,130,130, + 130,130, 0, 0,130, 0,230,230, 9, 0,230,230, 0, 0,220, 0, + 0, 0, 0, 7, 0, 9, 9, 0, 9, 9, 0, 0, 0,230, 0, 0, + 0,228, 0, 0, 0,222,230,220,220, 0, 0, 0,230, 0, 0,220, + 230,220, 0,220,230,230,230, 0, 0, 0, 9, 9, 0, 0, 7, 0, + 230, 0, 1, 1, 1, 0, 0, 0,230,234,214,220,202,230,230,230, + 230,230,232,228,228,220,218,230,233,220,230,220,230,230, 1, 1, + 1, 1, 1,230, 0, 1, 1,230,220,230, 1, 1, 0, 0,218,228, + 232,222,224,224, 0, 8, 8, 0, 0, 0, 0,220,230, 0,230,230, + 220, 0, 0,230, 0, 0, 26, 0, 0,220, 0,230,230, 1,220, 0, + 0,230,220, 0, 0, 0,220,220, 0, 0,230,220, 0, 9, 7, 0, + 0, 7, 9, 0, 0, 0, 9, 7, 6, 6, 0, 0, 0, 0, 1, 0, + 0,216,216, 1, 1, 1, 0, 0, 0,226,216,216,216,216,216, 0, + 220,220,220, 0,232,232,220,230,230,230, 7, 0, 16, 17, 17, 33, + 17, 49, 17, 17, 84, 97,135,145, 26, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17,177, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 5, 3, - 3, 3, 3, 3, 6, 7, 8, 3, 3, 3, 3, 3, 9, 10, 11, 12, - 13, 3, 3, 3, 3, 3, 3, 3, 3, 14, 3, 15, 3, 3, 3, 3, - 3, 3, 16, 17, 18, 19, 20, 21, 3, 3, 3, 22, 23, 24, 3, 3, - 3, 3, 3, 3, 25, 3, 3, 3, 3, 3, 3, 3, 3, 26, 3, 3, - 27, 28, 0, 1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 3, - 0, 0, 0, 3, 0, 0, 0, 0, 0, 4, 0, 5, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 7, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 0, - 0, 0, 0, 9, 0, 9, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, - 13, 0, 0, 14, 15, 16, 6, 0, 17, 18, 19, 19, 19, 20, 21, 22, - 23, 24, 19, 25, 0, 26, 27, 19, 19, 28, 29, 30, 0, 31, 0, 0, - 0, 8, 0, 0, 0, 0, 0, 0, 0, 19, 28, 0, 32, 33, 9, 34, - 35, 19, 0, 0, 36, 37, 38, 39, 40, 19, 0, 41, 42, 43, 44, 31, - 0, 1, 45, 42, 0, 0, 0, 0, 0, 32, 14, 14, 0, 0, 0, 0, - 14, 0, 0, 46, 47, 47, 47, 47, 48, 49, 47, 47, 47, 47, 50, 51, - 52, 53, 43, 21, 0, 0, 0, 0, 0, 0, 0, 54, 6, 55, 0, 14, - 19, 1, 0, 0, 0, 0, 56, 57, 0, 0, 0, 0, 0, 19, 58, 31, - 0, 0, 0, 0, 0, 0, 0, 59, 14, 0, 0, 0, 0, 1, 0, 2, - 0, 0, 0, 3, 0, 0, 0, 60, 61, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 2, 3, 0, 4, 5, 0, 0, 6, 0, 0, - 0, 7, 0, 0, 0, 1, 1, 0, 0, 8, 9, 0, 8, 9, 0, 0, - 0, 0, 8, 9, 10, 11, 12, 0, 0, 0, 13, 0, 0, 0, 0, 14, - 15, 16, 17, 0, 0, 0, 1, 0, 0, 18, 19, 0, 0, 0, 20, 0, - 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 8, - 21, 9, 0, 0, 22, 0, 0, 0, 0, 1, 0, 23, 24, 25, 0, 0, - 26, 0, 0, 0, 8, 21, 27, 0, 1, 0, 0, 1, 1, 1, 1, 0, - 1, 28, 29, 30, 0, 31, 32, 20, 1, 1, 0, 0, 0, 8, 21, 9, - 1, 4, 5, 0, 0, 0, 33, 9, 0, 1, 1, 1, 0, 8, 21, 21, - 21, 21, 34, 1, 35, 21, 21, 21, 9, 36, 0, 0, 37, 38, 1, 0, - 39, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 8, 21, 9, 1, 0, - 0, 0, 40, 0, 8, 21, 21, 21, 21, 21, 21, 21, 21, 9, 0, 1, - 1, 1, 1, 8, 21, 21, 21, 9, 0, 0, 0, 41, 0, 42, 43, 0, - 0, 0, 1, 44, 0, 0, 0, 45, 8, 9, 1, 0, 0, 0, 8, 21, - 21, 21, 9, 0, 1, 0, 1, 1, 8, 21, 21, 9, 0, 4, 5, 8, - 9, 1, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 9, 10, 11, 11, 11, 11, 12, 13, - 13, 13, 13, 14, 15, 16, 17, 18, 19, 20, 21, 13, 22, 13, 13, 13, - 13, 23, 24, 24, 25, 26, 13, 13, 13, 27, 28, 29, 13, 30, 31, 32, - 33, 34, 35, 36, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 37, 7, 38, 39, 7, 40, 7, 7, - 7, 41, 13, 42, 7, 7, 43, 7, 44, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 45, 0, 0, 1, 2, 2, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, 32, 32, 33, 34, 35, 36, 37, 37, - 37, 37, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, - 51, 52, 2, 2, 53, 54, 55, 56, 57, 58, 59, 59, 59, 59, 60, 59, - 59, 59, 59, 59, 59, 59, 61, 61, 59, 59, 59, 59, 62, 63, 64, 65, - 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 59, 70, 70, - 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, - 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, - 70, 79, 70, 70, 70, 70, 80, 80, 80, 80, 80, 80, 80, 80, 80, 81, - 82, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 95, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, 70, 70, 97, 98, 99,100,101,101, - 102,103,104,105,106,107,108,109,110,111, 96,112,113,114,115,116, - 117,118,119,119,120,121,122,123,124,125,126,127,128,129,130,131, - 132, 96,133,134,135,136,137,138,139,140,141,142,143, 96,144,145, - 96,146,147,148,149, 96,150,151,152,153,154,155,156, 96,157,158, - 159,160, 96,161,162,163,164,164,164,164,164,164,164,165,166,164, - 167, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96,168,169,169,169,169,169,169,169,169,170, 96, - 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,171,171, - 171,171,172, 96, 96, 96,173,173,173,173,174,175,176,177, 96, 96, - 96, 96,178,179,180,181,182,182,182,182,182,182,182,182,182,182, - 182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182, - 182,182,182,182,182,183,182,182,182,182,182,182,184,184,184,185, - 186, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96,187,188,189,190,191,191,192, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,193,194, - 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96,195,196, 59,197,198,199,200,201,202, 96,203,204, - 205, 59, 59,206, 59,207,208,208,208,208,208,209, 96, 96, 96, 96, - 96, 96, 96, 96,210, 96,211,212,213, 96, 96,214, 96, 96, 96,215, - 96, 96, 96, 96, 96,216,217,218,219, 96, 96, 96, 96, 96,220,221, - 222, 96,223,224, 96, 96,225,226, 59,227,228, 96, 59, 59, 59, 59, - 59, 59, 59,229,230,231,232,233, 59, 59,234,235, 59,236, 96, 96, - 96, 96, 96, 96, 96, 96, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, - 70, 70, 70,237, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, - 70, 70, 70, 70,238, 70,239, 70, 70, 70, 70, 70, 70, 70, 70, 70, - 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, - 70, 70, 70,240, 70, 70, 70, 70, 70, 70, 70, 70, 70,241, 70, 70, - 70, 70,242, 96, 96, 96, 70, 70, 70, 70,243, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 70, 70, 70, 70, 70, 70,244, 70, 70, 70, - 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,245, 96, 96, - 96, 96, 96, 96, 96, 96,246, 96,247,248, 0, 1, 2, 2, 0, 1, - 2, 2, 2, 3, 4, 5, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 0, 0, 0, 19, 0, 19, 0, 0, 0, 0, 0, - 26, 26, 1, 1, 1, 1, 9, 9, 9, 9, 0, 9, 9, 9, 2, 2, - 9, 9, 9, 9, 0, 9, 2, 2, 2, 2, 9, 0, 9, 0, 9, 9, - 9, 2, 9, 2, 9, 9, 9, 9, 2, 9, 9, 9, 55, 55, 55, 55, - 55, 55, 6, 6, 6, 6, 6, 1, 1, 6, 2, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 2, 2, 14, 14, 14, 14, 14, 14, 14, 14, 14, - 14, 2, 2, 2, 2, 14, 14, 2, 2, 2, 3, 3, 3, 3, 3, 0, - 3, 3, 0, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 1, 1, 1, - 3, 3, 1, 3, 3, 3, 37, 37, 37, 37, 37, 37, 2, 37, 37, 37, - 37, 2, 2, 37, 37, 37, 38, 38, 38, 38, 38, 38, 2, 2, 64, 64, - 64, 64, 64, 64, 64, 2, 2, 64, 64, 64, 90, 90, 90, 90, 90, 90, - 2, 2, 90, 90, 90, 2, 95, 95, 95, 95, 2, 2, 95, 2, 3, 3, - 3, 2, 3, 3, 2, 2, 3, 3, 0, 3, 7, 7, 7, 7, 7, 1, - 1, 1, 1, 7, 7, 7, 0, 0, 7, 7, 5, 5, 5, 5, 2, 5, - 5, 5, 5, 2, 2, 5, 5, 2, 5, 5, 5, 2, 5, 2, 2, 2, - 5, 5, 5, 5, 2, 2, 5, 5, 5, 2, 2, 2, 2, 5, 5, 5, - 2, 5, 2, 11, 11, 11, 11, 11, 11, 2, 2, 2, 2, 11, 11, 2, - 2, 11, 11, 11, 11, 11, 11, 2, 11, 11, 2, 11, 11, 2, 11, 11, - 2, 2, 2, 11, 2, 2, 11, 2, 11, 2, 2, 2, 11, 11, 2, 10, - 10, 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 2, 10, 10, 10, 10, - 2, 2, 10, 2, 2, 2, 2, 2, 10, 10, 2, 21, 21, 21, 21, 21, - 21, 21, 21, 2, 2, 21, 21, 2, 21, 21, 21, 21, 2, 2, 21, 21, - 2, 21, 2, 2, 21, 21, 2, 2, 22, 22, 2, 22, 22, 22, 22, 22, - 22, 2, 22, 2, 22, 22, 22, 22, 2, 2, 2, 22, 22, 2, 2, 2, - 2, 22, 22, 2, 2, 2, 22, 22, 22, 22, 23, 23, 23, 23, 23, 2, - 23, 23, 23, 23, 2, 2, 2, 23, 23, 2, 23, 23, 23, 2, 2, 23, - 2, 2, 2, 2, 23, 23, 2, 2, 2, 23, 16, 16, 16, 16, 16, 2, - 16, 16, 2, 16, 16, 16, 16, 16, 2, 2, 2, 16, 16, 2, 2, 2, - 16, 16, 20, 20, 20, 20, 20, 2, 20, 20, 2, 2, 20, 20, 2, 36, - 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2, 36, 36, 36, 36, - 2, 36, 2, 36, 2, 2, 2, 2, 36, 2, 2, 2, 2, 36, 36, 2, - 36, 2, 36, 2, 2, 2, 2, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 2, 2, 2, 2, 0, 2, 18, 18, 2, 18, 2, 18, 18, 18, 18, - 18, 2, 18, 18, 18, 18, 2, 18, 2, 18, 18, 18, 2, 2, 18, 2, - 18, 2, 25, 25, 25, 25, 2, 25, 25, 25, 25, 2, 2, 2, 25, 2, - 25, 25, 25, 0, 0, 0, 0, 25, 25, 2, 33, 33, 33, 33, 8, 8, - 8, 8, 8, 8, 2, 8, 2, 8, 2, 2, 8, 8, 8, 0, 12, 12, - 12, 12, 30, 30, 30, 30, 30, 2, 30, 30, 30, 30, 2, 2, 30, 30, - 30, 2, 2, 30, 30, 30, 30, 2, 2, 2, 29, 29, 29, 29, 29, 29, - 2, 2, 28, 28, 28, 28, 34, 34, 34, 34, 34, 2, 2, 2, 35, 35, - 35, 35, 35, 35, 35, 0, 0, 0, 35, 35, 35, 2, 2, 2, 45, 45, - 45, 45, 45, 45, 2, 2, 2, 2, 2, 45, 44, 44, 44, 44, 44, 0, - 0, 2, 43, 43, 43, 43, 46, 46, 46, 46, 46, 2, 46, 46, 31, 31, - 31, 31, 31, 31, 2, 2, 32, 32, 0, 0, 32, 0, 32, 32, 32, 32, - 32, 32, 32, 32, 2, 2, 32, 2, 2, 2, 32, 32, 32, 2, 28, 28, - 2, 2, 48, 48, 48, 48, 48, 48, 48, 2, 48, 2, 2, 2, 52, 52, - 52, 52, 52, 52, 2, 2, 52, 2, 2, 2, 58, 58, 58, 58, 58, 58, - 2, 2, 58, 58, 58, 2, 2, 2, 58, 58, 54, 54, 54, 54, 2, 2, - 54, 54, 91, 91, 91, 91, 91, 91, 91, 2, 91, 2, 2, 91, 91, 91, - 2, 2, 1, 1, 1, 2, 62, 62, 62, 62, 62, 2, 2, 2, 62, 62, - 62, 2, 76, 76, 76, 76, 93, 93, 93, 93, 70, 70, 70, 70, 2, 2, - 2, 70, 70, 70, 2, 2, 2, 70, 70, 70, 73, 73, 73, 73, 6, 2, - 2, 2, 8, 8, 8, 2, 2, 8, 8, 8, 1, 1, 1, 0, 1, 0, - 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 2, 19, 19, - 9, 9, 9, 9, 9, 6, 19, 9, 9, 9, 9, 9, 19, 19, 9, 9, - 9, 19, 6, 19, 19, 19, 19, 19, 19, 9, 9, 9, 2, 2, 2, 9, - 2, 9, 2, 9, 9, 9, 1, 1, 0, 0, 0, 2, 0, 0, 0, 19, - 2, 2, 0, 0, 0, 19, 0, 0, 0, 2, 19, 2, 2, 2, 0, 2, - 2, 2, 1, 2, 2, 2, 0, 0, 9, 0, 0, 0, 19, 19, 27, 27, - 27, 27, 2, 2, 0, 0, 0, 0, 2, 0, 56, 56, 56, 56, 2, 55, - 55, 55, 61, 61, 61, 61, 2, 2, 2, 61, 61, 2, 2, 2, 0, 0, - 2, 2, 13, 13, 13, 13, 13, 13, 2, 13, 13, 13, 2, 2, 0, 13, - 0, 13, 0, 13, 13, 13, 13, 13, 1, 1, 1, 1, 12, 12, 2, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 1, 1, 0, 0, 15, - 15, 15, 0, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 0, 2, 26, - 26, 26, 26, 26, 26, 26, 2, 12, 12, 12, 12, 12, 12, 2, 12, 12, - 12, 0, 39, 39, 39, 39, 39, 2, 2, 2, 39, 39, 39, 2, 86, 86, - 86, 86, 77, 77, 77, 77, 79, 79, 79, 79, 19, 19, 19, 2, 19, 19, - 2, 19, 2, 19, 19, 19, 19, 19, 2, 2, 2, 2, 19, 19, 60, 60, - 60, 60, 60, 2, 2, 2, 65, 65, 65, 65, 75, 75, 75, 75, 75, 75, - 2, 2, 2, 2, 75, 75, 69, 69, 69, 69, 69, 69, 0, 69, 74, 74, - 74, 74, 2, 2, 2, 74, 12, 2, 2, 2, 84, 84, 84, 84, 84, 84, - 2, 0, 84, 84, 2, 2, 2, 2, 84, 84, 33, 33, 33, 2, 68, 68, - 68, 68, 68, 68, 68, 2, 68, 68, 2, 2, 92, 92, 92, 92, 92, 92, - 92, 2, 2, 2, 2, 92, 87, 87, 87, 87, 87, 87, 87, 2, 19, 9, - 19, 19, 19, 19, 0, 0, 87, 87, 2, 2, 2, 2, 2, 12, 2, 2, - 2, 4, 14, 2, 14, 2, 14, 14, 2, 14, 14, 2, 14, 14, 2, 2, - 2, 3, 3, 3, 0, 0, 2, 2, 3, 3, 1, 1, 6, 6, 3, 2, - 3, 3, 3, 2, 2, 0, 2, 0, 0, 0, 0, 0, 17, 17, 17, 17, - 0, 0, 2, 2, 12, 12, 49, 49, 49, 49, 2, 49, 49, 49, 49, 49, - 49, 2, 49, 49, 2, 49, 49, 49, 2, 2, 9, 2, 2, 2, 0, 1, - 2, 2, 71, 71, 71, 71, 71, 2, 2, 2, 67, 67, 67, 67, 67, 2, - 2, 2, 42, 42, 42, 42, 2, 42, 42, 42, 41, 41, 41, 41, 41, 41, - 41, 2,118,118,118,118,118,118,118, 2, 53, 53, 53, 53, 53, 53, - 2, 53, 59, 59, 59, 59, 59, 59, 2, 2, 40, 40, 40, 40, 51, 51, - 51, 51, 50, 50, 50, 50, 50, 50, 2, 2,135,135,135,135,106,106, - 106,106,104,104,104,104, 2, 2, 2,104,161,161,161,161,161,161, - 161, 2,161,161, 2,161,161, 2, 2, 2,110,110,110,110,110,110, - 110, 2,110,110, 2, 2, 19, 2, 19, 19, 47, 47, 47, 47, 47, 47, - 2, 2, 47, 2, 47, 47, 47, 47, 2, 47, 47, 2, 2, 2, 47, 2, - 2, 47, 81, 81, 81, 81, 81, 81, 2, 81,120,120,120,120,116,116, - 116,116,116,116,116, 2, 2, 2, 2,116,128,128,128,128,128,128, - 128, 2,128,128, 2, 2, 2, 2, 2,128, 66, 66, 66, 66, 2, 2, - 2, 66, 72, 72, 72, 72, 72, 72, 2, 2, 2, 2, 2, 72, 98, 98, - 98, 98, 97, 97, 97, 97, 2, 2, 97, 97, 57, 57, 57, 57, 2, 57, - 57, 2, 2, 57, 57, 57, 57, 57, 2, 2, 57, 57, 57, 2, 2, 2, - 2, 57, 57, 2, 2, 2, 88, 88, 88, 88,117,117,117,117,112,112, - 112,112,112,112,112, 2, 2, 2, 2,112, 78, 78, 78, 78, 78, 78, - 2, 2, 2, 78, 78, 78, 83, 83, 83, 83, 83, 83, 2, 2, 82, 82, - 82, 82, 82, 82, 82, 2,122,122,122,122,122,122, 2, 2, 2,122, - 122,122,122, 2, 2, 2, 89, 89, 89, 89, 89, 2, 2, 2,130,130, - 130,130,130,130,130, 2, 2, 2,130,130,144,144,144,144,144,144, - 2, 2,156,156,156,156,156,156, 2,156,156,156, 2, 2, 2, 3, - 3, 3,147,147,147,147,148,148,148,148,148,148, 2, 2,158,158, - 158,158,158,158, 2, 2,153,153,153,153,149,149,149,149,149,149, - 149, 2, 94, 94, 94, 94, 94, 94, 2, 2, 2, 2, 94, 94, 2, 2, - 2, 94, 85, 85, 85, 85, 85, 85, 85, 2, 2, 85, 2, 2,101,101, - 101,101,101, 2, 2, 2,101,101, 2, 2, 96, 96, 96, 96, 96, 2, - 96, 96,111,111,111,111,111,111,111, 2,100,100,100,100,108,108, - 108,108,108,108, 2,108,108,108, 2, 2,129,129,129,129,129,129, - 129, 2,129, 2,129,129,129,129, 2,129,129,129, 2, 2,109,109, - 109,109,109,109,109, 2,109,109, 2, 2,107,107,107,107, 2,107, - 107,107,107, 2, 2,107,107, 2,107,107,107,107, 2, 1,107,107, - 2, 2,107, 2, 2, 2, 2, 2, 2,107, 2, 2,107,107,137,137, - 137,137, 2,137,137,137,137,137, 2, 2,124,124,124,124,124,124, - 2, 2,123,123,123,123,123,123, 2, 2,114,114,114,114,114, 2, - 2, 2,114,114, 2, 2,102,102,102,102,102,102, 2, 2,126,126, - 126,126,126,126,126, 2, 2,126,126,126,142,142,142,142,125,125, - 125,125,125,125,125, 2, 2, 2, 2,125,154,154,154,154,154,154, - 154, 2, 2,154, 2, 2, 2,154,154, 2,154,154, 2,154,154, 2, - 2,154,154,154, 2, 2,150,150,150,150, 2, 2,150,150,150, 2, - 2, 2,141,141,141,141,140,140,140,140,140,140,140, 2,121,121, - 121,121,121, 2, 2, 2, 7, 7, 2, 2,133,133,133,133,133, 2, - 133,133,133,133,133, 2,133,133, 2, 2,133, 2, 2, 2,134,134, - 134,134, 2, 2,134,134, 2,134,134,134,134,134,134, 2,138,138, - 138,138,138,138,138, 2,138,138, 2,138, 2, 2,138, 2,138,138, - 2, 2,143,143,143,143,143,143, 2,143,143, 2,143,143,143,143, - 143, 2,143, 2, 2, 2,143,143, 2, 2,145,145,145,145,145, 2, - 2, 2,163,163,163,163,163, 2,163,163,163,163,163, 2, 2, 2, - 163,163,163,163, 2, 2, 86, 2, 2, 2, 63, 63, 63, 63, 63, 63, - 2, 2, 63, 63, 63, 2, 63, 2, 2, 2,157,157,157,157,157,157, - 157, 2, 80, 80, 80, 80, 80, 80, 2, 2,127,127,127,127,127,127, - 127, 2, 79, 2, 2, 2,115,115,115,115,115,115,115, 2,115,115, - 2, 2, 2, 2,115,115,159,159,159,159,159,159,159, 2,159,159, - 2, 2,103,103,103,103,103,103, 2, 2,119,119,119,119,119,119, - 2, 2,119,119, 2,119, 2,119,119,119,146,146,146,146,146,146, - 146, 2, 99, 99, 99, 99, 99, 99, 99, 2, 2, 2, 2, 99,136,139, - 13, 13,155, 2, 2, 2,136,136,136,136,155,155,155,155,155,155, - 2, 2,136, 2, 2, 2, 2, 17, 17, 17, 2, 17, 17, 2, 17, 15, - 15, 15, 17, 17, 17, 2, 2, 2, 15, 2, 2, 17, 2, 2,139,139, - 139,139,105,105,105,105,105,105,105, 2,105, 2, 2, 2,105,105, - 2, 2, 1, 1, 2, 2, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, - 1, 1, 2, 2, 0, 2, 2, 0, 0, 2, 0, 2, 0, 2,131,131, - 131,131, 2, 2, 2,131, 2,131,131,131, 56, 56, 56, 2, 56, 2, - 2, 56, 56, 56, 2, 56, 56, 2, 56, 56, 6, 6, 2, 2, 2, 2, - 2, 6,151,151,151,151,151, 2, 2, 2,151,151, 2, 2, 2, 2, - 151,151,160,160,160,160,160,160,160, 2,152,152,152,152,152,152, - 2, 2, 2, 2, 2,152,164,164,164,164,164,164, 2, 2, 2, 30, - 30, 2,113,113,113,113,113, 2, 2,113,113,113,113, 2,132,132, - 132,132,132,132, 2, 2, 2, 2,132,132, 2, 3, 3, 2, 3, 2, - 2, 3, 2, 3, 2, 3, 2, 2, 3, 2, 3, 2, 3, 2, 3, 3, - 2, 3, 15, 0, 0, 2, 13, 2, 2, 2, 13, 13, 13, 2, 2, 0, - 2, 2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, 10, - 9, 11, 12, 13, 9, 9, 9, 14, 9, 9, 15, 9, 9, 9, 9, 9, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,177, 0, 1, 2, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 3, 3, 3, 3, 3, 5, 3, 3, 3, 3, 3, 6, 7, 8, 3, + 3, 3, 3, 3, 9, 10, 11, 12, 13, 3, 3, 3, 3, 3, 3, 3, + 3, 14, 3, 15, 3, 3, 3, 3, 3, 3, 16, 17, 18, 19, 20, 21, + 3, 3, 3, 22, 23, 24, 3, 3, 3, 3, 3, 3, 25, 3, 3, 3, + 3, 3, 3, 3, 3, 26, 3, 3, 27, 28, 0, 1, 0, 0, 0, 0, + 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 0, + 0, 4, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 8, 9, 0, 0, 0, 0, 0, 0, 9, 0, 9, 0, 0, + 0, 0, 0, 0, 0, 10, 11, 12, 13, 0, 0, 14, 15, 16, 6, 0, + 17, 18, 19, 19, 19, 20, 21, 22, 23, 24, 19, 25, 0, 26, 27, 19, + 19, 28, 29, 30, 0, 31, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, + 0, 19, 28, 0, 32, 33, 9, 34, 35, 19, 0, 0, 36, 37, 38, 39, + 40, 19, 0, 41, 42, 43, 44, 31, 0, 1, 45, 42, 0, 0, 0, 0, + 0, 32, 14, 14, 0, 0, 0, 0, 14, 0, 0, 46, 47, 47, 47, 47, + 48, 49, 47, 47, 47, 47, 50, 51, 52, 53, 43, 21, 0, 0, 0, 0, + 0, 0, 0, 54, 6, 55, 0, 14, 19, 1, 0, 0, 0, 0, 56, 57, + 0, 0, 0, 0, 0, 19, 58, 31, 0, 0, 0, 0, 0, 0, 0, 59, + 14, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 60, + 61, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 3, + 0, 4, 5, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 1, 1, 0, + 0, 8, 9, 0, 8, 9, 0, 0, 0, 0, 8, 9, 10, 11, 12, 0, + 0, 0, 13, 0, 0, 0, 0, 14, 15, 16, 17, 0, 0, 0, 1, 0, + 0, 18, 19, 0, 0, 0, 20, 0, 0, 0, 1, 1, 1, 1, 0, 1, + 1, 1, 1, 1, 1, 1, 0, 8, 21, 9, 0, 0, 22, 0, 0, 0, + 0, 1, 0, 23, 24, 25, 0, 0, 26, 0, 0, 0, 8, 21, 27, 0, + 1, 0, 0, 1, 1, 1, 1, 0, 1, 28, 29, 30, 0, 31, 32, 20, + 1, 1, 0, 0, 0, 8, 21, 9, 1, 4, 5, 0, 0, 0, 33, 9, + 0, 1, 1, 1, 0, 8, 21, 21, 21, 21, 34, 1, 35, 21, 21, 21, + 9, 36, 0, 0, 37, 38, 1, 0, 39, 0, 0, 0, 1, 0, 1, 0, + 0, 0, 0, 8, 21, 9, 1, 0, 0, 0, 40, 0, 8, 21, 21, 21, + 21, 21, 21, 21, 21, 9, 0, 1, 1, 1, 1, 8, 21, 21, 21, 9, + 0, 0, 0, 41, 0, 42, 43, 0, 0, 0, 1, 44, 0, 0, 0, 45, + 8, 9, 1, 0, 0, 0, 8, 21, 21, 21, 9, 0, 1, 0, 1, 1, + 8, 21, 21, 9, 0, 4, 5, 8, 9, 1, 0, 0, 0, 1, 2, 3, + 4, 5, 5, 5, 5, 5, 6, 7, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 9, 16, 17, 18, 9, 19, 20, 21, 22, 23, 24, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 25, 26, 27, 5, 28, 29, 5, 30, 31, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 32, 0, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 20, 21, 22, 23, 24, 25, 26, 27, 28, 1, 29, 30, 31, + 32, 32, 33, 32, 32, 32, 34, 32, 32, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 45, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 46, 46, + 46, 46, 47, 48, 49, 50, 51, 52, 53, 54, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 44, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 95, + 95, 96, 97, 98, 56, 56, 56, 56, 56, 56, 56, 56, 56, 99,100,100, + 100,100,101,100,100,100,100,100,100,100,100,100,100,100,100,100, + 100,102,103,103,104, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,105, + 56, 56, 56, 56, 56, 56,106,106,107,108, 56,109,110,111,112,112, + 112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112, + 112,112,112,112,112,113,112,112,112,114,115,116, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,117,118,119, + 120, 56, 56, 56, 56, 56, 56, 56, 56, 56,121, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,122, 32,123,124,125,126, + 127,128,129,130,131,132,133,133,134, 56, 56, 56, 56,135,136,137, + 138, 56,139,140, 56,141,142,143, 56, 56,144,145,146, 56,147,148, + 149, 32, 32, 32,150,151,152, 32,153,154, 56, 56, 56, 56, 44, 44, + 44, 44, 44, 44,155, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44,156,157, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,158, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44,159, 44, 44,160, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 44, 44,161, 56, 56, 56, 56, 56, 44, 44, + 44,162, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44,163, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,164,165, + 0, 1, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 0, 0, 0, 0, 0, 0, + 19, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 0, 19, 0, + 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 0, 0, 0, 0, 0, + 26, 26, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, + 9, 9, 0, 9, 9, 9, 2, 2, 9, 9, 9, 9, 0, 9, 2, 2, + 2, 2, 9, 0, 9, 0, 9, 9, 9, 2, 9, 2, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 9, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 1, 1, 6, 2, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 2, 4, 4, 4, 2, 2, 4, 4, 4, 2, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 2, 2, + 2, 2, 2, 2, 2, 2, 14, 14, 14, 2, 2, 2, 2, 14, 14, 14, + 14, 14, 14, 2, 2, 2, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, + 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 0, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, 3, + 3, 3, 3, 3, 3, 3, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 2, 37, 37, 37, 37, 2, 2, 37, 37, 37, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 2, 2, 2, 2, 2, 2, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 2, 2, 64, 64, 64, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 2, 2, 90, 90, + 90, 90, 90, 90, 90, 2, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 2, 2, 95, 2, 37, 37, 37, 2, 2, 2, 2, 2, 3, 3, + 3, 3, 3, 3, 3, 2, 3, 3, 2, 2, 2, 2, 2, 3, 3, 3, + 0, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7, 7, 7, 1, + 1, 1, 1, 7, 7, 7, 7, 7, 7, 7, 0, 0, 7, 7, 5, 5, + 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 5, 5, 2, + 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, + 5, 5, 5, 5, 5, 5, 5, 2, 5, 2, 2, 2, 5, 5, 5, 5, + 2, 2, 5, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, 2, 2, 2, + 2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 5, 5, 2, 5, 5, 5, + 5, 5, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 11, + 11, 11, 2, 11, 11, 11, 11, 11, 11, 2, 2, 2, 2, 11, 11, 2, + 2, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, + 11, 11, 11, 11, 11, 11, 11, 2, 11, 11, 2, 11, 11, 2, 11, 11, + 2, 2, 11, 2, 11, 11, 11, 2, 2, 11, 11, 11, 2, 2, 2, 11, + 2, 2, 2, 2, 2, 2, 2, 11, 11, 11, 11, 2, 11, 2, 2, 2, + 2, 2, 2, 2, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, 2, 10, + 10, 10, 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, + 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, + 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 2, 10, 10, 10, 10, 10, + 2, 2, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, 2, 2, 10, 2, + 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 2, 2, 10, 10, 10, 10, + 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 2, 21, + 21, 21, 2, 21, 21, 21, 21, 21, 21, 21, 21, 2, 2, 21, 21, 2, + 2, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, + 21, 21, 21, 21, 21, 21, 21, 2, 21, 21, 2, 21, 21, 21, 21, 21, + 2, 2, 21, 21, 21, 21, 21, 2, 2, 21, 21, 21, 2, 2, 2, 2, + 2, 2, 2, 21, 21, 21, 2, 2, 2, 2, 21, 21, 2, 21, 21, 21, + 21, 21, 2, 2, 21, 21, 2, 2, 22, 22, 2, 22, 22, 22, 22, 22, + 22, 2, 2, 2, 22, 22, 22, 2, 22, 22, 22, 22, 2, 2, 2, 22, + 22, 2, 22, 2, 22, 22, 2, 2, 2, 22, 22, 2, 2, 2, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 2, 2, 2, 2, 22, 22, 22, 2, + 2, 2, 2, 2, 2, 22, 2, 2, 2, 2, 2, 2, 22, 22, 22, 22, + 22, 2, 2, 2, 2, 2, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 2, 23, 23, 23, 2, 23, 23, 23, 23, 23, 23, 23, 23, + 2, 2, 23, 23, 23, 23, 23, 2, 23, 23, 23, 23, 2, 2, 2, 2, + 2, 2, 2, 23, 23, 2, 23, 23, 23, 2, 2, 23, 2, 2, 23, 23, + 23, 23, 2, 2, 23, 23, 2, 2, 2, 2, 2, 2, 2, 23, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 16, 16, 16, 2, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 16, 16, 16, 16, 16, + 2, 2, 16, 16, 16, 16, 16, 2, 16, 16, 16, 16, 2, 2, 2, 2, + 2, 2, 2, 16, 16, 2, 16, 16, 16, 16, 2, 2, 16, 16, 2, 16, + 16, 16, 2, 2, 2, 2, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 2, 20, 20, 20, 2, 20, 20, 20, 20, 20, 20, 2, 2, + 2, 2, 20, 20, 20, 20, 20, 20, 20, 20, 2, 2, 20, 20, 2, 36, + 36, 36, 2, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 2, 2, 2, 36, 36, 36, 36, 36, 36, 36, 36, + 2, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 36, 2, 2, 2, 2, + 36, 2, 2, 2, 2, 36, 36, 36, 36, 36, 36, 2, 36, 2, 2, 2, + 2, 2, 2, 2, 36, 36, 2, 2, 36, 36, 36, 2, 2, 2, 2, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 2, 2, 2, 2, 0, 24, 24, 24, 24, 2, 2, 2, 2, 2, 18, + 18, 2, 18, 2, 18, 18, 18, 18, 18, 2, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 2, 18, 2, 18, 18, 18, + 18, 18, 18, 18, 2, 2, 18, 18, 18, 18, 18, 2, 18, 2, 18, 18, + 18, 18, 18, 18, 18, 2, 18, 18, 2, 2, 18, 18, 18, 18, 25, 25, + 25, 25, 25, 25, 25, 25, 2, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 2, 2, 2, 25, 25, 25, 25, 25, 2, 25, 25, 25, 25, + 25, 25, 25, 0, 0, 0, 0, 25, 25, 2, 2, 2, 2, 2, 33, 33, + 33, 33, 33, 33, 33, 33, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 2, 8, 2, 2, 2, 2, 2, 8, 2, 2, 8, 8, + 8, 0, 8, 8, 8, 8, 12, 12, 12, 12, 12, 12, 12, 12, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 2, 30, 30, 30, 30, 2, 2, 30, 30, + 30, 30, 30, 30, 30, 2, 30, 30, 30, 2, 2, 30, 30, 30, 30, 30, + 30, 30, 30, 2, 2, 2, 30, 30, 2, 2, 2, 2, 2, 2, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 2, 2, 28, 28, + 28, 28, 28, 28, 28, 28, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 2, 2, 2, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 0, 0, 0, 35, 35, 35, 2, 2, 2, 2, 2, 2, 2, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 0, 0, 2, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 2, 2, 2, 2, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 2, 46, 46, 46, 2, 46, 46, 2, 2, 2, 2, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 2, 2, 31, 31, + 2, 2, 2, 2, 2, 2, 32, 32, 0, 0, 32, 0, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 2, 2, 2, 2, 2, 2, 32, 2, + 2, 2, 2, 2, 2, 2, 32, 32, 32, 2, 2, 2, 2, 2, 28, 28, + 28, 28, 28, 28, 2, 2, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 2, 48, 48, 48, 48, 2, 2, 2, 2, 48, 2, + 2, 2, 48, 48, 48, 48, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 2, 2, 52, 52, 52, 52, 52, 2, 2, 2, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 2, 2, 2, 2, 58, 58, + 2, 2, 2, 2, 2, 2, 58, 58, 58, 2, 2, 2, 58, 58, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 2, 2, 54, 54, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 2, 91, 91, + 91, 91, 91, 2, 2, 91, 91, 91, 2, 2, 2, 2, 2, 2, 91, 91, + 91, 91, 91, 91, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 62, 62, + 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 2, 62, 62, 76, 76, + 76, 76, 76, 76, 76, 76, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 2, 2, 2, 2, 2, 2, 2, 2, 93, 93, 93, 93, 70, 70, + 70, 70, 70, 70, 70, 70, 2, 2, 2, 70, 70, 70, 70, 70, 70, 70, + 2, 2, 2, 70, 70, 70, 73, 73, 73, 73, 73, 73, 73, 73, 6, 6, + 6, 2, 2, 2, 2, 2, 8, 8, 8, 2, 2, 8, 8, 8, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, + 0, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 9, 9, 9, 9, + 9, 6, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 9, 9, 9, 9, + 19, 19, 19, 19, 9, 9, 9, 9, 9, 19, 19, 19, 19, 19, 6, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 9, 9, + 9, 9, 9, 9, 2, 2, 2, 9, 2, 9, 2, 9, 2, 9, 9, 9, + 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 2, 2, 9, 9, 9, 9, + 9, 9, 2, 9, 9, 9, 2, 2, 9, 9, 9, 2, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 2, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 2, 0, 0, 0, 19, 2, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 2, 19, 19, + 19, 19, 19, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, + 19, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 19, 0, + 0, 0, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, + 0, 2, 2, 2, 2, 2, 27, 27, 27, 27, 27, 27, 27, 27, 0, 0, + 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 56, 56, + 56, 56, 56, 56, 56, 56, 55, 55, 55, 55, 2, 2, 2, 2, 2, 55, + 55, 55, 55, 55, 55, 55, 61, 61, 61, 61, 61, 61, 61, 61, 2, 2, + 2, 2, 2, 2, 2, 61, 61, 2, 2, 2, 2, 2, 2, 2, 0, 0, + 0, 0, 0, 0, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, 13, 13, + 13, 13, 13, 13, 2, 2, 0, 0, 0, 0, 0, 13, 0, 13, 0, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 1, 1, 1, 1, 12, 12, 13, 13, + 13, 13, 0, 0, 0, 0, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 1, + 1, 0, 0, 15, 15, 15, 0, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 0, 0, 17, 17, 17, 2, 2, + 2, 2, 2, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 2, 2, 2, + 2, 2, 2, 2, 2, 0, 12, 12, 12, 12, 12, 12, 12, 0, 17, 17, + 17, 17, 17, 17, 17, 0, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 2, 2, 2, 39, 39, 39, 39, 39, 39, 39, 2, 86, 86, + 86, 86, 86, 86, 86, 86, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 2, 2, 2, 2, 79, 79, 79, 79, 79, 79, 79, 79, 0, 0, + 19, 19, 19, 19, 19, 19, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 2, 2, 19, 19, 2, 19, 2, 19, 19, 19, 2, 2, + 19, 19, 19, 19, 19, 19, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 2, 2, 2, 65, 65, 65, 65, 65, 65, 65, 65, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 2, 2, 2, 2, + 2, 2, 2, 2, 75, 75, 75, 75, 2, 2, 2, 2, 2, 2, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 0, 69, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 74, 12, 12, 12, 12, 12, 2, 2, 2, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 2, 0, 84, 84, + 2, 2, 2, 2, 84, 84, 33, 33, 33, 33, 33, 33, 33, 2, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 2, 68, 68, + 68, 68, 68, 68, 2, 2, 68, 68, 2, 2, 68, 68, 68, 68, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 2, 2, 2, 2, 2, 2, 2, + 2, 92, 92, 92, 92, 92, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 2, 2, 30, 30, 30, 30, 30, 30, 2, 19, 19, + 19, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 19, 19, 19, 19, + 0, 0, 2, 2, 2, 2, 87, 87, 87, 87, 87, 87, 2, 2, 87, 87, + 2, 2, 2, 2, 2, 2, 12, 12, 12, 12, 2, 2, 2, 2, 2, 2, + 2, 12, 12, 12, 12, 12, 13, 13, 2, 2, 2, 2, 2, 2, 19, 19, + 19, 19, 19, 19, 19, 2, 2, 2, 2, 4, 4, 4, 4, 4, 2, 2, + 2, 2, 2, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 2, 14, 14, + 14, 14, 14, 2, 14, 2, 14, 14, 2, 14, 14, 2, 14, 14, 3, 3, + 3, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 0, 0, 2, 2, 3, 3, 3, 3, 3, 3, 2, 2, + 2, 2, 2, 2, 2, 3, 1, 1, 1, 1, 1, 1, 6, 6, 0, 0, + 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, + 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 2, 2, 0, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 17, 17, + 17, 17, 17, 17, 0, 0, 2, 2, 12, 12, 12, 12, 12, 12, 2, 2, + 12, 12, 12, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 2, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 2, 49, 49, 49, 2, 49, 49, 2, 49, 49, 49, + 49, 49, 49, 49, 2, 2, 49, 49, 49, 2, 2, 2, 2, 2, 0, 0, + 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, + 0, 0, 0, 2, 2, 2, 9, 2, 2, 2, 2, 2, 2, 2, 0, 0, + 0, 0, 0, 1, 2, 2, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 2, 2, 2, 67, 67, 67, 67, 67, 67, 67, 67, 67, 2, + 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 2, 2, 2, 2, 2,118,118,118,118,118,118,118,118,118,118, + 118, 2, 2, 2, 2, 2, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 2, 53, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 2, 2, 2, 2, 59, 59, 59, 59, 59, 59, 2, 2, 40, 40, + 40, 40, 40, 40, 40, 40, 51, 51, 51, 51, 51, 51, 51, 51, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 2, 2, 50, 50, + 2, 2, 2, 2, 2, 2,135,135,135,135,135,135,135,135,135,135, + 135,135, 2, 2, 2, 2,106,106,106,106,106,106,106,106,104,104, + 104,104,104,104,104,104,104,104,104,104, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2,104,161,161,161,161,161,161,161,161,161,161, + 161, 2,161,161,161,161,161,161,161, 2,161,161, 2,161,161,161, + 2,161,161,161,161,161,161,161, 2,161,161, 2, 2, 2,170,170, + 170,170,170,170,170,170,170,170,170,170, 2, 2, 2, 2,110,110, + 110,110,110,110,110,110,110,110,110,110,110,110,110, 2,110,110, + 110,110,110,110, 2, 2, 19, 19, 19, 19, 19, 19, 2, 19, 19, 2, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 2, 2, 2, 2, 2, 47, 47, + 47, 47, 47, 47, 2, 2, 47, 2, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 2, 47, 47, 2, + 2, 2, 47, 2, 2, 47, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 2, 81,120,120,120,120,120,120,120,120,116,116, + 116,116,116,116,116,116,116,116,116,116,116,116,116, 2, 2, 2, + 2, 2, 2, 2, 2,116,128,128,128,128,128,128,128,128,128,128, + 128, 2,128,128, 2, 2, 2, 2, 2,128,128,128,128,128, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 2, 2, 2, 66, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 2, 2, 2, 2, 2, 72, 98, 98, + 98, 98, 98, 98, 98, 98, 97, 97, 97, 97, 97, 97, 97, 97, 2, 2, + 2, 2, 97, 97, 97, 97, 2, 2, 97, 97, 97, 97, 97, 97, 57, 57, + 57, 57, 2, 57, 57, 2, 2, 2, 2, 2, 57, 57, 57, 57, 57, 57, + 57, 57, 2, 57, 57, 57, 2, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 2, 2, 57, 57, + 57, 2, 2, 2, 2, 57, 57, 2, 2, 2, 2, 2, 2, 2, 88, 88, + 88, 88, 88, 88, 88, 88,117,117,117,117,117,117,117,117,112,112, + 112,112,112,112,112,112,112,112,112,112,112,112,112, 2, 2, 2, + 2,112,112,112,112,112, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, + 78, 78, 78, 78, 2, 2, 2, 78, 78, 78, 78, 78, 78, 78, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 2, 2, 82, 82, + 82, 82, 82, 82, 82, 82, 82, 82, 82, 2, 2, 2, 2, 2,122,122, + 122,122,122,122,122,122,122,122, 2, 2, 2, 2, 2, 2, 2,122, + 122,122,122, 2, 2, 2, 2,122,122,122,122,122,122,122, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 2, 2, 2, 2, 2, 2, 2,130,130, + 130,130,130,130,130,130,130,130,130, 2, 2, 2, 2, 2, 2, 2, + 130,130,130,130,130,130,144,144,144,144,144,144,144,144,144,144, + 2, 2, 2, 2, 2, 2,165,165,165,165,165,165,165,165,165,165, + 165,165,165,165, 2, 2, 2,165,165,165,165,165,165,165, 2, 2, + 2, 2, 2, 2,165,165,156,156,156,156,156,156,156,156,156,156, + 2,156,156,156, 2, 2,156,156, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3,147,147, + 147,147,147,147,147,147,148,148,148,148,148,148,148,148,148,148, + 2, 2, 2, 2, 2, 2,158,158,158,158,158,158,158,158,158,158, + 2, 2, 2, 2, 2, 2,153,153,153,153,153,153,153,153,153,153, + 153,153, 2, 2, 2, 2,149,149,149,149,149,149,149,149,149,149, + 149,149,149,149,149, 2, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 2, 2, 2, 2, 94, 94, 94, 94, 94, 94, 2, 2, + 2, 2, 2, 2, 2, 94, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 85, 2, 2,101,101, + 101,101,101,101,101,101,101, 2, 2, 2, 2, 2, 2, 2,101,101, + 2, 2, 2, 2, 2, 2, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 2, 96, 96,111,111,111,111,111,111,111,111,111,111, + 111,111,111,111,111, 2,100,100,100,100,100,100,100,100, 2, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2,108,108, + 108,108,108,108,108,108,108,108, 2,108,108,108,108,108,108,108, + 2, 2, 2, 2, 2, 2,129,129,129,129,129,129,129, 2,129, 2, + 129,129,129,129, 2,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129, 2,129,129,129, 2, 2, 2, 2, 2, 2,109,109, + 109,109,109,109,109,109,109,109,109, 2, 2, 2, 2, 2,109,109, + 2, 2, 2, 2, 2, 2,107,107,107,107, 2,107,107,107,107,107, + 107,107,107, 2, 2,107,107, 2, 2,107,107,107,107,107,107,107, + 107,107,107,107,107,107,107, 2,107,107,107,107,107,107,107, 2, + 107,107, 2,107,107,107,107,107, 2, 1,107,107,107,107,107, 2, + 2,107,107,107, 2, 2,107, 2, 2, 2, 2, 2, 2,107, 2, 2, + 2, 2, 2,107,107,107,107,107,107,107, 2, 2,107,107,107,107, + 107,107,107, 2, 2, 2,171,171,171,171,171,171,171,171,171,171, + 2,171, 2, 2,171, 2,171,171,171,171,171,171, 2,171,171, 2, + 171, 2, 2,171, 2,171,171,171,171, 2,171,171,171,171,171, 2, + 2, 2, 2, 2, 2, 2, 2,171,171, 2, 2, 2, 2, 2,137,137, + 137,137,137,137,137,137,137,137,137,137, 2,137,137,137,137,137, + 2, 2, 2, 2, 2, 2,124,124,124,124,124,124,124,124,124,124, + 2, 2, 2, 2, 2, 2,123,123,123,123,123,123,123,123,123,123, + 123,123,123,123, 2, 2,114,114,114,114,114,114,114,114,114,114, + 114,114,114, 2, 2, 2,114,114, 2, 2, 2, 2, 2, 2, 32, 32, + 32, 32, 32, 2, 2, 2,102,102,102,102,102,102,102,102,102,102, + 2, 2, 2, 2, 2, 2, 33, 33, 33, 33, 2, 2, 2, 2,126,126, + 126,126,126,126,126,126,126,126,126, 2, 2,126,126,126,126,126, + 126,126, 2, 2, 2, 2,126,126,126,126,126,126,126, 2,142,142, + 142,142,142,142,142,142,142,142,142,142, 2, 2, 2, 2,125,125, + 125,125,125,125,125,125,125,125,125, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2,125,154,154,154,154,154,154,154, 2, 2,154, + 2, 2,154,154,154,154,154,154,154,154, 2,154,154, 2,154,154, + 154,154,154,154,154,154,154,154,154,154,154,154, 2,154,154, 2, + 2,154,154,154,154,154,154,154, 2, 2, 2, 2, 2, 2,150,150, + 150,150,150,150,150,150, 2, 2,150,150,150,150,150,150,150,150, + 150,150,150, 2, 2, 2,141,141,141,141,141,141,141,141,140,140, + 140,140,140,140,140,140,140,140,140, 2, 2, 2, 2, 2,121,121, + 121,121,121,121,121,121,121, 2, 2, 2, 2, 2, 2, 2, 7, 7, + 2, 2, 2, 2, 2, 2,169,169,169,169,169,169,169,169,169,169, + 2, 2, 2, 2, 2, 2,133,133,133,133,133,133,133,133,133, 2, + 133,133,133,133,133,133,133,133,133,133,133,133,133, 2,133,133, + 133,133,133,133, 2, 2,133,133,133,133,133, 2, 2, 2,134,134, + 134,134,134,134,134,134, 2, 2,134,134,134,134,134,134, 2,134, + 134,134,134,134,134,134,134,134,134,134,134,134,134, 2,138,138, + 138,138,138,138,138, 2,138,138, 2,138,138,138,138,138,138,138, + 138,138,138,138,138,138, 2, 2,138, 2,138,138, 2,138,138,138, + 2, 2, 2, 2, 2, 2,143,143,143,143,143,143, 2,143,143, 2, + 143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143, + 143,143,143,143,143, 2,143,143, 2,143,143,143,143,143,143, 2, + 2, 2, 2, 2, 2, 2,143,143, 2, 2, 2, 2, 2, 2,145,145, + 145,145,145,145,145,145,145, 2, 2, 2, 2, 2, 2, 2,163,163, + 163,163,163,163,163,163,163, 2,163,163,163,163,163,163,163,163, + 163, 2, 2, 2,163,163,163,163,163, 2, 2, 2, 2, 2, 86, 2, + 2, 2, 2, 2, 2, 2, 22, 22, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 22, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 2, 2, 2, 2, 2, 2, 63, 63, 63, 63, 63, 63, 63, 2, 63, 63, + 63, 63, 63, 2, 2, 2, 63, 63, 63, 63, 2, 2, 2, 2,157,157, + 157,157,157,157,157,157,157,157,157, 2, 2, 2, 2, 2, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 2, 2, 80, 80, + 80, 2, 2, 2, 2, 2,127,127,127,127,127,127,127,127,127,127, + 127,127,127,127,127, 2,166,166,166,166,166,166,166,166,166,166, + 2, 2, 2, 2, 2, 2, 79, 2, 2, 2, 2, 2, 2, 2,115,115, + 115,115,115,115,115,115,115,115,115,115,115,115,115, 2,115,115, + 2, 2, 2, 2,115,115,159,159,159,159,159,159,159,159,159,159, + 159,159,159,159,159, 2,159,159, 2, 2, 2, 2, 2, 2,103,103, + 103,103,103,103,103,103,103,103,103,103,103,103, 2, 2,119,119, + 119,119,119,119,119,119,119,119,119,119,119,119, 2, 2,119,119, + 2,119,119,119,119,119, 2, 2, 2, 2, 2,119,119,119,167,167, + 167,167,167,167,167,167,167,167, 2, 2, 2, 2, 2, 2,146,146, + 146,146,146,146,146,146,146,146,146, 2, 2, 2, 2, 2, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 2, 2, 2, 2, 99, 2, 2, + 2, 2, 2, 2, 2, 99,136,139, 13, 13,155, 2, 2, 2,136,136, + 136,136,136,136,136,136,155,155,155,155,155,155,155,155,155,155, + 155,155,155,155, 2, 2, 2, 2, 2, 2, 2, 2, 2,155,136, 2, + 2, 2, 2, 2, 2, 2, 17, 17, 17, 17, 2, 17, 17, 17, 17, 17, + 17, 17, 2, 17, 17, 2, 17, 15, 15, 15, 15, 15, 15, 15, 17, 17, + 17, 2, 2, 2, 2, 2, 2, 2, 15, 2, 2, 2, 2, 2, 15, 15, + 15, 2, 2, 17, 2, 2, 2, 2, 2, 2, 17, 17, 17, 17,139,139, + 139,139,139,139,139,139,139,139,139,139, 2, 2, 2, 2,105,105, + 105,105,105,105,105,105,105,105,105, 2, 2, 2, 2, 2,105,105, + 105,105,105, 2, 2, 2,105, 2, 2, 2, 2, 2, 2, 2,105,105, + 2, 2,105,105,105,105, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 2, 2, + 0, 2, 2, 0, 0, 2, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, + 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, + 0, 2, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, + 0, 0, 0, 2, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, + 0, 0, 0, 0, 0, 0,131,131,131,131,131,131,131,131,131,131, + 131,131, 2, 2, 2, 2, 2, 2, 2,131,131,131,131,131, 2,131, + 131,131,131,131,131,131, 2, 2, 2, 2, 2, 19, 19, 19, 56, 56, + 56, 56, 56, 56, 56, 2, 56, 2, 2, 56, 56, 56, 56, 56, 56, 56, + 2, 56, 56, 2, 56, 56, 56, 56, 56, 2, 2, 2, 2, 2, 6, 6, + 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6,151,151, + 151,151,151,151,151,151,151,151,151,151,151, 2, 2, 2,151,151, + 151,151,151,151, 2, 2,151,151, 2, 2, 2, 2,151,151,160,160, + 160,160,160,160,160,160,160,160,160,160,160,160,160, 2,152,152, + 152,152,152,152,152,152,152,152, 2, 2, 2, 2, 2,152,164,164, + 164,164,164,164,164,164,164,164, 2, 2, 2, 2, 2, 2,168,168, + 168,168,168,168,168,168,168,168,168, 2, 2, 2, 2,168, 30, 30, + 30, 30, 2, 30, 30, 2,113,113,113,113,113,113,113,113,113,113, + 113,113,113, 2, 2,113,113,113,113,113,113,113,113, 2,132,132, + 132,132,132,132,132,132,132,132,132,132, 2, 2, 2, 2,132,132, + 2, 2, 2, 2,132,132, 3, 3, 3, 3, 2, 3, 3, 3, 2, 3, + 3, 2, 3, 2, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 2, 3, 3, 3, 3, 2, 3, 2, 3, 2, 2, 2, 2, 2, 2, + 3, 2, 2, 2, 2, 3, 2, 3, 2, 3, 2, 3, 3, 3, 2, 3, + 2, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 2, 3, 2, 3, 3, + 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 3, + 3, 3, 2, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 0, 0, 15, 0, 0, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 0, 13, 2, + 2, 2, 2, 2, 2, 2, 13, 13, 13, 2, 2, 2, 2, 2, 2, 0, + 2, 2, 2, 2, 2, 2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 9, 9, 9, 10, 9, 11, 12, 13, 9, 9, 9, 14, 9, 9, 15, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 16, 17, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 18, 19, 20, 9, 21, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 16, 17, 9, 9, 9, 9, 18, 9, 9, 9, 9, 9, 19, 20, + 21, 9, 22, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 23, 9, 9, 9, 9, 9, 24, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 25, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 22, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -3704,60 +3934,66 @@ _hb_ucd_u8[14752] = 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 23, 24, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, - 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 13, 14, 15, 16, 17, 18, - 19, 20, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 23, 0, 0, 24, 25, 26, 27, 28, 29, 30, 0, 0, - 31, 32, 0, 33, 0, 34, 0, 35, 0, 0, 0, 0, 36, 37, 38, 39, + 9, 9, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 24, 25, 26, 27, 28, + 29, 30, 0, 0, 31, 32, 0, 33, 0, 34, 0, 35, 0, 0, 0, 0, + 36, 37, 38, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 41, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 42, 0, 0, + 0, 0, 0, 0, 0, 0, 43, 44, 0, 45, 0, 0, 0, 0, 0, 0, + 46, 47, 0, 0, 0, 0, 0, 48, 0, 49, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, + 53, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, + 55, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, + 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 58, 59, 60, 61, 62, 63, 64, 65, + 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 43, 44, 0, 45, 0, 0, 0, 0, 0, 0, 46, 47, 0, 0, - 0, 0, 0, 48, 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 53, 0, 0, 0, - 0, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, - 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 58, 59, 60, 61, 62, 63, 64, 65, 0, 0, 0, 0, - 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 67, 68, 0, 69, 70, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, + 99,100,101,102,103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,104, 0, 0, 0, 0, 0, 0,105,106, 0, + 107, 0, 0, 0,108, 0,109, 0,110, 0,111,112,113, 0,114, 0, + 0, 0,115, 0, 0, 0,116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,118,119,120,121, 0,122,123,124, + 125,126, 0,127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,128,129,130,131,132,133,134,135,136,137,138,139, + 140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155, + 156,157, 0, 0, 0,158,159,160,161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 67, 68, 0, 69, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, - 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102, - 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0,104, 0, 0, 0, 0, 0, 0,105,106, 0,107, 0, 0, 0, - 108, 0,109, 0,110, 0,111,112,113, 0,114, 0, 0, 0,115, 0, - 0, 0,116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0,118,119,120,121, 0,122,123,124,125,126, 0,127, + 162, 0,163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,164,165, 0, + 0, 0, 0, 0, 0, 0,166, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,167, 0, 0, 0,168,169, 0, 0, + 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,171, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, - 144,145,146,147,148,149,150,151,152,153,154,155,156,157, 0, 0, - 0,158,159,160,161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0,162,163, 0, 0, 0, 0, 0, - 0, 0,164, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0,165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,166, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,167, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,174, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0,168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,169,170, 0, 0, 0, 0,171,172, 0, 0, 0, - 173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188, - 189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204, - 205,206, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, + 0, 0, 0, 0, 0, 0, 0, 0, 0,176,177, 0, 0, 0, 0,178, + 179, 0, 0, 0,180,181,182,183,184,185,186,187,188,189,190,191, + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, + 208,209,210,211,212,213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 3, 4, }; static const uint16_t -_hb_ucd_u16[10060] = +_hb_ucd_u16[9668] = { 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 10, 11, 12, 13, 13, 13, 14, 15, 13, 13, 16, 17, 18, 19, 20, 21, 22, 13, 23, @@ -3776,9 +4012,10 @@ _hb_ucd_u16[10060] = 136, 48, 48, 137, 138, 139, 140, 140, 141, 48, 142, 143, 144, 145, 140, 140, 146, 147, 148, 149, 150, 48, 151, 152, 153, 154, 32, 155, 156, 157, 140, 140, 48, 48, 158, 159, 160, 161, 162, 163, 164, 165, 9, 9, 166, 11, 11, 167, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 168, 169, 48, 48, - 168, 48, 48, 170, 171, 172, 48, 48, 48, 171, 48, 48, 48, 173, 174, 175, - 48, 176, 9, 9, 9, 9, 9, 177, 178, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 168, 169, 48, 48, 168, 48, 48, 170, 171, 172, 48, 48, + 48, 171, 48, 48, 48, 173, 174, 175, 48, 176, 9, 9, 9, 9, 9, 177, + 178, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 179, 48, 180, 181, 48, 48, 48, 48, 182, 183, 48, 184, 48, 185, 48, 186, 187, 188, 48, 48, 48, 189, 190, 191, 192, 193, 194, 192, 48, 48, 195, 48, 48, 196, 197, 48, 198, 48, 48, 48, 48, 199, @@ -3791,28 +4028,34 @@ _hb_ucd_u16[10060] = 241, 242, 241, 241, 242, 243, 241, 244, 245, 245, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 272, 273, 274, 275, 209, 276, 277, 209, 278, - 279, 279, 279, 279, 279, 279, 279, 279, 280, 209, 281, 209, 209, 209, 209, 282, - 209, 283, 279, 284, 209, 285, 286, 209, 209, 209, 287, 140, 288, 140, 271, 271, - 271, 289, 209, 209, 209, 209, 290, 271, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 291, 292, 209, 209, 293, 209, 209, 209, 209, 209, 209, 294, 209, - 209, 209, 209, 209, 209, 209, 295, 296, 271, 297, 209, 209, 298, 279, 299, 279, - 300, 301, 279, 279, 279, 302, 279, 303, 209, 209, 209, 279, 304, 209, 209, 305, - 209, 306, 209, 209, 209, 209, 209, 209, 9, 9, 9, 11, 11, 11, 307, 308, - 13, 13, 13, 13, 13, 13, 309, 310, 11, 11, 311, 48, 48, 48, 312, 313, - 48, 314, 315, 315, 315, 315, 32, 32, 316, 317, 318, 319, 320, 321, 140, 140, - 209, 322, 209, 209, 209, 209, 209, 323, 209, 209, 209, 209, 209, 324, 140, 209, - 325, 326, 327, 328, 136, 48, 48, 48, 48, 329, 178, 48, 48, 48, 48, 330, - 331, 48, 48, 136, 48, 48, 48, 48, 200, 332, 48, 48, 209, 209, 333, 48, - 209, 334, 335, 209, 336, 337, 209, 209, 335, 209, 209, 337, 209, 209, 209, 209, - 48, 48, 48, 48, 209, 209, 209, 209, 48, 338, 48, 48, 48, 48, 48, 48, - 151, 209, 209, 209, 287, 48, 48, 229, 339, 48, 340, 140, 13, 13, 341, 342, - 13, 343, 48, 48, 48, 48, 344, 345, 31, 346, 347, 348, 13, 13, 13, 349, - 350, 351, 352, 353, 354, 355, 140, 356, 357, 48, 358, 359, 48, 48, 48, 360, - 361, 48, 48, 362, 363, 192, 32, 364, 64, 48, 365, 48, 366, 367, 48, 151, - 76, 48, 48, 368, 369, 370, 371, 372, 48, 48, 373, 374, 375, 376, 48, 377, - 48, 48, 48, 378, 379, 380, 381, 382, 383, 384, 315, 11, 11, 385, 386, 11, - 11, 11, 11, 11, 48, 48, 387, 192, 48, 48, 388, 48, 389, 48, 48, 206, - 390, 390, 390, 390, 390, 390, 390, 390, 391, 391, 391, 391, 391, 391, 391, 391, + 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, + 280, 209, 281, 209, 209, 209, 209, 282, 209, 283, 279, 284, 209, 285, 286, 209, + 209, 209, 176, 140, 287, 140, 271, 271, 271, 288, 209, 209, 209, 209, 289, 271, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 290, 291, 209, 209, 292, + 209, 209, 209, 209, 209, 209, 293, 209, 209, 209, 209, 209, 209, 209, 209, 209, + 209, 209, 209, 209, 209, 209, 294, 295, 271, 296, 209, 209, 297, 279, 298, 279, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, + 279, 279, 279, 279, 279, 279, 279, 279, 299, 300, 279, 279, 279, 301, 279, 302, + 209, 209, 209, 279, 303, 209, 209, 304, 209, 305, 209, 209, 209, 209, 209, 209, + 9, 9, 9, 11, 11, 11, 306, 307, 13, 13, 13, 13, 13, 13, 308, 309, + 11, 11, 310, 48, 48, 48, 311, 312, 48, 313, 314, 314, 314, 314, 32, 32, + 315, 316, 317, 318, 319, 320, 140, 140, 209, 321, 209, 209, 209, 209, 209, 322, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 323, 140, 209, + 324, 325, 326, 327, 136, 48, 48, 48, 48, 328, 178, 48, 48, 48, 48, 329, + 330, 48, 48, 136, 48, 48, 48, 48, 200, 331, 48, 48, 209, 209, 332, 48, + 209, 333, 334, 209, 335, 336, 209, 209, 334, 209, 209, 336, 209, 209, 209, 209, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 209, 209, 209, 209, + 48, 337, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 151, 209, 209, 209, 338, 48, 48, 229, + 339, 48, 340, 140, 13, 13, 341, 342, 13, 343, 48, 48, 48, 48, 344, 345, + 31, 346, 347, 348, 13, 13, 13, 349, 350, 351, 352, 353, 354, 355, 140, 356, + 357, 48, 358, 359, 48, 48, 48, 360, 361, 48, 48, 362, 363, 192, 32, 364, + 64, 48, 365, 48, 366, 367, 48, 151, 76, 48, 48, 368, 369, 370, 371, 372, + 48, 48, 373, 374, 375, 376, 48, 377, 48, 48, 48, 378, 379, 380, 381, 382, + 383, 384, 314, 11, 11, 385, 386, 11, 11, 11, 11, 11, 48, 48, 387, 192, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 388, 48, 389, 48, 48, 206, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 48, 48, 48, 48, 48, 48, 204, 48, 48, 48, 48, 48, 48, 207, 140, 140, 392, 393, 394, 395, 396, 48, 48, 48, 48, 48, 48, 397, 398, 399, 48, 48, 48, 48, 48, 400, 209, 48, 48, 48, 48, 401, 48, 48, 402, 140, 140, 403, @@ -3823,571 +4066,540 @@ _hb_ucd_u16[10060] = 140, 140, 140, 140, 140, 140, 140, 140, 48, 151, 48, 48, 48, 100, 429, 430, 48, 48, 431, 48, 432, 48, 48, 433, 48, 434, 48, 48, 435, 436, 140, 140, 9, 9, 437, 11, 11, 48, 48, 48, 48, 204, 192, 9, 9, 438, 11, 439, - 48, 48, 440, 48, 48, 48, 441, 442, 442, 443, 444, 445, 140, 140, 140, 140, - 48, 48, 48, 314, 48, 199, 440, 140, 446, 27, 27, 447, 140, 140, 140, 140, + 48, 48, 440, 48, 48, 48, 441, 442, 442, 443, 444, 445, 48, 48, 48, 388, + 48, 48, 48, 313, 48, 199, 440, 140, 446, 27, 27, 447, 140, 140, 140, 140, 448, 48, 48, 449, 48, 450, 48, 451, 48, 200, 452, 140, 140, 140, 48, 453, 48, 454, 48, 455, 140, 140, 140, 140, 48, 48, 48, 456, 271, 457, 271, 271, 458, 459, 48, 460, 461, 462, 48, 463, 48, 464, 140, 140, 465, 48, 466, 467, 48, 48, 48, 468, 48, 469, 48, 470, 48, 471, 472, 140, 140, 140, 140, 140, 48, 48, 48, 48, 196, 140, 140, 140, 9, 9, 9, 473, 11, 11, 11, 474, - 48, 48, 475, 192, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 271, 476, - 48, 48, 477, 478, 140, 140, 140, 479, 48, 464, 480, 48, 62, 481, 140, 48, - 482, 140, 140, 48, 483, 140, 48, 314, 484, 48, 48, 485, 486, 457, 487, 488, - 222, 48, 48, 489, 490, 48, 196, 192, 491, 48, 492, 493, 494, 48, 48, 495, - 222, 48, 48, 496, 497, 498, 499, 500, 48, 97, 501, 502, 503, 140, 140, 140, - 504, 505, 506, 48, 48, 507, 508, 192, 509, 83, 84, 510, 511, 512, 513, 514, - 48, 48, 48, 515, 516, 517, 478, 140, 48, 48, 48, 518, 519, 192, 140, 140, - 48, 48, 520, 521, 522, 523, 140, 140, 48, 48, 48, 524, 525, 192, 526, 140, - 48, 48, 527, 528, 192, 140, 140, 140, 48, 173, 529, 530, 314, 140, 140, 140, - 48, 48, 501, 531, 140, 140, 140, 140, 140, 140, 9, 9, 11, 11, 148, 532, - 533, 534, 48, 535, 536, 192, 140, 140, 140, 140, 537, 48, 48, 538, 539, 140, - 540, 48, 48, 541, 542, 543, 48, 48, 544, 545, 546, 48, 48, 48, 48, 196, - 547, 140, 140, 140, 140, 140, 140, 140, 84, 48, 520, 548, 549, 148, 175, 550, - 48, 551, 552, 553, 140, 140, 140, 140, 554, 48, 48, 555, 556, 192, 557, 48, - 558, 559, 192, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 560, - 561, 115, 48, 562, 563, 192, 140, 140, 140, 140, 140, 100, 271, 564, 565, 566, - 48, 207, 140, 140, 140, 140, 140, 140, 272, 272, 272, 272, 272, 272, 567, 568, - 48, 48, 48, 48, 388, 140, 140, 140, 140, 48, 48, 48, 48, 48, 48, 569, - 48, 48, 48, 570, 571, 572, 140, 140, 48, 48, 48, 48, 314, 140, 140, 140, - 48, 48, 48, 196, 48, 200, 370, 48, 48, 48, 48, 200, 192, 48, 204, 573, - 48, 48, 48, 574, 575, 576, 577, 578, 48, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 9, 9, 11, 11, 271, 579, 140, 140, 140, 140, 140, 140, - 48, 48, 48, 48, 580, 581, 582, 582, 583, 584, 140, 140, 140, 140, 585, 586, - 48, 48, 48, 48, 48, 48, 48, 440, 48, 48, 48, 48, 48, 199, 140, 140, - 196, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 587, - 48, 48, 588, 589, 140, 590, 591, 48, 48, 48, 48, 48, 48, 48, 48, 206, - 48, 48, 48, 48, 48, 48, 71, 151, 196, 592, 593, 140, 140, 140, 140, 140, - 32, 32, 594, 32, 595, 209, 209, 209, 209, 209, 209, 209, 323, 140, 140, 140, - 209, 209, 209, 209, 209, 209, 209, 324, 209, 209, 596, 209, 209, 209, 597, 598, - 599, 209, 600, 209, 209, 209, 288, 140, 209, 209, 209, 209, 601, 140, 140, 140, - 140, 140, 140, 140, 271, 602, 271, 602, 209, 209, 209, 209, 209, 287, 271, 461, - 9, 603, 11, 604, 605, 606, 241, 9, 607, 608, 609, 610, 611, 9, 603, 11, - 612, 613, 11, 614, 615, 616, 617, 9, 618, 11, 9, 603, 11, 604, 605, 11, - 241, 9, 607, 617, 9, 618, 11, 9, 603, 11, 619, 9, 620, 621, 622, 623, - 11, 624, 9, 625, 626, 627, 628, 11, 629, 9, 630, 11, 631, 632, 632, 632, - 32, 32, 32, 633, 32, 32, 634, 635, 636, 637, 45, 140, 140, 140, 140, 140, - 638, 639, 640, 140, 140, 140, 140, 140, 641, 642, 643, 27, 27, 27, 644, 140, - 645, 140, 140, 140, 140, 140, 140, 140, 48, 48, 151, 646, 647, 140, 140, 140, - 140, 48, 648, 140, 48, 48, 649, 650, 140, 140, 140, 140, 140, 48, 651, 192, - 140, 140, 140, 140, 140, 140, 652, 200, 48, 48, 48, 48, 653, 595, 140, 140, - 9, 9, 607, 11, 654, 370, 140, 140, 140, 140, 140, 140, 140, 140, 140, 499, - 271, 271, 655, 656, 140, 140, 140, 140, 499, 271, 657, 658, 140, 140, 140, 140, - 659, 48, 660, 661, 662, 663, 664, 665, 666, 206, 667, 206, 140, 140, 140, 668, - 209, 209, 669, 209, 209, 209, 209, 209, 209, 323, 334, 670, 670, 670, 209, 324, - 671, 209, 209, 209, 209, 209, 209, 209, 209, 209, 672, 140, 140, 140, 673, 209, - 674, 209, 209, 669, 675, 676, 324, 140, 209, 209, 209, 209, 209, 209, 209, 677, - 209, 209, 209, 209, 209, 678, 426, 426, 209, 209, 209, 209, 209, 209, 209, 679, - 209, 209, 209, 209, 209, 176, 669, 427, 669, 209, 209, 209, 680, 176, 209, 209, - 680, 209, 672, 676, 140, 140, 140, 140, 209, 209, 209, 209, 209, 323, 672, 426, - 675, 209, 209, 681, 682, 669, 675, 675, 209, 683, 209, 209, 288, 140, 140, 192, - 48, 48, 48, 48, 48, 48, 140, 140, 48, 48, 48, 207, 48, 48, 48, 48, - 48, 204, 48, 48, 48, 48, 48, 48, 48, 48, 478, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 100, 48, 48, 48, 48, 48, 48, 204, 140, 140, - 48, 204, 140, 140, 140, 140, 140, 140, 48, 48, 48, 48, 71, 48, 48, 48, - 48, 48, 48, 140, 140, 140, 140, 140, 684, 140, 570, 570, 570, 570, 570, 570, + 48, 48, 475, 192, 476, 9, 477, 11, 478, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 271, 479, 48, 48, 480, 481, 482, 140, 140, 483, + 48, 464, 484, 48, 62, 485, 140, 48, 486, 140, 140, 48, 487, 140, 48, 313, + 488, 48, 48, 489, 490, 457, 491, 492, 222, 48, 48, 493, 494, 48, 196, 192, + 495, 48, 496, 497, 498, 48, 48, 499, 222, 48, 48, 500, 501, 502, 503, 504, + 48, 97, 505, 506, 507, 140, 140, 140, 508, 509, 510, 48, 48, 511, 512, 192, + 513, 83, 84, 514, 515, 516, 517, 518, 519, 48, 48, 520, 521, 522, 523, 140, + 48, 48, 48, 524, 525, 526, 481, 140, 48, 48, 48, 527, 528, 192, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 529, 530, 531, 532, 140, 140, + 48, 48, 48, 533, 534, 192, 535, 140, 48, 48, 536, 537, 192, 538, 539, 140, + 48, 540, 541, 542, 313, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 48, 48, 505, 543, 140, 140, 140, 140, 140, 140, 9, 9, 11, 11, 148, 544, + 545, 546, 48, 547, 548, 192, 140, 140, 140, 140, 549, 48, 48, 550, 551, 140, + 552, 48, 48, 553, 554, 555, 48, 48, 556, 557, 558, 48, 48, 48, 48, 196, + 559, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 560, 192, + 84, 48, 529, 561, 562, 148, 175, 563, 48, 564, 565, 566, 140, 140, 140, 140, + 567, 48, 48, 568, 569, 192, 570, 48, 571, 572, 192, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 573, + 574, 115, 48, 575, 576, 577, 140, 140, 140, 140, 140, 100, 271, 578, 579, 580, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 207, 140, 140, 140, 140, 140, 140, + 272, 272, 272, 272, 272, 272, 581, 582, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 388, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 48, 48, 48, 48, 583, + 48, 48, 48, 584, 585, 586, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 71, + 48, 48, 48, 48, 313, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 48, 587, 588, 192, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 48, 48, 48, 196, 48, 200, 370, 48, 48, 48, 48, 200, 192, 48, 204, 589, + 48, 48, 48, 590, 591, 592, 593, 594, 48, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 595, 48, 596, 192, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 9, 9, 11, 11, 271, 597, 140, 140, 140, 140, 140, 140, + 48, 48, 48, 48, 598, 599, 600, 600, 601, 602, 140, 140, 140, 140, 603, 604, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 440, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 199, 140, 605, + 196, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 606, + 48, 48, 607, 608, 140, 609, 610, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 206, + 48, 48, 48, 48, 48, 48, 71, 151, 196, 611, 612, 140, 140, 140, 140, 140, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 192, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 322, 140, 140, 140, 140, + 32, 32, 613, 32, 614, 209, 209, 209, 209, 209, 209, 209, 322, 140, 140, 140, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 323, + 209, 209, 615, 209, 209, 209, 616, 617, 618, 209, 619, 209, 209, 209, 287, 140, + 209, 209, 209, 209, 620, 140, 140, 140, 140, 140, 140, 140, 271, 621, 271, 621, + 209, 209, 209, 209, 209, 338, 271, 461, 140, 140, 140, 140, 140, 140, 140, 140, + 9, 622, 11, 623, 624, 625, 241, 9, 626, 627, 628, 629, 630, 9, 622, 11, + 631, 632, 11, 633, 634, 635, 636, 9, 637, 11, 9, 622, 11, 623, 624, 11, + 241, 9, 626, 636, 9, 637, 11, 9, 622, 11, 638, 9, 639, 640, 641, 642, + 11, 643, 9, 644, 645, 646, 647, 11, 648, 9, 649, 11, 650, 538, 538, 538, + 32, 32, 32, 651, 32, 32, 652, 653, 654, 655, 45, 140, 140, 140, 140, 140, + 656, 657, 658, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 659, 660, 661, 27, 27, 27, 662, 140, 663, 140, 140, 140, 140, 140, 140, 140, + 48, 48, 151, 664, 665, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 666, 140, 48, 48, 667, 668, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 669, 192, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 587, 670, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 671, 200, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 672, 614, 140, 140, + 9, 9, 626, 11, 673, 370, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 503, 271, 271, 674, 675, 140, 140, 140, 140, + 503, 271, 676, 677, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 678, 48, 679, 680, 681, 682, 683, 684, 685, 206, 686, 206, 140, 140, 140, 687, + 209, 209, 688, 209, 209, 209, 209, 209, 209, 322, 333, 689, 689, 689, 209, 323, + 690, 209, 209, 209, 209, 209, 209, 209, 209, 209, 691, 140, 140, 140, 692, 209, + 693, 209, 209, 688, 694, 695, 323, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 696, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 697, 426, 426, + 209, 209, 209, 209, 209, 209, 209, 698, 209, 209, 209, 209, 209, 176, 688, 427, + 688, 209, 209, 209, 699, 176, 209, 209, 699, 209, 691, 688, 695, 140, 140, 140, + 209, 209, 209, 209, 209, 322, 691, 426, 700, 209, 209, 209, 701, 702, 176, 694, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 703, 209, 209, 209, 209, 209, 192, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 140, 140, + 48, 48, 48, 207, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 204, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 481, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 100, 48, + 48, 48, 48, 48, 48, 204, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 48, 204, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 48, 48, 48, 48, 71, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 140, 140, 140, 140, 140, + 704, 140, 584, 584, 584, 584, 584, 584, 140, 140, 140, 140, 140, 140, 140, 140, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 140, - 391, 391, 391, 391, 391, 391, 391, 685, 391, 391, 391, 391, 391, 391, 391, 686, - 0, 0, 0, 0, 1, 2, 1, 2, 0, 0, 3, 3, 4, 5, 4, 5, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 0, 0, 7, 0, - 8, 8, 8, 8, 8, 8, 8, 9, 10, 11, 12, 11, 11, 11, 13, 11, - 14, 14, 14, 14, 14, 14, 14, 14, 15, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 14, 16, 17, 18, 17, 17, 19, 20, 21, 21, 22, 21, 23, 24, - 25, 26, 27, 27, 28, 29, 27, 30, 27, 27, 27, 27, 27, 31, 27, 27, - 32, 33, 33, 33, 34, 27, 27, 27, 35, 35, 35, 36, 37, 37, 37, 38, - 39, 39, 40, 41, 42, 43, 44, 27, 45, 46, 27, 27, 27, 27, 47, 27, - 48, 48, 48, 48, 48, 49, 50, 48, 51, 52, 53, 54, 55, 56, 57, 58, - 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, - 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, - 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, - 107, 108, 109, 109, 110, 111, 112, 109, 113, 114, 115, 116, 117, 118, 119, 120, - 121, 122, 122, 123, 122, 124, 125, 125, 126, 127, 128, 129, 130, 131, 125, 125, - 132, 132, 132, 132, 133, 132, 134, 135, 132, 133, 132, 136, 136, 137, 125, 125, - 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 139, 139, 140, 139, 139, 141, - 142, 142, 142, 142, 142, 142, 142, 142, 143, 143, 143, 143, 144, 145, 143, 143, - 144, 143, 143, 146, 147, 148, 143, 143, 143, 147, 143, 143, 143, 149, 143, 150, - 143, 151, 152, 152, 152, 152, 152, 153, 154, 154, 154, 154, 154, 154, 154, 154, - 155, 156, 157, 157, 157, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, - 168, 168, 168, 168, 168, 169, 170, 170, 171, 172, 173, 173, 173, 173, 173, 174, - 173, 173, 175, 154, 154, 154, 154, 176, 177, 178, 179, 179, 180, 181, 182, 183, - 184, 184, 185, 184, 186, 187, 168, 168, 188, 189, 190, 190, 190, 191, 190, 192, - 193, 193, 194, 8, 195, 125, 125, 125, 196, 196, 196, 196, 197, 196, 196, 198, - 199, 199, 199, 199, 200, 200, 200, 201, 202, 202, 202, 203, 204, 205, 205, 205, - 206, 139, 139, 207, 208, 209, 210, 211, 4, 4, 212, 4, 4, 213, 214, 215, - 4, 4, 4, 216, 8, 8, 8, 8, 11, 217, 11, 11, 217, 218, 11, 219, - 11, 11, 11, 220, 220, 221, 11, 222, 223, 0, 0, 0, 0, 0, 224, 225, - 226, 227, 0, 0, 228, 8, 8, 229, 0, 0, 230, 231, 232, 0, 4, 4, - 233, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 234, 125, 235, 125, 0, 0, 236, 236, 236, 236, 236, 236, 236, 236, - 0, 0, 0, 0, 0, 0, 0, 237, 0, 238, 0, 0, 0, 0, 0, 0, - 239, 239, 239, 239, 239, 239, 4, 4, 240, 240, 240, 240, 240, 240, 240, 241, - 139, 139, 140, 242, 242, 242, 243, 244, 143, 245, 246, 246, 246, 246, 14, 14, - 0, 0, 0, 0, 0, 247, 125, 125, 248, 249, 248, 248, 248, 248, 248, 250, - 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 251, 125, 0, - 252, 0, 253, 254, 255, 256, 256, 256, 256, 257, 258, 259, 259, 259, 259, 260, - 261, 262, 262, 263, 142, 142, 142, 142, 264, 0, 262, 262, 0, 0, 265, 259, - 142, 264, 0, 0, 0, 0, 142, 266, 0, 0, 0, 0, 0, 259, 259, 267, - 259, 259, 259, 259, 259, 268, 0, 0, 248, 248, 248, 248, 0, 0, 0, 0, - 269, 269, 269, 269, 269, 269, 269, 269, 270, 269, 269, 269, 271, 272, 272, 272, - 273, 273, 273, 273, 273, 273, 273, 273, 273, 273, 274, 125, 14, 14, 14, 14, - 14, 14, 275, 275, 275, 275, 275, 276, 0, 0, 277, 4, 4, 4, 4, 4, - 278, 4, 4, 4, 279, 280, 125, 281, 282, 282, 283, 284, 285, 285, 285, 286, - 287, 287, 287, 287, 288, 289, 48, 48, 290, 290, 291, 292, 292, 293, 142, 294, - 295, 295, 295, 295, 296, 297, 138, 298, 299, 299, 299, 300, 301, 302, 138, 138, - 303, 303, 303, 303, 304, 305, 306, 307, 308, 309, 246, 4, 4, 310, 311, 152, - 152, 152, 152, 152, 306, 306, 312, 313, 142, 142, 314, 142, 315, 142, 142, 316, - 125, 125, 125, 125, 125, 125, 125, 125, 248, 248, 248, 248, 248, 248, 317, 248, - 248, 248, 248, 248, 248, 318, 125, 125, 319, 320, 21, 321, 322, 27, 27, 27, - 27, 27, 27, 27, 323, 324, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 325, 27, 27, 27, 27, 27, 326, 27, 27, 327, 125, 125, 27, - 8, 284, 328, 0, 0, 329, 330, 331, 27, 27, 27, 27, 27, 27, 27, 332, - 333, 0, 1, 2, 1, 2, 334, 258, 259, 335, 142, 264, 336, 337, 338, 339, - 340, 341, 342, 343, 344, 344, 125, 125, 341, 341, 341, 341, 341, 341, 341, 345, - 346, 0, 0, 347, 11, 11, 11, 11, 348, 349, 350, 125, 125, 0, 0, 351, - 352, 353, 354, 354, 354, 355, 356, 357, 358, 358, 359, 360, 361, 362, 362, 363, - 364, 365, 366, 366, 367, 368, 125, 125, 369, 369, 369, 369, 369, 370, 370, 370, - 371, 372, 373, 374, 374, 375, 374, 376, 377, 377, 378, 379, 379, 379, 380, 381, - 381, 382, 383, 384, 125, 125, 125, 125, 385, 385, 385, 385, 385, 385, 385, 385, - 385, 385, 385, 386, 385, 387, 388, 125, 389, 4, 4, 390, 125, 125, 125, 125, - 391, 392, 392, 393, 394, 395, 396, 396, 397, 398, 399, 125, 125, 125, 400, 401, - 402, 403, 404, 405, 125, 125, 125, 125, 406, 406, 407, 408, 407, 409, 407, 407, - 410, 411, 412, 413, 414, 414, 415, 415, 416, 416, 125, 125, 417, 417, 418, 419, - 420, 420, 420, 421, 422, 423, 424, 425, 426, 427, 428, 125, 125, 125, 125, 125, - 429, 429, 429, 429, 430, 125, 125, 125, 431, 431, 431, 432, 431, 431, 431, 433, - 434, 434, 435, 436, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 27, 45, - 437, 437, 438, 439, 125, 125, 125, 440, 441, 441, 442, 443, 443, 444, 125, 445, - 446, 125, 125, 447, 448, 125, 449, 450, 451, 451, 451, 451, 452, 453, 451, 454, - 455, 455, 455, 455, 456, 457, 458, 459, 460, 460, 460, 461, 462, 463, 463, 464, - 465, 465, 465, 465, 465, 465, 466, 467, 468, 469, 468, 468, 470, 125, 125, 125, - 471, 472, 473, 474, 474, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, - 485, 485, 485, 485, 485, 486, 487, 125, 488, 488, 488, 488, 489, 490, 125, 125, - 491, 491, 491, 492, 491, 493, 125, 125, 494, 494, 494, 494, 495, 496, 497, 125, - 498, 498, 498, 499, 499, 125, 125, 125, 500, 501, 502, 500, 503, 125, 125, 125, - 504, 504, 504, 505, 125, 125, 125, 125, 125, 125, 506, 506, 506, 506, 506, 507, - 508, 509, 510, 511, 512, 513, 125, 125, 125, 125, 514, 515, 515, 514, 516, 125, - 517, 517, 517, 517, 518, 519, 519, 519, 519, 519, 520, 154, 521, 521, 521, 522, - 523, 125, 125, 125, 125, 125, 125, 125, 524, 525, 525, 526, 527, 525, 528, 529, - 529, 530, 531, 532, 125, 125, 125, 125, 533, 534, 534, 535, 536, 537, 538, 539, - 540, 541, 542, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 543, 544, - 545, 546, 545, 547, 545, 548, 125, 125, 125, 125, 125, 549, 550, 550, 550, 551, - 552, 552, 552, 552, 552, 552, 552, 552, 552, 553, 125, 125, 125, 125, 125, 125, - 552, 552, 552, 552, 552, 552, 554, 555, 552, 552, 552, 552, 556, 125, 125, 125, - 125, 557, 557, 557, 557, 557, 557, 558, 559, 559, 559, 559, 559, 559, 559, 559, - 559, 559, 559, 559, 559, 560, 125, 125, 561, 561, 561, 561, 561, 561, 561, 561, - 561, 561, 561, 561, 562, 125, 125, 125, 275, 275, 275, 275, 275, 275, 275, 275, - 275, 275, 275, 563, 564, 565, 566, 567, 567, 567, 567, 568, 569, 570, 571, 572, - 573, 573, 573, 573, 574, 575, 576, 577, 573, 125, 125, 125, 125, 125, 125, 125, - 125, 125, 125, 125, 578, 578, 578, 578, 578, 579, 125, 125, 125, 125, 125, 125, - 580, 580, 580, 580, 581, 580, 580, 580, 582, 580, 125, 125, 125, 125, 583, 584, - 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 586, - 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 588, 125, 125, - 589, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 590, - 591, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, - 256, 256, 592, 593, 125, 594, 595, 596, 596, 596, 596, 596, 596, 596, 596, 596, - 596, 596, 596, 596, 596, 596, 596, 597, 598, 598, 598, 598, 598, 598, 599, 600, - 601, 602, 603, 125, 125, 125, 125, 125, 8, 8, 604, 8, 605, 0, 0, 0, - 0, 0, 0, 0, 603, 125, 125, 125, 0, 0, 0, 0, 0, 0, 0, 606, - 0, 0, 607, 0, 0, 0, 608, 609, 610, 0, 611, 0, 0, 0, 235, 125, - 11, 11, 11, 11, 612, 125, 125, 125, 125, 125, 125, 125, 0, 603, 0, 603, - 0, 0, 0, 0, 0, 234, 0, 613, 0, 0, 0, 0, 0, 224, 0, 0, - 0, 614, 615, 616, 617, 0, 0, 0, 618, 619, 0, 620, 621, 622, 0, 0, - 0, 0, 623, 0, 0, 0, 0, 0, 0, 0, 0, 0, 624, 0, 0, 0, - 625, 625, 625, 625, 625, 625, 625, 625, 626, 627, 628, 125, 125, 125, 125, 125, - 4, 629, 630, 125, 125, 125, 125, 125, 631, 632, 633, 14, 14, 14, 634, 125, - 635, 125, 125, 125, 125, 125, 125, 125, 636, 636, 637, 638, 639, 125, 125, 125, - 125, 640, 641, 125, 642, 642, 642, 643, 125, 125, 125, 125, 125, 644, 644, 645, - 125, 125, 125, 125, 125, 125, 646, 647, 648, 648, 648, 648, 648, 648, 648, 648, - 648, 648, 648, 648, 649, 650, 125, 125, 651, 651, 651, 651, 652, 653, 125, 125, - 125, 125, 125, 125, 125, 125, 125, 333, 0, 0, 0, 654, 125, 125, 125, 125, - 333, 0, 0, 247, 125, 125, 125, 125, 655, 27, 656, 657, 658, 659, 660, 661, - 662, 663, 664, 663, 125, 125, 125, 665, 0, 0, 357, 0, 0, 0, 0, 0, - 0, 603, 226, 333, 333, 333, 0, 606, 0, 0, 247, 125, 125, 125, 666, 0, - 667, 0, 0, 357, 613, 668, 606, 125, 0, 0, 0, 0, 0, 669, 349, 349, - 0, 0, 0, 0, 0, 0, 0, 670, 0, 0, 0, 0, 0, 284, 357, 228, - 357, 0, 0, 0, 671, 284, 0, 0, 671, 0, 247, 668, 125, 125, 125, 125, - 0, 0, 0, 0, 0, 603, 247, 349, 613, 0, 0, 672, 673, 357, 613, 613, - 0, 329, 0, 0, 235, 125, 125, 284, 248, 248, 248, 248, 248, 248, 125, 125, - 248, 248, 248, 318, 248, 248, 248, 248, 248, 317, 248, 248, 248, 248, 248, 248, - 248, 248, 584, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 674, 248, - 248, 248, 248, 248, 248, 317, 125, 125, 248, 317, 125, 125, 125, 125, 125, 125, - 248, 248, 248, 248, 675, 248, 248, 248, 248, 248, 248, 125, 125, 125, 125, 125, - 676, 125, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 1, 2, 2, 2, - 2, 2, 3, 0, 0, 0, 4, 0, 2, 2, 2, 2, 2, 3, 2, 2, - 2, 2, 5, 0, 2, 5, 6, 0, 7, 7, 7, 7, 8, 9, 10, 11, - 12, 13, 14, 15, 8, 8, 8, 8, 16, 8, 8, 8, 17, 18, 18, 18, - 19, 19, 19, 19, 19, 20, 19, 19, 21, 22, 22, 22, 22, 22, 22, 22, - 22, 23, 21, 22, 22, 22, 23, 21, 24, 25, 25, 25, 25, 25, 25, 25, - 25, 25, 12, 12, 25, 25, 26, 27, 25, 28, 12, 12, 29, 30, 29, 31, - 29, 29, 32, 32, 29, 29, 29, 29, 31, 29, 33, 7, 7, 34, 29, 29, - 35, 29, 29, 29, 29, 29, 29, 30, 36, 36, 36, 37, 36, 36, 36, 36, - 36, 36, 38, 39, 40, 40, 40, 40, 41, 12, 12, 12, 42, 42, 42, 42, - 42, 42, 43, 44, 45, 45, 45, 45, 45, 45, 45, 46, 45, 45, 45, 47, - 48, 48, 48, 48, 48, 48, 48, 49, 36, 36, 38, 12, 29, 29, 29, 50, - 51, 12, 29, 29, 52, 29, 29, 29, 53, 53, 53, 53, 54, 55, 53, 53, - 53, 56, 53, 53, 57, 58, 57, 59, 59, 57, 57, 57, 57, 57, 60, 57, - 61, 62, 63, 57, 57, 59, 59, 64, 12, 65, 12, 66, 57, 62, 57, 57, - 57, 57, 57, 64, 67, 67, 68, 69, 70, 71, 71, 71, 71, 71, 72, 71, - 72, 73, 74, 72, 68, 69, 70, 74, 75, 12, 67, 76, 12, 77, 71, 71, - 71, 68, 12, 12, 78, 78, 79, 80, 80, 79, 79, 79, 79, 79, 81, 79, - 81, 78, 82, 79, 79, 80, 80, 82, 83, 12, 12, 12, 79, 84, 79, 79, - 82, 12, 78, 79, 85, 85, 86, 87, 87, 86, 86, 86, 86, 86, 88, 86, - 88, 85, 89, 86, 86, 87, 87, 89, 12, 85, 12, 90, 86, 91, 86, 86, - 86, 86, 12, 12, 92, 93, 94, 92, 95, 96, 97, 95, 98, 99, 94, 92, - 100, 100, 96, 92, 94, 92, 95, 96, 99, 98, 12, 12, 12, 92, 100, 100, - 100, 100, 94, 12, 101, 101, 101, 102, 102, 101, 101, 101, 101, 101, 102, 101, - 101, 101, 103, 101, 101, 102, 102, 103, 12, 104, 105, 106, 101, 107, 101, 101, - 12, 108, 101, 101, 109, 109, 109, 110, 110, 109, 109, 109, 109, 109, 110, 109, - 109, 111, 112, 109, 109, 110, 110, 112, 12, 113, 12, 113, 109, 114, 109, 109, - 111, 12, 12, 12, 115, 115, 115, 116, 116, 115, 115, 115, 115, 115, 115, 115, - 115, 116, 116, 115, 12, 115, 115, 115, 115, 117, 115, 115, 118, 118, 119, 119, - 119, 120, 121, 119, 119, 119, 119, 119, 122, 119, 119, 123, 119, 120, 124, 125, - 119, 126, 119, 119, 12, 121, 119, 119, 121, 127, 12, 12, 128, 129, 129, 129, - 129, 129, 129, 129, 129, 129, 130, 131, 129, 129, 129, 12, 12, 12, 12, 12, - 132, 133, 134, 135, 135, 135, 135, 135, 135, 136, 135, 135, 135, 135, 135, 137, - 135, 138, 135, 134, 135, 135, 137, 135, 139, 139, 139, 139, 139, 139, 140, 139, - 139, 139, 139, 141, 140, 139, 139, 139, 139, 139, 139, 142, 139, 143, 144, 12, - 145, 145, 145, 145, 146, 146, 146, 146, 146, 147, 12, 148, 146, 146, 149, 146, - 150, 150, 150, 150, 151, 151, 151, 151, 151, 151, 152, 153, 151, 154, 152, 153, - 152, 153, 151, 154, 152, 153, 151, 151, 151, 154, 151, 151, 151, 151, 154, 155, - 151, 151, 151, 156, 151, 151, 153, 12, 157, 157, 157, 157, 157, 158, 157, 158, - 159, 159, 159, 159, 160, 160, 160, 160, 160, 160, 160, 161, 162, 162, 162, 162, - 162, 162, 163, 164, 162, 162, 165, 12, 166, 166, 166, 166, 166, 167, 12, 168, - 169, 169, 169, 169, 169, 170, 12, 12, 171, 171, 171, 171, 171, 12, 12, 12, - 172, 172, 172, 173, 173, 12, 12, 12, 174, 174, 174, 174, 174, 174, 174, 175, - 174, 174, 175, 12, 176, 177, 178, 178, 178, 178, 179, 12, 178, 178, 178, 178, - 178, 178, 180, 12, 178, 178, 181, 12, 159, 182, 12, 12, 183, 183, 183, 183, - 183, 183, 183, 184, 183, 183, 183, 12, 185, 183, 183, 183, 186, 186, 186, 186, - 186, 186, 186, 187, 186, 188, 12, 12, 189, 189, 189, 189, 189, 189, 189, 12, - 189, 189, 190, 12, 189, 189, 191, 192, 193, 193, 193, 193, 193, 193, 193, 194, - 195, 195, 195, 195, 195, 195, 195, 196, 195, 195, 195, 197, 195, 195, 198, 12, - 195, 195, 195, 198, 7, 7, 7, 199, 200, 200, 200, 200, 200, 200, 200, 201, - 200, 200, 200, 202, 203, 203, 203, 203, 204, 204, 204, 204, 204, 12, 12, 204, - 205, 205, 205, 205, 205, 205, 206, 205, 205, 205, 207, 208, 209, 209, 209, 209, - 19, 19, 210, 12, 146, 146, 211, 212, 203, 203, 12, 12, 213, 7, 7, 7, - 214, 7, 215, 216, 0, 215, 217, 12, 2, 218, 219, 2, 2, 2, 2, 220, - 221, 218, 222, 2, 2, 2, 223, 2, 2, 2, 2, 224, 8, 225, 8, 225, - 8, 8, 226, 226, 8, 8, 8, 225, 8, 15, 8, 8, 8, 10, 8, 227, - 10, 15, 8, 14, 0, 0, 0, 228, 0, 229, 0, 0, 230, 0, 0, 231, - 0, 0, 0, 232, 2, 2, 2, 233, 234, 12, 12, 12, 235, 12, 12, 12, - 0, 236, 237, 0, 4, 0, 0, 0, 0, 0, 0, 4, 2, 2, 5, 12, - 0, 232, 12, 12, 0, 0, 232, 12, 238, 238, 238, 238, 0, 239, 0, 0, - 0, 240, 0, 0, 241, 241, 241, 241, 18, 18, 18, 18, 18, 12, 242, 18, - 243, 243, 243, 243, 243, 243, 12, 244, 245, 12, 12, 244, 151, 154, 12, 12, - 151, 154, 151, 154, 0, 0, 0, 246, 247, 247, 247, 247, 247, 247, 248, 247, - 247, 12, 12, 12, 247, 249, 12, 12, 0, 250, 0, 0, 251, 247, 252, 253, - 0, 0, 247, 0, 254, 255, 255, 255, 255, 255, 255, 255, 255, 256, 257, 258, - 259, 260, 260, 260, 260, 260, 260, 260, 260, 260, 261, 259, 12, 262, 263, 263, - 263, 263, 263, 263, 264, 150, 150, 150, 150, 150, 150, 265, 0, 12, 12, 131, - 150, 150, 150, 266, 260, 260, 260, 261, 260, 260, 0, 0, 267, 267, 267, 267, - 267, 267, 267, 268, 267, 269, 12, 12, 270, 270, 270, 270, 271, 271, 271, 271, - 271, 271, 271, 12, 272, 272, 272, 272, 272, 272, 12, 12, 237, 2, 2, 2, - 2, 2, 231, 2, 2, 2, 273, 12, 274, 275, 276, 12, 277, 2, 2, 2, - 278, 278, 278, 278, 278, 278, 278, 279, 0, 0, 246, 12, 280, 280, 280, 280, - 280, 280, 12, 12, 281, 281, 281, 281, 281, 282, 12, 283, 281, 281, 282, 12, - 284, 284, 284, 284, 284, 284, 284, 285, 286, 286, 286, 286, 286, 12, 12, 287, - 150, 150, 150, 288, 289, 289, 289, 289, 289, 289, 289, 290, 289, 289, 291, 292, - 145, 145, 145, 293, 294, 294, 294, 294, 294, 295, 12, 12, 294, 294, 294, 296, - 294, 294, 296, 294, 297, 297, 297, 297, 298, 12, 12, 12, 12, 12, 299, 297, - 300, 300, 300, 300, 300, 301, 12, 12, 155, 154, 155, 154, 155, 154, 12, 12, - 2, 2, 3, 2, 2, 302, 303, 12, 300, 300, 300, 304, 300, 300, 304, 12, - 150, 12, 12, 12, 150, 265, 305, 150, 150, 150, 150, 12, 247, 247, 247, 249, - 247, 247, 249, 12, 2, 273, 12, 12, 306, 22, 12, 24, 25, 26, 25, 307, - 308, 309, 25, 25, 50, 12, 12, 12, 310, 29, 29, 29, 29, 29, 29, 311, - 312, 29, 29, 29, 29, 29, 12, 310, 7, 7, 7, 313, 232, 0, 0, 0, - 0, 232, 0, 12, 29, 314, 29, 29, 29, 29, 29, 315, 316, 0, 0, 0, - 0, 317, 260, 260, 260, 260, 260, 318, 319, 150, 319, 150, 319, 150, 319, 288, - 0, 232, 0, 232, 12, 12, 316, 246, 320, 320, 320, 321, 320, 320, 320, 320, - 320, 322, 320, 320, 320, 320, 322, 323, 320, 320, 320, 324, 320, 320, 322, 12, - 232, 131, 0, 0, 0, 131, 0, 0, 8, 8, 8, 14, 0, 0, 0, 234, - 325, 12, 12, 12, 0, 0, 0, 326, 327, 327, 327, 327, 327, 327, 327, 328, - 329, 329, 329, 329, 330, 12, 12, 12, 215, 0, 0, 0, 0, 0, 0, 12, - 331, 331, 331, 331, 331, 12, 12, 332, 333, 333, 333, 333, 333, 333, 334, 12, - 335, 335, 335, 335, 335, 335, 336, 12, 337, 337, 337, 337, 337, 337, 337, 338, - 339, 339, 339, 339, 339, 12, 339, 339, 339, 340, 12, 12, 341, 341, 341, 341, - 342, 342, 342, 342, 343, 343, 343, 343, 343, 343, 343, 344, 343, 343, 344, 12, - 345, 345, 345, 345, 345, 12, 345, 345, 345, 345, 345, 12, 346, 346, 346, 346, - 346, 346, 12, 12, 347, 347, 347, 347, 347, 12, 12, 348, 349, 349, 350, 349, - 350, 351, 349, 349, 351, 349, 349, 349, 351, 349, 351, 352, 353, 353, 353, 353, - 353, 354, 12, 12, 353, 355, 12, 12, 353, 353, 12, 12, 2, 274, 2, 2, - 356, 2, 273, 12, 357, 358, 359, 357, 357, 357, 357, 357, 357, 360, 361, 362, - 363, 363, 363, 363, 363, 364, 363, 363, 365, 365, 365, 365, 366, 366, 366, 366, - 366, 366, 366, 367, 12, 368, 366, 366, 369, 369, 369, 369, 370, 371, 372, 369, - 373, 373, 373, 373, 373, 373, 373, 374, 375, 375, 375, 375, 375, 375, 376, 377, - 378, 378, 378, 378, 379, 379, 379, 379, 379, 379, 12, 379, 380, 379, 379, 379, - 381, 382, 12, 381, 381, 383, 383, 381, 381, 381, 381, 381, 381, 384, 385, 386, - 381, 381, 387, 12, 388, 388, 388, 388, 389, 389, 389, 389, 390, 390, 390, 390, - 390, 391, 392, 390, 390, 391, 12, 12, 393, 393, 393, 393, 393, 394, 395, 393, - 396, 396, 396, 396, 396, 397, 396, 396, 398, 398, 398, 398, 399, 12, 398, 398, - 400, 400, 400, 400, 401, 12, 402, 403, 12, 12, 402, 400, 404, 404, 404, 404, - 404, 404, 405, 12, 406, 406, 406, 406, 407, 12, 12, 12, 407, 12, 408, 406, - 409, 409, 409, 409, 409, 409, 12, 12, 409, 409, 410, 12, 411, 411, 411, 411, - 411, 411, 412, 413, 413, 12, 12, 12, 12, 12, 12, 414, 415, 415, 415, 415, - 415, 415, 12, 12, 416, 416, 416, 416, 416, 416, 417, 12, 418, 418, 418, 418, - 418, 418, 419, 12, 420, 420, 420, 420, 420, 420, 420, 12, 421, 421, 421, 421, - 421, 422, 12, 12, 423, 423, 423, 423, 423, 423, 423, 424, 425, 423, 423, 423, - 423, 424, 12, 426, 427, 427, 427, 427, 428, 12, 12, 429, 430, 430, 430, 430, - 430, 430, 431, 12, 430, 430, 432, 12, 433, 433, 433, 433, 433, 434, 433, 433, - 433, 433, 12, 12, 435, 435, 435, 435, 435, 436, 12, 12, 437, 437, 437, 437, - 118, 119, 119, 119, 119, 127, 12, 12, 438, 438, 438, 438, 439, 438, 438, 438, - 440, 12, 12, 12, 441, 442, 443, 444, 441, 441, 441, 444, 441, 441, 445, 12, - 446, 446, 446, 446, 446, 446, 447, 12, 446, 446, 448, 12, 449, 450, 449, 451, - 451, 449, 449, 449, 449, 449, 452, 449, 452, 450, 453, 449, 449, 451, 451, 454, - 455, 456, 12, 450, 449, 457, 449, 455, 449, 455, 12, 12, 458, 458, 458, 458, - 458, 458, 458, 459, 460, 12, 12, 12, 461, 461, 461, 461, 461, 461, 12, 12, - 461, 461, 462, 12, 463, 463, 463, 463, 463, 464, 463, 463, 463, 463, 463, 464, - 465, 465, 465, 465, 465, 466, 12, 12, 465, 465, 467, 12, 178, 178, 178, 180, - 468, 468, 468, 468, 468, 468, 469, 12, 470, 470, 470, 470, 470, 470, 471, 472, - 470, 470, 470, 12, 470, 471, 12, 12, 473, 473, 473, 473, 473, 473, 473, 12, - 474, 474, 474, 474, 475, 12, 12, 476, 477, 478, 479, 477, 477, 480, 477, 477, - 477, 477, 477, 477, 477, 481, 482, 477, 477, 478, 12, 12, 477, 477, 483, 12, - 484, 484, 485, 484, 484, 484, 484, 484, 484, 486, 12, 12, 487, 487, 487, 487, - 487, 487, 12, 12, 488, 488, 488, 488, 489, 12, 12, 12, 490, 490, 490, 490, - 490, 490, 491, 12, 53, 53, 492, 12, 493, 493, 494, 493, 493, 493, 493, 493, - 493, 495, 493, 493, 493, 496, 12, 12, 493, 493, 493, 497, 498, 498, 498, 498, - 499, 498, 498, 498, 498, 498, 500, 498, 498, 501, 12, 12, 502, 503, 504, 502, - 502, 502, 502, 502, 502, 503, 505, 504, 502, 502, 12, 12, 502, 502, 506, 12, - 507, 508, 509, 507, 507, 507, 507, 507, 507, 507, 507, 510, 508, 507, 511, 12, - 507, 507, 512, 12, 513, 513, 513, 513, 513, 513, 514, 12, 515, 515, 515, 515, - 516, 515, 515, 515, 515, 515, 517, 518, 515, 515, 519, 12, 520, 12, 12, 12, - 100, 100, 100, 100, 96, 12, 12, 98, 521, 521, 521, 521, 521, 521, 522, 12, - 521, 521, 521, 523, 521, 524, 12, 12, 521, 12, 12, 12, 525, 525, 525, 525, - 526, 12, 12, 12, 527, 527, 527, 527, 527, 528, 12, 12, 529, 529, 529, 529, - 529, 530, 12, 12, 272, 272, 531, 12, 532, 532, 532, 532, 532, 532, 532, 533, - 532, 532, 534, 535, 536, 536, 536, 536, 536, 536, 536, 537, 536, 536, 538, 12, - 539, 539, 539, 539, 539, 539, 539, 540, 539, 540, 12, 12, 541, 541, 541, 541, - 541, 542, 12, 12, 541, 541, 543, 541, 543, 541, 541, 541, 541, 541, 12, 544, - 545, 545, 545, 545, 545, 545, 546, 12, 547, 547, 547, 547, 547, 547, 548, 549, - 547, 547, 12, 549, 550, 551, 12, 12, 249, 12, 12, 12, 552, 552, 552, 552, - 552, 552, 12, 12, 553, 553, 553, 553, 553, 554, 12, 12, 552, 552, 555, 12, - 260, 556, 260, 557, 558, 255, 255, 255, 559, 12, 12, 12, 560, 12, 12, 12, - 256, 561, 12, 12, 12, 260, 12, 12, 562, 562, 562, 562, 562, 562, 562, 12, - 563, 563, 563, 563, 563, 563, 564, 12, 563, 563, 563, 565, 563, 563, 565, 12, - 563, 563, 566, 563, 0, 12, 12, 12, 7, 7, 7, 567, 7, 199, 12, 12, - 0, 246, 12, 12, 0, 232, 316, 0, 0, 568, 228, 0, 0, 0, 568, 7, - 213, 569, 7, 0, 0, 0, 570, 228, 8, 225, 12, 12, 0, 0, 234, 12, - 0, 0, 0, 229, 571, 572, 316, 229, 0, 0, 240, 316, 0, 316, 0, 0, - 0, 240, 232, 316, 0, 229, 0, 229, 0, 0, 240, 232, 0, 573, 239, 0, - 229, 0, 0, 0, 0, 246, 0, 0, 0, 0, 0, 239, 574, 574, 574, 574, - 574, 574, 574, 12, 12, 12, 575, 574, 576, 574, 574, 574, 2, 2, 2, 273, - 12, 275, 273, 12, 241, 577, 241, 241, 241, 241, 578, 241, 579, 580, 577, 12, - 19, 19, 19, 581, 12, 12, 12, 582, 583, 583, 583, 583, 583, 583, 583, 584, - 583, 583, 583, 585, 583, 583, 585, 586, 587, 587, 587, 587, 587, 587, 587, 588, - 589, 589, 589, 589, 589, 589, 590, 591, 592, 592, 592, 592, 592, 592, 593, 12, - 151, 154, 151, 594, 151, 151, 151, 154, 595, 595, 595, 595, 595, 596, 595, 595, - 595, 597, 12, 12, 598, 598, 598, 598, 598, 598, 598, 12, 598, 598, 599, 600, - 0, 234, 12, 12, 29, 414, 29, 29, 601, 602, 414, 29, 50, 29, 603, 12, - 604, 310, 603, 414, 601, 602, 603, 603, 601, 602, 50, 29, 50, 29, 414, 605, - 29, 29, 606, 29, 29, 29, 29, 12, 414, 414, 606, 29, 51, 12, 12, 12, - 12, 239, 0, 0, 607, 12, 12, 12, 246, 12, 12, 12, 0, 0, 12, 0, - 0, 232, 131, 0, 0, 0, 12, 12, 0, 0, 0, 240, 0, 246, 12, 239, - 608, 12, 12, 12, 247, 247, 609, 12, 610, 12, 12, 12, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 939, 940, 941, 942, - 946, 948, 0, 962, 969, 970, 971, 976,1001,1002,1003,1008, 0,1033,1040,1041, - 1042,1043,1047, 0, 0,1080,1081,1082,1086,1110, 0, 0,1124,1125,1126,1127, - 1131,1133, 0,1147,1154,1155,1156,1161,1187,1188,1189,1193, 0,1219,1226,1227, - 1228,1229,1233, 0, 0,1267,1268,1269,1273,1298, 0,1303, 943,1128, 944,1129, - 954,1139, 958,1143, 959,1144, 960,1145, 961,1146, 964,1149, 0, 0, 973,1158, - 974,1159, 975,1160, 983,1168, 978,1163, 988,1173, 990,1175, 991,1176, 993,1178, - 994,1179, 0, 0,1004,1190,1005,1191,1006,1192,1014,1199,1007, 0, 0, 0, - 1016,1201,1020,1206, 0,1022,1208,1025,1211,1023,1209, 0, 0, 0, 0,1032, - 1218,1037,1223,1035,1221, 0, 0, 0,1044,1230,1045,1231,1049,1235, 0, 0, - 1058,1244,1064,1250,1060,1246,1066,1252,1067,1253,1072,1258,1069,1255,1077,1264, - 1074,1261, 0, 0,1083,1270,1084,1271,1085,1272,1088,1275,1089,1276,1096,1283, - 1103,1290,1111,1299,1115,1118,1307,1120,1309,1121,1310, 0,1053,1239, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1093,1280, 0, 0, 0, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 705, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 706, + 0, 0, 1, 1, 0, 2, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 5, 0, 6, 7, 7, 7, 8, 9, 10, 11, 12, + 13, 13, 13, 13, 14, 13, 13, 13, 13, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 23, 23, 26, 23, 27, 28, 29, 23, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 23, 23, 39, 40, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 82, 86, 86, 87, 88, 89, 90, 91, 82, + 92, 92, 92, 92, 92, 93, 94, 95, 96, 96, 96, 96, 96, 96, 96, 96, + 97, 97, 98, 97, 99, 100, 101, 97, 102, 97, 103, 104, 105, 106, 106, 107, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 109, 110, 110, 111, + 112, 113, 114, 115, 116, 116, 117, 118, 119, 120, 120, 121, 120, 122, 108, 123, + 124, 125, 126, 127, 128, 129, 130, 116, 131, 132, 133, 134, 135, 136, 137, 82, + 138, 138, 139, 138, 140, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, + 4, 151, 152, 153, 4, 154, 7, 7, 155, 11, 156, 157, 11, 158, 159, 160, + 161, 0, 0, 162, 163, 0, 164, 165, 0, 166, 167, 4, 168, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 169, 170, 0, 0, 0, 0, 0, + 171, 171, 171, 171, 171, 171, 171, 171, 0, 0, 0, 172, 173, 0, 0, 0, + 174, 174, 174, 4, 175, 175, 175, 176, 93, 177, 178, 179, 180, 181, 181, 13, + 0, 0, 182, 82, 183, 184, 184, 185, 184, 184, 184, 184, 184, 184, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 96, 96, 198, 199, 0, 200, + 201, 0, 0, 202, 0, 0, 203, 204, 194, 194, 205, 0, 0, 0, 0, 0, + 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 0, 0, + 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 207, 206, 208, 209, + 210, 210, 210, 210, 210, 210, 210, 210, 210, 211, 13, 13, 13, 212, 212, 213, + 0, 214, 4, 4, 215, 4, 216, 217, 218, 219, 220, 221, 222, 222, 223, 40, + 224, 225, 226, 227, 228, 228, 229, 230, 231, 232, 233, 92, 234, 234, 235, 236, + 237, 238, 239, 240, 106, 106, 241, 242, 96, 96, 96, 96, 96, 243, 244, 245, + 82, 82, 82, 82, 82, 82, 82, 82, 184, 184, 184, 246, 184, 184, 247, 82, + 248, 249, 250, 23, 23, 23, 251, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 252, 23, 23, 253, 23, 254, 255, 256, 257, 258, 259, 23, 23, 23, 260, + 261, 1, 1, 262, 263, 201, 264, 265, 266, 267, 268, 82, 269, 269, 269, 270, + 271, 272, 11, 11, 273, 274, 187, 275, 82, 82, 82, 82, 276, 277, 278, 279, + 280, 281, 282, 283, 284, 285, 286, 82, 287, 287, 288, 289, 290, 291, 292, 293, + 294, 295, 296, 297, 298, 299, 300, 301, 302, 302, 302, 302, 302, 302, 302, 302, + 302, 303, 304, 305, 306, 307, 82, 82, 308, 309, 310, 311, 312, 313, 82, 314, + 315, 316, 82, 82, 317, 318, 319, 320, 321, 322, 323, 324, 325, 82, 326, 327, + 328, 329, 330, 331, 332, 333, 82, 82, 334, 334, 335, 82, 336, 337, 336, 338, + 339, 340, 341, 342, 343, 82, 82, 82, 82, 82, 82, 344, 345, 346, 347, 348, + 349, 350, 351, 352, 353, 354, 355, 356, 357, 357, 358, 359, 360, 360, 361, 362, + 363, 364, 365, 366, 367, 367, 367, 368, 369, 370, 371, 82, 372, 373, 374, 375, + 376, 377, 378, 379, 380, 381, 382, 383, 384, 384, 385, 386, 387, 387, 388, 82, + 82, 82, 82, 82, 389, 390, 391, 82, 392, 392, 393, 394, 395, 396, 397, 398, + 399, 400, 401, 82, 82, 82, 82, 82, 402, 403, 82, 82, 82, 404, 404, 405, + 406, 407, 408, 82, 82, 409, 410, 411, 412, 412, 413, 414, 414, 415, 416, 417, + 418, 82, 82, 82, 82, 82, 419, 420, 421, 422, 423, 424, 425, 426, 82, 82, + 427, 428, 429, 430, 431, 432, 82, 82, 82, 82, 82, 82, 82, 82, 82, 433, + 434, 435, 436, 82, 82, 437, 438, 439, 440, 440, 440, 440, 440, 440, 440, 440, + 440, 440, 440, 440, 441, 82, 82, 82, 440, 440, 440, 442, 440, 440, 440, 440, + 440, 440, 443, 82, 82, 82, 82, 82, 82, 82, 82, 82, 444, 445, 445, 446, + 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 448, 447, 447, 447, 447, 447, + 447, 447, 447, 447, 447, 447, 447, 449, 450, 450, 450, 450, 450, 450, 450, 450, + 450, 450, 451, 82, 82, 82, 82, 82, 452, 453, 82, 82, 82, 82, 82, 82, + 212, 212, 212, 212, 212, 212, 212, 212, 212, 454, 455, 456, 457, 458, 459, 460, + 461, 461, 462, 463, 464, 82, 82, 82, 82, 82, 465, 466, 82, 82, 82, 82, + 82, 82, 467, 467, 468, 82, 82, 82, 469, 469, 470, 469, 471, 82, 82, 472, + 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 474, + 475, 475, 475, 475, 475, 475, 475, 475, 475, 475, 475, 475, 475, 475, 476, 477, + 478, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 479, + 480, 191, 191, 191, 191, 191, 191, 191, 191, 481, 482, 483, 484, 484, 484, 484, + 484, 484, 484, 484, 484, 484, 484, 485, 486, 486, 486, 487, 488, 489, 82, 82, + 0, 0, 0, 0, 0, 0, 0, 490, 0, 0, 0, 0, 0, 491, 82, 82, + 7, 492, 493, 0, 0, 0, 489, 82, 0, 0, 0, 0, 0, 0, 0, 494, + 0, 495, 0, 496, 497, 498, 0, 170, 11, 11, 499, 82, 82, 82, 491, 491, + 0, 0, 500, 501, 82, 82, 82, 82, 0, 0, 502, 0, 503, 504, 505, 0, + 506, 507, 508, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 509, 0, 0, + 0, 0, 0, 0, 0, 0, 510, 0, 511, 511, 511, 511, 511, 511, 511, 511, + 511, 511, 511, 511, 512, 513, 82, 82, 514, 515, 82, 82, 82, 82, 82, 82, + 516, 517, 13, 518, 519, 82, 82, 82, 520, 521, 522, 82, 82, 82, 82, 82, + 82, 82, 82, 82, 523, 524, 525, 526, 82, 82, 82, 82, 82, 82, 527, 528, + 82, 82, 82, 82, 82, 82, 529, 530, 82, 82, 82, 82, 82, 82, 82, 531, + 532, 532, 532, 532, 532, 532, 533, 82, 534, 534, 535, 82, 82, 82, 82, 82, + 82, 82, 82, 536, 0, 537, 82, 82, 261, 182, 82, 82, 82, 82, 82, 82, + 538, 539, 540, 541, 542, 543, 82, 544, 0, 545, 0, 0, 491, 546, 547, 494, + 0, 0, 0, 0, 0, 548, 82, 549, 550, 551, 552, 553, 82, 82, 82, 82, + 0, 0, 0, 0, 0, 0, 554, 555, 0, 0, 0, 556, 0, 0, 490, 557, + 545, 0, 558, 0, 559, 560, 561, 82, 0, 0, 491, 562, 563, 0, 564, 565, + 0, 0, 0, 0, 258, 0, 0, 490, 184, 184, 184, 184, 184, 184, 184, 82, + 184, 247, 184, 184, 184, 184, 184, 184, 566, 184, 184, 184, 184, 184, 184, 184, + 184, 184, 184, 184, 184, 567, 184, 184, 184, 184, 184, 184, 184, 184, 184, 568, + 184, 184, 566, 82, 82, 82, 82, 82, 566, 82, 82, 82, 82, 82, 82, 82, + 184, 184, 569, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 570, 82, 82, + 571, 0, 0, 0, 82, 82, 82, 82, 7, 7, 7, 7, 7, 7, 7, 572, + 0, 0, 0, 0, 1, 2, 2, 3, 0, 4, 0, 4, 2, 2, 5, 2, + 2, 2, 2, 2, 2, 2, 2, 6, 7, 8, 0, 0, 9, 9, 9, 9, + 9, 9, 10, 11, 12, 13, 14, 14, 15, 14, 14, 14, 14, 14, 14, 14, + 16, 17, 14, 14, 18, 18, 18, 18, 19, 18, 18, 18, 18, 18, 20, 21, + 21, 21, 22, 20, 21, 21, 21, 21, 21, 23, 24, 25, 25, 25, 25, 25, + 25, 26, 25, 25, 25, 27, 28, 26, 29, 30, 31, 32, 31, 31, 31, 31, + 33, 34, 35, 31, 31, 31, 36, 31, 31, 31, 31, 29, 37, 38, 37, 37, + 37, 37, 37, 37, 37, 39, 31, 31, 40, 40, 40, 40, 40, 40, 41, 26, + 42, 42, 42, 42, 42, 42, 42, 43, 44, 44, 44, 44, 44, 45, 44, 46, + 47, 47, 47, 48, 37, 49, 31, 31, 31, 50, 51, 31, 52, 31, 31, 31, + 53, 53, 53, 53, 53, 53, 54, 53, 55, 53, 53, 53, 56, 57, 58, 59, + 59, 60, 61, 62, 57, 63, 64, 65, 66, 59, 59, 67, 68, 69, 70, 71, + 71, 72, 73, 74, 69, 75, 76, 77, 78, 71, 79, 26, 80, 81, 82, 83, + 83, 84, 85, 86, 81, 87, 88, 26, 89, 83, 90, 91, 92, 93, 94, 95, + 95, 96, 97, 98, 93, 99, 100, 101, 102, 95, 95, 26, 103, 104, 105, 106, + 107, 104, 108, 109, 104, 105, 110, 26, 111, 108, 108, 112, 113, 114, 115, 113, + 113, 115, 113, 116, 114, 117, 118, 119, 120, 113, 121, 113, 122, 123, 124, 122, + 122, 124, 125, 126, 123, 127, 128, 128, 129, 122, 130, 26, 131, 132, 133, 131, + 131, 131, 131, 131, 132, 133, 134, 131, 135, 131, 131, 131, 136, 137, 138, 139, + 137, 137, 140, 141, 138, 142, 143, 137, 144, 137, 145, 26, 146, 147, 147, 147, + 147, 147, 147, 148, 147, 147, 147, 149, 26, 26, 26, 26, 150, 151, 152, 152, + 153, 152, 152, 154, 155, 156, 152, 157, 158, 158, 158, 158, 158, 159, 158, 158, + 158, 160, 159, 158, 158, 158, 158, 159, 158, 158, 158, 161, 158, 161, 162, 163, + 164, 164, 164, 164, 165, 165, 165, 165, 166, 167, 165, 165, 165, 165, 165, 168, + 169, 169, 169, 169, 170, 170, 170, 170, 170, 171, 172, 171, 170, 171, 170, 170, + 170, 170, 171, 172, 171, 170, 172, 170, 170, 170, 171, 170, 170, 170, 170, 173, + 170, 170, 170, 174, 170, 170, 170, 175, 176, 176, 176, 176, 176, 176, 177, 177, + 178, 178, 178, 178, 179, 179, 179, 180, 181, 181, 181, 181, 181, 182, 181, 183, + 184, 184, 185, 186, 187, 187, 188, 26, 189, 189, 190, 26, 191, 192, 193, 26, + 194, 194, 194, 194, 194, 194, 194, 195, 194, 196, 194, 196, 197, 198, 198, 199, + 198, 198, 198, 198, 198, 198, 198, 200, 198, 201, 178, 178, 178, 178, 202, 26, + 203, 203, 203, 204, 203, 205, 203, 205, 206, 203, 207, 207, 207, 208, 209, 26, + 210, 210, 210, 210, 210, 211, 210, 210, 210, 212, 210, 213, 214, 214, 214, 215, + 216, 216, 216, 216, 216, 216, 216, 217, 216, 216, 216, 218, 216, 219, 216, 219, + 216, 220, 9, 9, 9, 221, 26, 26, 222, 222, 222, 222, 222, 223, 222, 222, + 224, 224, 224, 224, 225, 225, 225, 225, 225, 225, 226, 227, 228, 228, 228, 228, + 228, 228, 228, 229, 228, 230, 231, 231, 231, 231, 231, 231, 18, 232, 165, 165, + 165, 165, 165, 233, 224, 26, 234, 9, 235, 236, 237, 238, 239, 240, 2, 2, + 2, 2, 2, 241, 242, 243, 2, 244, 2, 2, 2, 245, 14, 14, 246, 246, + 246, 246, 14, 247, 14, 14, 14, 246, 14, 14, 248, 14, 248, 14, 249, 250, + 14, 14, 251, 252, 0, 253, 0, 0, 254, 0, 255, 256, 0, 257, 2, 258, + 259, 26, 9, 9, 9, 9, 260, 26, 261, 262, 4, 0, 0, 263, 0, 0, + 2, 264, 0, 0, 0, 265, 26, 26, 0, 266, 26, 26, 267, 267, 267, 267, + 0, 0, 268, 0, 0, 0, 269, 0, 270, 270, 270, 270, 17, 17, 17, 17, + 17, 17, 271, 272, 166, 167, 273, 273, 273, 273, 273, 273, 273, 274, 275, 274, + 170, 170, 172, 26, 172, 172, 172, 172, 0, 0, 0, 276, 277, 277, 277, 278, + 277, 277, 277, 277, 277, 277, 279, 26, 277, 277, 280, 26, 26, 26, 0, 0, + 281, 0, 0, 0, 282, 283, 0, 284, 285, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 287, 288, 289, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 291, + 292, 293, 293, 293, 293, 293, 294, 169, 169, 295, 0, 0, 293, 293, 293, 293, + 276, 296, 290, 290, 169, 169, 169, 295, 169, 169, 169, 297, 0, 0, 290, 290, + 290, 290, 290, 298, 290, 290, 290, 0, 299, 299, 299, 299, 299, 300, 299, 299, + 301, 26, 302, 302, 302, 302, 302, 302, 303, 303, 303, 303, 303, 304, 26, 26, + 305, 305, 305, 305, 305, 305, 305, 26, 306, 2, 2, 2, 2, 307, 2, 2, + 2, 308, 309, 258, 26, 26, 310, 2, 311, 311, 311, 311, 311, 312, 0, 265, + 313, 313, 313, 313, 313, 313, 313, 26, 314, 314, 314, 314, 315, 316, 314, 317, + 318, 318, 318, 318, 318, 319, 320, 320, 320, 320, 321, 322, 169, 169, 169, 323, + 324, 324, 324, 324, 324, 325, 324, 326, 164, 164, 164, 327, 328, 328, 328, 328, + 328, 328, 329, 26, 328, 330, 328, 331, 332, 332, 332, 332, 333, 26, 26, 334, + 335, 335, 336, 26, 337, 337, 337, 26, 172, 172, 2, 2, 2, 2, 2, 338, + 339, 340, 176, 176, 335, 335, 335, 335, 335, 341, 335, 342, 343, 26, 169, 169, + 295, 344, 169, 169, 169, 169, 169, 343, 277, 280, 277, 277, 277, 277, 277, 345, + 346, 26, 347, 348, 25, 25, 349, 350, 351, 25, 31, 31, 352, 26, 353, 31, + 31, 31, 31, 354, 31, 31, 355, 31, 31, 356, 26, 26, 26, 26, 31, 31, + 9, 9, 0, 265, 9, 357, 0, 0, 0, 0, 358, 0, 257, 359, 360, 31, + 31, 31, 31, 361, 362, 0, 0, 0, 363, 290, 289, 290, 290, 290, 290, 364, + 365, 365, 365, 366, 257, 257, 26, 367, 368, 369, 368, 368, 370, 368, 368, 371, + 368, 372, 368, 372, 368, 368, 368, 368, 368, 368, 368, 373, 374, 0, 0, 0, + 0, 0, 375, 0, 14, 252, 0, 376, 377, 26, 26, 26, 0, 0, 0, 378, + 379, 379, 379, 380, 381, 381, 381, 381, 381, 381, 382, 26, 383, 0, 0, 359, + 384, 384, 384, 384, 385, 386, 387, 387, 387, 388, 389, 389, 389, 389, 389, 390, + 391, 391, 391, 392, 393, 393, 393, 393, 394, 393, 395, 26, 396, 396, 396, 396, + 396, 396, 397, 397, 397, 397, 397, 397, 398, 398, 398, 399, 398, 400, 401, 401, + 401, 401, 402, 401, 401, 401, 401, 402, 403, 403, 403, 403, 403, 26, 404, 404, + 404, 404, 404, 404, 405, 406, 407, 408, 407, 408, 409, 407, 410, 407, 410, 411, + 412, 412, 412, 412, 412, 412, 413, 26, 414, 414, 414, 414, 414, 414, 415, 26, + 414, 414, 416, 26, 414, 26, 26, 26, 417, 2, 2, 2, 2, 2, 418, 419, + 420, 421, 422, 422, 422, 422, 423, 424, 425, 425, 426, 425, 427, 427, 427, 427, + 428, 428, 428, 429, 430, 428, 26, 26, 431, 431, 432, 433, 434, 434, 434, 435, + 436, 436, 436, 437, 438, 438, 438, 438, 439, 439, 439, 440, 439, 439, 441, 439, + 439, 439, 439, 439, 442, 443, 444, 445, 446, 446, 447, 448, 446, 449, 446, 449, + 450, 450, 450, 450, 451, 451, 451, 451, 452, 452, 452, 452, 453, 454, 453, 26, + 455, 455, 455, 455, 455, 455, 456, 457, 458, 458, 459, 458, 460, 460, 461, 460, + 462, 462, 463, 464, 26, 465, 26, 26, 466, 466, 466, 466, 466, 467, 26, 26, + 468, 468, 468, 468, 468, 468, 469, 26, 468, 468, 469, 470, 471, 471, 471, 471, + 471, 26, 471, 472, 473, 473, 473, 473, 474, 475, 473, 473, 474, 476, 26, 26, + 31, 31, 31, 50, 477, 477, 477, 477, 477, 478, 479, 26, 480, 26, 26, 26, + 26, 26, 26, 481, 482, 482, 482, 482, 482, 26, 483, 483, 483, 483, 483, 484, + 26, 26, 485, 485, 485, 486, 26, 26, 26, 26, 487, 487, 487, 488, 26, 26, + 489, 489, 490, 26, 491, 491, 491, 491, 491, 492, 493, 491, 491, 491, 492, 494, + 495, 495, 495, 495, 496, 497, 498, 498, 498, 499, 498, 500, 501, 501, 501, 501, + 501, 501, 502, 501, 501, 26, 503, 503, 503, 503, 504, 26, 505, 505, 505, 505, + 506, 137, 507, 26, 508, 508, 509, 508, 508, 508, 508, 508, 510, 26, 26, 26, + 511, 512, 513, 514, 513, 515, 516, 516, 516, 516, 516, 516, 516, 517, 516, 518, + 519, 520, 521, 522, 522, 523, 524, 525, 520, 526, 527, 528, 529, 530, 530, 26, + 531, 532, 531, 531, 531, 531, 533, 531, 534, 535, 533, 536, 537, 26, 26, 26, + 538, 538, 538, 538, 538, 538, 538, 539, 540, 26, 26, 26, 541, 541, 541, 541, + 541, 26, 541, 542, 543, 543, 543, 543, 543, 543, 544, 543, 543, 543, 543, 544, + 545, 545, 545, 545, 546, 26, 545, 547, 198, 548, 26, 26, 549, 549, 549, 549, + 549, 549, 549, 550, 549, 550, 164, 164, 551, 26, 26, 26, 552, 552, 552, 553, + 552, 554, 552, 552, 555, 26, 26, 26, 556, 556, 556, 556, 556, 556, 556, 557, + 558, 558, 558, 558, 558, 558, 559, 560, 561, 562, 563, 564, 564, 564, 565, 566, + 561, 26, 564, 567, 568, 569, 568, 568, 568, 568, 568, 569, 570, 26, 26, 26, + 571, 571, 571, 571, 571, 26, 572, 572, 572, 572, 572, 572, 573, 26, 178, 178, + 574, 574, 574, 574, 574, 574, 574, 575, 53, 576, 26, 26, 577, 577, 577, 577, + 578, 26, 577, 578, 579, 580, 579, 579, 579, 579, 581, 579, 582, 26, 579, 579, + 579, 583, 584, 584, 584, 584, 585, 584, 584, 586, 587, 26, 588, 589, 590, 590, + 590, 590, 588, 591, 590, 26, 590, 592, 593, 594, 595, 595, 595, 596, 597, 598, + 595, 599, 26, 26, 600, 600, 600, 601, 602, 602, 603, 602, 602, 602, 602, 604, + 602, 602, 602, 605, 26, 26, 606, 26, 108, 108, 108, 108, 108, 108, 607, 608, + 609, 609, 609, 609, 609, 609, 609, 610, 609, 611, 612, 26, 613, 26, 26, 26, + 26, 26, 614, 614, 614, 614, 614, 614, 614, 614, 615, 26, 616, 616, 616, 616, + 616, 616, 617, 26, 616, 616, 616, 618, 619, 619, 619, 619, 620, 26, 26, 26, + 621, 621, 621, 621, 621, 621, 621, 622, 305, 305, 305, 623, 624, 624, 624, 625, + 624, 626, 627, 627, 627, 627, 627, 627, 627, 627, 627, 628, 627, 629, 630, 630, + 630, 631, 631, 26, 632, 632, 632, 632, 633, 26, 632, 634, 634, 632, 632, 635, + 632, 632, 26, 26, 636, 636, 636, 636, 636, 636, 636, 637, 638, 638, 638, 638, + 638, 638, 638, 639, 640, 640, 640, 640, 640, 641, 640, 640, 640, 642, 640, 640, + 643, 26, 345, 26, 644, 644, 644, 644, 644, 644, 644, 26, 645, 645, 645, 645, + 645, 645, 646, 26, 26, 26, 26, 647, 644, 648, 26, 26, 26, 26, 649, 650, + 651, 286, 286, 286, 652, 26, 653, 26, 26, 26, 654, 26, 655, 26, 656, 656, + 656, 656, 656, 656, 656, 656, 656, 657, 658, 658, 658, 658, 658, 659, 658, 660, + 658, 661, 658, 662, 359, 26, 26, 26, 0, 0, 0, 265, 0, 0, 359, 26, + 9, 663, 9, 9, 221, 26, 0, 0, 0, 0, 276, 26, 257, 362, 0, 0, + 664, 665, 0, 666, 667, 668, 0, 0, 0, 669, 0, 0, 246, 26, 26, 26, + 0, 0, 257, 26, 0, 0, 0, 259, 0, 0, 254, 0, 0, 0, 0, 254, + 670, 671, 0, 672, 673, 0, 0, 0, 269, 674, 254, 254, 0, 0, 0, 675, + 676, 677, 678, 0, 276, 0, 0, 0, 0, 268, 0, 0, 679, 679, 679, 679, + 679, 680, 26, 681, 682, 679, 26, 26, 2, 2, 2, 346, 683, 419, 26, 26, + 684, 270, 270, 685, 686, 687, 18, 18, 18, 688, 26, 26, 26, 689, 26, 26, + 690, 690, 690, 690, 690, 691, 690, 692, 690, 693, 26, 26, 26, 26, 694, 694, + 694, 695, 26, 26, 696, 696, 696, 696, 696, 696, 696, 697, 26, 26, 698, 698, + 698, 698, 698, 699, 26, 26, 700, 700, 700, 700, 700, 701, 172, 702, 170, 172, + 703, 703, 703, 703, 704, 703, 705, 26, 706, 706, 706, 706, 706, 707, 706, 708, + 26, 26, 362, 0, 0, 0, 376, 26, 709, 31, 31, 31, 710, 711, 712, 713, + 714, 715, 710, 716, 710, 712, 712, 717, 31, 718, 31, 719, 720, 718, 31, 719, + 26, 26, 721, 26, 0, 359, 0, 0, 0, 257, 362, 0, 362, 0, 362, 0, + 0, 276, 26, 26, 722, 0, 0, 0, 723, 26, 0, 0, 0, 0, 0, 359, + 0, 259, 265, 26, 276, 26, 26, 26, 0, 0, 0, 724, 0, 376, 0, 376, + 0, 0, 257, 725, 0, 359, 259, 26, 0, 26, 0, 265, 0, 26, 0, 0, + 0, 276, 0, 359, 265, 26, 26, 26, 0, 276, 0, 376, 0, 726, 0, 0, + 257, 722, 0, 727, 0, 265, 0, 259, 277, 277, 277, 280, 345, 26, 277, 277, + 728, 26, 277, 277, 277, 729, 277, 277, 277, 277, 26, 26, 730, 26, 26, 26, + 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 939, 940, 941, 942, 946, 948, 0, 962, 969, 970, 971, 976, + 1001,1002,1003,1008, 0,1033,1040,1041,1042,1043,1047, 0, 0,1080,1081,1082, + 1086,1110, 0, 0,1124,1125,1126,1127,1131,1133, 0,1147,1154,1155,1156,1161, + 1187,1188,1189,1193, 0,1219,1226,1227,1228,1229,1233, 0, 0,1267,1268,1269, + 1273,1298, 0,1303, 943,1128, 944,1129, 954,1139, 958,1143, 959,1144, 960,1145, + 961,1146, 964,1149, 0, 0, 973,1158, 974,1159, 975,1160, 983,1168, 978,1163, + 988,1173, 990,1175, 991,1176, 993,1178, 994,1179, 0, 0,1004,1190,1005,1191, + 1006,1192,1014,1199,1007, 0, 0, 0,1016,1201,1020,1206, 0,1022,1208,1025, + 1211,1023,1209, 0, 0, 0, 0,1032,1218,1037,1223,1035,1221, 0, 0, 0, + 1044,1230,1045,1231,1049,1235, 0, 0,1058,1244,1064,1250,1060,1246,1066,1252, + 1067,1253,1072,1258,1069,1255,1077,1264,1074,1261, 0, 0,1083,1270,1084,1271, + 1085,1272,1088,1275,1089,1276,1096,1283,1103,1290,1111,1299,1115,1118,1307,1120, + 1309,1121,1310, 0,1053,1239, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1093,1280, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 949,1134,1010,1195,1050,1236,1090, - 1277,1341,1368,1340,1367,1342,1369,1339,1366, 0,1320,1347,1418,1419,1323,1350, - 0, 0, 992,1177,1018,1204,1055,1241,1416,1417,1415,1424,1202, 0, 0, 0, - 987,1172, 0, 0,1031,1217,1321,1348,1322,1349,1338,1365, 950,1135, 951,1136, - 979,1164, 980,1165,1011,1196,1012,1197,1051,1237,1052,1238,1061,1247,1062,1248, - 1091,1278,1092,1279,1071,1257,1076,1263, 0, 0, 997,1182, 0, 0, 0, 0, - 0, 0, 945,1130, 982,1167,1337,1364,1335,1362,1046,1232,1422,1423,1113,1301, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 10, - 1425, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, - 0,1314,1427, 5,1434,1438,1443, 0,1450, 0,1455,1461,1514, 0, 0, 0, + 0, 949,1134,1010,1195,1050,1236,1090,1277,1341,1368,1340,1367,1342,1369,1339, + 1366, 0,1320,1347,1418,1419,1323,1350, 0, 0, 992,1177,1018,1204,1055,1241, + 1416,1417,1415,1424,1202, 0, 0, 0, 987,1172, 0, 0,1031,1217,1321,1348, + 1322,1349,1338,1365, 950,1135, 951,1136, 979,1164, 980,1165,1011,1196,1012,1197, + 1051,1237,1052,1238,1061,1247,1062,1248,1091,1278,1092,1279,1071,1257,1076,1263, + 0, 0, 997,1182, 0, 0, 0, 0, 0, 0, 945,1130, 982,1167,1337,1364, + 1335,1362,1046,1232,1422,1423,1113,1301, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8, 9, 0, 10,1425, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0, 0,1314,1427, 5,1434,1438,1443, 0, + 1450, 0,1455,1461,1514, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1446,1458, + 1468,1476,1480,1486,1517, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1489,1503, + 1494,1500,1508, 0, 0, 0, 0,1520,1521, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1526,1528, 0,1525, 0, 0, 0,1522, 0, 0, 0, 0, + 1536,1532,1539, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1534, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1556, 0, 0, + 0, 0, 0, 0,1548,1550, 0,1547, 0, 0, 0,1567, 0, 0, 0, 0, + 1558,1554,1561, 0, 0, 0, 0, 0, 0, 0,1568,1569, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1529,1551, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1523,1545,1524,1546, 0, 0,1527,1549, 0, 0,1570,1571, + 1530,1552,1531,1553, 0, 0,1533,1555,1535,1557,1537,1559, 0, 0,1572,1573, + 1544,1566,1538,1560,1540,1562,1541,1563,1542,1564, 0, 0,1543,1565, 0, 0, + 0, 0, 0, 0, 0, 0,1606,1607,1609,1608,1610, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1613, 0,1611, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,1612, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1620, 0, 0, + 0, 0, 0, 0, 0,1623, 0, 0,1624, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1614,1615,1616,1617, + 1618,1619,1621,1622, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1628, + 1629, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1625,1626, 0,1627, 0, 0, 0,1634, 0, 0,1635, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1630,1631,1632, + 0, 0,1633, 0, 0, 0, 0, 0, 0, 0, 0, 0,1639, 0, 0,1638, + 1640, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1636,1637, 0, 0, 0, 0, 0, 0,1641, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1642,1644, + 1643, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1645, 0, 0, 0, + 0, 0, 0, 0,1646, 0, 0, 0, 0, 0, 0,1648,1649, 0,1647,1650, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1651,1653, + 1652, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1654, 0, + 1655,1657,1656, 0, 0, 0, 0,1659, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1660, 0, 0, 0, 0,1661, 0, 0, 0, 0,1662, 0, 0, 0, 0, + 1663, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1658, 0, 0, + 0, 0, 0, 0, 0, 0, 0,1664, 0,1665,1673, 0,1674, 0, 0, 0, + 0, 0, 0, 0, 0,1666, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,1668, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1669, 0, 0, 0, 0,1670, 0, 0, 0, 0,1671, 0, 0, 0, 0, + 1672, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1667, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1675, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1676, 0,1677, 0,1678, 0, + 1679, 0,1680, 0, 0, 0,1681, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1682, + 0,1683, 0, 0,1684,1685, 0,1686, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 953,1138, 955,1140, 956,1141, 957,1142,1324,1351, 963,1148, + 965,1150, 968,1153, 966,1151, 967,1152,1378,1380,1379,1381, 984,1169, 985,1170, + 1420,1421, 986,1171, 989,1174, 995,1180, 998,1183, 996,1181, 999,1184,1000,1185, + 1015,1200,1329,1356,1017,1203,1019,1205,1021,1207,1024,1210,1687,1688,1027,1213, + 1026,1212,1028,1214,1029,1215,1030,1216,1034,1220,1036,1222,1039,1225,1038,1224, + 1334,1361,1336,1363,1382,1384,1383,1385,1056,1242,1057,1243,1059,1245,1063,1249, + 1689,1690,1065,1251,1068,1254,1070,1256,1386,1387,1388,1389,1691,1692,1073,1259, + 1075,1262,1079,1266,1078,1265,1095,1282,1098,1285,1097,1284,1390,1391,1392,1393, + 1099,1286,1100,1287,1101,1288,1102,1289,1105,1292,1104,1291,1106,1294,1107,1295, + 1108,1296,1114,1302,1119,1308,1122,1311,1123,1312,1186,1260,1293,1305, 0,1394, + 0, 0, 0, 0, 952,1137, 947,1132,1317,1344,1316,1343,1319,1346,1318,1345, + 1693,1695,1371,1375,1370,1374,1373,1377,1372,1376,1694,1696, 981,1166, 977,1162, + 972,1157,1326,1353,1325,1352,1328,1355,1327,1354,1697,1698,1009,1194,1013,1198, + 1054,1240,1048,1234,1331,1358,1330,1357,1333,1360,1332,1359,1699,1700,1396,1401, + 1395,1400,1398,1403,1397,1402,1399,1404,1094,1281,1087,1274,1406,1411,1405,1410, + 1408,1413,1407,1412,1409,1414,1109,1297,1117,1306,1116,1304,1112,1300, 0, 0, + 0, 0, 0, 0,1471,1472,1701,1705,1702,1706,1703,1707,1430,1431,1715,1719, + 1716,1720,1717,1721,1477,1478,1729,1731,1730,1732, 0, 0,1435,1436,1733,1735, + 1734,1736, 0, 0,1481,1482,1737,1741,1738,1742,1739,1743,1439,1440,1751,1755, + 1752,1756,1753,1757,1490,1491,1765,1768,1766,1769,1767,1770,1447,1448,1771,1774, + 1772,1775,1773,1776,1495,1496,1777,1779,1778,1780, 0, 0,1451,1452,1781,1783, + 1782,1784, 0, 0,1504,1505,1785,1788,1786,1789,1787,1790, 0,1459, 0,1791, + 0,1792, 0,1793,1509,1510,1794,1798,1795,1799,1796,1800,1462,1463,1808,1812, + 1809,1813,1810,1814,1467, 21,1475, 22,1479, 23,1485, 24,1493, 27,1499, 28, + 1507, 29, 0, 0,1704,1708,1709,1710,1711,1712,1713,1714,1718,1722,1723,1724, + 1725,1726,1727,1728,1740,1744,1745,1746,1747,1748,1749,1750,1754,1758,1759,1760, + 1761,1762,1763,1764,1797,1801,1802,1803,1804,1805,1806,1807,1811,1815,1816,1817, + 1818,1819,1820,1821,1470,1469,1822,1474,1465, 0,1473,1825,1429,1428,1426, 12, + 1432, 0, 26, 0, 0,1315,1823,1484,1466, 0,1483,1829,1433, 13,1437, 14, + 1441,1826,1827,1828,1488,1487,1513, 19, 0, 0,1492,1515,1445,1444,1442, 15, + 0,1831,1832,1833,1502,1501,1516, 25,1497,1498,1506,1518,1457,1456,1454, 17, + 1453,1313, 11, 3, 0, 0,1824,1512,1519, 0,1511,1830,1449, 16,1460, 18, + 1464, 4, 0, 0, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 6, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1834,1835, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0,1446,1458,1468,1476,1480,1486,1517, 0, 0, 0, + 0, 0,1836, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1837,1839,1838, 0, 0, 0, 0,1840, 0, 0, 0, 0,1841, 0, 0, + 1842, 0, 0, 0, 0, 0, 0, 0,1843, 0,1844, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1845, 0, 0,1846, 0, 0,1847, 0,1848, 0, 0, + 0, 0, 0, 0, 937, 0,1850, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1849, 936, 938,1851,1852, 0, 0,1853,1854, 0, 0,1855,1856, 0, 0, + 0, 0, 0, 0,1857,1858, 0, 0,1861,1862, 0, 0,1863,1864, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0,1489,1503,1494,1500,1508, 0, 0, 0, 0,1520, - 1521, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1526,1528, 0,1525, - 0, 0, 0,1522, 0, 0, 0, 0,1536,1532,1539, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,1534, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,1556, 0, 0, 0, 0, 0, 0,1548,1550, 0,1547, - 0, 0, 0,1567, 0, 0, 0, 0,1558,1554,1561, 0, 0, 0, 0, 0, - 0, 0,1568,1569, 0, 0, 0, 0, 0, 0, 0, 0, 0,1529,1551, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1523,1545,1524,1546, - 0, 0,1527,1549, 0, 0,1570,1571,1530,1552,1531,1553, 0, 0,1533,1555, - 1535,1557,1537,1559, 0, 0,1572,1573,1544,1566,1538,1560,1540,1562,1541,1563, - 1542,1564, 0, 0,1543,1565, 0, 0, 0, 0, 0, 0, 0, 0,1606,1607, - 1609,1608,1610, 0, 0, 0, 0, 0, 0, 0, 0, 0,1613, 0,1611, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1612, + 1867,1868,1869,1870,1859,1860,1865,1866, 0, 0, 0, 0, 0, 0,1871,1872, + 1873,1874, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,1620, 0, 0, 0, 0, 0, 0, 0,1623, 0, 0, - 1624, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0,1614,1615,1616,1617,1618,1619,1621,1622, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,1628,1629, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0,1625,1626, 0,1627, 0, 0, 0,1634, - 0, 0,1635, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,1630,1631,1632, 0, 0,1633, 0, 0, 0, 0, 0, - 0, 0, 0, 0,1639, 0, 0,1638,1640, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0,1636,1637, 0, 0, 0, 0, 0, 0, - 1641, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0,1642,1644,1643, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0,1645, 0, 0, 0, 0, 0, 0, 0,1646, 0, 0, 0, - 0, 0, 0,1648,1649, 0,1647,1650, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0,1651,1653,1652, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0,1654, 0,1655,1657,1656, 0, 0, 0, 0,1659, - 0, 0, 0, 0, 0, 0, 0, 0, 0,1660, 0, 0, 0, 0,1661, 0, - 0, 0, 0,1662, 0, 0, 0, 0,1663, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,1658, 0, 0, 0, 0, 0, 0, 0, 0, 0,1664, - 0,1665,1673, 0,1674, 0, 0, 0, 0, 0, 0, 0, 0,1666, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1668, - 0, 0, 0, 0, 0, 0, 0, 0, 0,1669, 0, 0, 0, 0,1670, 0, - 0, 0, 0,1671, 0, 0, 0, 0,1672, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,1667, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0,1675, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0,1676, 0,1677, 0,1678, 0,1679, 0,1680, 0, 0, 0,1681, 0, + 1875, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1877, 0,1878, 0,1879, 0,1880, 0,1881, 0,1882, 0,1883, 0,1884, 0, + 1885, 0,1886, 0,1887, 0,1888, 0, 0,1889, 0,1890, 0,1891, 0, 0, + 0, 0, 0, 0,1892,1893, 0,1894,1895, 0,1896,1897, 0,1898,1899, 0, + 1900,1901, 0, 0, 0, 0, 0, 0,1876, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1902, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1904, 0,1905, 0,1906, 0,1907, 0,1908, 0,1909, 0,1910, 0,1911, 0, + 1912, 0,1913, 0,1914, 0,1915, 0, 0,1916, 0,1917, 0,1918, 0, 0, + 0, 0, 0, 0,1919,1920, 0,1921,1922, 0,1923,1924, 0,1925,1926, 0, + 1927,1928, 0, 0, 0, 0, 0, 0,1903, 0, 0,1929,1930,1931,1932, 0, + 0, 0,1933, 0, 710, 385, 724, 715, 455, 103, 186, 825, 825, 242, 751, 205, + 241, 336, 524, 601, 663, 676, 688, 738, 411, 434, 474, 500, 649, 746, 799, 108, + 180, 416, 482, 662, 810, 275, 462, 658, 692, 344, 618, 679, 293, 388, 440, 492, + 740, 116, 146, 168, 368, 414, 481, 527, 606, 660, 665, 722, 781, 803, 809, 538, + 553, 588, 642, 758, 811, 701, 233, 299, 573, 612, 487, 540, 714, 779, 232, 267, + 412, 445, 457, 585, 594, 766, 167, 613, 149, 148, 560, 589, 648, 768, 708, 345, + 411, 704, 105, 259, 313, 496, 518, 174, 542, 120, 307, 101, 430, 372, 584, 183, + 228, 529, 650, 697, 424, 732, 428, 349, 632, 355, 517, 110, 135, 147, 403, 580, + 624, 700, 750, 170, 193, 245, 297, 374, 463, 543, 763, 801, 812, 815, 162, 384, + 420, 730, 287, 330, 337, 366, 459, 476, 509, 558, 591, 610, 726, 652, 734, 759, + 154, 163, 198, 473, 683, 697, 292, 311, 353, 423, 572, 494, 113, 217, 259, 280, + 314, 499, 506, 603, 608, 752, 778, 782, 788, 117, 557, 748, 774, 320, 109, 126, + 260, 265, 373, 411, 479, 523, 655, 737, 823, 380, 765, 161, 395, 398, 438, 451, + 502, 516, 537, 583, 791, 136, 340, 769, 122, 273, 446, 727, 305, 322, 400, 496, + 771, 155, 190, 269, 377, 391, 406, 432, 501, 519, 599, 684, 687, 749, 776, 175, + 452, 191, 480, 510, 659, 772, 805, 813, 397, 444, 619, 566, 568, 575, 491, 471, + 707, 111, 636, 156, 153, 288, 346, 578, 256, 435, 383, 729, 680, 767, 694, 295, + 128, 210, 0, 0, 227, 0, 379, 0, 0, 150, 493, 525, 544, 551, 552, 556, + 783, 576, 604, 0, 661, 0, 703, 0, 0, 735, 743, 0, 0, 0, 793, 794, + 795, 808, 741, 773, 118, 127, 130, 166, 169, 177, 207, 213, 215, 226, 229, 268, + 270, 317, 327, 329, 335, 369, 375, 381, 404, 441, 448, 458, 477, 484, 503, 539, + 545, 547, 546, 548, 549, 550, 554, 555, 561, 564, 569, 591, 593, 595, 598, 607, + 620, 625, 625, 651, 690, 695, 705, 706, 716, 717, 733, 735, 777, 786, 790, 315, + 869, 623, 0, 0, 102, 145, 134, 115, 129, 138, 165, 171, 207, 202, 206, 212, + 227, 231, 240, 243, 250, 254, 294, 296, 303, 308, 319, 325, 321, 329, 326, 335, + 341, 357, 360, 362, 370, 379, 388, 389, 393, 421, 424, 438, 456, 454, 458, 465, + 477, 535, 485, 490, 493, 507, 512, 514, 521, 522, 525, 526, 528, 533, 532, 541, + 565, 569, 574, 586, 591, 597, 607, 637, 647, 674, 691, 693, 695, 698, 703, 699, + 705, 704, 702, 706, 709, 717, 728, 736, 747, 754, 770, 777, 783, 784, 786, 787, + 790, 802, 825, 848, 847, 857, 55, 65, 66, 883, 892, 916, 822, 824, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,1682, 0,1683, 0, 0,1684,1685, 0,1686, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 953,1138, 955,1140, - 956,1141, 957,1142,1324,1351, 963,1148, 965,1150, 968,1153, 966,1151, 967,1152, - 1378,1380,1379,1381, 984,1169, 985,1170,1420,1421, 986,1171, 989,1174, 995,1180, - 998,1183, 996,1181, 999,1184,1000,1185,1015,1200,1329,1356,1017,1203,1019,1205, - 1021,1207,1024,1210,1687,1688,1027,1213,1026,1212,1028,1214,1029,1215,1030,1216, - 1034,1220,1036,1222,1039,1225,1038,1224,1334,1361,1336,1363,1382,1384,1383,1385, - 1056,1242,1057,1243,1059,1245,1063,1249,1689,1690,1065,1251,1068,1254,1070,1256, - 1386,1387,1388,1389,1691,1692,1073,1259,1075,1262,1079,1266,1078,1265,1095,1282, - 1098,1285,1097,1284,1390,1391,1392,1393,1099,1286,1100,1287,1101,1288,1102,1289, - 1105,1292,1104,1291,1106,1294,1107,1295,1108,1296,1114,1302,1119,1308,1122,1311, - 1123,1312,1186,1260,1293,1305, 0,1394, 0, 0, 0, 0, 952,1137, 947,1132, - 1317,1344,1316,1343,1319,1346,1318,1345,1693,1695,1371,1375,1370,1374,1373,1377, - 1372,1376,1694,1696, 981,1166, 977,1162, 972,1157,1326,1353,1325,1352,1328,1355, - 1327,1354,1697,1698,1009,1194,1013,1198,1054,1240,1048,1234,1331,1358,1330,1357, - 1333,1360,1332,1359,1699,1700,1396,1401,1395,1400,1398,1403,1397,1402,1399,1404, - 1094,1281,1087,1274,1406,1411,1405,1410,1408,1413,1407,1412,1409,1414,1109,1297, - 1117,1306,1116,1304,1112,1300, 0, 0, 0, 0, 0, 0,1471,1472,1701,1705, - 1702,1706,1703,1707,1430,1431,1715,1719,1716,1720,1717,1721,1477,1478,1729,1731, - 1730,1732, 0, 0,1435,1436,1733,1735,1734,1736, 0, 0,1481,1482,1737,1741, - 1738,1742,1739,1743,1439,1440,1751,1755,1752,1756,1753,1757,1490,1491,1765,1768, - 1766,1769,1767,1770,1447,1448,1771,1774,1772,1775,1773,1776,1495,1496,1777,1779, - 1778,1780, 0, 0,1451,1452,1781,1783,1782,1784, 0, 0,1504,1505,1785,1788, - 1786,1789,1787,1790, 0,1459, 0,1791, 0,1792, 0,1793,1509,1510,1794,1798, - 1795,1799,1796,1800,1462,1463,1808,1812,1809,1813,1810,1814,1467, 21,1475, 22, - 1479, 23,1485, 24,1493, 27,1499, 28,1507, 29, 0, 0,1704,1708,1709,1710, - 1711,1712,1713,1714,1718,1722,1723,1724,1725,1726,1727,1728,1740,1744,1745,1746, - 1747,1748,1749,1750,1754,1758,1759,1760,1761,1762,1763,1764,1797,1801,1802,1803, - 1804,1805,1806,1807,1811,1815,1816,1817,1818,1819,1820,1821,1470,1469,1822,1474, - 1465, 0,1473,1825,1429,1428,1426, 12,1432, 0, 26, 0, 0,1315,1823,1484, - 1466, 0,1483,1829,1433, 13,1437, 14,1441,1826,1827,1828,1488,1487,1513, 19, - 0, 0,1492,1515,1445,1444,1442, 15, 0,1831,1832,1833,1502,1501,1516, 25, - 1497,1498,1506,1518,1457,1456,1454, 17,1453,1313, 11, 3, 0, 0,1824,1512, - 1519, 0,1511,1830,1449, 16,1460, 18,1464, 4, 0, 0, 30, 31, 0, 0, + 0,1586, 0,1605, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1602,1603, + 1934,1935,1574,1575,1576,1577,1579,1580,1581,1583,1584, 0,1585,1587,1588,1589, + 1591, 0,1592, 0,1593,1594, 0,1595,1596, 0,1598,1599,1600,1601,1604,1582, + 1578,1590,1597, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1936, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1937, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1938, 0, + 1939, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1940, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 20, 0, 0, 0, 2, 6, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0,1834,1835, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1836, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0,1837,1839,1838, 0, 0, 0, 0, - 1840, 0, 0, 0, 0,1841, 0, 0,1842, 0, 0, 0, 0, 0, 0, 0, - 1843, 0,1844, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1845, 0, 0, - 1846, 0, 0,1847, 0,1848, 0, 0, 0, 0, 0, 0, 937, 0,1850, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0,1849, 936, 938,1851,1852, 0, 0, - 1853,1854, 0, 0,1855,1856, 0, 0, 0, 0, 0, 0,1857,1858, 0, 0, - 1861,1862, 0, 0,1863,1864, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0,1867,1868,1869,1870,1859,1860,1865,1866, - 0, 0, 0, 0, 0, 0,1871,1872,1873,1874, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 32, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0,1875, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0,1877, 0,1878, 0,1879, 0,1880, 0, - 1881, 0,1882, 0,1883, 0,1884, 0,1885, 0,1886, 0,1887, 0,1888, 0, - 0,1889, 0,1890, 0,1891, 0, 0, 0, 0, 0, 0,1892,1893, 0,1894, - 1895, 0,1896,1897, 0,1898,1899, 0,1900,1901, 0, 0, 0, 0, 0, 0, - 1876, 0, 0, 0, 0, 0, 0, 0, 0, 0,1902, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0,1904, 0,1905, 0,1906, 0,1907, 0, - 1908, 0,1909, 0,1910, 0,1911, 0,1912, 0,1913, 0,1914, 0,1915, 0, - 0,1916, 0,1917, 0,1918, 0, 0, 0, 0, 0, 0,1919,1920, 0,1921, - 1922, 0,1923,1924, 0,1925,1926, 0,1927,1928, 0, 0, 0, 0, 0, 0, - 1903, 0, 0,1929,1930,1931,1932, 0, 0, 0,1933, 0, 710, 385, 724, 715, - 455, 103, 186, 825, 825, 242, 751, 205, 241, 336, 524, 601, 663, 676, 688, 738, - 411, 434, 474, 500, 649, 746, 799, 108, 180, 416, 482, 662, 810, 275, 462, 658, - 692, 344, 618, 679, 293, 388, 440, 492, 740, 116, 146, 168, 368, 414, 481, 527, - 606, 660, 665, 722, 781, 803, 809, 538, 553, 588, 642, 758, 811, 701, 233, 299, - 573, 612, 487, 540, 714, 779, 232, 267, 412, 445, 457, 585, 594, 766, 167, 613, - 149, 148, 560, 589, 648, 768, 708, 345, 411, 704, 105, 259, 313, 496, 518, 174, - 542, 120, 307, 101, 430, 372, 584, 183, 228, 529, 650, 697, 424, 732, 428, 349, - 632, 355, 517, 110, 135, 147, 403, 580, 624, 700, 750, 170, 193, 245, 297, 374, - 463, 543, 763, 801, 812, 815, 162, 384, 420, 730, 287, 330, 337, 366, 459, 476, - 509, 558, 591, 610, 726, 652, 734, 759, 154, 163, 198, 473, 683, 697, 292, 311, - 353, 423, 572, 494, 113, 217, 259, 280, 314, 499, 506, 603, 608, 752, 778, 782, - 788, 117, 557, 748, 774, 320, 109, 126, 260, 265, 373, 411, 479, 523, 655, 737, - 823, 380, 765, 161, 395, 398, 438, 451, 502, 516, 537, 583, 791, 136, 340, 769, - 122, 273, 446, 727, 305, 322, 400, 496, 771, 155, 190, 269, 377, 391, 406, 432, - 501, 519, 599, 684, 687, 749, 776, 175, 452, 191, 480, 510, 659, 772, 805, 813, - 397, 444, 619, 566, 568, 575, 491, 471, 707, 111, 636, 156, 153, 288, 346, 578, - 256, 435, 383, 729, 680, 767, 694, 295, 128, 210, 0, 0, 227, 0, 379, 0, - 0, 150, 493, 525, 544, 551, 552, 556, 783, 576, 604, 0, 661, 0, 703, 0, - 0, 735, 743, 0, 0, 0, 793, 794, 795, 808, 741, 773, 118, 127, 130, 166, - 169, 177, 207, 213, 215, 226, 229, 268, 270, 317, 327, 329, 335, 369, 375, 381, - 404, 441, 448, 458, 477, 484, 503, 539, 545, 547, 546, 548, 549, 550, 554, 555, - 561, 564, 569, 591, 593, 595, 598, 607, 620, 625, 625, 651, 690, 695, 705, 706, - 716, 717, 733, 735, 777, 786, 790, 315, 869, 623, 0, 0, 102, 145, 134, 115, - 129, 138, 165, 171, 207, 202, 206, 212, 227, 231, 240, 243, 250, 254, 294, 296, - 303, 308, 319, 325, 321, 329, 326, 335, 341, 357, 360, 362, 370, 379, 388, 389, - 393, 421, 424, 438, 456, 454, 458, 465, 477, 535, 485, 490, 493, 507, 512, 514, - 521, 522, 525, 526, 528, 533, 532, 541, 565, 569, 574, 586, 591, 597, 607, 637, - 647, 674, 691, 693, 695, 698, 703, 699, 705, 704, 702, 706, 709, 717, 728, 736, - 747, 754, 770, 777, 783, 784, 786, 787, 790, 802, 825, 848, 847, 857, 55, 65, - 66, 883, 892, 916, 822, 824, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0,1586, 0,1605, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0,1602,1603,1934,1935,1574,1575,1576,1577,1579,1580, - 1581,1583,1584, 0,1585,1587,1588,1589,1591, 0,1592, 0,1593,1594, 0,1595, - 1596, 0,1598,1599,1600,1601,1604,1582,1578,1590,1597, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0,1936, 0,1937, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,1938, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1939,1940, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,1941,1942, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,1944,1943, 0,1945, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0,1946,1947, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0,1948, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1949,1950,1951,1952,1953,1954, - 1955, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,1956,1957,1958,1960,1959,1961, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 104, 107, 826, - 114, 118, 119, 121, 123, 124, 127, 125, 34, 830, 130, 131, 132, 137, 827, 35, - 133, 139, 829, 142, 143, 112, 144, 145, 924, 151, 152, 37, 157, 158, 159, 160, - 38, 165, 166, 169, 171, 172, 173, 174, 176, 177, 178, 179, 181, 182, 182, 182, - 833, 468, 184, 185, 834, 187, 188, 189, 196, 192, 194, 195, 197, 199, 200, 201, - 203, 204, 204, 206, 208, 209, 211, 218, 213, 219, 214, 216, 153, 234, 221, 222, - 223, 220, 225, 224, 230, 835, 235, 236, 237, 238, 239, 244, 836, 837, 247, 248, - 249, 246, 251, 39, 40, 253, 255, 255, 838, 257, 258, 259, 261, 839, 262, 263, - 301, 264, 41, 266, 270, 272, 271, 841, 274, 842, 277, 276, 278, 281, 282, 42, - 283, 284, 285, 286, 43, 843, 44, 289, 290, 291, 293, 934, 298, 845, 845, 621, - 300, 300, 45, 852, 894, 302, 304, 46, 306, 309, 310, 312, 316, 48, 47, 317, - 846, 318, 323, 324, 325, 324, 328, 329, 333, 331, 332, 334, 335, 336, 338, 339, - 342, 343, 347, 351, 849, 350, 348, 352, 354, 359, 850, 361, 358, 356, 49, 363, - 365, 367, 364, 50, 369, 371, 851, 376, 386, 378, 53, 381, 52, 51, 140, 141, - 387, 382, 614, 78, 388, 389, 390, 394, 392, 856, 54, 399, 396, 402, 404, 858, - 405, 401, 407, 55, 408, 409, 410, 413, 859, 415, 56, 417, 860, 418, 57, 419, - 422, 424, 425, 861, 840, 862, 426, 863, 429, 431, 427, 433, 437, 441, 438, 439, - 442, 443, 864, 436, 449, 450, 58, 454, 453, 865, 447, 460, 866, 867, 461, 466, - 465, 464, 59, 467, 470, 469, 472, 828, 475, 868, 478, 870, 483, 485, 486, 871, - 488, 489, 872, 873, 495, 497, 60, 498, 61, 61, 504, 505, 507, 508, 511, 62, - 513, 874, 515, 875, 518, 844, 520, 876, 877, 878, 63, 64, 528, 880, 879, 881, - 882, 530, 531, 531, 533, 66, 534, 67, 68, 884, 536, 538, 541, 69, 885, 549, - 886, 887, 556, 559, 70, 561, 562, 563, 888, 889, 889, 567, 71, 890, 570, 571, - 72, 891, 577, 73, 581, 579, 582, 893, 587, 74, 590, 592, 596, 75, 895, 896, - 76, 897, 600, 898, 602, 605, 607, 899, 900, 609, 901, 611, 853, 77, 615, 616, - 79, 617, 252, 902, 903, 854, 855, 621, 622, 731, 80, 627, 626, 628, 164, 629, - 630, 631, 633, 904, 632, 634, 639, 640, 635, 641, 646, 651, 638, 643, 644, 645, - 905, 907, 906, 81, 653, 654, 656, 911, 657, 908, 82, 83, 909, 910, 84, 664, - 665, 666, 667, 669, 668, 671, 670, 674, 672, 673, 675, 85, 677, 678, 86, 681, - 682, 912, 685, 686, 87, 689, 36, 913, 914, 88, 89, 696, 702, 709, 711, 915, - 712, 713, 718, 719, 917, 831, 721, 720, 723, 832, 725, 728, 918, 919, 739, 742, - 744, 920, 745, 753, 756, 757, 755, 760, 761, 921, 762, 90, 764, 922, 91, 775, - 279, 780, 923, 925, 92, 93, 785, 926, 94, 927, 787, 787, 789, 928, 792, 95, - 796, 797, 798, 800, 96, 929, 802, 804, 806, 97, 98, 807, 930, 99, 931, 932, - 933, 814, 100, 816, 817, 818, 819, 820, 821, 935, 0, 0, + 0, 0,1941,1942, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1943, + 1944, 0, 0, 0, 0, 0, 0,1945, 0,1946, 0, 0, 0, 0, 0, 0, + 0, 0,1947, 0, 0,1948, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1950, 0,1949,1951, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1953, + 1952, 0,1954, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1955,1956, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1957, 0, 0, 0, + 0, 0, 0, 0, 0,1958,1961,1959,1965,1960,1962,1964,1963, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1967,1966,1968, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1969,1970,1971,1972,1973,1974,1975, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1976, + 1977,1978,1980,1979,1981, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 106, 104, 107, 826, 114, 118, 119, 121, 123, 124, 127, 125, + 34, 830, 130, 131, 132, 137, 827, 35, 133, 139, 829, 142, 143, 112, 144, 145, + 924, 151, 152, 37, 157, 158, 159, 160, 38, 165, 166, 169, 171, 172, 173, 174, + 176, 177, 178, 179, 181, 182, 182, 182, 833, 468, 184, 185, 834, 187, 188, 189, + 196, 192, 194, 195, 197, 199, 200, 201, 203, 204, 204, 206, 208, 209, 211, 218, + 213, 219, 214, 216, 153, 234, 221, 222, 223, 220, 225, 224, 230, 835, 235, 236, + 237, 238, 239, 244, 836, 837, 247, 248, 249, 246, 251, 39, 40, 253, 255, 255, + 838, 257, 258, 259, 261, 839, 262, 263, 301, 264, 41, 266, 270, 272, 271, 841, + 274, 842, 277, 276, 278, 281, 282, 42, 283, 284, 285, 286, 43, 843, 44, 289, + 290, 291, 293, 934, 298, 845, 845, 621, 300, 300, 45, 852, 894, 302, 304, 46, + 306, 309, 310, 312, 316, 48, 47, 317, 846, 318, 323, 324, 325, 324, 328, 329, + 333, 331, 332, 334, 335, 336, 338, 339, 342, 343, 347, 351, 849, 350, 348, 352, + 354, 359, 850, 361, 358, 356, 49, 363, 365, 367, 364, 50, 369, 371, 851, 376, + 386, 378, 53, 381, 52, 51, 140, 141, 387, 382, 614, 78, 388, 389, 390, 394, + 392, 856, 54, 399, 396, 402, 404, 858, 405, 401, 407, 55, 408, 409, 410, 413, + 859, 415, 56, 417, 860, 418, 57, 419, 422, 424, 425, 861, 840, 862, 426, 863, + 429, 431, 427, 433, 437, 441, 438, 439, 442, 443, 864, 436, 449, 450, 58, 454, + 453, 865, 447, 460, 866, 867, 461, 466, 465, 464, 59, 467, 470, 469, 472, 828, + 475, 868, 478, 870, 483, 485, 486, 871, 488, 489, 872, 873, 495, 497, 60, 498, + 61, 61, 504, 505, 507, 508, 511, 62, 513, 874, 515, 875, 518, 844, 520, 876, + 877, 878, 63, 64, 528, 880, 879, 881, 882, 530, 531, 531, 533, 66, 534, 67, + 68, 884, 536, 538, 541, 69, 885, 549, 886, 887, 556, 559, 70, 561, 562, 563, + 888, 889, 889, 567, 71, 890, 570, 571, 72, 891, 577, 73, 581, 579, 582, 893, + 587, 74, 590, 592, 596, 75, 895, 896, 76, 897, 600, 898, 602, 605, 607, 899, + 900, 609, 901, 611, 853, 77, 615, 616, 79, 617, 252, 902, 903, 854, 855, 621, + 622, 731, 80, 627, 626, 628, 164, 629, 630, 631, 633, 904, 632, 634, 639, 640, + 635, 641, 646, 651, 638, 643, 644, 645, 905, 907, 906, 81, 653, 654, 656, 911, + 657, 908, 82, 83, 909, 910, 84, 664, 665, 666, 667, 669, 668, 671, 670, 674, + 672, 673, 675, 85, 677, 678, 86, 681, 682, 912, 685, 686, 87, 689, 36, 913, + 914, 88, 89, 696, 702, 709, 711, 915, 712, 713, 718, 719, 917, 831, 721, 720, + 723, 832, 725, 728, 918, 919, 739, 742, 744, 920, 745, 753, 756, 757, 755, 760, + 761, 921, 762, 90, 764, 922, 91, 775, 279, 780, 923, 925, 92, 93, 785, 926, + 94, 927, 787, 787, 789, 928, 792, 95, 796, 797, 798, 800, 96, 929, 802, 804, + 806, 97, 98, 807, 930, 99, 931, 932, 933, 814, 100, 816, 817, 818, 819, 820, + 821, 935, 0, 0, }; static const int16_t _hb_ucd_i16[92] = @@ -4403,12 +4615,12 @@ _hb_ucd_i16[92] = static inline uint_fast8_t _hb_ucd_gc (unsigned u) { - return u<1114110u?_hb_ucd_u8[6808+(((_hb_ucd_u8[1312+(((_hb_ucd_u16[((_hb_ucd_u8[544+(((_hb_ucd_u8[u>>1>>3>>3>>4])<<4)+((u>>1>>3>>3)&15u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:2; + return u<1114110u?_hb_ucd_u8[6472+(((_hb_ucd_u8[816+(((_hb_ucd_u16[((_hb_ucd_u8[272+(((_hb_ucd_u8[u>>1>>3>>4>>4])<<4)+((u>>1>>3>>4)&15u))])<<4)+((u>>1>>3)&15u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:2; } static inline uint_fast8_t _hb_ucd_ccc (unsigned u) { - return u<125259u?_hb_ucd_u8[8800+(((_hb_ucd_u8[8244+(((_hb_ucd_u8[7784+(((_hb_ucd_u8[7432+(((_hb_ucd_u8[7186+(u>>2>>2>>2>>3)])<<3)+((u>>2>>2>>2)&7u))])<<2)+((u>>2>>2)&3u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:0; + return u<125259u?_hb_ucd_u8[8504+(((_hb_ucd_u8[7936+(((_hb_ucd_u8[7460+(((_hb_ucd_u8[7100+(((_hb_ucd_u8[6854+(u>>2>>2>>2>>3)])<<3)+((u>>2>>2>>2)&7u))])<<2)+((u>>2>>2)&3u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:0; } static inline unsigned _hb_ucd_b4 (const uint8_t* a, unsigned i) @@ -4418,55 +4630,55 @@ _hb_ucd_b4 (const uint8_t* a, unsigned i) static inline int_fast16_t _hb_ucd_bmg (unsigned u) { - return u<65380u?_hb_ucd_i16[((_hb_ucd_u8[9692+(((_hb_ucd_u8[9460+(((_hb_ucd_u8[9364+(((_hb_ucd_b4(9300+_hb_ucd_u8,u>>1>>2>>3>>3))<<3)+((u>>1>>2>>3)&7u))])<<3)+((u>>1>>2)&7u))])<<2)+((u>>1)&3u))])<<1)+((u)&1u)]:0; + return u<65380u?_hb_ucd_i16[((_hb_ucd_u8[9396+(((_hb_ucd_u8[9164+(((_hb_ucd_u8[9068+(((_hb_ucd_b4(9004+_hb_ucd_u8,u>>1>>2>>3>>3))<<3)+((u>>1>>2>>3)&7u))])<<3)+((u>>1>>2)&7u))])<<2)+((u>>1)&3u))])<<1)+((u)&1u)]:0; } static inline uint_fast8_t _hb_ucd_sc (unsigned u) { - return u<918000u?_hb_ucd_u8[11126+(((_hb_ucd_u16[4040+(((_hb_ucd_u16[2048+(((_hb_ucd_u8[10390+(((_hb_ucd_u8[9940+(u>>2>>2>>3>>4)])<<4)+((u>>2>>2>>3)&15u))])<<3)+((u>>2>>2)&7u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:2; + return u<918000u?_hb_ucd_u8[10398+(((_hb_ucd_u16[3952+(((_hb_ucd_u16[2624+(((_hb_ucd_u8[9870+(((_hb_ucd_u8[9644+(u>>3>>2>>3>>4)])<<4)+((u>>3>>2>>3)&15u))])<<3)+((u>>3>>2)&7u))])<<2)+((u>>3)&3u))])<<3)+((u)&7u))]:2; } static inline uint_fast16_t _hb_ucd_dm (unsigned u) { - return u<195102u?_hb_ucd_u16[6748+(((_hb_ucd_u8[13952+(((_hb_ucd_u8[13570+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:0; + return u<195102u?_hb_ucd_u16[6244+(((_hb_ucd_u8[16628+(((_hb_ucd_u8[16246+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:0; } #else static const uint8_t -_hb_ucd_u8[13386] = +_hb_ucd_u8[13730] = { 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 9, 10, 7, 7, 7, 7, 7, 11, 12, 12, 12, 13, - 14, 15, 16, 17, 18, 19, 20, 21, 22, 21, 21, 21, 21, 23, 7, 7, - 7, 24, 21, 21, 21, 25, 26, 27, 21, 28, 29, 30, 31, 32, 33, 34, + 14, 15, 16, 17, 18, 19, 20, 7, 21, 22, 22, 22, 23, 24, 7, 7, + 7, 25, 22, 22, 22, 26, 27, 28, 22, 29, 30, 31, 32, 33, 34, 35, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 35, 21, 36, - 7, 7, 7, 7, 37, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 38, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 21, 22, 36, + 7, 7, 7, 7, 37, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 38, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, @@ -4486,30 +4698,30 @@ _hb_ucd_u8[13386] = 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, 100,100, 34, 34, 34, 34,101,102, 34, 34,103,104,105,106,107,108, 34, 34,109,110,111,112,113,114,115,116,117,118, 34, 34, 34,119, - 120,121,122,123,124,125,126,127, 34,128,129,111,130,131,132,133, - 134,135,136,137,138,139,140,111,141,142,111,143,144,145,146,111, - 147,148,149,150,151,152,153,111,154,155,156,157,111,158,159,160, - 34, 34, 34, 34, 34, 34, 34, 34,161, 34, 34,111,111,111,111,111, - 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,162, - 34, 34, 34, 34, 34, 34, 34, 34,163,111,111,111,111,111,111,111, + 120,121,122,123,124,125,126,127, 34,128,129,130,131,132,133,134, + 135,136,137,138,139,140,141,142,143,144,111,145,146,147,148,111, + 149,150,151,152,153,154,155,156,157,158,159,160,111,161,162,163, + 34, 34, 34, 34, 34, 34, 34, 34,164, 34, 34,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,165, + 34, 34, 34, 34, 34, 34, 34, 34,166, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,111,111,111, 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111, - 111,111,111,111,111,111,111,111, 34, 34, 34, 34, 34,111,111,111, - 34, 34, 34, 34,164,165,166, 34,111,111,111,111,167,168,169,170, + 111,111,167,111,111,111,111,111,111,111,111,111,111,111,111,111, + 34, 34, 34, 34,168,169,170, 34,111,111,171,111,172,173,174,175, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,111,111,111,111,111, 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,119, 34, 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111,111,111, - 111,111,111,111,111,111,111,111, 34,171,111,111,111,111,111,111, - 111,111,111,111,111,111,111,111,111,111,111,111,111,111,172, 67, - 67, 67,173,174,175,130, 65,111,176,177,178,179,180,181,182,183, - 67, 67, 67, 67,184,185,111,111,111,111,111,111,111,111,186,111, - 187,188,189,111,111,190,111,111,111,191,111,111,111,111,111, 34, - 34,192,193,111,111,111,111,111,130,194,195,111, 34,196,111,111, - 67, 67,197, 67, 67,111, 67,198, 67, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 67, 67, 67,199,111,111,111,111,111,111,111,111, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,111,111,111, + 111,111,111,111,111,111,111,111, 34,176,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111, 67,177, 67, 67, 67, 67,178, 67, + 67, 67,179,180,181,131, 65,111,182,183,184,185,186,187,188,189, + 67, 67, 67, 67,190,191,111,111,111,111,111,111,111,111,192,111, + 193,194,195,111,111,196,111,111,111,197,111,198,111,111,111, 34, + 34,199,200,111,111,111,111,111,131,201,202,111, 34,203,111,111, + 67, 67,204, 67, 67,111, 67,205, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67,177,111,111,111,111,111,111,111,111, 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111,111,111,111, 34, 34, 34, 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111, - 200,111,188,188,111,111,111,111,111,111,111,111,111,111,111,111, + 206,111,194,194,111,111,111,111,111,111,111,111,111,111,111,111, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 4, 5, 6, 2, 7, 7, 7, 7, 7, 2, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, @@ -4544,8 +4756,8 @@ _hb_ucd_u8[13386] = 36, 36, 36, 36, 36, 64, 43, 43, 43, 43, 40, 21, 2, 40, 69, 20, 36, 36, 36, 43, 43, 69, 43, 43, 43, 43, 69, 43, 69, 43, 43, 43, 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 36, 36, 64, 43, 43, 2, - 36, 36, 36, 36, 74, 36, 36, 36, 59, 59, 59, 59, 43, 43, 43, 43, - 36, 36, 36, 36, 75, 43, 43, 43, 43, 76, 43, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 74, 36, 36, 36, 59, 59, 59, 75, 43, 43, 43, 43, + 36, 36, 36, 36, 76, 43, 43, 43, 43, 75, 43, 43, 43, 43, 43, 43, 43, 77, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 77, 65, 78, 79, 43, 43, 43, 77, 78, 79, 78, 64, 43, 43, 43, 36, 36, 36, 36, 36, 43, 2, 7, 7, 7, 7, 7, 80, 36, 36, 36, 36, 36, 36, 36, @@ -4590,130 +4802,135 @@ _hb_ucd_u8[13386] = 36, 43, 77, 78, 78, 78, 78, 81, 36, 43, 97, 2, 2, 2, 2, 2, 36, 43, 43, 43, 43, 43, 43, 43, 36, 36, 43, 79, 43, 43, 43, 78, 78, 78, 78, 77, 79, 43, 43, 43, 43, 43, 2, 80, 2, 60, 64, 43, - 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 98, 2, 56, 43, 76, - 36, 75, 36, 36, 36, 36, 36, 36, 36, 36, 64, 65, 36, 36, 36, 36, + 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 98, 2, 56, 43, 75, + 36, 76, 36, 36, 36, 36, 36, 36, 36, 36, 64, 65, 36, 36, 36, 36, 36, 36, 36, 36, 64, 36, 36, 36, 43, 77, 78, 79, 77, 78, 78, 78, 78, 77, 78, 78, 79, 43, 43, 43, 61, 61, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 27, 27, 61, 36, 36, 36, 64, 77, 79, 43, 2, 36, 36, 82, 77, 43, 43, 43, 43, 77, 77, 79, 43, 43, 43, 77, 78, 78, 79, 43, 43, 43, 43, 43, 43, 2, 2, 2, 80, 2, 2, 2, 2, 43, 43, 43, 43, 43, 43, 43, 99, 43, 43, 81, 36, 36, 36, 36, 36, - 36, 36, 77, 43, 43, 77, 77, 78, 78, 77, 81, 36, 36, 36, 36, 36, + 36, 36, 77, 43, 43, 77, 77, 78, 78, 77, 81, 36, 36, 36, 36, 2, 89, 61, 61, 61, 61, 47, 43, 43, 43, 43, 61, 61, 61, 61, 21, 2, 43, 81, 36, 36, 36, 36, 36, 36, 82, 43, 43, 78, 43, 79, 43, 36, 36, 36, 36, 77, 43, 78, 79, 79, 43, 78, 78, 78, 78, 78, 2, 2, 36, 36, 78, 78, 78, 78, 43, 43, 43, 43, 78, 43, 43, 57, 2, 2, 7, 7, 7, 7, 7, 7, 86, 36, 36, 36, 36, 36, 40, 40, 40, 2, - 43, 57, 43, 43, 43, 43, 43, 43, 77, 43, 43, 43, 65, 36, 64, 36, - 36, 36, 65, 82, 43, 36, 36, 36, 16, 16, 16, 16, 16, 16, 40, 40, - 40, 40, 40, 40, 40, 44, 16, 16, 16, 16, 16, 16, 44, 16, 16, 16, - 16, 16, 16, 16, 16,100, 40, 40, 32, 32, 32, 16, 16, 16, 16, 32, - 16, 16, 16, 16, 11, 11, 11, 11, 16, 16, 16, 16, 34, 11, 11, 11, - 16, 16, 16, 16,101,101,101,101, 16, 16, 16, 16, 11, 11,102,103, - 41, 16, 16, 16, 11, 11,102, 41, 16, 16, 16, 16, 11, 11,104, 41, - 105,105,105,105,105,106, 59, 59, 51, 51, 51, 2,107,108,107,108, - 2, 2, 2, 2,109, 59, 59,110, 2, 2, 2, 2,111,112, 2,113, - 114, 2,115,116, 2, 2, 2, 2, 2, 9,114, 2, 2, 2, 2,117, - 59, 59, 59, 59, 59, 59, 59, 59,118, 40, 27, 27, 27, 8,115,119, - 27, 27, 27, 27, 27, 8,115, 94, 20, 20, 20, 20, 20, 20, 20, 20, - 43, 43, 43, 43, 43, 43,120, 48, 99, 48, 99, 43, 43, 43, 43, 43, - 61,121, 61,122, 61, 34, 11, 16, 11, 32,122, 61, 46, 11, 11, 61, - 61, 61,121,121,121, 11, 11,123, 11, 11, 35, 36, 39, 61, 16, 11, - 8, 8, 46, 16, 16, 26, 61,124, 95, 95, 95, 95, 95, 95, 95, 95, - 95,125,126, 95,127, 61, 61, 61, 8, 8,128, 61, 61, 8, 61, 61, - 128, 26, 61,128, 61, 61, 61,128, 61, 61, 61, 61, 61, 61, 61, 8, - 61,128,128, 61, 61, 61, 61, 61, 61, 61, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 61, 61, 61, 61, 4, 4, 61, 61, - 8, 61, 61, 61,129,130, 61, 61, 61, 61, 61, 61, 61, 61,128, 61, - 61, 61, 61, 61, 61, 26, 8, 8, 8, 8, 61, 61, 61, 61, 61, 61, - 61, 61, 61, 61, 61, 61, 8, 8, 8, 61, 61, 61, 61, 61, 61, 61, - 27, 27, 27, 27, 27, 27, 61, 61, 61, 61, 61, 61, 61, 27, 27, 27, - 61, 61, 61, 26, 61, 61, 61, 61, 26, 61, 61, 61, 61, 61, 61, 61, - 61, 61, 61, 61, 8, 8, 8, 8, 61, 61, 61, 61, 61, 61, 61, 26, - 61, 61, 61, 61, 4, 4, 4, 4, 4, 4, 4, 27, 27, 27, 27, 27, - 27, 27, 61, 61, 61, 61, 61, 61, 8, 8,115,131, 8, 8, 8, 8, - 8, 8, 8, 4, 4, 4, 4, 4, 8,115,132,132,132,132,132,132, - 132,132,132,132,131, 8, 8, 8, 8, 8, 8, 8, 4, 4, 8, 8, - 8, 8, 8, 8, 8, 8, 4, 8, 8, 8,128, 26, 8, 8,128, 61, - 32, 11, 32, 34, 34, 34, 34, 11, 32, 32, 34, 16, 16, 16, 40, 11, - 32, 32,124, 61, 61,122, 34,133, 43, 32, 16, 16, 50, 2, 90, 2, - 36, 36, 36, 36, 36, 36, 36, 75, 2, 2, 2, 2, 2, 2, 2, 56, - 2,107,107, 2,111,112,107, 2, 2, 2, 2, 6, 2, 98,107, 2, - 107, 4, 4, 4, 4, 2, 2, 80, 2, 2, 2, 2, 2, 51, 2, 2, - 98,134, 2, 2, 2, 2, 2, 2, 61, 2,135,132,132,132,136, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 1, 2,137,138, 4, 4, 4, 4, - 4, 61, 4, 4, 4, 4,139, 94,140, 95, 95, 95, 95, 43, 43, 78, - 141, 40, 40, 61, 95,142, 58, 61, 72, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 64,143,144, 63, 36, 36, 36, 36, 36, 58, 40, 63, - 61, 27, 27, 61, 61, 61, 61, 61, 27, 27, 27, 27, 27, 61, 61, 61, - 61, 61, 61, 61, 27, 27, 27, 27,145, 27, 27, 27, 27, 27, 27, 27, - 36, 36, 75, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,146, 2, - 32, 32, 32, 32, 32, 32, 32, 64, 48,147, 43, 43, 43, 43, 43, 80, - 32, 32, 32, 32, 32, 32, 40, 43, 36, 36, 36, 95, 95, 95, 95, 95, - 43, 2, 2, 2, 2, 2, 2, 2, 41, 41, 41,144, 40, 40, 40, 40, - 41, 32, 32, 32, 32, 32, 32, 32, 16, 32, 32, 32, 32, 32, 32, 32, - 44, 16, 16, 16, 34, 34, 34, 32, 32, 32, 32, 32, 42,148, 34, 35, - 32, 32, 16, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 11, 11, 32, - 11, 11, 32, 32, 32, 32, 32, 32, 32, 32, 11, 11, 34, 16, 16, 16, - 32, 16, 16, 32, 32, 16, 16, 16, 16, 40,149, 35, 40, 35, 36, 36, - 36, 65, 36, 65, 36, 64, 36, 36, 36, 82, 79, 77, 61, 61, 43, 43, - 27, 27, 27, 61,150, 61, 61, 61, 36, 36, 2, 2, 2, 2, 2, 2, - 78, 36, 36, 36, 36, 36, 36, 36, 36, 36, 78, 78, 78, 78, 78, 78, - 78, 78, 43, 43, 43, 43, 43, 2, 43, 36, 36, 36, 2, 66, 66, 64, - 36, 36, 36, 43, 43, 43, 43, 2, 36, 36, 36, 64, 43, 43, 43, 43, - 43, 78, 78, 78, 78, 78, 78, 97, 36, 64, 78, 43, 43, 78, 43, 78, - 97, 2, 2, 2, 2, 2, 2, 80, 7, 7, 7, 7, 7, 7, 7, 2, - 36, 36, 64, 63, 36, 36, 36, 36, 36, 36, 36, 36, 64, 43, 43, 77, - 79, 77, 79, 43, 43, 43, 43, 43, 36, 64, 36, 36, 36, 36, 77, 78, - 7, 7, 7, 7, 7, 7, 2, 2, 63, 36, 36, 71, 61, 82, 77, 36, - 65, 43, 65, 64, 65, 36, 36, 43, 36, 36, 36, 36, 36, 36, 75, 2, - 36, 36, 36, 36, 36, 82, 43, 78, 2, 75,151, 43, 43, 43, 43, 43, - 16, 16, 16, 16, 16,103, 40, 40, 16, 16, 16, 16,100, 41, 41, 41, - 36, 82, 79, 78, 77, 97, 79, 43,152,152,152,152,152,152,152,152, - 153,153,153,153,153,153,153,153, 16, 16, 16, 16, 16, 16, 35, 65, - 36, 36, 36, 36,154, 36, 36, 36, 36, 41, 41, 41, 41, 41, 41, 41, - 41, 74, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,132, - 36, 36, 36, 36, 36, 36, 36, 71, 36, 36, 36, 36, 36, 36,150, 61, - 2, 2, 2,135,116, 2, 2, 2, 6,155,156,132,132,132,132,132, - 132,132,116,135,116, 2,113,157, 2, 2, 2, 2,139,132,132,116, - 2,158, 8, 8, 60, 2, 2, 2, 36, 36, 36, 36, 36, 36, 36,159, - 2, 2, 3, 2, 4, 5, 6, 2, 16, 16, 16, 16, 16, 17, 18,115, - 116, 4, 2, 36, 36, 36, 36, 36, 63, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 40, 20,160, 53, 20, 26, 8,128, 61, - 61, 61, 61, 61,161, 59, 61, 61, 2, 2, 2, 90, 27, 27, 27, 27, - 27, 27, 27, 84, 61, 61, 61, 61, 95, 95,127, 27, 84, 61, 61, 61, - 61, 61, 61, 61, 61, 27, 61, 61, 61, 61, 61, 61, 61, 61, 47, 43, - 162,162,162,162,162,162,162,162,163, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 87, 36,138, 36, 36, 36, 36, 95, 95, 95, - 36, 36, 36, 36, 36, 36, 36, 58,164, 95, 95, 95, 95, 95, 95, 95, - 11, 11, 11, 32, 16, 16, 16, 16, 36, 36, 36, 58, 27, 27, 27, 27, - 36, 36, 36, 71,145, 27, 27, 27, 36, 36, 36,165, 27, 27, 27, 27, - 36, 36, 36, 36, 36,165, 27, 27, 36, 36, 36, 27, 27, 27, 27, 30, - 36, 36, 36, 36, 36, 36, 27, 36, 64, 43, 43, 43, 43, 43, 43, 43, - 36, 36, 36, 36, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36,165, 30, - 36, 36, 36, 36, 36, 36,165, 27, 36, 36, 36, 36, 72, 36, 36, 36, - 36, 36, 64, 43, 43,163, 27, 27, 36, 36, 36, 36, 58, 2, 2, 2, - 36, 36, 36, 36, 27, 27, 27, 27, 16, 16, 16, 16, 16, 27, 27, 27, - 36, 36, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 64,166, 51, - 27, 27, 27, 87, 36, 36, 36, 36,163, 27, 30, 2, 2, 2, 2, 2, - 36, 43, 43, 2, 2, 2, 2, 2, 36, 36,165, 27, 27, 27, 27, 27, - 79, 81, 36, 36, 36, 36, 36, 36, 43, 43, 43, 57, 2, 2, 2, 2, - 2, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 7, 7, 7, 7, 7, - 65, 64, 65, 36, 36, 36, 36, 64, 78, 79, 43, 77, 79, 57, 73, 2, - 2, 43, 43, 43, 43, 43, 67, 59, 36, 36, 36, 64, 43, 43, 79, 43, - 43, 43, 43, 7, 7, 7, 7, 7, 2, 2, 82, 81, 36, 36, 36, 36, - 36, 64, 2, 36, 36, 36, 36, 36, 36, 82, 78, 43, 43, 43, 43, 77, - 81, 36, 58, 2, 56, 43, 57, 79, 7, 7, 7, 7, 7, 58, 58, 2, - 90, 27, 27, 27, 27, 27, 27, 27, 36, 36, 36, 36, 36, 36, 78, 79, - 43, 78, 77, 43, 2, 2, 2, 65, 36, 36, 36, 36, 36, 36, 36, 64, - 77, 78, 78, 78, 78, 78, 78, 78, 36, 36, 36, 82, 78, 78, 81, 36, - 36, 78, 78, 43, 43, 43, 43, 43, 36, 36, 82, 78, 43, 43, 43, 43, - 78, 43, 77, 65, 36, 58, 2, 2, 7, 7, 7, 7, 7, 2, 2, 65, - 78, 79, 43, 43, 77, 77, 78, 79, 77, 43, 36, 66, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 82, 78, 43, 43, 43, 78, 78, 43, 79, - 57, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 43, 43, - 78, 79, 43, 43, 43, 77, 79, 79, 57, 2, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 64, 79, 78, 43, 43, 43, 79, 58, 2, 2, 2, + 16, 16, 16, 16, 34, 16, 16, 16, 43, 57, 43, 43, 43, 43, 43, 43, + 77, 43, 43, 43, 65, 36, 64, 36, 36, 36, 65, 82, 43, 36, 36, 36, + 16, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 44, 16, 16, + 16, 16, 16, 16, 44, 16, 16, 16, 16, 16, 16, 16, 16,100, 40, 40, + 32, 32, 32, 16, 16, 16, 16, 32, 16, 16, 16, 16, 11, 11, 11, 11, + 16, 16, 16, 16, 34, 11, 11, 11, 16, 16, 16, 16,101,101,101,101, + 16, 16, 16, 16, 11, 11,102,103, 41, 16, 16, 16, 11, 11,102, 41, + 16, 16, 16, 16, 11, 11,104, 41,105,105,105,105,105,106, 59, 59, + 51, 51, 51, 2,107,108,107,108, 2, 2, 2, 2,109, 59, 59,110, + 2, 2, 2, 2,111,112, 2,113,114, 2,115,116, 2, 2, 2, 2, + 2, 9,114, 2, 2, 2, 2,117, 59, 59, 59, 59, 59, 59, 59, 59, + 118, 40, 27, 27, 27, 8,115,119, 27, 27, 27, 27, 27, 8,115, 94, + 20, 20, 20, 20, 20, 20, 20, 20, 43, 43, 43, 43, 43, 43,120, 48, + 99, 48, 99, 43, 43, 43, 43, 43, 61,121, 61,122, 61, 34, 11, 16, + 11, 32,122, 61, 46, 11, 11, 61, 61, 61,121,121,121, 11, 11,123, + 11, 11, 35, 36, 39, 61, 16, 11, 8, 8, 46, 16, 16, 26, 61,124, + 95, 95, 95, 95, 95, 95, 95, 95, 95,125,126, 95,127, 61, 61, 61, + 8, 8,128, 61, 61, 8, 61, 61,128, 26, 61,128, 61, 61, 61,128, + 61, 61, 61, 61, 61, 61, 61, 8, 61,128,128, 61, 61, 61, 61, 61, + 61, 61, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 61, 61, 61, 61, 4, 4, 61, 61, 8, 61, 61, 61,129,130, 61, 61, + 61, 61, 61, 61, 61, 61,128, 61, 61, 61, 61, 61, 61, 26, 8, 8, + 8, 8, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 8, 8, + 8, 61, 61, 61, 61, 61, 61, 61, 27, 27, 27, 27, 27, 27, 61, 61, + 61, 61, 61, 61, 61, 27, 27, 27, 61, 61, 61, 26, 61, 61, 61, 61, + 26, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 8, 8, 8, 8, + 61, 61, 61, 61, 61, 61, 61, 26, 61, 61, 61, 61, 4, 4, 4, 4, + 4, 4, 4, 27, 27, 27, 27, 27, 27, 27, 61, 61, 61, 61, 61, 61, + 8, 8,115,131, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4, + 8,115,132,132,132,132,132,132,132,132,132,132,131, 8, 8, 8, + 8, 8, 8, 8, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8, + 8, 8,128, 26, 8, 8,128, 61, 32, 11, 32, 34, 34, 34, 34, 11, + 32, 32, 34, 16, 16, 16, 40, 11, 32, 32,124, 61, 61,122, 34,133, + 43, 32, 16, 16, 50, 2, 90, 2, 36, 36, 36, 36, 36, 36, 36, 76, + 2, 2, 2, 2, 2, 2, 2, 56, 2,107,107, 2,111,112,107, 2, + 2, 2, 2, 6, 2, 98,107, 2,107, 4, 4, 4, 4, 2, 2, 80, + 2, 2, 2, 2, 2, 51, 2, 2, 98,134, 2, 2, 2, 2, 2, 2, + 61, 2,135,132,132,132,136, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 1, 2,137,138, 4, 4, 4, 4, 4, 61, 4, 4, 4, 4,139, 94, + 140, 95, 95, 95, 95, 43, 43, 78,141, 40, 40, 61, 95,142, 58, 61, + 72, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 64,143,144, 63, + 36, 36, 36, 36, 36, 58, 40, 63, 61, 27, 27, 61, 61, 61, 61, 61, + 27, 27, 27, 27, 27, 61, 61, 61, 61, 61, 61, 61, 27, 27, 27, 27, + 145, 27, 27, 27, 27, 27, 27, 27, 36, 36, 76, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36,146, 2, 32, 32, 32, 32, 32, 32, 32, 64, + 48,147, 43, 43, 43, 43, 43, 80, 32, 32, 32, 32, 32, 32, 40, 43, + 36, 36, 36, 95, 95, 95, 95, 95, 43, 2, 2, 2, 2, 2, 2, 2, + 41, 41, 41,144, 40, 40, 40, 40, 41, 32, 32, 32, 32, 32, 32, 32, + 16, 32, 32, 32, 32, 32, 32, 32, 44, 16, 16, 16, 34, 34, 34, 32, + 32, 32, 32, 32, 42,148, 34, 35, 32, 32, 16, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 11, 11, 32, 11, 11, 32, 32, 32, 32, 32, 32, + 32, 32, 11, 11, 34, 34, 32, 16, 32, 16, 16, 32, 32, 32, 11, 11, + 11, 40,149, 35, 40, 35, 36, 36, 36, 65, 36, 65, 36, 64, 36, 36, + 36, 82, 79, 77, 61, 61, 43, 43, 27, 27, 27, 61,150, 61, 61, 61, + 36, 36, 2, 2, 2, 2, 2, 2, 78, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 78, 78, 78, 78, 78, 78, 78, 78, 43, 43, 43, 43, 43, 2, + 43, 36, 36, 36, 2, 66, 66, 64, 36, 36, 36, 43, 43, 43, 43, 2, + 36, 36, 36, 64, 43, 43, 43, 43, 43, 78, 78, 78, 78, 78, 78, 97, + 36, 64, 78, 43, 43, 78, 43, 78, 97, 2, 2, 2, 2, 2, 2, 80, + 7, 7, 7, 7, 7, 7, 7, 2, 36, 36, 64, 63, 36, 36, 36, 36, + 36, 36, 36, 36, 64, 43, 43, 77, 79, 77, 79, 43, 43, 43, 43, 43, + 36, 64, 36, 36, 36, 36, 77, 78, 7, 7, 7, 7, 7, 7, 2, 2, + 63, 36, 36, 71, 61, 82, 77, 36, 65, 43, 65, 64, 65, 36, 36, 43, + 36, 36, 36, 36, 36, 36, 76, 2, 36, 36, 36, 36, 36, 82, 43, 78, + 2, 76,151, 43, 43, 43, 43, 43, 16, 16, 16, 16, 16,103, 40, 40, + 16, 16, 16, 16,100, 41, 41, 41, 36, 82, 79, 78, 77, 97, 79, 43, + 152,152,152,152,152,152,152,152,153,153,153,153,153,153,153,153, + 16, 16, 16, 16, 16, 16, 35, 65, 36, 36, 36, 36,154, 36, 36, 36, + 36, 41, 41, 41, 41, 41, 41, 41, 41, 74, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36,132, 36, 36, 36, 36, 36, 36, 36, 71, + 36, 36, 36, 36, 36, 36,150, 61, 2, 2, 2,135,116, 2, 2, 2, + 6,155,156,132,132,132,132,132,132,132,116,135,116, 2,113,157, + 2, 2, 2, 2,139,132,132,116, 2,158, 8, 8, 60, 2, 2, 2, + 36, 36, 36, 36, 36, 36, 36,159, 2, 2, 3, 2, 4, 5, 6, 2, + 16, 16, 16, 16, 16, 17, 18,115,116, 4, 2, 36, 36, 36, 36, 36, + 63, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 40, + 20,160, 53, 20, 26, 8,128, 61, 61, 61, 61, 61,161, 59, 61, 61, + 2, 2, 2, 90, 27, 27, 27, 27, 27, 27, 27, 84, 61, 61, 61, 61, + 95, 95,127, 27, 84, 61, 61, 61, 61, 61, 61, 61, 61, 27, 61, 61, + 61, 61, 61, 61, 61, 61, 47, 43,162,162,162,162,162,162,162,162, + 163, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 87, 36, + 138, 36, 36, 36, 36, 95, 95, 95, 36, 36, 36, 36, 36, 36, 36, 58, + 164, 95, 95, 95, 95, 95, 95, 95, 11, 11, 11, 32, 16, 16, 16, 16, + 36, 36, 36, 58, 27, 27, 27, 27, 36, 36, 36, 71,145, 27, 27, 27, + 36, 36, 36,165, 27, 27, 27, 27, 36, 36, 36, 36, 36,165, 27, 27, + 36, 36, 36, 27, 27, 27, 27, 30, 36, 36, 36, 36, 36, 36, 27, 36, + 64, 43, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36,165, 30, 36, 36, 36, 36, 36, 36,165, 27, + 36, 36, 36, 36, 72, 36, 36, 36, 36, 36, 64, 43, 43,163, 27, 27, + 36, 36, 36, 36, 58, 2, 2, 2, 36, 36, 36, 36, 27, 27, 27, 27, + 16, 16, 16, 16, 16, 27, 27, 27, 36, 36, 43, 43, 43, 43, 43, 43, + 7, 7, 7, 7, 7, 36, 36, 63, 11, 11, 11, 11,166, 43, 43,141, + 16, 16, 16, 16, 16, 16, 16, 8, 36, 36, 36, 36, 36, 64,167, 51, + 36, 36, 36, 36, 36, 36, 43, 43, 27, 27, 27, 87, 36, 36, 36, 36, + 163, 27, 30, 2, 2, 2, 2, 2, 36, 43, 43, 2, 2, 2, 2, 2, + 36, 36,165, 27, 27, 27, 27, 27, 79, 81, 36, 36, 36, 36, 36, 36, + 43, 43, 43, 57, 2, 2, 2, 2, 2, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 7, 7, 7, 7, 7, 65, 64, 65, 36, 36, 36, 36, 64, + 78, 79, 43, 77, 79, 57, 73, 2, 2, 43, 43, 43, 43, 43, 67, 59, + 36, 36, 36, 64, 43, 43, 79, 43, 43, 43, 43, 7, 7, 7, 7, 7, + 2, 2, 82, 81, 36, 36, 36, 36, 36, 64, 2, 36, 36, 36, 36, 36, + 36, 82, 78, 43, 43, 43, 43, 77, 81, 36, 58, 2, 56, 43, 57, 79, + 7, 7, 7, 7, 7, 58, 58, 2, 90, 27, 27, 27, 27, 27, 27, 27, + 36, 36, 36, 36, 36, 36, 78, 79, 43, 78, 77, 43, 2, 2, 2, 65, + 36, 36, 36, 36, 36, 36, 36, 64, 77, 78, 78, 78, 78, 78, 78, 78, + 36, 36, 36, 82, 78, 78, 81, 36, 36, 78, 78, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 78, 79, 43, 43, 43, 78, 78, 78, 78, 78, 78, 77, + 65, 65, 2, 2, 2, 2, 2, 2, 56, 43, 43, 43, 43, 43, 43, 43, + 36, 36, 82, 78, 43, 43, 43, 43, 78, 43, 77, 65, 36, 58, 2, 2, + 7, 7, 7, 7, 7, 2, 2, 65, 78, 79, 43, 43, 77, 77, 78, 79, + 77, 43, 36, 66, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 82, + 78, 43, 43, 43, 78, 78, 43, 79, 57, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 36, 36, 43, 43, 78, 79, 43, 43, 43, 77, 79, 79, + 57, 2, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 64, 79, 78, + 43, 43, 43, 79, 58, 2, 2, 2, 36, 36, 36, 36, 36, 36, 64, 79, 78, 43, 43, 79, 43, 43, 43, 43, 7, 7, 7, 7, 7, 27, 2, 89, 43, 43, 43, 43, 79, 57, 2, 2, 27, 27, 27, 27, 27, 27, 27, 87, 78, 78, 78, 78, 78, 79, 77, 65, 81, 79, 2, 2, 2, 2, 2, 2, @@ -4721,39 +4938,41 @@ _hb_ucd_u8[13386] = 78, 78, 78, 78, 78, 78, 78, 78, 64, 43, 43, 43, 43, 65, 36, 36, 36, 64, 43, 43, 77, 64, 43, 57, 2, 2, 2, 56, 43, 43, 43, 43, 64, 43, 43, 77, 79, 43, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, - 43, 43, 43, 77, 43, 2, 66, 2, 43, 43, 43, 43, 43, 43, 43, 79, - 58, 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 36, 36, 36, 36, 36, + 43, 43, 43, 77, 43, 2, 66, 2, 58, 2, 2, 2, 2, 2, 2, 2, + 43, 43, 43, 43, 43, 43, 43, 79, 2, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 77, 43, 43, 43, 77, 43, 79, 43, 43, 43, 43, 43, 43, 43, 43, 64, 43, 43, 43, 43, 36, 36, 36, 36, 36, 78, 78, 78, 43, 77, 79, 79, 36, 36, 36, 36, 36, 64, 77, 97, 2, 2, 2, 2, 43, 82, 36, 36, 36, 36, 36, 36, 36, 36, 78, 43, 43, 43, 43, 78, - 77, 57, 2, 2, 2, 2, 2, 2, 27, 27, 84, 61, 61, 61, 53, 20, - 150, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 21, - 65, 36, 36, 64, 43, 43, 43, 43, 43, 43, 57, 2, 2, 2, 2, 2, - 43, 43, 43, 57, 2, 2, 61, 61, 40, 40, 89, 61, 61, 61, 61, 61, - 7, 7, 7, 7, 7,167, 27, 27, 27, 87, 36, 36, 36, 36, 36, 36, - 27, 27, 27, 30, 2, 2, 2, 2, 82, 78, 78, 78, 78, 78, 78, 78, - 78, 78, 78, 78, 78, 78, 78, 79, 43, 68, 40, 40, 40, 40, 40, 40, - 40, 80, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36, 47, 57, - 61, 61,168, 79, 43, 61,168, 78, 78,169, 59, 59, 59, 76, 43, 43, - 43, 70, 47, 43, 43, 43, 61, 61, 61, 61, 61, 61, 61, 43, 43, 61, - 61, 43, 70, 61, 61, 61, 61, 61, 11, 11, 11, 11, 11, 16, 16, 16, - 16, 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 16, - 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 11, 11, - 11, 11, 11, 16, 16, 16, 16, 16, 31, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, - 11, 11, 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, - 16, 16, 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, - 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, - 16, 33, 16, 16, 16, 32, 16, 7, 43, 43, 43, 70, 61, 47, 43, 43, - 43, 43, 43, 43, 43, 43, 70, 61, 61, 61, 47, 61, 61, 61, 61, 61, - 61, 61, 70, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 56, 43, 43, - 16, 16, 16, 16, 16, 39, 16, 16, 43, 43, 43, 68, 40, 40, 40, 40, - 7, 7, 7, 7, 7, 7, 7, 71, 36, 36, 36, 36, 36, 36, 36, 43, - 36, 36, 36, 36, 36, 36, 43, 43, 7, 7, 7, 7, 7, 7, 7,170, - 36, 36, 36, 36, 36, 75, 43, 43, 16, 16, 43, 43, 43, 68, 40, 40, - 27, 27, 27, 27, 27, 27,145, 27,171, 27, 27, 27, 27, 27, 27, 27, + 77, 57, 2, 2, 2, 2, 2, 2, 7, 7, 7, 7, 7, 43, 43, 43, + 27, 27, 84, 61, 61, 61, 53, 20,150, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 21, 65, 36, 36, 64, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 78, 79, 43, + 43, 43, 57, 2, 2, 2, 2, 2, 43, 43, 43, 57, 2, 2, 61, 61, + 40, 40, 89, 61, 61, 61, 61, 61, 7, 7, 7, 7, 7,168, 27, 27, + 27, 87, 36, 36, 36, 36, 36, 36, 40, 63, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 76,146, 2, 27, 27, 27, 30, 2, 2, 2, 2, + 82, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 79, + 43, 68, 40, 40, 40, 40, 40, 40, 40, 80, 43, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 47, 57, 61, 61,169, 79, 43, 61,169, 78, + 78,170, 59, 59, 59, 75, 43, 43, 43, 70, 47, 43, 43, 43, 61, 61, + 61, 61, 61, 61, 61, 43, 43, 61, 61, 43, 70, 61, 61, 61, 61, 61, + 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 16, 11, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, + 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, + 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, + 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, + 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 32, 16, 7, + 43, 43, 43, 70, 61, 47, 43, 43, 43, 43, 43, 43, 43, 43, 70, 61, + 61, 61, 47, 61, 61, 61, 61, 61, 61, 61, 70, 21, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 56, 43, 43, 16, 16, 16, 16, 16, 39, 16, 16, + 43, 43, 43, 68, 40, 40, 40, 40, 7, 7, 7, 7, 7, 7, 7, 71, + 7, 7, 7, 7, 7, 7, 7,171, 36, 36, 36, 36, 36, 76, 43, 43, + 172, 7, 7, 7, 7, 7, 7, 85, 16, 16, 43, 43, 43, 68, 40, 40, + 27, 27, 27, 27, 27, 27,145, 27,173, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,145, 27, 27, 27, 27, 27, 27, 84, 61, 61, 61, 61, 61, 61, 25, 41, 41, 0, 0, 29, 21, 21, 21, 23, 21, 22, 18, 21, 25, 21, 17, 13, 13, 25, 25, 25, 21, 21, 9, 9, 9, @@ -4764,8 +4983,8 @@ _hb_ucd_u8[13386] = 6, 5, 9, 21, 25, 9, 26, 12, 11, 11, 9, 6, 5, 21, 17, 17, 17, 26, 26, 23, 23, 12, 17, 12, 21, 12, 12, 21, 7, 21, 1, 1, 21, 23, 26, 26, 1, 21, 6, 7, 7, 12, 12, 7, 21, 7, 12, 1, - 12, 6, 6, 12, 12, 26, 7, 26, 26, 7, 21, 1, 24, 7, 7, 6, - 1, 12, 12, 10, 10, 10, 10, 12, 21, 6, 10, 7, 7, 10, 23, 7, + 12, 6, 6, 12, 12, 26, 7, 26, 26, 7, 21, 1, 24, 7, 1, 12, + 7, 6, 12, 10, 10, 10, 10, 12, 21, 6, 10, 7, 7, 10, 23, 7, 15, 26, 13, 21, 13, 7, 15, 7, 12, 23, 21, 26, 21, 15, 17, 7, 29, 7, 7, 22, 18, 18, 14, 14, 14, 7, 10, 21, 17, 21, 11, 12, 5, 6, 8, 8, 8, 24, 5, 24, 9, 24, 29, 29, 29, 1, 20, 19, @@ -4776,247 +4995,250 @@ _hb_ucd_u8[13386] = 26, 14, 17, 6, 14, 6, 12, 24, 24, 6, 26, 15, 6, 21, 11, 21, 24, 9, 6, 9, 23, 26, 6, 10, 4, 4, 3, 3, 7, 25, 17, 16, 16, 22, 16, 16, 25, 17, 7, 1, 25, 24, 26, 1, 2, 2, 12, 15, - 21, 14, 7, 15, 12, 17, 13, 15, 26, 10, 10, 1, 13, 23, 23, 15, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 10, 11, 12, 13, 0, - 14, 0, 0, 0, 0, 0, 15, 0, 16, 0, 0, 0, 0, 0, 0, 0, + 21, 14, 7, 15, 9, 12, 12, 17, 13, 15, 26, 10, 10, 1, 13, 23, + 7, 13, 23, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 10, + 11, 12, 13, 0, 14, 0, 0, 0, 0, 0, 15, 0, 16, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 21, - 22, 23, 0, 0, 0, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 0, 20, 0, 21, 22, 23, 0, 0, 0, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 35, 0, 0, 0, 0, 36, 0, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 35, 0, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 38, 39, 0, 0, 0, 0, 0, 0, - 40, 41, 42, 0, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 2, 0, 0, 0, 0, 3, 0, 0, 0, 4, 5, 6, 7, - 0, 8, 9, 10, 0, 11, 12, 13, 14, 15, 16, 17, 16, 18, 16, 19, - 16, 19, 16, 19, 0, 19, 16, 20, 16, 19, 21, 19, 0, 22, 23, 24, - 25, 26, 27, 28, 29, 30, 31, 0, 32, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 34, 0, 0, 35, 0, 0, - 36, 0, 37, 0, 0, 0, 38, 39, 40, 41, 42, 43, 44, 45, 46, 0, - 0, 47, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, - 0, 50, 0, 51, 0, 52, 53, 0, 54, 0, 0, 0, 0, 0, 0, 55, - 56, 57, 0, 0, 0, 0, 58, 0, 0, 59, 60, 61, 62, 63, 0, 0, - 64, 65, 0, 0, 0, 66, 0, 0, 0, 0, 67, 0, 0, 0, 68, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, - 0, 70, 0, 71, 0, 0, 72, 0, 0, 73, 0, 0, 0, 0, 0, 0, - 0, 0, 74, 0, 0, 0, 0, 0, 75, 76, 0, 77, 78, 0, 0, 79, - 80, 0, 81, 62, 0, 82, 83, 0, 0, 84, 85, 86, 0, 0, 0, 87, - 0, 88, 0, 0, 51, 89, 51, 0, 90, 0, 91, 0, 0, 0, 80, 0, - 0, 0, 92, 93, 0, 94, 95, 96, 97, 0, 0, 0, 0, 0, 51, 0, - 0, 0, 0, 98, 99, 0, 0, 0, 0, 0, 0,100, 0, 0, 0, 0, - 0,101,102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,103, 0, 0, - 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,105,106, 0, 0,107, - 0, 0, 0, 0, 0, 0,108, 0,109, 0,102, 0, 0, 0, 0, 0, - 110,111, 0, 0, 0, 0, 0, 0, 0,112, 0, 0, 0, 0, 0, 0, - 0,113, 0,114, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, - 7, 0, 8, 0, 0, 0, 0, 9, 10, 11, 12, 0, 0, 0, 0, 13, - 0, 0, 14, 15, 0, 16, 0, 17, 18, 0, 0, 19, 0, 20, 21, 0, - 0, 0, 0, 0, 22, 23, 0, 24, 25, 0, 0, 26, 0, 0, 0, 27, - 0, 0, 28, 29, 30, 31, 0, 0, 0, 32, 33, 34, 0, 0, 33, 0, - 0, 35, 33, 0, 0, 0, 33, 36, 0, 0, 0, 0, 0, 37, 38, 0, - 0, 0, 0, 0, 0, 39, 40, 0, 0, 0, 0, 0, 0, 41, 42, 0, - 0, 0, 0, 43, 0, 44, 0, 0, 0, 45, 46, 0, 0, 0, 47, 0, - 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 50, 0, 0, 0, 51, - 0, 52, 0, 53, 0, 0, 0, 0, 54, 0, 0, 0, 0, 55, 0, 56, - 0, 0, 0, 0, 57, 58, 0, 0, 0, 59, 60, 0, 0, 0, 0, 0, - 0, 61, 52, 0, 62, 63, 0, 0, 64, 0, 0, 0, 65, 66, 0, 0, - 0, 67, 0, 68, 69, 70, 71, 72, 1, 73, 0, 74, 75, 76, 0, 0, - 77, 78, 0, 0, 0, 79, 0, 0, 1, 1, 0, 0, 80, 0, 0, 81, - 0, 0, 0, 0, 77, 82, 0, 83, 0, 0, 0, 0, 0, 78, 84, 0, - 85, 0, 52, 0, 1, 78, 0, 0, 86, 0, 0, 87, 0, 0, 0, 0, - 0, 88, 57, 0, 0, 0, 0, 0, 0, 89, 90, 0, 0, 84, 0, 0, - 33, 0, 0, 91, 0, 0, 0, 0, 92, 0, 0, 0, 0, 49, 0, 0, - 93, 0, 0, 0, 0, 94, 95, 0, 0, 96, 0, 0, 97, 0, 0, 0, - 98, 0, 0, 0, 99, 0, 0, 0, 0,100,101, 93, 0, 0,102, 0, - 0, 0, 84, 0, 0,103, 0, 0, 0,104,105, 0, 0,106,107, 0, - 0, 0, 0, 0, 0,108, 0, 0,109, 0, 0, 0, 0,110, 33, 0, - 111,112,113, 35, 0, 0,114, 0, 0, 0,115, 0, 0, 0, 0, 0, - 0,116, 0, 0,117, 0, 0, 0, 0,118, 88, 0, 0, 0, 0, 0, - 57, 0, 0, 0, 0, 52,119, 0, 0, 0, 0,120, 0, 0,121, 0, - 0, 0, 0,119, 0, 0,122, 0, 0, 0, 0, 0, 0,123, 0, 0, - 0,124, 0, 0, 0,125, 0,126, 0, 0, 0, 0,127,128,129, 0, - 130, 0,131, 0, 0, 0,132,133,134, 0, 77, 0, 0, 0, 0, 0, - 35, 0, 0, 0,135, 0, 0, 0,136, 0, 0,137, 0, 0,138, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, - 7, 4, 4, 8, 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, 18, 1, - 1, 1, 19, 1, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, 25, 26, - 27, 28, 29, 30, 0, 0, 1, 1, 31, 0, 0, 0, 32, 33, 34, 35, - 1, 36, 37, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, 42, 0, - 0, 0, 43, 36, 44, 45, 21, 45, 46, 0, 0, 0, 19, 1, 21, 0, - 0, 47, 0, 38, 48, 1, 1, 49, 49, 50, 0, 0, 51, 0, 0, 0, - 52, 1, 0, 0, 38, 14, 4, 1, 1, 1, 53, 21, 43, 52, 54, 21, - 35, 1, 0, 0, 0, 55, 0, 0, 0, 56, 57, 58, 0, 0, 0, 0, - 0, 59, 0, 60, 0, 0, 0, 0, 61, 62, 0, 0, 63, 0, 0, 0, - 64, 0, 0, 0, 65, 0, 0, 0, 66, 0, 0, 0, 67, 0, 0, 0, - 68, 0, 0, 69, 70, 0, 71, 72, 73, 74, 75, 76, 0, 0, 0, 77, - 0, 0, 0, 78, 79, 0, 0, 0, 0, 47, 0, 0, 0, 49, 0, 80, - 0, 0, 0, 62, 0, 0, 63, 0, 0, 81, 0, 0, 82, 0, 0, 0, - 83, 0, 0, 19, 84, 0, 62, 0, 0, 0, 0, 49, 1, 85, 1, 52, - 15, 86, 36, 10, 21, 87, 0, 55, 0, 0, 0, 0, 19, 10, 1, 0, - 0, 0, 0, 0, 88, 0, 0, 89, 0, 0, 88, 0, 0, 0, 0, 78, - 0, 0, 87, 9, 12, 4, 90, 8, 91, 47, 0, 58, 50, 0, 21, 1, - 21, 92, 93, 1, 1, 1, 1, 94, 95, 96, 97, 1, 98, 58, 81, 99, - 100, 4, 58, 0, 0, 0, 0, 0, 0, 19, 50, 0, 0, 0, 0, 0, - 0, 61, 0, 0,101,102, 0, 0,103, 0, 0, 1, 1, 50, 0, 0, - 0, 38, 0, 63, 0, 0, 0, 0, 0, 62, 0, 0,104, 68, 61, 0, - 0, 0, 78, 0, 0, 0,105,106, 58, 38, 81, 0, 0, 0, 0, 0, - 0,107, 1, 14, 4, 12, 84, 0, 0, 0, 0, 38, 87, 0, 0, 0, - 0,108, 0, 0,109, 61, 0,110, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 40, 0, 0, + 0, 0, 0, 0, 41, 42, 43, 0, 44, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 3, 0, 0, 0, + 4, 5, 6, 7, 0, 8, 9, 10, 0, 11, 12, 13, 14, 15, 16, 17, + 16, 18, 16, 19, 16, 19, 16, 19, 0, 19, 16, 20, 16, 19, 21, 19, + 0, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 34, 0, + 0, 35, 0, 0, 36, 0, 37, 0, 0, 0, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 0, 0, 47, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, + 0, 0, 0, 0, 0, 50, 0, 51, 0, 52, 53, 0, 54, 0, 0, 0, + 0, 0, 0, 55, 56, 57, 0, 0, 0, 0, 58, 0, 0, 59, 60, 61, + 62, 63, 0, 0, 64, 65, 0, 0, 0, 66, 0, 0, 0, 0, 67, 0, + 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 69, 0, 0, 0, 70, 0, 71, 0, 0, 72, 0, 0, 73, 0, 0, + 0, 0, 0, 0, 0, 0, 74, 75, 0, 0, 0, 0, 76, 77, 0, 78, + 79, 0, 0, 80, 81, 0, 82, 62, 0, 83, 84, 0, 0, 85, 86, 87, + 0, 88, 0, 89, 0, 90, 0, 0, 51, 91, 51, 0, 92, 0, 93, 0, + 0, 0, 81, 0, 0, 0, 94, 95, 0, 96, 97, 98, 99, 0, 0, 0, + 0, 0, 51, 0, 0, 0, 0,100,101, 0, 0, 0, 0, 0, 0,102, + 0, 0, 0, 0, 0, 0,103, 0, 0, 0, 0, 0, 0,104,105, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,106, 0, 0,107, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,108,109, 0, 0,110, 0, 0, 0, 0, + 0, 0,111, 0,112, 0,105, 0, 0, 0, 0, 0,113,114, 0, 0, + 0, 0, 0, 0, 0,115, 0, 0, 0,116, 0, 0, 0,117, 0,118, + 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 0, 8, 0, + 0, 0, 0, 9, 10, 11, 12, 0, 0, 0, 0, 13, 0, 0, 14, 15, + 0, 16, 0, 17, 18, 0, 0, 19, 0, 20, 21, 0, 0, 0, 0, 0, + 22, 23, 0, 24, 25, 0, 0, 26, 0, 0, 0, 27, 0, 0, 28, 29, + 30, 31, 0, 0, 0, 32, 33, 34, 0, 0, 33, 0, 0, 35, 33, 0, + 0, 0, 33, 36, 0, 0, 0, 0, 0, 37, 38, 0, 0, 0, 0, 0, + 0, 39, 40, 0, 0, 0, 0, 0, 0, 41, 42, 0, 0, 0, 0, 43, + 0, 44, 0, 0, 0, 45, 46, 0, 0, 0, 47, 0, 0, 0, 0, 0, + 0, 48, 49, 0, 0, 0, 0, 50, 0, 0, 0, 51, 0, 52, 0, 53, + 0, 0, 0, 0, 54, 0, 0, 0, 0, 55, 0, 56, 0, 0, 0, 0, + 57, 58, 0, 0, 0, 59, 60, 0, 0, 0, 0, 0, 0, 61, 52, 0, + 62, 63, 0, 0, 64, 0, 0, 0, 65, 66, 0, 0, 0, 67, 0, 68, + 69, 70, 71, 72, 1, 73, 0, 74, 75, 76, 0, 0, 77, 78, 0, 0, + 0, 79, 0, 0, 1, 1, 0, 0, 80, 0, 0, 81, 0, 0, 0, 0, + 77, 82, 0, 83, 0, 0, 0, 0, 0, 78, 84, 0, 85, 0, 52, 0, + 1, 78, 0, 0, 86, 0, 0, 87, 0, 0, 0, 0, 0, 88, 57, 0, + 0, 0, 0, 0, 0, 89, 90, 0, 0, 84, 0, 0, 33, 0, 0, 91, + 0, 0, 0, 0, 92, 0, 0, 0, 0, 49, 0, 0, 93, 0, 0, 0, + 0, 94, 95, 0, 0, 96, 0, 0, 97, 0, 0, 0, 98, 0, 0, 0, + 99, 0, 0, 0,100, 0, 0, 0, 0,101,102, 93, 0, 0,103, 0, + 0, 0, 84, 0, 0,104, 0, 0, 0,105,106, 0, 0,107,108, 0, + 0, 0, 0, 0, 0,109, 0, 0,110, 0, 0, 0, 0,111, 33, 0, + 112,113,114, 57, 0, 0,115, 35, 0, 0,116, 0, 0, 0,117, 0, + 0, 0, 0, 0, 0,118, 0, 0,119, 0, 0, 0, 0,120, 88, 0, + 0, 0, 0, 0, 57, 0, 0, 0, 0, 52,121, 0, 0, 0, 0,122, + 0, 0,123, 0, 0, 0, 0,121, 0, 0,124, 0, 0, 0, 0, 0, + 79, 0, 0, 0, 0,125, 0, 0, 0,126, 0, 0, 0,127, 0,128, + 0, 0, 0, 0,129,130,131, 0,132, 0,133, 0, 0, 0,134,135, + 136, 0, 77, 0, 0, 0, 0, 0, 35, 0, 0, 0,137, 0, 0, 0, + 138, 0, 0, 0,139, 0, 0,140, 0, 0,141, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 4, 4, 8, + 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, 18, 1, 1, 1, 19, 1, + 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, 25, 26, 27, 28, 29, 30, + 0, 0, 1, 1, 31, 0, 0, 0, 32, 33, 34, 35, 1, 36, 37, 0, + 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, 42, 0, 0, 0, 43, 36, + 44, 45, 21, 45, 46, 0, 0, 0, 19, 1, 21, 0, 0, 47, 0, 38, + 48, 1, 1, 49, 49, 50, 0, 0, 51, 0, 0, 19, 52, 1, 0, 0, + 38, 14, 4, 1, 1, 1, 53, 21, 43, 52, 54, 21, 35, 1, 0, 0, + 0, 55, 0, 0, 0, 56, 57, 58, 0, 0, 0, 0, 0, 59, 0, 60, + 0, 0, 0, 0, 61, 62, 0, 0, 63, 0, 0, 0, 64, 0, 0, 0, + 65, 0, 0, 0, 66, 0, 0, 0, 67, 0, 0, 0, 68, 0, 0, 69, + 70, 0, 71, 72, 73, 74, 75, 76, 0, 0, 0, 77, 0, 0, 0, 78, + 79, 0, 0, 0, 0, 47, 0, 0, 0, 49, 0, 80, 0, 0, 0, 62, + 0, 0, 63, 0, 0, 81, 0, 0, 82, 0, 0, 0, 83, 0, 0, 19, + 84, 0, 62, 0, 0, 0, 0, 49, 1, 85, 1, 52, 15, 86, 36, 10, + 21, 87, 0, 55, 0, 0, 0, 0, 19, 10, 1, 0, 0, 0, 0, 0, + 88, 0, 0, 89, 0, 0, 88, 0, 0, 0, 0, 78, 0, 0, 87, 9, + 12, 4, 90, 8, 91, 47, 0, 58, 50, 0, 21, 1, 21, 92, 93, 1, + 1, 1, 1, 94, 95, 96, 97, 1, 98, 58, 81, 99,100, 4, 58, 0, + 0, 0, 0, 0, 0, 19, 50, 0, 0, 0, 0, 0, 0, 61, 0, 0, + 101,102, 0, 0,103, 0, 0, 1, 1, 50, 0, 0, 0, 38, 0, 63, + 0, 0, 0, 0, 0, 62, 0, 0,104, 68, 61, 0, 0, 0, 78, 0, + 0, 0,105,106, 58, 38, 81, 0, 0, 0, 0, 0, 0,107, 1, 14, + 4, 12, 84, 0, 0, 0, 0, 38, 87, 0, 0, 0, 0,108, 0, 0, + 109, 61, 0,110, 0, 0, 0, 1, 0, 0, 0, 0, 49, 50, 0, 0, 19, 58, 0, 0, 0, 51, 0,111, 14, 52,112, 41, 0, 0, 62, 0, 0, 61, 0, 0,113, 0, 87, 0, 0, 0, 61, 62, 0, 0, 62, 0, 89, 0, 0,113, 0, 0, 0, 0,114, 0, 0, 0, 78, 55, 0, 38, - 1, 58, 1, 58, 0, 0, 63, 89, 0, 0,115, 0, 0, 0, 55, 0, - 0, 0, 0,115, 0, 0, 0, 0, 61, 0, 0, 0, 0, 79, 0, 61, - 0, 0, 0, 0, 56, 0, 89, 80, 0, 0, 79, 0, 0, 0, 8, 91, - 0, 0, 1, 87, 0, 0,116, 0, 0, 0, 0, 0, 0,117, 0,118, - 119,120,121, 0,104, 4,122, 49, 23, 0, 0, 0, 38, 50, 38, 58, - 0, 0, 1, 87, 1, 1, 1, 1, 39, 1, 48,105, 87, 0, 0, 0, - 0, 1, 0, 0, 0,123, 4,122, 0, 0, 0, 1,124, 0, 0, 0, - 0, 0,230,230,230,230,230,232,220,220,220,220,232,216,220,220, - 220,220,220,202,202,220,220,220,220,202,202,220,220,220, 1, 1, - 1, 1, 1,220,220,220,220,230,230,230,230,240,230,220,220,220, - 230,230,230,220,220, 0,230,230,230,220,220,220,220,230,232,220, - 220,230,233,234,234,233,234,234,233,230, 0, 0, 0,230, 0,220, - 230,230,230,230,220,230,230,230,222,220,230,230,220,220,230,222, - 228,230, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, - 0, 23, 0, 24, 25, 0,230,220, 0, 18, 30, 31, 32, 0, 0, 0, - 0, 27, 28, 29, 30, 31, 32, 33, 34,230,230,220,220,230,220,230, - 230,220, 35, 0, 0, 0, 0, 0,230,230,230, 0, 0,230,230, 0, - 220,230,230,220, 0, 0, 0, 36, 0, 0,230,220,230,230,220,220, - 230,220,220,230,220,230,220,230,230, 0, 0,220, 0, 0,230,230, - 0,230, 0,230,230,230,230,230, 0, 0, 0,220,220,220,230,220, - 220,220,230,230, 0,220, 27, 28, 29,230, 7, 0, 0, 0, 0, 9, - 0, 0, 0,230,220,230,230, 0, 0, 0, 0, 0,230, 0, 0, 84, - 91, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 9, 0,103,103, - 9, 0,107,107,107,107,118,118, 9, 0,122,122,122,122,220,220, - 0, 0, 0,220, 0,220, 0,216, 0, 0, 0,129,130, 0,132, 0, - 0, 0, 0, 0,130,130,130,130, 0, 0,130, 0,230,230, 9, 0, - 230,230, 0, 0,220, 0, 0, 0, 0, 7, 0, 9, 9, 0, 9, 9, - 0, 0, 0,230, 0, 0, 0,228, 0, 0, 0,222,230,220,220, 0, - 0, 0,230, 0, 0,220,230,220, 0,220,230,230,230, 0, 0, 0, - 9, 9, 0, 0, 7, 0,230, 0, 1, 1, 1, 0, 0, 0,230,234, - 214,220,202,230,230,230,230,230,232,228,228,220,218,230,233,220, - 230,220,230,230, 1, 1, 1, 1, 1,230, 0, 1, 1,230,220,230, - 1, 1, 0, 0,218,228,232,222,224,224, 0, 8, 8, 0, 0, 0, - 0,220,230, 0,230,230,220, 0, 0,230, 0, 0, 26, 0, 0,220, - 0,230,230, 1,220, 0, 0,230,220, 0, 0, 0,220,220, 0, 0, - 230,220, 0, 9, 7, 0, 0, 7, 9, 0, 0, 0, 9, 7, 6, 6, - 0, 0, 0, 0, 1, 0, 0,216,216, 1, 1, 1, 0, 0, 0,226, - 216,216,216,216,216, 0,220,220,220, 0,232,232,220,230,230,230, - 7, 0, 16, 17, 17, 33, 17, 49, 17, 17, 84, 97,135,145, 26, 17, + 1, 58, 1, 58, 0, 0, 0, 0, 0, 88, 63, 89, 0, 0,115, 0, + 0, 0, 55, 0, 0, 0, 0,115, 0, 0, 0, 0, 61, 0, 0, 0, + 0, 79, 0, 61, 0, 0, 0, 0, 56, 0, 89, 80, 0, 0, 79, 0, + 0, 0, 8, 91, 0, 0, 1, 87, 0, 0,116, 0, 0, 0, 0, 0, + 0,117, 0,118,119,120,121, 0,104, 4,122, 49, 23, 0, 0, 0, + 38, 50, 38, 58, 0, 0, 1, 87, 1, 1, 1, 1, 39, 1, 48,105, + 87, 0, 0, 0, 0, 1, 0, 0, 0,123, 0, 0, 0,112, 4,122, + 0, 0, 0, 1,124, 0, 0, 0, 0, 0,230,230,230,230,230,232, + 220,220,220,220,232,216,220,220,220,220,220,202,202,220,220,220, + 220,202,202,220,220,220, 1, 1, 1, 1, 1,220,220,220,220,230, + 230,230,230,240,230,220,220,220,230,230,230,220,220, 0,230,230, + 230,220,220,220,220,230,232,220,220,230,233,234,234,233,234,234, + 233,230, 0, 0, 0,230, 0,220,230,230,230,230,220,230,230,230, + 222,220,230,230,220,220,230,222,228,230, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 19, 20, 21, 22, 0, 23, 0, 24, 25, 0,230,220, + 0, 18, 30, 31, 32, 0, 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, + 34,230,230,220,220,230,220,230,230,220, 35, 0, 0, 0, 0, 0, + 230,230,230, 0, 0,230,230, 0,220,230,230,220, 0, 0, 0, 36, + 0, 0,230,220,230,230,220,220,230,220,220,230,220,230,220,230, + 230, 0, 0,220, 0, 0,230,230, 0,230, 0,230,230,230,230,230, + 0, 0, 0,220,220,220,230,220,220,220,230,230, 0,220, 27, 28, + 29,230, 7, 0, 0, 0, 0, 9, 0, 0, 0,230,220,230,230, 0, + 0, 0, 0, 0,230, 0, 0, 84, 91, 0, 0, 0, 0, 9, 9, 0, + 0, 0, 0, 0, 9, 0,103,103, 9, 0,107,107,107,107,118,118, + 9, 0,122,122,122,122,220,220, 0, 0, 0,220, 0,220, 0,216, + 0, 0, 0,129,130, 0,132, 0, 0, 0, 0, 0,130,130,130,130, + 0, 0,130, 0,230,230, 9, 0,230,230, 0, 0,220, 0, 0, 0, + 0, 7, 0, 9, 9, 0, 9, 9, 0, 0, 0,230, 0, 0, 0,228, + 0, 0, 0,222,230,220,220, 0, 0, 0,230, 0, 0,220,230,220, + 0,220,230,230,230, 0, 0, 0, 9, 9, 0, 0, 7, 0,230, 0, + 1, 1, 1, 0, 0, 0,230,234,214,220,202,230,230,230,230,230, + 232,228,228,220,218,230,233,220,230,220,230,230, 1, 1, 1, 1, + 1,230, 0, 1, 1,230,220,230, 1, 1, 0, 0,218,228,232,222, + 224,224, 0, 8, 8, 0, 0, 0, 0,220,230, 0,230,230,220, 0, + 0,230, 0, 0, 26, 0, 0,220, 0,230,230, 1,220, 0, 0,230, + 220, 0, 0, 0,220,220, 0, 0,230,220, 0, 9, 7, 0, 0, 7, + 9, 0, 0, 0, 9, 7, 6, 6, 0, 0, 0, 0, 1, 0, 0,216, + 216, 1, 1, 1, 0, 0, 0,226,216,216,216,216,216, 0,220,220, + 220, 0,232,232,220,230,230,230, 7, 0, 16, 17, 17, 33, 17, 49, + 17, 17, 84, 97,135,145, 26, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17,177, 0, 1, 2, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, + 3, 3, 3, 3, 5, 3, 3, 3, 3, 3, 6, 7, 8, 3, 3, 3, + 3, 3, 9, 10, 11, 12, 13, 3, 3, 3, 3, 3, 3, 3, 3, 14, + 3, 15, 3, 3, 3, 3, 3, 3, 16, 17, 18, 19, 20, 21, 3, 3, + 3, 22, 23, 24, 3, 3, 3, 3, 3, 3, 25, 3, 3, 3, 3, 3, + 3, 3, 3, 26, 3, 3, 27, 28, 0, 1, 0, 0, 0, 0, 0, 1, + 0, 2, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 4, + 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 6, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 8, 9, 0, 0, 0, 0, 0, 0, 9, 0, 9, 0, 0, 0, 0, + 0, 0, 0, 10, 11, 12, 13, 0, 0, 14, 15, 16, 6, 0, 17, 18, + 19, 19, 19, 20, 21, 22, 23, 24, 19, 25, 0, 26, 27, 19, 19, 28, + 29, 30, 0, 31, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 19, + 28, 0, 32, 33, 9, 34, 35, 19, 0, 0, 36, 37, 38, 39, 40, 19, + 0, 41, 42, 43, 44, 31, 0, 1, 45, 42, 0, 0, 0, 0, 0, 32, + 14, 14, 0, 0, 0, 0, 14, 0, 0, 46, 47, 47, 47, 47, 48, 49, + 47, 47, 47, 47, 50, 51, 52, 53, 43, 21, 0, 0, 0, 0, 0, 0, + 0, 54, 6, 55, 0, 14, 19, 1, 0, 0, 0, 0, 56, 57, 0, 0, + 0, 0, 0, 19, 58, 31, 0, 0, 0, 0, 0, 0, 0, 59, 14, 0, + 0, 0, 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 60, 61, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 3, 0, 4, + 5, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 1, 1, 0, 0, 8, + 9, 0, 8, 9, 0, 0, 0, 0, 8, 9, 10, 11, 12, 0, 0, 0, + 13, 0, 0, 0, 0, 14, 15, 16, 17, 0, 0, 0, 1, 0, 0, 18, + 19, 0, 0, 0, 20, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, + 1, 1, 1, 1, 0, 8, 21, 9, 0, 0, 22, 0, 0, 0, 0, 1, + 0, 23, 24, 25, 0, 0, 26, 0, 0, 0, 8, 21, 27, 0, 1, 0, + 0, 1, 1, 1, 1, 0, 1, 28, 29, 30, 0, 31, 32, 20, 1, 1, + 0, 0, 0, 8, 21, 9, 1, 4, 5, 0, 0, 0, 33, 9, 0, 1, + 1, 1, 0, 8, 21, 21, 21, 21, 34, 1, 35, 21, 21, 21, 9, 36, + 0, 0, 37, 38, 1, 0, 39, 0, 0, 0, 1, 0, 1, 0, 0, 0, + 0, 8, 21, 9, 1, 0, 0, 0, 40, 0, 8, 21, 21, 21, 21, 21, + 21, 21, 21, 9, 0, 1, 1, 1, 1, 8, 21, 21, 21, 9, 0, 0, + 0, 41, 0, 42, 43, 0, 0, 0, 1, 44, 0, 0, 0, 45, 8, 9, + 1, 0, 0, 0, 8, 21, 21, 21, 9, 0, 1, 0, 1, 1, 8, 21, + 21, 9, 0, 4, 5, 8, 9, 1, 0, 0, 0, 1, 2, 3, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 3, 3, 3, 3, 3, 3, + 3, 15, 3, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17,177, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 5, 3, 3, 3, - 3, 3, 6, 7, 8, 3, 3, 3, 3, 3, 9, 10, 11, 12, 13, 3, - 3, 3, 3, 3, 3, 3, 3, 14, 3, 15, 3, 3, 3, 3, 3, 3, - 16, 17, 18, 19, 20, 21, 3, 3, 3, 22, 23, 24, 3, 3, 3, 3, - 3, 3, 25, 3, 3, 3, 3, 3, 3, 3, 3, 26, 3, 3, 27, 28, - 0, 1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, - 0, 3, 0, 0, 0, 0, 0, 4, 0, 5, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 0, 0, 0, - 0, 9, 0, 9, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 0, - 0, 14, 15, 16, 6, 0, 17, 18, 19, 19, 19, 20, 21, 22, 23, 24, - 19, 25, 0, 26, 27, 19, 19, 28, 29, 30, 0, 31, 0, 0, 0, 8, - 0, 0, 0, 0, 0, 0, 0, 19, 28, 0, 32, 33, 9, 34, 35, 19, - 0, 0, 36, 37, 38, 39, 40, 19, 0, 41, 42, 43, 44, 31, 0, 1, - 45, 42, 0, 0, 0, 0, 0, 32, 14, 14, 0, 0, 0, 0, 14, 0, - 0, 46, 47, 47, 47, 47, 48, 49, 47, 47, 47, 47, 50, 51, 52, 53, - 43, 21, 0, 0, 0, 0, 0, 0, 0, 54, 6, 55, 0, 14, 19, 1, - 0, 0, 0, 0, 56, 57, 0, 0, 0, 0, 0, 19, 58, 31, 0, 0, - 0, 0, 0, 0, 0, 59, 14, 0, 0, 0, 0, 1, 0, 2, 0, 0, - 0, 3, 0, 0, 0, 60, 61, 0, 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 2, 3, 0, 4, 5, 0, 0, 6, 0, 0, 0, 7, - 0, 0, 0, 1, 1, 0, 0, 8, 9, 0, 8, 9, 0, 0, 0, 0, - 8, 9, 10, 11, 12, 0, 0, 0, 13, 0, 0, 0, 0, 14, 15, 16, - 17, 0, 0, 0, 1, 0, 0, 18, 19, 0, 0, 0, 20, 0, 0, 0, - 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 8, 21, 9, - 0, 0, 22, 0, 0, 0, 0, 1, 0, 23, 24, 25, 0, 0, 26, 0, - 0, 0, 8, 21, 27, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 28, - 29, 30, 0, 31, 32, 20, 1, 1, 0, 0, 0, 8, 21, 9, 1, 4, - 5, 0, 0, 0, 33, 9, 0, 1, 1, 1, 0, 8, 21, 21, 21, 21, - 34, 1, 35, 21, 21, 21, 9, 36, 0, 0, 37, 38, 1, 0, 39, 0, - 0, 0, 1, 0, 1, 0, 0, 0, 0, 8, 21, 9, 1, 0, 0, 0, - 40, 0, 8, 21, 21, 21, 21, 21, 21, 21, 21, 9, 0, 1, 1, 1, - 1, 8, 21, 21, 21, 9, 0, 0, 0, 41, 0, 42, 43, 0, 0, 0, - 1, 44, 0, 0, 0, 45, 8, 9, 1, 0, 0, 0, 8, 21, 21, 21, - 9, 0, 1, 0, 1, 1, 8, 21, 21, 9, 0, 4, 5, 8, 9, 1, - 0, 0, 0, 1, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, - 13, 14, 3, 3, 3, 3, 3, 3, 3, 15, 3, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 18, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, - 12, 13, 14, 15, 16, 17, 17, 17, 18, 17, 19, 20, 21, 22, 23, 23, - 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 23, 23, 23, 23, 23, - 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, - 23, 23, 23, 23, 25, 25, 26, 27, 28, 29, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 18, 0, 0, 1, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 17, 17, + 18, 17, 19, 20, 21, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 24, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 25, 25, 26, 27, + 28, 29, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 52, 53, 31, + 31, 31, 31, 54, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 56, 57, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 58, 31, 31, 31, + 59, 60, 61, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 63, 64, 65, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 66, 67, 68, 31, 31, 31, 31, 69, 31, 31, 31, 31, 31, + 31, 31, 17, 70, 71, 72, 17, 17, 73, 74, 31, 75, 76, 77, 78, 79, + 80, 31, 81, 82, 17, 83, 17, 17, 17, 17, 31, 31, 23, 23, 23, 23, + 23, 23, 23, 84, 31, 31, 31, 31, 23, 84, 31, 31, 23, 23, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 52, 53, 31, 31, 31, 31, 54, 55, 55, 56, 31, - 31, 31, 31, 31, 31, 31, 57, 58, 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, 59, 60, 31, 61, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 63, 64, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 65, 66, 67, 31, 31, - 31, 31, 68, 31, 31, 31, 31, 31, 31, 31, 31, 69, 70, 71, 17, 17, - 72, 73, 31, 74, 75, 76, 77, 78, 79, 31, 80, 81, 17, 82, 17, 17, - 17, 17, 31, 31, 23, 23, 23, 23, 23, 23, 23, 83, 31, 31, 31, 31, - 23, 83, 31, 31, 23, 23, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 84, 0, 0, 1, 0, 1, 2, 3, 0, 1, 2, 3, - 4, 5, 6, 7, 0, 1, 2, 3, 4, 4, 4, 4, 4, 4, 5, 6, - 7, 8, 9, 10, 11, 11, 12, 11, 13, 14, 15, 16, 17, 18, 19, 20, - 21, 22, 23, 24, 25, 26, 19, 27, 28, 29, 30, 30, 31, 31, 32, 32, - 33, 33, 34, 34, 35, 35, 36, 36, 37, 37, 38, 38, 39, 40, 41, 41, - 42, 42, 42, 43, 44, 44, 45, 46, 47, 47, 47, 47, 48, 48, 48, 48, - 48, 48, 49, 50, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 53, - 54, 55, 56, 56, 57, 58, 59, 51, 60, 61, 62, 63, 64, 65, 66, 7, - 67, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 7, 4, 4, 4, 4, - 77, 77, 77, 77, 78, 79, 80, 81, 82, 83, 84, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 85, 85, 85, 85, 0, 0, 0, 0, 86, 87, 88, 88, - 89, 90, 48, 91, 0, 0, 92, 92, 92, 92, 92, 93, 94, 95, 96, 97, - 98, 47, 99,100,101,102, 0,103,104,105, 0, 0, 92, 92, 92, 92, - 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 0,106,106,106,106, - 106,106,106,106,106,106,106,107,108,108,108,108,108, 11,109,110, - 111, 4,112, 4,113,114,115,116,117,118,119,120,121,122,123,124, - 125,126, 50,127, 47, 47, 47, 47, 47, 47, 47, 47,128,128,128,128, - 128,128,128,128,128,128,128,128, 92, 92, 92, 92, 92, 92, 92, 92, - 129,130, 19, 19, 19, 19, 19, 19,131, 19, 19, 19,132,133, 19,134, - 135,136,137,101,138,138,138,138, 0, 77,139,140,128,128,141,142, - 143,144,145,146,147,148,149,150,151,152,153,153,154,154,154,154, - 154,154, 4, 4,155,156,157,158,159,160,161,162,163,164,165,166, - 167,168,169,169,170,170,171,171,172,172,128,128, 19, 19,173,174, - 175,176,177,178,179,179,180,181,182,183,184,185,186,186,187,188, - 189,190,128,128,191,191,192,192,128,128,193,193,194,195,196,196, - 197,197,128,128,198,198,199,199,200,200,201,201,202,203,204,205, - 28, 28,128,128,206,207,208,208,209,210,211,211,128,128,212,212, - 213,213,214, 34,215,215,215,215,215,215,215,215,215,215,215,215, - 215,215,128,128,128,128,128,128,128,128,216,216,217,217,217,217, - 217,217,217,217,217,217,128,128,128,128,128,128,218,218,218,218, - 218,218,218,218,218,218,128,128,128,128,128,128,110,110,110,110, - 110,110,110,110,110,219,220,221,222,222,222,222,223,223,223,223, - 224,224,224,225,226,226,226,226,226,226,226,226,226,226,226,226, - 227,227,227,227,227,227,227,227,226,226,128,128,128,128,128,128, - 128,128,104,104,228,229,229,229,230,231,232,232,232,232,232,232, - 128,128,128,128,233,233,234, 0,128,128,128,128,128,128,128,128, - 7,235, 0, 0, 0, 0, 0, 0, 0,236,237, 0, 77, 77, 0, 0, - 0, 0,128,128,238,238,238,238,238,238,238,238,238,238,238,238, - 128,128,128,128,128,128,128,128, 4, 4,128,128,239, 11, 11, 11, - 240,240,128,128,128,128,241,242,128,128,128,128,128,128,243,243, - 128,128,128,128,128,128,128,128,128,128, 48, 48,244,244,244,244, - 245,245,128,128, 0, 0, 0, 0, 0, 0,128,128, 19, 19, 19, 19, - 128,128,128,128,246, 0,128,128, 0, 0, 0, 0, 92, 92,128,128, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 85, 0, 0, 1, + 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, + 4, 4, 4, 4, 4, 4, 5, 6, 7, 8, 9, 10, 11, 11, 12, 11, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 19, 27, + 28, 29, 30, 30, 31, 31, 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, + 37, 37, 38, 38, 39, 40, 41, 41, 42, 42, 42, 43, 44, 44, 45, 46, + 47, 47, 47, 47, 48, 48, 48, 48, 48, 48, 49, 50, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59, 51, + 60, 61, 62, 63, 64, 65, 66, 7, 67, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 7, 4, 4, 4, 4, 77, 77, 77, 77, 78, 79, 80, 81, + 82, 83, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 85, 85, 85, + 0, 0, 0, 0, 86, 87, 88, 88, 89, 90, 48, 91, 0, 0, 92, 92, + 92, 92, 92, 93, 94, 95, 96, 97, 98, 47, 99,100,101,102, 0,103, + 104,105, 0, 0, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 0,106,106,106,106,106,106,106,106,106,106,106,107, + 108,108,108,108,108, 11,109,110,111, 4,112, 4,113,114,115,116, + 117,118,119,120,121,122,123,124,125,126, 50,127, 47, 47, 47, 47, + 47, 47, 47, 47,128,128,128,128,128,128,128,128,128,128,128,128, + 92, 92, 92, 92, 92, 92, 92, 92,129,130, 19, 19, 19, 19, 19, 19, + 131, 19, 19, 19,132,133, 19,134,135,136,137,101,138,138,138,138, + 0, 77,139,140,128,128,141,142,143,144,145,146,147,148,149,150, + 151,152,153,154,155,155,155,155,155,155, 4, 4,156,157,158,159, + 160,161,162,163,164,165,166,167,168,169,170,170,171,171,172,172, + 173,174,174,174, 19, 19,175,176,177,178,179,180,181,181,182,183, + 184,185,186,187,188,188,189,190,191,192,193,193,194,194,195,195, + 128,128,196,196,197,198,199,200,201,201,128,128,202,202,203,203, + 204,204,205,205,206,207,208,209, 28, 28,210,210,211,212,213,213, + 214,215,216,216,128,128,217,217,218,218,219, 34,220,220,220,220, + 220,220,220,220,220,220,220,220,220,220,128,128,128,128,128,128, + 128,128,221,221,222,222,222,222,222,222,222,222,223,223,223,223, + 223,223,223,223,223,223,128,128,128,128,128,128,128,128,128,128, + 224,224,128,128,110,110,110,110,110,110,110,110,110,225,226,227, + 228,228,228,228,128,128,128,128,229,229,128,128,230,230,230,230, + 231,231,231,232,233,233,233,233,233,233,233,233,233,233,233,233, + 234,234,234,234,234,234,234,234,233,233,128,128,128,128,128,128, + 128,128,104,104,235,236,236,236,237,238,239,239,239,239,239,239, + 128,128,128,128,240,240,241, 0,128,128,128,128, 0, 0, 0, 0, + 7,242, 0, 0, 0, 0, 0, 0, 0,243,244, 0, 77, 77, 0, 0, + 0, 0,128,128,245,245,245,245,245,245,245,245,245,245,245,245, + 128,128,128,128,128,128,128,128, 4, 4,128,128,246, 11, 11, 11, + 247,247,128,128,128,128,248,249,128,128,128,128,128,128,250,250, + 128,128,251,251,128,128,128,128,128,128, 48, 48,252,252,252,252, + 253,253,128,128, 0, 0, 0, 0, 0, 0,128,128, 19, 19, 19, 19, + 128,128,128,128,254, 0,128,128, 0, 0, 0, 0, 92, 92,128,128, 128,128,128,128, 0, 0,128,128, 7, 7, 7, 7, 0, 0, 0, 0, 1, 2, 1, 2, 0, 0, 3, 3, 4, 5, 4, 5, 4, 4, 4, 4, 4, 4, 4, 6, 0, 0, 7, 0, 8, 8, 8, 8, 8, 8, 8, 9, @@ -5056,30 +5278,32 @@ _hb_ucd_u8[13386] = 137,137,138,138,138,138,139, 0,140,140,140,141,141,142,142,142, 143,143,144,144,144,144,144,144,145,145,145,145,145,146,146,146, 147,147,147,148,148,148,148,148,149,149,149,150,150,150,150,151, - 151,151,151,151,152,152,152,152,153,153,153,153,154,154,155,155, - 156,156,156,156,156,156,157,157,158,158,159,159,159,159,159,159, - 160,160,161,161,161,161,161,161,162,162,162,162,162,162,163,163, - 164,164,164,164,165,165,165,165,166,166,166,166,167,167,168,168, - 169,169,169,169,170,170,170,170,171,171,171,171,172,172,172,172, - 173,173,173,173,173,173,173,174,175,175,175,176,176,176,176,177, - 177,177,177,178,178,178,179,179,180,180,180,180,181,181,181,181, - 181,182,182,182,183,183,183,183,183,184,184,184,185,185,185,185, - 185,185,186, 43,187,187,187,187,188,188,188,189,189,189,189,189, - 190,190,190,191,190,190,190,190,192,192,192,192,193,193,193,193, - 194,194,194,194,195,195,195,195,195,195, 66, 66,196,196,196,196, - 197,197,197,197,198,198,198,198,199,199,199,199,200,200,200,200, - 201,201,201,201,202,202,202,202,202,203,203,203,203,203,203, 55, - 204,204,204,204,205,205,205,205,205,205,205,206,206,206,206,206, - 207,207,207,207,207,207,208,208,208,208,208,208,209,209,209,209, - 210,210,210,210,110,110,110,110,211,211,211,211,212,212,212,212, - 213,213,213,213,214,214,214,214,215,215,215,216,216,216,216,216, - 216,217,217,217,218,218,218,218,219,219,219,219,220,220,220,220, - 220,220,221, 94,222,222,222,222,223,223,223,223,224, 99, 99, 99, - 99, 99, 99, 99, 99, 99,102,225, 99,226,102,227,227,227,227,227, - 228,228,228,228,228,228, 0, 0, 8, 0, 0, 0, 0, 0,229,230, - 231, 0,232, 0,233,233,233,233, 91, 91, 91, 13,234,234,234,234, - 235,235,235,235,236,236,236,236,237,237,237,237,238,238,238,238, - 239,239,239,239,240, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, + 151,151,151,151,152,152,152,152,153,153,153,153,154,154,154,154, + 155,155,156,156,157,157,157,157,157,157,158,158,159,159,160,160, + 160,160,160,160,161,161,162,162,162,162,162,162,163,163,163,163, + 163,163,164,164,165,165,165,165,166,166,166,166,167,167,167,167, + 168,168,169,169,170,170,170,170,171,171,171,171,172,172,172,172, + 173,173,173,173,174,174,174,174,175,175,175,175,176, 21, 21, 21, + 177,177,177,178,178,178,178,179,179,179,179,180,180,180,181,181, + 182,182,182,182,183,183,183,183,183,184,184,184,185,185,185,185, + 185,186,186,186,187,187,187,187,187,187,188, 43,189,189,189,189, + 190,190,190,191,191,191,191,191,192,192,192,193,192,192,192,192, + 194,194,194,194,195,195,195,195,196,196,196,196,197,197,197,197, + 198,198,198,198,198,198, 66, 66,199,199,199,199,199, 49, 49, 49, + 200,200,200,200,201,201,201,201,202,202,202,202,203,203,203,203, + 204,204,204,204,205,205,205,205,205,206,206,206,206,206,206, 55, + 207,207,207,207,208,208,208,208,209,209,209,209,209,209,209,210, + 210,210,210,210,211,211,211,211,211,211,212,212,212,212,212,212, + 213,213,213,213,214,214,214,214,110,110,110,110,215,215,215,215, + 216,216,216,216,217,217,217,217,218,218,218,218,219,219,219,219, + 220,220,220,221,221,221,221,221,221,222,222,222,223,223,223,223, + 224,224,224,224,225,225,225,225,226,226,226,226,226,226,227, 94, + 228,228,228,228,229,229,229,229,230, 99, 99, 99, 99, 99, 99, 99, + 99, 99,102,231, 99,232,102,233,233,233,233,233,234,234,234,234, + 234,234, 0, 0, 8, 0, 0, 0, 0, 0,235,236,237, 0,238, 0, + 239,239,239,239, 91, 91, 91, 13,240,240,240,240,241,241,241,241, + 242,242,242,242,243,243,243,243,244,244,244,244,245,245,245,245, + 246,246,246,246,247, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 3, 0, 0, 0, 4, 0, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 5, 0, 2, 5, 6, 0, 7, 7, 7, 7, 8, 9, 8, 10, 8, 11, 8, 8, 8, 8, 8, 8, 12, 13, 13, 13, 14, 14, 14, 14, @@ -5123,98 +5347,102 @@ _hb_ucd_u8[13386] = 163,163,163,163,164,164,164,164,165,165,165,165,166,166,166,166, 167,167,167,167,168,168,168,168,169,169,169,169,170,170,170,170, 171,171,171,171,172,172,172,172,173,173,173,173,174,174,174,174, - 174,174,174,175,176,176,176,176,177,177,177,177,178,178,178,178, + 175,175,175,175,176,176,176,176,177, 20, 20, 20,178,178,178,178, 179,179,179,179,180,180,180,180,181,181,181,181,182,182,182,182, 183,183,183,183,184,184,184,184,185,185,185,185,186,186,186,186, - 187, 45, 45, 45,188,188,188,188,189,189,189,189,190,190,190,190, - 191,191,191,191,191,191,192,191,193,193,193,193,194,194,194,194, + 187,187,187,187,188,188,188,188,189, 45, 45, 45,190,190,190,190, + 191,191,191,191,192,192,192,192,193,193,193,193,193,193,194,193, 195,195,195,195,196,196,196,196,197,197,197,197,198,198,198,198, 199,199,199,199,200,200,200,200,201,201,201,201,202,202,202,202, 203,203,203,203,204,204,204,204,205,205,205,205,206,206,206,206, 207,207,207,207,208,208,208,208,209,209,209,209,210,210,210,210, 211,211,211,211,212,212,212,212,213,213,213,213,214,214,214,214, 215,215,215,215,216,216,216,216,217,217,217,217,218,218,218,218, - 219,219,219,219,220,220,220,220,221,221,221,221,222,223,223,223, - 224,224,224,224,223,223,223,223,225,106,106,106,226,106,106,106, - 106,227,109,109,228,228,228,228,229,229,229,229, 0,230, 86, 0, - 0, 0,230, 7, 82,138, 7, 0, 0, 0,231, 86,232,232,232,232, - 233,233,233,233,234,234,234,234,235,235,235,235,236,236,236,236, - 237,237,237,237,238,238,238,238,239, 0, 0, 0, 0, 0, 0, 0, - 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 0, 0, 19, 0, - 19, 0, 0, 0, 0, 0, 26, 26, 1, 1, 1, 1, 9, 9, 9, 9, - 0, 9, 9, 9, 9, 9, 0, 9, 9, 0, 9, 0, 9, 9, 55, 55, - 55, 55, 55, 55, 6, 6, 6, 6, 6, 1, 1, 6, 6, 4, 4, 4, - 4, 4, 4, 4, 4, 14, 14, 14, 14, 14, 14, 14, 3, 3, 3, 3, - 3, 0, 3, 3, 0, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 1, - 1, 1, 3, 3, 1, 3, 3, 3, 37, 37, 37, 37, 38, 38, 38, 38, - 64, 64, 64, 64, 90, 90, 90, 90, 95, 95, 95, 95, 3, 3, 0, 3, - 7, 7, 7, 7, 7, 1, 1, 1, 1, 7, 7, 7, 0, 0, 7, 7, - 5, 5, 5, 5, 11, 11, 11, 11, 10, 10, 10, 10, 21, 21, 21, 21, - 22, 22, 22, 22, 23, 23, 23, 23, 16, 16, 16, 16, 20, 20, 20, 20, - 36, 36, 36, 36, 24, 24, 24, 24, 24, 24, 24, 0, 18, 18, 18, 18, - 25, 25, 25, 25, 25, 0, 0, 0, 0, 25, 25, 25, 33, 33, 33, 33, - 8, 8, 8, 8, 8, 8, 8, 0, 12, 12, 12, 12, 30, 30, 30, 30, - 29, 29, 29, 29, 28, 28, 28, 28, 34, 34, 34, 34, 35, 35, 35, 35, - 35, 35, 35, 0, 0, 0, 35, 35, 45, 45, 45, 45, 44, 44, 44, 44, - 44, 0, 0, 0, 43, 43, 43, 43, 46, 46, 46, 46, 31, 31, 31, 31, - 32, 32, 0, 0, 32, 0, 32, 32, 32, 32, 32, 32, 48, 48, 48, 48, - 52, 52, 52, 52, 58, 58, 58, 58, 54, 54, 54, 54, 91, 91, 91, 91, - 62, 62, 62, 62, 76, 76, 76, 76, 93, 93, 93, 93, 70, 70, 70, 70, - 73, 73, 73, 73, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, - 0, 1, 0, 0, 1, 1, 0, 0, 19, 19, 9, 9, 9, 9, 9, 6, - 19, 9, 9, 9, 9, 9, 19, 19, 9, 9, 9, 19, 6, 19, 19, 19, - 19, 19, 19, 9, 0, 0, 0, 19, 0, 0, 9, 0, 0, 0, 19, 19, - 27, 27, 27, 27, 56, 56, 56, 56, 61, 61, 61, 61, 13, 13, 13, 13, - 0, 13, 0, 13, 0, 13, 13, 13, 13, 13, 1, 1, 1, 1, 12, 12, - 0, 15, 15, 15, 15, 15, 15, 15, 15, 1, 1, 0, 0, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 0, 26, 26, 26, 26, 26, 12, 12, 12, - 12, 12, 12, 0, 39, 39, 39, 39, 86, 86, 86, 86, 77, 77, 77, 77, - 79, 79, 79, 79, 60, 60, 60, 60, 65, 65, 65, 65, 75, 75, 75, 75, - 69, 69, 69, 69, 69, 69, 0, 69, 74, 74, 74, 74, 84, 84, 84, 84, - 84, 84, 84, 0, 68, 68, 68, 68, 92, 92, 92, 92, 87, 87, 87, 87, - 19, 9, 19, 19, 19, 19, 0, 0, 2, 2, 2, 2, 19, 19, 19, 4, - 3, 3, 0, 0, 1, 1, 6, 6, 0, 0, 17, 17, 17, 17, 0, 0, - 49, 49, 49, 49, 0, 1, 1, 1, 71, 71, 71, 71, 67, 67, 67, 67, - 42, 42, 42, 42, 41, 41, 41, 41,118,118,118,118, 53, 53, 53, 53, - 59, 59, 59, 59, 40, 40, 40, 40, 51, 51, 51, 51, 50, 50, 50, 50, - 135,135,135,135,106,106,106,106,104,104,104,104,161,161,161,161, + 219,219,219,219,220,220,220,220,221,221,221,221,222,222,222,222, + 223,223,223,223,224,224,224,224,225,225,225,225,226,226,226,226, + 227,227,227,227,228,229,229,229,230,230,230,230,229,229,229,229, + 231,106,106,106,232,106,106,106,106,233,109,109,234,234,234,234, + 235,235,235,235, 0,236, 86, 0, 0, 0,236, 7, 82,138, 7, 0, + 0, 0,237, 86,238,238,238,238,239,239,239,239,240,240,240,240, + 241,241,241,241,242,242,242,242,243,243,243,243,244,244,244,244, + 245,245,245,245,246, 0, 0, 0, 0, 0, 0, 0, 0, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 0, 0, 0, 19, 0, 19, 0, 0, 0, + 0, 0, 26, 26, 1, 1, 1, 1, 9, 9, 9, 9, 0, 9, 9, 9, + 9, 9, 0, 9, 9, 0, 9, 0, 9, 9, 55, 55, 55, 55, 55, 55, + 6, 6, 6, 6, 6, 1, 1, 6, 6, 4, 4, 4, 4, 4, 4, 4, + 4, 14, 14, 14, 14, 14, 14, 14, 3, 3, 3, 3, 3, 0, 3, 3, + 0, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 1, 1, 1, 3, 3, + 1, 3, 3, 3, 37, 37, 37, 37, 38, 38, 38, 38, 64, 64, 64, 64, + 90, 90, 90, 90, 95, 95, 95, 95, 3, 3, 0, 3, 7, 7, 7, 7, + 7, 1, 1, 1, 1, 7, 7, 7, 0, 0, 7, 7, 5, 5, 5, 5, + 11, 11, 11, 11, 10, 10, 10, 10, 21, 21, 21, 21, 22, 22, 22, 22, + 23, 23, 23, 23, 16, 16, 16, 16, 20, 20, 20, 20, 36, 36, 36, 36, + 24, 24, 24, 24, 24, 24, 24, 0, 18, 18, 18, 18, 25, 25, 25, 25, + 25, 0, 0, 0, 0, 25, 25, 25, 33, 33, 33, 33, 8, 8, 8, 8, + 8, 8, 8, 0, 12, 12, 12, 12, 30, 30, 30, 30, 29, 29, 29, 29, + 28, 28, 28, 28, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 0, + 0, 0, 35, 35, 45, 45, 45, 45, 44, 44, 44, 44, 44, 0, 0, 0, + 43, 43, 43, 43, 46, 46, 46, 46, 31, 31, 31, 31, 32, 32, 0, 0, + 32, 0, 32, 32, 32, 32, 32, 32, 48, 48, 48, 48, 52, 52, 52, 52, + 58, 58, 58, 58, 54, 54, 54, 54, 91, 91, 91, 91, 62, 62, 62, 62, + 76, 76, 76, 76, 93, 93, 93, 93, 70, 70, 70, 70, 73, 73, 73, 73, + 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, + 1, 1, 0, 0, 19, 19, 9, 9, 9, 9, 9, 6, 19, 9, 9, 9, + 9, 9, 19, 19, 9, 9, 9, 19, 6, 19, 19, 19, 19, 19, 19, 9, + 0, 0, 0, 19, 0, 0, 9, 0, 0, 0, 19, 19, 27, 27, 27, 27, + 56, 56, 56, 56, 61, 61, 61, 61, 13, 13, 13, 13, 0, 13, 0, 13, + 0, 13, 13, 13, 13, 13, 1, 1, 1, 1, 12, 12, 0, 15, 15, 15, + 15, 15, 15, 15, 15, 1, 1, 0, 0, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 0, 26, 26, 26, 26, 26, 12, 12, 12, 12, 12, 12, 0, + 39, 39, 39, 39, 86, 86, 86, 86, 77, 77, 77, 77, 79, 79, 79, 79, + 60, 60, 60, 60, 65, 65, 65, 65, 75, 75, 75, 75, 69, 69, 69, 69, + 69, 69, 0, 69, 74, 74, 74, 74, 84, 84, 84, 84, 84, 84, 84, 0, + 68, 68, 68, 68, 92, 92, 92, 92, 87, 87, 87, 87, 19, 9, 19, 19, + 19, 19, 0, 0, 2, 2, 2, 2, 19, 19, 19, 4, 3, 3, 0, 0, + 1, 1, 6, 6, 0, 0, 17, 17, 17, 17, 0, 0, 49, 49, 49, 49, + 0, 1, 1, 1, 71, 71, 71, 71, 67, 67, 67, 67, 42, 42, 42, 42, + 41, 41, 41, 41,118,118,118,118, 53, 53, 53, 53, 59, 59, 59, 59, + 40, 40, 40, 40, 51, 51, 51, 51, 50, 50, 50, 50,135,135,135,135, + 106,106,106,106,104,104,104,104,161,161,161,161,170,170,170,170, 110,110,110,110, 47, 47, 47, 47, 81, 81, 81, 81,120,120,120,120, 116,116,116,116,128,128,128,128, 66, 66, 66, 66, 72, 72, 72, 72, 98, 98, 98, 98, 97, 97, 97, 97, 57, 57, 57, 57, 88, 88, 88, 88, 117,117,117,117,112,112,112,112, 78, 78, 78, 78, 83, 83, 83, 83, 82, 82, 82, 82,122,122,122,122, 89, 89, 89, 89,130,130,130,130, - 144,144,144,144,156,156,156,156,156, 3, 3, 3,147,147,147,147, - 148,148,148,148,158,158,158,158,153,153,153,153,149,149,149,149, - 94, 94, 94, 94, 85, 85, 85, 85,101,101,101,101, 96, 96, 96, 96, - 111,111,111,111,100,100,100,100,100, 36, 36, 36,108,108,108,108, - 129,129,129,129,109,109,109,109,107,107,107,107,107,107,107, 1, - 137,137,137,137,124,124,124,124,123,123,123,123,114,114,114,114, - 102,102,102,102,126,126,126,126,142,142,142,142,125,125,125,125, - 154,154,154,154,150,150,150,150,141,141,141,141,140,140,140,140, - 121,121,121,121,133,133,133,133,134,134,134,134,138,138,138,138, - 143,143,143,143,145,145,145,145,163,163,163,163, 63, 63, 63, 63, - 157,157,157,157, 80, 80, 80, 80,127,127,127,127,115,115,115,115, - 159,159,159,159,103,103,103,103,119,119,119,119,146,146,146,146, - 99, 99, 99, 99,136,139, 13, 13,155,155,155,155,136,136,136,136, - 17, 15, 15, 15, 17, 17, 15, 15, 15, 17, 17, 17,139,139,139,139, - 105,105,105,105, 0, 0, 0, 1, 0, 0, 1, 1,131,131,131,131, - 151,151,151,151,160,160,160,160,152,152,152,152,164,164,164,164, - 113,113,113,113,132,132,132,132, 15, 0, 0, 0, 0, 1, 2, 3, - 4, 5, 6, 7, 8, 9, 9, 9, 9, 10, 9, 11, 12, 13, 9, 9, - 9, 14, 9, 9, 15, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 144,144,144,144,165,165,165,165,156,156,156,156,156,156, 3, 3, + 147,147,147,147,148,148,148,148,158,158,158,158,153,153,153,153, + 149,149,149,149, 94, 94, 94, 94, 85, 85, 85, 85,101,101,101,101, + 96, 96, 96, 96,111,111,111,111,100,100,100,100,100, 36, 36, 36, + 108,108,108,108,129,129,129,129,109,109,109,109,107,107,107,107, + 107,107,107, 1,171,171,171,171,137,137,137,137,124,124,124,124, + 123,123,123,123,114,114,114,114,102,102,102,102,126,126,126,126, + 142,142,142,142,125,125,125,125,154,154,154,154,150,150,150,150, + 141,141,141,141,140,140,140,140,121,121,121,121,169,169,169,169, + 133,133,133,133,134,134,134,134,138,138,138,138,143,143,143,143, + 145,145,145,145,163,163,163,163, 63, 63, 63, 63,157,157,157,157, + 80, 80, 80, 80,127,127,127,127,166,166,166,166,115,115,115,115, + 159,159,159,159,103,103,103,103,119,119,119,119,167,167,167,167, + 146,146,146,146, 99, 99, 99, 99,136,139, 13, 13,155,155,155,155, + 136,136,136,136, 17, 15, 15, 15, 17, 17, 15, 15, 15, 17, 17, 17, + 139,139,139,139,105,105,105,105, 0, 0, 0, 1, 0, 0, 1, 1, + 131,131,131,131,151,151,151,151,160,160,160,160,152,152,152,152, + 164,164,164,164,168,168,168,168,113,113,113,113,132,132,132,132, + 15, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, + 9, 10, 9, 11, 12, 13, 9, 9, 9, 14, 9, 9, 15, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 16, 17, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 18, 19, 20, 9, 21, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 16, 17, 9, 9, 9, 9, 18, 9, 9, 9, 9, 9, 19, 20, 21, 9, + 22, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 23, 9, 9, 9, 9, 9, 24, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 25, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 22, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -5223,60 +5451,66 @@ _hb_ucd_u8[13386] = 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 23, 24, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 0, 0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, - 0, 24, 25, 26, 27, 28, 29, 30, 0, 0, 31, 32, 0, 33, 0, 34, - 0, 35, 0, 0, 0, 0, 36, 37, 38, 39, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 41, 42, 0, 0, 0, 0, 0, 0, 0, 0, + 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 23, 0, 0, 24, 25, 26, 27, 28, 29, 30, + 0, 0, 31, 32, 0, 33, 0, 34, 0, 35, 0, 0, 0, 0, 36, 37, + 38, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 44, 0, 45, - 0, 0, 0, 0, 0, 0, 46, 47, 0, 0, 0, 0, 0, 48, 0, 49, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, - 0, 0, 0, 52, 0, 0, 53, 0, 0, 0, 0, 0, 0, 0, 54, 0, - 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, 0, 56, 0, - 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 59, - 60, 61, 62, 63, 64, 65, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 43, 44, 0, 45, 0, 0, 0, 0, 0, 0, 46, 47, + 0, 0, 0, 0, 0, 48, 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 53, 0, + 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, 55, 0, + 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 58, 59, 60, 61, 62, 63, 64, 65, 0, 0, + 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 68, 0, 69, 70, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 72, 73, 74, 75, 76, - 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, - 93, 94, 95, 96, 97, 98, 99,100,101,102,103, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,104, 0, 0, 0, - 0, 0, 0,105,106, 0,107, 0, 0, 0,108, 0,109, 0,110, 0, - 111,112,113, 0,114, 0, 0, 0,115, 0, 0, 0,116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,117, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,118,119, - 120,121, 0,122,123,124,125,126, 0,127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128,129,130,131,132,133, - 134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149, - 150,151,152,153,154,155,156,157, 0, 0, 0,158,159,160,161, 0, + 0, 0, 67, 68, 0, 69, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,100, + 101,102,103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,104, 0, 0, 0, 0, 0, 0,105,106, 0,107, 0, + 0, 0,108, 0,109, 0,110, 0,111,112,113, 0,114, 0, 0, 0, + 115, 0, 0, 0,116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0,162,163, 0, 0, 0, 0, 0, 0, 0,164, 0, 0, 0, + 0, 0, 0, 0, 0, 0,118,119,120,121, 0,122,123,124,125,126, + 0,127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,165, 0, + 0, 0,128,129,130,131,132,133,134,135,136,137,138,139,140,141, + 142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157, + 0, 0, 0,158,159,160,161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,166, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,168, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,162, 0, + 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,164,165, 0, 0, 0, + 0, 0, 0, 0,166, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,169, - 170, 0, 0, 0, 0,171,172, 0, 0, 0,173,174,175,176,177,178, - 179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194, - 195,196,197,198,199,200,201,202,203,204,205,206, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,167, 0, 0, 0,168,169, 0, 0,170, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,171, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, + 0, 0, 0, 0, 0,173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,174, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,175, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,176,177, 0, 0, 0, 0,178,179, 0, + 0, 0,180,181,182,183,184,185,186,187,188,189,190,191,192,193, + 194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209, + 210,211,212,213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, + 3, 4, }; static const uint16_t -_hb_ucd_u16[4920] = +_hb_ucd_u16[5080] = { 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 10, 11, 12, 13, 13, 13, 14, 15, 13, 13, 16, 17, 18, 19, 20, 21, 22, 13, 23, @@ -5303,82 +5537,85 @@ _hb_ucd_u16[4920] = 47, 47, 165, 166, 167, 47, 47, 47, 47, 47, 47, 47, 47, 168, 146, 146, 47, 169, 47, 47, 47, 170, 171, 172, 160, 160, 173, 174, 32, 32, 32, 32, 175, 47, 47, 176, 177, 122, 178, 179, 180, 47, 181, 61, 47, 47, 182, 183, - 47, 47, 184, 185, 186, 61, 47, 187, 11, 9, 9, 9, 66, 188, 189, 190, - 11, 11, 191, 27, 27, 27, 192, 193, 11, 194, 27, 27, 32, 32, 32, 32, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 195, 13, 13, 13, 13, 13, 13, - 196, 196, 196, 196, 196, 197, 196, 11, 198, 198, 198, 199, 200, 201, 201, 200, - 202, 203, 204, 205, 206, 207, 208, 209, 210, 27, 211, 211, 211, 212, 213, 32, - 214, 215, 216, 217, 218, 145, 219, 219, 220, 221, 222, 146, 223, 224, 146, 225, - 226, 226, 226, 226, 226, 226, 226, 226, 227, 146, 228, 146, 146, 146, 146, 229, - 146, 230, 226, 231, 146, 232, 233, 146, 146, 146, 146, 146, 146, 146, 145, 145, - 145, 234, 146, 146, 146, 146, 235, 145, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 236, 237, 146, 146, 238, 146, 146, 146, 146, 146, 146, 239, 146, - 146, 146, 146, 146, 146, 146, 240, 241, 145, 242, 146, 146, 243, 226, 244, 226, - 245, 246, 226, 226, 226, 247, 226, 248, 146, 146, 146, 226, 249, 146, 146, 146, - 9, 9, 9, 11, 11, 11, 250, 251, 13, 13, 13, 13, 13, 13, 252, 253, - 11, 11, 11, 47, 47, 47, 254, 255, 47, 47, 47, 47, 47, 47, 32, 32, - 256, 257, 258, 259, 260, 261, 262, 262, 263, 264, 265, 266, 267, 47, 47, 47, - 47, 268, 148, 47, 47, 47, 47, 269, 47, 270, 47, 47, 146, 146, 146, 47, - 146, 146, 271, 146, 272, 273, 146, 146, 271, 146, 146, 273, 146, 146, 146, 146, - 47, 47, 47, 47, 146, 146, 146, 146, 47, 274, 47, 47, 47, 47, 47, 47, - 47, 146, 146, 146, 146, 47, 47, 187, 275, 47, 61, 47, 13, 13, 276, 277, - 13, 278, 47, 47, 47, 47, 279, 280, 31, 281, 282, 283, 13, 13, 13, 284, - 285, 286, 287, 288, 289, 290, 11, 291, 292, 47, 293, 294, 47, 47, 47, 295, - 296, 47, 47, 297, 298, 160, 32, 299, 61, 47, 300, 47, 301, 302, 47, 47, - 72, 47, 47, 303, 304, 305, 306, 61, 47, 47, 307, 308, 309, 310, 47, 311, - 47, 47, 47, 312, 58, 313, 314, 315, 47, 47, 47, 11, 11, 316, 317, 11, - 11, 11, 11, 11, 47, 47, 318, 160, 319, 319, 319, 319, 319, 319, 319, 319, - 320, 320, 320, 320, 320, 320, 320, 320, 11, 321, 322, 47, 47, 47, 47, 47, - 47, 47, 47, 323, 31, 324, 47, 47, 47, 47, 47, 325, 146, 47, 47, 47, - 47, 47, 47, 47, 326, 146, 146, 327, 32, 328, 32, 329, 330, 331, 332, 47, - 47, 47, 47, 47, 47, 47, 47, 333, 334, 2, 3, 4, 5, 335, 336, 337, - 47, 338, 47, 47, 47, 47, 339, 340, 341, 145, 145, 342, 219, 219, 219, 343, - 344, 146, 146, 146, 146, 146, 146, 345, 346, 346, 346, 346, 346, 346, 346, 346, - 47, 47, 47, 47, 47, 47, 347, 145, 47, 47, 348, 47, 349, 47, 47, 60, - 47, 350, 47, 47, 47, 351, 219, 219, 9, 9, 147, 11, 11, 47, 47, 47, - 47, 47, 160, 9, 9, 147, 11, 11, 47, 47, 47, 47, 47, 47, 350, 9, - 9, 352, 11, 11, 11, 11, 11, 11, 27, 27, 27, 27, 27, 27, 27, 27, - 47, 47, 47, 47, 47, 353, 47, 354, 47, 47, 355, 145, 145, 145, 47, 356, - 47, 357, 47, 350, 66, 66, 66, 66, 47, 47, 47, 358, 145, 145, 145, 145, - 359, 47, 47, 360, 145, 66, 47, 361, 47, 362, 145, 145, 363, 47, 364, 66, - 47, 47, 47, 365, 47, 366, 47, 366, 47, 365, 144, 145, 145, 145, 145, 145, - 9, 9, 9, 9, 11, 11, 11, 367, 47, 47, 368, 160, 160, 160, 160, 160, - 145, 145, 145, 145, 145, 145, 145, 145, 47, 47, 369, 47, 47, 47, 47, 143, - 47, 362, 370, 47, 60, 371, 66, 47, 372, 66, 66, 47, 373, 145, 47, 47, - 374, 47, 47, 360, 375, 376, 377, 378, 180, 47, 47, 379, 380, 47, 47, 160, - 97, 47, 381, 382, 383, 47, 47, 384, 180, 47, 47, 385, 386, 387, 388, 145, - 47, 47, 389, 390, 359, 32, 32, 32, 47, 47, 365, 47, 47, 391, 172, 160, - 92, 47, 47, 113, 392, 393, 394, 32, 47, 47, 47, 395, 396, 397, 47, 47, - 47, 47, 47, 398, 399, 160, 160, 160, 47, 47, 400, 401, 402, 403, 32, 32, - 47, 47, 47, 404, 405, 160, 66, 66, 47, 47, 406, 407, 160, 160, 160, 160, - 47, 143, 408, 409, 47, 47, 47, 47, 47, 47, 389, 410, 66, 66, 66, 66, - 9, 9, 9, 9, 11, 11, 128, 411, 47, 47, 47, 412, 413, 160, 160, 160, - 47, 47, 47, 47, 47, 414, 415, 416, 417, 47, 47, 418, 419, 420, 47, 47, - 421, 422, 66, 47, 47, 47, 47, 47, 66, 66, 66, 66, 66, 66, 66, 66, - 47, 47, 400, 423, 424, 128, 145, 425, 47, 156, 426, 427, 32, 32, 32, 32, - 47, 47, 47, 359, 428, 160, 47, 47, 429, 430, 160, 160, 160, 160, 160, 160, - 47, 47, 47, 47, 47, 47, 47, 431, 432, 47, 47, 433, 434, 160, 160, 160, - 47, 47, 47, 47, 145, 435, 436, 437, 219, 219, 219, 219, 219, 219, 219, 66, - 47, 47, 47, 47, 47, 47, 47, 424, 47, 47, 47, 208, 438, 32, 32, 32, - 47, 47, 47, 47, 47, 47, 305, 47, 47, 47, 47, 47, 160, 47, 47, 439, - 47, 47, 47, 440, 441, 442, 443, 47, 9, 9, 9, 9, 9, 9, 11, 11, - 145, 444, 66, 66, 66, 66, 66, 66, 47, 47, 47, 47, 391, 445, 416, 416, - 446, 447, 27, 27, 27, 27, 448, 416, 47, 449, 208, 208, 208, 208, 208, 208, - 32, 32, 32, 32, 32, 146, 146, 146, 146, 146, 146, 146, 146, 146, 450, 451, - 452, 146, 453, 146, 146, 146, 146, 146, 146, 146, 146, 146, 454, 146, 146, 146, - 9, 455, 11, 456, 457, 11, 196, 9, 458, 459, 9, 460, 11, 9, 455, 11, - 456, 457, 11, 196, 9, 458, 459, 9, 460, 11, 9, 455, 11, 456, 457, 11, - 196, 9, 458, 459, 9, 460, 11, 9, 455, 11, 196, 9, 461, 462, 463, 464, - 11, 465, 9, 466, 467, 468, 469, 11, 470, 9, 471, 11, 472, 160, 160, 160, - 32, 32, 32, 473, 32, 32, 474, 475, 476, 477, 32, 32, 32, 32, 32, 32, - 478, 11, 11, 11, 11, 11, 11, 11, 32, 32, 32, 27, 27, 27, 27, 27, - 32, 32, 32, 32, 32, 32, 32, 32, 47, 47, 47, 479, 480, 146, 146, 146, - 47, 47, 481, 32, 47, 47, 482, 483, 47, 47, 47, 47, 47, 47, 484, 160, - 47, 47, 47, 47, 355, 32, 32, 32, 9, 9, 458, 11, 485, 305, 66, 66, - 145, 145, 486, 487, 145, 145, 145, 145, 145, 145, 488, 145, 145, 145, 145, 145, - 47, 47, 47, 47, 47, 47, 47, 226, 489, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 490, 146, 146, 146, 146, 146, 146, 146, 160, - 208, 208, 208, 208, 208, 208, 208, 208, 0, 0, 0, 0, 0, 0, 0, 0, + 47, 47, 184, 185, 186, 61, 47, 187, 188, 9, 9, 9, 66, 189, 190, 191, + 11, 11, 192, 27, 27, 27, 193, 194, 11, 195, 27, 27, 32, 32, 32, 32, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 196, 13, 13, 13, 13, 13, 13, + 197, 197, 197, 197, 197, 198, 197, 11, 199, 199, 199, 200, 201, 202, 202, 201, + 203, 204, 205, 206, 207, 208, 209, 210, 211, 27, 212, 212, 212, 213, 214, 32, + 215, 216, 217, 218, 219, 145, 220, 220, 221, 222, 223, 146, 224, 225, 146, 226, + 227, 227, 227, 227, 227, 227, 227, 227, 228, 146, 229, 146, 146, 146, 146, 230, + 146, 231, 227, 232, 146, 233, 234, 146, 146, 146, 146, 146, 146, 146, 145, 145, + 145, 235, 146, 146, 146, 146, 236, 145, 146, 146, 146, 146, 146, 146, 146, 146, + 146, 146, 146, 237, 238, 146, 146, 239, 146, 146, 146, 146, 146, 146, 240, 146, + 146, 146, 146, 146, 146, 146, 241, 242, 145, 243, 146, 146, 244, 227, 245, 227, + 246, 247, 227, 227, 227, 248, 227, 249, 146, 146, 146, 227, 250, 146, 146, 146, + 9, 9, 9, 11, 11, 11, 251, 252, 13, 13, 13, 13, 13, 13, 253, 254, + 11, 11, 11, 47, 47, 47, 255, 256, 47, 47, 47, 47, 47, 47, 32, 32, + 257, 258, 259, 260, 261, 262, 263, 263, 264, 265, 266, 267, 268, 47, 47, 47, + 47, 269, 148, 47, 47, 47, 47, 270, 47, 271, 47, 47, 146, 146, 146, 47, + 146, 146, 272, 146, 273, 274, 146, 146, 272, 146, 146, 274, 146, 146, 146, 146, + 47, 47, 47, 47, 146, 146, 146, 146, 47, 275, 47, 47, 47, 47, 47, 47, + 47, 146, 146, 146, 146, 47, 47, 187, 276, 47, 61, 47, 13, 13, 277, 278, + 13, 279, 47, 47, 47, 47, 280, 281, 31, 282, 283, 284, 13, 13, 13, 285, + 286, 287, 288, 289, 290, 291, 9, 292, 293, 47, 294, 295, 47, 47, 47, 296, + 297, 47, 47, 298, 299, 160, 32, 300, 61, 47, 301, 47, 302, 303, 47, 47, + 72, 47, 47, 304, 305, 306, 307, 61, 47, 47, 308, 309, 310, 311, 47, 312, + 47, 47, 47, 313, 58, 314, 315, 316, 47, 47, 47, 11, 11, 317, 318, 11, + 11, 11, 11, 11, 47, 47, 319, 160, 320, 320, 320, 320, 320, 320, 320, 320, + 321, 321, 321, 321, 321, 321, 321, 321, 11, 322, 323, 47, 47, 47, 47, 47, + 47, 47, 47, 324, 31, 325, 47, 47, 47, 47, 47, 326, 146, 47, 47, 47, + 47, 47, 47, 47, 327, 146, 146, 328, 32, 329, 32, 330, 331, 332, 333, 47, + 47, 47, 47, 47, 47, 47, 47, 334, 335, 2, 3, 4, 5, 336, 337, 338, + 47, 339, 47, 47, 47, 47, 340, 341, 342, 145, 145, 343, 220, 220, 220, 344, + 345, 146, 146, 146, 146, 146, 146, 346, 347, 347, 347, 347, 347, 347, 347, 347, + 47, 47, 47, 47, 47, 47, 348, 145, 47, 47, 349, 47, 350, 47, 47, 60, + 47, 351, 47, 47, 47, 352, 220, 220, 9, 9, 147, 11, 11, 47, 47, 47, + 47, 47, 160, 9, 9, 147, 11, 11, 47, 47, 47, 47, 47, 47, 351, 9, + 9, 353, 11, 11, 47, 47, 47, 47, 27, 27, 27, 27, 27, 27, 27, 27, + 47, 47, 47, 47, 47, 354, 47, 355, 47, 47, 356, 145, 145, 145, 47, 357, + 47, 358, 47, 351, 66, 66, 66, 66, 47, 47, 47, 359, 145, 145, 145, 145, + 360, 47, 47, 361, 145, 66, 47, 362, 47, 363, 145, 145, 364, 47, 365, 66, + 47, 47, 47, 366, 47, 367, 47, 367, 47, 366, 144, 145, 145, 145, 145, 145, + 9, 9, 9, 9, 11, 11, 11, 368, 47, 47, 369, 160, 370, 9, 371, 11, + 372, 227, 227, 227, 227, 227, 227, 227, 145, 145, 145, 145, 145, 145, 145, 145, + 47, 47, 373, 47, 47, 47, 47, 374, 47, 363, 375, 47, 60, 376, 66, 47, + 377, 66, 66, 47, 378, 145, 47, 47, 379, 47, 47, 361, 380, 381, 382, 383, + 180, 47, 47, 384, 385, 47, 47, 160, 97, 47, 386, 387, 388, 47, 47, 389, + 180, 47, 47, 390, 391, 392, 393, 145, 47, 47, 394, 395, 360, 32, 32, 32, + 47, 47, 366, 47, 47, 396, 172, 160, 92, 47, 47, 113, 397, 398, 399, 32, + 47, 47, 47, 400, 401, 402, 403, 32, 47, 47, 47, 404, 405, 406, 47, 47, + 47, 47, 47, 407, 408, 160, 160, 160, 47, 47, 409, 410, 411, 412, 32, 32, + 47, 47, 47, 413, 414, 160, 66, 66, 47, 47, 415, 416, 160, 160, 160, 160, + 47, 417, 418, 419, 47, 47, 47, 47, 47, 47, 394, 420, 66, 66, 66, 66, + 9, 9, 9, 9, 11, 11, 128, 421, 47, 47, 47, 422, 423, 160, 160, 160, + 47, 47, 47, 47, 47, 424, 425, 426, 427, 47, 47, 428, 429, 430, 47, 47, + 431, 432, 66, 47, 47, 47, 47, 47, 66, 66, 66, 66, 66, 66, 66, 66, + 47, 47, 47, 47, 47, 47, 433, 160, 47, 47, 409, 434, 433, 128, 145, 435, + 47, 156, 436, 437, 32, 32, 32, 32, 47, 47, 47, 360, 438, 160, 47, 47, + 439, 440, 160, 160, 160, 160, 160, 160, 47, 47, 47, 47, 47, 47, 47, 441, + 442, 47, 47, 443, 444, 445, 32, 32, 47, 47, 47, 47, 145, 446, 447, 448, + 220, 220, 220, 220, 220, 220, 220, 66, 47, 47, 47, 47, 47, 47, 47, 433, + 47, 47, 47, 209, 449, 32, 47, 47, 47, 450, 451, 160, 160, 160, 160, 160, + 47, 47, 47, 47, 47, 47, 306, 47, 47, 47, 47, 47, 160, 47, 47, 452, + 47, 47, 47, 453, 454, 455, 456, 47, 27, 27, 27, 27, 457, 47, 458, 160, + 9, 9, 9, 9, 9, 9, 11, 11, 145, 459, 66, 66, 66, 66, 66, 66, + 47, 47, 47, 47, 396, 460, 426, 426, 461, 462, 27, 27, 27, 27, 463, 426, + 47, 464, 209, 209, 209, 209, 209, 209, 146, 146, 146, 146, 146, 146, 146, 160, + 32, 32, 32, 32, 32, 146, 146, 146, 146, 146, 146, 146, 146, 146, 465, 466, + 467, 146, 468, 146, 146, 146, 146, 146, 146, 146, 146, 146, 469, 146, 146, 146, + 9, 470, 11, 471, 472, 11, 197, 9, 473, 474, 9, 475, 11, 9, 470, 11, + 471, 472, 11, 197, 9, 473, 474, 9, 475, 11, 9, 470, 11, 471, 472, 11, + 197, 9, 473, 474, 9, 475, 11, 9, 470, 11, 197, 9, 476, 477, 478, 479, + 11, 480, 9, 481, 482, 483, 484, 11, 485, 9, 486, 11, 487, 160, 160, 160, + 32, 32, 32, 488, 32, 32, 489, 490, 491, 492, 32, 32, 32, 32, 32, 32, + 493, 11, 11, 11, 11, 11, 11, 11, 32, 32, 32, 27, 27, 27, 27, 27, + 32, 32, 32, 32, 32, 32, 32, 32, 47, 47, 47, 494, 495, 146, 146, 146, + 47, 47, 450, 32, 47, 47, 374, 496, 47, 47, 47, 47, 47, 47, 497, 160, + 47, 47, 47, 47, 47, 47, 450, 498, 47, 47, 47, 47, 356, 32, 32, 32, + 9, 9, 473, 11, 499, 306, 66, 66, 145, 145, 500, 501, 145, 145, 145, 145, + 145, 145, 502, 145, 145, 145, 145, 145, 47, 47, 47, 47, 47, 47, 47, 227, + 503, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 504, + 209, 209, 209, 209, 209, 209, 209, 209, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 939, 940, 941, 942, 946, 948, 0, 962, 969, 970, 971, 976,1001,1002,1003,1008, 0,1033,1040,1041,1042,1043,1047, 0, 0,1080,1081,1082,1086,1110, 0, 0,1124,1125,1126,1127,1131,1133, 0,1147, @@ -5541,16 +5778,23 @@ _hb_ucd_u16[4920] = 0, 0,1602,1603,1934,1935,1574,1575,1576,1577,1579,1580,1581,1583,1584, 0, 1585,1587,1588,1589,1591, 0,1592, 0,1593,1594, 0,1595,1596, 0,1598,1599, 1600,1601,1604,1582,1578,1590,1597, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0,1936, 0,1937, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0,1938, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0,1939,1940, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0,1941,1942, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0,1944,1943, 0,1945, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0,1946,1947, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1948, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0,1949,1950,1951,1952,1953,1954,1955, 0, 0, 0, + 0,1936, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1937, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1938, 0,1939, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1940, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1941,1942, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1943,1944, 0, 0, 0, 0, 0, 0,1945, 0,1946, 0, 0, + 0, 0, 0, 0, 0, 0,1947, 0, 0,1948, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1950, 0,1949, + 1951, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1953,1952, 0,1954, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1955,1956, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1957, 0, 0, 0, 0, 0, 0, 0, 0,1958,1961,1959,1965,1960,1962,1964, + 1963, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1967,1966,1968, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1969,1970,1971,1972,1973,1974,1975, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0,1956,1957,1958,1960,1959,1961, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1976,1977,1978,1980,1979,1981, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 104, 107, 826, 114, 118, 119, 121, 123, 124, 127, 125, 34, 830, 130, 131, 132, 137, 827, 35, 133, 139, 829, 142, 143, 112, 144, 145, 924, 151, 152, 37, 157, 158, 159, 160, 38, 165, 166, 169, @@ -5601,12 +5845,12 @@ _hb_ucd_i16[92] = static inline uint_fast8_t _hb_ucd_gc (unsigned u) { - return u<1114112u?_hb_ucd_u8[5096+(((_hb_ucd_u8[1168+(((_hb_ucd_u16[((_hb_ucd_u8[544+(((_hb_ucd_u8[u>>1>>3>>3>>4])<<4)+((u>>1>>3>>3)&15u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:2; + return u<1114112u?_hb_ucd_u8[5208+(((_hb_ucd_u8[1168+(((_hb_ucd_u16[((_hb_ucd_u8[544+(((_hb_ucd_u8[u>>1>>3>>3>>4])<<4)+((u>>1>>3>>3)&15u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:2; } static inline uint_fast8_t _hb_ucd_ccc (unsigned u) { - return u<125259u?_hb_ucd_u8[7054+(((_hb_ucd_u8[6498+(((_hb_ucd_u8[6038+(((_hb_ucd_u8[5686+(((_hb_ucd_u8[5440+(u>>2>>2>>2>>3)])<<3)+((u>>2>>2>>2)&7u))])<<2)+((u>>2>>2)&3u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:0; + return u<125259u?_hb_ucd_u8[7206+(((_hb_ucd_u8[6638+(((_hb_ucd_u8[6162+(((_hb_ucd_u8[5802+(((_hb_ucd_u8[5556+(u>>2>>2>>2>>3)])<<3)+((u>>2>>2>>2)&7u))])<<2)+((u>>2>>2)&3u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:0; } static inline unsigned _hb_ucd_b4 (const uint8_t* a, unsigned i) @@ -5616,17 +5860,17 @@ _hb_ucd_b4 (const uint8_t* a, unsigned i) static inline int_fast16_t _hb_ucd_bmg (unsigned u) { - return u<65380u?_hb_ucd_i16[((_hb_ucd_u8[7946+(((_hb_ucd_u8[7714+(((_hb_ucd_u8[7618+(((_hb_ucd_b4(7554+_hb_ucd_u8,u>>1>>2>>3>>3))<<3)+((u>>1>>2>>3)&7u))])<<3)+((u>>1>>2)&7u))])<<2)+((u>>1)&3u))])<<1)+((u)&1u)]:0; + return u<65380u?_hb_ucd_i16[((_hb_ucd_u8[8098+(((_hb_ucd_u8[7866+(((_hb_ucd_u8[7770+(((_hb_ucd_b4(7706+_hb_ucd_u8,u>>1>>2>>3>>3))<<3)+((u>>1>>2>>3)&7u))])<<3)+((u>>1>>2)&7u))])<<2)+((u>>1)&3u))])<<1)+((u)&1u)]:0; } static inline uint_fast8_t _hb_ucd_sc (unsigned u) { - return u<918016u?_hb_ucd_u8[11244+(((_hb_ucd_u8[10280+(((_hb_ucd_u8[9292+(((_hb_ucd_u8[8612+(((_hb_ucd_u8[8308+(((_hb_ucd_u8[8194+(u>>2>>2>>2>>3>>4)])<<4)+((u>>2>>2>>2>>3)&15u))])<<3)+((u>>2>>2>>2)&7u))])<<2)+((u>>2>>2)&3u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:2; + return u<918016u?_hb_ucd_u8[11464+(((_hb_ucd_u8[10472+(((_hb_ucd_u8[9452+(((_hb_ucd_u8[8764+(((_hb_ucd_u8[8460+(((_hb_ucd_u8[8346+(u>>2>>2>>2>>3>>4)])<<4)+((u>>2>>2>>2>>3)&15u))])<<3)+((u>>2>>2>>2)&7u))])<<2)+((u>>2>>2)&3u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:2; } static inline uint_fast16_t _hb_ucd_dm (unsigned u) { - return u<195102u?_hb_ucd_u16[1608+(((_hb_ucd_u8[12586+(((_hb_ucd_u8[12204+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:0; + return u<195102u?_hb_ucd_u16[1656+(((_hb_ucd_u8[12834+(((_hb_ucd_u8[12452+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:0; } #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-unicode-emoji-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-unicode-emoji-table.hh index e607e8ca8290d..4bc8d64c28f8a 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-unicode-emoji-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-unicode-emoji-table.hh @@ -7,13 +7,13 @@ * on file with this header: * * # emoji-data.txt - * # Date: 2023-02-01, 02:22:54 GMT - * # © 2023 Unicode®, Inc. + * # Date: 2024-05-01, 21:25:24 GMT + * # © 2024 Unicode®, Inc. * # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. - * # For terms of use, see https://www.unicode.org/terms_of_use.html + * # For terms of use and license, see https://www.unicode.org/terms_of_use.html * # * # Emoji Data for UTS #51 - * # Used with Emoji Version 15.1 and subsequent minor revisions (if any) + * # Used with Emoji Version 16.0 and subsequent minor revisions (if any) * # * # For documentation and usage, see https://www.unicode.org/reports/tr51 */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-utf.hh b/src/java.desktop/share/native/libharfbuzz/hb-utf.hh index 9175df265b9d1..7fd3bf8828b21 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-utf.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-utf.hh @@ -458,19 +458,21 @@ struct hb_ascii_t template static inline const typename utf_t::codepoint_t * hb_utf_offset_to_pointer (const typename utf_t::codepoint_t *start, + const typename utf_t::codepoint_t *text, + unsigned text_len, signed offset) { hb_codepoint_t unicode; while (offset-- > 0) start = utf_t::next (start, - start + utf_t::max_len, + text + text_len, &unicode, HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT); while (offset++ < 0) start = utf_t::prev (start, - start - utf_t::max_len, + text, &unicode, HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-vector.hh b/src/java.desktop/share/native/libharfbuzz/hb-vector.hh index 001789a3037b4..cd27da96648af 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-vector.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-vector.hh @@ -37,6 +37,8 @@ template struct hb_vector_t { + static constexpr bool realloc_move = true; + typedef Type item_t; static constexpr unsigned item_size = hb_static_size (Type); using array_t = typename std::conditional, hb_array_t>::type; @@ -51,32 +53,29 @@ struct hb_vector_t } template - hb_vector_t (const Iterable &o) : hb_vector_t () + explicit hb_vector_t (const Iterable &o) : hb_vector_t () { - auto iter = hb_iter (o); - if (iter.is_random_access_iterator || iter.has_fast_len) - alloc (hb_len (iter), true); - hb_copy (iter, *this); + extend (o); } hb_vector_t (const hb_vector_t &o) : hb_vector_t () { - alloc (o.length, true); + alloc_exact (o.length); if (unlikely (in_error ())) return; copy_array (o.as_array ()); } hb_vector_t (array_t o) : hb_vector_t () { - alloc (o.length, true); + alloc_exact (o.length); if (unlikely (in_error ())) return; copy_array (o); } hb_vector_t (c_array_t o) : hb_vector_t () { - alloc (o.length, true); + alloc_exact (o.length); if (unlikely (in_error ())) return; copy_array (o); } - hb_vector_t (hb_vector_t &&o) + hb_vector_t (hb_vector_t &&o) noexcept { allocated = o.allocated; length = o.length; @@ -85,6 +84,41 @@ struct hb_vector_t } ~hb_vector_t () { fini (); } + template + void extend (const Iterable &o) + { + auto iter = hb_iter (o); + if (iter.is_random_access_iterator || iter.has_fast_len) + { + if (unlikely (!alloc (hb_len (iter), true))) + return; + unsigned count = hb_len (iter); + for (unsigned i = 0; i < count; i++) + push_has_room (*iter++); + } + while (iter) + { + if (unlikely (!alloc (length + 1))) + return; + unsigned room = allocated - length; + for (unsigned i = 0; i < room && iter; i++) + push_has_room (*iter++); + } + } + void extend (array_t o) + { + alloc (length + o.length); + if (unlikely (in_error ())) return; + copy_array (o); + } + void extend (c_array_t o) + { + alloc (length + o.length); + if (unlikely (in_error ())) return; + copy_array (o); + } + public: int allocated = 0; /* < 0 means allocation failed. */ unsigned int length = 0; @@ -120,7 +154,7 @@ struct hb_vector_t resize (0); } - friend void swap (hb_vector_t& a, hb_vector_t& b) + friend void swap (hb_vector_t& a, hb_vector_t& b) noexcept { hb_swap (a.allocated, b.allocated); hb_swap (a.length, b.length); @@ -130,14 +164,15 @@ struct hb_vector_t hb_vector_t& operator = (const hb_vector_t &o) { reset (); - alloc (o.length, true); + alloc_exact (o.length); if (unlikely (in_error ())) return *this; + length = 0; copy_array (o.as_array ()); return *this; } - hb_vector_t& operator = (hb_vector_t &&o) + hb_vector_t& operator = (hb_vector_t &&o) noexcept { hb_swap (*this, o); return *this; @@ -216,6 +251,10 @@ struct hb_vector_t // reference to it. return std::addressof (Crap (Type)); + return push_has_room (std::forward (args)...); + } + template Type *push_has_room (Args&&... args) + { /* Emplace. */ Type *p = std::addressof (arrayZ[length++]); return new (p) Type (std::forward (args)...); @@ -268,10 +307,9 @@ struct hb_vector_t } return new_array; } - /* Specialization for hb_vector_t> to speed up. */ + /* Specialization for types that can be moved using realloc(). */ template ) || - hb_is_same (T, hb_array_t ))> + hb_enable_if (T::realloc_move)> Type * realloc_vector (unsigned new_allocated, hb_priority<1>) { @@ -310,18 +348,23 @@ struct hb_vector_t length = size; } + template + void + copy_array (hb_array_t other) + { + assert ((int) (length + other.length) <= allocated); + hb_memcpy ((void *) (arrayZ + length), (const void *) other.arrayZ, other.length * item_size); + length += other.length; + } template void copy_array (hb_array_t other) { - length = other.length; - if (!HB_OPTIMIZE_SIZE_VAL && sizeof (T) >= sizeof (long long)) - /* This runs faster because of alignment. */ - for (unsigned i = 0; i < length; i++) - arrayZ[i] = other.arrayZ[i]; - else - hb_memcpy ((void *) arrayZ, (const void *) other.arrayZ, length * item_size); + assert ((int) (length + other.length) <= allocated); + hb_memcpy ((void *) (arrayZ + length), (const void *) other.arrayZ, other.length * item_size); + length += other.length; } template other) { - length = 0; - while (length < other.length) - { - length++; - new (std::addressof (arrayZ[length - 1])) Type (other.arrayZ[length - 1]); - } + assert ((int) (length + other.length) <= allocated); + for (unsigned i = 0; i < other.length; i++) + new (std::addressof (arrayZ[length + i])) Type (other.arrayZ[i]); + length += other.length; } template other) { - length = 0; - while (length < other.length) + assert ((int) (length + other.length) <= allocated); + for (unsigned i = 0; i < other.length; i++) { - length++; - new (std::addressof (arrayZ[length - 1])) Type (); - arrayZ[length - 1] = other.arrayZ[length - 1]; + new (std::addressof (arrayZ[length + i])) Type (); + arrayZ[length + i] = other.arrayZ[i]; } + length += other.length; } void @@ -401,7 +442,6 @@ struct hb_vector_t new_allocated += (new_allocated >> 1) + 8; } - /* Reallocate */ bool overflows = @@ -431,6 +471,15 @@ struct hb_vector_t return true; } + bool alloc_exact (unsigned int size) + { + return alloc (size, true); + } + + void clear () + { + resize (0); + } bool resize (int size_, bool initialize = true, bool exact = false) { @@ -496,7 +545,7 @@ struct hb_vector_t shrink_vector (size); if (shrink_memory) - alloc (size, true); /* To force shrinking memory if needed. */ + alloc_exact (size); /* To force shrinking memory if needed. */ } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-version.h b/src/java.desktop/share/native/libharfbuzz/hb-version.h index 3627da3bf0ce7..e41286d2d8c70 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-version.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-version.h @@ -41,7 +41,7 @@ HB_BEGIN_DECLS * * The major component of the library version available at compile-time. */ -#define HB_VERSION_MAJOR 8 +#define HB_VERSION_MAJOR 11 /** * HB_VERSION_MINOR: * @@ -53,14 +53,14 @@ HB_BEGIN_DECLS * * The micro component of the library version available at compile-time. */ -#define HB_VERSION_MICRO 2 +#define HB_VERSION_MICRO 0 /** * HB_VERSION_STRING: * * A string literal containing the library version available at compile-time. */ -#define HB_VERSION_STRING "8.2.2" +#define HB_VERSION_STRING "11.2.0" /** * HB_VERSION_ATLEAST: diff --git a/src/java.desktop/share/native/libharfbuzz/hb.hh b/src/java.desktop/share/native/libharfbuzz/hb.hh index 65d2dd58a2039..8c430e5770db7 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb.hh @@ -64,6 +64,7 @@ #pragma GCC diagnostic error "-Wbitwise-instead-of-logical" #pragma GCC diagnostic error "-Wcast-align" #pragma GCC diagnostic error "-Wcast-function-type" +#pragma GCC diagnostic error "-Wcast-function-type-strict" #pragma GCC diagnostic error "-Wconstant-conversion" #pragma GCC diagnostic error "-Wcomma" #pragma GCC diagnostic error "-Wdelete-non-virtual-dtor" @@ -83,10 +84,12 @@ #pragma GCC diagnostic error "-Wredundant-decls" #pragma GCC diagnostic error "-Wreorder" #pragma GCC diagnostic error "-Wsign-compare" +#pragma GCC diagnostic error "-Wstrict-flex-arrays" #pragma GCC diagnostic error "-Wstrict-prototypes" #pragma GCC diagnostic error "-Wstring-conversion" #pragma GCC diagnostic error "-Wswitch-enum" #pragma GCC diagnostic error "-Wtautological-overlap-compare" +#pragma GCC diagnostic error "-Wuninitialized" #pragma GCC diagnostic error "-Wunneeded-internal-declaration" #pragma GCC diagnostic error "-Wunused" #pragma GCC diagnostic error "-Wunused-local-typedefs" @@ -129,6 +132,7 @@ #pragma GCC diagnostic ignored "-Wclass-memaccess" #pragma GCC diagnostic ignored "-Wcast-function-type-strict" // https://github.com/harfbuzz/harfbuzz/pull/3859#issuecomment-1295409126 #pragma GCC diagnostic ignored "-Wdangling-reference" // https://github.com/harfbuzz/harfbuzz/issues/4043 +#pragma GCC diagnostic ignored "-Wdangling-pointer" // Trigerred by hb_decycler_node_t(). #pragma GCC diagnostic ignored "-Wformat-nonliteral" #pragma GCC diagnostic ignored "-Wformat-zero-length" #pragma GCC diagnostic ignored "-Wmissing-field-initializers" @@ -177,6 +181,11 @@ #define HB_EXTERN __declspec (dllexport) extern #endif +// https://github.com/harfbuzz/harfbuzz/pull/4619 +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS 1 +#endif + #include "hb.h" #define HB_H_IN #include "hb-ot.h" @@ -212,35 +221,14 @@ #include #endif -#define HB_PASTE1(a,b) a##b -#define HB_PASTE(a,b) HB_PASTE1(a,b) - - -/* Compile-time custom allocator support. */ - -#if !defined(HB_CUSTOM_MALLOC) \ - && defined(hb_malloc_impl) \ - && defined(hb_calloc_impl) \ - && defined(hb_realloc_impl) \ - && defined(hb_free_impl) -#define HB_CUSTOM_MALLOC +#ifndef PRId32 +# define PRId32 "d" +# define PRIu32 "u" +# define PRIx32 "x" #endif -#ifdef HB_CUSTOM_MALLOC -extern "C" void* hb_malloc_impl(size_t size); -extern "C" void* hb_calloc_impl(size_t nmemb, size_t size); -extern "C" void* hb_realloc_impl(void *ptr, size_t size); -extern "C" void hb_free_impl(void *ptr); -#define hb_malloc hb_malloc_impl -#define hb_calloc hb_calloc_impl -#define hb_realloc hb_realloc_impl -#define hb_free hb_free_impl -#else -#define hb_malloc malloc -#define hb_calloc calloc -#define hb_realloc realloc -#define hb_free free -#endif +#define HB_PASTE1(a,b) a##b +#define HB_PASTE(a,b) HB_PASTE1(a,b) /* @@ -268,7 +256,9 @@ extern "C" void hb_free_impl(void *ptr); #define __attribute__(x) #endif -#if defined(__GNUC__) && (__GNUC__ >= 3) +#if defined(__MINGW32__) && (__GNUC__ >= 3) && !defined(__clang__) +#define HB_PRINTF_FUNC(format_idx, arg_idx) __attribute__((__format__ (gnu_printf, format_idx, arg_idx))) +#elif defined(__GNUC__) && (__GNUC__ >= 3) #define HB_PRINTF_FUNC(format_idx, arg_idx) __attribute__((__format__ (__printf__, format_idx, arg_idx))) #else #define HB_PRINTF_FUNC(format_idx, arg_idx) @@ -475,7 +465,7 @@ static int HB_UNUSED _hb_errno = 0; # define hb_atexit atexit # else template struct hb_atexit_t { ~hb_atexit_t () { function (); } }; -# define hb_atexit(f) static hb_atexit_t _hb_atexit_##__LINE__; +# define hb_atexit(f) static hb_atexit_t _hb_atexit_##__LINE__ # endif #endif #endif @@ -523,6 +513,29 @@ static_assert ((sizeof (hb_var_int_t) == 4), ""); #define HB_PI 3.14159265358979f #define HB_2_PI (2.f * HB_PI) +/* Compile-time custom allocator support. */ + +#if !defined(HB_CUSTOM_MALLOC) \ + && defined(hb_malloc_impl) \ + && defined(hb_calloc_impl) \ + && defined(hb_realloc_impl) \ + && defined(hb_free_impl) +#define HB_CUSTOM_MALLOC +#endif + +#ifdef HB_CUSTOM_MALLOC +extern "C" void* hb_malloc_impl(size_t size); +extern "C" void* hb_calloc_impl(size_t nmemb, size_t size); +extern "C" void* hb_realloc_impl(void *ptr, size_t size); +extern "C" void hb_free_impl(void *ptr); +#else +#define hb_malloc_impl malloc +#define hb_calloc_impl calloc +#define hb_realloc_impl realloc +#define hb_free_impl free +#endif + + /* Headers we include for everyone. Keep topologically sorted by dependency. * They express dependency amongst themselves, but no other file should include diff --git a/src/java.desktop/share/native/libjsound/MidiInDevice.c b/src/java.desktop/share/native/libjsound/MidiInDevice.c index fe320a59a874f..fb27602f39915 100644 --- a/src/java.desktop/share/native/libjsound/MidiInDevice.c +++ b/src/java.desktop/share/native/libjsound/MidiInDevice.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -137,7 +137,7 @@ Java_com_sun_media_sound_MidiInDevice_nGetTimeStamp(JNIEnv* e, jobject thisObj, /* Handle error codes. */ if (ret < -1) { - ERROR1("Java_com_sun_media_sound_MidiInDevice_nGetTimeStamp: MIDI_IN_GetTimeStamp returned %lld\n", ret); + ERROR1("Java_com_sun_media_sound_MidiInDevice_nGetTimeStamp: MIDI_IN_GetTimeStamp returned %lld\n", (long long int) ret); ret = -1; } return ret; diff --git a/src/java.desktop/share/native/libjsound/MidiOutDevice.c b/src/java.desktop/share/native/libjsound/MidiOutDevice.c index 29ef140b89d91..2f351bb1720f9 100644 --- a/src/java.desktop/share/native/libjsound/MidiOutDevice.c +++ b/src/java.desktop/share/native/libjsound/MidiOutDevice.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -94,7 +94,7 @@ Java_com_sun_media_sound_MidiOutDevice_nGetTimeStamp(JNIEnv* e, jobject thisObj, /* Handle error codes. */ if (ret < -1) { - ERROR1("Java_com_sun_media_sound_MidiOutDevice_nGetTimeStamp: MIDI_IN_GetTimeStamp returned %lld\n", ret); + ERROR1("Java_com_sun_media_sound_MidiOutDevice_nGetTimeStamp: MIDI_IN_GetTimeStamp returned %lld\n", (long long int) ret); ret = -1; } return ret; diff --git a/src/java.desktop/share/native/liblcms/cmsalpha.c b/src/java.desktop/share/native/liblcms/cmsalpha.c index 78d3ca6b67151..2e50b65be24cb 100644 --- a/src/java.desktop/share/native/liblcms/cmsalpha.c +++ b/src/java.desktop/share/native/liblcms/cmsalpha.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2023 Marti Maria Saguer +// Copyright (c) 1998-2024 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -612,8 +612,8 @@ void _cmsHandleExtraChannels(_cmsTRANSFORM* p, const void* in, cmsUInt8Number* SourcePtr; cmsUInt8Number* DestPtr; - cmsUInt32Number SourceStrideIncrement = 0; - cmsUInt32Number DestStrideIncrement = 0; + size_t SourceStrideIncrement = 0; + size_t DestStrideIncrement = 0; // The loop itself for (i = 0; i < LineCount; i++) { @@ -640,8 +640,8 @@ void _cmsHandleExtraChannels(_cmsTRANSFORM* p, const void* in, cmsUInt8Number* SourcePtr[cmsMAXCHANNELS]; cmsUInt8Number* DestPtr[cmsMAXCHANNELS]; - cmsUInt32Number SourceStrideIncrements[cmsMAXCHANNELS]; - cmsUInt32Number DestStrideIncrements[cmsMAXCHANNELS]; + size_t SourceStrideIncrements[cmsMAXCHANNELS]; + size_t DestStrideIncrements[cmsMAXCHANNELS]; memset(SourceStrideIncrements, 0, sizeof(SourceStrideIncrements)); memset(DestStrideIncrements, 0, sizeof(DestStrideIncrements)); diff --git a/src/java.desktop/share/native/liblcms/cmscam02.c b/src/java.desktop/share/native/liblcms/cmscam02.c index 71a48d43de4ad..45ef4eef970ca 100644 --- a/src/java.desktop/share/native/liblcms/cmscam02.c +++ b/src/java.desktop/share/native/liblcms/cmscam02.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2023 Marti Maria Saguer +// Copyright (c) 1998-2024 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -117,17 +117,16 @@ cmsFloat64Number computeFL(cmsCIECAM02* pMod) return FL; } -static -cmsFloat64Number computeD(cmsCIECAM02* pMod) +static cmsFloat64Number computeD(cmsCIECAM02* pMod) { - cmsFloat64Number D; + cmsFloat64Number D, temp; - D = pMod->F - (1.0/3.6)*(exp(((-pMod ->LA-42) / 92.0))); + temp = 1.0 - ((1.0 / 3.6) * exp((-pMod->LA - 42) / 92.0)); + D = pMod->F * temp; return D; } - static CAM02COLOR XYZtoCAT02(CAM02COLOR clr) { diff --git a/src/java.desktop/share/native/liblcms/cmscgats.c b/src/java.desktop/share/native/liblcms/cmscgats.c index 57725ae4731a2..3e62d064c3f6a 100644 --- a/src/java.desktop/share/native/liblcms/cmscgats.c +++ b/src/java.desktop/share/native/liblcms/cmscgats.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2023 Marti Maria Saguer +// Copyright (c) 1998-2024 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -444,10 +444,11 @@ static void StringClear(string* s) { s->len = 0; + s->begin[0] = 0; } static -void StringAppend(string* s, char c) +cmsBool StringAppend(string* s, char c) { if (s->len + 1 >= s->max) { @@ -455,6 +456,8 @@ void StringAppend(string* s, char c) s->max *= 10; new_ptr = (char*) AllocChunk(s->it8, s->max); + if (new_ptr == NULL) return FALSE; + if (new_ptr != NULL && s->begin != NULL) memcpy(new_ptr, s->begin, s->len); @@ -466,6 +469,8 @@ void StringAppend(string* s, char c) s->begin[s->len++] = c; s->begin[s->len] = 0; } + + return TRUE; } static @@ -475,13 +480,15 @@ char* StringPtr(string* s) } static -void StringCat(string* s, const char* c) +cmsBool StringCat(string* s, const char* c) { while (*c) { - StringAppend(s, *c); + if (!StringAppend(s, *c)) return FALSE; c++; } + + return TRUE; } @@ -830,7 +837,12 @@ void InStringSymbol(cmsIT8* it8) if (it8->ch == '\n' || it8->ch == '\r' || it8->ch == 0) break; else { - StringAppend(it8->str, (char)it8->ch); + if (!StringAppend(it8->str, (char)it8->ch)) { + + SynError(it8, "Out of memory"); + return; + } + NextCh(it8); } } @@ -860,7 +872,11 @@ void InSymbol(cmsIT8* it8) do { - StringAppend(it8->id, (char) it8->ch); + if (!StringAppend(it8->id, (char)it8->ch)) { + + SynError(it8, "Out of memory"); + return; + } NextCh(it8); @@ -904,7 +920,6 @@ void InSymbol(cmsIT8* it8) if ((cmsFloat64Number) it8->inum * 16.0 + (cmsFloat64Number) j > (cmsFloat64Number)+2147483647.0) { SynError(it8, "Invalid hexadecimal number"); - it8->sy = SEOF; return; } @@ -926,7 +941,6 @@ void InSymbol(cmsIT8* it8) if ((cmsFloat64Number) it8->inum * 2.0 + j > (cmsFloat64Number)+2147483647.0) { SynError(it8, "Invalid binary number"); - it8->sy = SEOF; return; } @@ -979,11 +993,19 @@ void InSymbol(cmsIT8* it8) } StringClear(it8->id); - StringCat(it8->id, buffer); + if (!StringCat(it8->id, buffer)) { + + SynError(it8, "Out of memory"); + return; + } do { - StringAppend(it8->id, (char) it8->ch); + if (!StringAppend(it8->id, (char)it8->ch)) { + + SynError(it8, "Out of memory"); + return; + } NextCh(it8); @@ -1038,7 +1060,6 @@ void InSymbol(cmsIT8* it8) default: SynError(it8, "Unrecognized character: 0x%x", it8 ->ch); - it8->sy = SEOF; return; } @@ -1053,24 +1074,21 @@ void InSymbol(cmsIT8* it8) if(it8 -> IncludeSP >= (MAXINCLUDE-1)) { SynError(it8, "Too many recursion levels"); - it8->sy = SEOF; return; } InStringSymbol(it8); if (!Check(it8, SSTRING, "Filename expected")) - { - it8->sy = SEOF; return; - } + FileNest = it8 -> FileStack[it8 -> IncludeSP + 1]; if(FileNest == NULL) { FileNest = it8 ->FileStack[it8 -> IncludeSP + 1] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX)); if (FileNest == NULL) { + SynError(it8, "Out of memory"); - it8->sy = SEOF; return; } } @@ -1078,8 +1096,8 @@ void InSymbol(cmsIT8* it8) if (BuildAbsolutePath(StringPtr(it8->str), it8->FileStack[it8->IncludeSP]->FileName, FileNest->FileName, cmsMAX_PATH-1) == FALSE) { + SynError(it8, "File path too long"); - it8->sy = SEOF; return; } @@ -1087,7 +1105,6 @@ void InSymbol(cmsIT8* it8) if (FileNest->Stream == NULL) { SynError(it8, "File %s not found", FileNest->FileName); - it8->sy = SEOF; return; } it8->IncludeSP++; @@ -1102,10 +1119,10 @@ void InSymbol(cmsIT8* it8) static cmsBool CheckEOLN(cmsIT8* it8) { - if (!Check(it8, SEOLN, "Expected separator")) return FALSE; - while (it8 -> sy == SEOLN) - InSymbol(it8); - return TRUE; + if (!Check(it8, SEOLN, "Expected separator")) return FALSE; + while (it8->sy == SEOLN) + InSymbol(it8); + return TRUE; } @@ -1114,8 +1131,8 @@ cmsBool CheckEOLN(cmsIT8* it8) static void Skip(cmsIT8* it8, SYMBOL sy) { - if (it8->sy == sy && it8->sy != SEOF) - InSymbol(it8); + if (it8->sy == sy && it8->sy != SEOF && it8->sy != SSYNERROR) + InSymbol(it8); } @@ -1124,7 +1141,7 @@ static void SkipEOLN(cmsIT8* it8) { while (it8->sy == SEOLN) { - InSymbol(it8); + InSymbol(it8); } } @@ -1235,9 +1252,12 @@ void* AllocChunk(cmsIT8* it8, cmsUInt32Number size) cmsUInt8Number* ptr; size = _cmsALIGNMEM(size); + if (size == 0) return NULL; if (size > Free) { + cmsUInt8Number* new_block; + if (it8 -> Allocator.BlockSize == 0) it8 -> Allocator.BlockSize = 20*1024; @@ -1248,7 +1268,11 @@ void* AllocChunk(cmsIT8* it8, cmsUInt32Number size) it8 ->Allocator.BlockSize = size; it8 ->Allocator.Used = 0; - it8 ->Allocator.Block = (cmsUInt8Number*) AllocBigBlock(it8, it8 ->Allocator.BlockSize); + new_block = (cmsUInt8Number*)AllocBigBlock(it8, it8->Allocator.BlockSize); + if (new_block == NULL) + return NULL; + + it8->Allocator.Block = new_block; } if (it8->Allocator.Block == NULL) @@ -1258,7 +1282,6 @@ void* AllocChunk(cmsIT8* it8, cmsUInt32Number size) it8 ->Allocator.Used += size; return (void*) ptr; - } @@ -1266,9 +1289,12 @@ void* AllocChunk(cmsIT8* it8, cmsUInt32Number size) static char *AllocString(cmsIT8* it8, const char* str) { - cmsUInt32Number Size = (cmsUInt32Number) strlen(str)+1; + cmsUInt32Number Size; char *ptr; + if (str == NULL) return NULL; + + Size = (cmsUInt32Number)strlen(str) + 1; ptr = (char *) AllocChunk(it8, Size); if (ptr) memcpy(ptr, str, Size-1); @@ -1404,10 +1430,13 @@ KEYVALUE* AddAvailableSampleID(cmsIT8* it8, const char* Key) static -void AllocTable(cmsIT8* it8) +cmsBool AllocTable(cmsIT8* it8) { TABLE* t; + if (it8->TablesCount >= (MAXTABLES-1)) + return FALSE; + t = it8 ->Tab + it8 ->TablesCount; t->HeaderList = NULL; @@ -1415,6 +1444,7 @@ void AllocTable(cmsIT8* it8) t->Data = NULL; it8 ->TablesCount++; + return TRUE; } @@ -1426,7 +1456,10 @@ cmsInt32Number CMSEXPORT cmsIT8SetTable(cmsHANDLE IT8, cmsUInt32Number nTable) if (nTable == it8 ->TablesCount) { - AllocTable(it8); + if (!AllocTable(it8)) { + SynError(it8, "Too many tables"); + return -1; + } } else { SynError(it8, "Table %d is out of sequence", nTable); @@ -1610,8 +1643,8 @@ cmsInt32Number satoi(const char* b) if (b == NULL) return 0; n = atoi(b); - if (n > 0x7fffffffL) return 0x7fffffffL; - if (n < -0x7ffffffeL) return -0x7ffffffeL; + if (n > 0x7ffffff0L) return 0x7ffffff0L; + if (n < -0x7ffffff0L) return -0x7ffffff0L; return (cmsInt32Number)n; } @@ -1620,22 +1653,26 @@ cmsInt32Number satoi(const char* b) static cmsBool AllocateDataFormat(cmsIT8* it8) { + cmsUInt32Number size; + TABLE* t = GetTable(it8); - if (t -> DataFormat) return TRUE; // Already allocated + if (t->DataFormat) return TRUE; // Already allocated - t -> nSamples = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS")); + t->nSamples = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS")); - if (t -> nSamples <= 0) { + if (t->nSamples <= 0 || t->nSamples > 0x7ffe) { - SynError(it8, "AllocateDataFormat: Unknown NUMBER_OF_FIELDS"); + SynError(it8, "Wrong NUMBER_OF_FIELDS"); return FALSE; - } + } + + size = ((cmsUInt32Number)t->nSamples + 1) * sizeof(char*); - t -> DataFormat = (char**) AllocChunk (it8, ((cmsUInt32Number) t->nSamples + 1) * sizeof(char *)); + t->DataFormat = (char**)AllocChunk(it8, size); if (t->DataFormat == NULL) { - SynError(it8, "AllocateDataFormat: Unable to allocate dataFormat array"); + SynError(it8, "Unable to allocate dataFormat array"); return FALSE; } @@ -1664,7 +1701,7 @@ cmsBool SetDataFormat(cmsIT8* it8, int n, const char *label) return FALSE; } - if (n > t -> nSamples) { + if (n >= t -> nSamples) { SynError(it8, "More than NUMBER_OF_FIELDS fields."); return FALSE; } @@ -1713,13 +1750,14 @@ cmsBool AllocateDataSet(cmsIT8* it8) t-> nSamples = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS")); t-> nPatches = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS")); - if (t -> nSamples < 0 || t->nSamples > 0x7ffe || t->nPatches < 0 || t->nPatches > 0x7ffe) + if (t -> nSamples < 0 || t->nSamples > 0x7ffe || t->nPatches < 0 || t->nPatches > 0x7ffe || + (t->nPatches * t->nSamples) > 200000) { SynError(it8, "AllocateDataSet: too much data"); return FALSE; } else { - // Some dumb analizers warns of possible overflow here, just take a look couple of lines above. + // Some dumb analyzers warns of possible overflow here, just take a look couple of lines above. t->Data = (char**)AllocChunk(it8, ((cmsUInt32Number)t->nSamples + 1) * ((cmsUInt32Number)t->nPatches + 1) * sizeof(char*)); if (t->Data == NULL) { @@ -1748,8 +1786,11 @@ char* GetData(cmsIT8* it8, int nSet, int nField) static cmsBool SetData(cmsIT8* it8, int nSet, int nField, const char *Val) { + char* ptr; + TABLE* t = GetTable(it8); + if (!t->Data) { if (!AllocateDataSet(it8)) return FALSE; } @@ -1766,7 +1807,11 @@ cmsBool SetData(cmsIT8* it8, int nSet, int nField, const char *Val) } - t->Data [nSet * t -> nSamples + nField] = AllocString(it8, Val); + ptr = AllocString(it8, Val); + if (ptr == NULL) + return FALSE; + + t->Data [nSet * t -> nSamples + nField] = ptr; return TRUE; } @@ -2121,7 +2166,7 @@ cmsBool DataSection (cmsIT8* it8) if (!AllocateDataSet(it8)) return FALSE; } - while (it8->sy != SEND_DATA && it8->sy != SEOF) + while (it8->sy != SEND_DATA && it8->sy != SEOF && it8->sy != SSYNERROR) { if (iField >= t -> nSamples) { iField = 0; @@ -2129,7 +2174,7 @@ cmsBool DataSection (cmsIT8* it8) } - if (it8->sy != SEND_DATA && it8->sy != SEOF) { + if (it8->sy != SEND_DATA && it8->sy != SEOF && it8->sy != SSYNERROR) { switch (it8->sy) { @@ -2225,8 +2270,8 @@ cmsBool HeaderSection(cmsIT8* it8) if (!GetVal(it8, Buffer, MAXSTR - 1, "Property data expected")) return FALSE; if (Key->WriteAs != WRITE_PAIR) { - AddToList(it8, &GetTable(it8)->HeaderList, VarName, NULL, Buffer, - (it8->sy == SSTRING) ? WRITE_STRINGIFY : WRITE_UNCOOKED); + if (AddToList(it8, &GetTable(it8)->HeaderList, VarName, NULL, Buffer, + (it8->sy == SSTRING) ? WRITE_STRINGIFY : WRITE_UNCOOKED) == NULL) return FALSE; } else { const char *Subkey; @@ -2332,9 +2377,10 @@ cmsBool ParseIT8(cmsIT8* it8, cmsBool nosheet) if (!DataSection(it8)) return FALSE; - if (it8 -> sy != SEOF) { + if (it8 -> sy != SEOF && it8->sy != SSYNERROR) { + + if (!AllocTable(it8)) return FALSE; - AllocTable(it8); it8 ->nTable = it8 ->TablesCount - 1; // Read sheet type if present. We only support identifier and string. @@ -3064,7 +3110,8 @@ cmsBool ParseCube(cmsIT8* cube, cmsStage** Shaper, cmsStage** CLUT, char title[] InSymbol(cube); - while (cube->sy != SEOF) { + while (cube->sy != SEOF && cube->sy != SSYNERROR) { + switch (cube->sy) { // Set profile description diff --git a/src/java.desktop/share/native/liblcms/cmscnvrt.c b/src/java.desktop/share/native/liblcms/cmscnvrt.c index d18865b15b9a3..9f8619cb9dac1 100644 --- a/src/java.desktop/share/native/liblcms/cmscnvrt.c +++ b/src/java.desktop/share/native/liblcms/cmscnvrt.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2023 Marti Maria Saguer +// Copyright (c) 1998-2024 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -750,7 +750,6 @@ static cmsBool is_cmyk_devicelink(cmsHPROFILE hProfile) { return cmsGetDeviceClass(hProfile) == cmsSigLinkClass && - cmsGetColorSpace(hProfile) == cmsSigCmykData && cmsGetColorSpace(hProfile) == cmsSigCmykData; } diff --git a/src/java.desktop/share/native/liblcms/cmserr.c b/src/java.desktop/share/native/liblcms/cmserr.c index 9fb7db89c9a6c..d421c550d32d7 100644 --- a/src/java.desktop/share/native/liblcms/cmserr.c +++ b/src/java.desktop/share/native/liblcms/cmserr.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2023 Marti Maria Saguer +// Copyright (c) 1998-2024 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), diff --git a/src/java.desktop/share/native/liblcms/cmsgamma.c b/src/java.desktop/share/native/liblcms/cmsgamma.c index 8e489a43c5533..773858b0c1f02 100644 --- a/src/java.desktop/share/native/liblcms/cmsgamma.c +++ b/src/java.desktop/share/native/liblcms/cmsgamma.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2023 Marti Maria Saguer +// Copyright (c) 1998-2024 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), diff --git a/src/java.desktop/share/native/liblcms/cmsgmt.c b/src/java.desktop/share/native/liblcms/cmsgmt.c index e9ee73b52cd0d..03ac70202a50a 100644 --- a/src/java.desktop/share/native/liblcms/cmsgmt.c +++ b/src/java.desktop/share/native/liblcms/cmsgmt.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2021 Marti Maria Saguer +// Copyright (c) 1998-2024 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -327,8 +327,9 @@ cmsPipeline* _cmsCreateGamutCheckPipeline(cmsContext ContextID, cmsUInt32Number dwFormat; GAMUTCHAIN Chain; cmsUInt32Number nGridpoints; - cmsInt32Number nChannels; + cmsInt32Number nChannels, nInputChannels; cmsColorSpaceSignature ColorSpace; + cmsColorSpaceSignature InputColorSpace; cmsUInt32Number i; cmsHPROFILE ProfileList[256]; cmsBool BPCList[256]; @@ -374,11 +375,13 @@ cmsPipeline* _cmsCreateGamutCheckPipeline(cmsContext ContextID, AdaptationList[nGamutPCSposition] = 1.0; IntentList[nGamutPCSposition] = INTENT_RELATIVE_COLORIMETRIC; - ColorSpace = cmsGetColorSpace(hGamut); nChannels = cmsChannelsOfColorSpace(ColorSpace); nGridpoints = _cmsReasonableGridpointsByColorspace(ColorSpace, cmsFLAGS_HIGHRESPRECALC); - dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2)); + + InputColorSpace = cmsGetColorSpace(ProfileList[0]); + nInputChannels = cmsChannelsOfColorSpace(InputColorSpace); + dwFormat = (CHANNELS_SH(nInputChannels)|BYTES_SH(2)); // 16 bits to Lab double Chain.hInput = cmsCreateExtendedTransform(ContextID, diff --git a/src/java.desktop/share/native/liblcms/cmshalf.c b/src/java.desktop/share/native/liblcms/cmshalf.c index 5babb063eb000..7e5f7a3c7e03a 100644 --- a/src/java.desktop/share/native/liblcms/cmshalf.c +++ b/src/java.desktop/share/native/liblcms/cmshalf.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2023 Marti Maria Saguer +// Copyright (c) 1998-2024 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), diff --git a/src/java.desktop/share/native/liblcms/cmsintrp.c b/src/java.desktop/share/native/liblcms/cmsintrp.c index 9837454df0bb6..43c47429c3cda 100644 --- a/src/java.desktop/share/native/liblcms/cmsintrp.c +++ b/src/java.desktop/share/native/liblcms/cmsintrp.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2023 Marti Maria Saguer +// Copyright (c) 1998-2024 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -524,7 +524,7 @@ void TrilinearInterpFloat(const cmsFloat32Number Input[], py = fclamp(Input[1]) * p->Domain[1]; pz = fclamp(Input[2]) * p->Domain[2]; - x0 = (int) floor(px); fx = px - (cmsFloat32Number) x0; // We need full floor funcionality here + x0 = (int) floor(px); fx = px - (cmsFloat32Number) x0; // We need full floor functionality here y0 = (int) floor(py); fy = py - (cmsFloat32Number) y0; z0 = (int) floor(pz); fz = pz - (cmsFloat32Number) z0; diff --git a/src/java.desktop/share/native/liblcms/cmsio0.c b/src/java.desktop/share/native/liblcms/cmsio0.c index 05baa9392e21a..5258b7939d2bf 100644 --- a/src/java.desktop/share/native/liblcms/cmsio0.c +++ b/src/java.desktop/share/native/liblcms/cmsio0.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2023 Marti Maria Saguer +// Copyright (c) 1998-2024 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -306,6 +306,11 @@ cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromMem(cmsContext ContextID, void *Buff fm = (FILEMEM*) _cmsMallocZero(ContextID, sizeof(FILEMEM)); if (fm == NULL) goto Error; + if (Buffer == NULL) { + cmsSignalError(ContextID, cmsERROR_WRITE, "Couldn't write profile to NULL pointer"); + goto Error; + } + fm ->Block = (cmsUInt8Number*) Buffer; fm ->FreeBlockOnClose = FALSE; fm ->Size = size; diff --git a/src/java.desktop/share/native/liblcms/cmsio1.c b/src/java.desktop/share/native/liblcms/cmsio1.c index e42d4d3898730..48772c7cbde9d 100644 --- a/src/java.desktop/share/native/liblcms/cmsio1.c +++ b/src/java.desktop/share/native/liblcms/cmsio1.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2023 Marti Maria Saguer +// Copyright (c) 1998-2024 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), diff --git a/src/java.desktop/share/native/liblcms/cmslut.c b/src/java.desktop/share/native/liblcms/cmslut.c index b544c94862592..3cf4e8cac5a6b 100644 --- a/src/java.desktop/share/native/liblcms/cmslut.c +++ b/src/java.desktop/share/native/liblcms/cmslut.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2023 Marti Maria Saguer +// Copyright (c) 1998-2024 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -1109,7 +1109,7 @@ cmsStage* _cmsStageNormalizeFromLabFloat(cmsContext ContextID) return mpe; } -// Fom XYZ to floating point PCS +// From XYZ to floating point PCS cmsStage* _cmsStageNormalizeFromXyzFloat(cmsContext ContextID) { #define n (32768.0/65535.0) diff --git a/src/java.desktop/share/native/liblcms/cmsmd5.c b/src/java.desktop/share/native/liblcms/cmsmd5.c index 01aa44de85a09..d9b9a4e526080 100644 --- a/src/java.desktop/share/native/liblcms/cmsmd5.c +++ b/src/java.desktop/share/native/liblcms/cmsmd5.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2023 Marti Maria Saguer +// Copyright (c) 1998-2024 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -280,8 +280,8 @@ void CMSEXPORT cmsMD5finish(cmsProfileID* ProfileID, cmsHANDLE Handle) // Assuming io points to an ICC profile, compute and store MD5 checksum -// In the header, rendering intentent, attributes and ID should be set to zero -// before computing MD5 checksum (per 6.1.13 in ICC spec) +// In the header, rendering intentent, flags and ID should be set to zero +// before computing MD5 checksum (per 7.2.18 of ICC spec 4.4) cmsBool CMSEXPORT cmsMD5computeID(cmsHPROFILE hProfile) { @@ -299,8 +299,8 @@ cmsBool CMSEXPORT cmsMD5computeID(cmsHPROFILE hProfile) // Save a copy of the profile header memmove(&Keep, Icc, sizeof(_cmsICCPROFILE)); - // Set RI, attributes and ID - memset(&Icc ->attributes, 0, sizeof(Icc ->attributes)); + // Set RI, flags and ID + Icc ->flags = 0; Icc ->RenderingIntent = 0; memset(&Icc ->ProfileID, 0, sizeof(Icc ->ProfileID)); diff --git a/src/java.desktop/share/native/liblcms/cmsmtrx.c b/src/java.desktop/share/native/liblcms/cmsmtrx.c index 599c290bd7051..841da662a1079 100644 --- a/src/java.desktop/share/native/liblcms/cmsmtrx.c +++ b/src/java.desktop/share/native/liblcms/cmsmtrx.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2023 Marti Maria Saguer +// Copyright (c) 1998-2024 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), diff --git a/src/java.desktop/share/native/liblcms/cmsnamed.c b/src/java.desktop/share/native/liblcms/cmsnamed.c index d3cd97d4aea5f..451bfe9f34d57 100644 --- a/src/java.desktop/share/native/liblcms/cmsnamed.c +++ b/src/java.desktop/share/native/liblcms/cmsnamed.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2023 Marti Maria Saguer +// Copyright (c) 1998-2024 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -598,7 +598,7 @@ cmsUInt32Number CMSEXPORT cmsMLUgetASCII(const cmsMLU* mlu, if (BufferSize < ASCIIlen + 1) ASCIIlen = BufferSize - 1; - // Precess each character + // Process each character for (i=0; i < ASCIIlen; i++) { wchar_t wc = Wide[i]; diff --git a/src/java.desktop/share/native/liblcms/cmsopt.c b/src/java.desktop/share/native/liblcms/cmsopt.c index 421a4f4a70193..767008e68c58c 100644 --- a/src/java.desktop/share/native/liblcms/cmsopt.c +++ b/src/java.desktop/share/native/liblcms/cmsopt.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2023 Marti Maria Saguer +// Copyright (c) 1998-2024 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), diff --git a/src/java.desktop/share/native/liblcms/cmspack.c b/src/java.desktop/share/native/liblcms/cmspack.c index fc875995a80c2..d430e73051ded 100644 --- a/src/java.desktop/share/native/liblcms/cmspack.c +++ b/src/java.desktop/share/native/liblcms/cmspack.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2023 Marti Maria Saguer +// Copyright (c) 1998-2024 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -217,7 +217,7 @@ cmsUInt8Number* UnrollPlanarBytes(CMSREGISTER _cmsTRANSFORM* info, else { if (Premul && Extra) - alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(accum[(nChan) * Stride])); + alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(accum[nChan * Stride])); } for (i=0; i < nChan; i++) { @@ -606,8 +606,8 @@ cmsUInt8Number* UnrollAnyWordsPremul(CMSREGISTER _cmsTRANSFORM* info, cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; cmsUInt32Number i; - cmsUInt16Number alpha = (ExtraFirst ? accum[0] : accum[nChan - 1]); - cmsUInt32Number alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(alpha)); + cmsUInt16Number alpha = (ExtraFirst ? ((cmsUInt16Number*)accum)[0] : ((cmsUInt16Number*)accum)[nChan]); + cmsUInt32Number alpha_factor = _cmsToFixedDomain(alpha); if (ExtraFirst) { accum += sizeof(cmsUInt16Number); @@ -691,8 +691,8 @@ cmsUInt8Number* UnrollPlanarWordsPremul(CMSREGISTER _cmsTRANSFORM* info, cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; cmsUInt8Number* Init = accum; - cmsUInt16Number alpha = (ExtraFirst ? accum[0] : accum[(nChan - 1) * Stride]); - cmsUInt32Number alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(alpha)); + cmsUInt16Number alpha = (ExtraFirst ? ((cmsUInt16Number*)accum)[0] : ((cmsUInt16Number*)accum)[nChan * Stride / 2]); + cmsUInt32Number alpha_factor = _cmsToFixedDomain(alpha); if (ExtraFirst) { accum += Stride; @@ -1107,8 +1107,7 @@ cmsINLINE cmsBool IsInkSpace(cmsUInt32Number Type) } // Return the size in bytes of a given formatter -static -cmsUInt32Number PixelSize(cmsUInt32Number Format) +cmsINLINE cmsUInt32Number PixelSize(cmsUInt32Number Format) { cmsUInt32Number fmt_bytes = T_BYTES(Format); @@ -1454,7 +1453,7 @@ cmsUInt8Number* UnrollDoublesToFloat(_cmsTRANSFORM* info, if (Premul && Extra) { if (Planar) - alpha_factor = (ExtraFirst ? ptr[0] : ptr[(nChan) * Stride]) / maximum; + alpha_factor = (ExtraFirst ? ptr[0] : ptr[nChan * Stride]) / maximum; else alpha_factor = (ExtraFirst ? ptr[0] : ptr[nChan]) / maximum; } @@ -3052,6 +3051,7 @@ cmsUInt8Number* PackWordsFromFloat(_cmsTRANSFORM* info, if (ExtraFirst) start = Extra; + Stride /= 2; for (i = 0; i < nChan; i++) { cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; @@ -3115,7 +3115,7 @@ cmsUInt8Number* PackFloatsFromFloat(_cmsTRANSFORM* info, v = maximum - v; if (Planar) - ((cmsFloat32Number*)output)[(i + start)* Stride] = (cmsFloat32Number)v; + ((cmsFloat32Number*)output)[(i + start) * Stride] = (cmsFloat32Number)v; else ((cmsFloat32Number*)output)[i + start] = (cmsFloat32Number)v; } @@ -3455,7 +3455,7 @@ cmsUInt8Number* UnrollHalfToFloat(_cmsTRANSFORM* info, cmsUInt32Number i, start = 0; cmsFloat32Number maximum = IsInkSpace(info ->InputFormat) ? 100.0F : 1.0F; - Stride /= PixelSize(info->OutputFormat); + Stride /= PixelSize(info->InputFormat); if (ExtraFirst) start = Extra; @@ -4062,6 +4062,9 @@ cmsUInt32Number CMSEXPORT cmsFormatterForColorspaceOfProfile(cmsHPROFILE hProfil // Unsupported color space? if (nOutputChans < 0) return 0; + // Fix float spaces + nBytes &= 7; + // Create a fake formatter for result return FLOAT_SH(Float) | COLORSPACE_SH(ColorSpaceBits) | BYTES_SH(nBytes) | CHANNELS_SH(nOutputChans); } @@ -4079,6 +4082,9 @@ cmsUInt32Number CMSEXPORT cmsFormatterForPCSOfProfile(cmsHPROFILE hProfile, cmsU // Unsupported color space? if (nOutputChans < 0) return 0; + // Fix float spaces + nBytes &= 7; + // Create a fake formatter for result return FLOAT_SH(Float) | COLORSPACE_SH(ColorSpaceBits) | BYTES_SH(nBytes) | CHANNELS_SH(nOutputChans); } diff --git a/src/java.desktop/share/native/liblcms/cmspcs.c b/src/java.desktop/share/native/liblcms/cmspcs.c index c4739840053b3..5f1b1f0d8e6db 100644 --- a/src/java.desktop/share/native/liblcms/cmspcs.c +++ b/src/java.desktop/share/native/liblcms/cmspcs.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2023 Marti Maria Saguer +// Copyright (c) 1998-2024 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), diff --git a/src/java.desktop/share/native/liblcms/cmsplugin.c b/src/java.desktop/share/native/liblcms/cmsplugin.c index f84e0172c813d..aaad39f52b04c 100644 --- a/src/java.desktop/share/native/liblcms/cmsplugin.c +++ b/src/java.desktop/share/native/liblcms/cmsplugin.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2023 Marti Maria Saguer +// Copyright (c) 1998-2024 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -935,7 +935,10 @@ cmsContext CMSEXPORT cmsDupContext(cmsContext ContextID, void* NewUserData) if (!InitContextMutex()) return NULL; // Setup default memory allocators - memcpy(&ctx->DefaultMemoryManager, &src->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager)); + if (ContextID == NULL) + _cmsInstallAllocFunctions(NULL, &ctx->DefaultMemoryManager); + else + memcpy(&ctx->DefaultMemoryManager, &src->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager)); // Maintain the linked list _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); diff --git a/src/java.desktop/share/native/liblcms/cmsps2.c b/src/java.desktop/share/native/liblcms/cmsps2.c index 9a2ab464f315a..476817e9c1a2d 100644 --- a/src/java.desktop/share/native/liblcms/cmsps2.c +++ b/src/java.desktop/share/native/liblcms/cmsps2.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2023 Marti Maria Saguer +// Copyright (c) 1998-2024 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -494,7 +494,7 @@ void Emit1Gamma(cmsIOHANDLER* m, cmsToneCurve* Table) // Bounds check EmitRangeCheck(m); - // Emit intepolation code + // Emit interpolation code // PostScript code Stack // =============== ======================== @@ -618,7 +618,7 @@ int OutputValueSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUI } - // Hadle the parenthesis on rows + // Handle the parenthesis on rows if (In[0] != sc ->FirstComponent) { @@ -694,8 +694,10 @@ void WriteCLUT(cmsIOHANDLER* m, cmsStage* mpe, const char* PreMaj, _cmsIOPrintf(m, "["); - for (i = 0; i < sc.Pipeline->Params->nInputs; i++) - _cmsIOPrintf(m, " %d ", sc.Pipeline->Params->nSamples[i]); + for (i = 0; i < sc.Pipeline->Params->nInputs; i++) { + if (i < MAX_INPUT_DIMENSIONS) + _cmsIOPrintf(m, " %d ", sc.Pipeline->Params->nSamples[i]); + } _cmsIOPrintf(m, " [\n"); @@ -869,13 +871,13 @@ cmsToneCurve* ExtractGray2Y(cmsContext ContextID, cmsHPROFILE hProfile, cmsUInt3 // a more perceptually uniform space... I do choose Lab. static -int WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags) +cmsBool WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags) { cmsHPROFILE hLab; cmsHTRANSFORM xform; cmsUInt32Number nChannels; cmsUInt32Number InputFormat; - int rc; + cmsHPROFILE Profiles[2]; cmsCIEXYZ BlackPointAdaptedToD50; @@ -900,7 +902,7 @@ int WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent, if (xform == NULL) { cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Profile -> Lab"); - return 0; + return FALSE; } // Only 1, 3 and 4 channels are allowed @@ -919,29 +921,35 @@ int WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number OutFrm = TYPE_Lab_16; cmsPipeline* DeviceLink; _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform; + cmsBool rc; DeviceLink = cmsPipelineDup(v ->Lut); - if (DeviceLink == NULL) return 0; + if (DeviceLink == NULL) { + cmsDeleteTransform(xform); + return FALSE; + } dwFlags |= cmsFLAGS_FORCE_CLUT; _cmsOptimizePipeline(m->ContextID, &DeviceLink, Intent, &InputFormat, &OutFrm, &dwFlags); rc = EmitCIEBasedDEF(m, DeviceLink, Intent, &BlackPointAdaptedToD50); cmsPipelineFree(DeviceLink); - if (rc == 0) return 0; + if (!rc) { + cmsDeleteTransform(xform); + return FALSE; + } } break; default: + cmsDeleteTransform(xform); cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Only 3, 4 channels are supported for CSA. This profile has %d channels.", nChannels); - return 0; + return FALSE; } - cmsDeleteTransform(xform); - - return 1; + return TRUE; } static @@ -1284,7 +1292,7 @@ void EmitXYZ2Lab(cmsIOHANDLER* m) // 8 bits. static -int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags) +cmsBool WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags) { cmsHPROFILE hLab; cmsHTRANSFORM xform; @@ -1302,7 +1310,7 @@ int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent cmsStage* first; hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL); - if (hLab == NULL) return 0; + if (hLab == NULL) return FALSE; OutputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE); nChannels = T_CHANNELS(OutputFormat); @@ -1327,7 +1335,7 @@ int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent if (xform == NULL) { cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Lab -> Profile in CRD creation"); - return 0; + return FALSE; } // Get a copy of the internal devicelink @@ -1335,17 +1343,22 @@ int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent DeviceLink = cmsPipelineDup(v ->Lut); if (DeviceLink == NULL) { cmsDeleteTransform(xform); - return 0; + cmsSignalError(m->ContextID, cmsERROR_CORRUPTION_DETECTED, "Cannot access link for CRD"); + return FALSE; } // We need a CLUT dwFlags |= cmsFLAGS_FORCE_CLUT; - _cmsOptimizePipeline(m->ContextID, &DeviceLink, RelativeEncodingIntent, &InFrm, &OutputFormat, &dwFlags); + if (!_cmsOptimizePipeline(m->ContextID, &DeviceLink, RelativeEncodingIntent, &InFrm, &OutputFormat, &dwFlags)) { + cmsPipelineFree(DeviceLink); + cmsDeleteTransform(xform); + cmsSignalError(m->ContextID, cmsERROR_CORRUPTION_DETECTED, "Cannot create CLUT table for CRD"); + return FALSE; + } _cmsIOPrintf(m, "<<\n"); _cmsIOPrintf(m, "/ColorRenderingType 1\n"); - cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0); // Emit headers, etc. @@ -1367,6 +1380,13 @@ int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent first = cmsPipelineGetPtrToFirstStage(DeviceLink); if (first != NULL) { + if (first->Type != cmsSigCLutElemType) { + cmsPipelineFree(DeviceLink); + cmsDeleteTransform(xform); + cmsSignalError(m->ContextID, cmsERROR_CORRUPTION_DETECTED, "Cannot create CLUT, revise your flags!"); + return FALSE; + } + WriteCLUT(m, first, "<", ">\n", "", "", lFixWhite, ColorSpace); } @@ -1389,7 +1409,7 @@ int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent cmsPipelineFree(DeviceLink); cmsDeleteTransform(xform); - return 1; + return TRUE; } diff --git a/src/java.desktop/share/native/liblcms/cmssamp.c b/src/java.desktop/share/native/liblcms/cmssamp.c index 74f5f4bff29b5..ca5c4a9d69318 100644 --- a/src/java.desktop/share/native/liblcms/cmssamp.c +++ b/src/java.desktop/share/native/liblcms/cmssamp.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2023 Marti Maria Saguer +// Copyright (c) 1998-2024 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), diff --git a/src/java.desktop/share/native/liblcms/cmssm.c b/src/java.desktop/share/native/liblcms/cmssm.c index 3e1486ae3ae1b..e2a810a266954 100644 --- a/src/java.desktop/share/native/liblcms/cmssm.c +++ b/src/java.desktop/share/native/liblcms/cmssm.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2023 Marti Maria Saguer +// Copyright (c) 1998-2024 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), diff --git a/src/java.desktop/share/native/liblcms/cmstypes.c b/src/java.desktop/share/native/liblcms/cmstypes.c index 862f393497aa4..22514f8822682 100644 --- a/src/java.desktop/share/native/liblcms/cmstypes.c +++ b/src/java.desktop/share/native/liblcms/cmstypes.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2023 Marti Maria Saguer +// Copyright (c) 1998-2024 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -599,6 +599,178 @@ void Type_ColorantOrderType_Free(struct _cms_typehandler_struct* self, void* Ptr _cmsFree(self ->ContextID, Ptr); } +// ******************************************************************************** +// Type cmsSigUInt8ArrayType +// ******************************************************************************** +// This type represents an array of generic 1-byte/8-bit quantity. + +static +void* Type_UInt8_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUInt8Number* array; + cmsUInt32Number i, n; + + *nItems = 0; + n = SizeOfTag / sizeof(cmsUInt8Number); + array = (cmsUInt8Number*)_cmsCalloc(self->ContextID, n, sizeof(cmsUInt8Number)); + if (array == NULL) return NULL; + + for (i = 0; i < n; i++) { + + if (!_cmsReadUInt8Number(io, &array[i])) { + + _cmsFree(self->ContextID, array); + return NULL; + } + } + + *nItems = n; + return (void*)array; +} + +static +cmsBool Type_UInt8_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsUInt8Number* Value = (cmsUInt8Number*)Ptr; + cmsUInt32Number i; + + for (i = 0; i < nItems; i++) { + + if (!_cmsWriteUInt8Number(io, Value[i])) return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(self); +} + +static +void* Type_UInt8_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self->ContextID, Ptr, n * sizeof(cmsUInt8Number)); +} + + +static +void Type_UInt8_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self->ContextID, Ptr); +} + +// ******************************************************************************** +// Type cmsSigUInt32ArrayType +// ******************************************************************************** +// This type represents an array of generic 4-byte/32-bit quantity. +static +void* Type_UInt32_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUInt32Number* array; + cmsUInt32Number i, n; + + *nItems = 0; + n = SizeOfTag / sizeof(cmsUInt32Number); + array = (cmsUInt32Number*)_cmsCalloc(self->ContextID, n, sizeof(cmsUInt32Number)); + if (array == NULL) return NULL; + + for (i = 0; i < n; i++) { + + if (!_cmsReadUInt32Number(io, &array[i])) { + + _cmsFree(self->ContextID, array); + return NULL; + } + } + + *nItems = n; + return (void*)array; +} + +static +cmsBool Type_UInt32_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsUInt32Number* Value = (cmsUInt32Number*)Ptr; + cmsUInt32Number i; + + for (i = 0; i < nItems; i++) { + + if (!_cmsWriteUInt32Number(io, Value[i])) return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(self); +} + +static +void* Type_UInt32_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self->ContextID, Ptr, n * sizeof(cmsUInt32Number)); +} + + +static +void Type_UInt32_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self->ContextID, Ptr); +} + +// ******************************************************************************** +// Type cmsSigUInt64ArrayType +// ******************************************************************************** +// This type represents an array of generic 8-byte/64-bit quantity. +static +void* Type_UInt64_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUInt64Number* array; + cmsUInt32Number i, n; + + *nItems = 0; + n = SizeOfTag / sizeof(cmsUInt64Number); + array = (cmsUInt64Number*)_cmsCalloc(self->ContextID, n, sizeof(cmsUInt64Number)); + if (array == NULL) return NULL; + + for (i = 0; i < n; i++) { + + if (!_cmsReadUInt64Number(io, &array[i])) { + + _cmsFree(self->ContextID, array); + return NULL; + } + } + + *nItems = n; + return (void*)array; +} + +static +cmsBool Type_UInt64_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsUInt64Number* Value = (cmsUInt64Number*)Ptr; + cmsUInt32Number i; + + for (i = 0; i < nItems; i++) { + + if (!_cmsWriteUInt64Number(io, &Value[i])) return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(self); +} + +static +void* Type_UInt64_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self->ContextID, Ptr, n * sizeof(cmsUInt64Number)); +} + + +static +void Type_UInt64_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self->ContextID, Ptr); +} + // ******************************************************************************** // Type cmsSigS15Fixed16ArrayType // ******************************************************************************** @@ -968,6 +1140,8 @@ void *Type_Text_Description_Read(struct _cms_typehandler_struct* self, cmsIOHAND // Read len of ASCII if (!_cmsReadUInt32Number(io, &AsciiCount)) return NULL; + if (AsciiCount > 0x7ffff) return NULL; + SizeOfTag -= sizeof(cmsUInt32Number); // Check for size @@ -999,7 +1173,8 @@ void *Type_Text_Description_Read(struct _cms_typehandler_struct* self, cmsIOHAND if (!_cmsReadUInt32Number(io, &UnicodeCount)) goto Done; SizeOfTag -= 2* sizeof(cmsUInt32Number); - if (UnicodeCount == 0 || SizeOfTag < UnicodeCount*sizeof(cmsUInt16Number)) goto Done; + if (UnicodeCount == 0 || UnicodeCount > 0x7ffff || + SizeOfTag < UnicodeCount*sizeof(cmsUInt16Number)) goto Done; UnicodeString = (wchar_t*)_cmsMallocZero(self->ContextID, (UnicodeCount + 1) * sizeof(wchar_t)); if (UnicodeString == NULL) goto Done; @@ -1129,7 +1304,7 @@ cmsBool Type_Text_Description_Write(struct _cms_typehandler_struct* self, cmsIO if (!io ->Write(io, 67, Filler)) goto Error; // possibly add pad at the end of tag - if(len_aligned - len_tag_requirement > 0) + if (len_aligned > len_tag_requirement) if (!io ->Write(io, len_aligned - len_tag_requirement, Filler)) goto Error; rc = TRUE; @@ -5191,7 +5366,7 @@ cmsBool ReadOneWChar(cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i, wchar if (!io -> Seek(io, e -> Offsets[i])) return FALSE; nChars = e ->Sizes[i] / sizeof(cmsUInt16Number); - + if (nChars > 0x7ffff) return FALSE; *wcstr = (wchar_t*) _cmsMallocZero(e ->ContextID, (nChars + 1) * sizeof(wchar_t)); if (*wcstr == NULL) return FALSE; @@ -5772,7 +5947,10 @@ static const _cmsTagTypeLinkedList SupportedTagTypes[] = { {TYPE_HANDLER(cmsSigDictType, Dictionary), (_cmsTagTypeLinkedList*) &SupportedTagTypes[30] }, {TYPE_HANDLER(cmsSigcicpType, VideoSignal), (_cmsTagTypeLinkedList*) &SupportedTagTypes[31] }, {TYPE_HANDLER(cmsSigVcgtType, vcgt), (_cmsTagTypeLinkedList*) &SupportedTagTypes[32] }, -{TYPE_HANDLER(cmsSigMHC2Type, MHC2), NULL } +{TYPE_HANDLER(cmsSigMHC2Type, MHC2), (_cmsTagTypeLinkedList*) &SupportedTagTypes[33] }, +{TYPE_HANDLER(cmsSigUInt8ArrayType, UInt8), (_cmsTagTypeLinkedList*) &SupportedTagTypes[34] }, +{TYPE_HANDLER(cmsSigUInt32ArrayType, UInt32), (_cmsTagTypeLinkedList*) &SupportedTagTypes[35] }, +{TYPE_HANDLER(cmsSigUInt64ArrayType, UInt64), NULL } }; diff --git a/src/java.desktop/share/native/liblcms/cmsvirt.c b/src/java.desktop/share/native/liblcms/cmsvirt.c index e8d18d4ca9f95..1ef86dae05447 100644 --- a/src/java.desktop/share/native/liblcms/cmsvirt.c +++ b/src/java.desktop/share/native/liblcms/cmsvirt.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2023 Marti Maria Saguer +// Copyright (c) 1998-2024 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -412,7 +412,7 @@ int InkLimitingSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUI Out[1] = _cmsQuickSaturateWord(In[1] * Ratio); // M Out[2] = _cmsQuickSaturateWord(In[2] * Ratio); // Y - Out[3] = In[3]; // K (untouched) + Out[3] = In[3]; // K (untouched) return TRUE; } @@ -433,7 +433,7 @@ cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLinkTHR(cmsContext ContextID, return NULL; } - if (Limit < 0.0 || Limit > 400) { + if (Limit < 1.0 || Limit > 400) { cmsSignalError(ContextID, cmsERROR_RANGE, "InkLimiting: Limit should be between 1..400"); if (Limit < 1) Limit = 1; @@ -705,7 +705,7 @@ cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfile(void) * * This virtual profile cannot be saved as an ICC file */ -cmsHPROFILE cmsCreate_OkLabProfile(cmsContext ctx) +cmsHPROFILE CMSEXPORT cmsCreate_OkLabProfile(cmsContext ctx) { cmsStage* XYZPCS = _cmsStageNormalizeFromXyzFloat(ctx); cmsStage* PCSXYZ = _cmsStageNormalizeToXyzFloat(ctx); @@ -774,6 +774,8 @@ cmsHPROFILE cmsCreate_OkLabProfile(cmsContext ctx) cmsPipeline* BToA = cmsPipelineAlloc(ctx, 3, 3); cmsHPROFILE hProfile = cmsCreateProfilePlaceholder(ctx); + if (!hProfile) // can't allocate + goto error; cmsSetProfileVersion(hProfile, 4.4); diff --git a/src/java.desktop/share/native/liblcms/cmswtpnt.c b/src/java.desktop/share/native/liblcms/cmswtpnt.c index 27d7180353189..ebba2cd6a9788 100644 --- a/src/java.desktop/share/native/liblcms/cmswtpnt.c +++ b/src/java.desktop/share/native/liblcms/cmswtpnt.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2023 Marti Maria Saguer +// Copyright (c) 1998-2024 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), diff --git a/src/java.desktop/share/native/liblcms/cmsxform.c b/src/java.desktop/share/native/liblcms/cmsxform.c index 86afd7202fdbf..1eb3eecbf1823 100644 --- a/src/java.desktop/share/native/liblcms/cmsxform.c +++ b/src/java.desktop/share/native/liblcms/cmsxform.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2023 Marti Maria Saguer +// Copyright (c) 1998-2024 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -295,7 +295,7 @@ void FloatXFORM(_cmsTRANSFORM* p, cmsUInt8Number* output; cmsFloat32Number fIn[cmsMAXCHANNELS], fOut[cmsMAXCHANNELS]; cmsFloat32Number OutOfGamut; - cmsUInt32Number i, j, c, strideIn, strideOut; + size_t i, j, c, strideIn, strideOut; _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride); @@ -322,9 +322,11 @@ void FloatXFORM(_cmsTRANSFORM* p, // Is current color out of gamut? if (OutOfGamut > 0.0) { + _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*)_cmsContextGetClientChunk(p->ContextID, AlarmCodesContext); + // Certainly, out of gamut for (c = 0; c < cmsMAXCHANNELS; c++) - fOut[c] = -1.0; + fOut[c] = ContextAlarmCodes->AlarmCodes[c] / 65535.0F; } else { @@ -361,7 +363,7 @@ void NullFloatXFORM(_cmsTRANSFORM* p, cmsUInt8Number* accum; cmsUInt8Number* output; cmsFloat32Number fIn[cmsMAXCHANNELS]; - cmsUInt32Number i, j, strideIn, strideOut; + size_t i, j, strideIn, strideOut; _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride); @@ -399,7 +401,7 @@ void NullXFORM(_cmsTRANSFORM* p, cmsUInt8Number* accum; cmsUInt8Number* output; cmsUInt16Number wIn[cmsMAXCHANNELS]; - cmsUInt32Number i, j, strideIn, strideOut; + size_t i, j, strideIn, strideOut; _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride); @@ -409,17 +411,17 @@ void NullXFORM(_cmsTRANSFORM* p, for (i = 0; i < LineCount; i++) { - accum = (cmsUInt8Number*)in + strideIn; - output = (cmsUInt8Number*)out + strideOut; + accum = (cmsUInt8Number*)in + strideIn; + output = (cmsUInt8Number*)out + strideOut; - for (j = 0; j < PixelsPerLine; j++) { + for (j = 0; j < PixelsPerLine; j++) { - accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn); - output = p->ToOutput(p, wIn, output, Stride->BytesPerPlaneOut); - } + accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn); + output = p->ToOutput(p, wIn, output, Stride->BytesPerPlaneOut); + } - strideIn += Stride->BytesPerLineIn; - strideOut += Stride->BytesPerLineOut; + strideIn += Stride->BytesPerLineIn; + strideOut += Stride->BytesPerLineOut; } } @@ -437,7 +439,7 @@ void PrecalculatedXFORM(_cmsTRANSFORM* p, CMSREGISTER cmsUInt8Number* accum; CMSREGISTER cmsUInt8Number* output; cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; - cmsUInt32Number i, j, strideIn, strideOut; + size_t i, j, strideIn, strideOut; _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride); @@ -500,7 +502,7 @@ void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p, cmsUInt8Number* accum; cmsUInt8Number* output; cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; - cmsUInt32Number i, j, strideIn, strideOut; + size_t i, j, strideIn, strideOut; _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride); @@ -511,18 +513,18 @@ void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p, for (i = 0; i < LineCount; i++) { - accum = (cmsUInt8Number*)in + strideIn; - output = (cmsUInt8Number*)out + strideOut; + accum = (cmsUInt8Number*)in + strideIn; + output = (cmsUInt8Number*)out + strideOut; - for (j = 0; j < PixelsPerLine; j++) { + for (j = 0; j < PixelsPerLine; j++) { - accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn); - TransformOnePixelWithGamutCheck(p, wIn, wOut); - output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut); - } + accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn); + TransformOnePixelWithGamutCheck(p, wIn, wOut); + output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut); + } - strideIn += Stride->BytesPerLineIn; - strideOut += Stride->BytesPerLineOut; + strideIn += Stride->BytesPerLineIn; + strideOut += Stride->BytesPerLineOut; } } @@ -540,7 +542,7 @@ void CachedXFORM(_cmsTRANSFORM* p, cmsUInt8Number* output; cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; _cmsCACHE Cache; - cmsUInt32Number i, j, strideIn, strideOut; + size_t i, j, strideIn, strideOut; _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride); @@ -595,7 +597,7 @@ void CachedXFORMGamutCheck(_cmsTRANSFORM* p, cmsUInt8Number* output; cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; _cmsCACHE Cache; - cmsUInt32Number i, j, strideIn, strideOut; + size_t i, j, strideIn, strideOut; _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride); @@ -712,7 +714,7 @@ void _cmsTransform2toTransformAdaptor(struct _cmstransform_struct *CMMcargo, const cmsStride* Stride) { - cmsUInt32Number i, strideIn, strideOut; + size_t i, strideIn, strideOut; _cmsHandleExtraChannels(CMMcargo, InputBuffer, OutputBuffer, PixelsPerLine, LineCount, Stride); @@ -1099,7 +1101,7 @@ cmsBool IsProperColorSpace(cmsColorSpaceSignature Check, cmsUInt32Number dwForm int Space1 = (int) T_COLORSPACE(dwFormat); int Space2 = _cmsLCMScolorSpace(Check); - if (Space1 == PT_ANY) return TRUE; + if (Space1 == PT_ANY) return (T_CHANNELS(dwFormat) == cmsChannelsOf(Check)); if (Space1 == Space2) return TRUE; if (Space1 == PT_LabV2 && Space2 == PT_Lab) return TRUE; @@ -1160,7 +1162,15 @@ cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID, cmsColorSpaceSignature EntryColorSpace; cmsColorSpaceSignature ExitColorSpace; cmsPipeline* Lut; - cmsUInt32Number LastIntent = Intents[nProfiles-1]; + cmsUInt32Number LastIntent; + + // Safeguard + if (nProfiles <= 0 || nProfiles > 255) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles); + return NULL; + } + + LastIntent = Intents[nProfiles - 1]; // If it is a fake transform if (dwFlags & cmsFLAGS_NULLTRANSFORM) diff --git a/src/java.desktop/share/native/liblcms/lcms2.h b/src/java.desktop/share/native/liblcms/lcms2.h index 2d9a8b1248f93..5ba0966130887 100644 --- a/src/java.desktop/share/native/liblcms/lcms2.h +++ b/src/java.desktop/share/native/liblcms/lcms2.h @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2023 Marti Maria Saguer +// Copyright (c) 1998-2025 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -52,7 +52,7 @@ // //--------------------------------------------------------------------------------- // -// Version 2.16 +// Version 2.17 // #ifndef _lcms2_H @@ -93,6 +93,9 @@ // Uncomment this to remove the "register" storage class // #define CMS_NO_REGISTER_KEYWORD 1 +// Uncomment this to remove visibility attribute when building shared objects +// #define CMS_NO_VISIBILITY 1 + // ********** End of configuration toggles ****************************** // Needed for streams @@ -113,7 +116,7 @@ extern "C" { #endif // Version/release -#define LCMS_VERSION 2160 +#define LCMS_VERSION 2170 // I will give the chance of redefining basic types for compilers that are not fully C99 compliant #ifndef CMS_BASIC_TYPES_ALREADY_DEFINED @@ -277,7 +280,7 @@ typedef int cmsBool; # define CMSAPI # endif #else // not Windows -# ifdef HAVE_FUNC_ATTRIBUTE_VISIBILITY +# if defined(HAVE_FUNC_ATTRIBUTE_VISIBILITY) && !defined(CMS_NO_VISIBILITY) # define CMSEXPORT # define CMSAPI __attribute__((visibility("default"))) # else @@ -987,7 +990,6 @@ typedef void* cmsHTRANSFORM; // IEEE 754-2008 "half" #define TYPE_GRAY_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(2)) #define TYPE_RGB_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)) -#define TYPE_RGBA_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)) #define TYPE_CMYK_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)) #define TYPE_RGBA_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)) diff --git a/src/java.desktop/share/native/liblcms/lcms2_internal.h b/src/java.desktop/share/native/liblcms/lcms2_internal.h index 75973edad0d2c..d14c0dd823ea0 100644 --- a/src/java.desktop/share/native/liblcms/lcms2_internal.h +++ b/src/java.desktop/share/native/liblcms/lcms2_internal.h @@ -27,9 +27,10 @@ // However, the following notice accompanied the original version of this // file: // + // // Little Color Management System -// Copyright (c) 1998-2023 Marti Maria Saguer +// Copyright (c) 1998-2024 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -467,7 +468,7 @@ cmsBool _cmsRegisterTransformPlugin(cmsContext ContextID, cmsPluginBase* Plugin // Mutex cmsBool _cmsRegisterMutexPlugin(cmsContext ContextID, cmsPluginBase* Plugin); -// Paralellization +// Parallelization cmsBool _cmsRegisterParallelizationPlugin(cmsContext ContextID, cmsPluginBase* Plugin); // --------------------------------------------------------------------------------------------------------- @@ -1000,7 +1001,7 @@ cmsBool _cmsReadCHAD(cmsMAT3* Dest, cmsHPROFILE hProfile); // Link several profiles to obtain a single LUT modelling the whole color transform. Intents, Black point // compensation and Adaptation parameters may vary across profiles. BPC and Adaptation refers to the PCS -// after the profile. I.e, BPC[0] refers to connexion between profile(0) and profile(1) +// after the profile. I.e, BPC[0] refers to connetion between profile(0) and profile(1) cmsPipeline* _cmsLinkProfiles(cmsContext ContextID, cmsUInt32Number nProfiles, cmsUInt32Number TheIntents[], diff --git a/src/java.desktop/share/native/liblcms/lcms2_plugin.h b/src/java.desktop/share/native/liblcms/lcms2_plugin.h index e7e7bd1f0ce1b..bdfc76f6bf5d5 100644 --- a/src/java.desktop/share/native/liblcms/lcms2_plugin.h +++ b/src/java.desktop/share/native/liblcms/lcms2_plugin.h @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2023 Marti Maria Saguer +// Copyright (c) 1998-2024 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES b/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES index 441b57ecf1ab2..2478fd0fc083c 100644 --- a/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES +++ b/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES @@ -6196,6 +6196,114 @@ Version 1.6.43 [February 23, 2024] consistency verification and text linting. Added version consistency verification to pngtest.c also. +Version 1.6.44 [September 12, 2024] + Hardened calculations in chroma handling to prevent overflows, and + relaxed a constraint in cHRM validation to accomodate the standard + ACES AP1 set of color primaries. + (Contributed by John Bowler) + Removed the ASM implementation of ARM Neon optimizations and updated + the build accordingly. Only the remaining C implementation shall be + used from now on, thus ensuring the support of the PAC/BTI security + features on ARM64. + (Contributed by Ross Burton and John Bowler) + Fixed the pickup of the PNG_HARDWARE_OPTIMIZATIONS option in the + CMake build on FreeBSD/amd64. This is an important performance fix + on this platform. + Applied various fixes and improvements to the CMake build. + (Contributed by Eric Riff, Benjamin Buch and Erik Scholz) + Added fuzzing targets for the simplified read API. + (Contributed by Mikhail Khachayants) + Fixed a build error involving pngtest.c under a custom config. + This was a regression introduced in a code cleanup in libpng-1.6.43. + (Contributed by Ben Wagner) + Fixed and improved the config files for AppVeyor CI and Travis CI. + +Version 1.6.45 [January 7, 2025] + Added support for the cICP chunk. + (Contributed by Lucas Chollet and John Bowler) + Adjusted and improved various checks in colorspace calculations. + (Contributed by John Bowler) + Rearranged the write order of colorspace chunks for better conformance + with the PNG v3 draft specification. + (Contributed by John Bowler) + Raised the minimum required CMake version from 3.6 to 3.14. + Forked off a development branch for libpng version 1.8. + +Version 1.6.46 [January 23, 2025] + Added support for the mDCV and cLLI chunks. + (Contributed by John Bowler) + Fixed a build issue affecting C89 compilers. + This was a regression introduced in libpng-1.6.45. + (Contributed by John Bowler) + Added makefile.c89, specifically for testing C89 compilers. + Cleaned up contrib/pngminus: corrected an old typo, removed an old + workaround, and updated the CMake file. + +Version 1.6.47 [February 18, 2025] + Modified the behaviour of colorspace chunks in order to adhere + to the new precedence rules formulated in the latest draft of + the PNG Specification. + (Contributed by John Bowler) + Fixed a latent bug in `png_write_iCCP`. + This would have been a read-beyond-end-of-malloc vulnerability, + introduced early in the libpng-1.6.0 development, yet (fortunately!) + it was inaccessible before the above-mentioned modification of the + colorspace precedence rules, due to pre-existing colorspace checks. + (Reported by Bob Friesenhahn; fixed by John Bowler) + +Version 1.6.48 [April 30, 2025] + Fixed the floating-point version of the mDCv setter `png_set_mDCv`. + (Reported by Mohit Bakshi; fixed by John Bowler) + Added #error directives to discourage the inclusion of private + libpng implementation header files in PNG-supporting applications. + Added the CMake build option `PNG_LIBCONF_HEADER`, to be used as an + alternative to `DFA_XTRA`. + Removed the Travis CI configuration files, with heartfelt thanks for + their generous support of our project over the past five years! + +Version 1.6.49 [June 12, 2025] + Added SIMD-optimized code for the RISC-V Vector Extension (RVV). + (Contributed by Manfred Schlaegl, Dragos Tiselice and Filip Wasil) + Added various fixes and improvements to the build scripts and to + the sample code. + +Version 1.6.50 [July 1, 2025] + Improved the detection of the RVV Extension on the RISC-V platform. + (Contributed by Filip Wasil) + Replaced inline ASM with C intrinsics in the RVV code. + (Contributed by Filip Wasil) + Fixed a decoder defect in which unknown chunks trailing IDAT, set + to go through the unknown chunk handler, incorrectly triggered + out-of-place IEND errors. + (Contributed by John Bowler) + Fixed the CMake file for cross-platform builds that require `libm`. + +Version 1.6.51 [November 21, 2025] + Fixed CVE-2025-64505 (moderate severity): + Heap buffer overflow in `png_do_quantize` via malformed palette index. + (Reported by Samsung; analyzed by Fabio Gritti.) + Fixed CVE-2025-64506 (moderate severity): + Heap buffer over-read in `png_write_image_8bit` with 8-bit input and + `convert_to_8bit` enabled. + (Reported by Samsung and ; + analyzed by Fabio Gritti.) + Fixed CVE-2025-64720 (high severity): + Buffer overflow in `png_image_read_composite` via incorrect palette + premultiplication. + (Reported by Samsung; analyzed by John Bowler.) + Fixed CVE-2025-65018 (high severity): + Heap buffer overflow in `png_combine_row` triggered via + `png_image_finish_read`. + (Reported by .) + Fixed a memory leak in `png_set_quantize`. + (Reported by Samsung; analyzed by Fabio Gritti.) + Removed the experimental and incomplete ERROR_NUMBERS code. + (Contributed by Tobias Stoeckmann.) + Improved the RISC-V vector extension support; required RVV 1.0 or newer. + (Contributed by Filip Wasil.) + Added GitHub Actions workflows for automated testing. + Performed various refactorings and cleanups. + Send comments/corrections/commendations to png-mng-implement at lists.sf.net. Subscription is required; visit https://lists.sourceforge.net/lists/listinfo/png-mng-implement diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/LICENSE b/src/java.desktop/share/native/libsplashscreen/libpng/LICENSE index 25f298f0fcfd8..ea6df986cb6a7 100644 --- a/src/java.desktop/share/native/libsplashscreen/libpng/LICENSE +++ b/src/java.desktop/share/native/libsplashscreen/libpng/LICENSE @@ -4,8 +4,8 @@ COPYRIGHT NOTICE, DISCLAIMER, and LICENSE PNG Reference Library License version 2 --------------------------------------- - * Copyright (c) 1995-2024 The PNG Reference Library Authors. - * Copyright (c) 2018-2024 Cosmin Truta. + * Copyright (c) 1995-2025 The PNG Reference Library Authors. + * Copyright (c) 2018-2025 Cosmin Truta. * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson. * Copyright (c) 1996-1997 Andreas Dilger. * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/README b/src/java.desktop/share/native/libsplashscreen/libpng/README index a6ca3ae9f9406..5ea329ee3dae6 100644 --- a/src/java.desktop/share/native/libsplashscreen/libpng/README +++ b/src/java.desktop/share/native/libsplashscreen/libpng/README @@ -1,4 +1,4 @@ -README for libpng version 1.6.43 +README for libpng version 1.6.51 ================================ See the note about version numbers near the top of `png.h`. @@ -147,6 +147,7 @@ Files included in this distribution loongarch/ => Optimized code for LoongArch LSX mips/ => Optimized code for MIPS MSA and MIPS MMI powerpc/ => Optimized code for PowerPC VSX + riscv/ => Optimized code for the RISC-V platform ci/ => Scripts for continuous integration contrib/ => External contributions arm-neon/ => Optimized code for the ARM-NEON platform @@ -157,13 +158,12 @@ Files included in this distribution "PNG: The Definitive Guide" by Greg Roelofs, O'Reilly, 1999 libtests/ => Test programs - oss-fuzz/ => Files used by the OSS-Fuzz project for fuzz-testing - libpng pngexif/ => Program to inspect the EXIF information in PNG files pngminim/ => Minimal decoder, encoder, and progressive decoder programs demonstrating the use of pngusr.dfa pngminus/ => Simple pnm2png and png2pnm programs pngsuite/ => Test images + riscv-rvv/ => Optimized code for the RISC-V Vector platform testpngs/ => Test images tools/ => Various tools visupng/ => VisualPng, a Windows viewer for PNG images diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/png.c b/src/java.desktop/share/native/libsplashscreen/libpng/png.c index 232dff876c793..7d85e7c8d5f9d 100644 --- a/src/java.desktop/share/native/libsplashscreen/libpng/png.c +++ b/src/java.desktop/share/native/libsplashscreen/libpng/png.c @@ -29,7 +29,7 @@ * However, the following notice accompanied the original version of this * file and, per its terms, should not be removed: * - * Copyright (c) 2018-2024 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -42,7 +42,34 @@ #include "pngpriv.h" /* Generate a compiler error if there is an old png.h in the search path. */ -typedef png_libpng_version_1_6_43 Your_png_h_is_not_version_1_6_43; +typedef png_libpng_version_1_6_51 Your_png_h_is_not_version_1_6_51; + +/* Sanity check the chunks definitions - PNG_KNOWN_CHUNKS from pngpriv.h and the + * corresponding macro definitions. This causes a compile time failure if + * something is wrong but generates no code. + * + * (1) The first check is that the PNG_CHUNK(cHNK, index) 'index' values must + * increment from 0 to the last value. + */ +#define PNG_CHUNK(cHNK, index) != (index) || ((index)+1) + +#if 0 PNG_KNOWN_CHUNKS < 0 +# error PNG_KNOWN_CHUNKS chunk definitions are not in order +#endif + +#undef PNG_CHUNK + +/* (2) The chunk name macros, png_cHNK, must all be valid and defined. Since + * this is a preprocessor test undefined pp-tokens come out as zero and will + * fail this test. + */ +#define PNG_CHUNK(cHNK, index) !PNG_CHUNK_NAME_VALID(png_ ## cHNK) || + +#if PNG_KNOWN_CHUNKS 0 +# error png_cHNK not defined for some known cHNK +#endif + +#undef PNG_CHUNK /* Tells libpng that we have already handled the first "num_bytes" bytes * of the PNG file signature. If the PNG data is embedded into another @@ -110,10 +137,16 @@ png_zalloc,(voidpf png_ptr, uInt items, uInt size),PNG_ALLOCATED) if (png_ptr == NULL) return NULL; - if (items >= (~(png_alloc_size_t)0)/size) + /* This check against overflow is vestigial, dating back from + * the old times when png_zalloc used to be an exported function. + * We're still keeping it here for now, as an extra-cautious + * prevention against programming errors inside zlib, although it + * should rather be a debug-time assertion instead. + */ + if (size != 0 && items >= (~(png_alloc_size_t)0) / size) { - png_warning (png_voidcast(png_structrp, png_ptr), - "Potential overflow in png_zalloc()"); + png_warning(png_voidcast(png_structrp, png_ptr), + "Potential overflow in png_zalloc()"); return NULL; } @@ -240,10 +273,6 @@ png_user_version_check(png_structrp png_ptr, png_const_charp user_png_ver) png_warning(png_ptr, m); #endif -#ifdef PNG_ERROR_NUMBERS_SUPPORTED - png_ptr->flags = 0; -#endif - return 0; } @@ -270,21 +299,23 @@ png_create_png_struct,(png_const_charp user_png_ver, png_voidp error_ptr, */ memset(&create_struct, 0, (sizeof create_struct)); - /* Added at libpng-1.2.6 */ # ifdef PNG_USER_LIMITS_SUPPORTED create_struct.user_width_max = PNG_USER_WIDTH_MAX; create_struct.user_height_max = PNG_USER_HEIGHT_MAX; # ifdef PNG_USER_CHUNK_CACHE_MAX - /* Added at libpng-1.2.43 and 1.4.0 */ create_struct.user_chunk_cache_max = PNG_USER_CHUNK_CACHE_MAX; # endif -# ifdef PNG_USER_CHUNK_MALLOC_MAX - /* Added at libpng-1.2.43 and 1.4.1, required only for read but exists - * in png_struct regardless. - */ +# if PNG_USER_CHUNK_MALLOC_MAX > 0 /* default to compile-time limit */ create_struct.user_chunk_malloc_max = PNG_USER_CHUNK_MALLOC_MAX; + + /* No compile-time limit, so initialize to the system limit: */ +# elif defined PNG_MAX_MALLOC_64K /* legacy system limit */ + create_struct.user_chunk_malloc_max = 65536U; + +# else /* modern system limit SIZE_MAX (C99) */ + create_struct.user_chunk_malloc_max = PNG_SIZE_MAX; # endif # endif @@ -626,13 +657,6 @@ png_free_data(png_const_structrp png_ptr, png_inforp info_ptr, png_uint_32 mask, /* Free any eXIf entry */ if (((mask & PNG_FREE_EXIF) & info_ptr->free_me) != 0) { -# ifdef PNG_READ_eXIf_SUPPORTED - if (info_ptr->eXIf_buf) - { - png_free(png_ptr, info_ptr->eXIf_buf); - info_ptr->eXIf_buf = NULL; - } -# endif if (info_ptr->exif) { png_free(png_ptr, info_ptr->exif); @@ -707,7 +731,7 @@ png_get_io_ptr(png_const_structrp png_ptr) * function of your own because "FILE *" isn't necessarily available. */ void PNGAPI -png_init_io(png_structrp png_ptr, png_FILE_p fp) +png_init_io(png_structrp png_ptr, FILE *fp) { png_debug(1, "in png_init_io"); @@ -822,8 +846,8 @@ png_get_copyright(png_const_structrp png_ptr) return PNG_STRING_COPYRIGHT #else return PNG_STRING_NEWLINE \ - "libpng version 1.6.43" PNG_STRING_NEWLINE \ - "Copyright (c) 2018-2024 Cosmin Truta" PNG_STRING_NEWLINE \ + "libpng version 1.6.51" PNG_STRING_NEWLINE \ + "Copyright (c) 2018-2025 Cosmin Truta" PNG_STRING_NEWLINE \ "Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson" \ PNG_STRING_NEWLINE \ "Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \ @@ -1067,210 +1091,132 @@ png_zstream_error(png_structrp png_ptr, int ret) } } -/* png_convert_size: a PNGAPI but no longer in png.h, so deleted - * at libpng 1.5.5! - */ - -/* Added at libpng version 1.2.34 and 1.4.0 (moved from pngset.c) */ -#ifdef PNG_GAMMA_SUPPORTED /* always set if COLORSPACE */ -static int -png_colorspace_check_gamma(png_const_structrp png_ptr, - png_colorspacerp colorspace, png_fixed_point gAMA, int from) - /* This is called to check a new gamma value against an existing one. The - * routine returns false if the new gamma value should not be written. - * - * 'from' says where the new gamma value comes from: - * - * 0: the new gamma value is the libpng estimate for an ICC profile - * 1: the new gamma value comes from a gAMA chunk - * 2: the new gamma value comes from an sRGB chunk - */ +#ifdef PNG_COLORSPACE_SUPPORTED +static png_int_32 +png_fp_add(png_int_32 addend0, png_int_32 addend1, int *error) { - png_fixed_point gtest; - - if ((colorspace->flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 && - (png_muldiv(>est, colorspace->gamma, PNG_FP_1, gAMA) == 0 || - png_gamma_significant(gtest) != 0)) + /* Safely add two fixed point values setting an error flag and returning 0.5 + * on overflow. + * IMPLEMENTATION NOTE: ANSI requires signed overflow not to occur, therefore + * relying on addition of two positive values producing a negative one is not + * safe. + */ + if (addend0 > 0) { - /* Either this is an sRGB image, in which case the calculated gamma - * approximation should match, or this is an image with a profile and the - * value libpng calculates for the gamma of the profile does not match the - * value recorded in the file. The former, sRGB, case is an error, the - * latter is just a warning. - */ - if ((colorspace->flags & PNG_COLORSPACE_FROM_sRGB) != 0 || from == 2) - { - png_chunk_report(png_ptr, "gamma value does not match sRGB", - PNG_CHUNK_ERROR); - /* Do not overwrite an sRGB value */ - return from == 2; - } - - else /* sRGB tag not involved */ - { - png_chunk_report(png_ptr, "gamma value does not match libpng estimate", - PNG_CHUNK_WARNING); - return from == 1; - } + if (0x7fffffff - addend0 >= addend1) + return addend0+addend1; } - - return 1; -} - -void /* PRIVATE */ -png_colorspace_set_gamma(png_const_structrp png_ptr, - png_colorspacerp colorspace, png_fixed_point gAMA) -{ - /* Changed in libpng-1.5.4 to limit the values to ensure overflow can't - * occur. Since the fixed point representation is asymmetrical it is - * possible for 1/gamma to overflow the limit of 21474 and this means the - * gamma value must be at least 5/100000 and hence at most 20000.0. For - * safety the limits here are a little narrower. The values are 0.00016 to - * 6250.0, which are truly ridiculous gamma values (and will produce - * displays that are all black or all white.) - * - * In 1.6.0 this test replaces the ones in pngrutil.c, in the gAMA chunk - * handling code, which only required the value to be >0. - */ - png_const_charp errmsg; - - if (gAMA < 16 || gAMA > 625000000) - errmsg = "gamma value out of range"; - -# ifdef PNG_READ_gAMA_SUPPORTED - /* Allow the application to set the gamma value more than once */ - else if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 && - (colorspace->flags & PNG_COLORSPACE_FROM_gAMA) != 0) - errmsg = "duplicate"; -# endif - - /* Do nothing if the colorspace is already invalid */ - else if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0) - return; - - else + else if (addend0 < 0) { - if (png_colorspace_check_gamma(png_ptr, colorspace, gAMA, - 1/*from gAMA*/) != 0) - { - /* Store this gamma value. */ - colorspace->gamma = gAMA; - colorspace->flags |= - (PNG_COLORSPACE_HAVE_GAMMA | PNG_COLORSPACE_FROM_gAMA); - } - - /* At present if the check_gamma test fails the gamma of the colorspace is - * not updated however the colorspace is not invalidated. This - * corresponds to the case where the existing gamma comes from an sRGB - * chunk or profile. An error message has already been output. - */ - return; + if (-0x7fffffff - addend0 <= addend1) + return addend0+addend1; } + else + return addend1; - /* Error exit - errmsg has been set. */ - colorspace->flags |= PNG_COLORSPACE_INVALID; - png_chunk_report(png_ptr, errmsg, PNG_CHUNK_WRITE_ERROR); + *error = 1; + return PNG_FP_1/2; } -void /* PRIVATE */ -png_colorspace_sync_info(png_const_structrp png_ptr, png_inforp info_ptr) +static png_int_32 +png_fp_sub(png_int_32 addend0, png_int_32 addend1, int *error) { - if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0) + /* As above but calculate addend0-addend1. */ + if (addend1 > 0) { - /* Everything is invalid */ - info_ptr->valid &= ~(PNG_INFO_gAMA|PNG_INFO_cHRM|PNG_INFO_sRGB| - PNG_INFO_iCCP); - -# ifdef PNG_COLORSPACE_SUPPORTED - /* Clean up the iCCP profile now if it won't be used. */ - png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, -1/*not used*/); -# else - PNG_UNUSED(png_ptr) -# endif + if (-0x7fffffff + addend1 <= addend0) + return addend0-addend1; } - - else + else if (addend1 < 0) { -# ifdef PNG_COLORSPACE_SUPPORTED - /* Leave the INFO_iCCP flag set if the pngset.c code has already set - * it; this allows a PNG to contain a profile which matches sRGB and - * yet still have that profile retrievable by the application. - */ - if ((info_ptr->colorspace.flags & PNG_COLORSPACE_MATCHES_sRGB) != 0) - info_ptr->valid |= PNG_INFO_sRGB; - - else - info_ptr->valid &= ~PNG_INFO_sRGB; - - if ((info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) - info_ptr->valid |= PNG_INFO_cHRM; - - else - info_ptr->valid &= ~PNG_INFO_cHRM; -# endif - - if ((info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) != 0) - info_ptr->valid |= PNG_INFO_gAMA; - - else - info_ptr->valid &= ~PNG_INFO_gAMA; + if (0x7fffffff + addend1 >= addend0) + return addend0-addend1; } + else + return addend0; + + *error = 1; + return PNG_FP_1/2; } -#ifdef PNG_READ_SUPPORTED -void /* PRIVATE */ -png_colorspace_sync(png_const_structrp png_ptr, png_inforp info_ptr) +static int +png_safe_add(png_int_32 *addend0_and_result, png_int_32 addend1, + png_int_32 addend2) { - if (info_ptr == NULL) /* reduce code size; check here not in the caller */ - return; - - info_ptr->colorspace = png_ptr->colorspace; - png_colorspace_sync_info(png_ptr, info_ptr); + /* Safely add three integers. Returns 0 on success, 1 on overflow. Does not + * set the result on overflow. + */ + int error = 0; + int result = png_fp_add(*addend0_and_result, + png_fp_add(addend1, addend2, &error), + &error); + if (!error) *addend0_and_result = result; + return error; } -#endif -#endif /* GAMMA */ -#ifdef PNG_COLORSPACE_SUPPORTED /* Added at libpng-1.5.5 to support read and write of true CIEXYZ values for * cHRM, as opposed to using chromaticities. These internal APIs return * non-zero on a parameter error. The X, Y and Z values are required to be * positive and less than 1.0. */ -static int +int /* PRIVATE */ png_xy_from_XYZ(png_xy *xy, const png_XYZ *XYZ) { - png_int_32 d, dwhite, whiteX, whiteY; + /* NOTE: returns 0 on success, 1 means error. */ + png_int_32 d, dred, dgreen, dblue, dwhite, whiteX, whiteY; - d = XYZ->red_X + XYZ->red_Y + XYZ->red_Z; - if (png_muldiv(&xy->redx, XYZ->red_X, PNG_FP_1, d) == 0) + /* 'd' in each of the blocks below is just X+Y+Z for each component, + * x, y and z are X,Y,Z/(X+Y+Z). + */ + d = XYZ->red_X; + if (png_safe_add(&d, XYZ->red_Y, XYZ->red_Z)) return 1; - if (png_muldiv(&xy->redy, XYZ->red_Y, PNG_FP_1, d) == 0) + dred = d; + if (png_muldiv(&xy->redx, XYZ->red_X, PNG_FP_1, dred) == 0) + return 1; + if (png_muldiv(&xy->redy, XYZ->red_Y, PNG_FP_1, dred) == 0) return 1; - dwhite = d; - whiteX = XYZ->red_X; - whiteY = XYZ->red_Y; - d = XYZ->green_X + XYZ->green_Y + XYZ->green_Z; - if (png_muldiv(&xy->greenx, XYZ->green_X, PNG_FP_1, d) == 0) + d = XYZ->green_X; + if (png_safe_add(&d, XYZ->green_Y, XYZ->green_Z)) + return 1; + dgreen = d; + if (png_muldiv(&xy->greenx, XYZ->green_X, PNG_FP_1, dgreen) == 0) return 1; - if (png_muldiv(&xy->greeny, XYZ->green_Y, PNG_FP_1, d) == 0) + if (png_muldiv(&xy->greeny, XYZ->green_Y, PNG_FP_1, dgreen) == 0) return 1; - dwhite += d; - whiteX += XYZ->green_X; - whiteY += XYZ->green_Y; - d = XYZ->blue_X + XYZ->blue_Y + XYZ->blue_Z; - if (png_muldiv(&xy->bluex, XYZ->blue_X, PNG_FP_1, d) == 0) + d = XYZ->blue_X; + if (png_safe_add(&d, XYZ->blue_Y, XYZ->blue_Z)) + return 1; + dblue = d; + if (png_muldiv(&xy->bluex, XYZ->blue_X, PNG_FP_1, dblue) == 0) return 1; - if (png_muldiv(&xy->bluey, XYZ->blue_Y, PNG_FP_1, d) == 0) + if (png_muldiv(&xy->bluey, XYZ->blue_Y, PNG_FP_1, dblue) == 0) return 1; - dwhite += d; - whiteX += XYZ->blue_X; - whiteY += XYZ->blue_Y; - /* The reference white is simply the sum of the end-point (X,Y,Z) vectors, - * thus: + /* The reference white is simply the sum of the end-point (X,Y,Z) vectors so + * the fillowing calculates (X+Y+Z) of the reference white (media white, + * encoding white) itself: */ + d = dblue; + if (png_safe_add(&d, dred, dgreen)) + return 1; + dwhite = d; + + /* Find the white X,Y values from the sum of the red, green and blue X,Y + * values. + */ + d = XYZ->red_X; + if (png_safe_add(&d, XYZ->green_X, XYZ->blue_X)) + return 1; + whiteX = d; + + d = XYZ->red_Y; + if (png_safe_add(&d, XYZ->green_Y, XYZ->blue_Y)) + return 1; + whiteY = d; + if (png_muldiv(&xy->whitex, whiteX, PNG_FP_1, dwhite) == 0) return 1; if (png_muldiv(&xy->whitey, whiteY, PNG_FP_1, dwhite) == 0) @@ -1279,9 +1225,10 @@ png_xy_from_XYZ(png_xy *xy, const png_XYZ *XYZ) return 0; } -static int +int /* PRIVATE */ png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy) { + /* NOTE: returns 0 on success, 1 means error. */ png_fixed_point red_inverse, green_inverse, blue_scale; png_fixed_point left, right, denominator; @@ -1289,15 +1236,24 @@ png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy) * have end points with 0 tristimulus values (these are impossible end * points, but they are used to cover the possible colors). We check * xy->whitey against 5, not 0, to avoid a possible integer overflow. + * + * The limits here will *not* accept ACES AP0, where bluey is -7700 + * (-0.0770) because the PNG spec itself requires the xy values to be + * unsigned. whitey is also required to be 5 or more to avoid overflow. + * + * Instead the upper limits have been relaxed to accomodate ACES AP1 where + * redz ends up as -600 (-0.006). ProPhotoRGB was already "in range." + * The new limit accomodates the AP0 and AP1 ranges for z but not AP0 redy. */ - if (xy->redx < 0 || xy->redx > PNG_FP_1) return 1; - if (xy->redy < 0 || xy->redy > PNG_FP_1-xy->redx) return 1; - if (xy->greenx < 0 || xy->greenx > PNG_FP_1) return 1; - if (xy->greeny < 0 || xy->greeny > PNG_FP_1-xy->greenx) return 1; - if (xy->bluex < 0 || xy->bluex > PNG_FP_1) return 1; - if (xy->bluey < 0 || xy->bluey > PNG_FP_1-xy->bluex) return 1; - if (xy->whitex < 0 || xy->whitex > PNG_FP_1) return 1; - if (xy->whitey < 5 || xy->whitey > PNG_FP_1-xy->whitex) return 1; + const png_fixed_point fpLimit = PNG_FP_1+(PNG_FP_1/10); + if (xy->redx < 0 || xy->redx > fpLimit) return 1; + if (xy->redy < 0 || xy->redy > fpLimit-xy->redx) return 1; + if (xy->greenx < 0 || xy->greenx > fpLimit) return 1; + if (xy->greeny < 0 || xy->greeny > fpLimit-xy->greenx) return 1; + if (xy->bluex < 0 || xy->bluex > fpLimit) return 1; + if (xy->bluey < 0 || xy->bluey > fpLimit-xy->bluex) return 1; + if (xy->whitex < 0 || xy->whitex > fpLimit) return 1; + if (xy->whitey < 5 || xy->whitey > fpLimit-xy->whitex) return 1; /* The reverse calculation is more difficult because the original tristimulus * value had 9 independent values (red,green,blue)x(X,Y,Z) however only 8 @@ -1442,18 +1398,23 @@ png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy) * (green-x - blue-x)*(red-y - blue-y)-(green-y - blue-y)*(red-x - blue-x) * * Accuracy: - * The input values have 5 decimal digits of accuracy. The values are all in - * the range 0 < value < 1, so simple products are in the same range but may - * need up to 10 decimal digits to preserve the original precision and avoid - * underflow. Because we are using a 32-bit signed representation we cannot - * match this; the best is a little over 9 decimal digits, less than 10. + * The input values have 5 decimal digits of accuracy. + * + * In the previous implementation the values were all in the range 0 < value + * < 1, so simple products are in the same range but may need up to 10 + * decimal digits to preserve the original precision and avoid underflow. + * Because we are using a 32-bit signed representation we cannot match this; + * the best is a little over 9 decimal digits, less than 10. + * + * This range has now been extended to allow values up to 1.1, or 110,000 in + * fixed point. * * The approach used here is to preserve the maximum precision within the * signed representation. Because the red-scale calculation above uses the - * difference between two products of values that must be in the range -1..+1 - * it is sufficient to divide the product by 7; ceil(100,000/32767*2). The - * factor is irrelevant in the calculation because it is applied to both - * numerator and denominator. + * difference between two products of values that must be in the range + * -1.1..+1.1 it is sufficient to divide the product by 8; + * ceil(121,000/32767*2). The factor is irrelevant in the calculation + * because it is applied to both numerator and denominator. * * Note that the values of the differences of the products of the * chromaticities in the above equations tend to be small, for example for @@ -1475,49 +1436,64 @@ png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy) * Adobe Wide Gamut RGB * 0.258728243040113 0.724682314948566 0.016589442011321 */ - /* By the argument, above overflow should be impossible here. The return - * value of 2 indicates an internal error to the caller. - */ - if (png_muldiv(&left, xy->greenx-xy->bluex, xy->redy - xy->bluey, 7) == 0) - return 2; - if (png_muldiv(&right, xy->greeny-xy->bluey, xy->redx - xy->bluex, 7) == 0) - return 2; - denominator = left - right; - - /* Now find the red numerator. */ - if (png_muldiv(&left, xy->greenx-xy->bluex, xy->whitey-xy->bluey, 7) == 0) - return 2; - if (png_muldiv(&right, xy->greeny-xy->bluey, xy->whitex-xy->bluex, 7) == 0) - return 2; - - /* Overflow is possible here and it indicates an extreme set of PNG cHRM - * chunk values. This calculation actually returns the reciprocal of the - * scale value because this allows us to delay the multiplication of white-y - * into the denominator, which tends to produce a small number. - */ - if (png_muldiv(&red_inverse, xy->whitey, denominator, left-right) == 0 || - red_inverse <= xy->whitey /* r+g+b scales = white scale */) - return 1; + { + int error = 0; - /* Similarly for green_inverse: */ - if (png_muldiv(&left, xy->redy-xy->bluey, xy->whitex-xy->bluex, 7) == 0) - return 2; - if (png_muldiv(&right, xy->redx-xy->bluex, xy->whitey-xy->bluey, 7) == 0) - return 2; - if (png_muldiv(&green_inverse, xy->whitey, denominator, left-right) == 0 || - green_inverse <= xy->whitey) - return 1; + /* By the argument above overflow should be impossible here, however the + * code now simply returns a failure code. The xy subtracts in the + * arguments to png_muldiv are *not* checked for overflow because the + * checks at the start guarantee they are in the range 0..110000 and + * png_fixed_point is a 32-bit signed number. + */ + if (png_muldiv(&left, xy->greenx-xy->bluex, xy->redy - xy->bluey, 8) == 0) + return 1; + if (png_muldiv(&right, xy->greeny-xy->bluey, xy->redx - xy->bluex, 8) == + 0) + return 1; + denominator = png_fp_sub(left, right, &error); + if (error) return 1; - /* And the blue scale, the checks above guarantee this can't overflow but it - * can still produce 0 for extreme cHRM values. - */ - blue_scale = png_reciprocal(xy->whitey) - png_reciprocal(red_inverse) - - png_reciprocal(green_inverse); - if (blue_scale <= 0) - return 1; + /* Now find the red numerator. */ + if (png_muldiv(&left, xy->greenx-xy->bluex, xy->whitey-xy->bluey, 8) == 0) + return 1; + if (png_muldiv(&right, xy->greeny-xy->bluey, xy->whitex-xy->bluex, 8) == + 0) + return 1; + /* Overflow is possible here and it indicates an extreme set of PNG cHRM + * chunk values. This calculation actually returns the reciprocal of the + * scale value because this allows us to delay the multiplication of + * white-y into the denominator, which tends to produce a small number. + */ + if (png_muldiv(&red_inverse, xy->whitey, denominator, + png_fp_sub(left, right, &error)) == 0 || error || + red_inverse <= xy->whitey /* r+g+b scales = white scale */) + return 1; - /* And fill in the png_XYZ: */ + /* Similarly for green_inverse: */ + if (png_muldiv(&left, xy->redy-xy->bluey, xy->whitex-xy->bluex, 8) == 0) + return 1; + if (png_muldiv(&right, xy->redx-xy->bluex, xy->whitey-xy->bluey, 8) == 0) + return 1; + if (png_muldiv(&green_inverse, xy->whitey, denominator, + png_fp_sub(left, right, &error)) == 0 || error || + green_inverse <= xy->whitey) + return 1; + + /* And the blue scale, the checks above guarantee this can't overflow but + * it can still produce 0 for extreme cHRM values. + */ + blue_scale = png_fp_sub(png_fp_sub(png_reciprocal(xy->whitey), + png_reciprocal(red_inverse), &error), + png_reciprocal(green_inverse), &error); + if (error || blue_scale <= 0) + return 1; + } + + /* And fill in the png_XYZ. Again the subtracts are safe because of the + * checks on the xy values at the start (the subtracts just calculate the + * corresponding z values.) + */ if (png_muldiv(&XYZ->red_X, xy->redx, PNG_FP_1, red_inverse) == 0) return 1; if (png_muldiv(&XYZ->red_Y, xy->redy, PNG_FP_1, red_inverse) == 0) @@ -1544,250 +1520,9 @@ png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy) return 0; /*success*/ } +#endif /* COLORSPACE */ -static int -png_XYZ_normalize(png_XYZ *XYZ) -{ - png_int_32 Y; - - if (XYZ->red_Y < 0 || XYZ->green_Y < 0 || XYZ->blue_Y < 0 || - XYZ->red_X < 0 || XYZ->green_X < 0 || XYZ->blue_X < 0 || - XYZ->red_Z < 0 || XYZ->green_Z < 0 || XYZ->blue_Z < 0) - return 1; - - /* Normalize by scaling so the sum of the end-point Y values is PNG_FP_1. - * IMPLEMENTATION NOTE: ANSI requires signed overflow not to occur, therefore - * relying on addition of two positive values producing a negative one is not - * safe. - */ - Y = XYZ->red_Y; - if (0x7fffffff - Y < XYZ->green_X) - return 1; - Y += XYZ->green_Y; - if (0x7fffffff - Y < XYZ->blue_X) - return 1; - Y += XYZ->blue_Y; - - if (Y != PNG_FP_1) - { - if (png_muldiv(&XYZ->red_X, XYZ->red_X, PNG_FP_1, Y) == 0) - return 1; - if (png_muldiv(&XYZ->red_Y, XYZ->red_Y, PNG_FP_1, Y) == 0) - return 1; - if (png_muldiv(&XYZ->red_Z, XYZ->red_Z, PNG_FP_1, Y) == 0) - return 1; - - if (png_muldiv(&XYZ->green_X, XYZ->green_X, PNG_FP_1, Y) == 0) - return 1; - if (png_muldiv(&XYZ->green_Y, XYZ->green_Y, PNG_FP_1, Y) == 0) - return 1; - if (png_muldiv(&XYZ->green_Z, XYZ->green_Z, PNG_FP_1, Y) == 0) - return 1; - - if (png_muldiv(&XYZ->blue_X, XYZ->blue_X, PNG_FP_1, Y) == 0) - return 1; - if (png_muldiv(&XYZ->blue_Y, XYZ->blue_Y, PNG_FP_1, Y) == 0) - return 1; - if (png_muldiv(&XYZ->blue_Z, XYZ->blue_Z, PNG_FP_1, Y) == 0) - return 1; - } - - return 0; -} - -static int -png_colorspace_endpoints_match(const png_xy *xy1, const png_xy *xy2, int delta) -{ - /* Allow an error of +/-0.01 (absolute value) on each chromaticity */ - if (PNG_OUT_OF_RANGE(xy1->whitex, xy2->whitex,delta) || - PNG_OUT_OF_RANGE(xy1->whitey, xy2->whitey,delta) || - PNG_OUT_OF_RANGE(xy1->redx, xy2->redx, delta) || - PNG_OUT_OF_RANGE(xy1->redy, xy2->redy, delta) || - PNG_OUT_OF_RANGE(xy1->greenx, xy2->greenx,delta) || - PNG_OUT_OF_RANGE(xy1->greeny, xy2->greeny,delta) || - PNG_OUT_OF_RANGE(xy1->bluex, xy2->bluex, delta) || - PNG_OUT_OF_RANGE(xy1->bluey, xy2->bluey, delta)) - return 0; - return 1; -} - -/* Added in libpng-1.6.0, a different check for the validity of a set of cHRM - * chunk chromaticities. Earlier checks used to simply look for the overflow - * condition (where the determinant of the matrix to solve for XYZ ends up zero - * because the chromaticity values are not all distinct.) Despite this it is - * theoretically possible to produce chromaticities that are apparently valid - * but that rapidly degrade to invalid, potentially crashing, sets because of - * arithmetic inaccuracies when calculations are performed on them. The new - * check is to round-trip xy -> XYZ -> xy and then check that the result is - * within a small percentage of the original. - */ -static int -png_colorspace_check_xy(png_XYZ *XYZ, const png_xy *xy) -{ - int result; - png_xy xy_test; - - /* As a side-effect this routine also returns the XYZ endpoints. */ - result = png_XYZ_from_xy(XYZ, xy); - if (result != 0) - return result; - - result = png_xy_from_XYZ(&xy_test, XYZ); - if (result != 0) - return result; - - if (png_colorspace_endpoints_match(xy, &xy_test, - 5/*actually, the math is pretty accurate*/) != 0) - return 0; - - /* Too much slip */ - return 1; -} - -/* This is the check going the other way. The XYZ is modified to normalize it - * (another side-effect) and the xy chromaticities are returned. - */ -static int -png_colorspace_check_XYZ(png_xy *xy, png_XYZ *XYZ) -{ - int result; - png_XYZ XYZtemp; - - result = png_XYZ_normalize(XYZ); - if (result != 0) - return result; - - result = png_xy_from_XYZ(xy, XYZ); - if (result != 0) - return result; - - XYZtemp = *XYZ; - return png_colorspace_check_xy(&XYZtemp, xy); -} - -/* Used to check for an endpoint match against sRGB */ -static const png_xy sRGB_xy = /* From ITU-R BT.709-3 */ -{ - /* color x y */ - /* red */ 64000, 33000, - /* green */ 30000, 60000, - /* blue */ 15000, 6000, - /* white */ 31270, 32900 -}; - -static int -png_colorspace_set_xy_and_XYZ(png_const_structrp png_ptr, - png_colorspacerp colorspace, const png_xy *xy, const png_XYZ *XYZ, - int preferred) -{ - if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0) - return 0; - - /* The consistency check is performed on the chromaticities; this factors out - * variations because of the normalization (or not) of the end point Y - * values. - */ - if (preferred < 2 && - (colorspace->flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) - { - /* The end points must be reasonably close to any we already have. The - * following allows an error of up to +/-.001 - */ - if (png_colorspace_endpoints_match(xy, &colorspace->end_points_xy, - 100) == 0) - { - colorspace->flags |= PNG_COLORSPACE_INVALID; - png_benign_error(png_ptr, "inconsistent chromaticities"); - return 0; /* failed */ - } - - /* Only overwrite with preferred values */ - if (preferred == 0) - return 1; /* ok, but no change */ - } - - colorspace->end_points_xy = *xy; - colorspace->end_points_XYZ = *XYZ; - colorspace->flags |= PNG_COLORSPACE_HAVE_ENDPOINTS; - - /* The end points are normally quoted to two decimal digits, so allow +/-0.01 - * on this test. - */ - if (png_colorspace_endpoints_match(xy, &sRGB_xy, 1000) != 0) - colorspace->flags |= PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB; - - else - colorspace->flags &= PNG_COLORSPACE_CANCEL( - PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB); - - return 2; /* ok and changed */ -} - -int /* PRIVATE */ -png_colorspace_set_chromaticities(png_const_structrp png_ptr, - png_colorspacerp colorspace, const png_xy *xy, int preferred) -{ - /* We must check the end points to ensure they are reasonable - in the past - * color management systems have crashed as a result of getting bogus - * colorant values, while this isn't the fault of libpng it is the - * responsibility of libpng because PNG carries the bomb and libpng is in a - * position to protect against it. - */ - png_XYZ XYZ; - - switch (png_colorspace_check_xy(&XYZ, xy)) - { - case 0: /* success */ - return png_colorspace_set_xy_and_XYZ(png_ptr, colorspace, xy, &XYZ, - preferred); - - case 1: - /* We can't invert the chromaticities so we can't produce value XYZ - * values. Likely as not a color management system will fail too. - */ - colorspace->flags |= PNG_COLORSPACE_INVALID; - png_benign_error(png_ptr, "invalid chromaticities"); - break; - - default: - /* libpng is broken; this should be a warning but if it happens we - * want error reports so for the moment it is an error. - */ - colorspace->flags |= PNG_COLORSPACE_INVALID; - png_error(png_ptr, "internal error checking chromaticities"); - } - - return 0; /* failed */ -} - -int /* PRIVATE */ -png_colorspace_set_endpoints(png_const_structrp png_ptr, - png_colorspacerp colorspace, const png_XYZ *XYZ_in, int preferred) -{ - png_XYZ XYZ = *XYZ_in; - png_xy xy; - - switch (png_colorspace_check_XYZ(&xy, &XYZ)) - { - case 0: - return png_colorspace_set_xy_and_XYZ(png_ptr, colorspace, &xy, &XYZ, - preferred); - - case 1: - /* End points are invalid. */ - colorspace->flags |= PNG_COLORSPACE_INVALID; - png_benign_error(png_ptr, "invalid end points"); - break; - - default: - colorspace->flags |= PNG_COLORSPACE_INVALID; - png_error(png_ptr, "internal error checking chromaticities"); - } - - return 0; /* failed */ -} - -#if defined(PNG_sRGB_SUPPORTED) || defined(PNG_iCCP_SUPPORTED) +#ifdef PNG_READ_iCCP_SUPPORTED /* Error message generation */ static char png_icc_tag_char(png_uint_32 byte) @@ -1827,15 +1562,12 @@ is_ICC_signature(png_alloc_size_t it) } static int -png_icc_profile_error(png_const_structrp png_ptr, png_colorspacerp colorspace, - png_const_charp name, png_alloc_size_t value, png_const_charp reason) +png_icc_profile_error(png_const_structrp png_ptr, png_const_charp name, + png_alloc_size_t value, png_const_charp reason) { size_t pos; char message[196]; /* see below for calculation */ - if (colorspace != NULL) - colorspace->flags |= PNG_COLORSPACE_INVALID; - pos = png_safecat(message, (sizeof message), 0, "profile '"); /* 9 chars */ pos = png_safecat(message, pos+79, pos, name); /* Truncate to 79 chars */ pos = png_safecat(message, (sizeof message), pos, "': "); /* +2 = 90 */ @@ -1862,109 +1594,11 @@ png_icc_profile_error(png_const_structrp png_ptr, png_colorspacerp colorspace, pos = png_safecat(message, (sizeof message), pos, reason); PNG_UNUSED(pos) - /* This is recoverable, but make it unconditionally an app_error on write to - * avoid writing invalid ICC profiles into PNG files (i.e., we handle them - * on read, with a warning, but on write unless the app turns off - * application errors the PNG won't be written.) - */ - png_chunk_report(png_ptr, message, - (colorspace != NULL) ? PNG_CHUNK_ERROR : PNG_CHUNK_WRITE_ERROR); + png_chunk_benign_error(png_ptr, message); return 0; } -#endif /* sRGB || iCCP */ -#ifdef PNG_sRGB_SUPPORTED -int /* PRIVATE */ -png_colorspace_set_sRGB(png_const_structrp png_ptr, png_colorspacerp colorspace, - int intent) -{ - /* sRGB sets known gamma, end points and (from the chunk) intent. */ - /* IMPORTANT: these are not necessarily the values found in an ICC profile - * because ICC profiles store values adapted to a D50 environment; it is - * expected that the ICC profile mediaWhitePointTag will be D50; see the - * checks and code elsewhere to understand this better. - * - * These XYZ values, which are accurate to 5dp, produce rgb to gray - * coefficients of (6968,23435,2366), which are reduced (because they add up - * to 32769 not 32768) to (6968,23434,2366). These are the values that - * libpng has traditionally used (and are the best values given the 15bit - * algorithm used by the rgb to gray code.) - */ - static const png_XYZ sRGB_XYZ = /* D65 XYZ (*not* the D50 adapted values!) */ - { - /* color X Y Z */ - /* red */ 41239, 21264, 1933, - /* green */ 35758, 71517, 11919, - /* blue */ 18048, 7219, 95053 - }; - - /* Do nothing if the colorspace is already invalidated. */ - if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0) - return 0; - - /* Check the intent, then check for existing settings. It is valid for the - * PNG file to have cHRM or gAMA chunks along with sRGB, but the values must - * be consistent with the correct values. If, however, this function is - * called below because an iCCP chunk matches sRGB then it is quite - * conceivable that an older app recorded incorrect gAMA and cHRM because of - * an incorrect calculation based on the values in the profile - this does - * *not* invalidate the profile (though it still produces an error, which can - * be ignored.) - */ - if (intent < 0 || intent >= PNG_sRGB_INTENT_LAST) - return png_icc_profile_error(png_ptr, colorspace, "sRGB", - (png_alloc_size_t)intent, "invalid sRGB rendering intent"); - - if ((colorspace->flags & PNG_COLORSPACE_HAVE_INTENT) != 0 && - colorspace->rendering_intent != intent) - return png_icc_profile_error(png_ptr, colorspace, "sRGB", - (png_alloc_size_t)intent, "inconsistent rendering intents"); - - if ((colorspace->flags & PNG_COLORSPACE_FROM_sRGB) != 0) - { - png_benign_error(png_ptr, "duplicate sRGB information ignored"); - return 0; - } - - /* If the standard sRGB cHRM chunk does not match the one from the PNG file - * warn but overwrite the value with the correct one. - */ - if ((colorspace->flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0 && - !png_colorspace_endpoints_match(&sRGB_xy, &colorspace->end_points_xy, - 100)) - png_chunk_report(png_ptr, "cHRM chunk does not match sRGB", - PNG_CHUNK_ERROR); - - /* This check is just done for the error reporting - the routine always - * returns true when the 'from' argument corresponds to sRGB (2). - */ - (void)png_colorspace_check_gamma(png_ptr, colorspace, PNG_GAMMA_sRGB_INVERSE, - 2/*from sRGB*/); - - /* intent: bugs in GCC force 'int' to be used as the parameter type. */ - colorspace->rendering_intent = (png_uint_16)intent; - colorspace->flags |= PNG_COLORSPACE_HAVE_INTENT; - - /* endpoints */ - colorspace->end_points_xy = sRGB_xy; - colorspace->end_points_XYZ = sRGB_XYZ; - colorspace->flags |= - (PNG_COLORSPACE_HAVE_ENDPOINTS|PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB); - - /* gamma */ - colorspace->gamma = PNG_GAMMA_sRGB_INVERSE; - colorspace->flags |= PNG_COLORSPACE_HAVE_GAMMA; - - /* Finally record that we have an sRGB profile */ - colorspace->flags |= - (PNG_COLORSPACE_MATCHES_sRGB|PNG_COLORSPACE_FROM_sRGB); - - return 1; /* set */ -} -#endif /* sRGB */ - -#ifdef PNG_iCCP_SUPPORTED /* Encoded value of D50 as an ICC XYZNumber. From the ICC 2010 spec the value * is XYZ(0.9642,1.0,0.8249), which scales to: * @@ -1974,21 +1608,19 @@ static const png_byte D50_nCIEXYZ[12] = { 0x00, 0x00, 0xf6, 0xd6, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d }; static int /* bool */ -icc_check_length(png_const_structrp png_ptr, png_colorspacerp colorspace, - png_const_charp name, png_uint_32 profile_length) +icc_check_length(png_const_structrp png_ptr, png_const_charp name, + png_uint_32 profile_length) { if (profile_length < 132) - return png_icc_profile_error(png_ptr, colorspace, name, profile_length, - "too short"); + return png_icc_profile_error(png_ptr, name, profile_length, "too short"); return 1; } -#ifdef PNG_READ_iCCP_SUPPORTED int /* PRIVATE */ -png_icc_check_length(png_const_structrp png_ptr, png_colorspacerp colorspace, - png_const_charp name, png_uint_32 profile_length) +png_icc_check_length(png_const_structrp png_ptr, png_const_charp name, + png_uint_32 profile_length) { - if (!icc_check_length(png_ptr, colorspace, name, profile_length)) + if (!icc_check_length(png_ptr, name, profile_length)) return 0; /* This needs to be here because the 'normal' check is in @@ -1997,30 +1629,17 @@ png_icc_check_length(png_const_structrp png_ptr, png_colorspacerp colorspace, * the caller supplies the profile buffer so libpng doesn't allocate it. See * the call to icc_check_length below (the write case). */ -# ifdef PNG_SET_USER_LIMITS_SUPPORTED - else if (png_ptr->user_chunk_malloc_max > 0 && - png_ptr->user_chunk_malloc_max < profile_length) - return png_icc_profile_error(png_ptr, colorspace, name, profile_length, - "exceeds application limits"); -# elif PNG_USER_CHUNK_MALLOC_MAX > 0 - else if (PNG_USER_CHUNK_MALLOC_MAX < profile_length) - return png_icc_profile_error(png_ptr, colorspace, name, profile_length, - "exceeds libpng limits"); -# else /* !SET_USER_LIMITS */ - /* This will get compiled out on all 32-bit and better systems. */ - else if (PNG_SIZE_MAX < profile_length) - return png_icc_profile_error(png_ptr, colorspace, name, profile_length, - "exceeds system limits"); -# endif /* !SET_USER_LIMITS */ + if (profile_length > png_chunk_max(png_ptr)) + return png_icc_profile_error(png_ptr, name, profile_length, + "profile too long"); return 1; } -#endif /* READ_iCCP */ int /* PRIVATE */ -png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, - png_const_charp name, png_uint_32 profile_length, - png_const_bytep profile/* first 132 bytes only */, int color_type) +png_icc_check_header(png_const_structrp png_ptr, png_const_charp name, + png_uint_32 profile_length, + png_const_bytep profile/* first 132 bytes only */, int color_type) { png_uint_32 temp; @@ -2031,18 +1650,18 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, */ temp = png_get_uint_32(profile); if (temp != profile_length) - return png_icc_profile_error(png_ptr, colorspace, name, temp, + return png_icc_profile_error(png_ptr, name, temp, "length does not match profile"); temp = (png_uint_32) (*(profile+8)); if (temp > 3 && (profile_length & 3)) - return png_icc_profile_error(png_ptr, colorspace, name, profile_length, + return png_icc_profile_error(png_ptr, name, profile_length, "invalid length"); temp = png_get_uint_32(profile+128); /* tag count: 12 bytes/tag */ if (temp > 357913930 || /* (2^32-4-132)/12: maximum possible tag count */ profile_length < 132+12*temp) /* truncated tag table */ - return png_icc_profile_error(png_ptr, colorspace, name, temp, + return png_icc_profile_error(png_ptr, name, temp, "tag count too large"); /* The 'intent' must be valid or we can't store it, ICC limits the intent to @@ -2050,14 +1669,14 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, */ temp = png_get_uint_32(profile+64); if (temp >= 0xffff) /* The ICC limit */ - return png_icc_profile_error(png_ptr, colorspace, name, temp, + return png_icc_profile_error(png_ptr, name, temp, "invalid rendering intent"); /* This is just a warning because the profile may be valid in future * versions. */ if (temp >= PNG_sRGB_INTENT_LAST) - (void)png_icc_profile_error(png_ptr, NULL, name, temp, + (void)png_icc_profile_error(png_ptr, name, temp, "intent outside defined range"); /* At this point the tag table can't be checked because it hasn't necessarily @@ -2074,7 +1693,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, */ temp = png_get_uint_32(profile+36); /* signature 'ascp' */ if (temp != 0x61637370) - return png_icc_profile_error(png_ptr, colorspace, name, temp, + return png_icc_profile_error(png_ptr, name, temp, "invalid signature"); /* Currently the PCS illuminant/adopted white point (the computational @@ -2085,7 +1704,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, * following is just a warning. */ if (memcmp(profile+68, D50_nCIEXYZ, 12) != 0) - (void)png_icc_profile_error(png_ptr, NULL, name, 0/*no tag value*/, + (void)png_icc_profile_error(png_ptr, name, 0/*no tag value*/, "PCS illuminant is not D50"); /* The PNG spec requires this: @@ -2113,18 +1732,18 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, { case 0x52474220: /* 'RGB ' */ if ((color_type & PNG_COLOR_MASK_COLOR) == 0) - return png_icc_profile_error(png_ptr, colorspace, name, temp, + return png_icc_profile_error(png_ptr, name, temp, "RGB color space not permitted on grayscale PNG"); break; case 0x47524159: /* 'GRAY' */ if ((color_type & PNG_COLOR_MASK_COLOR) != 0) - return png_icc_profile_error(png_ptr, colorspace, name, temp, + return png_icc_profile_error(png_ptr, name, temp, "Gray color space not permitted on RGB PNG"); break; default: - return png_icc_profile_error(png_ptr, colorspace, name, temp, + return png_icc_profile_error(png_ptr, name, temp, "invalid ICC profile color space"); } @@ -2149,7 +1768,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, case 0x61627374: /* 'abst' */ /* May not be embedded in an image */ - return png_icc_profile_error(png_ptr, colorspace, name, temp, + return png_icc_profile_error(png_ptr, name, temp, "invalid embedded Abstract ICC profile"); case 0x6c696e6b: /* 'link' */ @@ -2159,7 +1778,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, * therefore a DeviceLink profile should not be found embedded in a * PNG. */ - return png_icc_profile_error(png_ptr, colorspace, name, temp, + return png_icc_profile_error(png_ptr, name, temp, "unexpected DeviceLink ICC profile class"); case 0x6e6d636c: /* 'nmcl' */ @@ -2167,7 +1786,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, * contain an AToB0 tag that is open to misinterpretation. Almost * certainly it will fail the tests below. */ - (void)png_icc_profile_error(png_ptr, NULL, name, temp, + (void)png_icc_profile_error(png_ptr, name, temp, "unexpected NamedColor ICC profile class"); break; @@ -2177,7 +1796,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, * tag content to ensure they are backward compatible with one of the * understood profiles. */ - (void)png_icc_profile_error(png_ptr, NULL, name, temp, + (void)png_icc_profile_error(png_ptr, name, temp, "unrecognized ICC profile class"); break; } @@ -2193,7 +1812,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, break; default: - return png_icc_profile_error(png_ptr, colorspace, name, temp, + return png_icc_profile_error(png_ptr, name, temp, "unexpected ICC PCS encoding"); } @@ -2201,9 +1820,9 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, } int /* PRIVATE */ -png_icc_check_tag_table(png_const_structrp png_ptr, png_colorspacerp colorspace, - png_const_charp name, png_uint_32 profile_length, - png_const_bytep profile /* header plus whole tag table */) +png_icc_check_tag_table(png_const_structrp png_ptr, png_const_charp name, + png_uint_32 profile_length, + png_const_bytep profile /* header plus whole tag table */) { png_uint_32 tag_count = png_get_uint_32(profile+128); png_uint_32 itag; @@ -2229,7 +1848,7 @@ png_icc_check_tag_table(png_const_structrp png_ptr, png_colorspacerp colorspace, * profile. */ if (tag_start > profile_length || tag_length > profile_length - tag_start) - return png_icc_profile_error(png_ptr, colorspace, name, tag_id, + return png_icc_profile_error(png_ptr, name, tag_id, "ICC profile tag outside profile"); if ((tag_start & 3) != 0) @@ -2238,307 +1857,132 @@ png_icc_check_tag_table(png_const_structrp png_ptr, png_colorspacerp colorspace, * only a warning here because libpng does not care about the * alignment. */ - (void)png_icc_profile_error(png_ptr, NULL, name, tag_id, + (void)png_icc_profile_error(png_ptr, name, tag_id, "ICC profile tag start not a multiple of 4"); } } return 1; /* success, maybe with warnings */ } +#endif /* READ_iCCP */ -#ifdef PNG_sRGB_SUPPORTED -#if PNG_sRGB_PROFILE_CHECKS >= 0 -/* Information about the known ICC sRGB profiles */ -static const struct -{ - png_uint_32 adler, crc, length; - png_uint_32 md5[4]; - png_byte have_md5; - png_byte is_broken; - png_uint_16 intent; - -# define PNG_MD5(a,b,c,d) { a, b, c, d }, (a!=0)||(b!=0)||(c!=0)||(d!=0) -# define PNG_ICC_CHECKSUM(adler, crc, md5, intent, broke, date, length, fname)\ - { adler, crc, length, md5, broke, intent }, - -} png_sRGB_checks[] = -{ - /* This data comes from contrib/tools/checksum-icc run on downloads of - * all four ICC sRGB profiles from www.color.org. - */ - /* adler32, crc32, MD5[4], intent, date, length, file-name */ - PNG_ICC_CHECKSUM(0x0a3fd9f6, 0x3b8772b9, - PNG_MD5(0x29f83dde, 0xaff255ae, 0x7842fae4, 0xca83390d), 0, 0, - "2009/03/27 21:36:31", 3048, "sRGB_IEC61966-2-1_black_scaled.icc") - - /* ICC sRGB v2 perceptual no black-compensation: */ - PNG_ICC_CHECKSUM(0x4909e5e1, 0x427ebb21, - PNG_MD5(0xc95bd637, 0xe95d8a3b, 0x0df38f99, 0xc1320389), 1, 0, - "2009/03/27 21:37:45", 3052, "sRGB_IEC61966-2-1_no_black_scaling.icc") - - PNG_ICC_CHECKSUM(0xfd2144a1, 0x306fd8ae, - PNG_MD5(0xfc663378, 0x37e2886b, 0xfd72e983, 0x8228f1b8), 0, 0, - "2009/08/10 17:28:01", 60988, "sRGB_v4_ICC_preference_displayclass.icc") - - /* ICC sRGB v4 perceptual */ - PNG_ICC_CHECKSUM(0x209c35d2, 0xbbef7812, - PNG_MD5(0x34562abf, 0x994ccd06, 0x6d2c5721, 0xd0d68c5d), 0, 0, - "2007/07/25 00:05:37", 60960, "sRGB_v4_ICC_preference.icc") - - /* The following profiles have no known MD5 checksum. If there is a match - * on the (empty) MD5 the other fields are used to attempt a match and - * a warning is produced. The first two of these profiles have a 'cprt' tag - * which suggests that they were also made by Hewlett Packard. - */ - PNG_ICC_CHECKSUM(0xa054d762, 0x5d5129ce, - PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 0, - "2004/07/21 18:57:42", 3024, "sRGB_IEC61966-2-1_noBPC.icc") - - /* This is a 'mntr' (display) profile with a mediaWhitePointTag that does not - * match the D50 PCS illuminant in the header (it is in fact the D65 values, - * so the white point is recorded as the un-adapted value.) The profiles - * below only differ in one byte - the intent - and are basically the same as - * the previous profile except for the mediaWhitePointTag error and a missing - * chromaticAdaptationTag. - */ - PNG_ICC_CHECKSUM(0xf784f3fb, 0x182ea552, - PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 0, 1/*broken*/, - "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 perceptual") - - PNG_ICC_CHECKSUM(0x0398f3fc, 0xf29e526d, - PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 1/*broken*/, - "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 media-relative") -}; - +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +#if (defined PNG_READ_mDCV_SUPPORTED) || (defined PNG_READ_cHRM_SUPPORTED) static int -png_compare_ICC_profile_with_sRGB(png_const_structrp png_ptr, - png_const_bytep profile, uLong adler) +have_chromaticities(png_const_structrp png_ptr) { - /* The quick check is to verify just the MD5 signature and trust the - * rest of the data. Because the profile has already been verified for - * correctness this is safe. png_colorspace_set_sRGB will check the 'intent' - * field too, so if the profile has been edited with an intent not defined - * by sRGB (but maybe defined by a later ICC specification) the read of - * the profile will fail at that point. + /* Handle new PNGv3 chunks and the precedence rules to determine whether + * png_struct::chromaticities must be processed. Only required for RGB to + * gray. + * + * mDCV: this is the mastering colour space and it is independent of the + * encoding so it needs to be used regardless of the encoded space. + * + * cICP: first in priority but not yet implemented - the chromaticities come + * from the 'primaries'. + * + * iCCP: not supported by libpng (so ignored) + * + * sRGB: the defaults match sRGB + * + * cHRM: calculate the coefficients */ +# ifdef PNG_READ_mDCV_SUPPORTED + if (png_has_chunk(png_ptr, mDCV)) + return 1; +# define check_chromaticities 1 +# endif /*mDCV*/ - png_uint_32 length = 0; - png_uint_32 intent = 0x10000; /* invalid */ -#if PNG_sRGB_PROFILE_CHECKS > 1 - uLong crc = 0; /* the value for 0 length data */ -#endif - unsigned int i; - -#ifdef PNG_SET_OPTION_SUPPORTED - /* First see if PNG_SKIP_sRGB_CHECK_PROFILE has been set to "on" */ - if (((png_ptr->options >> PNG_SKIP_sRGB_CHECK_PROFILE) & 3) == - PNG_OPTION_ON) - return 0; -#endif - - for (i=0; i < (sizeof png_sRGB_checks) / (sizeof png_sRGB_checks[0]); ++i) - { - if (png_get_uint_32(profile+84) == png_sRGB_checks[i].md5[0] && - png_get_uint_32(profile+88) == png_sRGB_checks[i].md5[1] && - png_get_uint_32(profile+92) == png_sRGB_checks[i].md5[2] && - png_get_uint_32(profile+96) == png_sRGB_checks[i].md5[3]) - { - /* This may be one of the old HP profiles without an MD5, in that - * case we can only use the length and Adler32 (note that these - * are not used by default if there is an MD5!) - */ -# if PNG_sRGB_PROFILE_CHECKS == 0 - if (png_sRGB_checks[i].have_md5 != 0) - return 1+png_sRGB_checks[i].is_broken; -# endif - - /* Profile is unsigned or more checks have been configured in. */ - if (length == 0) - { - length = png_get_uint_32(profile); - intent = png_get_uint_32(profile+64); - } - - /* Length *and* intent must match */ - if (length == (png_uint_32) png_sRGB_checks[i].length && - intent == (png_uint_32) png_sRGB_checks[i].intent) - { - /* Now calculate the adler32 if not done already. */ - if (adler == 0) - { - adler = adler32(0, NULL, 0); - adler = adler32(adler, profile, length); - } - - if (adler == png_sRGB_checks[i].adler) - { - /* These basic checks suggest that the data has not been - * modified, but if the check level is more than 1 perform - * our own crc32 checksum on the data. - */ -# if PNG_sRGB_PROFILE_CHECKS > 1 - if (crc == 0) - { - crc = crc32(0, NULL, 0); - crc = crc32(crc, profile, length); - } - - /* So this check must pass for the 'return' below to happen. - */ - if (crc == png_sRGB_checks[i].crc) -# endif - { - if (png_sRGB_checks[i].is_broken != 0) - { - /* These profiles are known to have bad data that may cause - * problems if they are used, therefore attempt to - * discourage their use, skip the 'have_md5' warning below, - * which is made irrelevant by this error. - */ - png_chunk_report(png_ptr, "known incorrect sRGB profile", - PNG_CHUNK_ERROR); - } - - /* Warn that this being done; this isn't even an error since - * the profile is perfectly valid, but it would be nice if - * people used the up-to-date ones. - */ - else if (png_sRGB_checks[i].have_md5 == 0) - { - png_chunk_report(png_ptr, - "out-of-date sRGB profile with no signature", - PNG_CHUNK_WARNING); - } - - return 1+png_sRGB_checks[i].is_broken; - } - } +# ifdef PNG_READ_sRGB_SUPPORTED + if (png_has_chunk(png_ptr, sRGB)) + return 0; +# endif /*sRGB*/ -# if PNG_sRGB_PROFILE_CHECKS > 0 - /* The signature matched, but the profile had been changed in some - * way. This probably indicates a data error or uninformed hacking. - * Fall through to "no match". - */ - png_chunk_report(png_ptr, - "Not recognizing known sRGB profile that has been edited", - PNG_CHUNK_WARNING); - break; -# endif - } - } - } +# ifdef PNG_READ_cHRM_SUPPORTED + if (png_has_chunk(png_ptr, cHRM)) + return 1; +# define check_chromaticities 1 +# endif /*cHRM*/ - return 0; /* no match */ + return 0; /* sRGB defaults */ } +#endif /* READ_mDCV || READ_cHRM */ void /* PRIVATE */ -png_icc_set_sRGB(png_const_structrp png_ptr, - png_colorspacerp colorspace, png_const_bytep profile, uLong adler) +png_set_rgb_coefficients(png_structrp png_ptr) { - /* Is this profile one of the known ICC sRGB profiles? If it is, just set - * the sRGB information. + /* Set the rgb_to_gray coefficients from the colorspace if available. Note + * that '_set' means that png_rgb_to_gray was called **and** it successfully + * set up the coefficients. */ - if (png_compare_ICC_profile_with_sRGB(png_ptr, profile, adler) != 0) - (void)png_colorspace_set_sRGB(png_ptr, colorspace, - (int)/*already checked*/png_get_uint_32(profile+64)); -} -#endif /* PNG_sRGB_PROFILE_CHECKS >= 0 */ -#endif /* sRGB */ - -int /* PRIVATE */ -png_colorspace_set_ICC(png_const_structrp png_ptr, png_colorspacerp colorspace, - png_const_charp name, png_uint_32 profile_length, png_const_bytep profile, - int color_type) -{ - if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0) - return 0; - - if (icc_check_length(png_ptr, colorspace, name, profile_length) != 0 && - png_icc_check_header(png_ptr, colorspace, name, profile_length, profile, - color_type) != 0 && - png_icc_check_tag_table(png_ptr, colorspace, name, profile_length, - profile) != 0) + if (png_ptr->rgb_to_gray_coefficients_set == 0) { -# if defined(PNG_sRGB_SUPPORTED) && PNG_sRGB_PROFILE_CHECKS >= 0 - /* If no sRGB support, don't try storing sRGB information */ - png_icc_set_sRGB(png_ptr, colorspace, profile, 0); -# endif - return 1; - } +# if check_chromaticities + png_XYZ xyz; - /* Failure case */ - return 0; -} -#endif /* iCCP */ - -#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED -void /* PRIVATE */ -png_colorspace_set_rgb_coefficients(png_structrp png_ptr) -{ - /* Set the rgb_to_gray coefficients from the colorspace. */ - if (png_ptr->rgb_to_gray_coefficients_set == 0 && - (png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) - { - /* png_set_background has not been called, get the coefficients from the Y - * values of the colorspace colorants. - */ - png_fixed_point r = png_ptr->colorspace.end_points_XYZ.red_Y; - png_fixed_point g = png_ptr->colorspace.end_points_XYZ.green_Y; - png_fixed_point b = png_ptr->colorspace.end_points_XYZ.blue_Y; - png_fixed_point total = r+g+b; - - if (total > 0 && - r >= 0 && png_muldiv(&r, r, 32768, total) && r >= 0 && r <= 32768 && - g >= 0 && png_muldiv(&g, g, 32768, total) && g >= 0 && g <= 32768 && - b >= 0 && png_muldiv(&b, b, 32768, total) && b >= 0 && b <= 32768 && - r+g+b <= 32769) + if (have_chromaticities(png_ptr) && + png_XYZ_from_xy(&xyz, &png_ptr->chromaticities) == 0) { - /* We allow 0 coefficients here. r+g+b may be 32769 if two or - * all of the coefficients were rounded up. Handle this by - * reducing the *largest* coefficient by 1; this matches the - * approach used for the default coefficients in pngrtran.c + /* png_set_rgb_to_gray has not set the coefficients, get them from the + * Y * values of the colorspace colorants. */ - int add = 0; + png_fixed_point r = xyz.red_Y; + png_fixed_point g = xyz.green_Y; + png_fixed_point b = xyz.blue_Y; + png_fixed_point total = r+g+b; + + if (total > 0 && + r >= 0 && png_muldiv(&r, r, 32768, total) && r >= 0 && r <= 32768 && + g >= 0 && png_muldiv(&g, g, 32768, total) && g >= 0 && g <= 32768 && + b >= 0 && png_muldiv(&b, b, 32768, total) && b >= 0 && b <= 32768 && + r+g+b <= 32769) + { + /* We allow 0 coefficients here. r+g+b may be 32769 if two or + * all of the coefficients were rounded up. Handle this by + * reducing the *largest* coefficient by 1; this matches the + * approach used for the default coefficients in pngrtran.c + */ + int add = 0; - if (r+g+b > 32768) - add = -1; - else if (r+g+b < 32768) - add = 1; + if (r+g+b > 32768) + add = -1; + else if (r+g+b < 32768) + add = 1; - if (add != 0) - { - if (g >= r && g >= b) - g += add; - else if (r >= g && r >= b) - r += add; - else - b += add; - } + if (add != 0) + { + if (g >= r && g >= b) + g += add; + else if (r >= g && r >= b) + r += add; + else + b += add; + } - /* Check for an internal error. */ - if (r+g+b != 32768) - png_error(png_ptr, - "internal error handling cHRM coefficients"); + /* Check for an internal error. */ + if (r+g+b != 32768) + png_error(png_ptr, + "internal error handling cHRM coefficients"); - else - { - png_ptr->rgb_to_gray_red_coeff = (png_uint_16)r; - png_ptr->rgb_to_gray_green_coeff = (png_uint_16)g; + else + { + png_ptr->rgb_to_gray_red_coeff = (png_uint_16)r; + png_ptr->rgb_to_gray_green_coeff = (png_uint_16)g; + } } } - - /* This is a png_error at present even though it could be ignored - - * it should never happen, but it is important that if it does, the - * bug is fixed. - */ else - png_error(png_ptr, "internal error handling cHRM->XYZ"); +# endif /* check_chromaticities */ + { + /* Use the historical REC 709 (etc) values: */ + png_ptr->rgb_to_gray_red_coeff = 6968; + png_ptr->rgb_to_gray_green_coeff = 23434; + /* png_ptr->rgb_to_gray_blue_coeff = 2366; */ + } } } #endif /* READ_RGB_TO_GRAY */ -#endif /* COLORSPACE */ - void /* PRIVATE */ png_check_IHDR(png_const_structrp png_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, @@ -3320,7 +2764,27 @@ png_fixed(png_const_structrp png_ptr, double fp, png_const_charp text) } #endif -#if defined(PNG_GAMMA_SUPPORTED) || defined(PNG_COLORSPACE_SUPPORTED) ||\ +#if defined(PNG_FLOATING_POINT_SUPPORTED) && \ + !defined(PNG_FIXED_POINT_MACRO_SUPPORTED) && \ + (defined(PNG_cLLI_SUPPORTED) || defined(PNG_mDCV_SUPPORTED)) +png_uint_32 +png_fixed_ITU(png_const_structrp png_ptr, double fp, png_const_charp text) +{ + double r = floor(10000 * fp + .5); + + if (r > 2147483647. || r < 0) + png_fixed_error(png_ptr, text); + +# ifndef PNG_ERROR_TEXT_SUPPORTED + PNG_UNUSED(text) +# endif + + return (png_uint_32)r; +} +#endif + + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_COLORSPACE_SUPPORTED) ||\ defined(PNG_INCH_CONVERSIONS_SUPPORTED) || defined(PNG_READ_pHYs_SUPPORTED) /* muldiv functions */ /* This API takes signed arguments and rounds the result to the nearest @@ -3328,7 +2792,7 @@ png_fixed(png_const_structrp png_ptr, double fp, png_const_charp text) * the nearest .00001). Overflow and divide by zero are signalled in * the result, a boolean - true on success, false on overflow. */ -int +int /* PRIVATE */ png_muldiv(png_fixed_point_p res, png_fixed_point a, png_int_32 times, png_int_32 divisor) { @@ -3442,27 +2906,7 @@ png_muldiv(png_fixed_point_p res, png_fixed_point a, png_int_32 times, return 0; } -#endif /* READ_GAMMA || INCH_CONVERSIONS */ - -#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_INCH_CONVERSIONS_SUPPORTED) -/* The following is for when the caller doesn't much care about the - * result. - */ -png_fixed_point -png_muldiv_warn(png_const_structrp png_ptr, png_fixed_point a, png_int_32 times, - png_int_32 divisor) -{ - png_fixed_point result; - - if (png_muldiv(&result, a, times, divisor) != 0) - return result; - - png_warning(png_ptr, "fixed point overflow ignored"); - return 0; -} -#endif -#ifdef PNG_GAMMA_SUPPORTED /* more fixed point functions for gamma */ /* Calculate a reciprocal, return 0 on div-by-zero or overflow. */ png_fixed_point png_reciprocal(png_fixed_point a) @@ -3481,26 +2925,38 @@ png_reciprocal(png_fixed_point a) return 0; /* error/overflow */ } +#endif /* READ_GAMMA || COLORSPACE || INCH_CONVERSIONS || READ_pHYS */ +#ifdef PNG_READ_GAMMA_SUPPORTED /* This is the shared test on whether a gamma value is 'significant' - whether * it is worth doing gamma correction. */ int /* PRIVATE */ png_gamma_significant(png_fixed_point gamma_val) { + /* sRGB: 1/2.2 == 0.4545(45) + * AdobeRGB: 1/(2+51/256) ~= 0.45471 5dp + * + * So the correction from AdobeRGB to sRGB (output) is: + * + * 2.2/(2+51/256) == 1.00035524 + * + * I.e. vanishly small (<4E-4) but still detectable in 16-bit linear (+/- + * 23). Note that the Adobe choice seems to be something intended to give an + * exact number with 8 binary fractional digits - it is the closest to 2.2 + * that is possible a base 2 .8p representation. + */ return gamma_val < PNG_FP_1 - PNG_GAMMA_THRESHOLD_FIXED || gamma_val > PNG_FP_1 + PNG_GAMMA_THRESHOLD_FIXED; } -#endif -#ifdef PNG_READ_GAMMA_SUPPORTED -#ifdef PNG_16BIT_SUPPORTED +#ifndef PNG_FLOATING_ARITHMETIC_SUPPORTED /* A local convenience routine. */ static png_fixed_point png_product2(png_fixed_point a, png_fixed_point b) { - /* The required result is 1/a * 1/b; the following preserves accuracy. */ -#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + /* The required result is a * b; the following preserves accuracy. */ +#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED /* Should now be unused */ double r = a * 1E-5; r *= b; r = floor(r+.5); @@ -3516,9 +2972,8 @@ png_product2(png_fixed_point a, png_fixed_point b) return 0; /* overflow */ } -#endif /* 16BIT */ +#endif /* FLOATING_ARITHMETIC */ -/* The inverse of the above. */ png_fixed_point png_reciprocal2(png_fixed_point a, png_fixed_point b) { @@ -4171,10 +3626,27 @@ png_destroy_gamma_table(png_structrp png_ptr) * tables, we don't make a full table if we are reducing to 8-bit in * the future. Note also how the gamma_16 tables are segmented so that * we don't need to allocate > 64K chunks for a full 16-bit table. + * + * TODO: move this to pngrtran.c and make it static. Better yet create + * pngcolor.c and put all the PNG_COLORSPACE stuff in there. */ +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +# define GAMMA_TRANSFORMS 1 /* #ifdef CSE */ +#else +# define GAMMA_TRANSFORMS 0 +#endif + void /* PRIVATE */ png_build_gamma_table(png_structrp png_ptr, int bit_depth) { + png_fixed_point file_gamma, screen_gamma; + png_fixed_point correction; +# if GAMMA_TRANSFORMS + png_fixed_point file_to_linear, linear_to_screen; +# endif + png_debug(1, "in png_build_gamma_table"); /* Remove any existing table; this copes with multiple calls to @@ -4189,27 +3661,44 @@ png_build_gamma_table(png_structrp png_ptr, int bit_depth) png_destroy_gamma_table(png_ptr); } + /* The following fields are set, finally, in png_init_read_transformations. + * If file_gamma is 0 (unset) nothing can be done otherwise if screen_gamma + * is 0 (unset) there is no gamma correction but to/from linear is possible. + */ + file_gamma = png_ptr->file_gamma; + screen_gamma = png_ptr->screen_gamma; +# if GAMMA_TRANSFORMS + file_to_linear = png_reciprocal(file_gamma); +# endif + + if (screen_gamma > 0) + { +# if GAMMA_TRANSFORMS + linear_to_screen = png_reciprocal(screen_gamma); +# endif + correction = png_reciprocal2(screen_gamma, file_gamma); + } + else /* screen gamma unknown */ + { +# if GAMMA_TRANSFORMS + linear_to_screen = file_gamma; +# endif + correction = PNG_FP_1; + } + if (bit_depth <= 8) { - png_build_8bit_table(png_ptr, &png_ptr->gamma_table, - png_ptr->screen_gamma > 0 ? - png_reciprocal2(png_ptr->colorspace.gamma, - png_ptr->screen_gamma) : PNG_FP_1); + png_build_8bit_table(png_ptr, &png_ptr->gamma_table, correction); -#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ - defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ - defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +#if GAMMA_TRANSFORMS if ((png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY)) != 0) { - png_build_8bit_table(png_ptr, &png_ptr->gamma_to_1, - png_reciprocal(png_ptr->colorspace.gamma)); + png_build_8bit_table(png_ptr, &png_ptr->gamma_to_1, file_to_linear); png_build_8bit_table(png_ptr, &png_ptr->gamma_from_1, - png_ptr->screen_gamma > 0 ? - png_reciprocal(png_ptr->screen_gamma) : - png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */); + linear_to_screen); } -#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ +#endif /* GAMMA_TRANSFORMS */ } #ifdef PNG_16BIT_SUPPORTED else @@ -4275,32 +3764,26 @@ png_build_gamma_table(png_structrp png_ptr, int bit_depth) * reduced to 8 bits. */ if ((png_ptr->transformations & (PNG_16_TO_8 | PNG_SCALE_16_TO_8)) != 0) - png_build_16to8_table(png_ptr, &png_ptr->gamma_16_table, shift, - png_ptr->screen_gamma > 0 ? png_product2(png_ptr->colorspace.gamma, - png_ptr->screen_gamma) : PNG_FP_1); - + png_build_16to8_table(png_ptr, &png_ptr->gamma_16_table, shift, + png_reciprocal(correction)); else - png_build_16bit_table(png_ptr, &png_ptr->gamma_16_table, shift, - png_ptr->screen_gamma > 0 ? png_reciprocal2(png_ptr->colorspace.gamma, - png_ptr->screen_gamma) : PNG_FP_1); + png_build_16bit_table(png_ptr, &png_ptr->gamma_16_table, shift, + correction); -#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ - defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ - defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +# if GAMMA_TRANSFORMS if ((png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY)) != 0) { png_build_16bit_table(png_ptr, &png_ptr->gamma_16_to_1, shift, - png_reciprocal(png_ptr->colorspace.gamma)); + file_to_linear); /* Notice that the '16 from 1' table should be full precision, however * the lookup on this table still uses gamma_shift, so it can't be. * TODO: fix this. */ png_build_16bit_table(png_ptr, &png_ptr->gamma_16_from_1, shift, - png_ptr->screen_gamma > 0 ? png_reciprocal(png_ptr->screen_gamma) : - png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */); + linear_to_screen); } -#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ +#endif /* GAMMA_TRANSFORMS */ } #endif /* 16BIT */ } @@ -4515,7 +3998,7 @@ png_image_free_function(png_voidp argument) # ifdef PNG_STDIO_SUPPORTED if (cp->owned_file != 0) { - FILE *fp = png_voidcast(FILE*, cp->png_ptr->io_ptr); + FILE *fp = png_voidcast(FILE *, cp->png_ptr->io_ptr); cp->owned_file = 0; /* Ignore errors here. */ diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/png.h b/src/java.desktop/share/native/libsplashscreen/libpng/png.h index 9f61a773c1ddb..d39ff73552c60 100644 --- a/src/java.desktop/share/native/libsplashscreen/libpng/png.h +++ b/src/java.desktop/share/native/libsplashscreen/libpng/png.h @@ -29,9 +29,9 @@ * However, the following notice accompanied the original version of this * file and, per its terms, should not be removed: * - * libpng version 1.6.43 + * libpng version 1.6.51 * - * Copyright (c) 2018-2024 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -43,7 +43,7 @@ * libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger * libpng versions 0.97, January 1998, through 1.6.35, July 2018: * Glenn Randers-Pehrson - * libpng versions 1.6.36, December 2018, through 1.6.43, February 2024: + * libpng versions 1.6.36, December 2018, through 1.6.51, November 2025: * Cosmin Truta * See also "Contributing Authors", below. */ @@ -55,8 +55,8 @@ * PNG Reference Library License version 2 * --------------------------------------- * - * * Copyright (c) 1995-2024 The PNG Reference Library Authors. - * * Copyright (c) 2018-2024 Cosmin Truta. + * * Copyright (c) 1995-2025 The PNG Reference Library Authors. + * * Copyright (c) 2018-2025 Cosmin Truta. * * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson. * * Copyright (c) 1996-1997 Andreas Dilger. * * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -267,7 +267,7 @@ * ... * 1.5.30 15 10530 15.so.15.30[.0] * ... - * 1.6.43 16 10643 16.so.16.43[.0] + * 1.6.51 16 10651 16.so.16.51[.0] * * Henceforth the source version will match the shared-library major and * minor numbers; the shared-library major version number will be used for @@ -303,7 +303,7 @@ */ /* Version information for png.h - this should match the version in png.c */ -#define PNG_LIBPNG_VER_STRING "1.6.43" +#define PNG_LIBPNG_VER_STRING "1.6.51" #define PNG_HEADER_VERSION_STRING " libpng version " PNG_LIBPNG_VER_STRING "\n" /* The versions of shared library builds should stay in sync, going forward */ @@ -314,18 +314,18 @@ /* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */ #define PNG_LIBPNG_VER_MAJOR 1 #define PNG_LIBPNG_VER_MINOR 6 -#define PNG_LIBPNG_VER_RELEASE 43 +#define PNG_LIBPNG_VER_RELEASE 51 /* This should be zero for a public release, or non-zero for a * development version. */ -#define PNG_LIBPNG_VER_BUILD 0 +#define PNG_LIBPNG_VER_BUILD 0 /* Release Status */ -#define PNG_LIBPNG_BUILD_ALPHA 1 -#define PNG_LIBPNG_BUILD_BETA 2 -#define PNG_LIBPNG_BUILD_RC 3 -#define PNG_LIBPNG_BUILD_STABLE 4 +#define PNG_LIBPNG_BUILD_ALPHA 1 +#define PNG_LIBPNG_BUILD_BETA 2 +#define PNG_LIBPNG_BUILD_RC 3 +#define PNG_LIBPNG_BUILD_STABLE 4 #define PNG_LIBPNG_BUILD_RELEASE_STATUS_MASK 7 /* Release-Specific Flags */ @@ -345,7 +345,7 @@ * From version 1.0.1 it is: * XXYYZZ, where XX=major, YY=minor, ZZ=release */ -#define PNG_LIBPNG_VER 10643 /* 1.6.43 */ +#define PNG_LIBPNG_VER 10651 /* 1.6.51 */ /* Library configuration: these options cannot be changed after * the library has been built. @@ -455,7 +455,7 @@ extern "C" { /* This triggers a compiler error in png.c, if png.c and png.h * do not agree upon the version number. */ -typedef char* png_libpng_version_1_6_43; +typedef char* png_libpng_version_1_6_51; /* Basic control structions. Read libpng-manual.txt or libpng.3 for more info. * @@ -773,6 +773,21 @@ typedef png_unknown_chunk * * png_unknown_chunkpp; #define PNG_INFO_sCAL 0x4000U /* ESR, 1.0.6 */ #define PNG_INFO_IDAT 0x8000U /* ESR, 1.0.6 */ #define PNG_INFO_eXIf 0x10000U /* GR-P, 1.6.31 */ +#define PNG_INFO_cICP 0x20000U /* PNGv3: 1.6.45 */ +#define PNG_INFO_cLLI 0x40000U /* PNGv3: 1.6.45 */ +#define PNG_INFO_mDCV 0x80000U /* PNGv3: 1.6.45 */ +/* APNG: these chunks are stored as unknown, these flags are never set + * however they are provided as a convenience for implementors of APNG and + * avoids any merge conflicts. + * + * Private chunks: these chunk names violate the chunk name recommendations + * because the chunk definitions have no signature and because the private + * chunks with these names have been reserved. Private definitions should + * avoid them. + */ +#define PNG_INFO_acTL 0x100000U /* PNGv3: 1.6.45: unknown */ +#define PNG_INFO_fcTL 0x200000U /* PNGv3: 1.6.45: unknown */ +#define PNG_INFO_fdAT 0x400000U /* PNGv3: 1.6.45: unknown */ /* This is used for the transformation routines, as some of them * change these values for the row. It also should enable using @@ -852,7 +867,7 @@ typedef PNG_CALLBACK(int, *png_user_chunk_ptr, (png_structp, * your compiler. This may be very difficult - try using a different compiler * to build the library! */ -PNG_FUNCTION(void, (PNGCAPI *png_longjmp_ptr), PNGARG((jmp_buf, int)), typedef); +PNG_FUNCTION(void, (PNGCAPI *png_longjmp_ptr), (jmp_buf, int), typedef); #endif /* Transform masks for the high-level interface */ @@ -1584,7 +1599,7 @@ PNG_EXPORT(226, void, png_set_text_compression_method, (png_structrp png_ptr, #ifdef PNG_STDIO_SUPPORTED /* Initialize the input/output for the PNG file to the default functions. */ -PNG_EXPORT(74, void, png_init_io, (png_structrp png_ptr, png_FILE_p fp)); +PNG_EXPORT(74, void, png_init_io, (png_structrp png_ptr, FILE *fp)); #endif /* Replace the (error and abort), and warning functions with user @@ -2002,6 +2017,46 @@ PNG_FIXED_EXPORT(233, void, png_set_cHRM_XYZ_fixed, (png_const_structrp png_ptr, png_fixed_point int_blue_Z)) #endif +#ifdef PNG_cICP_SUPPORTED +PNG_EXPORT(250, png_uint_32, png_get_cICP, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_bytep colour_primaries, + png_bytep transfer_function, png_bytep matrix_coefficients, + png_bytep video_full_range_flag)); +#endif + +#ifdef PNG_cICP_SUPPORTED +PNG_EXPORT(251, void, png_set_cICP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_byte colour_primaries, + png_byte transfer_function, png_byte matrix_coefficients, + png_byte video_full_range_flag)); +#endif + +#ifdef PNG_cLLI_SUPPORTED +PNG_FP_EXPORT(252, png_uint_32, png_get_cLLI, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *maximum_content_light_level, + double *maximum_frame_average_light_level)) +PNG_FIXED_EXPORT(253, png_uint_32, png_get_cLLI_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + /* The values below are in cd/m2 (nits) and are scaled by 10,000; not + * 100,000 as in the case of png_fixed_point. + */ + png_uint_32p maximum_content_light_level_scaled_by_10000, + png_uint_32p maximum_frame_average_light_level_scaled_by_10000)) +#endif + +#ifdef PNG_cLLI_SUPPORTED +PNG_FP_EXPORT(254, void, png_set_cLLI, (png_const_structrp png_ptr, + png_inforp info_ptr, double maximum_content_light_level, + double maximum_frame_average_light_level)) +PNG_FIXED_EXPORT(255, void, png_set_cLLI_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, + /* The values below are in cd/m2 (nits) and are scaled by 10,000; not + * 100,000 as in the case of png_fixed_point. + */ + png_uint_32 maximum_content_light_level_scaled_by_10000, + png_uint_32 maximum_frame_average_light_level_scaled_by_10000)) +#endif + #ifdef PNG_eXIf_SUPPORTED PNG_EXPORT(246, png_uint_32, png_get_eXIf, (png_const_structrp png_ptr, png_inforp info_ptr, png_bytep *exif)); @@ -2046,6 +2101,60 @@ PNG_EXPORT(144, void, png_set_IHDR, (png_const_structrp png_ptr, int color_type, int interlace_method, int compression_method, int filter_method)); +#ifdef PNG_mDCV_SUPPORTED +PNG_FP_EXPORT(256, png_uint_32, png_get_mDCV, (png_const_structrp png_ptr, + png_const_inforp info_ptr, + /* The chromaticities of the mastering display. As cHRM, but independent of + * the encoding endpoints in cHRM, or cICP, or iCCP. These values will + * always be in the range 0 to 1.3107. + */ + double *white_x, double *white_y, double *red_x, double *red_y, + double *green_x, double *green_y, double *blue_x, double *blue_y, + /* Mastering display luminance in cd/m2 (nits). */ + double *mastering_display_maximum_luminance, + double *mastering_display_minimum_luminance)) + +PNG_FIXED_EXPORT(257, png_uint_32, png_get_mDCV_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_white_x, png_fixed_point *int_white_y, + png_fixed_point *int_red_x, png_fixed_point *int_red_y, + png_fixed_point *int_green_x, png_fixed_point *int_green_y, + png_fixed_point *int_blue_x, png_fixed_point *int_blue_y, + /* Mastering display luminance in cd/m2 (nits) multiplied (scaled) by + * 10,000. + */ + png_uint_32p mastering_display_maximum_luminance_scaled_by_10000, + png_uint_32p mastering_display_minimum_luminance_scaled_by_10000)) +#endif + +#ifdef PNG_mDCV_SUPPORTED +PNG_FP_EXPORT(258, void, png_set_mDCV, (png_const_structrp png_ptr, + png_inforp info_ptr, + /* The chromaticities of the mastering display. As cHRM, but independent of + * the encoding endpoints in cHRM, or cICP, or iCCP. + */ + double white_x, double white_y, double red_x, double red_y, double green_x, + double green_y, double blue_x, double blue_y, + /* Mastering display luminance in cd/m2 (nits). */ + double mastering_display_maximum_luminance, + double mastering_display_minimum_luminance)) + +PNG_FIXED_EXPORT(259, void, png_set_mDCV_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, + /* The admissible range of these values is not the full range of a PNG + * fixed point value. Negative values cannot be encoded and the maximum + * value is about 1.3 */ + png_fixed_point int_white_x, png_fixed_point int_white_y, + png_fixed_point int_red_x, png_fixed_point int_red_y, + png_fixed_point int_green_x, png_fixed_point int_green_y, + png_fixed_point int_blue_x, png_fixed_point int_blue_y, + /* These are PNG unsigned 4 byte values: 31-bit unsigned values. The MSB + * must be zero. + */ + png_uint_32 mastering_display_maximum_luminance_scaled_by_10000, + png_uint_32 mastering_display_minimum_luminance_scaled_by_10000)) +#endif + #ifdef PNG_oFFs_SUPPORTED PNG_EXPORT(145, png_uint_32, png_get_oFFs, (png_const_structrp png_ptr, png_const_inforp info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, @@ -3008,7 +3117,7 @@ PNG_EXPORT(234, int, png_image_begin_read_from_file, (png_imagep image, */ PNG_EXPORT(235, int, png_image_begin_read_from_stdio, (png_imagep image, - FILE* file)); + FILE *file)); /* The PNG header is read from the stdio FILE object. */ #endif /* STDIO */ @@ -3083,7 +3192,7 @@ PNG_EXPORT(239, int, png_image_write_to_file, (png_imagep image, PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file, int convert_to_8_bit, const void *buffer, png_int_32 row_stride, const void *colormap)); - /* Write the image to the given (FILE*). */ + /* Write the image to the given FILE object. */ #endif /* SIMPLIFIED_WRITE_STDIO */ /* With all write APIs if image is in one of the linear formats with 16-bit @@ -3223,26 +3332,45 @@ PNG_EXPORT(245, int, png_image_write_to_memory, (png_imagep image, void *memory, * selected at run time. */ #ifdef PNG_SET_OPTION_SUPPORTED + +/* HARDWARE: ARM Neon SIMD instructions supported */ #ifdef PNG_ARM_NEON_API_SUPPORTED -# define PNG_ARM_NEON 0 /* HARDWARE: ARM Neon SIMD instructions supported */ +# define PNG_ARM_NEON 0 #endif -#define PNG_MAXIMUM_INFLATE_WINDOW 2 /* SOFTWARE: force maximum window */ -#define PNG_SKIP_sRGB_CHECK_PROFILE 4 /* SOFTWARE: Check ICC profile for sRGB */ + +/* SOFTWARE: Force maximum window */ +#define PNG_MAXIMUM_INFLATE_WINDOW 2 + +/* SOFTWARE: Check ICC profile for sRGB */ +#define PNG_SKIP_sRGB_CHECK_PROFILE 4 + +/* HARDWARE: MIPS MSA SIMD instructions supported */ #ifdef PNG_MIPS_MSA_API_SUPPORTED -# define PNG_MIPS_MSA 6 /* HARDWARE: MIPS Msa SIMD instructions supported */ +# define PNG_MIPS_MSA 6 #endif + +/* SOFTWARE: Disable Adler32 check on IDAT */ #ifdef PNG_DISABLE_ADLER32_CHECK_SUPPORTED -# define PNG_IGNORE_ADLER32 8 /* SOFTWARE: disable Adler32 check on IDAT */ +# define PNG_IGNORE_ADLER32 8 #endif + +/* HARDWARE: PowerPC VSX SIMD instructions supported */ #ifdef PNG_POWERPC_VSX_API_SUPPORTED -# define PNG_POWERPC_VSX 10 /* HARDWARE: PowerPC VSX SIMD instructions - * supported */ +# define PNG_POWERPC_VSX 10 #endif + +/* HARDWARE: MIPS MMI SIMD instructions supported */ #ifdef PNG_MIPS_MMI_API_SUPPORTED -# define PNG_MIPS_MMI 12 /* HARDWARE: MIPS MMI SIMD instructions supported */ +# define PNG_MIPS_MMI 12 +#endif + +/* HARDWARE: RISC-V RVV SIMD instructions supported */ +#ifdef PNG_RISCV_RVV_API_SUPPORTED +# define PNG_RISCV_RVV 14 #endif -#define PNG_OPTION_NEXT 14 /* Next option - numbers must be even */ +/* Next option - numbers must be even */ +#define PNG_OPTION_NEXT 16 /* Return values: NOTE: there are four values and 'off' is *not* zero */ #define PNG_OPTION_UNSET 0 /* Unset - defaults to off */ @@ -3266,7 +3394,7 @@ PNG_EXPORT(244, int, png_set_option, (png_structrp png_ptr, int option, * one to use is one more than this.) */ #ifdef PNG_EXPORT_LAST_ORDINAL - PNG_EXPORT_LAST_ORDINAL(249); + PNG_EXPORT_LAST_ORDINAL(259); #endif #ifdef __cplusplus diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h b/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h index b3b441b1122c2..4bc5f7bb4680c 100644 --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h @@ -29,9 +29,9 @@ * However, the following notice accompanied the original version of this * file and, per its terms, should not be removed: * - * libpng version 1.6.43 + * libpng version 1.6.51 * - * Copyright (c) 2018-2024 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -116,7 +116,7 @@ /* The PNGARG macro was used in versions of libpng prior to 1.6.0 to protect * against legacy (pre ISOC90) compilers that did not understand function - * prototypes. It is not required for modern C compilers. + * prototypes. [Deprecated.] */ #ifndef PNGARG # define PNGARG(arglist) arglist @@ -248,25 +248,13 @@ /* NOTE: PNGCBAPI always defaults to PNGCAPI. */ # if defined(PNGAPI) && !defined(PNG_USER_PRIVATEBUILD) -# error "PNG_USER_PRIVATEBUILD must be defined if PNGAPI is changed" +# error PNG_USER_PRIVATEBUILD must be defined if PNGAPI is changed # endif -# if (defined(_MSC_VER) && _MSC_VER < 800) ||\ - (defined(__BORLANDC__) && __BORLANDC__ < 0x500) - /* older Borland and MSC - * compilers used '__export' and required this to be after - * the type. - */ -# ifndef PNG_EXPORT_TYPE -# define PNG_EXPORT_TYPE(type) type PNG_IMPEXP -# endif -# define PNG_DLL_EXPORT __export -# else /* newer compiler */ -# define PNG_DLL_EXPORT __declspec(dllexport) -# ifndef PNG_DLL_IMPORT -# define PNG_DLL_IMPORT __declspec(dllimport) -# endif -# endif /* compiler */ +# define PNG_DLL_EXPORT __declspec(dllexport) +# ifndef PNG_DLL_IMPORT +# define PNG_DLL_IMPORT __declspec(dllimport) +# endif #else /* !Windows */ # if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) @@ -326,7 +314,7 @@ #ifndef PNG_EXPORTA # define PNG_EXPORTA(ordinal, type, name, args, attributes) \ - PNG_FUNCTION(PNG_EXPORT_TYPE(type), (PNGAPI name), PNGARG(args), \ + PNG_FUNCTION(PNG_EXPORT_TYPE(type), (PNGAPI name), args, \ PNG_LINKAGE_API attributes) #endif @@ -344,7 +332,7 @@ #endif #ifndef PNG_CALLBACK -# define PNG_CALLBACK(type, name, args) type (PNGCBAPI name) PNGARG(args) +# define PNG_CALLBACK(type, name, args) type (PNGCBAPI name) args #endif /* Support for compiler specific function attributes. These are used @@ -508,7 +496,7 @@ #if CHAR_BIT == 8 && UCHAR_MAX == 255 typedef unsigned char png_byte; #else -# error "libpng requires 8-bit bytes" +# error libpng requires 8-bit bytes #endif #if INT_MIN == -32768 && INT_MAX == 32767 @@ -516,7 +504,7 @@ #elif SHRT_MIN == -32768 && SHRT_MAX == 32767 typedef short png_int_16; #else -# error "libpng requires a signed 16-bit type" +# error libpng requires a signed 16-bit integer type #endif #if UINT_MAX == 65535 @@ -524,7 +512,7 @@ #elif USHRT_MAX == 65535 typedef unsigned short png_uint_16; #else -# error "libpng requires an unsigned 16-bit type" +# error libpng requires an unsigned 16-bit integer type #endif #if INT_MIN < -2147483646 && INT_MAX > 2147483646 @@ -532,7 +520,7 @@ #elif LONG_MIN < -2147483646 && LONG_MAX > 2147483646 typedef long int png_int_32; #else -# error "libpng requires a signed 32-bit (or more) type" +# error libpng requires a signed 32-bit (or longer) integer type #endif #if UINT_MAX > 4294967294U @@ -540,7 +528,7 @@ #elif ULONG_MAX > 4294967294U typedef unsigned long int png_uint_32; #else -# error "libpng requires an unsigned 32-bit (or more) type" +# error libpng requires an unsigned 32-bit (or longer) integer type #endif /* Prior to 1.6.0, it was possible to disable the use of size_t and ptrdiff_t. @@ -621,10 +609,6 @@ typedef const png_fixed_point * png_const_fixed_point_p; typedef size_t * png_size_tp; typedef const size_t * png_const_size_tp; -#ifdef PNG_STDIO_SUPPORTED -typedef FILE * png_FILE_p; -#endif - #ifdef PNG_FLOATING_POINT_SUPPORTED typedef double * png_doublep; typedef const double * png_const_doublep; @@ -646,6 +630,15 @@ typedef double * * png_doublepp; /* Pointers to pointers to pointers; i.e., pointer to array */ typedef char * * * png_charppp; +#ifdef PNG_STDIO_SUPPORTED +/* With PNG_STDIO_SUPPORTED it was possible to use I/O streams that were + * not necessarily stdio FILE streams, to allow building Windows applications + * before Win32 and Windows CE applications before WinCE 3.0, but that kind + * of support has long been discontinued. + */ +typedef FILE * png_FILE_p; /* [Deprecated] */ +#endif + #endif /* PNG_BUILDING_SYMBOL_TABLE */ #endif /* PNGCONF_H */ diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngdebug.h b/src/java.desktop/share/native/libsplashscreen/libpng/pngdebug.h index 8eb5400ea9a83..6ea2644dfcd8b 100644 --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngdebug.h +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngdebug.h @@ -22,14 +22,14 @@ * questions. */ -/* pngdebug.h - Debugging macros for libpng, also used in pngtest.c +/* pngdebug.h - internal debugging macros for libpng * * This file is available under and governed by the GNU General Public * License version 2 only, as published by the Free Software Foundation. * However, the following notice accompanied the original version of this * file and, per its terms, should not be removed: * - * Copyright (c) 2018 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2013 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -39,6 +39,10 @@ * and license in png.h */ +#ifndef PNGPRIV_H +# error This file must not be included by applications; please include +#endif + /* Define PNG_DEBUG at compile time for debugging information. Higher * numbers for PNG_DEBUG mean more debugging information. This has * only been added since version 0.95 so it is not implemented throughout @@ -63,9 +67,6 @@ #define PNGDEBUG_H /* These settings control the formatting of messages in png.c and pngerror.c */ /* Moved to pngdebug.h at 1.5.0 */ -# ifndef PNG_LITERAL_SHARP -# define PNG_LITERAL_SHARP 0x23 -# endif # ifndef PNG_LITERAL_LEFT_SQUARE_BRACKET # define PNG_LITERAL_LEFT_SQUARE_BRACKET 0x5b # endif diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngerror.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngerror.c index ea8dd1721972c..44c86ebfef99e 100644 --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngerror.c +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngerror.c @@ -29,7 +29,7 @@ * However, the following notice accompanied the original version of this * file and, per its terms, should not be removed: * - * Copyright (c) 2018-2024 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -48,13 +48,14 @@ #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) -static PNG_FUNCTION(void, png_default_error,PNGARG((png_const_structrp png_ptr, - png_const_charp error_message)),PNG_NORETURN); +static PNG_FUNCTION(void /* PRIVATE */, +png_default_error,(png_const_structrp png_ptr, png_const_charp error_message), + PNG_NORETURN); #ifdef PNG_WARNINGS_SUPPORTED static void /* PRIVATE */ -png_default_warning PNGARG((png_const_structrp png_ptr, - png_const_charp warning_message)); +png_default_warning(png_const_structrp png_ptr, + png_const_charp warning_message); #endif /* WARNINGS */ /* This function is called whenever there is a fatal error. This function @@ -67,46 +68,6 @@ PNG_FUNCTION(void,PNGAPI png_error,(png_const_structrp png_ptr, png_const_charp error_message), PNG_NORETURN) { -#ifdef PNG_ERROR_NUMBERS_SUPPORTED - char msg[16]; - if (png_ptr != NULL) - { - if ((png_ptr->flags & - (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) != 0) - { - if (*error_message == PNG_LITERAL_SHARP) - { - /* Strip "#nnnn " from beginning of error message. */ - int offset; - for (offset = 1; offset<15; offset++) - if (error_message[offset] == ' ') - break; - - if ((png_ptr->flags & PNG_FLAG_STRIP_ERROR_TEXT) != 0) - { - int i; - for (i = 0; i < offset - 1; i++) - msg[i] = error_message[i + 1]; - msg[i - 1] = '\0'; - error_message = msg; - } - - else - error_message += offset; - } - - else - { - if ((png_ptr->flags & PNG_FLAG_STRIP_ERROR_TEXT) != 0) - { - msg[0] = '0'; - msg[1] = '\0'; - error_message = msg; - } - } - } - } -#endif if (png_ptr != NULL && png_ptr->error_fn != NULL) (*(png_ptr->error_fn))(png_constcast(png_structrp,png_ptr), error_message); @@ -244,21 +205,6 @@ void PNGAPI png_warning(png_const_structrp png_ptr, png_const_charp warning_message) { int offset = 0; - if (png_ptr != NULL) - { -#ifdef PNG_ERROR_NUMBERS_SUPPORTED - if ((png_ptr->flags & - (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) != 0) -#endif - { - if (*warning_message == PNG_LITERAL_SHARP) - { - for (offset = 1; offset < 15; offset++) - if (warning_message[offset] == ' ') - break; - } - } - } if (png_ptr != NULL && png_ptr->warning_fn != NULL) (*(png_ptr->warning_fn))(png_constcast(png_structrp,png_ptr), warning_message + offset); @@ -740,42 +686,9 @@ png_default_error,(png_const_structrp png_ptr, png_const_charp error_message), PNG_NORETURN) { #ifdef PNG_CONSOLE_IO_SUPPORTED -#ifdef PNG_ERROR_NUMBERS_SUPPORTED - /* Check on NULL only added in 1.5.4 */ - if (error_message != NULL && *error_message == PNG_LITERAL_SHARP) - { - /* Strip "#nnnn " from beginning of error message. */ - int offset; - char error_number[16]; - for (offset = 0; offset<15; offset++) - { - error_number[offset] = error_message[offset + 1]; - if (error_message[offset] == ' ') - break; - } - - if ((offset > 1) && (offset < 15)) - { - error_number[offset - 1] = '\0'; - fprintf(stderr, "libpng error no. %s: %s", - error_number, error_message + offset + 1); - fprintf(stderr, PNG_STRING_NEWLINE); - } - - else - { - fprintf(stderr, "libpng error: %s, offset=%d", - error_message, offset); - fprintf(stderr, PNG_STRING_NEWLINE); - } - } - else -#endif - { - fprintf(stderr, "libpng error: %s", error_message ? error_message : - "undefined"); - fprintf(stderr, PNG_STRING_NEWLINE); - } + fprintf(stderr, "libpng error: %s", error_message ? error_message : + "undefined"); + fprintf(stderr, PNG_STRING_NEWLINE); #else PNG_UNUSED(error_message) /* Make compiler happy */ #endif @@ -813,40 +726,8 @@ static void /* PRIVATE */ png_default_warning(png_const_structrp png_ptr, png_const_charp warning_message) { #ifdef PNG_CONSOLE_IO_SUPPORTED -# ifdef PNG_ERROR_NUMBERS_SUPPORTED - if (*warning_message == PNG_LITERAL_SHARP) - { - int offset; - char warning_number[16]; - for (offset = 0; offset < 15; offset++) - { - warning_number[offset] = warning_message[offset + 1]; - if (warning_message[offset] == ' ') - break; - } - - if ((offset > 1) && (offset < 15)) - { - warning_number[offset + 1] = '\0'; - fprintf(stderr, "libpng warning no. %s: %s", - warning_number, warning_message + offset); - fprintf(stderr, PNG_STRING_NEWLINE); - } - - else - { - fprintf(stderr, "libpng warning: %s", - warning_message); - fprintf(stderr, PNG_STRING_NEWLINE); - } - } - else -# endif - - { - fprintf(stderr, "libpng warning: %s", warning_message); - fprintf(stderr, PNG_STRING_NEWLINE); - } + fprintf(stderr, "libpng warning: %s", warning_message); + fprintf(stderr, PNG_STRING_NEWLINE); #else PNG_UNUSED(warning_message) /* Make compiler happy */ #endif @@ -894,12 +775,8 @@ png_get_error_ptr(png_const_structrp png_ptr) void PNGAPI png_set_strip_error_numbers(png_structrp png_ptr, png_uint_32 strip_mode) { - if (png_ptr != NULL) - { - png_ptr->flags &= - ((~(PNG_FLAG_STRIP_ERROR_NUMBERS | - PNG_FLAG_STRIP_ERROR_TEXT))&strip_mode); - } + PNG_UNUSED(png_ptr) + PNG_UNUSED(strip_mode) } #endif @@ -963,23 +840,37 @@ png_safe_warning(png_structp png_nonconst_ptr, png_const_charp warning_message) int /* PRIVATE */ png_safe_execute(png_imagep image, int (*function)(png_voidp), png_voidp arg) { - png_voidp saved_error_buf = image->opaque->error_buf; + const png_voidp saved_error_buf = image->opaque->error_buf; jmp_buf safe_jmpbuf; - int result; /* Safely execute function(arg), with png_error returning back here. */ if (setjmp(safe_jmpbuf) == 0) { + int result; + image->opaque->error_buf = safe_jmpbuf; result = function(arg); image->opaque->error_buf = saved_error_buf; - return result; + + if (result) + return 1; /* success */ } - /* On png_error, return via longjmp, pop the jmpbuf, and free the image. */ + /* The function failed either because of a caught png_error and a regular + * return of false above or because of an uncaught png_error from the + * function itself. Ensure that the error_buf is always set back to the + * value saved above: + */ image->opaque->error_buf = saved_error_buf; - png_image_free(image); - return 0; + + /* On the final false return, when about to return control to the caller, the + * image is freed (png_image_free does this check but it is duplicated here + * for clarity: + */ + if (saved_error_buf == NULL) + png_image_free(image); + + return 0; /* failure */ } #endif /* SIMPLIFIED READ || SIMPLIFIED_WRITE */ #endif /* READ || WRITE */ diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngget.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngget.c index 41e0a5abc3a8d..ed2e7f886f5db 100644 --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngget.c +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngget.c @@ -29,7 +29,7 @@ * However, the following notice accompanied the original version of this * file and, per its terms, should not be removed: * - * Copyright (c) 2018-2024 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -409,7 +409,13 @@ png_fixed_inches_from_microns(png_const_structrp png_ptr, png_int_32 microns) * Notice that this can overflow - a warning is output and 0 is * returned. */ - return png_muldiv_warn(png_ptr, microns, 500, 127); + png_fixed_point result; + + if (png_muldiv(&result, microns, 500, 127) != 0) + return result; + + png_warning(png_ptr, "fixed point overflow ignored"); + return 0; } png_fixed_point PNGAPI @@ -419,7 +425,7 @@ png_get_x_offset_inches_fixed(png_const_structrp png_ptr, return png_fixed_inches_from_microns(png_ptr, png_get_x_offset_microns(png_ptr, info_ptr)); } -#endif +#endif /* FIXED_POINT */ #ifdef PNG_FIXED_POINT_SUPPORTED png_fixed_point PNGAPI @@ -547,44 +553,31 @@ png_get_bKGD(png_const_structrp png_ptr, png_inforp info_ptr, # ifdef PNG_FLOATING_POINT_SUPPORTED png_uint_32 PNGAPI png_get_cHRM(png_const_structrp png_ptr, png_const_inforp info_ptr, - double *white_x, double *white_y, double *red_x, double *red_y, - double *green_x, double *green_y, double *blue_x, double *blue_y) + double *whitex, double *whitey, double *redx, double *redy, + double *greenx, double *greeny, double *bluex, double *bluey) { png_debug1(1, "in %s retrieval function", "cHRM"); - /* Quiet API change: this code used to only return the end points if a cHRM - * chunk was present, but the end points can also come from iCCP or sRGB - * chunks, so in 1.6.0 the png_get_ APIs return the end points regardless and - * the png_set_ APIs merely check that set end points are mutually - * consistent. - */ + /* PNGv3: this just returns the values store from the cHRM, if any. */ if (png_ptr != NULL && info_ptr != NULL && - (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) + (info_ptr->valid & PNG_INFO_cHRM) != 0) { - if (white_x != NULL) - *white_x = png_float(png_ptr, - info_ptr->colorspace.end_points_xy.whitex, "cHRM white X"); - if (white_y != NULL) - *white_y = png_float(png_ptr, - info_ptr->colorspace.end_points_xy.whitey, "cHRM white Y"); - if (red_x != NULL) - *red_x = png_float(png_ptr, info_ptr->colorspace.end_points_xy.redx, - "cHRM red X"); - if (red_y != NULL) - *red_y = png_float(png_ptr, info_ptr->colorspace.end_points_xy.redy, - "cHRM red Y"); - if (green_x != NULL) - *green_x = png_float(png_ptr, - info_ptr->colorspace.end_points_xy.greenx, "cHRM green X"); - if (green_y != NULL) - *green_y = png_float(png_ptr, - info_ptr->colorspace.end_points_xy.greeny, "cHRM green Y"); - if (blue_x != NULL) - *blue_x = png_float(png_ptr, info_ptr->colorspace.end_points_xy.bluex, - "cHRM blue X"); - if (blue_y != NULL) - *blue_y = png_float(png_ptr, info_ptr->colorspace.end_points_xy.bluey, - "cHRM blue Y"); + if (whitex != NULL) + *whitex = png_float(png_ptr, info_ptr->cHRM.whitex, "cHRM wx"); + if (whitey != NULL) + *whitey = png_float(png_ptr, info_ptr->cHRM.whitey, "cHRM wy"); + if (redx != NULL) + *redx = png_float(png_ptr, info_ptr->cHRM.redx, "cHRM rx"); + if (redy != NULL) + *redy = png_float(png_ptr, info_ptr->cHRM.redy, "cHRM ry"); + if (greenx != NULL) + *greenx = png_float(png_ptr, info_ptr->cHRM.greenx, "cHRM gx"); + if (greeny != NULL) + *greeny = png_float(png_ptr, info_ptr->cHRM.greeny, "cHRM gy"); + if (bluex != NULL) + *bluex = png_float(png_ptr, info_ptr->cHRM.bluex, "cHRM bx"); + if (bluey != NULL) + *bluey = png_float(png_ptr, info_ptr->cHRM.bluey, "cHRM by"); return PNG_INFO_cHRM; } @@ -597,38 +590,31 @@ png_get_cHRM_XYZ(png_const_structrp png_ptr, png_const_inforp info_ptr, double *green_Y, double *green_Z, double *blue_X, double *blue_Y, double *blue_Z) { + png_XYZ XYZ; png_debug1(1, "in %s retrieval function", "cHRM_XYZ(float)"); if (png_ptr != NULL && info_ptr != NULL && - (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) + (info_ptr->valid & PNG_INFO_cHRM) != 0 && + png_XYZ_from_xy(&XYZ, &info_ptr->cHRM) == 0) { if (red_X != NULL) - *red_X = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_X, - "cHRM red X"); + *red_X = png_float(png_ptr, XYZ.red_X, "cHRM red X"); if (red_Y != NULL) - *red_Y = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_Y, - "cHRM red Y"); + *red_Y = png_float(png_ptr, XYZ.red_Y, "cHRM red Y"); if (red_Z != NULL) - *red_Z = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_Z, - "cHRM red Z"); + *red_Z = png_float(png_ptr, XYZ.red_Z, "cHRM red Z"); if (green_X != NULL) - *green_X = png_float(png_ptr, - info_ptr->colorspace.end_points_XYZ.green_X, "cHRM green X"); + *green_X = png_float(png_ptr, XYZ.green_X, "cHRM green X"); if (green_Y != NULL) - *green_Y = png_float(png_ptr, - info_ptr->colorspace.end_points_XYZ.green_Y, "cHRM green Y"); + *green_Y = png_float(png_ptr, XYZ.green_Y, "cHRM green Y"); if (green_Z != NULL) - *green_Z = png_float(png_ptr, - info_ptr->colorspace.end_points_XYZ.green_Z, "cHRM green Z"); + *green_Z = png_float(png_ptr, XYZ.green_Z, "cHRM green Z"); if (blue_X != NULL) - *blue_X = png_float(png_ptr, - info_ptr->colorspace.end_points_XYZ.blue_X, "cHRM blue X"); + *blue_X = png_float(png_ptr, XYZ.blue_X, "cHRM blue X"); if (blue_Y != NULL) - *blue_Y = png_float(png_ptr, - info_ptr->colorspace.end_points_XYZ.blue_Y, "cHRM blue Y"); + *blue_Y = png_float(png_ptr, XYZ.blue_Y, "cHRM blue Y"); if (blue_Z != NULL) - *blue_Z = png_float(png_ptr, - info_ptr->colorspace.end_points_XYZ.blue_Z, "cHRM blue Z"); + *blue_Z = png_float(png_ptr, XYZ.blue_Z, "cHRM blue Z"); return PNG_INFO_cHRM; } @@ -645,29 +631,22 @@ png_get_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y, png_fixed_point *int_blue_Z) { + png_XYZ XYZ; png_debug1(1, "in %s retrieval function", "cHRM_XYZ"); if (png_ptr != NULL && info_ptr != NULL && - (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) + (info_ptr->valid & PNG_INFO_cHRM) != 0U && + png_XYZ_from_xy(&XYZ, &info_ptr->cHRM) == 0) { - if (int_red_X != NULL) - *int_red_X = info_ptr->colorspace.end_points_XYZ.red_X; - if (int_red_Y != NULL) - *int_red_Y = info_ptr->colorspace.end_points_XYZ.red_Y; - if (int_red_Z != NULL) - *int_red_Z = info_ptr->colorspace.end_points_XYZ.red_Z; - if (int_green_X != NULL) - *int_green_X = info_ptr->colorspace.end_points_XYZ.green_X; - if (int_green_Y != NULL) - *int_green_Y = info_ptr->colorspace.end_points_XYZ.green_Y; - if (int_green_Z != NULL) - *int_green_Z = info_ptr->colorspace.end_points_XYZ.green_Z; - if (int_blue_X != NULL) - *int_blue_X = info_ptr->colorspace.end_points_XYZ.blue_X; - if (int_blue_Y != NULL) - *int_blue_Y = info_ptr->colorspace.end_points_XYZ.blue_Y; - if (int_blue_Z != NULL) - *int_blue_Z = info_ptr->colorspace.end_points_XYZ.blue_Z; + if (int_red_X != NULL) *int_red_X = XYZ.red_X; + if (int_red_Y != NULL) *int_red_Y = XYZ.red_Y; + if (int_red_Z != NULL) *int_red_Z = XYZ.red_Z; + if (int_green_X != NULL) *int_green_X = XYZ.green_X; + if (int_green_Y != NULL) *int_green_Y = XYZ.green_Y; + if (int_green_Z != NULL) *int_green_Z = XYZ.green_Z; + if (int_blue_X != NULL) *int_blue_X = XYZ.blue_X; + if (int_blue_Y != NULL) *int_blue_Y = XYZ.blue_Y; + if (int_blue_Z != NULL) *int_blue_Z = XYZ.blue_Z; return PNG_INFO_cHRM; } @@ -676,31 +655,24 @@ png_get_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, png_uint_32 PNGAPI png_get_cHRM_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, - png_fixed_point *white_x, png_fixed_point *white_y, png_fixed_point *red_x, - png_fixed_point *red_y, png_fixed_point *green_x, png_fixed_point *green_y, - png_fixed_point *blue_x, png_fixed_point *blue_y) + png_fixed_point *whitex, png_fixed_point *whitey, png_fixed_point *redx, + png_fixed_point *redy, png_fixed_point *greenx, png_fixed_point *greeny, + png_fixed_point *bluex, png_fixed_point *bluey) { png_debug1(1, "in %s retrieval function", "cHRM"); + /* PNGv3: this just returns the values store from the cHRM, if any. */ if (png_ptr != NULL && info_ptr != NULL && - (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) + (info_ptr->valid & PNG_INFO_cHRM) != 0) { - if (white_x != NULL) - *white_x = info_ptr->colorspace.end_points_xy.whitex; - if (white_y != NULL) - *white_y = info_ptr->colorspace.end_points_xy.whitey; - if (red_x != NULL) - *red_x = info_ptr->colorspace.end_points_xy.redx; - if (red_y != NULL) - *red_y = info_ptr->colorspace.end_points_xy.redy; - if (green_x != NULL) - *green_x = info_ptr->colorspace.end_points_xy.greenx; - if (green_y != NULL) - *green_y = info_ptr->colorspace.end_points_xy.greeny; - if (blue_x != NULL) - *blue_x = info_ptr->colorspace.end_points_xy.bluex; - if (blue_y != NULL) - *blue_y = info_ptr->colorspace.end_points_xy.bluey; + if (whitex != NULL) *whitex = info_ptr->cHRM.whitex; + if (whitey != NULL) *whitey = info_ptr->cHRM.whitey; + if (redx != NULL) *redx = info_ptr->cHRM.redx; + if (redy != NULL) *redy = info_ptr->cHRM.redy; + if (greenx != NULL) *greenx = info_ptr->cHRM.greenx; + if (greeny != NULL) *greeny = info_ptr->cHRM.greeny; + if (bluex != NULL) *bluex = info_ptr->cHRM.bluex; + if (bluey != NULL) *bluey = info_ptr->cHRM.bluey; return PNG_INFO_cHRM; } @@ -717,11 +689,11 @@ png_get_gAMA_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, { png_debug1(1, "in %s retrieval function", "gAMA"); + /* PNGv3 compatibility: only report gAMA if it is really present. */ if (png_ptr != NULL && info_ptr != NULL && - (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 && - file_gamma != NULL) + (info_ptr->valid & PNG_INFO_gAMA) != 0) { - *file_gamma = info_ptr->colorspace.gamma; + if (file_gamma != NULL) *file_gamma = info_ptr->gamma; return PNG_INFO_gAMA; } @@ -736,12 +708,13 @@ png_get_gAMA(png_const_structrp png_ptr, png_const_inforp info_ptr, { png_debug1(1, "in %s retrieval function", "gAMA(float)"); + /* PNGv3 compatibility: only report gAMA if it is really present. */ if (png_ptr != NULL && info_ptr != NULL && - (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 && - file_gamma != NULL) + (info_ptr->valid & PNG_INFO_gAMA) != 0) { - *file_gamma = png_float(png_ptr, info_ptr->colorspace.gamma, - "png_get_gAMA"); + if (file_gamma != NULL) + *file_gamma = png_float(png_ptr, info_ptr->gamma, "gAMA"); + return PNG_INFO_gAMA; } @@ -758,9 +731,10 @@ png_get_sRGB(png_const_structrp png_ptr, png_const_inforp info_ptr, png_debug1(1, "in %s retrieval function", "sRGB"); if (png_ptr != NULL && info_ptr != NULL && - (info_ptr->valid & PNG_INFO_sRGB) != 0 && file_srgb_intent != NULL) + (info_ptr->valid & PNG_INFO_sRGB) != 0) { - *file_srgb_intent = info_ptr->colorspace.rendering_intent; + if (file_srgb_intent != NULL) + *file_srgb_intent = info_ptr->rendering_intent; return PNG_INFO_sRGB; } @@ -813,6 +787,136 @@ png_get_sPLT(png_const_structrp png_ptr, png_inforp info_ptr, } #endif +#ifdef PNG_cICP_SUPPORTED +png_uint_32 PNGAPI +png_get_cICP(png_const_structrp png_ptr, + png_const_inforp info_ptr, png_bytep colour_primaries, + png_bytep transfer_function, png_bytep matrix_coefficients, + png_bytep video_full_range_flag) +{ + png_debug1(1, "in %s retrieval function", "cICP"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_cICP) != 0 && + colour_primaries != NULL && transfer_function != NULL && + matrix_coefficients != NULL && video_full_range_flag != NULL) + { + *colour_primaries = info_ptr->cicp_colour_primaries; + *transfer_function = info_ptr->cicp_transfer_function; + *matrix_coefficients = info_ptr->cicp_matrix_coefficients; + *video_full_range_flag = info_ptr->cicp_video_full_range_flag; + return (PNG_INFO_cICP); + } + + return 0; +} +#endif + +#ifdef PNG_cLLI_SUPPORTED +# ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_cLLI_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_uint_32p maxCLL, + png_uint_32p maxFALL) +{ + png_debug1(1, "in %s retrieval function", "cLLI"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_cLLI) != 0) + { + if (maxCLL != NULL) *maxCLL = info_ptr->maxCLL; + if (maxFALL != NULL) *maxFALL = info_ptr->maxFALL; + return PNG_INFO_cLLI; + } + + return 0; +} +# endif + +# ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_cLLI(png_const_structrp png_ptr, png_const_inforp info_ptr, + double *maxCLL, double *maxFALL) +{ + png_debug1(1, "in %s retrieval function", "cLLI(float)"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_cLLI) != 0) + { + if (maxCLL != NULL) *maxCLL = info_ptr->maxCLL * .0001; + if (maxFALL != NULL) *maxFALL = info_ptr->maxFALL * .0001; + return PNG_INFO_cLLI; + } + + return 0; +} +# endif +#endif /* cLLI */ + +#ifdef PNG_mDCV_SUPPORTED +# ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_mDCV_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *white_x, png_fixed_point *white_y, + png_fixed_point *red_x, png_fixed_point *red_y, + png_fixed_point *green_x, png_fixed_point *green_y, + png_fixed_point *blue_x, png_fixed_point *blue_y, + png_uint_32p mastering_maxDL, png_uint_32p mastering_minDL) +{ + png_debug1(1, "in %s retrieval function", "mDCV"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_mDCV) != 0) + { + if (white_x != NULL) *white_x = info_ptr->mastering_white_x * 2; + if (white_y != NULL) *white_y = info_ptr->mastering_white_y * 2; + if (red_x != NULL) *red_x = info_ptr->mastering_red_x * 2; + if (red_y != NULL) *red_y = info_ptr->mastering_red_y * 2; + if (green_x != NULL) *green_x = info_ptr->mastering_green_x * 2; + if (green_y != NULL) *green_y = info_ptr->mastering_green_y * 2; + if (blue_x != NULL) *blue_x = info_ptr->mastering_blue_x * 2; + if (blue_y != NULL) *blue_y = info_ptr->mastering_blue_y * 2; + if (mastering_maxDL != NULL) *mastering_maxDL = info_ptr->mastering_maxDL; + if (mastering_minDL != NULL) *mastering_minDL = info_ptr->mastering_minDL; + return PNG_INFO_mDCV; + } + + return 0; +} +# endif + +# ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_mDCV(png_const_structrp png_ptr, png_const_inforp info_ptr, + double *white_x, double *white_y, double *red_x, double *red_y, + double *green_x, double *green_y, double *blue_x, double *blue_y, + double *mastering_maxDL, double *mastering_minDL) +{ + png_debug1(1, "in %s retrieval function", "mDCV(float)"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_mDCV) != 0) + { + if (white_x != NULL) *white_x = info_ptr->mastering_white_x * .00002; + if (white_y != NULL) *white_y = info_ptr->mastering_white_y * .00002; + if (red_x != NULL) *red_x = info_ptr->mastering_red_x * .00002; + if (red_y != NULL) *red_y = info_ptr->mastering_red_y * .00002; + if (green_x != NULL) *green_x = info_ptr->mastering_green_x * .00002; + if (green_y != NULL) *green_y = info_ptr->mastering_green_y * .00002; + if (blue_x != NULL) *blue_x = info_ptr->mastering_blue_x * .00002; + if (blue_y != NULL) *blue_y = info_ptr->mastering_blue_y * .00002; + if (mastering_maxDL != NULL) + *mastering_maxDL = info_ptr->mastering_maxDL * .0001; + if (mastering_minDL != NULL) + *mastering_minDL = info_ptr->mastering_minDL * .0001; + return PNG_INFO_mDCV; + } + + return 0; +} +# endif /* FLOATING_POINT */ +#endif /* mDCV */ + #ifdef PNG_eXIf_SUPPORTED png_uint_32 PNGAPI png_get_eXIf(png_const_structrp png_ptr, png_inforp info_ptr, diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pnginfo.h b/src/java.desktop/share/native/libsplashscreen/libpng/pnginfo.h index d241eaebffbbc..c79c6cc780f5e 100644 --- a/src/java.desktop/share/native/libsplashscreen/libpng/pnginfo.h +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pnginfo.h @@ -22,14 +22,14 @@ * questions. */ -/* pnginfo.h - header file for PNG reference library +/* pnginfo.h - internal structures for libpng * * This file is available under and governed by the GNU General Public * License version 2 only, as published by the Free Software Foundation. * However, the following notice accompanied the original version of this * file and, per its terms, should not be removed: * - * Copyright (c) 2018 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2013,2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -39,43 +39,20 @@ * and license in png.h */ - /* png_info is a structure that holds the information in a PNG file so - * that the application can find out the characteristics of the image. - * If you are reading the file, this structure will tell you what is - * in the PNG file. If you are writing the file, fill in the information - * you want to put into the PNG file, using png_set_*() functions, then - * call png_write_info(). - * - * The names chosen should be very close to the PNG specification, so - * consult that document for information about the meaning of each field. - * - * With libpng < 0.95, it was only possible to directly set and read the - * the values in the png_info_struct, which meant that the contents and - * order of the values had to remain fixed. With libpng 0.95 and later, - * however, there are now functions that abstract the contents of - * png_info_struct from the application, so this makes it easier to use - * libpng with dynamic libraries, and even makes it possible to use - * libraries that don't have all of the libpng ancillary chunk-handing - * functionality. In libpng-1.5.0 this was moved into a separate private - * file that is not visible to applications. +#ifndef PNGPRIV_H +# error This file must not be included by applications; please include +#endif + +/* INTERNAL, PRIVATE definition of a PNG. * - * The following members may have allocated storage attached that should be - * cleaned up before the structure is discarded: palette, trans, text, - * pcal_purpose, pcal_units, pcal_params, hist, iccp_name, iccp_profile, - * splt_palettes, scal_unit, row_pointers, and unknowns. By default, these - * are automatically freed when the info structure is deallocated, if they were - * allocated internally by libpng. This behavior can be changed by means - * of the png_data_freer() function. + * png_info is a modifiable description of a PNG datastream. The fields inside + * this structure are accessed through png_get_() functions and modified + * using png_set_() functions. * - * More allocation details: all the chunk-reading functions that - * change these members go through the corresponding png_set_* - * functions. A function to clear these members is available: see - * png_free_data(). The png_set_* functions do not depend on being - * able to point info structure members to any of the storage they are - * passed (they make their own copies), EXCEPT that the png_set_text - * functions use the same storage passed to them in the text_ptr or - * itxt_ptr structure argument, and the png_set_rows and png_set_unknowns - * functions do not make their own copies. + * Some functions in libpng do directly access members of png_info. However, + * this should be avoided. png_struct objects contain members which hold + * caches, sometimes optimised, of the values from png_info objects, and + * png_info is not passed to the functions which read and write image data. */ #ifndef PNGINFO_H #define PNGINFO_H @@ -115,18 +92,12 @@ struct png_info_def * and initialize the appropriate fields below. */ -#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED) - /* png_colorspace only contains 'flags' if neither GAMMA or COLORSPACE are - * defined. When COLORSPACE is switched on all the colorspace-defining - * chunks should be enabled, when GAMMA is switched on all the gamma-defining - * chunks should be enabled. If this is not done it becomes possible to read - * inconsistent PNG files and assign a probably incorrect interpretation to - * the information. (In other words, by carefully choosing which chunks to - * recognize the system configuration can select an interpretation for PNG - * files containing ambiguous data and this will result in inconsistent - * behavior between different libpng builds!) - */ - png_colorspace colorspace; +#ifdef PNG_cICP_SUPPORTED + /* cICP chunk data */ + png_byte cicp_colour_primaries; + png_byte cicp_transfer_function; + png_byte cicp_matrix_coefficients; + png_byte cicp_video_full_range_flag; #endif #ifdef PNG_iCCP_SUPPORTED @@ -136,6 +107,24 @@ struct png_info_def png_uint_32 iccp_proflen; /* ICC profile data length */ #endif +#ifdef PNG_cLLI_SUPPORTED + png_uint_32 maxCLL; /* cd/m2 (nits) * 10,000 */ + png_uint_32 maxFALL; +#endif + +#ifdef PNG_mDCV_SUPPORTED + png_uint_16 mastering_red_x; /* CIE (xy) x * 50,000 */ + png_uint_16 mastering_red_y; + png_uint_16 mastering_green_x; + png_uint_16 mastering_green_y; + png_uint_16 mastering_blue_x; + png_uint_16 mastering_blue_y; + png_uint_16 mastering_white_x; + png_uint_16 mastering_white_y; + png_uint_32 mastering_maxDL; /* cd/m2 (nits) * 10,000 */ + png_uint_32 mastering_minDL; +#endif + #ifdef PNG_TEXT_SUPPORTED /* The tEXt, and zTXt chunks contain human-readable textual data in * uncompressed, compressed, and optionally compressed forms, respectively. @@ -214,11 +203,8 @@ defined(PNG_READ_BACKGROUND_SUPPORTED) #endif #ifdef PNG_eXIf_SUPPORTED - int num_exif; /* Added at libpng-1.6.31 */ + png_uint_32 num_exif; /* Added at libpng-1.6.31 */ png_bytep exif; -# ifdef PNG_READ_eXIf_SUPPORTED - png_bytep eXIf_buf; /* Added at libpng-1.6.32 */ -# endif #endif #ifdef PNG_hIST_SUPPORTED @@ -291,5 +277,16 @@ defined(PNG_READ_BACKGROUND_SUPPORTED) png_bytepp row_pointers; /* the image bits */ #endif +#ifdef PNG_cHRM_SUPPORTED + png_xy cHRM; +#endif + +#ifdef PNG_gAMA_SUPPORTED + png_fixed_point gamma; +#endif + +#ifdef PNG_sRGB_SUPPORTED + int rendering_intent; +#endif }; #endif /* PNGINFO_H */ diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h b/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h index e238ccdb9a4d9..4cfae4747511e 100644 --- a/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h @@ -31,9 +31,9 @@ * However, the following notice accompanied the original version of this * file and, per its terms, should not be removed: */ -/* libpng version 1.6.43 */ +/* libpng version 1.6.51 */ -/* Copyright (c) 2018-2023 Cosmin Truta */ +/* Copyright (c) 2018-2025 Cosmin Truta */ /* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */ /* This code is released under the libpng license. */ diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngmem.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngmem.c index d5ad0735f37b2..12b71bcbc0235 100644 --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngmem.c +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngmem.c @@ -29,7 +29,7 @@ * However, the following notice accompanied the original version of this * file and, per its terms, should not be removed: * - * Copyright (c) 2018 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2014,2016 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -101,30 +101,29 @@ png_malloc_base,(png_const_structrp png_ptr, png_alloc_size_t size), * to implement a user memory handler. This checks to be sure it isn't * called with big numbers. */ -#ifndef PNG_USER_MEM_SUPPORTED - PNG_UNUSED(png_ptr) -#endif +# ifdef PNG_MAX_MALLOC_64K + /* This is support for legacy systems which had segmented addressing + * limiting the maximum allocation size to 65536. It takes precedence + * over PNG_SIZE_MAX which is set to 65535 on true 16-bit systems. + * + * TODO: libpng-1.8: finally remove both cases. + */ + if (size > 65536U) return NULL; +# endif - /* Some compilers complain that this is always true. However, it - * can be false when integer overflow happens. + /* This is checked too because the system malloc call below takes a (size_t). */ - if (size > 0 && size <= PNG_SIZE_MAX -# ifdef PNG_MAX_MALLOC_64K - && size <= 65536U -# endif - ) - { -#ifdef PNG_USER_MEM_SUPPORTED + if (size > PNG_SIZE_MAX) return NULL; + +# ifdef PNG_USER_MEM_SUPPORTED if (png_ptr != NULL && png_ptr->malloc_fn != NULL) return png_ptr->malloc_fn(png_constcast(png_structrp,png_ptr), size); +# else + PNG_UNUSED(png_ptr) +# endif - else -#endif - return malloc((size_t)size); /* checked for truncation above */ - } - - else - return NULL; + /* Use the system malloc */ + return malloc((size_t)/*SAFE*/size); /* checked for truncation above */ } #if defined(PNG_TEXT_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) ||\ diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngpread.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngpread.c index 816631cae189b..3bfa913000fa3 100644 --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngpread.c +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngpread.c @@ -29,7 +29,7 @@ * However, the following notice accompanied the original version of this * file and, per its terms, should not be removed: * - * Copyright (c) 2018-2024 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -60,6 +60,21 @@ if (png_ptr->push_length + 4 > png_ptr->buffer_size) \ if (png_ptr->buffer_size < N) \ { png_push_save_buffer(png_ptr); return; } +#ifdef PNG_READ_INTERLACING_SUPPORTED +/* Arrays to facilitate interlacing - use pass (0 - 6) as index. */ + +/* Start of interlace block */ +static const png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; +/* Offset to next interlace block */ +static const png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; +/* Start of interlace block in the y direction */ +static const png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; +/* Offset to next interlace block in the y direction */ +static const png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; + +/* TODO: Move these arrays to a common utility module to avoid duplication. */ +#endif + void PNGAPI png_process_data(png_structrp png_ptr, png_inforp info_ptr, png_bytep buffer, size_t buffer_size) @@ -207,17 +222,8 @@ png_push_read_chunk(png_structrp png_ptr, png_inforp info_ptr) */ if ((png_ptr->mode & PNG_HAVE_CHUNK_HEADER) == 0) { - png_byte chunk_length[4]; - png_byte chunk_tag[4]; - PNG_PUSH_SAVE_BUFFER_IF_LT(8) - png_push_fill_buffer(png_ptr, chunk_length, 4); - png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length); - png_reset_crc(png_ptr); - png_crc_read(png_ptr, chunk_tag, 4); - png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag); - png_check_chunk_name(png_ptr, png_ptr->chunk_name); - png_check_chunk_length(png_ptr, png_ptr->push_length); + png_ptr->push_length = png_read_chunk_header(png_ptr); png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; } @@ -252,19 +258,27 @@ png_push_read_chunk(png_structrp png_ptr, png_inforp info_ptr) png_benign_error(png_ptr, "Too many IDATs found"); } + else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) + { + /* These flags must be set consistently for all non-IDAT chunks, + * including the unknown chunks. + */ + png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT | PNG_AFTER_IDAT; + } + if (chunk_name == png_IHDR) { if (png_ptr->push_length != 13) png_error(png_ptr, "Invalid IHDR length"); PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_IHDR(png_ptr, info_ptr, png_ptr->push_length); + png_handle_chunk(png_ptr, info_ptr, png_ptr->push_length); } else if (chunk_name == png_IEND) { PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_IEND(png_ptr, info_ptr, png_ptr->push_length); + png_handle_chunk(png_ptr, info_ptr, png_ptr->push_length); png_ptr->process_mode = PNG_READ_DONE_MODE; png_push_have_end(png_ptr, info_ptr); @@ -281,12 +295,6 @@ png_push_read_chunk(png_structrp png_ptr, png_inforp info_ptr) } #endif - else if (chunk_name == png_PLTE) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_PLTE(png_ptr, info_ptr, png_ptr->push_length); - } - else if (chunk_name == png_IDAT) { png_ptr->idat_size = png_ptr->push_length; @@ -299,155 +307,10 @@ png_push_read_chunk(png_structrp png_ptr, png_inforp info_ptr) return; } -#ifdef PNG_READ_gAMA_SUPPORTED - else if (png_ptr->chunk_name == png_gAMA) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_gAMA(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_sBIT_SUPPORTED - else if (png_ptr->chunk_name == png_sBIT) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_sBIT(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_cHRM_SUPPORTED - else if (png_ptr->chunk_name == png_cHRM) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_cHRM(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_eXIf_SUPPORTED - else if (png_ptr->chunk_name == png_eXIf) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_eXIf(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_sRGB_SUPPORTED - else if (chunk_name == png_sRGB) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_sRGB(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_iCCP_SUPPORTED - else if (png_ptr->chunk_name == png_iCCP) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_iCCP(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_sPLT_SUPPORTED - else if (chunk_name == png_sPLT) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_sPLT(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_tRNS_SUPPORTED - else if (chunk_name == png_tRNS) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_tRNS(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_bKGD_SUPPORTED - else if (chunk_name == png_bKGD) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_bKGD(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_hIST_SUPPORTED - else if (chunk_name == png_hIST) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_hIST(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_pHYs_SUPPORTED - else if (chunk_name == png_pHYs) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_pHYs(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_oFFs_SUPPORTED - else if (chunk_name == png_oFFs) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_oFFs(png_ptr, info_ptr, png_ptr->push_length); - } -#endif - -#ifdef PNG_READ_pCAL_SUPPORTED - else if (chunk_name == png_pCAL) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_pCAL(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_sCAL_SUPPORTED - else if (chunk_name == png_sCAL) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_sCAL(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_tIME_SUPPORTED - else if (chunk_name == png_tIME) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_tIME(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_tEXt_SUPPORTED - else if (chunk_name == png_tEXt) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_tEXt(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_zTXt_SUPPORTED - else if (chunk_name == png_zTXt) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_zTXt(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_iTXt_SUPPORTED - else if (chunk_name == png_iTXt) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_iTXt(png_ptr, info_ptr, png_ptr->push_length); - } -#endif - else { PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length, - PNG_HANDLE_CHUNK_AS_DEFAULT); + png_handle_chunk(png_ptr, info_ptr, png_ptr->push_length); } png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; @@ -1004,27 +867,6 @@ png_push_process_row(png_structrp png_ptr) void /* PRIVATE */ png_read_push_finish_row(png_structrp png_ptr) { -#ifdef PNG_READ_INTERLACING_SUPPORTED - /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ - - /* Start of interlace block */ - static const png_byte png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; - - /* Offset to next interlace block */ - static const png_byte png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; - - /* Start of interlace block in the y direction */ - static const png_byte png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1}; - - /* Offset to next interlace block in the y direction */ - static const png_byte png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2}; - - /* Height of interlace block. This is not currently used - if you need - * it, uncomment it here and in png.h - static const png_byte png_pass_height[] = {8, 8, 4, 4, 2, 2, 1}; - */ -#endif - png_ptr->row_number++; if (png_ptr->row_number < png_ptr->num_rows) return; diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngpriv.h b/src/java.desktop/share/native/libsplashscreen/libpng/pngpriv.h index 18424542b00bf..dcd005efb3450 100644 --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngpriv.h +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngpriv.h @@ -29,7 +29,7 @@ * However, the following notice accompanied the original version of this * file and, per its terms, should not be removed: * - * Copyright (c) 2018-2024 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -48,8 +48,20 @@ * they should be well aware of the issues that may arise from doing so. */ + +/* pngpriv.h must be included first in each translation unit inside libpng. + * On the other hand, it must not be included at all, directly or indirectly, + * by any application code that uses the libpng API. + */ #ifndef PNGPRIV_H -#define PNGPRIV_H +# define PNGPRIV_H +#else +# error Duplicate inclusion of pngpriv.h; please check the libpng source files +#endif + +#if defined(PNG_H) || defined(PNGCONF_H) || defined(PNGLCONF_H) +# error This file must not be included by applications; please include +#endif /* Feature Test Macros. The following are defined here to ensure that correctly * implemented libraries reveal the APIs libpng needs to build and hide those @@ -86,7 +98,6 @@ */ #if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H) # include - /* Pick up the definition of 'restrict' from config.h if it was read: */ # define PNG_RESTRICT restrict #endif @@ -96,9 +107,7 @@ * are not internal definitions may be required. This is handled below just * before png.h is included, but load the configuration now if it is available. */ -#ifndef PNGLCONF_H -# include "pnglibconf.h" -#endif +#include "pnglibconf.h" /* Local renames may change non-exported API functions from png.h */ #if defined(PNG_PREFIX) && !defined(PNGPREFIX_H) @@ -163,52 +172,25 @@ # endif #endif +#ifndef PNG_RISCV_RVV_OPT + /* RISCV_RVV optimizations are being controlled by the compiler settings, + * typically the target compiler will define __riscv but the rvv extension + * availability has to be explicitly stated. This is why if no + * PNG_RISCV_RVV_OPT was defined then a runtime check will be executed. + * + * To enable RISCV_RVV optimizations unconditionally, and compile the + * associated code, pass --enable-riscv-rvv=yes or --enable-riscv-rvv=on + * to configure or put -DPNG_RISCV_RVV_OPT=2 in CPPFLAGS. + */ + +# define PNG_RISCV_RVV_OPT 0 +#endif + #if PNG_ARM_NEON_OPT > 0 /* NEON optimizations are to be at least considered by libpng, so enable the * callbacks to do this. */ # define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_neon - - /* By default the 'intrinsics' code in arm/filter_neon_intrinsics.c is used - * if possible - if __ARM_NEON__ is set and the compiler version is not known - * to be broken. This is controlled by PNG_ARM_NEON_IMPLEMENTATION which can - * be: - * - * 1 The intrinsics code (the default with __ARM_NEON__) - * 2 The hand coded assembler (the default without __ARM_NEON__) - * - * It is possible to set PNG_ARM_NEON_IMPLEMENTATION in CPPFLAGS, however - * this is *NOT* supported and may cease to work even after a minor revision - * to libpng. It *is* valid to do this for testing purposes, e.g. speed - * testing or a new compiler, but the results should be communicated to the - * libpng implementation list for incorporation in the next minor release. - */ -# ifndef PNG_ARM_NEON_IMPLEMENTATION -# if defined(__ARM_NEON__) || defined(__ARM_NEON) -# if defined(__clang__) - /* At present it is unknown by the libpng developers which versions - * of clang support the intrinsics, however some or perhaps all - * versions do not work with the assembler so this may be - * irrelevant, so just use the default (do nothing here.) - */ -# elif defined(__GNUC__) - /* GCC 4.5.4 NEON support is known to be broken. 4.6.3 is known to - * work, so if this *is* GCC, or G++, look for a version >4.5 - */ -# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6) -# define PNG_ARM_NEON_IMPLEMENTATION 2 -# endif /* no GNUC support */ -# endif /* __GNUC__ */ -# else /* !defined __ARM_NEON__ */ - /* The 'intrinsics' code simply won't compile without this -mfpu=neon: - */ -# if !defined(__aarch64__) && !defined(_M_ARM64) - /* The assembler code currently does not work on ARM64 */ -# define PNG_ARM_NEON_IMPLEMENTATION 2 -# endif /* __aarch64__ */ -# endif /* __ARM_NEON__ */ -# endif /* !PNG_ARM_NEON_IMPLEMENTATION */ - # ifndef PNG_ARM_NEON_IMPLEMENTATION /* Use the intrinsics code by default. */ # define PNG_ARM_NEON_IMPLEMENTATION 1 @@ -349,6 +331,16 @@ # define PNG_LOONGARCH_LSX_IMPLEMENTATION 0 #endif +#if PNG_RISCV_RVV_OPT > 0 && __riscv_v >= 1000000 +# define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_rvv +# ifndef PNG_RISCV_RVV_IMPLEMENTATION + /* Use the intrinsics code by default. */ +# define PNG_RISCV_RVV_IMPLEMENTATION 1 +# endif +#else +# define PNG_RISCV_RVV_IMPLEMENTATION 0 +#endif /* PNG_RISCV_RVV_OPT > 0 && __riscv_v >= 1000000 */ + /* Is this a build of a DLL where compilation of the object modules requires * different preprocessor settings to those required for a simple library? If * so PNG_BUILD_DLL must be set. @@ -741,13 +733,13 @@ #define PNG_FLAG_CRC_ANCILLARY_NOWARN 0x0200U #define PNG_FLAG_CRC_CRITICAL_USE 0x0400U #define PNG_FLAG_CRC_CRITICAL_IGNORE 0x0800U -#define PNG_FLAG_ASSUME_sRGB 0x1000U /* Added to libpng-1.5.4 */ +/* PNG_FLAG_ASSUME_sRGB unused 0x1000U * Added to libpng-1.5.4 */ #define PNG_FLAG_OPTIMIZE_ALPHA 0x2000U /* Added to libpng-1.5.4 */ #define PNG_FLAG_DETECT_UNINITIALIZED 0x4000U /* Added to libpng-1.5.4 */ /* #define PNG_FLAG_KEEP_UNKNOWN_CHUNKS 0x8000U */ /* #define PNG_FLAG_KEEP_UNSAFE_CHUNKS 0x10000U */ #define PNG_FLAG_LIBRARY_MISMATCH 0x20000U -#define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000U + /* 0x40000U unused */ #define PNG_FLAG_STRIP_ERROR_TEXT 0x80000U #define PNG_FLAG_BENIGN_ERRORS_WARN 0x100000U /* Added to libpng-1.4.0 */ #define PNG_FLAG_APP_WARNINGS_WARN 0x200000U /* Added to libpng-1.6.0 */ @@ -852,6 +844,8 @@ #ifdef PNG_FIXED_POINT_MACRO_SUPPORTED #define png_fixed(png_ptr, fp, s) ((fp) <= 21474 && (fp) >= -21474 ?\ ((png_fixed_point)(100000 * (fp))) : (png_fixed_error(png_ptr, s),0)) +#define png_fixed_ITU(png_ptr, fp, s) ((fp) <= 214748 && (fp) >= 0 ?\ + ((png_uint_32)(10000 * (fp))) : (png_fixed_error(png_ptr, s),0)) #endif /* else the corresponding function is defined below, inside the scope of the * cplusplus test. @@ -870,11 +864,31 @@ * * PNG_32b correctly produces a value shifted by up to 24 bits, even on * architectures where (int) is only 16 bits. + * + * 1.6.47: PNG_32b was made into a preprocessor evaluable macro by replacing the + * static_cast with a promoting binary operation using a guaranteed 32-bit + * (minimum) unsigned value. */ -#define PNG_32b(b,s) ((png_uint_32)(b) << (s)) +#define PNG_32b(b,s) (((0xFFFFFFFFU)&(b)) << (s)) #define PNG_U32(b1,b2,b3,b4) \ (PNG_32b(b1,24) | PNG_32b(b2,16) | PNG_32b(b3,8) | PNG_32b(b4,0)) +/* Chunk name validation. When using these macros all the arguments should be + * constants, otherwise code bloat may well occur. The macros are provided + * primarily for use in #if checks. + * + * PNG_32to8 produces a byte value with the right shift; used to extract the + * byte value from a chunk name. + */ +#define PNG_32to8(cn,s) (((cn) >> (s)) & 0xffU) +#define PNG_CN_VALID_UPPER(b) ((b) >= 65 && (b) <= 90) /* upper-case ASCII */ +#define PNG_CN_VALID_ASCII(b) PNG_CN_VALID_UPPER((b) & ~32U) +#define PNG_CHUNK_NAME_VALID(cn) (\ + PNG_CN_VALID_ASCII(PNG_32to8(cn,24)) && /* critical, !ancillary */\ + PNG_CN_VALID_ASCII(PNG_32to8(cn,16)) && /* public, !privately defined */\ + PNG_CN_VALID_UPPER(PNG_32to8(cn, 8)) && /* VALID, !reserved */\ + PNG_CN_VALID_ASCII(PNG_32to8(cn, 0)) /* data-dependent, !copy ok */) + /* Constants for known chunk types. * * MAINTAINERS: If you need to add a chunk, define the name here. @@ -902,9 +916,14 @@ #define png_IEND PNG_U32( 73, 69, 78, 68) #define png_IHDR PNG_U32( 73, 72, 68, 82) #define png_PLTE PNG_U32( 80, 76, 84, 69) +#define png_acTL PNG_U32( 97, 99, 84, 76) /* PNGv3: APNG */ #define png_bKGD PNG_U32( 98, 75, 71, 68) #define png_cHRM PNG_U32( 99, 72, 82, 77) +#define png_cICP PNG_U32( 99, 73, 67, 80) /* PNGv3 */ +#define png_cLLI PNG_U32( 99, 76, 76, 73) /* PNGv3 */ #define png_eXIf PNG_U32(101, 88, 73, 102) /* registered July 2017 */ +#define png_fcTL PNG_U32(102, 99, 84, 76) /* PNGv3: APNG */ +#define png_fdAT PNG_U32(102, 100, 65, 84) /* PNGv3: APNG */ #define png_fRAc PNG_U32(102, 82, 65, 99) /* registered, not defined */ #define png_gAMA PNG_U32(103, 65, 77, 65) #define png_gIFg PNG_U32(103, 73, 70, 103) @@ -913,6 +932,7 @@ #define png_hIST PNG_U32(104, 73, 83, 84) #define png_iCCP PNG_U32(105, 67, 67, 80) #define png_iTXt PNG_U32(105, 84, 88, 116) +#define png_mDCV PNG_U32(109, 68, 67, 86) /* PNGv3 */ #define png_oFFs PNG_U32(111, 70, 70, 115) #define png_pCAL PNG_U32(112, 67, 65, 76) #define png_pHYs PNG_U32(112, 72, 89, 115) @@ -953,11 +973,74 @@ #define PNG_CHUNK_RESERVED(c) (1 & ((c) >> 13)) #define PNG_CHUNK_SAFE_TO_COPY(c) (1 & ((c) >> 5)) +/* Known chunks. All supported chunks must be listed here. The macro PNG_CHUNK + * contains the four character ASCII name by which the chunk is identified. The + * macro is implemented as required to build tables or switch statements which + * require entries for every known chunk. The macro also contains an index + * value which should be in order (this is checked in png.c). + * + * Notice that "known" does not require "SUPPORTED"; tables should be built in + * such a way that chunks unsupported in a build require no more than the table + * entry (which should be small.) In particular function pointers for + * unsupported chunks should be NULL. + * + * At present these index values are not exported (not part of the public API) + * so can be changed at will. For convenience the names are in lexical sort + * order but with the critical chunks at the start in the order of occurence in + * a PNG. + * + * PNG_INFO_ values do not exist for every one of these chunk handles; for + * example PNG_INFO_{IDAT,IEND,tEXt,iTXt,zTXt} and possibly other chunks in the + * future. + */ +#define PNG_KNOWN_CHUNKS\ + PNG_CHUNK(IHDR, 0)\ + PNG_CHUNK(PLTE, 1)\ + PNG_CHUNK(IDAT, 2)\ + PNG_CHUNK(IEND, 3)\ + PNG_CHUNK(acTL, 4)\ + PNG_CHUNK(bKGD, 5)\ + PNG_CHUNK(cHRM, 6)\ + PNG_CHUNK(cICP, 7)\ + PNG_CHUNK(cLLI, 8)\ + PNG_CHUNK(eXIf, 9)\ + PNG_CHUNK(fcTL, 10)\ + PNG_CHUNK(fdAT, 11)\ + PNG_CHUNK(gAMA, 12)\ + PNG_CHUNK(hIST, 13)\ + PNG_CHUNK(iCCP, 14)\ + PNG_CHUNK(iTXt, 15)\ + PNG_CHUNK(mDCV, 16)\ + PNG_CHUNK(oFFs, 17)\ + PNG_CHUNK(pCAL, 18)\ + PNG_CHUNK(pHYs, 19)\ + PNG_CHUNK(sBIT, 20)\ + PNG_CHUNK(sCAL, 21)\ + PNG_CHUNK(sPLT, 22)\ + PNG_CHUNK(sRGB, 23)\ + PNG_CHUNK(tEXt, 24)\ + PNG_CHUNK(tIME, 25)\ + PNG_CHUNK(tRNS, 26)\ + PNG_CHUNK(zTXt, 27) + /* Gamma values (new at libpng-1.5.4): */ #define PNG_GAMMA_MAC_OLD 151724 /* Assume '1.8' is really 2.2/1.45! */ #define PNG_GAMMA_MAC_INVERSE 65909 #define PNG_GAMMA_sRGB_INVERSE 45455 +/* gamma sanity check. libpng cannot implement gamma transforms outside a + * certain limit because of its use of 16-bit fixed point intermediate values. + * Gamma values that are too large or too small will zap the 16-bit values all + * to 0 or 65535 resulting in an obvious 'bad' image. + * + * In libpng 1.6.0 the limits were changed from 0.07..3 to 0.01..100 to + * accommodate the optimal 16-bit gamma of 36 and its reciprocal. + * + * These are png_fixed_point integral values: + */ +#define PNG_LIB_GAMMA_MIN 1000 +#define PNG_LIB_GAMMA_MAX 10000000 + /* Almost everything below is C specific; the #defines above can be used in * non-C code (so long as it is C-preprocessed) the rest of this stuff cannot. */ @@ -970,17 +1053,15 @@ * must match that used in the build, or we must be using pnglibconf.h.prebuilt: */ #if PNG_ZLIB_VERNUM != 0 && PNG_ZLIB_VERNUM != ZLIB_VERNUM -# error ZLIB_VERNUM != PNG_ZLIB_VERNUM \ - "-I (include path) error: see the notes in pngpriv.h" - /* This means that when pnglibconf.h was built the copy of zlib.h that it - * used is not the same as the one being used here. Because the build of - * libpng makes decisions to use inflateInit2 and inflateReset2 based on the - * zlib version number and because this affects handling of certain broken - * PNG files the -I directives must match. +# error The include path of is incorrect + /* When pnglibconf.h was built, the copy of zlib.h that it used was not the + * same as the one being used here. Considering how libpng makes decisions + * to use the zlib API based on the zlib version number, the -I options must + * match. * - * The most likely explanation is that you passed a -I in CFLAGS. This will - * not work; all the preprocessor directives and in particular all the -I - * directives must be in CPPFLAGS. + * A possible cause of this mismatch is that you passed an -I option in + * CFLAGS, which is unlikely to work. All the preprocessor options, and all + * the -I options in particular, should be in CPPFLAGS. */ #endif @@ -1021,7 +1102,6 @@ extern "C" { * * All of these functions must be declared with PNG_INTERNAL_FUNCTION. */ - /* Zlib support */ #define PNG_UNEXPECTED_ZLIB_RETURN (-7) PNG_INTERNAL_FUNCTION(void, png_zstream_error,(png_structrp png_ptr, int ret), @@ -1040,6 +1120,7 @@ PNG_INTERNAL_FUNCTION(void,png_free_buffer_list,(png_structrp png_ptr, !defined(PNG_FIXED_POINT_MACRO_SUPPORTED) && \ (defined(PNG_gAMA_SUPPORTED) || defined(PNG_cHRM_SUPPORTED) || \ defined(PNG_sCAL_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_mDCV_SUPPORTED) || \ defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)) || \ (defined(PNG_sCAL_SUPPORTED) && \ defined(PNG_FLOATING_ARITHMETIC_SUPPORTED)) @@ -1047,12 +1128,38 @@ PNG_INTERNAL_FUNCTION(png_fixed_point,png_fixed,(png_const_structrp png_ptr, double fp, png_const_charp text),PNG_EMPTY); #endif +#if defined(PNG_FLOATING_POINT_SUPPORTED) && \ + !defined(PNG_FIXED_POINT_MACRO_SUPPORTED) && \ + (defined(PNG_cLLI_SUPPORTED) || defined(PNG_mDCV_SUPPORTED)) +PNG_INTERNAL_FUNCTION(png_uint_32,png_fixed_ITU,(png_const_structrp png_ptr, + double fp, png_const_charp text),PNG_EMPTY); +#endif + /* Check the user version string for compatibility, returns false if the version * numbers aren't compatible. */ PNG_INTERNAL_FUNCTION(int,png_user_version_check,(png_structrp png_ptr, png_const_charp user_png_ver),PNG_EMPTY); +#ifdef PNG_READ_SUPPORTED /* should only be used on read */ +/* Security: read limits on the largest allocations while reading a PNG. This + * avoids very large allocations caused by PNG files with damaged or altered + * chunk 'length' fields. + */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED /* run-time limit */ +# define png_chunk_max(png_ptr) ((png_ptr)->user_chunk_malloc_max) + +#elif PNG_USER_CHUNK_MALLOC_MAX > 0 /* compile-time limit */ +# define png_chunk_max(png_ptr) ((void)png_ptr, PNG_USER_CHUNK_MALLOC_MAX) + +#elif (defined PNG_MAX_MALLOC_64K) /* legacy system limit */ +# define png_chunk_max(png_ptr) ((void)png_ptr, 65536U) + +#else /* modern system limit SIZE_MAX (C99) */ +# define png_chunk_max(png_ptr) ((void)png_ptr, PNG_SIZE_MAX) +#endif +#endif /* READ */ + /* Internal base allocator - no messages, NULL on failure to allocate. This * does, however, call the application provided allocator and that could call * png_error (although that would be a bug in the application implementation.) @@ -1152,9 +1259,6 @@ PNG_INTERNAL_FUNCTION(void,png_crc_read,(png_structrp png_ptr, png_bytep buf, PNG_INTERNAL_FUNCTION(int,png_crc_finish,(png_structrp png_ptr, png_uint_32 skip),PNG_EMPTY); -/* Read the CRC from the file and compare it to the libpng calculated CRC */ -PNG_INTERNAL_FUNCTION(int,png_crc_error,(png_structrp png_ptr),PNG_EMPTY); - /* Calculate the CRC over a section of data. Note that we are only * passing a maximum of 64K on systems that have this as a memory limit, * since this is the maximum buffer size we can specify. @@ -1200,6 +1304,26 @@ PNG_INTERNAL_FUNCTION(void,png_write_cHRM_fixed,(png_structrp png_ptr, /* The xy value must have been previously validated */ #endif +#ifdef PNG_WRITE_cICP_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_cICP,(png_structrp png_ptr, + png_byte colour_primaries, png_byte transfer_function, + png_byte matrix_coefficients, png_byte video_full_range_flag), PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_cLLI_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_cLLI_fixed,(png_structrp png_ptr, + png_uint_32 maxCLL, png_uint_32 maxFALL), PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_mDCV_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_mDCV_fixed,(png_structrp png_ptr, + png_uint_16 red_x, png_uint_16 red_y, + png_uint_16 green_x, png_uint_16 green_y, + png_uint_16 blue_x, png_uint_16 blue_y, + png_uint_16 white_x, png_uint_16 white_y, + png_uint_32 maxDL, png_uint_32 minDL), PNG_EMPTY); +#endif + #ifdef PNG_WRITE_sRGB_SUPPORTED PNG_INTERNAL_FUNCTION(void,png_write_sRGB,(png_structrp png_ptr, int intent),PNG_EMPTY); @@ -1212,10 +1336,10 @@ PNG_INTERNAL_FUNCTION(void,png_write_eXIf,(png_structrp png_ptr, #ifdef PNG_WRITE_iCCP_SUPPORTED PNG_INTERNAL_FUNCTION(void,png_write_iCCP,(png_structrp png_ptr, - png_const_charp name, png_const_bytep profile), PNG_EMPTY); - /* The profile must have been previously validated for correctness, the - * length comes from the first four bytes. Only the base, deflate, - * compression is supported. + png_const_charp name, png_const_bytep profile, png_uint_32 proflen), + PNG_EMPTY); + /* Writes a previously 'set' profile. The profile argument is **not** + * compressed. */ #endif @@ -1451,6 +1575,23 @@ PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_lsx,(png_row_infop row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); #endif +#if PNG_RISCV_RVV_IMPLEMENTATION == 1 +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_rvv,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_rvv,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_rvv,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_rvv,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_rvv,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_rvv,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_rvv,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +#endif + /* Choose the best filter to use and filter the row data */ PNG_INTERNAL_FUNCTION(void,png_write_find_filter,(png_structrp png_ptr, png_row_infop row_info),PNG_EMPTY); @@ -1524,119 +1665,36 @@ PNG_INTERNAL_FUNCTION(void,png_do_bgr,(png_row_infop row_info, /* The following decodes the appropriate chunks, and does error correction, * then calls the appropriate callback for the chunk if it is valid. */ - -/* Decode the IHDR chunk */ -PNG_INTERNAL_FUNCTION(void,png_handle_IHDR,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_handle_PLTE,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_handle_IEND,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); - -#ifdef PNG_READ_bKGD_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_bKGD,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_cHRM_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_cHRM,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_eXIf_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_eXIf,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_gAMA_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_gAMA,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_hIST_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_hIST,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_iCCP_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_iCCP,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif /* READ_iCCP */ - -#ifdef PNG_READ_iTXt_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_iTXt,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_oFFs_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_oFFs,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_pCAL_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_pCAL,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_pHYs_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_pHYs,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_sBIT_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_sBIT,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_sCAL_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_sCAL,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_sPLT_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_sPLT,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif /* READ_sPLT */ - -#ifdef PNG_READ_sRGB_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_sRGB,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_tEXt_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_tEXt,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_tIME_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_tIME,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_tRNS_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_tRNS,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_zTXt_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_zTXt,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -PNG_INTERNAL_FUNCTION(void,png_check_chunk_name,(png_const_structrp png_ptr, - png_uint_32 chunk_name),PNG_EMPTY); - -PNG_INTERNAL_FUNCTION(void,png_check_chunk_length,(png_const_structrp png_ptr, - png_uint_32 chunk_length),PNG_EMPTY); - -PNG_INTERNAL_FUNCTION(void,png_handle_unknown,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length, int keep),PNG_EMPTY); +typedef enum +{ + /* Result of a call to png_handle_chunk made to handle the current chunk + * png_struct::chunk_name on read. Always informational, either the stream + * is read for the next chunk or the routine will call png_error. + * + * NOTE: order is important internally. handled_saved and above are regarded + * as handling the chunk. + */ + handled_error = 0, /* bad crc or known and bad format or too long */ + handled_discarded, /* not saved in the unknown chunk list */ + handled_saved, /* saved in the unknown chunk list */ + handled_ok /* known, supported and handled without error */ +} png_handle_result_code; + +PNG_INTERNAL_FUNCTION(png_handle_result_code,png_handle_unknown, + (png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length, int keep), + PNG_EMPTY); /* This is the function that gets called for unknown chunks. The 'keep' * argument is either non-zero for a known chunk that has been set to be * handled as unknown or zero for an unknown chunk. By default the function * just skips the chunk or errors out if it is critical. */ +PNG_INTERNAL_FUNCTION(png_handle_result_code,png_handle_chunk, + (png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); + /* This handles the current chunk png_ptr->chunk_name with unread + * data[length] and returns one of the above result codes. + */ + #if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) ||\ defined(PNG_HANDLE_AS_UNKNOWN_SUPPORTED) PNG_INTERNAL_FUNCTION(int,png_chunk_unknown_handling, @@ -1676,8 +1734,6 @@ PNG_INTERNAL_FUNCTION(void,png_process_IDAT_data,(png_structrp png_ptr, png_bytep buffer, size_t buffer_length),PNG_EMPTY); PNG_INTERNAL_FUNCTION(void,png_push_process_row,(png_structrp png_ptr), PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_handle_unknown,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); PNG_INTERNAL_FUNCTION(void,png_push_have_info,(png_structrp png_ptr, png_inforp info_ptr),PNG_EMPTY); PNG_INTERNAL_FUNCTION(void,png_push_have_end,(png_structrp png_ptr, @@ -1690,109 +1746,28 @@ PNG_INTERNAL_FUNCTION(void,png_process_some_data,(png_structrp png_ptr, png_inforp info_ptr),PNG_EMPTY); PNG_INTERNAL_FUNCTION(void,png_read_push_finish_row,(png_structrp png_ptr), PNG_EMPTY); -# ifdef PNG_READ_tEXt_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_push_handle_tEXt,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_read_tEXt,(png_structrp png_ptr, - png_inforp info_ptr),PNG_EMPTY); -# endif -# ifdef PNG_READ_zTXt_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_push_handle_zTXt,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_read_zTXt,(png_structrp png_ptr, - png_inforp info_ptr),PNG_EMPTY); -# endif -# ifdef PNG_READ_iTXt_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_push_handle_iTXt,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_read_iTXt,(png_structrp png_ptr, - png_inforp info_ptr),PNG_EMPTY); -# endif - #endif /* PROGRESSIVE_READ */ -/* Added at libpng version 1.6.0 */ -#ifdef PNG_GAMMA_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_colorspace_set_gamma,(png_const_structrp png_ptr, - png_colorspacerp colorspace, png_fixed_point gAMA), PNG_EMPTY); - /* Set the colorspace gamma with a value provided by the application or by - * the gAMA chunk on read. The value will override anything set by an ICC - * profile. - */ - -PNG_INTERNAL_FUNCTION(void,png_colorspace_sync_info,(png_const_structrp png_ptr, - png_inforp info_ptr), PNG_EMPTY); - /* Synchronize the info 'valid' flags with the colorspace */ - -PNG_INTERNAL_FUNCTION(void,png_colorspace_sync,(png_const_structrp png_ptr, - png_inforp info_ptr), PNG_EMPTY); - /* Copy the png_struct colorspace to the info_struct and call the above to - * synchronize the flags. Checks for NULL info_ptr and does nothing. - */ -#endif - -/* Added at libpng version 1.4.0 */ -#ifdef PNG_COLORSPACE_SUPPORTED -/* These internal functions are for maintaining the colorspace structure within - * a png_info or png_struct (or, indeed, both). - */ -PNG_INTERNAL_FUNCTION(int,png_colorspace_set_chromaticities, - (png_const_structrp png_ptr, png_colorspacerp colorspace, const png_xy *xy, - int preferred), PNG_EMPTY); - -PNG_INTERNAL_FUNCTION(int,png_colorspace_set_endpoints, - (png_const_structrp png_ptr, png_colorspacerp colorspace, const png_XYZ *XYZ, - int preferred), PNG_EMPTY); - -#ifdef PNG_sRGB_SUPPORTED -PNG_INTERNAL_FUNCTION(int,png_colorspace_set_sRGB,(png_const_structrp png_ptr, - png_colorspacerp colorspace, int intent), PNG_EMPTY); - /* This does set the colorspace gAMA and cHRM values too, but doesn't set the - * flags to write them, if it returns false there was a problem and an error - * message has already been output (but the colorspace may still need to be - * synced to record the invalid flag). - */ -#endif /* sRGB */ - #ifdef PNG_iCCP_SUPPORTED -PNG_INTERNAL_FUNCTION(int,png_colorspace_set_ICC,(png_const_structrp png_ptr, - png_colorspacerp colorspace, png_const_charp name, - png_uint_32 profile_length, png_const_bytep profile, int color_type), - PNG_EMPTY); - /* The 'name' is used for information only */ - /* Routines for checking parts of an ICC profile. */ #ifdef PNG_READ_iCCP_SUPPORTED PNG_INTERNAL_FUNCTION(int,png_icc_check_length,(png_const_structrp png_ptr, - png_colorspacerp colorspace, png_const_charp name, - png_uint_32 profile_length), PNG_EMPTY); + png_const_charp name, png_uint_32 profile_length), PNG_EMPTY); #endif /* READ_iCCP */ PNG_INTERNAL_FUNCTION(int,png_icc_check_header,(png_const_structrp png_ptr, - png_colorspacerp colorspace, png_const_charp name, - png_uint_32 profile_length, + png_const_charp name, png_uint_32 profile_length, png_const_bytep profile /* first 132 bytes only */, int color_type), PNG_EMPTY); PNG_INTERNAL_FUNCTION(int,png_icc_check_tag_table,(png_const_structrp png_ptr, - png_colorspacerp colorspace, png_const_charp name, - png_uint_32 profile_length, + png_const_charp name, png_uint_32 profile_length, png_const_bytep profile /* header plus whole tag table */), PNG_EMPTY); -#ifdef PNG_sRGB_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_icc_set_sRGB,( - png_const_structrp png_ptr, png_colorspacerp colorspace, - png_const_bytep profile, uLong adler), PNG_EMPTY); - /* 'adler' is the Adler32 checksum of the uncompressed profile data. It may - * be zero to indicate that it is not available. It is used, if provided, - * as a fast check on the profile when checking to see if it is sRGB. - */ -#endif #endif /* iCCP */ #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_colorspace_set_rgb_coefficients, - (png_structrp png_ptr), PNG_EMPTY); - /* Set the rgb_to_gray coefficients from the colorspace Y values */ +PNG_INTERNAL_FUNCTION(void,png_set_rgb_coefficients, (png_structrp png_ptr), + PNG_EMPTY); + /* Set the rgb_to_gray coefficients from the cHRM Y values (if unset) */ #endif /* READ_RGB_TO_GRAY */ -#endif /* COLORSPACE */ /* Added at libpng version 1.4.0 */ PNG_INTERNAL_FUNCTION(void,png_check_IHDR,(png_const_structrp png_ptr, @@ -2054,8 +2029,10 @@ PNG_INTERNAL_FUNCTION(int,png_check_fp_string,(png_const_charp string, size_t size),PNG_EMPTY); #endif /* pCAL || sCAL */ -#if defined(PNG_GAMMA_SUPPORTED) ||\ - defined(PNG_INCH_CONVERSIONS_SUPPORTED) || defined(PNG_READ_pHYs_SUPPORTED) +#if defined(PNG_READ_GAMMA_SUPPORTED) ||\ + defined(PNG_COLORSPACE_SUPPORTED) ||\ + defined(PNG_INCH_CONVERSIONS_SUPPORTED) ||\ + defined(PNG_READ_pHYs_SUPPORTED) /* Added at libpng version 1.5.0 */ /* This is a utility to provide a*times/div (rounded) and indicate * if there is an overflow. The result is a boolean - false (0) @@ -2064,22 +2041,14 @@ PNG_INTERNAL_FUNCTION(int,png_check_fp_string,(png_const_charp string, */ PNG_INTERNAL_FUNCTION(int,png_muldiv,(png_fixed_point_p res, png_fixed_point a, png_int_32 multiplied_by, png_int_32 divided_by),PNG_EMPTY); -#endif - -#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_INCH_CONVERSIONS_SUPPORTED) -/* Same deal, but issue a warning on overflow and return 0. */ -PNG_INTERNAL_FUNCTION(png_fixed_point,png_muldiv_warn, - (png_const_structrp png_ptr, png_fixed_point a, png_int_32 multiplied_by, - png_int_32 divided_by),PNG_EMPTY); -#endif -#ifdef PNG_GAMMA_SUPPORTED /* Calculate a reciprocal - used for gamma values. This returns * 0 if the argument is 0 in order to maintain an undefined value; * there are no warnings. */ PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal,(png_fixed_point a), PNG_EMPTY); +#endif #ifdef PNG_READ_GAMMA_SUPPORTED /* The same but gives a reciprocal of the product of two fixed point @@ -2088,14 +2057,22 @@ PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal,(png_fixed_point a), */ PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal2,(png_fixed_point a, png_fixed_point b),PNG_EMPTY); -#endif /* Return true if the gamma value is significantly different from 1.0 */ PNG_INTERNAL_FUNCTION(int,png_gamma_significant,(png_fixed_point gamma_value), PNG_EMPTY); -#endif -#ifdef PNG_READ_GAMMA_SUPPORTED +/* PNGv3: 'resolve' the file gamma according to the new PNGv3 rules for colour + * space information. + * + * NOTE: this uses precisely those chunks that libpng supports. For example it + * doesn't use iCCP and it can only use cICP for known and manageable + * transforms. For this reason a gamma specified by png_set_gamma always takes + * precedence. + */ +PNG_INTERNAL_FUNCTION(png_fixed_point,png_resolve_file_gamma, + (png_const_structrp png_ptr),PNG_EMPTY); + /* Internal fixed point gamma correction. These APIs are called as * required to convert single values - they don't need to be fast, * they are not used when processing image pixel values. @@ -2113,6 +2090,22 @@ PNG_INTERNAL_FUNCTION(void,png_destroy_gamma_table,(png_structrp png_ptr), PNG_EMPTY); PNG_INTERNAL_FUNCTION(void,png_build_gamma_table,(png_structrp png_ptr, int bit_depth),PNG_EMPTY); +#endif /* READ_GAMMA */ + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +/* Set the RGB coefficients if not already set by png_set_rgb_to_gray */ +PNG_INTERNAL_FUNCTION(void,png_set_rgb_coefficients,(png_structrp png_ptr), + PNG_EMPTY); +#endif + +#if defined(PNG_cHRM_SUPPORTED) || defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +PNG_INTERNAL_FUNCTION(int,png_XYZ_from_xy,(png_XYZ *XYZ, const png_xy *xy), + PNG_EMPTY); +#endif /* cHRM || READ_RGB_TO_GRAY */ + +#ifdef PNG_COLORSPACE_SUPPORTED +PNG_INTERNAL_FUNCTION(int,png_xy_from_XYZ,(png_xy *xy, const png_XYZ *XYZ), + PNG_EMPTY); #endif /* SIMPLIFIED READ/WRITE SUPPORT */ @@ -2211,6 +2204,11 @@ PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_lsx, (png_structp png_ptr, unsigned int bpp), PNG_EMPTY); #endif +# if PNG_RISCV_RVV_IMPLEMENTATION == 1 +PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_rvv, + (png_structp png_ptr, unsigned int bpp), PNG_EMPTY); +#endif + PNG_INTERNAL_FUNCTION(png_uint_32, png_check_keyword, (png_structrp png_ptr, png_const_charp key, png_bytep new_key), PNG_EMPTY); @@ -2246,4 +2244,3 @@ PNG_INTERNAL_FUNCTION(int, #endif #endif /* PNG_VERSION_INFO_ONLY */ -#endif /* PNGPRIV_H */ diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngread.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngread.c index e9e9447754523..b53668a09ce04 100644 --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngread.c +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngread.c @@ -29,7 +29,7 @@ * However, the following notice accompanied the original version of this * file and, per its terms, should not be removed: * - * Copyright (c) 2018-2024 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -160,14 +160,11 @@ png_read_info(png_structrp png_ptr, png_inforp info_ptr) png_ptr->mode |= PNG_AFTER_IDAT; } - /* This should be a binary subdivision search or a hash for - * matching the chunk name rather than a linear search. - */ if (chunk_name == png_IHDR) - png_handle_IHDR(png_ptr, info_ptr, length); + png_handle_chunk(png_ptr, info_ptr, length); else if (chunk_name == png_IEND) - png_handle_IEND(png_ptr, info_ptr, length); + png_handle_chunk(png_ptr, info_ptr, length); #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0) @@ -184,8 +181,6 @@ png_read_info(png_structrp png_ptr, png_inforp info_ptr) } } #endif - else if (chunk_name == png_PLTE) - png_handle_PLTE(png_ptr, info_ptr, length); else if (chunk_name == png_IDAT) { @@ -193,99 +188,8 @@ png_read_info(png_structrp png_ptr, png_inforp info_ptr) break; } -#ifdef PNG_READ_bKGD_SUPPORTED - else if (chunk_name == png_bKGD) - png_handle_bKGD(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_cHRM_SUPPORTED - else if (chunk_name == png_cHRM) - png_handle_cHRM(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_eXIf_SUPPORTED - else if (chunk_name == png_eXIf) - png_handle_eXIf(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_gAMA_SUPPORTED - else if (chunk_name == png_gAMA) - png_handle_gAMA(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_hIST_SUPPORTED - else if (chunk_name == png_hIST) - png_handle_hIST(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_oFFs_SUPPORTED - else if (chunk_name == png_oFFs) - png_handle_oFFs(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_pCAL_SUPPORTED - else if (chunk_name == png_pCAL) - png_handle_pCAL(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sCAL_SUPPORTED - else if (chunk_name == png_sCAL) - png_handle_sCAL(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_pHYs_SUPPORTED - else if (chunk_name == png_pHYs) - png_handle_pHYs(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sBIT_SUPPORTED - else if (chunk_name == png_sBIT) - png_handle_sBIT(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sRGB_SUPPORTED - else if (chunk_name == png_sRGB) - png_handle_sRGB(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_iCCP_SUPPORTED - else if (chunk_name == png_iCCP) - png_handle_iCCP(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sPLT_SUPPORTED - else if (chunk_name == png_sPLT) - png_handle_sPLT(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_tEXt_SUPPORTED - else if (chunk_name == png_tEXt) - png_handle_tEXt(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_tIME_SUPPORTED - else if (chunk_name == png_tIME) - png_handle_tIME(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_tRNS_SUPPORTED - else if (chunk_name == png_tRNS) - png_handle_tRNS(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_zTXt_SUPPORTED - else if (chunk_name == png_zTXt) - png_handle_zTXt(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_iTXt_SUPPORTED - else if (chunk_name == png_iTXt) - png_handle_iTXt(png_ptr, info_ptr, length); -#endif - else - png_handle_unknown(png_ptr, info_ptr, length, - PNG_HANDLE_CHUNK_AS_DEFAULT); + png_handle_chunk(png_ptr, info_ptr, length); } } #endif /* SEQUENTIAL_READ */ @@ -827,13 +731,18 @@ png_read_end(png_structrp png_ptr, png_inforp info_ptr) png_uint_32 chunk_name = png_ptr->chunk_name; if (chunk_name != png_IDAT) - png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT; + { + /* These flags must be set consistently for all non-IDAT chunks, + * including the unknown chunks. + */ + png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT | PNG_AFTER_IDAT; + } if (chunk_name == png_IEND) - png_handle_IEND(png_ptr, info_ptr, length); + png_handle_chunk(png_ptr, info_ptr, length); else if (chunk_name == png_IHDR) - png_handle_IHDR(png_ptr, info_ptr, length); + png_handle_chunk(png_ptr, info_ptr, length); else if (info_ptr == NULL) png_crc_finish(png_ptr, length); @@ -867,102 +776,9 @@ png_read_end(png_structrp png_ptr, png_inforp info_ptr) png_crc_finish(png_ptr, length); } - else if (chunk_name == png_PLTE) - png_handle_PLTE(png_ptr, info_ptr, length); - -#ifdef PNG_READ_bKGD_SUPPORTED - else if (chunk_name == png_bKGD) - png_handle_bKGD(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_cHRM_SUPPORTED - else if (chunk_name == png_cHRM) - png_handle_cHRM(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_eXIf_SUPPORTED - else if (chunk_name == png_eXIf) - png_handle_eXIf(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_gAMA_SUPPORTED - else if (chunk_name == png_gAMA) - png_handle_gAMA(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_hIST_SUPPORTED - else if (chunk_name == png_hIST) - png_handle_hIST(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_oFFs_SUPPORTED - else if (chunk_name == png_oFFs) - png_handle_oFFs(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_pCAL_SUPPORTED - else if (chunk_name == png_pCAL) - png_handle_pCAL(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sCAL_SUPPORTED - else if (chunk_name == png_sCAL) - png_handle_sCAL(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_pHYs_SUPPORTED - else if (chunk_name == png_pHYs) - png_handle_pHYs(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sBIT_SUPPORTED - else if (chunk_name == png_sBIT) - png_handle_sBIT(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sRGB_SUPPORTED - else if (chunk_name == png_sRGB) - png_handle_sRGB(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_iCCP_SUPPORTED - else if (chunk_name == png_iCCP) - png_handle_iCCP(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sPLT_SUPPORTED - else if (chunk_name == png_sPLT) - png_handle_sPLT(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_tEXt_SUPPORTED - else if (chunk_name == png_tEXt) - png_handle_tEXt(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_tIME_SUPPORTED - else if (chunk_name == png_tIME) - png_handle_tIME(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_tRNS_SUPPORTED - else if (chunk_name == png_tRNS) - png_handle_tRNS(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_zTXt_SUPPORTED - else if (chunk_name == png_zTXt) - png_handle_zTXt(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_iTXt_SUPPORTED - else if (chunk_name == png_iTXt) - png_handle_iTXt(png_ptr, info_ptr, length); -#endif else - png_handle_unknown(png_ptr, info_ptr, length, - PNG_HANDLE_CHUNK_AS_DEFAULT); + png_handle_chunk(png_ptr, info_ptr, length); } while ((png_ptr->mode & PNG_HAVE_IEND) == 0); } #endif /* SEQUENTIAL_READ */ @@ -1027,7 +843,8 @@ png_read_destroy(png_structrp png_ptr) #endif #if defined(PNG_READ_EXPAND_SUPPORTED) && \ - defined(PNG_ARM_NEON_IMPLEMENTATION) + (defined(PNG_ARM_NEON_IMPLEMENTATION) || \ + defined(PNG_RISCV_RVV_IMPLEMENTATION)) png_free(png_ptr, png_ptr->riffled_palette); png_ptr->riffled_palette = NULL; #endif @@ -1413,6 +1230,31 @@ png_image_format(png_structrp png_ptr) return format; } +static int +chromaticities_match_sRGB(const png_xy *xy) +{ +# define sRGB_TOLERANCE 1000 + static const png_xy sRGB_xy = /* From ITU-R BT.709-3 */ + { + /* color x y */ + /* red */ 64000, 33000, + /* green */ 30000, 60000, + /* blue */ 15000, 6000, + /* white */ 31270, 32900 + }; + + if (PNG_OUT_OF_RANGE(xy->whitex, sRGB_xy.whitex,sRGB_TOLERANCE) || + PNG_OUT_OF_RANGE(xy->whitey, sRGB_xy.whitey,sRGB_TOLERANCE) || + PNG_OUT_OF_RANGE(xy->redx, sRGB_xy.redx, sRGB_TOLERANCE) || + PNG_OUT_OF_RANGE(xy->redy, sRGB_xy.redy, sRGB_TOLERANCE) || + PNG_OUT_OF_RANGE(xy->greenx, sRGB_xy.greenx,sRGB_TOLERANCE) || + PNG_OUT_OF_RANGE(xy->greeny, sRGB_xy.greeny,sRGB_TOLERANCE) || + PNG_OUT_OF_RANGE(xy->bluex, sRGB_xy.bluex, sRGB_TOLERANCE) || + PNG_OUT_OF_RANGE(xy->bluey, sRGB_xy.bluey, sRGB_TOLERANCE)) + return 0; + return 1; +} + /* Is the given gamma significantly different from sRGB? The test is the same * one used in pngrtran.c when deciding whether to do gamma correction. The * arithmetic optimizes the division by using the fact that the inverse of the @@ -1421,22 +1263,44 @@ png_image_format(png_structrp png_ptr) static int png_gamma_not_sRGB(png_fixed_point g) { - if (g < PNG_FP_1) - { - /* An uninitialized gamma is assumed to be sRGB for the simplified API. */ - if (g == 0) - return 0; - - return png_gamma_significant((g * 11 + 2)/5 /* i.e. *2.2, rounded */); - } + /* 1.6.47: use the same sanity checks as used in pngrtran.c */ + if (g < PNG_LIB_GAMMA_MIN || g > PNG_LIB_GAMMA_MAX) + return 0; /* Includes the uninitialized value 0 */ - return 1; + return png_gamma_significant((g * 11 + 2)/5 /* i.e. *2.2, rounded */); } /* Do the main body of a 'png_image_begin_read' function; read the PNG file * header and fill in all the information. This is executed in a safe context, * unlike the init routine above. */ +static int +png_image_is_not_sRGB(png_const_structrp png_ptr) +{ + /* Does the colorspace **not** match sRGB? The flag is only set if the + * answer can be determined reliably. + * + * png_struct::chromaticities always exists since the simplified API + * requires rgb-to-gray. The mDCV, cICP and cHRM chunks may all set it to + * a non-sRGB value, so it needs to be checked but **only** if one of + * those chunks occured in the file. + */ + /* Highest priority: check to be safe. */ + if (png_has_chunk(png_ptr, cICP) || png_has_chunk(png_ptr, mDCV)) + return !chromaticities_match_sRGB(&png_ptr->chromaticities); + + /* If the image is marked as sRGB then it is... */ + if (png_has_chunk(png_ptr, sRGB)) + return 0; + + /* Last stop: cHRM, must check: */ + if (png_has_chunk(png_ptr, cHRM)) + return !chromaticities_match_sRGB(&png_ptr->chromaticities); + + /* Else default to sRGB */ + return 0; +} + static int png_image_read_header(png_voidp argument) { @@ -1458,17 +1322,13 @@ png_image_read_header(png_voidp argument) image->format = format; -#ifdef PNG_COLORSPACE_SUPPORTED - /* Does the colorspace match sRGB? If there is no color endpoint - * (colorant) information assume yes, otherwise require the - * 'ENDPOINTS_MATCHP_sRGB' colorspace flag to have been set. If the - * colorspace has been determined to be invalid ignore it. + /* Greyscale images don't (typically) have colour space information and + * using it is pretty much impossible, so use sRGB for grayscale (it + * doesn't matter r==g==b so the transform is irrelevant.) */ - if ((format & PNG_FORMAT_FLAG_COLOR) != 0 && ((png_ptr->colorspace.flags - & (PNG_COLORSPACE_HAVE_ENDPOINTS|PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB| - PNG_COLORSPACE_INVALID)) == PNG_COLORSPACE_HAVE_ENDPOINTS)) + if ((format & PNG_FORMAT_FLAG_COLOR) != 0 && + png_image_is_not_sRGB(png_ptr)) image->flags |= PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB; -#endif } /* We need the maximum number of entries regardless of the format the @@ -1503,7 +1363,7 @@ png_image_read_header(png_voidp argument) #ifdef PNG_STDIO_SUPPORTED int PNGAPI -png_image_begin_read_from_stdio(png_imagep image, FILE* file) +png_image_begin_read_from_stdio(png_imagep image, FILE *file) { if (image != NULL && image->version == PNG_IMAGE_VERSION) { @@ -1656,21 +1516,18 @@ png_image_skip_unused_chunks(png_structrp png_ptr) * potential vulnerability to security problems in the unused chunks. * * At present the iCCP chunk data isn't used, so iCCP chunk can be ignored - * too. This allows the simplified API to be compiled without iCCP support, - * however if the support is there the chunk is still checked to detect - * errors (which are unfortunately quite common.) + * too. This allows the simplified API to be compiled without iCCP support. */ { static const png_byte chunks_to_process[] = { 98, 75, 71, 68, '\0', /* bKGD */ 99, 72, 82, 77, '\0', /* cHRM */ + 99, 73, 67, 80, '\0', /* cICP */ 103, 65, 77, 65, '\0', /* gAMA */ -# ifdef PNG_READ_iCCP_SUPPORTED - 105, 67, 67, 80, '\0', /* iCCP */ -# endif + 109, 68, 67, 86, '\0', /* mDCV */ 115, 66, 73, 84, '\0', /* sBIT */ 115, 82, 71, 66, '\0', /* sRGB */ - }; + }; /* Ignore unknown chunks and all other chunks except for the * IHDR, PLTE, tRNS, IDAT, and IEND chunks. @@ -1699,7 +1556,15 @@ png_image_skip_unused_chunks(png_structrp png_ptr) static void set_file_encoding(png_image_read_control *display) { - png_fixed_point g = display->image->opaque->png_ptr->colorspace.gamma; + png_structrp png_ptr = display->image->opaque->png_ptr; + png_fixed_point g = png_resolve_file_gamma(png_ptr); + + /* PNGv3: the result may be 0 however the 'default_gamma' should have been + * set before this is called so zero is an error: + */ + if (g == 0) + png_error(png_ptr, "internal: default gamma not set"); + if (png_gamma_significant(g) != 0) { if (png_gamma_not_sRGB(g) != 0) @@ -2187,24 +2052,18 @@ png_image_read_colormap(png_voidp argument) /* Default the input file gamma if required - this is necessary because * libpng assumes that if no gamma information is present the data is in the * output format, but the simplified API deduces the gamma from the input - * format. + * format. The 'default' gamma value is also set by png_set_alpha_mode, but + * this is happening before any such call, so: + * + * TODO: should be an internal API and all this code should be copied into a + * single common gamma+colorspace file. */ - if ((png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) == 0) - { - /* Do this directly, not using the png_colorspace functions, to ensure - * that it happens even if the colorspace is invalid (though probably if - * it is the setting will be ignored) Note that the same thing can be - * achieved at the application interface with png_set_gAMA. - */ - if (png_ptr->bit_depth == 16 && - (image->flags & PNG_IMAGE_FLAG_16BIT_sRGB) == 0) - png_ptr->colorspace.gamma = PNG_GAMMA_LINEAR; - - else - png_ptr->colorspace.gamma = PNG_GAMMA_sRGB_INVERSE; + if (png_ptr->bit_depth == 16 && + (image->flags & PNG_IMAGE_FLAG_16BIT_sRGB) == 0) + png_ptr->default_gamma = PNG_GAMMA_LINEAR; - png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; - } + else + png_ptr->default_gamma = PNG_GAMMA_sRGB_INVERSE; /* Decide what to do based on the PNG color type of the input data. The * utility function png_create_colormap_entry deals with most aspects of the @@ -2582,6 +2441,8 @@ png_image_read_colormap(png_voidp argument) else { + const png_fixed_point gamma = png_resolve_file_gamma(png_ptr); + /* Either the input or the output has no alpha channel, so there * will be no non-opaque pixels in the color-map; it will just be * grayscale. @@ -2596,10 +2457,13 @@ png_image_read_colormap(png_voidp argument) * this case and doing it in the palette; this will result in * duplicate palette entries, but that's better than the * alternative of double gamma correction. + * + * NOTE: PNGv3: check the resolved result of all the potentially + * different colour space chunks. */ if ((png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA || png_ptr->num_trans > 0) && - png_gamma_not_sRGB(png_ptr->colorspace.gamma) != 0) + png_gamma_not_sRGB(gamma) != 0) { cmap_entries = (unsigned int)make_gray_file_colormap(display); data_encoding = P_FILE; @@ -2631,8 +2495,8 @@ png_image_read_colormap(png_voidp argument) if (output_encoding == P_sRGB) gray = png_sRGB_table[gray]; /* now P_LINEAR */ - gray = PNG_DIV257(png_gamma_16bit_correct(gray, - png_ptr->colorspace.gamma)); /* now P_FILE */ + gray = PNG_DIV257(png_gamma_16bit_correct(gray, gamma)); + /* now P_FILE */ /* And make sure the corresponding palette entry contains * exactly the required sRGB value. @@ -3294,6 +3158,54 @@ png_image_read_colormapped(png_voidp argument) } } +/* Row reading for interlaced 16-to-8 bit depth conversion with local buffer. */ +static int +png_image_read_direct_scaled(png_voidp argument) +{ + png_image_read_control *display = png_voidcast(png_image_read_control*, + argument); + png_imagep image = display->image; + png_structrp png_ptr = image->opaque->png_ptr; + png_bytep local_row = png_voidcast(png_bytep, display->local_row); + png_bytep first_row = png_voidcast(png_bytep, display->first_row); + ptrdiff_t row_bytes = display->row_bytes; + int passes; + + /* Handle interlacing. */ + switch (png_ptr->interlaced) + { + case PNG_INTERLACE_NONE: + passes = 1; + break; + + case PNG_INTERLACE_ADAM7: + passes = PNG_INTERLACE_ADAM7_PASSES; + break; + + default: + png_error(png_ptr, "unknown interlace type"); + } + + /* Read each pass using local_row as intermediate buffer. */ + while (--passes >= 0) + { + png_uint_32 y = image->height; + png_bytep output_row = first_row; + + for (; y > 0; --y) + { + /* Read into local_row (gets transformed 8-bit data). */ + png_read_row(png_ptr, local_row, NULL); + + /* Copy from local_row to user buffer. */ + memcpy(output_row, local_row, (size_t)row_bytes); + output_row += row_bytes; + } + } + + return 1; +} + /* Just the row reading part of png_image_read. */ static int png_image_read_composite(png_voidp argument) @@ -3712,6 +3624,7 @@ png_image_read_direct(png_voidp argument) int linear = (format & PNG_FORMAT_FLAG_LINEAR) != 0; int do_local_compose = 0; int do_local_background = 0; /* to avoid double gamma correction bug */ + int do_local_scale = 0; /* for interlaced 16-to-8 bit conversion */ int passes = 0; /* Add transforms to ensure the correct output format is produced then check @@ -3763,6 +3676,12 @@ png_image_read_direct(png_voidp argument) /* Set the gamma appropriately, linear for 16-bit input, sRGB otherwise. */ { + /* This is safe but should no longer be necessary as + * png_ptr->default_gamma should have been set after the + * info-before-IDAT was read in png_image_read_header. + * + * TODO: 1.8: remove this and see what happens. + */ png_fixed_point input_gamma_default; if ((base_format & PNG_FORMAT_FLAG_LINEAR) != 0 && @@ -3818,8 +3737,9 @@ png_image_read_direct(png_voidp argument) * yet; it's set below. png_struct::gamma, however, is set to the * final value. */ - if (png_muldiv(>est, output_gamma, png_ptr->colorspace.gamma, - PNG_FP_1) != 0 && png_gamma_significant(gtest) == 0) + if (png_muldiv(>est, output_gamma, + png_resolve_file_gamma(png_ptr), PNG_FP_1) != 0 && + png_gamma_significant(gtest) == 0) do_local_background = 0; else if (mode == PNG_ALPHA_STANDARD) @@ -3838,8 +3758,16 @@ png_image_read_direct(png_voidp argument) png_set_expand_16(png_ptr); else /* 8-bit output */ + { png_set_scale_16(png_ptr); + /* For interlaced images, use local_row buffer to avoid overflow + * in png_combine_row() which writes using IHDR bit-depth. + */ + if (png_ptr->interlaced != 0) + do_local_scale = 1; + } + change &= ~PNG_FORMAT_FLAG_LINEAR; } @@ -4115,6 +4043,24 @@ png_image_read_direct(png_voidp argument) return result; } + else if (do_local_scale != 0) + { + /* For interlaced 16-to-8 conversion, use an intermediate row buffer + * to avoid buffer overflows in png_combine_row. The local_row is sized + * for the transformed (8-bit) output, preventing the overflow that would + * occur if png_combine_row wrote 16-bit data directly to the user buffer. + */ + int result; + png_voidp row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr)); + + display->local_row = row; + result = png_safe_execute(image, png_image_read_direct_scaled, display); + display->local_row = NULL; + png_free(png_ptr, row); + + return result; + } + else { png_alloc_size_t row_bytes = (png_alloc_size_t)display->row_bytes; diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngrio.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngrio.c index 961d010df4276..50a424d0912b4 100644 --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngrio.c +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngrio.c @@ -29,7 +29,7 @@ * However, the following notice accompanied the original version of this * file and, per its terms, should not be removed: * - * Copyright (c) 2018 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -85,7 +85,7 @@ png_default_read_data(png_structp png_ptr, png_bytep data, size_t length) /* fread() returns 0 on error, so it is OK to store this in a size_t * instead of an int, which is what fread() actually returns. */ - check = fread(data, 1, length, png_voidcast(png_FILE_p, png_ptr->io_ptr)); + check = fread(data, 1, length, png_voidcast(FILE *, png_ptr->io_ptr)); if (check != length) png_error(png_ptr, "Read Error"); diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c index a393de4b79d07..a19615f49fe79 100644 --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c @@ -29,7 +29,7 @@ * However, the following notice accompanied the original version of this * file and, per its terms, should not be removed: * - * Copyright (c) 2018-2024 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -57,6 +57,12 @@ # endif #endif +#ifdef PNG_RISCV_RVV_IMPLEMENTATION +# if PNG_RISCV_RVV_IMPLEMENTATION == 1 +# define PNG_RISCV_RVV_INTRINSICS_AVAILABLE +# endif +#endif + #ifdef PNG_READ_SUPPORTED /* Set the action on getting a CRC error for an ancillary or critical chunk. */ @@ -247,9 +253,59 @@ png_set_strip_alpha(png_structrp png_ptr) #endif #if defined(PNG_READ_ALPHA_MODE_SUPPORTED) || defined(PNG_READ_GAMMA_SUPPORTED) +/* PNGv3 conformance: this private API exists to resolve the now mandatory error + * resolution when multiple conflicting sources of gamma or colour space + * information are available. + * + * Terminology (assuming power law, "gamma", encodings): + * "screen" gamma: a power law imposed by the output device when digital + * samples are converted to visible light output. The EOTF - volage to + * luminance on output. + * + * "file" gamma: a power law used to encode luminance levels from the input + * data (the scene or the mastering display system) into digital voltages. + * The OETF - luminance to voltage on input. + * + * gamma "correction": a power law matching the **inverse** of the overall + * transfer function from input luminance levels to output levels. The + * **inverse** of the OOTF; the correction "corrects" for the OOTF by aiming + * to make the overall OOTF (including the correction) linear. + * + * It is important to understand this terminology because the defined terms are + * scattered throughout the libpng code and it is very easy to end up with the + * inverse of the power law required. + * + * Variable and struct::member names: + * file_gamma OETF how the PNG data was encoded + * + * screen_gamma EOTF how the screen will decode digital levels + * + * -- not used -- OOTF the net effect OETF x EOTF + * gamma_correction the inverse of OOTF to make the result linear + * + * All versions of libpng require a call to "png_set_gamma" to establish the + * "screen" gamma, the power law representing the EOTF. png_set_gamma may also + * set or default the "file" gamma; the OETF. gamma_correction is calculated + * internally. + * + * The earliest libpng versions required file_gamma to be supplied to set_gamma. + * Later versions started allowing png_set_gamma and, later, png_set_alpha_mode, + * to cause defaulting from the file data. + * + * PNGv3 mandated a particular form for this defaulting, one that is compatible + * with what libpng did except that if libpng detected inconsistencies it marked + * all the chunks as "invalid". PNGv3 effectively invalidates this prior code. + * + * Behaviour implemented below: + * translate_gamma_flags(gamma, is_screen) + * The libpng-1.6 API for the gamma parameters to libpng APIs + * (png_set_gamma and png_set_alpha_mode at present). This allows the + * 'gamma' value to be passed as a png_fixed_point number or as one of a + * set of integral values for specific "well known" examples of transfer + * functions. This is compatible with PNGv3. + */ static png_fixed_point -translate_gamma_flags(png_structrp png_ptr, png_fixed_point output_gamma, - int is_screen) +translate_gamma_flags(png_fixed_point output_gamma, int is_screen) { /* Check for flag values. The main reason for having the old Mac value as a * flag is that it is pretty near impossible to work out what the correct @@ -259,14 +315,6 @@ translate_gamma_flags(png_structrp png_ptr, png_fixed_point output_gamma, if (output_gamma == PNG_DEFAULT_sRGB || output_gamma == PNG_FP_1 / PNG_DEFAULT_sRGB) { - /* If there is no sRGB support this just sets the gamma to the standard - * sRGB value. (This is a side effect of using this function!) - */ -# ifdef PNG_READ_sRGB_SUPPORTED - png_ptr->flags |= PNG_FLAG_ASSUME_sRGB; -# else - PNG_UNUSED(png_ptr) -# endif if (is_screen != 0) output_gamma = PNG_GAMMA_sRGB; else @@ -308,6 +356,33 @@ convert_gamma_value(png_structrp png_ptr, double output_gamma) return (png_fixed_point)output_gamma; } # endif + +static int +unsupported_gamma(png_structrp png_ptr, png_fixed_point gamma, int warn) +{ + /* Validate a gamma value to ensure it is in a reasonable range. The value + * is expected to be 1 or greater, but this range test allows for some + * viewing correction values. The intent is to weed out the API users + * who might use the inverse of the gamma value accidentally! + * + * 1.6.47: apply the test in png_set_gamma as well but only warn and return + * false if it fires. + * + * TODO: 1.8: make this an app_error in png_set_gamma as well. + */ + if (gamma < PNG_LIB_GAMMA_MIN || gamma > PNG_LIB_GAMMA_MAX) + { +# define msg "gamma out of supported range" + if (warn) + png_app_warning(png_ptr, msg); + else + png_app_error(png_ptr, msg); + return 1; +# undef msg + } + + return 0; +} #endif /* READ_ALPHA_MODE || READ_GAMMA */ #ifdef PNG_READ_ALPHA_MODE_SUPPORTED @@ -315,31 +390,29 @@ void PNGFAPI png_set_alpha_mode_fixed(png_structrp png_ptr, int mode, png_fixed_point output_gamma) { - int compose = 0; png_fixed_point file_gamma; + int compose = 0; png_debug(1, "in png_set_alpha_mode_fixed"); if (png_rtran_ok(png_ptr, 0) == 0) return; - output_gamma = translate_gamma_flags(png_ptr, output_gamma, 1/*screen*/); - - /* Validate the value to ensure it is in a reasonable range. The value - * is expected to be 1 or greater, but this range test allows for some - * viewing correction values. The intent is to weed out the API users - * who might use the inverse of the gamma value accidentally! - * - * In libpng 1.6.0, we changed from 0.07..3 to 0.01..100, to accommodate - * the optimal 16-bit gamma of 36 and its reciprocal. - */ - if (output_gamma < 1000 || output_gamma > 10000000) - png_error(png_ptr, "output gamma out of expected range"); + output_gamma = translate_gamma_flags(output_gamma, 1/*screen*/); + if (unsupported_gamma(png_ptr, output_gamma, 0/*error*/)) + return; /* The default file gamma is the inverse of the output gamma; the output - * gamma may be changed below so get the file value first: + * gamma may be changed below so get the file value first. The default_gamma + * is set here and from the simplified API (which uses a different algorithm) + * so don't overwrite a set value: */ - file_gamma = png_reciprocal(output_gamma); + file_gamma = png_ptr->default_gamma; + if (file_gamma == 0) + { + file_gamma = png_reciprocal(output_gamma); + png_ptr->default_gamma = file_gamma; + } /* There are really 8 possibilities here, composed of any combination * of: @@ -390,17 +463,7 @@ png_set_alpha_mode_fixed(png_structrp png_ptr, int mode, png_error(png_ptr, "invalid alpha mode"); } - /* Only set the default gamma if the file gamma has not been set (this has - * the side effect that the gamma in a second call to png_set_alpha_mode will - * be ignored.) - */ - if (png_ptr->colorspace.gamma == 0) - { - png_ptr->colorspace.gamma = file_gamma; - png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; - } - - /* But always set the output gamma: */ + /* Set the screen gamma values: */ png_ptr->screen_gamma = output_gamma; /* Finally, if pre-multiplying, set the background fields to achieve the @@ -410,7 +473,7 @@ png_set_alpha_mode_fixed(png_structrp png_ptr, int mode, { /* And obtain alpha pre-multiplication by composing on black: */ memset(&png_ptr->background, 0, (sizeof png_ptr->background)); - png_ptr->background_gamma = png_ptr->colorspace.gamma; /* just in case */ + png_ptr->background_gamma = file_gamma; /* just in case */ png_ptr->background_gamma_type = PNG_BACKGROUND_GAMMA_FILE; png_ptr->transformations &= ~PNG_BACKGROUND_EXPAND; @@ -467,9 +530,19 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, { int i; + /* Initialize the array to index colors. + * + * Ensure quantize_index can fit 256 elements (PNG_MAX_PALETTE_LENGTH) + * rather than num_palette elements. This is to prevent buffer overflows + * caused by malformed PNG files with out-of-range palette indices. + * + * Be careful to avoid leaking memory. Applications are allowed to call + * this function more than once per png_struct. + */ + png_free(png_ptr, png_ptr->quantize_index); png_ptr->quantize_index = (png_bytep)png_malloc(png_ptr, - (png_alloc_size_t)num_palette); - for (i = 0; i < num_palette; i++) + PNG_MAX_PALETTE_LENGTH); + for (i = 0; i < PNG_MAX_PALETTE_LENGTH; i++) png_ptr->quantize_index[i] = (png_byte)i; } @@ -481,15 +554,14 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, * Perhaps not the best solution, but good enough. */ - int i; + png_bytep quantize_sort; + int i, j; - /* Initialize an array to sort colors */ - png_ptr->quantize_sort = (png_bytep)png_malloc(png_ptr, + /* Initialize the local array to sort colors. */ + quantize_sort = (png_bytep)png_malloc(png_ptr, (png_alloc_size_t)num_palette); - - /* Initialize the quantize_sort array */ for (i = 0; i < num_palette; i++) - png_ptr->quantize_sort[i] = (png_byte)i; + quantize_sort[i] = (png_byte)i; /* Find the least used palette entries by starting a * bubble sort, and running it until we have sorted @@ -501,19 +573,18 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, for (i = num_palette - 1; i >= maximum_colors; i--) { int done; /* To stop early if the list is pre-sorted */ - int j; done = 1; for (j = 0; j < i; j++) { - if (histogram[png_ptr->quantize_sort[j]] - < histogram[png_ptr->quantize_sort[j + 1]]) + if (histogram[quantize_sort[j]] + < histogram[quantize_sort[j + 1]]) { png_byte t; - t = png_ptr->quantize_sort[j]; - png_ptr->quantize_sort[j] = png_ptr->quantize_sort[j + 1]; - png_ptr->quantize_sort[j + 1] = t; + t = quantize_sort[j]; + quantize_sort[j] = quantize_sort[j + 1]; + quantize_sort[j + 1] = t; done = 0; } } @@ -525,18 +596,18 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, /* Swap the palette around, and set up a table, if necessary */ if (full_quantize != 0) { - int j = num_palette; + j = num_palette; /* Put all the useful colors within the max, but don't * move the others. */ for (i = 0; i < maximum_colors; i++) { - if ((int)png_ptr->quantize_sort[i] >= maximum_colors) + if ((int)quantize_sort[i] >= maximum_colors) { do j--; - while ((int)png_ptr->quantize_sort[j] >= maximum_colors); + while ((int)quantize_sort[j] >= maximum_colors); palette[i] = palette[j]; } @@ -544,7 +615,7 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, } else { - int j = num_palette; + j = num_palette; /* Move all the used colors inside the max limit, and * develop a translation table. @@ -552,13 +623,13 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, for (i = 0; i < maximum_colors; i++) { /* Only move the colors we need to */ - if ((int)png_ptr->quantize_sort[i] >= maximum_colors) + if ((int)quantize_sort[i] >= maximum_colors) { png_color tmp_color; do j--; - while ((int)png_ptr->quantize_sort[j] >= maximum_colors); + while ((int)quantize_sort[j] >= maximum_colors); tmp_color = palette[j]; palette[j] = palette[i]; @@ -596,8 +667,7 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, } } } - png_free(png_ptr, png_ptr->quantize_sort); - png_ptr->quantize_sort = NULL; + png_free(png_ptr, quantize_sort); } else { @@ -848,8 +918,8 @@ png_set_gamma_fixed(png_structrp png_ptr, png_fixed_point scrn_gamma, return; /* New in libpng-1.5.4 - reserve particular negative values as flags. */ - scrn_gamma = translate_gamma_flags(png_ptr, scrn_gamma, 1/*screen*/); - file_gamma = translate_gamma_flags(png_ptr, file_gamma, 0/*file*/); + scrn_gamma = translate_gamma_flags(scrn_gamma, 1/*screen*/); + file_gamma = translate_gamma_flags(file_gamma, 0/*file*/); /* Checking the gamma values for being >0 was added in 1.5.4 along with the * premultiplied alpha support; this actually hides an undocumented feature @@ -863,17 +933,19 @@ png_set_gamma_fixed(png_structrp png_ptr, png_fixed_point scrn_gamma, * libpng-1.6.0. */ if (file_gamma <= 0) - png_error(png_ptr, "invalid file gamma in png_set_gamma"); - + png_app_error(png_ptr, "invalid file gamma in png_set_gamma"); if (scrn_gamma <= 0) - png_error(png_ptr, "invalid screen gamma in png_set_gamma"); + png_app_error(png_ptr, "invalid screen gamma in png_set_gamma"); - /* Set the gamma values unconditionally - this overrides the value in the PNG - * file if a gAMA chunk was present. png_set_alpha_mode provides a - * different, easier, way to default the file gamma. + if (unsupported_gamma(png_ptr, file_gamma, 1/*warn*/) || + unsupported_gamma(png_ptr, scrn_gamma, 1/*warn*/)) + return; + + /* 1.6.47: png_struct::file_gamma and png_struct::screen_gamma are now only + * written by this API. This removes dependencies on the order of API calls + * and allows the complex gamma checks to be delayed until needed. */ - png_ptr->colorspace.gamma = file_gamma; - png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; + png_ptr->file_gamma = file_gamma; png_ptr->screen_gamma = scrn_gamma; } @@ -1051,26 +1123,9 @@ png_set_rgb_to_gray_fixed(png_structrp png_ptr, int error_action, png_ptr->rgb_to_gray_coefficients_set = 1; } - else - { - if (red >= 0 && green >= 0) - png_app_warning(png_ptr, - "ignoring out of range rgb_to_gray coefficients"); - - /* Use the defaults, from the cHRM chunk if set, else the historical - * values which are close to the sRGB/HDTV/ITU-Rec 709 values. See - * png_do_rgb_to_gray for more discussion of the values. In this case - * the coefficients are not marked as 'set' and are not overwritten if - * something has already provided a default. - */ - if (png_ptr->rgb_to_gray_red_coeff == 0 && - png_ptr->rgb_to_gray_green_coeff == 0) - { - png_ptr->rgb_to_gray_red_coeff = 6968; - png_ptr->rgb_to_gray_green_coeff = 23434; - /* png_ptr->rgb_to_gray_blue_coeff = 2366; */ - } - } + else if (red >= 0 && green >= 0) + png_app_warning(png_ptr, + "ignoring out of range rgb_to_gray coefficients"); } } @@ -1311,6 +1366,80 @@ png_init_rgb_transformations(png_structrp png_ptr) #endif /* READ_EXPAND && READ_BACKGROUND */ } +#ifdef PNG_READ_GAMMA_SUPPORTED +png_fixed_point /* PRIVATE */ +png_resolve_file_gamma(png_const_structrp png_ptr) +{ + png_fixed_point file_gamma; + + /* The file gamma is determined by these precedence rules, in this order + * (i.e. use the first value found): + * + * png_set_gamma; png_struct::file_gammma if not zero, then: + * png_struct::chunk_gamma if not 0 (determined the PNGv3 rules), then: + * png_set_gamma; 1/png_struct::screen_gamma if not zero + * + * 0 (i.e. do no gamma handling) + */ + file_gamma = png_ptr->file_gamma; + if (file_gamma != 0) + return file_gamma; + + file_gamma = png_ptr->chunk_gamma; + if (file_gamma != 0) + return file_gamma; + + file_gamma = png_ptr->default_gamma; + if (file_gamma != 0) + return file_gamma; + + /* If png_reciprocal oveflows it returns 0 which indicates to the caller that + * there is no usable file gamma. (The checks added to png_set_gamma and + * png_set_alpha_mode should prevent a screen_gamma which would overflow.) + */ + if (png_ptr->screen_gamma != 0) + file_gamma = png_reciprocal(png_ptr->screen_gamma); + + return file_gamma; +} + +static int +png_init_gamma_values(png_structrp png_ptr) +{ + /* The following temporary indicates if overall gamma correction is + * required. + */ + int gamma_correction = 0; + png_fixed_point file_gamma, screen_gamma; + + /* Resolve the file_gamma. See above: if png_ptr::screen_gamma is set + * file_gamma will always be set here: + */ + file_gamma = png_resolve_file_gamma(png_ptr); + screen_gamma = png_ptr->screen_gamma; + + if (file_gamma > 0) /* file has been set */ + { + if (screen_gamma > 0) /* screen set too */ + gamma_correction = png_gamma_threshold(file_gamma, screen_gamma); + + else + /* Assume the output matches the input; a long time default behavior + * of libpng, although the standard has nothing to say about this. + */ + screen_gamma = png_reciprocal(file_gamma); + } + + else /* both unset, prevent corrections: */ + file_gamma = screen_gamma = PNG_FP_1; + + png_ptr->file_gamma = file_gamma; + png_ptr->screen_gamma = screen_gamma; + return gamma_correction; + +} +#endif /* READ_GAMMA */ + void /* PRIVATE */ png_init_read_transformations(png_structrp png_ptr) { @@ -1330,59 +1459,22 @@ png_init_read_transformations(png_structrp png_ptr) * the test needs to be performed later - here. In addition prior to 1.5.4 * the tests were repeated for the PALETTE color type here - this is no * longer necessary (and doesn't seem to have been necessary before.) + * + * PNGv3: the new mandatory precedence/priority rules for colour space chunks + * are handled here (by calling the above function). + * + * Turn the gamma transformation on or off as appropriate. Notice that + * PNG_GAMMA just refers to the file->screen correction. Alpha composition + * may independently cause gamma correction because it needs linear data + * (e.g. if the file has a gAMA chunk but the screen gamma hasn't been + * specified.) In any case this flag may get turned off in the code + * immediately below if the transform can be handled outside the row loop. */ - { - /* The following temporary indicates if overall gamma correction is - * required. - */ - int gamma_correction = 0; - - if (png_ptr->colorspace.gamma != 0) /* has been set */ - { - if (png_ptr->screen_gamma != 0) /* screen set too */ - gamma_correction = png_gamma_threshold(png_ptr->colorspace.gamma, - png_ptr->screen_gamma); - - else - /* Assume the output matches the input; a long time default behavior - * of libpng, although the standard has nothing to say about this. - */ - png_ptr->screen_gamma = png_reciprocal(png_ptr->colorspace.gamma); - } - - else if (png_ptr->screen_gamma != 0) - /* The converse - assume the file matches the screen, note that this - * perhaps undesirable default can (from 1.5.4) be changed by calling - * png_set_alpha_mode (even if the alpha handling mode isn't required - * or isn't changed from the default.) - */ - png_ptr->colorspace.gamma = png_reciprocal(png_ptr->screen_gamma); + if (png_init_gamma_values(png_ptr) != 0) + png_ptr->transformations |= PNG_GAMMA; - else /* neither are set */ - /* Just in case the following prevents any processing - file and screen - * are both assumed to be linear and there is no way to introduce a - * third gamma value other than png_set_background with 'UNIQUE', and, - * prior to 1.5.4 - */ - png_ptr->screen_gamma = png_ptr->colorspace.gamma = PNG_FP_1; - - /* We have a gamma value now. */ - png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; - - /* Now turn the gamma transformation on or off as appropriate. Notice - * that PNG_GAMMA just refers to the file->screen correction. Alpha - * composition may independently cause gamma correction because it needs - * linear data (e.g. if the file has a gAMA chunk but the screen gamma - * hasn't been specified.) In any case this flag may get turned off in - * the code immediately below if the transform can be handled outside the - * row loop. - */ - if (gamma_correction != 0) - png_ptr->transformations |= PNG_GAMMA; - - else - png_ptr->transformations &= ~PNG_GAMMA; - } + else + png_ptr->transformations &= ~PNG_GAMMA; #endif /* Certain transformations have the effect of preventing other @@ -1454,7 +1546,7 @@ png_init_read_transformations(png_structrp png_ptr) * appropriately. */ if ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0) - png_colorspace_set_rgb_coefficients(png_ptr); + png_set_rgb_coefficients(png_ptr); #endif #ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED @@ -1597,10 +1689,10 @@ png_init_read_transformations(png_structrp png_ptr) */ if ((png_ptr->transformations & PNG_GAMMA) != 0 || ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0 && - (png_gamma_significant(png_ptr->colorspace.gamma) != 0 || + (png_gamma_significant(png_ptr->file_gamma) != 0 || png_gamma_significant(png_ptr->screen_gamma) != 0)) || ((png_ptr->transformations & PNG_COMPOSE) != 0 && - (png_gamma_significant(png_ptr->colorspace.gamma) != 0 || + (png_gamma_significant(png_ptr->file_gamma) != 0 || png_gamma_significant(png_ptr->screen_gamma) != 0 # ifdef PNG_READ_BACKGROUND_SUPPORTED || (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_UNIQUE && @@ -1656,8 +1748,8 @@ png_init_read_transformations(png_structrp png_ptr) break; case PNG_BACKGROUND_GAMMA_FILE: - g = png_reciprocal(png_ptr->colorspace.gamma); - gs = png_reciprocal2(png_ptr->colorspace.gamma, + g = png_reciprocal(png_ptr->file_gamma); + gs = png_reciprocal2(png_ptr->file_gamma, png_ptr->screen_gamma); break; @@ -1718,19 +1810,51 @@ png_init_read_transformations(png_structrp png_ptr) } else /* if (png_ptr->trans_alpha[i] != 0xff) */ { - png_byte v, w; - - v = png_ptr->gamma_to_1[palette[i].red]; - png_composite(w, v, png_ptr->trans_alpha[i], back_1.red); - palette[i].red = png_ptr->gamma_from_1[w]; - - v = png_ptr->gamma_to_1[palette[i].green]; - png_composite(w, v, png_ptr->trans_alpha[i], back_1.green); - palette[i].green = png_ptr->gamma_from_1[w]; - - v = png_ptr->gamma_to_1[palette[i].blue]; - png_composite(w, v, png_ptr->trans_alpha[i], back_1.blue); - palette[i].blue = png_ptr->gamma_from_1[w]; + if ((png_ptr->flags & PNG_FLAG_OPTIMIZE_ALPHA) != 0) + { + /* Premultiply only: + * component = round((component * alpha) / 255) + */ + png_uint_32 component; + + component = png_ptr->gamma_to_1[palette[i].red]; + component = + (component * png_ptr->trans_alpha[i] + 128) / 255; + palette[i].red = png_ptr->gamma_from_1[component]; + + component = png_ptr->gamma_to_1[palette[i].green]; + component = + (component * png_ptr->trans_alpha[i] + 128) / 255; + palette[i].green = png_ptr->gamma_from_1[component]; + + component = png_ptr->gamma_to_1[palette[i].blue]; + component = + (component * png_ptr->trans_alpha[i] + 128) / 255; + palette[i].blue = png_ptr->gamma_from_1[component]; + } + else + { + /* Composite with background color: + * component = + * alpha * component + (1 - alpha) * background + */ + png_byte v, w; + + v = png_ptr->gamma_to_1[palette[i].red]; + png_composite(w, v, + png_ptr->trans_alpha[i], back_1.red); + palette[i].red = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[palette[i].green]; + png_composite(w, v, + png_ptr->trans_alpha[i], back_1.green); + palette[i].green = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[palette[i].blue]; + png_composite(w, v, + png_ptr->trans_alpha[i], back_1.blue); + palette[i].blue = png_ptr->gamma_from_1[w]; + } } } else @@ -1765,8 +1889,8 @@ png_init_read_transformations(png_structrp png_ptr) break; case PNG_BACKGROUND_GAMMA_FILE: - g = png_reciprocal(png_ptr->colorspace.gamma); - gs = png_reciprocal2(png_ptr->colorspace.gamma, + g = png_reciprocal(png_ptr->file_gamma); + gs = png_reciprocal2(png_ptr->file_gamma, png_ptr->screen_gamma); break; @@ -2016,11 +2140,11 @@ png_read_transform_info(png_structrp png_ptr, png_inforp info_ptr) * been called before this from png_read_update_info->png_read_start_row * sometimes does the gamma transform and cancels the flag. * - * TODO: this looks wrong; the info_ptr should end up with a gamma equal to - * the screen_gamma value. The following probably results in weirdness if - * the info_ptr is used by the app after the rows have been read. + * TODO: this is confusing. It only changes the result of png_get_gAMA and, + * yes, it does return the value that the transformed data effectively has + * but does any app really understand this? */ - info_ptr->colorspace.gamma = png_ptr->colorspace.gamma; + info_ptr->gamma = png_ptr->file_gamma; #endif if (info_ptr->bit_depth == 16) @@ -4953,13 +5077,8 @@ png_do_read_transformations(png_structrp png_ptr, png_row_infop row_info) #ifdef PNG_READ_QUANTIZE_SUPPORTED if ((png_ptr->transformations & PNG_QUANTIZE) != 0) - { png_do_quantize(row_info, png_ptr->row_buf + 1, png_ptr->palette_lookup, png_ptr->quantize_index); - - if (row_info->rowbytes == 0) - png_error(png_ptr, "png_do_quantize returned rowbytes=0"); - } #endif /* READ_QUANTIZE */ #ifdef PNG_READ_EXPAND_16_SUPPORTED diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngrutil.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngrutil.c index 5280140d12bcb..07d53cb2c7630 100644 --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngrutil.c +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngrutil.c @@ -29,7 +29,7 @@ * However, the following notice accompanied the original version of this * file and, per its terms, should not be removed: * - * Copyright (c) 2018-2024 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -46,6 +46,26 @@ #ifdef PNG_READ_SUPPORTED +/* The minimum 'zlib' stream is assumed to be just the 2 byte header, 5 bytes + * minimum 'deflate' stream, and the 4 byte checksum. + */ +#define LZ77Min (2U+5U+4U) + +#ifdef PNG_READ_INTERLACING_SUPPORTED +/* Arrays to facilitate interlacing - use pass (0 - 6) as index. */ + +/* Start of interlace block */ +static const png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; +/* Offset to next interlace block */ +static const png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; +/* Start of interlace block in the y direction */ +static const png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; +/* Offset to next interlace block in the y direction */ +static const png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; + +/* TODO: Move these arrays to a common utility module to avoid duplication. */ +#endif + png_uint_32 PNGAPI png_get_uint_31(png_const_structrp png_ptr, png_const_bytep buf) { @@ -57,30 +77,6 @@ png_get_uint_31(png_const_structrp png_ptr, png_const_bytep buf) return uval; } -#if defined(PNG_READ_gAMA_SUPPORTED) || defined(PNG_READ_cHRM_SUPPORTED) -/* The following is a variation on the above for use with the fixed - * point values used for gAMA and cHRM. Instead of png_error it - * issues a warning and returns (-1) - an invalid value because both - * gAMA and cHRM use *unsigned* integers for fixed point values. - */ -#define PNG_FIXED_ERROR (-1) - -static png_fixed_point /* PRIVATE */ -png_get_fixed_point(png_structrp png_ptr, png_const_bytep buf) -{ - png_uint_32 uval = png_get_uint_32(buf); - - if (uval <= PNG_UINT_31_MAX) - return (png_fixed_point)uval; /* known to be in range */ - - /* The caller can turn off the warning by passing NULL. */ - if (png_ptr != NULL) - png_warning(png_ptr, "PNG fixed point integer out of range"); - - return PNG_FIXED_ERROR; -} -#endif - #ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED /* NOTE: the read macros will obscure these definitions, so that if * PNG_USE_READ_MACROS is set the library will not use them internally, @@ -177,6 +173,38 @@ png_read_sig(png_structrp png_ptr, png_inforp info_ptr) png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; } +/* This function is called to verify that a chunk name is valid. + * Do this using the bit-whacking approach from contrib/tools/pngfix.c + * + * Copied from libpng 1.7. + */ +static int +check_chunk_name(png_uint_32 name) +{ + png_uint_32 t; + + /* Remove bit 5 from all but the reserved byte; this means + * every 8-bit unit must be in the range 65-90 to be valid. + * So bit 5 must be zero, bit 6 must be set and bit 7 zero. + */ + name &= ~PNG_U32(32,32,0,32); + t = (name & ~0x1f1f1f1fU) ^ 0x40404040U; + + /* Subtract 65 for each 8-bit quantity, this must not + * overflow and each byte must then be in the range 0-25. + */ + name -= PNG_U32(65,65,65,65); + t |= name; + + /* Subtract 26, handling the overflow which should set the + * top three bits of each byte. + */ + name -= PNG_U32(25,25,25,26); + t |= ~name; + + return (t & 0xe0e0e0e0U) == 0U; +} + /* Read the chunk header (length + type name). * Put the type name into png_ptr->chunk_name, and return the length. */ @@ -184,33 +212,36 @@ png_uint_32 /* PRIVATE */ png_read_chunk_header(png_structrp png_ptr) { png_byte buf[8]; - png_uint_32 length; + png_uint_32 chunk_name, length; #ifdef PNG_IO_STATE_SUPPORTED png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_HDR; #endif - /* Read the length and the chunk name. - * This must be performed in a single I/O call. + /* Read the length and the chunk name. png_struct::chunk_name is immediately + * updated even if they are detectably wrong. This aids error message + * handling by allowing png_chunk_error to be used. */ png_read_data(png_ptr, buf, 8); length = png_get_uint_31(png_ptr, buf); + png_ptr->chunk_name = chunk_name = PNG_CHUNK_FROM_STRING(buf+4); - /* Put the chunk name into png_ptr->chunk_name. */ - png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(buf+4); + /* Reset the crc and run it over the chunk name. */ + png_reset_crc(png_ptr); + png_calculate_crc(png_ptr, buf + 4, 4); png_debug2(0, "Reading chunk typeid = 0x%lx, length = %lu", (unsigned long)png_ptr->chunk_name, (unsigned long)length); - /* Reset the crc and run it over the chunk name. */ - png_reset_crc(png_ptr); - png_calculate_crc(png_ptr, buf + 4, 4); + /* Sanity check the length (first by <= 0x80) and the chunk name. An error + * here indicates a broken stream and libpng has no recovery from this. + */ + if (buf[0] >= 0x80U) + png_chunk_error(png_ptr, "bad header (invalid length)"); /* Check to see if chunk name is valid. */ - png_check_chunk_name(png_ptr, png_ptr->chunk_name); - - /* Check for too-large chunk length */ - png_check_chunk_length(png_ptr, length); + if (!check_chunk_name(chunk_name)) + png_chunk_error(png_ptr, "bad header (invalid type)"); #ifdef PNG_IO_STATE_SUPPORTED png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_DATA; @@ -230,13 +261,85 @@ png_crc_read(png_structrp png_ptr, png_bytep buf, png_uint_32 length) png_calculate_crc(png_ptr, buf, length); } +/* Compare the CRC stored in the PNG file with that calculated by libpng from + * the data it has read thus far. + */ +static int +png_crc_error(png_structrp png_ptr, int handle_as_ancillary) +{ + png_byte crc_bytes[4]; + png_uint_32 crc; + int need_crc = 1; + + /* There are four flags two for ancillary and two for critical chunks. The + * default setting of these flags is all zero. + * + * PNG_FLAG_CRC_ANCILLARY_USE + * PNG_FLAG_CRC_ANCILLARY_NOWARN + * USE+NOWARN: no CRC calculation (implemented here), else; + * NOWARN: png_chunk_error on error (implemented in png_crc_finish) + * else: png_chunk_warning on error (implemented in png_crc_finish) + * This is the default. + * + * I.e. NOWARN without USE produces png_chunk_error. The default setting + * where neither are set does the same thing. + * + * PNG_FLAG_CRC_CRITICAL_USE + * PNG_FLAG_CRC_CRITICAL_IGNORE + * IGNORE: no CRC calculation (implemented here), else; + * USE: png_chunk_warning on error (implemented in png_crc_finish) + * else: png_chunk_error on error (implemented in png_crc_finish) + * This is the default. + * + * This arose because of original mis-implementation and has persisted for + * compatibility reasons. + * + * TODO: the flag names are internal so maybe this can be changed to + * something comprehensible. + */ + if (handle_as_ancillary || PNG_CHUNK_ANCILLARY(png_ptr->chunk_name) != 0) + { + if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == + (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) + need_crc = 0; + } + + else /* critical */ + { + if ((png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) != 0) + need_crc = 0; + } + +#ifdef PNG_IO_STATE_SUPPORTED + png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_CRC; +#endif + + /* The chunk CRC must be serialized in a single I/O call. */ + png_read_data(png_ptr, crc_bytes, 4); + + if (need_crc != 0) + { + crc = png_get_uint_32(crc_bytes); + return crc != png_ptr->crc; + } + + else + return 0; +} + /* Optionally skip data and then check the CRC. Depending on whether we * are reading an ancillary or critical chunk, and how the program has set * things up, we may calculate the CRC on the data and print a message. * Returns '1' if there was a CRC error, '0' otherwise. + * + * There is one public version which is used in most places and another which + * takes the value for the 'critical' flag to check. This allows PLTE and IEND + * handling code to ignore the CRC error and removes some confusing code + * duplication. */ -int /* PRIVATE */ -png_crc_finish(png_structrp png_ptr, png_uint_32 skip) +static int +png_crc_finish_critical(png_structrp png_ptr, png_uint_32 skip, + int handle_as_ancillary) { /* The size of the local buffer for inflate is a good guess as to a * reasonable size to use for buffering reads from the application. @@ -254,14 +357,24 @@ png_crc_finish(png_structrp png_ptr, png_uint_32 skip) png_crc_read(png_ptr, tmpbuf, len); } - if (png_crc_error(png_ptr) != 0) + /* If 'handle_as_ancillary' has been requested and this is a critical chunk + * but PNG_FLAG_CRC_CRITICAL_IGNORE was set then png_read_crc did not, in + * fact, calculate the CRC so the ANCILLARY settings should not be used + * instead. + */ + if (handle_as_ancillary && + (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) != 0) + handle_as_ancillary = 0; + + /* TODO: this might be more comprehensible if png_crc_error was inlined here. + */ + if (png_crc_error(png_ptr, handle_as_ancillary) != 0) { - if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name) != 0 ? + /* See above for the explanation of how the flags work. */ + if (handle_as_ancillary || PNG_CHUNK_ANCILLARY(png_ptr->chunk_name) != 0 ? (png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) == 0 : (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE) != 0) - { png_chunk_warning(png_ptr, "CRC error"); - } else png_chunk_error(png_ptr, "CRC error"); @@ -272,61 +385,29 @@ png_crc_finish(png_structrp png_ptr, png_uint_32 skip) return 0; } -/* Compare the CRC stored in the PNG file with that calculated by libpng from - * the data it has read thus far. - */ int /* PRIVATE */ -png_crc_error(png_structrp png_ptr) +png_crc_finish(png_structrp png_ptr, png_uint_32 skip) { - png_byte crc_bytes[4]; - png_uint_32 crc; - int need_crc = 1; - - if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name) != 0) - { - if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == - (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) - need_crc = 0; - } - - else /* critical */ - { - if ((png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) != 0) - need_crc = 0; - } - -#ifdef PNG_IO_STATE_SUPPORTED - png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_CRC; -#endif - - /* The chunk CRC must be serialized in a single I/O call. */ - png_read_data(png_ptr, crc_bytes, 4); - - if (need_crc != 0) - { - crc = png_get_uint_32(crc_bytes); - return crc != png_ptr->crc; - } - - else - return 0; + return png_crc_finish_critical(png_ptr, skip, 0/*critical handling*/); } #if defined(PNG_READ_iCCP_SUPPORTED) || defined(PNG_READ_iTXt_SUPPORTED) ||\ defined(PNG_READ_pCAL_SUPPORTED) || defined(PNG_READ_sCAL_SUPPORTED) ||\ defined(PNG_READ_sPLT_SUPPORTED) || defined(PNG_READ_tEXt_SUPPORTED) ||\ - defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_SEQUENTIAL_READ_SUPPORTED) + defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_READ_eXIf_SUPPORTED) ||\ + defined(PNG_SEQUENTIAL_READ_SUPPORTED) /* Manage the read buffer; this simply reallocates the buffer if it is not small * enough (or if it is not allocated). The routine returns a pointer to the * buffer; if an error occurs and 'warn' is set the routine returns NULL, else - * it will call png_error (via png_malloc) on failure. (warn == 2 means - * 'silent'). + * it will call png_error on failure. */ static png_bytep -png_read_buffer(png_structrp png_ptr, png_alloc_size_t new_size, int warn) +png_read_buffer(png_structrp png_ptr, png_alloc_size_t new_size) { png_bytep buffer = png_ptr->read_buffer; + if (new_size > png_chunk_max(png_ptr)) return NULL; + if (buffer != NULL && new_size > png_ptr->read_buffer_size) { png_ptr->read_buffer = NULL; @@ -341,24 +422,17 @@ png_read_buffer(png_structrp png_ptr, png_alloc_size_t new_size, int warn) if (buffer != NULL) { - memset(buffer, 0, new_size); /* just in case */ +# ifndef PNG_NO_MEMZERO /* for detecting UIM bugs **only** */ + memset(buffer, 0, new_size); /* just in case */ +# endif png_ptr->read_buffer = buffer; png_ptr->read_buffer_size = new_size; } - - else if (warn < 2) /* else silent */ - { - if (warn != 0) - png_chunk_warning(png_ptr, "insufficient memory to read chunk"); - - else - png_chunk_error(png_ptr, "insufficient memory to read chunk"); - } } return buffer; } -#endif /* READ_iCCP|iTXt|pCAL|sCAL|sPLT|tEXt|zTXt|SEQUENTIAL_READ */ +#endif /* READ_iCCP|iTXt|pCAL|sCAL|sPLT|tEXt|zTXt|eXIf|SEQUENTIAL_READ */ /* png_inflate_claim: claim the zstream for some nefarious purpose that involves * decompression. Returns Z_OK on success, else a zlib error code. It checks @@ -645,16 +719,7 @@ png_decompress_chunk(png_structrp png_ptr, * maybe a '\0' terminator too. We have to assume that 'prefix_size' is * limited only by the maximum chunk size. */ - png_alloc_size_t limit = PNG_SIZE_MAX; - -# ifdef PNG_SET_USER_LIMITS_SUPPORTED - if (png_ptr->user_chunk_malloc_max > 0 && - png_ptr->user_chunk_malloc_max < limit) - limit = png_ptr->user_chunk_malloc_max; -# elif PNG_USER_CHUNK_MALLOC_MAX > 0 - if (PNG_USER_CHUNK_MALLOC_MAX < limit) - limit = PNG_USER_CHUNK_MALLOC_MAX; -# endif + png_alloc_size_t limit = png_chunk_max(png_ptr); if (limit >= prefix_size + (terminate != 0)) { @@ -859,9 +924,9 @@ png_inflate_read(png_structrp png_ptr, png_bytep read_buffer, uInt read_size, } #endif /* READ_iCCP */ +/* CHUNK HANDLING */ /* Read and check the IDHR chunk */ - -void /* PRIVATE */ +static png_handle_result_code png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_byte buf[13]; @@ -871,12 +936,7 @@ png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_IHDR"); - if ((png_ptr->mode & PNG_HAVE_IHDR) != 0) - png_chunk_error(png_ptr, "out of place"); - - /* Check the length */ - if (length != 13) - png_chunk_error(png_ptr, "invalid"); + /* Length and position are checked by the caller. */ png_ptr->mode |= PNG_HAVE_IHDR; @@ -930,257 +990,196 @@ png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug1(3, "bit_depth = %d", png_ptr->bit_depth); png_debug1(3, "channels = %d", png_ptr->channels); png_debug1(3, "rowbytes = %lu", (unsigned long)png_ptr->rowbytes); + + /* Rely on png_set_IHDR to completely validate the data and call png_error if + * it's wrong. + */ png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, interlace_type, compression_type, filter_type); + + return handled_ok; + PNG_UNUSED(length) } /* Read and check the palette */ -void /* PRIVATE */ +/* TODO: there are several obvious errors in this code when handling + * out-of-place chunks and there is much over-complexity caused by trying to + * patch up the problems. + */ +static png_handle_result_code png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { - png_color palette[PNG_MAX_PALETTE_LENGTH]; - int max_palette_length, num, i; -#ifdef PNG_POINTER_INDEXING_SUPPORTED - png_colorp pal_ptr; -#endif + png_const_charp errmsg = NULL; png_debug(1, "in png_handle_PLTE"); - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - /* Moved to before the 'after IDAT' check below because otherwise duplicate - * PLTE chunks are potentially ignored (the spec says there shall not be more - * than one PLTE, the error is not treated as benign, so this check trumps - * the requirement that PLTE appears before IDAT.) + /* 1.6.47: consistency. This used to be especially treated as a critical + * error even in an image which is not colour mapped, there isn't a good + * justification for treating some errors here one way and others another so + * everything uses the same logic. */ - else if ((png_ptr->mode & PNG_HAVE_PLTE) != 0) - png_chunk_error(png_ptr, "duplicate"); + if ((png_ptr->mode & PNG_HAVE_PLTE) != 0) + errmsg = "duplicate"; else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) - { - /* This is benign because the non-benign error happened before, when an - * IDAT was encountered in a color-mapped image with no PLTE. - */ - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } + errmsg = "out of place"; - png_ptr->mode |= PNG_HAVE_PLTE; + else if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0) + errmsg = "ignored in grayscale PNG"; - if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "ignored in grayscale PNG"); - return; - } + else if (length > 3*PNG_MAX_PALETTE_LENGTH || (length % 3) != 0) + errmsg = "invalid"; -#ifndef PNG_READ_OPT_PLTE_SUPPORTED - if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) - { - png_crc_finish(png_ptr, length); - return; - } -#endif + /* This drops PLTE in favour of tRNS or bKGD because both of those chunks + * can have an effect on the rendering of the image whereas PLTE only matters + * in the case of an 8-bit display with a decoder which controls the palette. + * + * The alternative here is to ignore the error and store the palette anyway; + * destroying the tRNS will definately cause problems. + * + * NOTE: the case of PNG_COLOR_TYPE_PALETTE need not be considered because + * the png_handle_ routines for the three 'after PLTE' chunks tRNS, bKGD and + * hIST all check for a preceding PLTE in these cases. + */ + else if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE && + (png_has_chunk(png_ptr, tRNS) || png_has_chunk(png_ptr, bKGD))) + errmsg = "out of place"; - if (length > 3*PNG_MAX_PALETTE_LENGTH || length % 3) + else { - png_crc_finish(png_ptr, length); - - if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) - png_chunk_benign_error(png_ptr, "invalid"); + /* If the palette has 256 or fewer entries but is too large for the bit + * depth we don't issue an error to preserve the behavior of previous + * libpng versions. We silently truncate the unused extra palette entries + * here. + */ + const unsigned max_palette_length = + (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) ? + 1U << png_ptr->bit_depth : PNG_MAX_PALETTE_LENGTH; - else - png_chunk_error(png_ptr, "invalid"); + /* The cast is safe because 'length' is less than + * 3*PNG_MAX_PALETTE_LENGTH + */ + const unsigned num = (length > 3U*max_palette_length) ? + max_palette_length : (unsigned)length / 3U; - return; - } + unsigned i, j; + png_byte buf[3*PNG_MAX_PALETTE_LENGTH]; + png_color palette[PNG_MAX_PALETTE_LENGTH]; - /* The cast is safe because 'length' is less than 3*PNG_MAX_PALETTE_LENGTH */ - num = (int)length / 3; + /* Read the chunk into the buffer then read to the end of the chunk. */ + png_crc_read(png_ptr, buf, num*3U); + png_crc_finish_critical(png_ptr, length - 3U*num, + /* Handle as ancillary if PLTE is optional: */ + png_ptr->color_type != PNG_COLOR_TYPE_PALETTE); - /* If the palette has 256 or fewer entries but is too large for the bit - * depth, we don't issue an error, to preserve the behavior of previous - * libpng versions. We silently truncate the unused extra palette entries - * here. - */ - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) - max_palette_length = (1 << png_ptr->bit_depth); - else - max_palette_length = PNG_MAX_PALETTE_LENGTH; - - if (num > max_palette_length) - num = max_palette_length; + for (i = 0U, j = 0U; i < num; i++) + { + palette[i].red = buf[j++]; + palette[i].green = buf[j++]; + palette[i].blue = buf[j++]; + } -#ifdef PNG_POINTER_INDEXING_SUPPORTED - for (i = 0, pal_ptr = palette; i < num; i++, pal_ptr++) - { - png_byte buf[3]; + /* A valid PLTE chunk has been read */ + png_ptr->mode |= PNG_HAVE_PLTE; - png_crc_read(png_ptr, buf, 3); - pal_ptr->red = buf[0]; - pal_ptr->green = buf[1]; - pal_ptr->blue = buf[2]; - } -#else - for (i = 0; i < num; i++) - { - png_byte buf[3]; - - png_crc_read(png_ptr, buf, 3); - /* Don't depend upon png_color being any order */ - palette[i].red = buf[0]; - palette[i].green = buf[1]; - palette[i].blue = buf[2]; + /* TODO: png_set_PLTE has the side effect of setting png_ptr->palette to + * its own copy of the palette. This has the side effect that when + * png_start_row is called (this happens after any call to + * png_read_update_info) the info_ptr palette gets changed. This is + * extremely unexpected and confusing. + * + * REVIEW: there have been consistent bugs in the past about gamma and + * similar transforms to colour mapped images being useless because the + * modified palette cannot be accessed because of the above. + * + * CONSIDER: Fix this by not sharing the palette in this way. But does + * this completely fix the problem? + */ + png_set_PLTE(png_ptr, info_ptr, palette, num); + return handled_ok; } -#endif - /* If we actually need the PLTE chunk (ie for a paletted image), we do - * whatever the normal CRC configuration tells us. However, if we - * have an RGB image, the PLTE can be considered ancillary, so - * we will act as though it is. - */ -#ifndef PNG_READ_OPT_PLTE_SUPPORTED + /* Here on error: errmsg is non NULL. */ if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) -#endif { - png_crc_finish(png_ptr, (png_uint_32) (length - (unsigned int)num * 3)); + png_crc_finish(png_ptr, length); + png_chunk_error(png_ptr, errmsg); } -#ifndef PNG_READ_OPT_PLTE_SUPPORTED - else if (png_crc_error(png_ptr) != 0) /* Only if we have a CRC error */ + else /* not critical to this image */ { - /* If we don't want to use the data from an ancillary chunk, - * we have two options: an error abort, or a warning and we - * ignore the data in this chunk (which should be OK, since - * it's considered ancillary for a RGB or RGBA image). - * - * IMPLEMENTATION NOTE: this is only here because png_crc_finish uses the - * chunk type to determine whether to check the ancillary or the critical - * flags. - */ - if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_USE) == 0) - { - if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) != 0) - return; - - else - png_chunk_error(png_ptr, "CRC error"); - } - - /* Otherwise, we (optionally) emit a warning and use the chunk. */ - else if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) == 0) - png_chunk_warning(png_ptr, "CRC error"); + png_crc_finish_critical(png_ptr, length, 1/*handle as ancillary*/); + png_chunk_benign_error(png_ptr, errmsg); } -#endif - /* TODO: png_set_PLTE has the side effect of setting png_ptr->palette to its - * own copy of the palette. This has the side effect that when png_start_row - * is called (this happens after any call to png_read_update_info) the - * info_ptr palette gets changed. This is extremely unexpected and - * confusing. - * - * Fix this by not sharing the palette in this way. - */ - png_set_PLTE(png_ptr, info_ptr, palette, num); - - /* The three chunks, bKGD, hIST and tRNS *must* appear after PLTE and before - * IDAT. Prior to 1.6.0 this was not checked; instead the code merely - * checked the apparent validity of a tRNS chunk inserted before PLTE on a - * palette PNG. 1.6.0 attempts to rigorously follow the standard and - * therefore does a benign error if the erroneous condition is detected *and* - * cancels the tRNS if the benign error returns. The alternative is to - * amend the standard since it would be rather hypocritical of the standards - * maintainers to ignore it. + /* Because PNG_UNUSED(errmsg) does not work if all the uses are compiled out + * (this does happen). */ -#ifdef PNG_READ_tRNS_SUPPORTED - if (png_ptr->num_trans > 0 || - (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS) != 0)) - { - /* Cancel this because otherwise it would be used if the transforms - * require it. Don't cancel the 'valid' flag because this would prevent - * detection of duplicate chunks. - */ - png_ptr->num_trans = 0; - - if (info_ptr != NULL) - info_ptr->num_trans = 0; - - png_chunk_benign_error(png_ptr, "tRNS must be after"); - } -#endif - -#ifdef PNG_READ_hIST_SUPPORTED - if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) != 0) - png_chunk_benign_error(png_ptr, "hIST must be after"); -#endif - -#ifdef PNG_READ_bKGD_SUPPORTED - if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) != 0) - png_chunk_benign_error(png_ptr, "bKGD must be after"); -#endif + return errmsg != NULL ? handled_error : handled_error; } -void /* PRIVATE */ +/* On read the IDAT chunk is always handled specially, even if marked for + * unknown handling (this is allowed), so: + */ +#define png_handle_IDAT NULL + +static png_handle_result_code png_handle_IEND(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_debug(1, "in png_handle_IEND"); - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0 || - (png_ptr->mode & PNG_HAVE_IDAT) == 0) - png_chunk_error(png_ptr, "out of place"); - png_ptr->mode |= (PNG_AFTER_IDAT | PNG_HAVE_IEND); - png_crc_finish(png_ptr, length); - if (length != 0) png_chunk_benign_error(png_ptr, "invalid"); + png_crc_finish_critical(png_ptr, length, 1/*handle as ancillary*/); + + return handled_ok; PNG_UNUSED(info_ptr) } #ifdef PNG_READ_gAMA_SUPPORTED -void /* PRIVATE */ +static png_handle_result_code png_handle_gAMA(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { - png_fixed_point igamma; + png_uint_32 ugamma; png_byte buf[4]; png_debug(1, "in png_handle_gAMA"); - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); + png_crc_read(png_ptr, buf, 4); - else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } + if (png_crc_finish(png_ptr, 0) != 0) + return handled_error; + + ugamma = png_get_uint_32(buf); - if (length != 4) + if (ugamma > PNG_UINT_31_MAX) { - png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "invalid"); - return; + return handled_error; } - png_crc_read(png_ptr, buf, 4); + png_set_gAMA_fixed(png_ptr, info_ptr, (png_fixed_point)/*SAFE*/ugamma); - if (png_crc_finish(png_ptr, 0) != 0) - return; - - igamma = png_get_fixed_point(NULL, buf); +#ifdef PNG_READ_GAMMA_SUPPORTED + /* PNGv3: chunk precedence for gamma is cICP, [iCCP], sRGB, gAMA. gAMA is + * at the end of the chain so simply check for an unset value. + */ + if (png_ptr->chunk_gamma == 0) + png_ptr->chunk_gamma = (png_fixed_point)/*SAFE*/ugamma; +#endif /*READ_GAMMA*/ - png_colorspace_set_gamma(png_ptr, &png_ptr->colorspace, igamma); - png_colorspace_sync(png_ptr, info_ptr); + return handled_ok; + PNG_UNUSED(length) } +#else +# define png_handle_gAMA NULL #endif #ifdef PNG_READ_sBIT_SUPPORTED -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { unsigned int truelen, i; @@ -1189,23 +1188,6 @@ png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_sBIT"); - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); - return; - } - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { truelen = 3; @@ -1218,25 +1200,25 @@ png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) sample_depth = png_ptr->bit_depth; } - if (length != truelen || length > 4) + if (length != truelen) { - png_chunk_benign_error(png_ptr, "invalid"); png_crc_finish(png_ptr, length); - return; + png_chunk_benign_error(png_ptr, "bad length"); + return handled_error; } buf[0] = buf[1] = buf[2] = buf[3] = sample_depth; png_crc_read(png_ptr, buf, truelen); if (png_crc_finish(png_ptr, 0) != 0) - return; + return handled_error; for (i=0; i sample_depth) { png_chunk_benign_error(png_ptr, "invalid"); - return; + return handled_error; } } @@ -1248,7 +1230,7 @@ png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_ptr->sig_bit.alpha = buf[3]; } - else + else /* grayscale */ { png_ptr->sig_bit.gray = buf[0]; png_ptr->sig_bit.red = buf[0]; @@ -1258,133 +1240,132 @@ png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) } png_set_sBIT(png_ptr, info_ptr, &(png_ptr->sig_bit)); + return handled_ok; } +#else +# define png_handle_sBIT NULL #endif #ifdef PNG_READ_cHRM_SUPPORTED -void /* PRIVATE */ -png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static png_int_32 +png_get_int_32_checked(png_const_bytep buf, int *error) { - png_byte buf[32]; - png_xy xy; + png_uint_32 uval = png_get_uint_32(buf); + if ((uval & 0x80000000) == 0) /* non-negative */ + return (png_int_32)uval; - png_debug(1, "in png_handle_cHRM"); + uval = (uval ^ 0xffffffff) + 1; /* 2's complement: -x = ~x+1 */ + if ((uval & 0x80000000) == 0) /* no overflow */ + return -(png_int_32)uval; - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); + /* This version of png_get_int_32 has a way of returning the error to the + * caller, so: + */ + *error = 1; + return 0; /* Safe */ +} - else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } +static png_handle_result_code /* PRIVATE */ +png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + int error = 0; + png_xy xy; + png_byte buf[32]; - if (length != 32) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); - return; - } + png_debug(1, "in png_handle_cHRM"); png_crc_read(png_ptr, buf, 32); if (png_crc_finish(png_ptr, 0) != 0) - return; + return handled_error; - xy.whitex = png_get_fixed_point(NULL, buf); - xy.whitey = png_get_fixed_point(NULL, buf + 4); - xy.redx = png_get_fixed_point(NULL, buf + 8); - xy.redy = png_get_fixed_point(NULL, buf + 12); - xy.greenx = png_get_fixed_point(NULL, buf + 16); - xy.greeny = png_get_fixed_point(NULL, buf + 20); - xy.bluex = png_get_fixed_point(NULL, buf + 24); - xy.bluey = png_get_fixed_point(NULL, buf + 28); - - if (xy.whitex == PNG_FIXED_ERROR || - xy.whitey == PNG_FIXED_ERROR || - xy.redx == PNG_FIXED_ERROR || - xy.redy == PNG_FIXED_ERROR || - xy.greenx == PNG_FIXED_ERROR || - xy.greeny == PNG_FIXED_ERROR || - xy.bluex == PNG_FIXED_ERROR || - xy.bluey == PNG_FIXED_ERROR) + xy.whitex = png_get_int_32_checked(buf + 0, &error); + xy.whitey = png_get_int_32_checked(buf + 4, &error); + xy.redx = png_get_int_32_checked(buf + 8, &error); + xy.redy = png_get_int_32_checked(buf + 12, &error); + xy.greenx = png_get_int_32_checked(buf + 16, &error); + xy.greeny = png_get_int_32_checked(buf + 20, &error); + xy.bluex = png_get_int_32_checked(buf + 24, &error); + xy.bluey = png_get_int_32_checked(buf + 28, &error); + + if (error) { - png_chunk_benign_error(png_ptr, "invalid values"); - return; + png_chunk_benign_error(png_ptr, "invalid"); + return handled_error; } - /* If a colorspace error has already been output skip this chunk */ - if ((png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0) - return; + /* png_set_cHRM may complain about some of the values but this doesn't matter + * because it was a cHRM and it did have vaguely (if, perhaps, ridiculous) + * values. Ridiculousity will be checked if the values are used later. + */ + png_set_cHRM_fixed(png_ptr, info_ptr, xy.whitex, xy.whitey, xy.redx, xy.redy, + xy.greenx, xy.greeny, xy.bluex, xy.bluey); - if ((png_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) != 0) - { - png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID; - png_colorspace_sync(png_ptr, info_ptr); - png_chunk_benign_error(png_ptr, "duplicate"); - return; - } + /* We only use 'chromaticities' for RGB to gray */ +# ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + /* There is no need to check sRGB here, cICP is NYI and iCCP is not + * supported so just check mDCV. + */ + if (!png_has_chunk(png_ptr, mDCV)) + { + png_ptr->chromaticities = xy; + } +# endif /* READ_RGB_TO_GRAY */ - png_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM; - (void)png_colorspace_set_chromaticities(png_ptr, &png_ptr->colorspace, &xy, - 1/*prefer cHRM values*/); - png_colorspace_sync(png_ptr, info_ptr); + return handled_ok; + PNG_UNUSED(length) } +#else +# define png_handle_cHRM NULL #endif #ifdef PNG_READ_sRGB_SUPPORTED -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_sRGB(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_byte intent; png_debug(1, "in png_handle_sRGB"); - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - if (length != 1) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); - return; - } - png_crc_read(png_ptr, &intent, 1); if (png_crc_finish(png_ptr, 0) != 0) - return; + return handled_error; - /* If a colorspace error has already been output skip this chunk */ - if ((png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0) - return; - - /* Only one sRGB or iCCP chunk is allowed, use the HAVE_INTENT flag to detect - * this. + /* This checks the range of the "rendering intent" because it is specified in + * the PNG spec itself; the "reserved" values will result in the chunk not + * being accepted, just as they do with the various "reserved" values in + * IHDR. */ - if ((png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_INTENT) != 0) + if (intent > 3/*PNGv3 spec*/) { - png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID; - png_colorspace_sync(png_ptr, info_ptr); - png_chunk_benign_error(png_ptr, "too many profiles"); - return; + png_chunk_benign_error(png_ptr, "invalid"); + return handled_error; } - (void)png_colorspace_set_sRGB(png_ptr, &png_ptr->colorspace, intent); - png_colorspace_sync(png_ptr, info_ptr); + png_set_sRGB(png_ptr, info_ptr, intent); + /* NOTE: png_struct::chromaticities is not set here because the RGB to gray + * coefficients are known without a need for the chromaticities. + */ + +#ifdef PNG_READ_GAMMA_SUPPORTED + /* PNGv3: chunk precedence for gamma is cICP, [iCCP], sRGB, gAMA. iCCP is + * not supported by libpng so the only requirement is to check for cICP + * setting the gamma (this is NYI, but this check is safe.) + */ + if (!png_has_chunk(png_ptr, cICP) || png_ptr->chunk_gamma == 0) + png_ptr->chunk_gamma = PNG_GAMMA_sRGB_INVERSE; +#endif /*READ_GAMMA*/ + + return handled_ok; + PNG_UNUSED(length) } +#else +# define png_handle_sRGB NULL #endif /* READ_sRGB */ #ifdef PNG_READ_iCCP_SUPPORTED -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) /* Note: this does not properly handle profiles that are > 64K under DOS */ { @@ -1393,44 +1374,10 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_iCCP"); - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - /* Consistent with all the above colorspace handling an obviously *invalid* - * chunk is just ignored, so does not invalidate the color space. An - * alternative is to set the 'invalid' flags at the start of this routine - * and only clear them in they were not set before and all the tests pass. + /* PNGv3: allow PNG files with both sRGB and iCCP because the PNG spec only + * ever said that there "should" be only one, not "shall" and the PNGv3 + * colour chunk precedence rules give a handling for this case anyway. */ - - /* The keyword must be at least one character and there is a - * terminator (0) byte and the compression method byte, and the - * 'zlib' datastream is at least 11 bytes. - */ - if (length < 14) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "too short"); - return; - } - - /* If a colorspace error has already been output skip this chunk */ - if ((png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0) - { - png_crc_finish(png_ptr, length); - return; - } - - /* Only one sRGB or iCCP chunk is allowed, use the HAVE_INTENT flag to detect - * this. - */ - if ((png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_INTENT) == 0) { uInt read_length, keyword_length; char keyword[81]; @@ -1440,19 +1387,16 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) */ read_length = 81; /* maximum */ if (read_length > length) - read_length = (uInt)length; + read_length = (uInt)/*SAFE*/length; png_crc_read(png_ptr, (png_bytep)keyword, read_length); length -= read_length; - /* The minimum 'zlib' stream is assumed to be just the 2 byte header, - * 5 bytes minimum 'deflate' stream, and the 4 byte checksum. - */ - if (length < 11) + if (length < LZ77Min) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "too short"); - return; + return handled_error; } keyword_length = 0; @@ -1489,15 +1433,14 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) */ png_uint_32 profile_length = png_get_uint_32(profile_header); - if (png_icc_check_length(png_ptr, &png_ptr->colorspace, - keyword, profile_length) != 0) + if (png_icc_check_length(png_ptr, keyword, profile_length) != + 0) { /* The length is apparently ok, so we can check the 132 * byte header. */ - if (png_icc_check_header(png_ptr, &png_ptr->colorspace, - keyword, profile_length, profile_header, - png_ptr->color_type) != 0) + if (png_icc_check_header(png_ptr, keyword, profile_length, + profile_header, png_ptr->color_type) != 0) { /* Now read the tag table; a variable size buffer is * needed at this point, allocate one for the whole @@ -1507,7 +1450,7 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_uint_32 tag_count = png_get_uint_32(profile_header + 128); png_bytep profile = png_read_buffer(png_ptr, - profile_length, 2/*silent*/); + profile_length); if (profile != NULL) { @@ -1526,8 +1469,7 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (size == 0) { if (png_icc_check_tag_table(png_ptr, - &png_ptr->colorspace, keyword, profile_length, - profile) != 0) + keyword, profile_length, profile) != 0) { /* The profile has been validated for basic * security issues, so read the whole thing in. @@ -1559,13 +1501,6 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_crc_finish(png_ptr, length); finished = 1; -# if defined(PNG_sRGB_SUPPORTED) && PNG_sRGB_PROFILE_CHECKS >= 0 - /* Check for a match against sRGB */ - png_icc_set_sRGB(png_ptr, - &png_ptr->colorspace, profile, - png_ptr->zstream.adler); -# endif - /* Steal the profile for info_ptr. */ if (info_ptr != NULL) { @@ -1588,11 +1523,7 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) } else - { - png_ptr->colorspace.flags |= - PNG_COLORSPACE_INVALID; errmsg = "out of memory"; - } } /* else the profile remains in the read @@ -1600,13 +1531,10 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) * chunks. */ - if (info_ptr != NULL) - png_colorspace_sync(png_ptr, info_ptr); - if (errmsg == NULL) { png_ptr->zowner = 0; - return; + return handled_ok; } } if (errmsg == NULL) @@ -1647,22 +1575,21 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) errmsg = "bad keyword"; } - else - errmsg = "too many profiles"; - /* Failure: the reason is in 'errmsg' */ if (finished == 0) png_crc_finish(png_ptr, length); - png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID; - png_colorspace_sync(png_ptr, info_ptr); if (errmsg != NULL) /* else already output */ png_chunk_benign_error(png_ptr, errmsg); + + return handled_error; } +#else +# define png_handle_iCCP NULL #endif /* READ_iCCP */ #ifdef PNG_READ_sPLT_SUPPORTED -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) /* Note: this does not properly handle chunks that are > 64K under DOS */ { @@ -1683,43 +1610,24 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (png_ptr->user_chunk_cache_max == 1) { png_crc_finish(png_ptr, length); - return; + return handled_error; } if (--png_ptr->user_chunk_cache_max == 1) { png_warning(png_ptr, "No space in chunk cache for sPLT"); png_crc_finish(png_ptr, length); - return; + return handled_error; } } #endif - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - -#ifdef PNG_MAX_MALLOC_64K - if (length > 65535U) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "too large to fit in memory"); - return; - } -#endif - - buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/); + buffer = png_read_buffer(png_ptr, length+1); if (buffer == NULL) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "out of memory"); - return; + return handled_error; } @@ -1730,7 +1638,7 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_crc_read(png_ptr, buffer, length); if (png_crc_finish(png_ptr, skip) != 0) - return; + return handled_error; buffer[length] = 0; @@ -1743,7 +1651,7 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (length < 2U || entry_start > buffer + (length - 2U)) { png_warning(png_ptr, "malformed sPLT chunk"); - return; + return handled_error; } new_palette.depth = *entry_start++; @@ -1757,7 +1665,7 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if ((data_length % (unsigned int)entry_size) != 0) { png_warning(png_ptr, "sPLT chunk has bad length"); - return; + return handled_error; } dl = (png_uint_32)(data_length / (unsigned int)entry_size); @@ -1766,7 +1674,7 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (dl > max_dl) { png_warning(png_ptr, "sPLT chunk too long"); - return; + return handled_error; } new_palette.nentries = (png_int_32)(data_length / (unsigned int)entry_size); @@ -1777,10 +1685,9 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (new_palette.entries == NULL) { png_warning(png_ptr, "sPLT chunk requires too much memory"); - return; + return handled_error; } -#ifdef PNG_POINTER_INDEXING_SUPPORTED for (i = 0; i < new_palette.nentries; i++) { pp = new_palette.entries + i; @@ -1803,31 +1710,6 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) pp->frequency = png_get_uint_16(entry_start); entry_start += 2; } -#else - pp = new_palette.entries; - - for (i = 0; i < new_palette.nentries; i++) - { - - if (new_palette.depth == 8) - { - pp[i].red = *entry_start++; - pp[i].green = *entry_start++; - pp[i].blue = *entry_start++; - pp[i].alpha = *entry_start++; - } - - else - { - pp[i].red = png_get_uint_16(entry_start); entry_start += 2; - pp[i].green = png_get_uint_16(entry_start); entry_start += 2; - pp[i].blue = png_get_uint_16(entry_start); entry_start += 2; - pp[i].alpha = png_get_uint_16(entry_start); entry_start += 2; - } - - pp[i].frequency = png_get_uint_16(entry_start); entry_start += 2; - } -#endif /* Discard all chunk data except the name and stash that */ new_palette.name = (png_charp)buffer; @@ -1835,34 +1717,20 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_set_sPLT(png_ptr, info_ptr, &new_palette, 1); png_free(png_ptr, new_palette.entries); + return handled_ok; } +#else +# define png_handle_sPLT NULL #endif /* READ_sPLT */ #ifdef PNG_READ_tRNS_SUPPORTED -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_byte readbuf[PNG_MAX_PALETTE_LENGTH]; png_debug(1, "in png_handle_tRNS"); - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); - return; - } - if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) { png_byte buf[2]; @@ -1871,7 +1739,7 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "invalid"); - return; + return handled_error; } png_crc_read(png_ptr, buf, 2); @@ -1887,7 +1755,7 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "invalid"); - return; + return handled_error; } png_crc_read(png_ptr, buf, length); @@ -1901,10 +1769,9 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { if ((png_ptr->mode & PNG_HAVE_PLTE) == 0) { - /* TODO: is this actually an error in the ISO spec? */ png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "out of place"); - return; + return handled_error; } if (length > (unsigned int) png_ptr->num_palette || @@ -1913,7 +1780,7 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "invalid"); - return; + return handled_error; } png_crc_read(png_ptr, readbuf, length); @@ -1924,13 +1791,13 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "invalid with alpha channel"); - return; + return handled_error; } if (png_crc_finish(png_ptr, 0) != 0) { png_ptr->num_trans = 0; - return; + return handled_error; } /* TODO: this is a horrible side effect in the palette case because the @@ -1939,11 +1806,14 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) */ png_set_tRNS(png_ptr, info_ptr, readbuf, png_ptr->num_trans, &(png_ptr->trans_color)); + return handled_ok; } +#else +# define png_handle_tRNS NULL #endif #ifdef PNG_READ_bKGD_SUPPORTED -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { unsigned int truelen; @@ -1952,27 +1822,17 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_bKGD"); - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0 || - (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && - (png_ptr->mode & PNG_HAVE_PLTE) == 0)) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) != 0) + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); - return; - } + if ((png_ptr->mode & PNG_HAVE_PLTE) == 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return handled_error; + } - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) truelen = 1; + } else if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0) truelen = 6; @@ -1984,13 +1844,13 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "invalid"); - return; + return handled_error; } png_crc_read(png_ptr, buf, truelen); if (png_crc_finish(png_ptr, 0) != 0) - return; + return handled_error; /* We convert the index value into RGB components so that we can allow * arbitrary RGB values for background when we have transparency, and @@ -2006,7 +1866,7 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (buf[0] >= info_ptr->num_palette) { png_chunk_benign_error(png_ptr, "invalid index"); - return; + return handled_error; } background.red = (png_uint_16)png_ptr->palette[buf[0]].red; @@ -2027,7 +1887,7 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (buf[0] != 0 || buf[1] >= (unsigned int)(1 << png_ptr->bit_depth)) { png_chunk_benign_error(png_ptr, "invalid gray level"); - return; + return handled_error; } } @@ -2045,7 +1905,7 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (buf[0] != 0 || buf[2] != 0 || buf[4] != 0) { png_chunk_benign_error(png_ptr, "invalid color"); - return; + return handled_error; } } @@ -2057,75 +1917,174 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) } png_set_bKGD(png_ptr, info_ptr, &background); + return handled_ok; +} +#else +# define png_handle_bKGD NULL +#endif + +#ifdef PNG_READ_cICP_SUPPORTED +static png_handle_result_code /* PRIVATE */ +png_handle_cICP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_byte buf[4]; + + png_debug(1, "in png_handle_cICP"); + + png_crc_read(png_ptr, buf, 4); + + if (png_crc_finish(png_ptr, 0) != 0) + return handled_error; + + png_set_cICP(png_ptr, info_ptr, buf[0], buf[1], buf[2], buf[3]); + + /* We only use 'chromaticities' for RGB to gray */ +# ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + if (!png_has_chunk(png_ptr, mDCV)) + { + /* TODO: png_ptr->chromaticities = chromaticities; */ + } +# endif /* READ_RGB_TO_GRAY */ + +#ifdef PNG_READ_GAMMA_SUPPORTED + /* PNGv3: chunk precedence for gamma is cICP, [iCCP], sRGB, gAMA. cICP is + * at the head so simply set the gamma if it can be determined. If not + * chunk_gamma remains unchanged; sRGB and gAMA handling check it for + * being zero. + */ + /* TODO: set png_struct::chunk_gamma when possible */ +#endif /*READ_GAMMA*/ + + return handled_ok; + PNG_UNUSED(length) +} +#else +# define png_handle_cICP NULL +#endif + +#ifdef PNG_READ_cLLI_SUPPORTED +static png_handle_result_code /* PRIVATE */ +png_handle_cLLI(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_byte buf[8]; + + png_debug(1, "in png_handle_cLLI"); + + png_crc_read(png_ptr, buf, 8); + + if (png_crc_finish(png_ptr, 0) != 0) + return handled_error; + + /* The error checking happens here, this puts it in just one place: */ + png_set_cLLI_fixed(png_ptr, info_ptr, png_get_uint_32(buf), + png_get_uint_32(buf+4)); + return handled_ok; + PNG_UNUSED(length) +} +#else +# define png_handle_cLLI NULL +#endif + +#ifdef PNG_READ_mDCV_SUPPORTED +static png_handle_result_code /* PRIVATE */ +png_handle_mDCV(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_xy chromaticities; + png_byte buf[24]; + + png_debug(1, "in png_handle_mDCV"); + + png_crc_read(png_ptr, buf, 24); + + if (png_crc_finish(png_ptr, 0) != 0) + return handled_error; + + /* The error checking happens here, this puts it in just one place. The + * odd /50000 scaling factor makes it more difficult but the (x.y) values are + * only two bytes so a <<1 is safe. + * + * WARNING: the PNG specification defines the cHRM chunk to **start** with + * the white point (x,y). The W3C PNG v3 specification puts the white point + * **after* R,G,B. The x,y values in mDCV are also scaled by 50,000 and + * stored in just two bytes, whereas those in cHRM are scaled by 100,000 and + * stored in four bytes. This is very, very confusing. These APIs remove + * the confusion by copying the existing, well established, API. + */ + chromaticities.redx = png_get_uint_16(buf+ 0U) << 1; /* red x */ + chromaticities.redy = png_get_uint_16(buf+ 2U) << 1; /* red y */ + chromaticities.greenx = png_get_uint_16(buf+ 4U) << 1; /* green x */ + chromaticities.greeny = png_get_uint_16(buf+ 6U) << 1; /* green y */ + chromaticities.bluex = png_get_uint_16(buf+ 8U) << 1; /* blue x */ + chromaticities.bluey = png_get_uint_16(buf+10U) << 1; /* blue y */ + chromaticities.whitex = png_get_uint_16(buf+12U) << 1; /* white x */ + chromaticities.whitey = png_get_uint_16(buf+14U) << 1; /* white y */ + + png_set_mDCV_fixed(png_ptr, info_ptr, + chromaticities.whitex, chromaticities.whitey, + chromaticities.redx, chromaticities.redy, + chromaticities.greenx, chromaticities.greeny, + chromaticities.bluex, chromaticities.bluey, + png_get_uint_32(buf+16U), /* peak luminance */ + png_get_uint_32(buf+20U));/* minimum perceivable luminance */ + + /* We only use 'chromaticities' for RGB to gray */ +# ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + png_ptr->chromaticities = chromaticities; +# endif /* READ_RGB_TO_GRAY */ + + return handled_ok; + PNG_UNUSED(length) } +#else +# define png_handle_mDCV NULL #endif #ifdef PNG_READ_eXIf_SUPPORTED -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_eXIf(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { - unsigned int i; + png_bytep buffer = NULL; png_debug(1, "in png_handle_eXIf"); - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - if (length < 2) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "too short"); - return; - } + buffer = png_read_buffer(png_ptr, length); - else if (info_ptr == NULL || (info_ptr->valid & PNG_INFO_eXIf) != 0) + if (buffer == NULL) { png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); - return; + png_chunk_benign_error(png_ptr, "out of memory"); + return handled_error; } - info_ptr->free_me |= PNG_FREE_EXIF; + png_crc_read(png_ptr, buffer, length); - info_ptr->eXIf_buf = png_voidcast(png_bytep, - png_malloc_warn(png_ptr, length)); + if (png_crc_finish(png_ptr, 0) != 0) + return handled_error; - if (info_ptr->eXIf_buf == NULL) + /* PNGv3: the code used to check the byte order mark at the start for MM or + * II, however PNGv3 states that the the first 4 bytes should be checked. + * The caller ensures that there are four bytes available. + */ { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of memory"); - return; - } + png_uint_32 header = png_get_uint_32(buffer); - for (i = 0; i < length; i++) - { - png_byte buf[1]; - png_crc_read(png_ptr, buf, 1); - info_ptr->eXIf_buf[i] = buf[0]; - if (i == 1) + /* These numbers are copied from the PNGv3 spec: */ + if (header != 0x49492A00 && header != 0x4D4D002A) { - if ((buf[0] != 'M' && buf[0] != 'I') || - (info_ptr->eXIf_buf[0] != buf[0])) - { - png_crc_finish(png_ptr, length - 2); - png_chunk_benign_error(png_ptr, "incorrect byte-order specifier"); - png_free(png_ptr, info_ptr->eXIf_buf); - info_ptr->eXIf_buf = NULL; - return; - } + png_chunk_benign_error(png_ptr, "invalid"); + return handled_error; } } - if (png_crc_finish(png_ptr, 0) == 0) - png_set_eXIf_1(png_ptr, info_ptr, length, info_ptr->eXIf_buf); - - png_free(png_ptr, info_ptr->eXIf_buf); - info_ptr->eXIf_buf = NULL; + png_set_eXIf_1(png_ptr, info_ptr, length, buffer); + return handled_ok; } +#else +# define png_handle_eXIf NULL #endif #ifdef PNG_READ_hIST_SUPPORTED -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { unsigned int num, i; @@ -2133,25 +2092,13 @@ png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_hIST"); - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0 || - (png_ptr->mode & PNG_HAVE_PLTE) == 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); - return; - } - - num = length / 2 ; + /* This cast is safe because the chunk definition limits the length to a + * maximum of 1024 bytes. + * + * TODO: maybe use png_uint_32 anyway, not unsigned int, to reduce the + * casts. + */ + num = (unsigned int)length / 2 ; if (length != num * 2 || num != (unsigned int)png_ptr->num_palette || @@ -2159,7 +2106,7 @@ png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "invalid"); - return; + return handled_error; } for (i = 0; i < num; i++) @@ -2171,14 +2118,17 @@ png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) } if (png_crc_finish(png_ptr, 0) != 0) - return; + return handled_error; png_set_hIST(png_ptr, info_ptr, readbuf); + return handled_ok; } +#else +# define png_handle_hIST NULL #endif #ifdef PNG_READ_pHYs_SUPPORTED -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_pHYs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_byte buf[9]; @@ -2187,44 +2137,24 @@ png_handle_pHYs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_pHYs"); - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); - return; - } - - if (length != 9) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); - return; - } - png_crc_read(png_ptr, buf, 9); if (png_crc_finish(png_ptr, 0) != 0) - return; + return handled_error; res_x = png_get_uint_32(buf); res_y = png_get_uint_32(buf + 4); unit_type = buf[8]; png_set_pHYs(png_ptr, info_ptr, res_x, res_y, unit_type); + return handled_ok; + PNG_UNUSED(length) } +#else +# define png_handle_pHYs NULL #endif #ifdef PNG_READ_oFFs_SUPPORTED -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_oFFs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_byte buf[9]; @@ -2233,45 +2163,25 @@ png_handle_oFFs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_oFFs"); - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); - return; - } - - if (length != 9) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); - return; - } - png_crc_read(png_ptr, buf, 9); if (png_crc_finish(png_ptr, 0) != 0) - return; + return handled_error; offset_x = png_get_int_32(buf); offset_y = png_get_int_32(buf + 4); unit_type = buf[8]; png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, unit_type); + return handled_ok; + PNG_UNUSED(length) } +#else +# define png_handle_oFFs NULL #endif #ifdef PNG_READ_pCAL_SUPPORTED /* Read the pCAL chunk (described in the PNG Extensions document) */ -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_int_32 X0, X1; @@ -2281,40 +2191,22 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) int i; png_debug(1, "in png_handle_pCAL"); - - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); - return; - } - png_debug1(2, "Allocating and reading pCAL chunk data (%u bytes)", length + 1); - buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/); + buffer = png_read_buffer(png_ptr, length+1); if (buffer == NULL) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "out of memory"); - return; + return handled_error; } png_crc_read(png_ptr, buffer, length); if (png_crc_finish(png_ptr, 0) != 0) - return; + return handled_error; buffer[length] = 0; /* Null terminate the last string */ @@ -2330,7 +2222,7 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (endptr - buf <= 12) { png_chunk_benign_error(png_ptr, "invalid"); - return; + return handled_error; } png_debug(3, "Reading pCAL X0, X1, type, nparams, and units"); @@ -2350,7 +2242,7 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) (type == PNG_EQUATION_HYPERBOLIC && nparams != 4)) { png_chunk_benign_error(png_ptr, "invalid parameter count"); - return; + return handled_error; } else if (type >= PNG_EQUATION_LAST) @@ -2369,7 +2261,7 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (params == NULL) { png_chunk_benign_error(png_ptr, "out of memory"); - return; + return handled_error; } /* Get pointers to the start of each parameter string. */ @@ -2387,20 +2279,29 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_free(png_ptr, params); png_chunk_benign_error(png_ptr, "invalid data"); - return; + return handled_error; } } png_set_pCAL(png_ptr, info_ptr, (png_charp)buffer, X0, X1, type, nparams, (png_charp)units, params); + /* TODO: BUG: png_set_pCAL calls png_chunk_report which, in this case, calls + * png_benign_error and that can error out. + * + * png_read_buffer needs to be allocated with space for both nparams and the + * parameter strings. Not hard to do. + */ png_free(png_ptr, params); + return handled_ok; } +#else +# define png_handle_pCAL NULL #endif #ifdef PNG_READ_sCAL_SUPPORTED /* Read the sCAL chunk */ -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_bytep buffer; @@ -2408,55 +2309,29 @@ png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) int state; png_debug(1, "in png_handle_sCAL"); - - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); - return; - } - - /* Need unit type, width, \0, height: minimum 4 bytes */ - else if (length < 4) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); - return; - } - png_debug1(2, "Allocating and reading sCAL chunk data (%u bytes)", length + 1); - buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/); + buffer = png_read_buffer(png_ptr, length+1); if (buffer == NULL) { - png_chunk_benign_error(png_ptr, "out of memory"); png_crc_finish(png_ptr, length); - return; + png_chunk_benign_error(png_ptr, "out of memory"); + return handled_error; } png_crc_read(png_ptr, buffer, length); buffer[length] = 0; /* Null terminate the last string */ if (png_crc_finish(png_ptr, 0) != 0) - return; + return handled_error; /* Validate the unit. */ if (buffer[0] != 1 && buffer[0] != 2) { png_chunk_benign_error(png_ptr, "invalid unit"); - return; + return handled_error; } /* Validate the ASCII numbers, need two ASCII numbers separated by @@ -2485,15 +2360,22 @@ png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_chunk_benign_error(png_ptr, "non-positive height"); else + { /* This is the (only) success case. */ png_set_sCAL_s(png_ptr, info_ptr, buffer[0], (png_charp)buffer+1, (png_charp)buffer+heighti); + return handled_ok; + } } + + return handled_error; } +#else +# define png_handle_sCAL NULL #endif #ifdef PNG_READ_tIME_SUPPORTED -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_byte buf[7]; @@ -2501,30 +2383,17 @@ png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_tIME"); - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); - return; - } - + /* TODO: what is this doing here? It should be happened in pngread.c and + * pngpread.c, although it could be moved to png_handle_chunk below and + * thereby avoid some code duplication. + */ if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) png_ptr->mode |= PNG_AFTER_IDAT; - if (length != 7) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); - return; - } - png_crc_read(png_ptr, buf, 7); if (png_crc_finish(png_ptr, 0) != 0) - return; + return handled_error; mod_time.second = buf[6]; mod_time.minute = buf[5]; @@ -2534,12 +2403,16 @@ png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) mod_time.year = png_get_uint_16(buf); png_set_tIME(png_ptr, info_ptr, &mod_time); + return handled_ok; + PNG_UNUSED(length) } +#else +# define png_handle_tIME NULL #endif #ifdef PNG_READ_tEXt_SUPPORTED /* Note: this does not properly handle chunks that are > 64K under DOS */ -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_text text_info; @@ -2556,45 +2429,31 @@ png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (png_ptr->user_chunk_cache_max == 1) { png_crc_finish(png_ptr, length); - return; + return handled_error; } if (--png_ptr->user_chunk_cache_max == 1) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "no space in chunk cache"); - return; + return handled_error; } } #endif - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) - png_ptr->mode |= PNG_AFTER_IDAT; - -#ifdef PNG_MAX_MALLOC_64K - if (length > 65535U) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "too large to fit in memory"); - return; - } -#endif - - buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/); + buffer = png_read_buffer(png_ptr, length+1); if (buffer == NULL) { + png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "out of memory"); - return; + return handled_error; } png_crc_read(png_ptr, buffer, length); if (png_crc_finish(png_ptr, skip) != 0) - return; + return handled_error; key = (png_charp)buffer; key[length] = 0; @@ -2613,14 +2472,19 @@ png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) text_info.text = text; text_info.text_length = strlen(text); - if (png_set_text_2(png_ptr, info_ptr, &text_info, 1) != 0) - png_warning(png_ptr, "Insufficient memory to process text chunk"); + if (png_set_text_2(png_ptr, info_ptr, &text_info, 1) == 0) + return handled_ok; + + png_chunk_benign_error(png_ptr, "out of memory"); + return handled_error; } +#else +# define png_handle_tEXt NULL #endif #ifdef PNG_READ_zTXt_SUPPORTED /* Note: this does not correctly handle chunks that are > 64K under DOS */ -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_const_charp errmsg = NULL; @@ -2635,40 +2499,35 @@ png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (png_ptr->user_chunk_cache_max == 1) { png_crc_finish(png_ptr, length); - return; + return handled_error; } if (--png_ptr->user_chunk_cache_max == 1) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "no space in chunk cache"); - return; + return handled_error; } } #endif - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) - png_ptr->mode |= PNG_AFTER_IDAT; - /* Note, "length" is sufficient here; we won't be adding - * a null terminator later. + * a null terminator later. The limit check in png_handle_chunk should be + * sufficient. */ - buffer = png_read_buffer(png_ptr, length, 2/*silent*/); + buffer = png_read_buffer(png_ptr, length); if (buffer == NULL) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "out of memory"); - return; + return handled_error; } png_crc_read(png_ptr, buffer, length); if (png_crc_finish(png_ptr, 0) != 0) - return; + return handled_error; /* TODO: also check that the keyword contents match the spec! */ for (keyword_length = 0; @@ -2721,8 +2580,10 @@ png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) text.lang = NULL; text.lang_key = NULL; - if (png_set_text_2(png_ptr, info_ptr, &text, 1) != 0) - errmsg = "insufficient memory"; + if (png_set_text_2(png_ptr, info_ptr, &text, 1) == 0) + return handled_ok; + + errmsg = "out of memory"; } } @@ -2730,14 +2591,16 @@ png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) errmsg = png_ptr->zstream.msg; } - if (errmsg != NULL) - png_chunk_benign_error(png_ptr, errmsg); + png_chunk_benign_error(png_ptr, errmsg); + return handled_error; } +#else +# define png_handle_zTXt NULL #endif #ifdef PNG_READ_iTXt_SUPPORTED /* Note: this does not correctly handle chunks that are > 64K under DOS */ -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_const_charp errmsg = NULL; @@ -2752,37 +2615,31 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (png_ptr->user_chunk_cache_max == 1) { png_crc_finish(png_ptr, length); - return; + return handled_error; } if (--png_ptr->user_chunk_cache_max == 1) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "no space in chunk cache"); - return; + return handled_error; } } #endif - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) - png_ptr->mode |= PNG_AFTER_IDAT; - - buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/); + buffer = png_read_buffer(png_ptr, length+1); if (buffer == NULL) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "out of memory"); - return; + return handled_error; } png_crc_read(png_ptr, buffer, length); if (png_crc_finish(png_ptr, 0) != 0) - return; + return handled_error; /* First the keyword. */ for (prefix_length=0; @@ -2872,8 +2729,10 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) text.text_length = 0; text.itxt_length = uncompressed_length; - if (png_set_text_2(png_ptr, info_ptr, &text, 1) != 0) - errmsg = "insufficient memory"; + if (png_set_text_2(png_ptr, info_ptr, &text, 1) == 0) + return handled_ok; + + errmsg = "out of memory"; } } @@ -2882,7 +2741,10 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (errmsg != NULL) png_chunk_benign_error(png_ptr, errmsg); + return handled_error; } +#else +# define png_handle_iTXt NULL #endif #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED @@ -2890,7 +2752,7 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) static int png_cache_unknown_chunk(png_structrp png_ptr, png_uint_32 length) { - png_alloc_size_t limit = PNG_SIZE_MAX; + const png_alloc_size_t limit = png_chunk_max(png_ptr); if (png_ptr->unknown_chunk.data != NULL) { @@ -2898,16 +2760,6 @@ png_cache_unknown_chunk(png_structrp png_ptr, png_uint_32 length) png_ptr->unknown_chunk.data = NULL; } -# ifdef PNG_SET_USER_LIMITS_SUPPORTED - if (png_ptr->user_chunk_malloc_max > 0 && - png_ptr->user_chunk_malloc_max < limit) - limit = png_ptr->user_chunk_malloc_max; - -# elif PNG_USER_CHUNK_MALLOC_MAX > 0 - if (PNG_USER_CHUNK_MALLOC_MAX < limit) - limit = PNG_USER_CHUNK_MALLOC_MAX; -# endif - if (length <= limit) { PNG_CSTRING_FROM_CHUNK(png_ptr->unknown_chunk.name, png_ptr->chunk_name); @@ -2946,11 +2798,11 @@ png_cache_unknown_chunk(png_structrp png_ptr, png_uint_32 length) #endif /* READ_UNKNOWN_CHUNKS */ /* Handle an unknown, or known but disabled, chunk */ -void /* PRIVATE */ +png_handle_result_code /*PRIVATE*/ png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length, int keep) { - int handled = 0; /* the chunk was handled */ + png_handle_result_code handled = handled_discarded; /* the default */ png_debug(1, "in png_handle_unknown"); @@ -2997,7 +2849,7 @@ png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr, * error at this point unless it is to be saved. * positive: The chunk was handled, libpng will ignore/discard it. */ - if (ret < 0) + if (ret < 0) /* handled_error */ png_chunk_error(png_ptr, "error in user chunk"); else if (ret == 0) @@ -3031,7 +2883,7 @@ png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr, else /* chunk was handled */ { - handled = 1; + handled = handled_ok; /* Critical chunks can be safely discarded at this point. */ keep = PNG_HANDLE_CHUNK_NEVER; } @@ -3116,7 +2968,7 @@ png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr, */ png_set_unknown_chunks(png_ptr, info_ptr, &png_ptr->unknown_chunk, 1); - handled = 1; + handled = handled_saved; # ifdef PNG_USER_LIMITS_SUPPORTED break; } @@ -3142,79 +2994,267 @@ png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr, #endif /* !READ_UNKNOWN_CHUNKS */ /* Check for unhandled critical chunks */ - if (handled == 0 && PNG_CHUNK_CRITICAL(png_ptr->chunk_name)) + if (handled < handled_saved && PNG_CHUNK_CRITICAL(png_ptr->chunk_name)) png_chunk_error(png_ptr, "unhandled critical chunk"); + + return handled; } -/* This function is called to verify that a chunk name is valid. - * This function can't have the "critical chunk check" incorporated - * into it, since in the future we will need to be able to call user - * functions to handle unknown critical chunks after we check that - * the chunk name itself is valid. +/* APNG handling: the minimal implementation of APNG handling in libpng 1.6 + * requires that those significant applications which already handle APNG not + * get hosed. To do this ensure the code here will have to ensure than APNG + * data by default (at least in 1.6) gets stored in the unknown chunk list. + * Maybe this can be relaxed in a few years but at present it's just the only + * safe way. + * + * ATM just cause unknown handling for all three chunks: */ +#define png_handle_acTL NULL +#define png_handle_fcTL NULL +#define png_handle_fdAT NULL -/* Bit hacking: the test for an invalid byte in the 4 byte chunk name is: +/* + * 1.6.47: This is the new table driven interface to all the chunk handling. * - * ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) + * The table describes the PNG standard rules for **reading** known chunks - + * every chunk which has an entry in PNG_KNOWN_CHUNKS. The table contains an + * entry for each PNG_INDEX_cHNK describing the rules. + * + * In this initial version the only information in the entry is the + * png_handle_cHNK function for the chunk in question. When chunk support is + * compiled out the entry will be NULL. */ - -void /* PRIVATE */ -png_check_chunk_name(png_const_structrp png_ptr, png_uint_32 chunk_name) +static const struct { - int i; - png_uint_32 cn=chunk_name; - - png_debug(1, "in png_check_chunk_name"); + png_handle_result_code (*handler)( + png_structrp, png_inforp, png_uint_32 length); + /* A chunk-specific 'handler', NULL if the chunk is not supported in this + * build. + */ - for (i=1; i<=4; ++i) + /* Crushing these values helps on modern 32-bit architectures because the + * pointer and the following bit fields both end up requiring 32 bits. + * Typically this will halve the table size. On 64-bit architectures the + * table entries will typically be 8 bytes. + */ + png_uint_32 max_length :12; /* Length min, max in bytes */ + png_uint_32 min_length :8; + /* Length errors on critical chunks have special handling to preserve the + * existing behaviour in libpng 1.6. Anciallary chunks are checked below + * and produce a 'benign' error. + */ + png_uint_32 pos_before :4; /* PNG_HAVE_ values chunk must precede */ + png_uint_32 pos_after :4; /* PNG_HAVE_ values chunk must follow */ + /* NOTE: PLTE, tRNS and bKGD require special handling which depends on + * the colour type of the base image. + */ + png_uint_32 multiple :1; /* Multiple occurences permitted */ + /* This is enabled for PLTE because PLTE may, in practice, be optional */ +} +read_chunks[PNG_INDEX_unknown] = +{ + /* Definitions as above but done indirectly by #define so that + * PNG_KNOWN_CHUNKS can be used safely to build the table in order. + * + * Each CDcHNK definition lists the values for the parameters **after** + * the first, 'handler', function. 'handler' is NULL when the chunk has no + * compiled in support. + */ +# define NoCheck 0x801U /* Do not check the maximum length */ +# define Limit 0x802U /* Limit to png_chunk_max bytes */ +# define LKMin 3U+LZ77Min /* Minimum length of keyword+LZ77 */ + +#define hIHDR PNG_HAVE_IHDR +#define hPLTE PNG_HAVE_PLTE +#define hIDAT PNG_HAVE_IDAT + /* For the two chunks, tRNS and bKGD which can occur in PNGs without a PLTE + * but must occur after the PLTE use this and put the check in the handler + * routine for colour mapped images were PLTE is required. Also put a check + * in PLTE for other image types to drop the PLTE if tRNS or bKGD have been + * seen. + */ +#define hCOL (PNG_HAVE_PLTE|PNG_HAVE_IDAT) + /* Used for the decoding chunks which must be before PLTE. */ +#define aIDAT PNG_AFTER_IDAT + + /* Chunks from W3C PNG v3: */ + /* cHNK max_len, min, before, after, multiple */ +# define CDIHDR 13U, 13U, hIHDR, 0, 0 +# define CDPLTE NoCheck, 0U, 0, hIHDR, 1 + /* PLTE errors are only critical for colour-map images, consequently the + * hander does all the checks. + */ +# define CDIDAT NoCheck, 0U, aIDAT, hIHDR, 1 +# define CDIEND NoCheck, 0U, 0, aIDAT, 0 + /* Historically data was allowed in IEND */ +# define CDtRNS 256U, 0U, hIDAT, hIHDR, 0 +# define CDcHRM 32U, 32U, hCOL, hIHDR, 0 +# define CDgAMA 4U, 4U, hCOL, hIHDR, 0 +# define CDiCCP NoCheck, LKMin, hCOL, hIHDR, 0 +# define CDsBIT 4U, 1U, hCOL, hIHDR, 0 +# define CDsRGB 1U, 1U, hCOL, hIHDR, 0 +# define CDcICP 4U, 4U, hCOL, hIHDR, 0 +# define CDmDCV 24U, 24U, hCOL, hIHDR, 0 +# define CDeXIf Limit, 4U, 0, hIHDR, 0 +# define CDcLLI 8U, 8U, hCOL, hIHDR, 0 +# define CDtEXt NoCheck, 2U, 0, hIHDR, 1 + /* Allocates 'length+1'; checked in the handler */ +# define CDzTXt Limit, LKMin, 0, hIHDR, 1 +# define CDiTXt NoCheck, 6U, 0, hIHDR, 1 + /* Allocates 'length+1'; checked in the handler */ +# define CDbKGD 6U, 1U, hIDAT, hIHDR, 0 +# define CDhIST 1024U, 0U, hPLTE, hIHDR, 0 +# define CDpHYs 9U, 9U, hIDAT, hIHDR, 0 +# define CDsPLT NoCheck, 3U, hIDAT, hIHDR, 1 + /* Allocates 'length+1'; checked in the handler */ +# define CDtIME 7U, 7U, 0, hIHDR, 0 +# define CDacTL 8U, 8U, hIDAT, hIHDR, 0 +# define CDfcTL 25U, 26U, 0, hIHDR, 1 +# define CDfdAT Limit, 4U, hIDAT, hIHDR, 1 + /* Supported chunks from PNG extensions 1.5.0, NYI so limit */ +# define CDoFFs 9U, 9U, hIDAT, hIHDR, 0 +# define CDpCAL NoCheck, 14U, hIDAT, hIHDR, 0 + /* Allocates 'length+1'; checked in the handler */ +# define CDsCAL Limit, 4U, hIDAT, hIHDR, 0 + /* Allocates 'length+1'; checked in the handler */ + +# define PNG_CHUNK(cHNK, index) { png_handle_ ## cHNK, CD ## cHNK }, + PNG_KNOWN_CHUNKS +# undef PNG_CHUNK +}; + + +static png_index +png_chunk_index_from_name(png_uint_32 chunk_name) +{ + /* For chunk png_cHNK return PNG_INDEX_cHNK. Return PNG_INDEX_unknown if + * chunk_name is not known. Notice that in a particular build "known" does + * not necessarily mean "supported", although the inverse applies. + */ + switch (chunk_name) { - int c = cn & 0xff; +# define PNG_CHUNK(cHNK, index)\ + case png_ ## cHNK: return PNG_INDEX_ ## cHNK; /* == index */ + + PNG_KNOWN_CHUNKS - if (c < 65 || c > 122 || (c > 90 && c < 97)) - png_chunk_error(png_ptr, "invalid chunk type"); +# undef PNG_CHUNK - cn >>= 8; + default: return PNG_INDEX_unknown; } } -void /* PRIVATE */ -png_check_chunk_length(png_const_structrp png_ptr, png_uint_32 length) +png_handle_result_code /*PRIVATE*/ +png_handle_chunk(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { - png_alloc_size_t limit = PNG_UINT_31_MAX; - -# ifdef PNG_SET_USER_LIMITS_SUPPORTED - if (png_ptr->user_chunk_malloc_max > 0 && - png_ptr->user_chunk_malloc_max < limit) - limit = png_ptr->user_chunk_malloc_max; -# elif PNG_USER_CHUNK_MALLOC_MAX > 0 - if (PNG_USER_CHUNK_MALLOC_MAX < limit) - limit = PNG_USER_CHUNK_MALLOC_MAX; -# endif - if (png_ptr->chunk_name == png_IDAT) + /* CSE: these things don't change, these autos are just to save typing and + * make the code more clear. + */ + const png_uint_32 chunk_name = png_ptr->chunk_name; + const png_index chunk_index = png_chunk_index_from_name(chunk_name); + + png_handle_result_code handled = handled_error; + png_const_charp errmsg = NULL; + + /* Is this a known chunk? If not there are no checks performed here; + * png_handle_unknown does the correct checks. This means that the values + * for known but unsupported chunks in the above table are not used here + * however the chunks_seen fields in png_struct are still set. + */ + if (chunk_index == PNG_INDEX_unknown || + read_chunks[chunk_index].handler == NULL) { - png_alloc_size_t idat_limit = PNG_UINT_31_MAX; - size_t row_factor = - (size_t)png_ptr->width - * (size_t)png_ptr->channels - * (png_ptr->bit_depth > 8? 2: 1) - + 1 - + (png_ptr->interlaced? 6: 0); - if (png_ptr->height > PNG_UINT_32_MAX/row_factor) - idat_limit = PNG_UINT_31_MAX; - else - idat_limit = png_ptr->height * row_factor; - row_factor = row_factor > 32566? 32566 : row_factor; - idat_limit += 6 + 5*(idat_limit/row_factor+1); /* zlib+deflate overhead */ - idat_limit=idat_limit < PNG_UINT_31_MAX? idat_limit : PNG_UINT_31_MAX; - limit = limit < idat_limit? idat_limit : limit; + handled = png_handle_unknown( + png_ptr, info_ptr, length, PNG_HANDLE_CHUNK_AS_DEFAULT); + } + + /* First check the position. The first check is historical; the stream must + * start with IHDR and anything else causes libpng to give up immediately. + */ + else if (chunk_index != PNG_INDEX_IHDR && + (png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_chunk_error(png_ptr, "missing IHDR"); /* NORETURN */ + + /* Before all the pos_before chunks, after all the pos_after chunks. */ + else if (((png_ptr->mode & read_chunks[chunk_index].pos_before) != 0) || + ((png_ptr->mode & read_chunks[chunk_index].pos_after) != + read_chunks[chunk_index].pos_after)) + { + errmsg = "out of place"; + } + + /* Now check for duplicates: duplicated critical chunks also produce a + * full error. + */ + else if (read_chunks[chunk_index].multiple == 0 && + png_file_has_chunk(png_ptr, chunk_index)) + { + errmsg = "duplicate"; + } + + else if (length < read_chunks[chunk_index].min_length) + errmsg = "too short"; + else + { + /* NOTE: apart from IHDR the critical chunks (PLTE, IDAT and IEND) are set + * up above not to do any length checks. + * + * The png_chunk_max check ensures that the variable length chunks are + * always checked at this point for being within the system allocation + * limits. + */ + unsigned max_length = read_chunks[chunk_index].max_length; + + switch (max_length) + { + case Limit: + /* png_read_chunk_header has already png_error'ed chunks with a + * length exceeding the 31-bit PNG limit, so just check the memory + * limit: + */ + if (length <= png_chunk_max(png_ptr)) + goto MeetsLimit; + + errmsg = "length exceeds libpng limit"; + break; + + default: + if (length <= max_length) + goto MeetsLimit; + + errmsg = "too long"; + break; + + case NoCheck: + MeetsLimit: + handled = read_chunks[chunk_index].handler( + png_ptr, info_ptr, length); + break; + } + } + + /* If there was an error or the chunk was simply skipped it is not counted as + * 'seen'. + */ + if (errmsg != NULL) + { + if (PNG_CHUNK_CRITICAL(chunk_name)) /* stop immediately */ + png_chunk_error(png_ptr, errmsg); + else /* ancillary chunk */ + { + /* The chunk data is skipped: */ + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, errmsg); + } } - if (length > limit) + else if (handled >= handled_saved) { - png_debug2(0," length = %lu, limit = %lu", - (unsigned long)length,(unsigned long)limit); - png_benign_error(png_ptr, "chunk data is too large"); + if (chunk_index != PNG_INDEX_unknown) + png_file_add_chunk(png_ptr, chunk_index); } + + return handled; } /* Combines the row recently read in with the existing pixels in the row. This @@ -3712,10 +3752,6 @@ void /* PRIVATE */ png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass, png_uint_32 transformations /* Because these may affect the byte layout */) { - /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ - /* Offset to next interlace block */ - static const unsigned int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; - png_debug(1, "in png_do_read_interlace"); if (row != NULL && row_info != NULL) { @@ -4208,6 +4244,9 @@ png_read_IDAT_data(png_structrp png_ptr, png_bytep output, avail_in = png_ptr->IDAT_read_size; + if (avail_in > png_chunk_max(png_ptr)) + avail_in = (uInt)/*SAFE*/png_chunk_max(png_ptr); + if (avail_in > png_ptr->idat_size) avail_in = (uInt)png_ptr->idat_size; @@ -4215,8 +4254,13 @@ png_read_IDAT_data(png_structrp png_ptr, png_bytep output, * to minimize memory usage by causing lots of re-allocs, but * realistically doing IDAT_read_size re-allocs is not likely to be a * big problem. + * + * An error here corresponds to the system being out of memory. */ - buffer = png_read_buffer(png_ptr, avail_in, 0/*error*/); + buffer = png_read_buffer(png_ptr, avail_in); + + if (buffer == NULL) + png_chunk_error(png_ptr, "out of memory"); png_crc_read(png_ptr, buffer, avail_in); png_ptr->idat_size -= avail_in; @@ -4353,20 +4397,6 @@ png_read_finish_IDAT(png_structrp png_ptr) void /* PRIVATE */ png_read_finish_row(png_structrp png_ptr) { - /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ - - /* Start of interlace block */ - static const png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; - - /* Offset to next interlace block */ - static const png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; - - /* Start of interlace block in the y direction */ - static const png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; - - /* Offset to next interlace block in the y direction */ - static const png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; - png_debug(1, "in png_read_finish_row"); png_ptr->row_number++; if (png_ptr->row_number < png_ptr->num_rows) @@ -4418,20 +4448,6 @@ png_read_finish_row(png_structrp png_ptr) void /* PRIVATE */ png_read_start_row(png_structrp png_ptr) { - /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ - - /* Start of interlace block */ - static const png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; - - /* Offset to next interlace block */ - static const png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; - - /* Start of interlace block in the y direction */ - static const png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; - - /* Offset to next interlace block in the y direction */ - static const png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; - unsigned int max_pixel_depth; size_t row_bytes; diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngset.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngset.c index f53ab6fa1d18f..0b2844f186451 100644 --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngset.c +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngset.c @@ -29,7 +29,7 @@ * However, the following notice accompanied the original version of this * file and, per its terms, should not be removed: * - * Copyright (c) 2018-2024 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -70,27 +70,21 @@ png_set_cHRM_fixed(png_const_structrp png_ptr, png_inforp info_ptr, png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y, png_fixed_point blue_x, png_fixed_point blue_y) { - png_xy xy; - png_debug1(1, "in %s storage function", "cHRM fixed"); if (png_ptr == NULL || info_ptr == NULL) return; - xy.redx = red_x; - xy.redy = red_y; - xy.greenx = green_x; - xy.greeny = green_y; - xy.bluex = blue_x; - xy.bluey = blue_y; - xy.whitex = white_x; - xy.whitey = white_y; - - if (png_colorspace_set_chromaticities(png_ptr, &info_ptr->colorspace, &xy, - 2/* override with app values*/) != 0) - info_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM; + info_ptr->cHRM.redx = red_x; + info_ptr->cHRM.redy = red_y; + info_ptr->cHRM.greenx = green_x; + info_ptr->cHRM.greeny = green_y; + info_ptr->cHRM.bluex = blue_x; + info_ptr->cHRM.bluey = blue_y; + info_ptr->cHRM.whitex = white_x; + info_ptr->cHRM.whitey = white_y; - png_colorspace_sync_info(png_ptr, info_ptr); + info_ptr->valid |= PNG_INFO_cHRM; } void PNGFAPI @@ -102,6 +96,7 @@ png_set_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_inforp info_ptr, png_fixed_point int_blue_Z) { png_XYZ XYZ; + png_xy xy; png_debug1(1, "in %s storage function", "cHRM XYZ fixed"); @@ -118,11 +113,14 @@ png_set_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_inforp info_ptr, XYZ.blue_Y = int_blue_Y; XYZ.blue_Z = int_blue_Z; - if (png_colorspace_set_endpoints(png_ptr, &info_ptr->colorspace, - &XYZ, 2) != 0) - info_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM; + if (png_xy_from_XYZ(&xy, &XYZ) == 0) + { + info_ptr->cHRM = xy; + info_ptr->valid |= PNG_INFO_cHRM; + } - png_colorspace_sync_info(png_ptr, info_ptr); + else + png_app_error(png_ptr, "invalid cHRM XYZ"); } # ifdef PNG_FLOATING_POINT_SUPPORTED @@ -162,6 +160,189 @@ png_set_cHRM_XYZ(png_const_structrp png_ptr, png_inforp info_ptr, double red_X, #endif /* cHRM */ +#ifdef PNG_cICP_SUPPORTED +void PNGAPI +png_set_cICP(png_const_structrp png_ptr, png_inforp info_ptr, + png_byte colour_primaries, png_byte transfer_function, + png_byte matrix_coefficients, png_byte video_full_range_flag) +{ + png_debug1(1, "in %s storage function", "cICP"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->cicp_colour_primaries = colour_primaries; + info_ptr->cicp_transfer_function = transfer_function; + info_ptr->cicp_matrix_coefficients = matrix_coefficients; + info_ptr->cicp_video_full_range_flag = video_full_range_flag; + + if (info_ptr->cicp_matrix_coefficients != 0) + { + png_warning(png_ptr, "Invalid cICP matrix coefficients"); + return; + } + + info_ptr->valid |= PNG_INFO_cICP; +} +#endif /* cICP */ + +#ifdef PNG_cLLI_SUPPORTED +void PNGFAPI +png_set_cLLI_fixed(png_const_structrp png_ptr, png_inforp info_ptr, + /* The values below are in cd/m2 (nits) and are scaled by 10,000; not + * 100,000 as in the case of png_fixed_point. + */ + png_uint_32 maxCLL, png_uint_32 maxFALL) +{ + png_debug1(1, "in %s storage function", "cLLI"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* Check the light level range: */ + if (maxCLL > 0x7FFFFFFFU || maxFALL > 0x7FFFFFFFU) + { + /* The limit is 200kcd/m2; somewhat bright but not inconceivable because + * human vision is said to run up to 100Mcd/m2. The sun is about 2Gcd/m2. + * + * The reference sRGB monitor is 80cd/m2 and the limit of PQ encoding is + * 2kcd/m2. + */ + png_chunk_report(png_ptr, "cLLI light level exceeds PNG limit", + PNG_CHUNK_WRITE_ERROR); + return; + } + + info_ptr->maxCLL = maxCLL; + info_ptr->maxFALL = maxFALL; + info_ptr->valid |= PNG_INFO_cLLI; +} + +# ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_cLLI(png_const_structrp png_ptr, png_inforp info_ptr, + double maxCLL, double maxFALL) +{ + png_set_cLLI_fixed(png_ptr, info_ptr, + png_fixed_ITU(png_ptr, maxCLL, "png_set_cLLI(maxCLL)"), + png_fixed_ITU(png_ptr, maxFALL, "png_set_cLLI(maxFALL)")); +} +# endif /* FLOATING_POINT */ +#endif /* cLLI */ + +#ifdef PNG_mDCV_SUPPORTED +static png_uint_16 +png_ITU_fixed_16(int *error, png_fixed_point v) +{ + /* Return a safe uint16_t value scaled according to the ITU H273 rules for + * 16-bit display chromaticities. Functions like the corresponding + * png_fixed() internal function with regard to errors: it's an error on + * write, a chunk_benign_error on read: See the definition of + * png_chunk_report in pngpriv.h. + */ + v /= 2; /* rounds to 0 in C: avoids insignificant arithmetic errors */ + if (v > 65535 || v < 0) + { + *error = 1; + return 0; + } + + return (png_uint_16)/*SAFE*/v; +} + +void PNGAPI +png_set_mDCV_fixed(png_const_structrp png_ptr, png_inforp info_ptr, + png_fixed_point white_x, png_fixed_point white_y, + png_fixed_point red_x, png_fixed_point red_y, + png_fixed_point green_x, png_fixed_point green_y, + png_fixed_point blue_x, png_fixed_point blue_y, + png_uint_32 maxDL, + png_uint_32 minDL) +{ + png_uint_16 rx, ry, gx, gy, bx, by, wx, wy; + int error; + + png_debug1(1, "in %s storage function", "mDCV"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* Check the input values to ensure they are in the expected range: */ + error = 0; + rx = png_ITU_fixed_16(&error, red_x); + ry = png_ITU_fixed_16(&error, red_y); + gx = png_ITU_fixed_16(&error, green_x); + gy = png_ITU_fixed_16(&error, green_y); + bx = png_ITU_fixed_16(&error, blue_x); + by = png_ITU_fixed_16(&error, blue_y); + wx = png_ITU_fixed_16(&error, white_x); + wy = png_ITU_fixed_16(&error, white_y); + + if (error) + { + png_chunk_report(png_ptr, + "mDCV chromaticities outside representable range", + PNG_CHUNK_WRITE_ERROR); + return; + } + + /* Check the light level range: */ + if (maxDL > 0x7FFFFFFFU || minDL > 0x7FFFFFFFU) + { + /* The limit is 200kcd/m2; somewhat bright but not inconceivable because + * human vision is said to run up to 100Mcd/m2. The sun is about 2Gcd/m2. + * + * The reference sRGB monitor is 80cd/m2 and the limit of PQ encoding is + * 2kcd/m2. + */ + png_chunk_report(png_ptr, "mDCV display light level exceeds PNG limit", + PNG_CHUNK_WRITE_ERROR); + return; + } + + /* All values are safe, the settings are accepted. + * + * IMPLEMENTATION NOTE: in practice the values can be checked and assigned + * but the result is confusing if a writing app calls png_set_mDCV more than + * once, the second time with an invalid value. This approach is more + * obviously correct at the cost of typing and a very slight machine + * overhead. + */ + info_ptr->mastering_red_x = rx; + info_ptr->mastering_red_y = ry; + info_ptr->mastering_green_x = gx; + info_ptr->mastering_green_y = gy; + info_ptr->mastering_blue_x = bx; + info_ptr->mastering_blue_y = by; + info_ptr->mastering_white_x = wx; + info_ptr->mastering_white_y = wy; + info_ptr->mastering_maxDL = maxDL; + info_ptr->mastering_minDL = minDL; + info_ptr->valid |= PNG_INFO_mDCV; +} + +# ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_mDCV(png_const_structrp png_ptr, png_inforp info_ptr, + double white_x, double white_y, double red_x, double red_y, double green_x, + double green_y, double blue_x, double blue_y, + double maxDL, double minDL) +{ + png_set_mDCV_fixed(png_ptr, info_ptr, + png_fixed(png_ptr, white_x, "png_set_mDCV(white(x))"), + png_fixed(png_ptr, white_y, "png_set_mDCV(white(y))"), + png_fixed(png_ptr, red_x, "png_set_mDCV(red(x))"), + png_fixed(png_ptr, red_y, "png_set_mDCV(red(y))"), + png_fixed(png_ptr, green_x, "png_set_mDCV(green(x))"), + png_fixed(png_ptr, green_y, "png_set_mDCV(green(y))"), + png_fixed(png_ptr, blue_x, "png_set_mDCV(blue(x))"), + png_fixed(png_ptr, blue_y, "png_set_mDCV(blue(y))"), + png_fixed_ITU(png_ptr, maxDL, "png_set_mDCV(maxDL)"), + png_fixed_ITU(png_ptr, minDL, "png_set_mDCV(minDL)")); +} +# endif /* FLOATING_POINT */ +#endif /* mDCV */ + #ifdef PNG_eXIf_SUPPORTED void PNGAPI png_set_eXIf(png_const_structrp png_ptr, png_inforp info_ptr, @@ -213,8 +394,8 @@ png_set_gAMA_fixed(png_const_structrp png_ptr, png_inforp info_ptr, if (png_ptr == NULL || info_ptr == NULL) return; - png_colorspace_set_gamma(png_ptr, &info_ptr->colorspace, file_gamma); - png_colorspace_sync_info(png_ptr, info_ptr); + info_ptr->gamma = file_gamma; + info_ptr->valid |= PNG_INFO_gAMA; } # ifdef PNG_FLOATING_POINT_SUPPORTED @@ -673,8 +854,8 @@ png_set_sRGB(png_const_structrp png_ptr, png_inforp info_ptr, int srgb_intent) if (png_ptr == NULL || info_ptr == NULL) return; - (void)png_colorspace_set_sRGB(png_ptr, &info_ptr->colorspace, srgb_intent); - png_colorspace_sync_info(png_ptr, info_ptr); + info_ptr->rendering_intent = srgb_intent; + info_ptr->valid |= PNG_INFO_sRGB; } void PNGAPI @@ -686,15 +867,20 @@ png_set_sRGB_gAMA_and_cHRM(png_const_structrp png_ptr, png_inforp info_ptr, if (png_ptr == NULL || info_ptr == NULL) return; - if (png_colorspace_set_sRGB(png_ptr, &info_ptr->colorspace, - srgb_intent) != 0) - { - /* This causes the gAMA and cHRM to be written too */ - info_ptr->colorspace.flags |= - PNG_COLORSPACE_FROM_gAMA|PNG_COLORSPACE_FROM_cHRM; - } + png_set_sRGB(png_ptr, info_ptr, srgb_intent); + +# ifdef PNG_gAMA_SUPPORTED + png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_sRGB_INVERSE); +# endif /* gAMA */ - png_colorspace_sync_info(png_ptr, info_ptr); +# ifdef PNG_cHRM_SUPPORTED + png_set_cHRM_fixed(png_ptr, info_ptr, + /* color x y */ + /* white */ 31270, 32900, + /* red */ 64000, 33000, + /* green */ 30000, 60000, + /* blue */ 15000, 6000); +# endif /* cHRM */ } #endif /* sRGB */ @@ -717,27 +903,6 @@ png_set_iCCP(png_const_structrp png_ptr, png_inforp info_ptr, if (compression_type != PNG_COMPRESSION_TYPE_BASE) png_app_error(png_ptr, "Invalid iCCP compression method"); - /* Set the colorspace first because this validates the profile; do not - * override previously set app cHRM or gAMA here (because likely as not the - * application knows better than libpng what the correct values are.) Pass - * the info_ptr color_type field to png_colorspace_set_ICC because in the - * write case it has not yet been stored in png_ptr. - */ - { - int result = png_colorspace_set_ICC(png_ptr, &info_ptr->colorspace, name, - proflen, profile, info_ptr->color_type); - - png_colorspace_sync_info(png_ptr, info_ptr); - - /* Don't do any of the copying if the profile was bad, or inconsistent. */ - if (result == 0) - return; - - /* But do write the gAMA and cHRM chunks from the profile. */ - info_ptr->colorspace.flags |= - PNG_COLORSPACE_FROM_gAMA|PNG_COLORSPACE_FROM_cHRM; - } - length = strlen(name)+1; new_iccp_name = png_voidcast(png_charp, png_malloc_warn(png_ptr, length)); @@ -1423,11 +1588,14 @@ png_set_keep_unknown_chunks(png_structrp png_ptr, int keep, static const png_byte chunks_to_ignore[] = { 98, 75, 71, 68, '\0', /* bKGD */ 99, 72, 82, 77, '\0', /* cHRM */ + 99, 73, 67, 80, '\0', /* cICP */ + 99, 76, 76, 73, '\0', /* cLLI */ 101, 88, 73, 102, '\0', /* eXIf */ 103, 65, 77, 65, '\0', /* gAMA */ 104, 73, 83, 84, '\0', /* hIST */ 105, 67, 67, 80, '\0', /* iCCP */ 105, 84, 88, 116, '\0', /* iTXt */ + 109, 68, 67, 86, '\0', /* mDCV */ 111, 70, 70, 115, '\0', /* oFFs */ 112, 67, 65, 76, '\0', /* pCAL */ 112, 72, 89, 115, '\0', /* pHYs */ @@ -1689,8 +1857,24 @@ png_set_chunk_malloc_max(png_structrp png_ptr, { png_debug(1, "in png_set_chunk_malloc_max"); + /* pngstruct::user_chunk_malloc_max is initialized to a non-zero value in + * png.c. This API supports '0' for unlimited, make sure the correct + * (unlimited) value is set here to avoid a need to check for 0 everywhere + * the parameter is used. + */ if (png_ptr != NULL) - png_ptr->user_chunk_malloc_max = user_chunk_malloc_max; + { + if (user_chunk_malloc_max == 0U) /* unlimited */ + { +# ifdef PNG_MAX_MALLOC_64K + png_ptr->user_chunk_malloc_max = 65536U; +# else + png_ptr->user_chunk_malloc_max = PNG_SIZE_MAX; +# endif + } + else + png_ptr->user_chunk_malloc_max = user_chunk_malloc_max; + } } #endif /* ?SET_USER_LIMITS */ diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngstruct.h b/src/java.desktop/share/native/libsplashscreen/libpng/pngstruct.h index f153bdec6020c..8edb4bc393a64 100644 --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngstruct.h +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngstruct.h @@ -22,14 +22,14 @@ * questions. */ -/* pngstruct.h - header file for PNG reference library +/* pngstruct.h - internal structures for libpng * * This file is available under and governed by the GNU General Public * License version 2 only, as published by the Free Software Foundation. * However, the following notice accompanied the original version of this * file and, per its terms, should not be removed: * - * Copyright (c) 2018-2022 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -39,11 +39,9 @@ * and license in png.h */ -/* The structure that holds the information to read and write PNG files. - * The only people who need to care about what is inside of this are the - * people who will be modifying the library for their own special needs. - * It should NOT be accessed directly by an application. - */ +#ifndef PNGPRIV_H +# error This file must not be included by applications; please include +#endif #ifndef PNGSTRUCT_H #define PNGSTRUCT_H @@ -98,13 +96,7 @@ typedef struct png_compression_buffer /* Colorspace support; structures used in png_struct, png_info and in internal * functions to hold and communicate information about the color space. - * - * PNG_COLORSPACE_SUPPORTED is only required if the application will perform - * colorspace corrections, otherwise all the colorspace information can be - * skipped and the size of libpng can be reduced (significantly) by compiling - * out the colorspace support. */ -#ifdef PNG_COLORSPACE_SUPPORTED /* The chromaticities of the red, green and blue colorants and the chromaticity * of the corresponding white point (i.e. of rgb(1.0,1.0,1.0)). */ @@ -125,48 +117,36 @@ typedef struct png_XYZ png_fixed_point green_X, green_Y, green_Z; png_fixed_point blue_X, blue_Y, blue_Z; } png_XYZ; -#endif /* COLORSPACE */ -#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED) -/* A colorspace is all the above plus, potentially, profile information; - * however at present libpng does not use the profile internally so it is only - * stored in the png_info struct (if iCCP is supported.) The rendering intent - * is retained here and is checked. - * - * The file gamma encoding information is also stored here and gamma correction - * is done by libpng, whereas color correction must currently be done by the - * application. +/* Chunk index values as an enum, PNG_INDEX_unknown is also a count of the + * number of chunks. */ -typedef struct png_colorspace +#define PNG_CHUNK(cHNK, i) PNG_INDEX_ ## cHNK = (i), +typedef enum { -#ifdef PNG_GAMMA_SUPPORTED - png_fixed_point gamma; /* File gamma */ -#endif - -#ifdef PNG_COLORSPACE_SUPPORTED - png_xy end_points_xy; /* End points as chromaticities */ - png_XYZ end_points_XYZ; /* End points as CIE XYZ colorant values */ - png_uint_16 rendering_intent; /* Rendering intent of a profile */ -#endif + PNG_KNOWN_CHUNKS + PNG_INDEX_unknown +} png_index; +#undef PNG_CHUNK - /* Flags are always defined to simplify the code. */ - png_uint_16 flags; /* As defined below */ -} png_colorspace, * PNG_RESTRICT png_colorspacerp; +/* Chunk flag values. These are (png_uint_32 values) with exactly one bit set + * and can be combined into a flag set with bitwise 'or'. + * + * TODO: C23: convert these macros to C23 inlines (which are static). + */ +#define png_chunk_flag_from_index(i) (0x80000000U >> (31 - (i))) + /* The flag coresponding to the given png_index enum value. This is defined + * for png_unknown as well (until it reaches the value 32) but this should + * not be relied on. + */ -typedef const png_colorspace * PNG_RESTRICT png_const_colorspacerp; +#define png_file_has_chunk(png_ptr, i)\ + (((png_ptr)->chunks & png_chunk_flag_from_index(i)) != 0) + /* The chunk has been recorded in png_struct */ -/* General flags for the 'flags' field */ -#define PNG_COLORSPACE_HAVE_GAMMA 0x0001 -#define PNG_COLORSPACE_HAVE_ENDPOINTS 0x0002 -#define PNG_COLORSPACE_HAVE_INTENT 0x0004 -#define PNG_COLORSPACE_FROM_gAMA 0x0008 -#define PNG_COLORSPACE_FROM_cHRM 0x0010 -#define PNG_COLORSPACE_FROM_sRGB 0x0020 -#define PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB 0x0040 -#define PNG_COLORSPACE_MATCHES_sRGB 0x0080 /* exact match on profile */ -#define PNG_COLORSPACE_INVALID 0x8000 -#define PNG_COLORSPACE_CANCEL(flags) (0xffff ^ (flags)) -#endif /* COLORSPACE || GAMMA */ +#define png_file_add_chunk(pnt_ptr, i)\ + ((void)((png_ptr)->chunks |= png_chunk_flag_from_index(i))) + /* Record the chunk in the png_struct */ struct png_struct_def { @@ -238,6 +218,11 @@ struct png_struct_def int zlib_set_strategy; #endif + png_uint_32 chunks; /* PNG_CF_ for every chunk read or (NYI) written */ +# define png_has_chunk(png_ptr, cHNK)\ + png_file_has_chunk(png_ptr, PNG_INDEX_ ## cHNK) + /* Convenience accessor - use this to check for a known chunk by name */ + png_uint_32 width; /* width of image in pixels */ png_uint_32 height; /* height of image in pixels */ png_uint_32 num_rows; /* number of rows in current pass */ @@ -314,9 +299,16 @@ struct png_struct_def png_uint_32 flush_rows; /* number of rows written since last flush */ #endif +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + png_xy chromaticities; /* From mDVC, cICP, [iCCP], sRGB or cHRM */ +#endif + #ifdef PNG_READ_GAMMA_SUPPORTED int gamma_shift; /* number of "insignificant" bits in 16-bit gamma */ - png_fixed_point screen_gamma; /* screen gamma value (display_exponent) */ + png_fixed_point screen_gamma; /* screen gamma value (display exponent) */ + png_fixed_point file_gamma; /* file gamma value (encoding exponent) */ + png_fixed_point chunk_gamma; /* from cICP, iCCP, sRGB or gAMA */ + png_fixed_point default_gamma;/* from png_set_alpha_mode */ png_bytep gamma_table; /* gamma table for 8-bit depth files */ png_uint_16pp gamma_16_table; /* gamma table for 16-bit depth files */ @@ -328,7 +320,7 @@ struct png_struct_def png_uint_16pp gamma_16_from_1; /* converts from 1.0 to screen */ png_uint_16pp gamma_16_to_1; /* converts from file to 1.0 */ #endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ -#endif +#endif /* READ_GAMMA */ #if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED) png_color_8 sig_bit; /* significant bits in each available channel */ @@ -378,8 +370,8 @@ struct png_struct_def /* To do: remove this from libpng-1.7 */ #ifdef PNG_TIME_RFC1123_SUPPORTED char time_buffer[29]; /* String to hold RFC 1123 time text */ -#endif -#endif +#endif /* TIME_RFC1123 */ +#endif /* LIBPNG_VER < 10700 */ /* New members added in libpng-1.0.6 */ @@ -389,8 +381,8 @@ struct png_struct_def png_voidp user_chunk_ptr; #ifdef PNG_READ_USER_CHUNKS_SUPPORTED png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */ -#endif -#endif +#endif /* READ_USER_CHUNKS */ +#endif /* USER_CHUNKS */ #ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED int unknown_default; /* As PNG_HANDLE_* */ @@ -412,7 +404,8 @@ struct png_struct_def /* New member added in libpng-1.6.36 */ #if defined(PNG_READ_EXPAND_SUPPORTED) && \ - defined(PNG_ARM_NEON_IMPLEMENTATION) + (defined(PNG_ARM_NEON_IMPLEMENTATION) || \ + defined(PNG_RISCV_RVV_IMPLEMENTATION)) png_bytep riffled_palette; /* buffer for accelerated palette expansion */ #endif @@ -441,7 +434,6 @@ struct png_struct_def #ifdef PNG_READ_QUANTIZE_SUPPORTED /* The following three members were added at version 1.0.14 and 1.2.4 */ - png_bytep quantize_sort; /* working sort array */ png_bytep index_to_palette; /* where the original index currently is in the palette */ png_bytep palette_to_index; /* which original index points to this @@ -497,11 +489,5 @@ struct png_struct_def /* New member added in libpng-1.5.7 */ void (*read_filter[PNG_FILTER_VALUE_LAST-1])(png_row_infop row_info, png_bytep row, png_const_bytep prev_row); - -#ifdef PNG_READ_SUPPORTED -#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED) - png_colorspace colorspace; -#endif -#endif }; #endif /* PNGSTRUCT_H */ diff --git a/src/java.desktop/share/native/libsplashscreen/splashscreen_gif.c b/src/java.desktop/share/native/libsplashscreen/splashscreen_gif.c index 3654c677493fa..cbdad61f78e83 100644 --- a/src/java.desktop/share/native/libsplashscreen/splashscreen_gif.c +++ b/src/java.desktop/share/native/libsplashscreen/splashscreen_gif.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -48,7 +48,7 @@ // restore the area overwritten by the graphic with // what was there prior to rendering the graphic. -static const char szNetscape20ext[11] = "NETSCAPE2.0"; +static const char szNetscape20ext[] = "NETSCAPE2.0"; #define NSEXT_LOOP 0x01 // Loop Count field code @@ -181,7 +181,7 @@ SplashDecodeGif(Splash * splash, GifFileType * gif) } case APPLICATION_EXT_FUNC_CODE: { - if (size == sizeof(szNetscape20ext) + if (size == strlen(szNetscape20ext) && memcmp(pExtension, szNetscape20ext, size) == 0) { int iSubCode; diff --git a/src/java.desktop/unix/classes/sun/awt/PlatformGraphicsInfo.java b/src/java.desktop/unix/classes/sun/awt/PlatformGraphicsInfo.java index c5f13770e4c86..d7bf467975f41 100644 --- a/src/java.desktop/unix/classes/sun/awt/PlatformGraphicsInfo.java +++ b/src/java.desktop/unix/classes/sun/awt/PlatformGraphicsInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,7 @@ package sun.awt; +import java.io.File; import java.awt.GraphicsEnvironment; import java.awt.Toolkit; import java.security.AccessController; @@ -43,17 +44,42 @@ public static Toolkit createToolkit() { /** * Called from java.awt.GraphicsEnvironment when * to check if on this platform, the JDK should default to - * headless mode, in the case the application did specify + * headless mode, in the case the application did not specify * a value for the java.awt.headless system property. */ @SuppressWarnings("removal") public static boolean getDefaultHeadlessProperty() { - return + boolean noDisplay = AccessController.doPrivileged((PrivilegedAction) () -> { final String display = System.getenv("DISPLAY"); - return display == null || display.trim().isEmpty(); + return display == null || display.trim().isEmpty(); + }); + if (noDisplay) { + return true; + } + /* + * If we positively identify a separate headless library support being present + * but no corresponding headful library, then we can support headless but + * not headful, so report that back to the caller. + * This does require duplication of knowing the name of the libraries + * also in libawt's OnLoad() but we need to make sure that the Java + * code is also set up as headless from the start - it is not so easy + * to try headful and then unwind that and then retry as headless. + */ + boolean headless = + AccessController.doPrivileged((PrivilegedAction) () -> { + String[] libDirs = System.getProperty("sun.boot.library.path", "").split(":"); + for (String libDir : libDirs) { + File headlessLib = new File(libDir, "libawt_headless.so"); + File xawtLib = new File(libDir, "libawt_xawt.so"); + if (headlessLib.exists() && !xawtLib.exists()) { + return true; + } + } + return false; }); + return headless; } /** @@ -63,7 +89,10 @@ public static boolean getDefaultHeadlessProperty() { */ public static String getDefaultHeadlessMessage() { return - "\nNo X11 DISPLAY variable was set,\n" + - "but this program performed an operation which requires it."; + """ + + No X11 DISPLAY variable was set, + or no headful library support was found, + but this program performed an operation which requires it."""; } } diff --git a/src/java.desktop/unix/classes/sun/awt/UNIXToolkit.java b/src/java.desktop/unix/classes/sun/awt/UNIXToolkit.java index 7446c196a46f3..0bc8d31d8b23e 100644 --- a/src/java.desktop/unix/classes/sun/awt/UNIXToolkit.java +++ b/src/java.desktop/unix/classes/sun/awt/UNIXToolkit.java @@ -50,7 +50,6 @@ import java.awt.image.WritableRaster; import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStreamReader; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Arrays; @@ -276,14 +275,12 @@ public boolean shouldDisableSystemTray() { return result; } - private Integer getGnomeShellMajorVersion() { + public Integer getGnomeShellMajorVersion() { try { Process process = new ProcessBuilder("/usr/bin/gnome-shell", "--version") .start(); - try (InputStreamReader isr = new InputStreamReader(process.getInputStream()); - BufferedReader reader = new BufferedReader(isr)) { - + try (BufferedReader reader = process.inputReader()) { if (process.waitFor(2, SECONDS) && process.exitValue() == 0) { String line = reader.readLine(); if (line != null) { diff --git a/src/java.desktop/unix/classes/sun/awt/X11/XCanvasPeer.java b/src/java.desktop/unix/classes/sun/awt/X11/XCanvasPeer.java index c7725bfb4046e..2045288bce45f 100644 --- a/src/java.desktop/unix/classes/sun/awt/X11/XCanvasPeer.java +++ b/src/java.desktop/unix/classes/sun/awt/X11/XCanvasPeer.java @@ -62,36 +62,33 @@ public GraphicsConfiguration getAppropriateGraphicsConfiguration( if (graphicsConfig == null || gc == null) { return gc; } - // Opt: Only need to do if we're not using the default GC - int screenNum = ((X11GraphicsDevice)gc.getDevice()).getScreen(); + final X11GraphicsDevice newDev = getSameScreenDevice(gc); + final int visualToLookFor = graphicsConfig.getVisual(); - X11GraphicsConfig parentgc; - // save vis id of current gc - int visual = graphicsConfig.getVisual(); - - X11GraphicsDevice newDev = (X11GraphicsDevice) GraphicsEnvironment. - getLocalGraphicsEnvironment(). - getScreenDevices()[screenNum]; - - for (int i = 0; i < newDev.getNumConfigs(screenNum); i++) { - if (visual == newDev.getConfigVisualId(i, screenNum)) { - // use that - graphicsConfig = (X11GraphicsConfig)newDev.getConfigurations()[i]; - break; + final GraphicsConfiguration[] configurations = newDev.getConfigurations(); + for (final GraphicsConfiguration config : configurations) { + final X11GraphicsConfig x11gc = (X11GraphicsConfig) config; + if (visualToLookFor == x11gc.getVisual()) { + graphicsConfig = x11gc; } } - // just in case... - if (graphicsConfig == null) { - graphicsConfig = (X11GraphicsConfig) GraphicsEnvironment. - getLocalGraphicsEnvironment(). - getScreenDevices()[screenNum]. - getDefaultConfiguration(); - } return graphicsConfig; } + private X11GraphicsDevice getSameScreenDevice(GraphicsConfiguration gc) { + XToolkit.awtLock(); // so that the number of screens doesn't change during + try { + final int screenNum = ((X11GraphicsDevice) gc.getDevice()).getScreen(); + return (X11GraphicsDevice) GraphicsEnvironment. + getLocalGraphicsEnvironment(). + getScreenDevices()[screenNum]; + } finally { + XToolkit.awtUnlock(); + } + } + protected boolean shouldFocusOnClick() { // Canvas should always be able to be focused by mouse clicks. return true; diff --git a/src/java.desktop/unix/classes/sun/awt/X11/XRobotPeer.java b/src/java.desktop/unix/classes/sun/awt/X11/XRobotPeer.java index 6d155c0bcc8b7..db3146bcbbd50 100644 --- a/src/java.desktop/unix/classes/sun/awt/X11/XRobotPeer.java +++ b/src/java.desktop/unix/classes/sun/awt/X11/XRobotPeer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,15 +36,13 @@ import sun.awt.X11GraphicsConfig; import sun.awt.X11GraphicsDevice; import sun.awt.screencast.ScreencastHelper; +import sun.awt.screencast.XdgDesktopPortal; import sun.security.action.GetPropertyAction; @SuppressWarnings("removal") final class XRobotPeer implements RobotPeer { private static final boolean tryGtk; - private static final String screenshotMethod; - private static final String METHOD_X11 = "x11"; - private static final String METHOD_SCREENCAST = "dbusScreencast"; static { loadNativeLibraries(); @@ -54,20 +52,6 @@ final class XRobotPeer implements RobotPeer { new GetPropertyAction("awt.robot.gtk", "true") )); - - boolean isOnWayland = false; - - if (Toolkit.getDefaultToolkit() instanceof SunToolkit sunToolkit) { - isOnWayland = sunToolkit.isRunningOnWayland(); - } - - screenshotMethod = AccessController.doPrivileged( - new GetPropertyAction( - "awt.robot.screenshotMethod", - isOnWayland - ? METHOD_SCREENCAST - : METHOD_X11 - )); } private static volatile boolean useGtk; @@ -92,39 +76,63 @@ final class XRobotPeer implements RobotPeer { @Override public void mouseMove(int x, int y) { mouseMoveImpl(xgc, xgc.scaleUp(x), xgc.scaleUp(y)); + if (XdgDesktopPortal.isRemoteDesktop() && ScreencastHelper.isAvailable()) { + // We still call mouseMoveImpl on purpose to change the mouse position + // within the XWayland server so that we can retrieve it later. + ScreencastHelper.remoteDesktopMouseMove(xgc.scaleUp(x), xgc.scaleUp(y)); + } } @Override public void mousePress(int buttons) { - mousePressImpl(buttons); + if (XdgDesktopPortal.isRemoteDesktop() && ScreencastHelper.isAvailable()) { + ScreencastHelper.remoteDesktopMouseButton(true, buttons); + } else { + mousePressImpl(buttons); + } } @Override public void mouseRelease(int buttons) { - mouseReleaseImpl(buttons); + if (XdgDesktopPortal.isRemoteDesktop() && ScreencastHelper.isAvailable()) { + ScreencastHelper.remoteDesktopMouseButton(false, buttons); + } else { + mouseReleaseImpl(buttons); + } } @Override public void mouseWheel(int wheelAmt) { - mouseWheelImpl(wheelAmt); + if (XdgDesktopPortal.isRemoteDesktop() && ScreencastHelper.isAvailable()) { + ScreencastHelper.remoteDesktopMouseWheel(wheelAmt); + } else { + mouseWheelImpl(wheelAmt); + } } @Override public void keyPress(int keycode) { - keyPressImpl(keycode); + if (XdgDesktopPortal.isRemoteDesktop() && ScreencastHelper.isAvailable()) { + ScreencastHelper.remoteDesktopKey(true, keycode); + } else { + keyPressImpl(keycode); + } } @Override public void keyRelease(int keycode) { - keyReleaseImpl(keycode); + if (XdgDesktopPortal.isRemoteDesktop() && ScreencastHelper.isAvailable()) { + ScreencastHelper.remoteDesktopKey(false, keycode); + } else { + keyReleaseImpl(keycode); + } } @Override public int getRGBPixel(int x, int y) { int[] pixelArray = new int[1]; - if (screenshotMethod.equals(METHOD_SCREENCAST) - && ScreencastHelper.isAvailable()) { - + if ((XdgDesktopPortal.isScreencast() + || XdgDesktopPortal.isRemoteDesktop()) && ScreencastHelper.isAvailable()) { ScreencastHelper.getRGBPixels(x, y, 1, 1, pixelArray); } else { getRGBPixelsImpl(xgc, x, y, 1, 1, pixelArray, useGtk); @@ -135,8 +143,8 @@ public int getRGBPixel(int x, int y) { @Override public int[] getRGBPixels(Rectangle bounds) { int[] pixelArray = new int[bounds.width * bounds.height]; - if (screenshotMethod.equals(METHOD_SCREENCAST) - && ScreencastHelper.isAvailable()) { + if ((XdgDesktopPortal.isScreencast() + || XdgDesktopPortal.isRemoteDesktop()) && ScreencastHelper.isAvailable()) { ScreencastHelper.getRGBPixels(bounds.x, bounds.y, bounds.width, bounds.height, diff --git a/src/java.desktop/unix/classes/sun/awt/X11/XToolkit.java b/src/java.desktop/unix/classes/sun/awt/X11/XToolkit.java index 8d5586741f277..2f0330bbd3ad4 100644 --- a/src/java.desktop/unix/classes/sun/awt/X11/XToolkit.java +++ b/src/java.desktop/unix/classes/sun/awt/X11/XToolkit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -116,6 +116,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.Hashtable; import java.util.Iterator; import java.util.LinkedList; import java.util.Map; @@ -141,6 +142,8 @@ import sun.awt.X11GraphicsEnvironment; import sun.awt.XSettings; import sun.awt.datatransfer.DataTransferer; +import sun.awt.screencast.ScreencastHelper; +import sun.awt.screencast.XdgDesktopPortal; import sun.awt.util.PerformanceLogger; import sun.awt.util.ThreadGroupUtils; import sun.font.FontConfigManager; @@ -358,6 +361,11 @@ public void dispatchEvent(XEvent ev) { } finally { awtLock(); } + } else { + final XAtom XA_NET_WORKAREA = XAtom.get("_NET_WORKAREA"); + final boolean rootWindowWorkareaResized = (ev.get_type() == XConstants.PropertyNotify + && ev.get_xproperty().get_atom() == XA_NET_WORKAREA.getAtom()); + if (rootWindowWorkareaResized) resetScreenInsetsCache(); } } }); @@ -848,8 +856,7 @@ private static Rectangle getWorkArea(long root, int scale) * When two screens overlap and the first contains a dock(*****), then * _NET_WORKAREA may start at point x1,y1 and end at point x2,y2. */ - @Override - public Insets getScreenInsets(final GraphicsConfiguration gc) { + private Insets getScreenInsetsImpl(final GraphicsConfiguration gc) { GraphicsDevice gd = gc.getDevice(); XNETProtocol np = XWM.getWM().getNETProtocol(); if (np == null || !(gd instanceof X11GraphicsDevice) || !np.active()) { @@ -877,6 +884,34 @@ public Insets getScreenInsets(final GraphicsConfiguration gc) { } } + private void resetScreenInsetsCache() { + final GraphicsDevice[] devices = ((X11GraphicsEnvironment)GraphicsEnvironment. + getLocalGraphicsEnvironment()).getScreenDevices(); + for (var gd : devices) { + ((X11GraphicsDevice)gd).resetInsets(); + } + } + + @Override + public Insets getScreenInsets(final GraphicsConfiguration gc) { + final GraphicsDevice gd = gc.getDevice(); + if (gd instanceof X11GraphicsDevice x11Device) { + Insets insets = x11Device.getInsets(); + if (insets == null) { + synchronized (x11Device) { + insets = x11Device.getInsets(); + if (insets == null) { + insets = getScreenInsetsImpl(gc); + x11Device.setInsets(insets); + } + } + } + return (Insets) insets.clone(); + } else { + return super.getScreenInsets(gc); + } + } + /* * The current implementation of disabling background erasing for * canvases is that we don't set any native background color @@ -1539,16 +1574,21 @@ public int getNumberOfButtons(){ awtLock(); try { if (numberOfButtons == 0) { - numberOfButtons = getNumberOfButtonsImpl(); - numberOfButtons = (numberOfButtons > MAX_BUTTONS_SUPPORTED)? MAX_BUTTONS_SUPPORTED : numberOfButtons; - //4th and 5th buttons are for wheel and shouldn't be reported as buttons. - //If we have more than 3 physical buttons and a wheel, we report N-2 buttons. - //If we have 3 physical buttons and a wheel, we report 3 buttons. - //If we have 1,2,3 physical buttons, we report it as is i.e. 1,2 or 3 respectively. - if (numberOfButtons >=5) { - numberOfButtons -= 2; - } else if (numberOfButtons == 4 || numberOfButtons ==5){ + if (XdgDesktopPortal.isRemoteDesktop() + && ScreencastHelper.isAvailable()) { numberOfButtons = 3; + } else { + numberOfButtons = getNumberOfButtonsImpl(); + numberOfButtons = (numberOfButtons > MAX_BUTTONS_SUPPORTED) ? MAX_BUTTONS_SUPPORTED : numberOfButtons; + //4th and 5th buttons are for wheel and shouldn't be reported as buttons. + //If we have more than 3 physical buttons and a wheel, we report N-2 buttons. + //If we have 3 physical buttons and a wheel, we report 3 buttons. + //If we have 1,2,3 physical buttons, we report it as is i.e. 1,2 or 3 respectively. + if (numberOfButtons >= 5) { + numberOfButtons -= 2; + } else if (numberOfButtons == 4 || numberOfButtons == 5) { + numberOfButtons = 3; + } } } //Assume don't have to re-query the number again and again. diff --git a/src/java.desktop/unix/classes/sun/awt/X11GraphicsDevice.java b/src/java.desktop/unix/classes/sun/awt/X11GraphicsDevice.java index 736a0d468f86b..0a1bc24599f89 100644 --- a/src/java.desktop/unix/classes/sun/awt/X11GraphicsDevice.java +++ b/src/java.desktop/unix/classes/sun/awt/X11GraphicsDevice.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,7 @@ import java.awt.GraphicsConfiguration; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; +import java.awt.Insets; import java.awt.Rectangle; import java.awt.Window; import java.security.AccessController; @@ -37,10 +38,12 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.Objects; import sun.awt.util.ThreadGroupUtils; import sun.java2d.SunGraphicsEnvironment; import sun.java2d.loops.SurfaceType; +import sun.awt.X11.XToolkit; import sun.java2d.opengl.GLXGraphicsConfig; import sun.java2d.pipe.Region; import sun.java2d.xr.XRGraphicsConfig; @@ -63,15 +66,26 @@ public final class X11GraphicsDevice extends GraphicsDevice private static AWTPermission fullScreenExclusivePermission; private static Boolean xrandrExtSupported; - private final Object configLock = new Object(); private SunDisplayChanger topLevels = new SunDisplayChanger(); private DisplayMode origDisplayMode; + private volatile Rectangle bounds; + private volatile Insets insets; private boolean shutdownHookRegistered; private int scale; + // Wayland clients are by design not allowed to change the resolution in Wayland. + // XRandR in Xwayland is just an emulation, it doesn't actually change the resolution. + // This emulation is per window/x11 client, so different clients can have + // different emulated resolutions at the same time. + // So any request to get the current display mode will always return + // the original screen resolution, even if we are in emulated resolution. + // To handle this situation, we store the last set display mode in this variable. + private volatile DisplayMode xwlCurrentDisplayMode; + public X11GraphicsDevice(int screennum) { this.screen = screennum; this.scale = initScaleFactor(); + this.bounds = getBoundsImpl(); } /** @@ -118,8 +132,22 @@ public int scaleDown(int x) { return Region.clipRound(x / (double)getScaleFactor()); } - public Rectangle getBounds() { + private Rectangle getBoundsImpl() { Rectangle rect = pGetBounds(getScreen()); + + if (XToolkit.isOnWayland() && xwlCurrentDisplayMode != null) { + // XRandR resolution change in Xwayland is an emulation, + // and implemented in such a way that multiple display modes + // for a device are only available in a single screen scenario, + // if we have multiple screens they will each have a single display mode + // (no emulated resolution change is available). + // So we don't have to worry about x and y for a screen here. + rect.setSize( + xwlCurrentDisplayMode.getWidth(), + xwlCurrentDisplayMode.getHeight() + ); + } + if (getScaleFactor() != 1) { rect.x = scaleDown(rect.x); rect.y = scaleDown(rect.y); @@ -129,6 +157,23 @@ public Rectangle getBounds() { return rect; } + public Rectangle getBounds() { + return bounds.getBounds(); + } + + public Insets getInsets() { + return insets; + } + + public void setInsets(Insets newInsets) { + Objects.requireNonNull(newInsets); + insets = newInsets; + } + + public void resetInsets() { + insets = null; + } + /** * Returns the identification string associated with this graphics * device. @@ -150,8 +195,11 @@ public String getIDstring() { @Override public GraphicsConfiguration[] getConfigurations() { if (configs == null) { - synchronized (configLock) { + XToolkit.awtLock(); + try { makeConfigurations(); + } finally { + XToolkit.awtUnlock(); } } return configs.clone(); @@ -238,8 +286,11 @@ private void addDoubleBufferVisual(int visNum) { @Override public GraphicsConfiguration getDefaultConfiguration() { if (defaultConfig == null) { - synchronized (configLock) { + XToolkit.awtLock(); + try { makeDefaultConfiguration(); + } finally { + XToolkit.awtUnlock(); } } return defaultConfig; @@ -288,7 +339,7 @@ depth, getConfigColormap(0, screen), private static native void enterFullScreenExclusive(long window); private static native void exitFullScreenExclusive(long window); - private static native boolean initXrandrExtension(); + private static native boolean initXrandrExtension(boolean useOldConfigDisplayMode); private static native DisplayMode getCurrentDisplayMode(int screen); private static native void enumDisplayModes(int screen, ArrayList modes); @@ -305,10 +356,11 @@ private static native void configDisplayMode(int screen, */ private static synchronized boolean isXrandrExtensionSupported() { if (xrandrExtSupported == null) { - xrandrExtSupported = - Boolean.valueOf(initXrandrExtension()); + boolean useOldConfigDisplayMode = + Boolean.getBoolean("awt.x11useOldConfigDisplayMode"); + xrandrExtSupported = initXrandrExtension(useOldConfigDisplayMode); } - return xrandrExtSupported.booleanValue(); + return xrandrExtSupported; } @Override @@ -396,10 +448,19 @@ private DisplayMode getDefaultDisplayMode() { @Override public synchronized DisplayMode getDisplayMode() { if (isFullScreenSupported()) { + if (XToolkit.isOnWayland() && xwlCurrentDisplayMode != null) { + return xwlCurrentDisplayMode; + } + DisplayMode mode = getCurrentDisplayMode(screen); if (mode == null) { mode = getDefaultDisplayMode(); } + + if (XToolkit.isOnWayland()) { + xwlCurrentDisplayMode = mode; + } + return mode; } else { if (origDisplayMode == null) { @@ -475,6 +536,10 @@ public synchronized void setDisplayMode(DisplayMode dm) { dm.getWidth(), dm.getHeight(), dm.getRefreshRate()); + if (XToolkit.isOnWayland()) { + xwlCurrentDisplayMode = dm; + } + // update bounds of the fullscreen window w.setBounds(0, 0, dm.getWidth(), dm.getHeight()); @@ -510,6 +575,8 @@ private synchronized DisplayMode getMatchingDisplayMode(DisplayMode dm) { @Override public synchronized void displayChanged() { scale = initScaleFactor(); + bounds = getBoundsImpl(); + insets = null; // On X11 the visuals do not change, and therefore we don't need // to reset the defaultConfig, config, doubleBufferVisuals, // neither do we need to reset the native data. @@ -571,6 +638,8 @@ public String toString() { } public void invalidate(X11GraphicsDevice device) { + assert XToolkit.isAWTLockHeldByCurrentThread(); + screen = device.screen; } } diff --git a/src/java.desktop/unix/classes/sun/awt/screencast/ScreencastHelper.java b/src/java.desktop/unix/classes/sun/awt/screencast/ScreencastHelper.java index 78661587554e3..ab05247e4da1f 100644 --- a/src/java.desktop/unix/classes/sun/awt/screencast/ScreencastHelper.java +++ b/src/java.desktop/unix/classes/sun/awt/screencast/ScreencastHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,6 +40,7 @@ import java.util.Set; import java.util.Timer; import java.util.TimerTask; +import java.util.function.Function; import java.util.stream.IntStream; /** @@ -54,10 +55,13 @@ public class ScreencastHelper { static final boolean SCREENCAST_DEBUG; private static final boolean IS_NATIVE_LOADED; - private static final int ERROR = -1; private static final int DENIED = -11; private static final int OUT_OF_BOUNDS = -12; + private static final int NO_STREAMS = -13; + + private static final int XDG_METHOD_SCREENCAST = 0; + private static final int XDG_METHOD_REMOTE_DESKTOP = 1; private static final int DELAY_BEFORE_SESSION_CLOSE = 2000; @@ -66,8 +70,7 @@ public class ScreencastHelper { = new Timer("auto-close screencast session", true); - private ScreencastHelper() { - } + private ScreencastHelper() {} static { SCREENCAST_DEBUG = Boolean.parseBoolean( @@ -80,9 +83,16 @@ private ScreencastHelper() { boolean loadFailed = false; + boolean shouldLoadNative = XdgDesktopPortal.isRemoteDesktop() + || XdgDesktopPortal.isScreencast(); + + int methodId = XdgDesktopPortal.isScreencast() + ? XDG_METHOD_SCREENCAST + : XDG_METHOD_REMOTE_DESKTOP; + if (!(Toolkit.getDefaultToolkit() instanceof UNIXToolkit tk && tk.loadGTK()) - || !loadPipewire(SCREENCAST_DEBUG)) { + || !(shouldLoadNative && loadPipewire(methodId, SCREENCAST_DEBUG))) { System.err.println( "Could not load native libraries for ScreencastHelper" @@ -98,7 +108,7 @@ public static boolean isAvailable() { return IS_NATIVE_LOADED; } - private static native boolean loadPipewire(boolean screencastDebug); + private static native boolean loadPipewire(int method, boolean isDebug); private static native int getRGBPixelsImpl( int x, int y, int width, int height, @@ -195,7 +205,7 @@ public static synchronized void getRGBPixels( if (retVal >= 0) { // we have received a screen data return; - } else if (!checkReturnValue(retVal)) { + } else if (!checkReturnValue(retVal, true)) { return; } // else, try other tokens } @@ -209,25 +219,72 @@ public static synchronized void getRGBPixels( null ); - checkReturnValue(retVal); + checkReturnValue(retVal, true); } - private static boolean checkReturnValue(int retVal) { + private static boolean checkReturnValue(int retVal, + boolean throwException) { if (retVal == DENIED) { - // user explicitly denied the capture, no more tries. - throw new SecurityException( - "Screen Capture in the selected area was not allowed" - ); + if (SCREENCAST_DEBUG) { + System.err.println("robot action: access denied by user."); + } + if (throwException) { + // user explicitly denied the capture, no more tries. + throw new SecurityException( + "Screen Capture in the selected area was not allowed" + ); + } } else if (retVal == ERROR) { if (SCREENCAST_DEBUG) { - System.err.println("Screen capture failed."); + System.err.println("robot action: failed."); } } else if (retVal == OUT_OF_BOUNDS) { if (SCREENCAST_DEBUG) { System.err.println( "Token does not provide access to requested area."); } + } else if (retVal == NO_STREAMS) { + if (SCREENCAST_DEBUG) { + System.err.println("robot action: no streams available"); + } } return retVal != ERROR; } + + private static void performWithToken(Function func) { + if (!XdgDesktopPortal.isRemoteDesktop() || !IS_NATIVE_LOADED) return; + + timerCloseSessionRestart(); + + for (TokenItem tokenItem : TokenStorage.getTokens(getSystemScreensBounds())) { + int retVal = func.apply(tokenItem.token); + + if (retVal >= 0 || !checkReturnValue(retVal, false)) { + return; + } + } + + checkReturnValue(func.apply(null), false); + } + + public static synchronized void remoteDesktopMouseMove(int x, int y) { + performWithToken((token) -> remoteDesktopMouseMoveImpl(x, y, token)); + } + + public static synchronized void remoteDesktopMouseButton(boolean isPress, int buttons) { + performWithToken((token) -> remoteDesktopMouseButtonImpl(isPress, buttons, token)); + } + + public static synchronized void remoteDesktopMouseWheel(int wheel) { + performWithToken((token) -> remoteDesktopMouseWheelImpl(wheel, token)); + } + + public static synchronized void remoteDesktopKey(boolean isPress, int key) { + performWithToken((token) -> remoteDesktopKeyImpl(isPress, key, token)); + } + + private static synchronized native int remoteDesktopMouseMoveImpl(int x, int y, String token); + private static synchronized native int remoteDesktopMouseButtonImpl(boolean isPress, int buttons, String token); + private static synchronized native int remoteDesktopMouseWheelImpl(int wheelAmt, String token); + private static synchronized native int remoteDesktopKeyImpl(boolean isPress, int key, String token); } diff --git a/src/java.desktop/unix/classes/sun/awt/screencast/TokenStorage.java b/src/java.desktop/unix/classes/sun/awt/screencast/TokenStorage.java index b05ff7f8c4add..191a66bf6c89a 100644 --- a/src/java.desktop/unix/classes/sun/awt/screencast/TokenStorage.java +++ b/src/java.desktop/unix/classes/sun/awt/screencast/TokenStorage.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -69,6 +69,9 @@ private TokenStorage() {} private static final String REL_NAME_SECONDARY = ".awt/robot/screencast-tokens.properties"; + private static final String REL_RD_NAME = + ".java/robot/remote-desktop-tokens.properties"; + private static final Properties PROPS = new Properties(); private static final Path PROPS_PATH; private static final Path PROP_FILENAME; @@ -107,11 +110,18 @@ private static Path setupPath() { return null; } - Path path = Path.of(userHome, REL_NAME); - Path secondaryPath = Path.of(userHome, REL_NAME_SECONDARY); + Path path; + Path secondaryPath = null; + + if (XdgDesktopPortal.isRemoteDesktop()) { + path = Path.of(userHome, REL_RD_NAME); + } else { + path = Path.of(userHome, REL_NAME); + secondaryPath = Path.of(userHome, REL_NAME_SECONDARY); + } boolean copyFromSecondary = !Files.isWritable(path) - && Files.isWritable(secondaryPath); + && secondaryPath != null && Files.isWritable(secondaryPath); Path workdir = path.getParent(); diff --git a/src/java.desktop/unix/classes/sun/awt/screencast/XdgDesktopPortal.java b/src/java.desktop/unix/classes/sun/awt/screencast/XdgDesktopPortal.java new file mode 100644 index 0000000000000..cf7713745ffbb --- /dev/null +++ b/src/java.desktop/unix/classes/sun/awt/screencast/XdgDesktopPortal.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.awt.screencast; + +import sun.awt.SunToolkit; +import sun.awt.UNIXToolkit; + +import java.awt.Toolkit; +import java.security.AccessController; +import sun.security.action.GetPropertyAction; + +public class XdgDesktopPortal { + private static final String METHOD_X11 = "x11"; + private static final String METHOD_SCREENCAST = "dbusScreencast"; + private static final String METHOD_REMOTE_DESKTOP = "dbusRemoteDesktop"; + + private static final String method; + private static final boolean isRemoteDesktop; + private static final boolean isScreencast; + + private XdgDesktopPortal() {} + + static { + boolean isOnWayland = false; + + if (Toolkit.getDefaultToolkit() instanceof SunToolkit sunToolkit) { + isOnWayland = sunToolkit.isRunningOnWayland(); + } + + String defaultMethod = METHOD_X11; + if (isOnWayland) { + Integer gnomeShellVersion = null; + + UNIXToolkit toolkit = (UNIXToolkit) Toolkit.getDefaultToolkit(); + if ("gnome".equals(toolkit.getDesktop())) { + gnomeShellVersion = toolkit.getGnomeShellMajorVersion(); + } + + defaultMethod = (gnomeShellVersion != null && gnomeShellVersion >= 47) + ? METHOD_REMOTE_DESKTOP + : METHOD_SCREENCAST; + } + + @SuppressWarnings("removal") + String m = AccessController.doPrivileged( + new GetPropertyAction( + "awt.robot.screenshotMethod", defaultMethod + )); + + if (!METHOD_REMOTE_DESKTOP.equals(m) + && !METHOD_SCREENCAST.equals(m) + && !METHOD_X11.equals(m)) { + m = defaultMethod; + } + + isRemoteDesktop = METHOD_REMOTE_DESKTOP.equals(m); + isScreencast = METHOD_SCREENCAST.equals(m); + method = m; + + } + + public static String getMethod() { + return method; + } + + public static boolean isRemoteDesktop() { + return isRemoteDesktop; + } + + public static boolean isScreencast() { + return isScreencast; + } +} diff --git a/src/java.desktop/unix/legal/pipewire.md b/src/java.desktop/unix/legal/pipewire.md index 88389a74e69dd..8275928e27150 100644 --- a/src/java.desktop/unix/legal/pipewire.md +++ b/src/java.desktop/unix/legal/pipewire.md @@ -1,4 +1,4 @@ -## PipeWire 0.3.68 +## PipeWire 1.3.81 ### PipeWire license: @@ -39,3 +39,8 @@ spa/include/spa/utils/string.h ``` Copyright © 2021 Red Hat, Inc. ``` + +spa/utils/cleanup.h: +``` +Copyright © 2023 PipeWire authors +``` diff --git a/src/java.desktop/unix/native/common/awt/awt_GraphicsEnv.h b/src/java.desktop/unix/native/common/awt/awt_GraphicsEnv.h index b7e2a71d41c02..ad4616a56dc5c 100644 --- a/src/java.desktop/unix/native/common/awt/awt_GraphicsEnv.h +++ b/src/java.desktop/unix/native/common/awt/awt_GraphicsEnv.h @@ -64,4 +64,11 @@ struct X11GraphicsConfigIDs { jfieldID bitsPerPixel; }; +#define MAX_DISPLAY_MODES 256 +typedef struct { + unsigned int width; + unsigned int height; + jint refresh; +} DisplayMode; + #endif /* _AWT_GRAPHICSENV_H_ */ diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/awt_GraphicsEnv.c b/src/java.desktop/unix/native/libawt_xawt/awt/awt_GraphicsEnv.c index eb2befffa30ee..d191aa4a6c0b4 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/awt_GraphicsEnv.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/awt_GraphicsEnv.c @@ -55,6 +55,7 @@ #include "gdefs.h" #include #include "Trace.h" +#include int awt_numScreens; /* Xinerama-aware number of screens */ @@ -76,6 +77,8 @@ jmethodID awtNotifyMID = NULL; jmethodID awtNotifyAllMID = NULL; jboolean awtLockInited = JNI_FALSE; +static Bool useNewConfigDisplayMode = True; + /** Convenience macro for loading the lock-related method IDs. */ #define GET_STATIC_METHOD(klass, method_id, method_name, method_sig) \ do { \ @@ -292,7 +295,10 @@ makeDefaultConfig(JNIEnv *env, int screen) { static void getAllConfigs (JNIEnv *env, int screen, AwtScreenDataPtr screenDataPtr) { + // NB: should be invoked only while holding the AWT lock + DASSERT(screen >= 0 && screen < awt_numScreens); + jboolean success = JNI_FALSE; int i; int n8p=0, n12p=0, n8s=0, n8gs=0, n8sg=0, n1sg=0, nTrue=0; int nConfig; @@ -314,8 +320,6 @@ getAllConfigs (JNIEnv *env, int screen, AwtScreenDataPtr screenDataPtr) { xinawareScreen = screen; } - AWT_LOCK (); - viTmp.screen = xinawareScreen; viTmp.depth = 8; @@ -372,7 +376,6 @@ getAllConfigs (JNIEnv *env, int screen, AwtScreenDataPtr screenDataPtr) { if (graphicsConfigs == NULL) { JNU_ThrowOutOfMemoryError((JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2), NULL); - AWT_UNLOCK(); return; } @@ -382,6 +385,9 @@ getAllConfigs (JNIEnv *env, int screen, AwtScreenDataPtr screenDataPtr) { * been reset, so we need to recreate the default config here. */ screenDataPtr->defaultConfig = makeDefaultConfig(env, screen); + if (screenDataPtr->defaultConfig == NULL) { + goto cleanup; + } } defaultConfig = screenDataPtr->defaultConfig; @@ -563,10 +569,17 @@ getAllConfigs (JNIEnv *env, int screen, AwtScreenDataPtr screenDataPtr) { sizeof (XVisualInfo)); } + success = JNI_TRUE; screenDataPtr->numConfigs = nConfig; screenDataPtr->configs = graphicsConfigs; cleanup: + if (success != JNI_TRUE) { + for (i = 0; i < nConfig; i++) { + free(graphicsConfigs[i]); + } + free(graphicsConfigs); + } if (n8p != 0) XFree (pVI8p); if (n12p != 0) @@ -581,7 +594,6 @@ getAllConfigs (JNIEnv *env, int screen, AwtScreenDataPtr screenDataPtr) { XFree (pVI1sg); if (nTrue != 0) XFree (pVITrue); - AWT_UNLOCK (); } /* @@ -770,6 +782,7 @@ JNIEnv *env, jobject this) } static void ensureConfigsInited(JNIEnv* env, int screen) { + // NB: should be invoked only while holding the AWT lock if (x11Screens[screen].numConfigs == 0) { if (env == NULL) { env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); @@ -780,6 +793,8 @@ static void ensureConfigsInited(JNIEnv* env, int screen) { AwtGraphicsConfigDataPtr getDefaultConfig(int screen) { + // NB: should be invoked only while holding the AWT lock + DASSERT(screen >= 0 && screen < awt_numScreens); ensureConfigsInited(NULL, screen); return x11Screens[screen].defaultConfig; } @@ -973,11 +988,11 @@ JNIEXPORT jint JNICALL Java_sun_awt_X11GraphicsDevice_getNumConfigs( JNIEnv *env, jobject this, jint screen) { - AWT_LOCK(); + // NB: should be invoked only while holding the AWT lock + DASSERT(screen >= 0 && screen < awt_numScreens); ensureConfigsInited(env, screen); - int configs = x11Screens[screen].numConfigs; - AWT_UNLOCK(); - return configs; + return x11Screens[screen].numConfigs; + } /* @@ -989,12 +1004,11 @@ JNIEXPORT jint JNICALL Java_sun_awt_X11GraphicsDevice_getConfigVisualId( JNIEnv *env, jobject this, jint index, jint screen) { - int visNum; - AWT_LOCK(); + // NB: should be invoked only while holding the AWT lock + DASSERT(screen >= 0 && screen < awt_numScreens); ensureConfigsInited(env, screen); jint id = (jint) (index == 0 ? x11Screens[screen].defaultConfig : x11Screens[screen].configs[index])->awt_visInfo.visualid; - AWT_UNLOCK(); return id; } @@ -1007,12 +1021,11 @@ JNIEXPORT jint JNICALL Java_sun_awt_X11GraphicsDevice_getConfigDepth( JNIEnv *env, jobject this, jint index, jint screen) { - int visNum; - AWT_LOCK(); + // NB: should be invoked only while holding the AWT lock + DASSERT(screen >= 0 && screen < awt_numScreens); ensureConfigsInited(env, screen); jint depth = (jint) (index == 0 ? x11Screens[screen].defaultConfig : x11Screens[screen].configs[index])->awt_visInfo.depth; - AWT_UNLOCK(); return depth; } @@ -1025,12 +1038,11 @@ JNIEXPORT jint JNICALL Java_sun_awt_X11GraphicsDevice_getConfigColormap( JNIEnv *env, jobject this, jint index, jint screen) { - int visNum; - AWT_LOCK(); + // NB: should be invoked only while holding the AWT lock + DASSERT(screen >= 0 && screen < awt_numScreens); ensureConfigsInited(env, screen); jint colormap = (jint) (index == 0 ? x11Screens[screen].defaultConfig : x11Screens[screen].configs[index])->awt_cmap; - AWT_UNLOCK(); return colormap; } @@ -1140,8 +1152,10 @@ JNIEXPORT void JNICALL Java_sun_awt_X11GraphicsConfig_init( JNIEnv *env, jobject this, jint visualNum, jint screen) { + // NB: should be invoked only while holding the AWT lock + DASSERT(screen >= 0 && screen < awt_numScreens); + AwtGraphicsConfigData *adata = NULL; - AWT_LOCK(); AwtScreenData asd = x11Screens[screen]; int i, n; int depth; @@ -1163,7 +1177,6 @@ JNIEnv *env, jobject this, jint visualNum, jint screen) /* If didn't find the visual, throw an exception... */ if (adata == (AwtGraphicsConfigData *) NULL) { - AWT_UNLOCK(); JNU_ThrowIllegalArgumentException(env, "Unknown Visual Specified"); return; } @@ -1182,7 +1195,6 @@ JNIEnv *env, jobject this, jint visualNum, jint screen) (*env)->SetIntField(env, this, x11GraphicsConfigIDs.bitsPerPixel, (jint)tempImage->bits_per_pixel); XDestroyImage(tempImage); - AWT_UNLOCK(); } /* @@ -1499,6 +1511,20 @@ typedef XRRCrtcInfo* (*XRRGetCrtcInfoType)(Display *dpy, typedef void (*XRRFreeCrtcInfoType)(XRRCrtcInfo *crtcInfo); +typedef void (*XRRSetScreenSizeType)(Display *dpy, Window window, + int width, int height, + int mmWidth, int mmHeight); + +typedef Status (*XRRSetCrtcConfigType)(Display *dpy, + XRRScreenResources *resources, + RRCrtc crtc, + Time timestamp, + int x, int y, + RRMode mode, + Rotation rotation, + RROutput *outputs, + int noutputs); + static XRRQueryVersionType awt_XRRQueryVersion; static XRRGetScreenInfoType awt_XRRGetScreenInfo; static XRRFreeScreenConfigInfoType awt_XRRFreeScreenConfigInfo; @@ -1514,6 +1540,8 @@ static XRRGetOutputInfoType awt_XRRGetOutputInfo; static XRRFreeOutputInfoType awt_XRRFreeOutputInfo; static XRRGetCrtcInfoType awt_XRRGetCrtcInfo; static XRRFreeCrtcInfoType awt_XRRFreeCrtcInfo; +static XRRSetScreenSizeType awt_XRRSetScreenSize; +static XRRSetCrtcConfigType awt_XRRSetCrtcConfig; #define LOAD_XRANDR_FUNC(f) \ do { \ @@ -1591,6 +1619,8 @@ X11GD_InitXrandrFuncs(JNIEnv *env) LOAD_XRANDR_FUNC(XRRFreeOutputInfo); LOAD_XRANDR_FUNC(XRRGetCrtcInfo); LOAD_XRANDR_FUNC(XRRFreeCrtcInfo); + LOAD_XRANDR_FUNC(XRRSetScreenSize); + LOAD_XRANDR_FUNC(XRRSetCrtcConfig); return JNI_TRUE; } @@ -1691,11 +1721,11 @@ X11GD_SetFullscreenMode(Window win, jboolean enabled) /* * Class: sun_awt_X11GraphicsDevice * Method: initXrandrExtension - * Signature: ()Z + * Signature: (Z)Z */ JNIEXPORT jboolean JNICALL Java_sun_awt_X11GraphicsDevice_initXrandrExtension - (JNIEnv *env, jclass x11gd) + (JNIEnv *env, jclass x11gd, jboolean useOldConfigDisplayMode) { #if defined(NO_XRANDR) return JNI_FALSE; @@ -1711,10 +1741,305 @@ Java_sun_awt_X11GraphicsDevice_initXrandrExtension } AWT_FLUSH_UNLOCK(); + useNewConfigDisplayMode = !useOldConfigDisplayMode; + return ret; #endif /* NO_XRANDR */ } +// --------------------------------------------------- +// display mode change via XRRSetCrtcConfig +// --------------------------------------------------- +#if !defined(NO_XRANDR) +static jint refreshRateFromModeInfo(const XRRModeInfo *modeInfo) { + if (!modeInfo->hTotal || !modeInfo->vTotal) { + return 0; + } + + double vTotal = modeInfo->vTotal; + + if (modeInfo->modeFlags & RR_Interlace) { + vTotal /= 2; + } + + if (modeInfo->modeFlags & RR_DoubleScan) { + vTotal *= 2; + } + + return (jint) round((double) modeInfo->dotClock / (vTotal * (double) modeInfo->hTotal)); +} + +static inline Bool isLandscapeOrientation(XRRCrtcInfo* info) { + if (!info) { + return True; + } + return info->rotation == RR_Rotate_0 || info->rotation == RR_Rotate_180; +} + +static Bool xrrGetInfoForScreen(XRRScreenResources *res, + int screen, + XRRCrtcInfo **outCrtcInfo, + XRROutputInfo **outOutputInfo) { + if (!res) { + return False; + } + + int screenX = 0; + int screenY = 0; + + if (usingXinerama) { + int nscreens = 0; + XineramaScreenInfo *screens = XineramaQueryScreens(awt_display, &nscreens); + + if (!screens) { + return False; + } + + if (screen >= nscreens) { + XFree(screens); + return False; + } + + XineramaScreenInfo xScreenInfo = screens[screen]; + + screenX = xScreenInfo.x_org; + screenY= xScreenInfo.y_org; + + XFree(screens); + } + + for (int i = 0; i < res->noutput; ++i) { + XRROutputInfo *output = awt_XRRGetOutputInfo(awt_display, res, res->outputs[i]); + if (!output) { + continue; + } + if (output->connection == RR_Connected && output->crtc) { + // output is connected and has an active mode + XRRCrtcInfo *crtcInfo = awt_XRRGetCrtcInfo(awt_display, res, output->crtc); + if (crtcInfo) { + if (crtcInfo->mode != None + && crtcInfo->x == screenX + && crtcInfo->y == screenY) { + if (outCrtcInfo) { + *outCrtcInfo = crtcInfo; + } else { + awt_XRRFreeCrtcInfo(crtcInfo); + } + if (outOutputInfo) { + *outOutputInfo = output; + } else { + awt_XRRFreeOutputInfo(output); + } + return True; + } + awt_XRRFreeCrtcInfo(crtcInfo); + } + } + awt_XRRFreeOutputInfo(output); + } + + return False; +} + +static jobject xrrGetCurrentDisplayMode(JNIEnv* env, int screen) { + XRRScreenResources *res = awt_XRRGetScreenResources(awt_display, DefaultRootWindow(awt_display)); + if (!res) { + return NULL; + } + + XRRCrtcInfo* currentCrtcInfo = NULL; + if (!xrrGetInfoForScreen(res, screen, ¤tCrtcInfo, NULL)) { + goto cleanup; + } + + if (!currentCrtcInfo || currentCrtcInfo->mode == None) { + goto cleanup; + } + + for (int i = 0; i < res->nmode; ++i) { + if (res->modes[i].id == currentCrtcInfo->mode) { + XRRModeInfo mode = res->modes[i]; + DisplayMode dm = { + mode.width, + mode.height, + refreshRateFromModeInfo(&mode) + }; + + Bool isLandscape = isLandscapeOrientation(currentCrtcInfo); + + jint resultWidth = isLandscape ? (jint) dm.width : (jint) dm.height; + jint resultHeight = isLandscape ? (jint) dm.height : (jint) dm.width; + + jobject displayMode = X11GD_CreateDisplayMode(env, + resultWidth, + resultHeight, + BIT_DEPTH_MULTI, + dm.refresh); + + awt_XRRFreeCrtcInfo(currentCrtcInfo); + awt_XRRFreeScreenResources(res); + + return displayMode; + } + } + + cleanup: + if (currentCrtcInfo) { + awt_XRRFreeCrtcInfo(currentCrtcInfo); + } + awt_XRRFreeScreenResources(res); + return NULL; +} + +static Bool isUniqueDisplayMode(DisplayMode seen[], int count, unsigned int width, unsigned int height, int refresh) { + for (int i = 0; i < count; ++i) { + if (seen[i].width == width && + seen[i].height == height && + seen[i].refresh == refresh) { + return False; + } + } + return True; +} + +static void xrrEnumDisplayModes(JNIEnv *env, jobject arrayList, jint screen) { + XRRScreenResources *res = awt_XRRGetScreenResources(awt_display, DefaultRootWindow(awt_display)); + if (!res) { + return; + } + + XRRCrtcInfo *crtcInfo = NULL; + XRROutputInfo *outputInfo = NULL; + if (!xrrGetInfoForScreen(res, screen, &crtcInfo, &outputInfo)) { + goto cleanup; + } + + DisplayMode seenModes[MAX_DISPLAY_MODES]; + int seenCount = 0; + + Bool isLandscape = isLandscapeOrientation(crtcInfo); + + for (int i = 0; i < outputInfo->nmode; ++i) { + RRMode mode_id = outputInfo->modes[i]; + + for (int j = 0; j < res->nmode; ++j) { + if (res->modes[j].id == mode_id) { + XRRModeInfo mode = res->modes[j]; + jint rr = refreshRateFromModeInfo(&mode); + + // The refresh rate is stored as an integer in Java, so we need to round the double value. + // Because of this rounding, duplicate modes may appear. We only keep the first one encountered. + if (isUniqueDisplayMode(seenModes, seenCount, mode.width, mode.height, rr)) { + seenModes[seenCount++] = (DisplayMode) { + mode.width, + mode.height, + rr + }; + X11GD_AddDisplayMode(env, arrayList, + isLandscape ? (jint) mode.width : (jint) mode.height, + isLandscape ? (jint) mode.height : (jint) mode.width, + BIT_DEPTH_MULTI, + rr); + if ((*env)->ExceptionCheck(env)) { + goto cleanup; + } + } + break; + } + } + } + + cleanup: + if (outputInfo) { + awt_XRRFreeOutputInfo(outputInfo); + } + if (crtcInfo) { + awt_XRRFreeCrtcInfo(crtcInfo); + } + awt_XRRFreeScreenResources(res); +} + +static void xrrChangeDisplayMode(jint screen, jint width, jint height, jint refreshRate) { + Drawable root = DefaultRootWindow(awt_display); + + + XRRScreenResources *res = awt_XRRGetScreenResources(awt_display, root); + if (!res) { + return; + } + + XRRCrtcInfo *crtcInfo = NULL; + XRROutputInfo *outputInfo = NULL; + + if (!xrrGetInfoForScreen(res, screen, &crtcInfo, &outputInfo)) { + goto cleanup; + } + + RRMode new_mode = None; + + Bool isLandscape = isLandscapeOrientation(crtcInfo); + + for (int i = 0; i < res->nmode; ++i) { + XRRModeInfo mode = res->modes[i]; + jint rr = refreshRateFromModeInfo(&mode); + + Bool matchW = (isLandscape ? mode.width : mode.height) == (unsigned int) width; + Bool matchH = (isLandscape ? mode.height : mode.width) == (unsigned int) height; + + if (matchW && matchH && rr == refreshRate) { + for (int j = 0; j < outputInfo->nmode; ++j) { + if (mode.id == outputInfo->modes[j]) { + // belongs to our output + new_mode = mode.id; + break; + } + } + if (new_mode != None) { + break; + } + } + } + + if (new_mode == None) { + goto cleanup; + } + + awt_XRRSetCrtcConfig (awt_display, res, outputInfo->crtc, CurrentTime, + 0, 0, None, RR_Rotate_0, NULL, 0); + + int resultMmWidth = outputInfo->mm_width + ? (int) outputInfo->mm_width + : DisplayWidthMM(awt_display, DefaultScreen(awt_display)); + + int resultMmHeight = outputInfo->mm_height + ? (int) outputInfo->mm_height + : XDisplayHeightMM(awt_display, DefaultScreen(awt_display)); + + awt_XRRSetScreenSize(awt_display, root, + width, height, + resultMmWidth, resultMmHeight); + + Status s = awt_XRRSetCrtcConfig(awt_display, res, outputInfo->crtc, + CurrentTime, + crtcInfo->x, crtcInfo->y, + new_mode, crtcInfo->rotation, + crtcInfo->outputs, crtcInfo->noutput); + + cleanup: + if (crtcInfo) { + awt_XRRFreeCrtcInfo(crtcInfo); + } + if (outputInfo) { + awt_XRRFreeOutputInfo(outputInfo); + } + awt_XRRFreeScreenResources(res); +} +#endif + +// --------------------------------------------------- +// display mode change via XRRSetCrtcConfig +// --------------------------------------------------- + /* * Class: sun_awt_X11GraphicsDevice * Method: getCurrentDisplayMode @@ -1727,9 +2052,17 @@ Java_sun_awt_X11GraphicsDevice_getCurrentDisplayMode #if defined(NO_XRANDR) return NULL; #else - XRRScreenConfiguration *config; jobject displayMode = NULL; + if (useNewConfigDisplayMode) { + AWT_LOCK(); + displayMode = xrrGetCurrentDisplayMode(env, screen); + AWT_FLUSH_UNLOCK(); + return displayMode; + } + + XRRScreenConfiguration *config; + AWT_LOCK(); if (screen < ScreenCount(awt_display)) { @@ -1780,7 +2113,12 @@ Java_sun_awt_X11GraphicsDevice_enumDisplayModes { #if !defined(NO_XRANDR) - AWT_LOCK(); + if (useNewConfigDisplayMode) { + AWT_LOCK(); + xrrEnumDisplayModes(env, arrayList, screen); + AWT_FLUSH_UNLOCK(); + return; + } if (XScreenCount(awt_display) > 0) { @@ -1830,6 +2168,15 @@ Java_sun_awt_X11GraphicsDevice_configDisplayMode jint screen, jint width, jint height, jint refreshRate) { #if !defined(NO_XRANDR) + if (useNewConfigDisplayMode) { + AWT_LOCK(); + XGrabServer(awt_display); + xrrChangeDisplayMode(screen, width, height, refreshRate); + XUngrabServer(awt_display); + AWT_FLUSH_UNLOCK(); + return; + } + jboolean success = JNI_FALSE; XRRScreenConfiguration *config; Drawable root; diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/awt_InputMethod.c b/src/java.desktop/unix/native/libawt_xawt/awt/awt_InputMethod.c index a2b887269a5b9..b257f4e175878 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/awt_InputMethod.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/awt_InputMethod.c @@ -1618,14 +1618,14 @@ JNIEXPORT jboolean JNICALL Java_sun_awt_X11InputMethodBase_setCompositionEnabled if (NULL != pX11IMData->statusWindow) { Window focus = 0; int revert_to; -#if defined(_LP64) && !defined(_LITTLE_ENDIAN) - // The Window value which is used for XGetICValues must be 32bit on BigEndian XOrg's xlib - unsigned int w = 0; -#else Window w = 0; -#endif XGetInputFocus(awt_display, &focus, &revert_to); XGetICValues(pX11IMData->current_ic, XNFocusWindow, &w, NULL); +#if defined(_LP64) && !defined(_LITTLE_ENDIAN) + // On 64bit BigEndian, + // Window value may be stored on high 32bit by XGetICValues via XIM + if (w > 0xffffffffUL) w = w >> 32; +#endif if (RevertToPointerRoot == revert_to && pX11IMData->ic_active != pX11IMData->ic_passive) { if (pX11IMData->current_ic == pX11IMData->ic_active) { @@ -1674,12 +1674,7 @@ JNIEXPORT jboolean JNICALL Java_sun_awt_X11InputMethodBase_isCompositionEnabledN { X11InputMethodData *pX11IMData = NULL; char * ret = NULL; -#if defined(__linux__) && defined(_LP64) && !defined(_LITTLE_ENDIAN) - // XIMPreeditState value which is used for XGetICValues must be 32bit on BigEndian XOrg's xlib - unsigned int state = XIMPreeditUnKnown; -#else XIMPreeditState state = XIMPreeditUnKnown; -#endif XVaNestedList pr_atrb; @@ -1695,6 +1690,11 @@ JNIEXPORT jboolean JNICALL Java_sun_awt_X11InputMethodBase_isCompositionEnabledN ret = XGetICValues(pX11IMData->current_ic, XNPreeditAttributes, pr_atrb, NULL); XFree((void *)pr_atrb); AWT_UNLOCK(); +#if defined(__linux__) && defined(_LP64) && !defined(_LITTLE_ENDIAN) + // On 64bit BigEndian, + // XIMPreeditState value may be stored on high 32bit by XGetICValues via XIM + if (state > 0xffffffffUL) state = state >> 32; +#endif if ((ret != 0) && ((strcmp(ret, XNPreeditAttributes) == 0) diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/fp_pipewire.h b/src/java.desktop/unix/native/libawt_xawt/awt/fp_pipewire.h index e5985af5eee14..d39f7c833b77b 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/fp_pipewire.h +++ b/src/java.desktop/unix/native/libawt_xawt/awt/fp_pipewire.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -58,7 +58,6 @@ void (*fp_pw_stream_destroy)(struct pw_stream *stream); void (*fp_pw_init)(int *argc, char **argv[]); -void (*fp_pw_deinit)(void); struct pw_core * (*fp_pw_context_connect_fd)(struct pw_context *context, diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c b/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c index 112b1b28f5777..6e6571e755121 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -283,6 +283,9 @@ GtkApi* gtk3_load(JNIEnv *env, const char* lib_name) fp_g_main_context_iteration = dl_symbol("g_main_context_iteration"); + fp_g_main_context_default = dl_symbol("g_main_context_default"); + fp_g_main_context_is_owner = dl_symbol("g_main_context_is_owner"); + fp_g_value_init = dl_symbol("g_value_init"); fp_g_type_is_a = dl_symbol("g_type_is_a"); @@ -554,6 +557,7 @@ GtkApi* gtk3_load(JNIEnv *env, const char* lib_name) fp_g_signal_connect_data = dl_symbol("g_signal_connect_data"); fp_gtk_widget_show = dl_symbol("gtk_widget_show"); fp_gtk_main = dl_symbol("gtk_main"); + fp_gtk_main_level = dl_symbol("gtk_main_level"); fp_g_path_get_dirname = dl_symbol("g_path_get_dirname"); @@ -611,11 +615,14 @@ GtkApi* gtk3_load(JNIEnv *env, const char* lib_name) glib_version_2_68 = !fp_glib_check_version(2, 68, 0); if (glib_version_2_68) { + // those function are called only by Screencast / Remote desktop fp_g_string_replace = dl_symbol("g_string_replace"); //since: 2.68 fp_g_uuid_string_is_valid = //since: 2.52 dl_symbol("g_uuid_string_is_valid"); + fp_g_variant_print = dl_symbol("g_variant_print"); // since 2.24 } fp_g_string_printf = dl_symbol("g_string_printf"); + fp_g_strconcat = dl_symbol("g_strconcat"); fp_g_error_free = dl_symbol("g_error_free"); fp_g_unix_fd_list_get = dl_symbol("g_unix_fd_list_get"); @@ -1525,7 +1532,7 @@ static void gtk3_paint_arrow(WidgetType widget_type, GtkStateType state_type, break; case COMBO_BOX_ARROW_BUTTON: - s = (int)(0.3 * height + 0.5) + 1; + s = (int)(0.3 * MIN(height, width) + 0.5) + 1; a = G_PI; break; @@ -3076,6 +3083,7 @@ static void gtk3_init(GtkApi* gtk) { gtk->g_variant_new_string = fp_g_variant_new_string; gtk->g_variant_new_boolean = fp_g_variant_new_boolean; gtk->g_variant_new_uint32 = fp_g_variant_new_uint32; + gtk->g_variant_print = fp_g_variant_print; gtk->g_variant_get = fp_g_variant_get; gtk->g_variant_get_string = fp_g_variant_get_string; @@ -3100,9 +3108,12 @@ static void gtk3_init(GtkApi* gtk) { gtk->g_string_free = fp_g_string_free; gtk->g_string_replace = fp_g_string_replace; gtk->g_string_printf = fp_g_string_printf; + gtk->g_strconcat = fp_g_strconcat; gtk->g_uuid_string_is_valid = fp_g_uuid_string_is_valid; gtk->g_main_context_iteration = fp_g_main_context_iteration; + gtk->g_main_context_default = fp_g_main_context_default; + gtk->g_main_context_is_owner = fp_g_main_context_is_owner; gtk->g_error_free = fp_g_error_free; gtk->g_unix_fd_list_get = fp_g_unix_fd_list_get; diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h b/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h index 054510d488b10..d3a83677dd623 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h +++ b/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -392,10 +392,14 @@ static void (*fp_g_object_set)(gpointer object, ...); static gboolean (*fp_g_main_context_iteration)(GMainContext *context, gboolean may_block); +static GMainContext *(*fp_g_main_context_default)(); +static gboolean (*fp_g_main_context_is_owner)(GMainContext* context); + static gboolean (*fp_g_str_has_prefix)(const gchar *str, const gchar *prefix); static gchar** (*fp_g_strsplit)(const gchar *string, const gchar *delimiter, gint max_tokens); static void (*fp_g_strfreev)(gchar **str_array); +static gchar* (*fp_g_strconcat)(const gchar* string1, ...); static cairo_surface_t* (*fp_cairo_image_surface_create)(cairo_format_t format, @@ -735,6 +739,8 @@ static GVariant *(*fp_g_variant_new_boolean)(gboolean value); static GVariant *(*fp_g_variant_new_uint32)(guint32 value); +static gchar *(*fp_g_variant_print) (GVariant* value, gboolean type_annotate); + static void (*fp_g_variant_get)(GVariant *value, const gchar *format_string, ...); diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.h b/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.h index aac1857181500..232ef9973c68b 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.h +++ b/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -678,6 +678,7 @@ typedef struct GtkApi { GVariant *(*g_variant_new_boolean)(gboolean value); GVariant *(*g_variant_new_uint32)(guint32 value); + gchar *(*g_variant_print)(GVariant* value, gboolean type_annotate); void (*g_variant_get)(GVariant *value, const gchar *format_string, @@ -734,6 +735,8 @@ typedef struct GtkApi { const gchar *format, ...); + gchar* (*g_strconcat)(const gchar* string1, ...); + gboolean (*g_uuid_string_is_valid)(const gchar *str); @@ -792,6 +795,8 @@ typedef struct GtkApi { gboolean (*g_main_context_iteration)(GMainContext *context, gboolean may_block); + GMainContext *(*g_main_context_default)(); + gboolean (*g_main_context_is_owner)(GMainContext* context); void (*g_error_free)(GError *error); diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/screencast_pipewire.c b/src/java.desktop/unix/native/libawt_xawt/awt/screencast_pipewire.c index e2f4ea31d2edb..62d12870ed2b0 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/screencast_pipewire.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/screencast_pipewire.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,12 +30,17 @@ #include #include "jni_util.h" #include "awt.h" + +#ifndef _AIX #include "screencast_pipewire.h" #include "fp_pipewire.h" +#include "java_awt_event_KeyEvent.h" + #include #include "gtk_interface.h" #include "gtk3_interface.h" +#include "canvas.h" int DEBUG_SCREENCAST_ENABLED = FALSE; @@ -49,6 +54,8 @@ static GString *activeSessionToken; struct ScreenSpace screenSpace = {0}; static struct PwLoopData pw = {0}; +volatile bool isGtkMainThread = FALSE; +gboolean isRemoteDesktop = FALSE; jclass tokenStorageClass = NULL; jmethodID storeTokenMethodID = NULL; @@ -132,10 +139,6 @@ static void doCleanup() { screenSpace.screenCount = 0; } - if (!sessionClosed) { - fp_pw_deinit(); - } - gtk->g_string_set_size(activeSessionToken, 0); sessionClosed = TRUE; } @@ -143,7 +146,7 @@ static void doCleanup() { /** * @return TRUE on success */ -static gboolean initScreencast(const gchar *token, +static gboolean initPortal(const gchar *token, GdkRectangle *affectedBounds, gint affectedBoundsLength) { gboolean isSameToken = !token @@ -170,8 +173,8 @@ static gboolean initScreencast(const gchar *token, if (!initScreenSpace() || !initXdgDesktopPortal() - || (pw.pwFd = getPipewireFd(token, - affectedBounds, + || !initAndStartSession(token, &pw.pwFd) + || (pw.pwFd = getPipewireFd(affectedBounds, affectedBoundsLength)) < 0) { doCleanup(); return FALSE; @@ -581,6 +584,13 @@ static gboolean doLoop(GdkRectangle requestedArea) { if (!pw.loop && !sessionClosed) { pw.loop = fp_pw_thread_loop_new("AWT Pipewire Thread", NULL); + if (!pw.loop) { + // in case someone called the pw_deinit before + DEBUG_SCREENCAST("pw_init\n", NULL); + fp_pw_init(NULL, NULL); + pw.loop = fp_pw_thread_loop_new("AWT Pipewire Thread", NULL); + } + if (!pw.loop) { DEBUG_SCREENCAST("!!! Could not create a loop\n", NULL); doCleanup(); @@ -712,7 +722,6 @@ static gboolean loadSymbols() { LOAD_SYMBOL(fp_pw_stream_disconnect, "pw_stream_disconnect"); LOAD_SYMBOL(fp_pw_stream_destroy, "pw_stream_destroy"); LOAD_SYMBOL(fp_pw_init, "pw_init"); - LOAD_SYMBOL(fp_pw_deinit, "pw_deinit"); LOAD_SYMBOL(fp_pw_context_connect_fd, "pw_context_connect_fd"); LOAD_SYMBOL(fp_pw_core_disconnect, "pw_core_disconnect"); LOAD_SYMBOL(fp_pw_context_new, "pw_context_new"); @@ -798,10 +807,19 @@ void storeRestoreToken(const gchar* oldToken, const gchar* newToken) { * Signature: (IZ)Z */ JNIEXPORT jboolean JNICALL Java_sun_awt_screencast_ScreencastHelper_loadPipewire( - JNIEnv *env, jclass cls, jboolean screencastDebug + JNIEnv *env, jclass cls, jint method, jboolean screencastDebug ) { DEBUG_SCREENCAST_ENABLED = screencastDebug; + if (method != XDG_METHOD_SCREENCAST + && method != XDG_METHOD_REMOTE_DESKTOP) { + return JNI_FALSE; + } + + isRemoteDesktop = method == XDG_METHOD_REMOTE_DESKTOP; + + DEBUG_SCREENCAST("method %d\n", method) + if (!loadSymbols()) { return JNI_FALSE; } @@ -874,7 +892,7 @@ static int makeScreencast( GdkRectangle *affectedScreenBounds, gint affectedBoundsLength ) { - if (!initScreencast(token, affectedScreenBounds, affectedBoundsLength)) { + if (!initPortal(token, affectedScreenBounds, affectedBoundsLength)) { return pw.pwFd; } @@ -945,10 +963,12 @@ JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_getRGBPixelsImpl const gchar *token = jtoken ? (*env)->GetStringUTFChars(env, jtoken, NULL) : NULL; + JNU_CHECK_EXCEPTION_RETURN(env, RESULT_ERROR); + isGtkMainThread = gtk->g_main_context_is_owner(gtk->g_main_context_default()); DEBUG_SCREENCAST( - "taking screenshot at \n\tx: %5i y %5i w %5i h %5i with token |%s|\n", - jx, jy, jwidth, jheight, token + "taking screenshot at \n\tx: %5i y %5i w %5i h %5i\n\twith token |%s| isGtkMainThread %d\n", + jx, jy, jwidth, jheight, token, isGtkMainThread ); int attemptResult = makeScreencast( @@ -1036,3 +1056,192 @@ JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_getRGBPixelsImpl releaseToken(env, jtoken, token); return 0; } + +/* + * Class: sun_awt_screencast_ScreencastHelper + * Method: remoteDesktopMouseMove + * Signature: (IILjava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_remoteDesktopMouseMoveImpl + (JNIEnv *env, jclass cls, jint jx, jint jy, jstring jtoken) { + + + const gchar *token = jtoken + ? (*env)->GetStringUTFChars(env, jtoken, NULL) + : NULL; + JNU_CHECK_EXCEPTION_RETURN(env, RESULT_ERROR); + + DEBUG_SCREENCAST("moving mouse to\n\t%d %d\n\twith token |%s|\n", jx, jy, token); + + gboolean result = initPortal(token, NULL, 0); + DEBUG_SCREENCAST("init result %b, moving to %d %d\n", result, jx, jy) + + if (result) { + if (!remoteDesktopMouseMove(jx, jy)) { + releaseToken(env, jtoken, token); + return RESULT_DENIED; + } + } + + releaseToken(env, jtoken, token); + + return result ? RESULT_OK : pw.pwFd; +} + +/* + * Class: sun_awt_screencast_ScreencastHelper + * Method: remoteDesktopMouseButtonImpl + * Signature: (ZILjava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_remoteDesktopMouseButtonImpl + (JNIEnv *env, jclass cls, jboolean isPress, jint buttons, jstring jtoken) { + + const gchar *token = jtoken + ? (*env)->GetStringUTFChars(env, jtoken, NULL) + : NULL; + JNU_CHECK_EXCEPTION_RETURN(env, RESULT_ERROR); + + gboolean result = initPortal(token, NULL, 0); + DEBUG_SCREENCAST("init result %b, mouse pressing %d\n", result, buttons) + + if (result) { + if (!remoteDesktopMouse(isPress, buttons)) { + releaseToken(env, jtoken, token); + return RESULT_DENIED; + } + } + + releaseToken(env, jtoken, token); + + return result ? RESULT_OK : pw.pwFd; +} + +/* + * Class: sun_awt_screencast_ScreencastHelper + * Method: remoteDesktopMouseWheelImpl + * Signature: (ILjava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_remoteDesktopMouseWheelImpl + (JNIEnv *env, jclass cls, jint jWheelAmt, jstring jtoken) { + + const gchar *token = jtoken + ? (*env)->GetStringUTFChars(env, jtoken, NULL) + : NULL; + JNU_CHECK_EXCEPTION_RETURN(env, RESULT_ERROR); + + gboolean result = initPortal(token, NULL, 0); + DEBUG_SCREENCAST("init result %b, mouse wheel %d\n", result, jWheelAmt) + + if (result) { + if (!remoteDesktopMouseWheel(jWheelAmt)) { + releaseToken(env, jtoken, token); + return RESULT_DENIED; + } + } + + releaseToken(env, jtoken, token); + + return result ? RESULT_OK : pw.pwFd; +} + +static int getNumpadKey(jint jkey) { + switch (jkey) { + case java_awt_event_KeyEvent_VK_NUMPAD0: return XK_KP_Insert; + case java_awt_event_KeyEvent_VK_NUMPAD1: return XK_KP_End; + case java_awt_event_KeyEvent_VK_NUMPAD2: return XK_KP_Down; + case java_awt_event_KeyEvent_VK_NUMPAD3: return XK_KP_Page_Down; + case java_awt_event_KeyEvent_VK_NUMPAD4: return XK_KP_Left; + case java_awt_event_KeyEvent_VK_NUMPAD5: return XK_KP_Begin; + case java_awt_event_KeyEvent_VK_NUMPAD6: return XK_KP_Right; + case java_awt_event_KeyEvent_VK_NUMPAD7: return XK_KP_Home; + case java_awt_event_KeyEvent_VK_NUMPAD8: return XK_KP_Up; + case java_awt_event_KeyEvent_VK_NUMPAD9: return XK_KP_Prior; + case java_awt_event_KeyEvent_VK_DECIMAL: + case java_awt_event_KeyEvent_VK_SEPARATOR: return XK_KP_Delete; + default: return 0; + } +} + +/* + * Class: sun_awt_screencast_ScreencastHelper + * Method: remoteDesktopKeyImpl + * Signature: (ZILjava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_remoteDesktopKeyImpl + (JNIEnv *env, jclass cls, jboolean isPress, jint jkey, jstring jtoken) { + + int key = getNumpadKey(jkey); + if (!key) { + AWT_LOCK(); + key = awt_getX11KeySym(jkey); + AWT_UNLOCK(); + } + + if (key == NoSymbol || (*env)->ExceptionCheck(env)) { + return RESULT_ERROR; + } + + const gchar *token = jtoken + ? (*env)->GetStringUTFChars(env, jtoken, NULL) + : NULL; + JNU_CHECK_EXCEPTION_RETURN(env, RESULT_ERROR); + + gboolean result = initPortal(token, NULL, 0); + DEBUG_SCREENCAST("init result %b, key %d -> %d isPress %b\n", result, jkey, key, isPress) + + if (result) { + if (!remoteDesktopKey(isPress, key)) { + releaseToken(env, jtoken, token); + return RESULT_DENIED; + } + } + + releaseToken(env, jtoken, token); + + return result ? RESULT_OK : pw.pwFd; +} + +#else +JNIEXPORT void JNICALL +Java_sun_awt_screencast_ScreencastHelper_closeSession(JNIEnv *env, jclass cls) {} + +JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_getRGBPixelsImpl( + JNIEnv *env, + jclass cls, + jint jx, + jint jy, + jint jwidth, + jint jheight, + jintArray pixelArray, + jintArray affectedScreensBoundsArray, + jstring jtoken +) { + return -1; /* RESULT_ERROR */ +} + +JNIEXPORT jboolean JNICALL Java_sun_awt_screencast_ScreencastHelper_loadPipewire( + JNIEnv *env, jclass cls, jint method, jboolean screencastDebug +) { + return JNI_FALSE; +} + +JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_remoteDesktopMouseMoveImpl + (JNIEnv *env, jclass cls, jint jx, jint jy, jstring token) { + return -1; +} + +JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_remoteDesktopMouseButtonImpl + (JNIEnv *env, jclass cls, jboolean isPress, jint buttons, jstring jtoken) { + return -1; +} + +JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_remoteDesktopMouseWheelImpl + (JNIEnv *env, jclass cls, jint jWheelAmt, jstring jtoken) { + return -1; +} + +JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_remoteDesktopKeyImpl + (JNIEnv *env, jclass cls, jboolean isPress, jint jkey, jstring jtoken) { + return -1; +} +#endif diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/screencast_pipewire.h b/src/java.desktop/unix/native/libawt_xawt/awt/screencast_pipewire.h index 07a7e91304c50..30ac0f3582a26 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/screencast_pipewire.h +++ b/src/java.desktop/unix/native/libawt_xawt/awt/screencast_pipewire.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,6 +41,8 @@ void storeRestoreToken(const gchar* oldToken, const gchar* newToken); +void print_gvariant_content(gchar *caption, GVariant *response); + struct ScreenProps { guint32 id; GdkRectangle bounds; diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/screencast_portal.c b/src/java.desktop/unix/native/libawt_xawt/awt/screencast_portal.c index 8590cf27da20f..fad2cab169656 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/screencast_portal.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/screencast_portal.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,18 +24,26 @@ */ #include "stdlib.h" -#include -#include #include -#include -#include + +#include "java_awt_event_InputEvent.h" + +#ifndef _AIX #include "screencast_pipewire.h" + #include "screencast_portal.h" +extern volatile bool isGtkMainThread; +extern gboolean isRemoteDesktop; extern struct ScreenSpace screenSpace; struct XdgDesktopPortalApi *portal = NULL; +extern int DEBUG_SCREENCAST_ENABLED; + +GDBusProxy *getProxy() { + return isRemoteDesktop ? portal->remoteDesktopProxy : portal->screenCastProxy; +} void errHandle( GError *error, @@ -67,6 +75,27 @@ gboolean validateToken(const gchar *token) { return isValid; } +void waitForCallback(struct DBusCallbackHelper *helper) { + if (!helper) { + return; + } + + if (isGtkMainThread) { + gtk->gtk_main(); + } else { + while (!helper->isDone) { + // do not block if there is a GTK loop running + gtk->g_main_context_iteration(NULL, gtk->gtk_main_level() == 0); + } + } +} + +void callbackEnd() { + if (isGtkMainThread) { + gtk->gtk_main_quit(); + } +} + /** * @return TRUE on success */ @@ -145,26 +174,38 @@ gboolean rebuildScreenData(GVariantIter *iterStreams, gboolean isTheOnlyMon) { } /** - * Checks screencast protocol version - * @return FALSE if version < 4, or could not be determined + * Checks the version of the Screencast/Remote Desktop protocol + * to determine whether it supports the restore_token. + * @return FALSE if version is below required, or could not be determined */ gboolean checkVersion() { static guint32 version = 0; + + const gchar *interface = isRemoteDesktop + ? PORTAL_IFACE_REMOTE_DESKTOP + : PORTAL_IFACE_SCREENCAST; + if (version == 0) { GError *error = NULL; + GVariant *retVersion = gtk->g_dbus_proxy_call_sync( - portal->screenCastProxy, + getProxy(), "org.freedesktop.DBus.Properties.Get", gtk->g_variant_new("(ss)", - "org.freedesktop.portal.ScreenCast", + interface, "version"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL ); + if (isRemoteDesktop) { + print_gvariant_content("checkVersion Remote Desktop", retVersion); + } else { + print_gvariant_content("checkVersion ScreenCast", retVersion); + } + if (!retVersion) { //no backend on system - DEBUG_SCREENCAST("!!! could not detect the screencast version\n", - NULL); + DEBUG_SCREENCAST("!!! could not detect the %s version\n", interface); return FALSE; } @@ -175,8 +216,7 @@ gboolean checkVersion() { if (!varVersion){ gtk->g_variant_unref(retVersion); - DEBUG_SCREENCAST("!!! could not get the screencast version\n", - NULL); + DEBUG_SCREENCAST("!!! could not get the %s version\n", interface); return FALSE; } @@ -187,16 +227,22 @@ gboolean checkVersion() { } - DEBUG_SCREENCAST("ScreenCast protocol version %d\n", version); - if (version < 4) { - DEBUG_SCREENCAST("!!! ScreenCast protocol version %d < 4," + gboolean isVersionOk = isRemoteDesktop + ? version >= PORTAL_MIN_VERSION_REMOTE_DESKTOP + : version >= PORTAL_MIN_VERSION_SCREENCAST; + + if (!isVersionOk) { + DEBUG_SCREENCAST("!!! %s protocol version %d < %d," " session restore is not available\n", - version); + interface, + version, + isRemoteDesktop + ? PORTAL_MIN_VERSION_REMOTE_DESKTOP + : PORTAL_MIN_VERSION_SCREENCAST + ); } - // restore_token was added in version 4, without it, - // user confirmation is required for every screenshot. - return version >= 4; + return isVersionOk; } /** @@ -241,9 +287,9 @@ gboolean initXdgDesktopPortal() { portal->connection, G_DBUS_PROXY_FLAGS_NONE, NULL, - "org.freedesktop.portal.Desktop", - "/org/freedesktop/portal/desktop", - "org.freedesktop.portal.ScreenCast", + PORTAL_DESKTOP_BUS_NAME, + PORTAL_DESKTOP_OBJECT_PATH, + PORTAL_IFACE_SCREENCAST, NULL, &err ); @@ -252,6 +298,29 @@ gboolean initXdgDesktopPortal() { DEBUG_SCREENCAST("Failed to get ScreenCast portal: %s", err->message); ERR_HANDLE(err); return FALSE; + } else { + DEBUG_SCREENCAST("%s: connection/sender name %s / %s\n", + "ScreenCast", name, + portal->senderName); + } + + if (isRemoteDesktop) { + portal->remoteDesktopProxy = gtk->g_dbus_proxy_new_sync( + portal->connection, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + PORTAL_DESKTOP_BUS_NAME, + PORTAL_DESKTOP_OBJECT_PATH, + PORTAL_IFACE_REMOTE_DESKTOP, + NULL, + &err + ); + + if (err) { + DEBUG_SCREENCAST("Failed to get Remote Desktop portal: %s", err->message); + ERR_HANDLE(err); + return FALSE; + } } return checkVersion(); @@ -312,8 +381,8 @@ static void registerScreenCastCallback( ) { helper->id = gtk->g_dbus_connection_signal_subscribe( portal->connection, - "org.freedesktop.portal.Desktop", - "org.freedesktop.portal.Request", + PORTAL_DESKTOP_BUS_NAME, + PORTAL_IFACE_REQUEST, "Response", path, NULL, @@ -358,10 +427,12 @@ static void callbackScreenCastCreateSession( if (status != 0) { DEBUG_SCREENCAST("Failed to create ScreenCast: %u\n", status); } else { - gtk->g_variant_lookup(result, "session_handle", "s", helper->data); + gboolean returned = gtk->g_variant_lookup(result, "session_handle", "s", helper->data); + DEBUG_SCREENCAST("session_handle returned %b %p\n", returned, helper->data) } helper->isDone = TRUE; + callbackEnd(); } gboolean portalScreenCastCreateSession() { @@ -404,6 +475,9 @@ gboolean portalScreenCastCreateSession() { gtk->g_variant_new_string(requestToken) ); + + DEBUG_SCREENCAST("sessionToken %s \n", sessionToken) + gtk->g_variant_builder_add( &builder, "{sv}", @@ -411,8 +485,14 @@ gboolean portalScreenCastCreateSession() { gtk->g_variant_new_string(sessionToken) ); + DEBUG_SCREENCAST("portalScreenCastCreateSession: proxy %s %p (rd: %p / sc: %p)\n", + isRemoteDesktop ? "remoteDesktop" : "screencast", + getProxy(), + portal->remoteDesktopProxy, + portal->screenCastProxy); + GVariant *response = gtk->g_dbus_proxy_call_sync( - portal->screenCastProxy, + getProxy(), "CreateSession", gtk->g_variant_new("(a{sv})", &builder), G_DBUS_CALL_FLAGS_NONE, @@ -421,16 +501,18 @@ gboolean portalScreenCastCreateSession() { &err ); + print_gvariant_content("CreateSession", response); + if (err) { DEBUG_SCREENCAST("Failed to create ScreenCast session: %s\n", err->message); ERR_HANDLE(err); } else { - while (!helper.isDone) { - gtk->g_main_context_iteration(NULL, TRUE); - } + waitForCallback(&helper); } + DEBUG_SCREENCAST("portal->screenCastSessionHandle %s\n", portal->screenCastSessionHandle); + unregisterScreenCastCallback(&helper); if (response) { gtk->g_variant_unref(response); @@ -472,6 +554,41 @@ static void callbackScreenCastSelectSources( if (result) { gtk->g_variant_unref(result); } + + callbackEnd(); +} + +static void callbackRemoteDesktopSelectDevices( + GDBusConnection *connection, + const char *senderName, + const char *objectPath, + const char *interfaceName, + const char *signalName, + GVariant *parameters, + void *data +) { + struct DBusCallbackHelper *helper = data; + + helper->data = (void *) 0; + + uint32_t status; + GVariant* result = NULL; + + gtk->g_variant_get(parameters, "(u@a{sv})", &status, &result); + + if (status != 0) { + DEBUG_SCREENCAST("Failed select devices: %u\n", status); + } else { + helper->data = (void *) 1; + } + + helper->isDone = TRUE; + + if (result) { + gtk->g_variant_unref(result); + } + + callbackEnd(); } gboolean portalScreenCastSelectSources(const gchar *token) { @@ -519,25 +636,33 @@ gboolean portalScreenCastSelectSources(const gchar *token) { gtk->g_variant_new_uint32(1) ); + // In the case of Remote Desktop, + // we add the restore_token and persist_mode to the SelectDevices call. + // 0: Do not persist (default) // 1: Permissions persist as long as the application is running // 2: Permissions persist until explicitly revoked - gtk->g_variant_builder_add( - &builder, - "{sv}", - "persist_mode", - gtk->g_variant_new_uint32(2) - ); - - if (validateToken(token)) { + if (!isRemoteDesktop) { gtk->g_variant_builder_add( &builder, "{sv}", - "restore_token", - gtk->g_variant_new_string(token) + "persist_mode", + gtk->g_variant_new_uint32(2) ); } + if (!isRemoteDesktop) { + if (validateToken(token)) { + DEBUG_SCREENCAST(">>> adding token %s\n", token); + gtk->g_variant_builder_add( + &builder, + "{sv}", + "restore_token", + gtk->g_variant_new_string(token) + ); + } + } + GVariant *response = gtk->g_dbus_proxy_call_sync( portal->screenCastProxy, "SelectSources", @@ -548,13 +673,13 @@ gboolean portalScreenCastSelectSources(const gchar *token) { &err ); + print_gvariant_content("SelectSources", response); + if (err) { DEBUG_SCREENCAST("Failed to call SelectSources: %s\n", err->message); ERR_HANDLE(err); } else { - while (!helper.isDone) { - gtk->g_main_context_iteration(NULL, TRUE); - } + waitForCallback(&helper); } unregisterScreenCastCallback(&helper); @@ -600,6 +725,15 @@ static void callbackScreenCastStart( G_VARIANT_TYPE_ARRAY ); + print_gvariant_content("Streams", streams); + + if (!streams) { + DEBUG_SCREENCAST("No streams available with current token\n", NULL); + startHelper->result = RESULT_NO_STREAMS; + helper->isDone = TRUE; + return; + } + GVariantIter iter; gtk->g_variant_iter_init( &iter, @@ -637,9 +771,9 @@ static void callbackScreenCastStart( helper->isDone = TRUE; - if (streams) { - gtk->g_variant_unref(streams); - } + gtk->g_variant_unref(streams); + + callbackEnd(); } ScreenCastResult portalScreenCastStart(const gchar *token) { @@ -680,7 +814,7 @@ ScreenCastResult portalScreenCastStart(const gchar *token) { ); GVariant *response = gtk->g_dbus_proxy_call_sync( - portal->screenCastProxy, + getProxy(), "Start", gtk->g_variant_new("(osa{sv})", portal->screenCastSessionHandle, "", &builder), G_DBUS_CALL_FLAGS_NONE, @@ -689,13 +823,13 @@ ScreenCastResult portalScreenCastStart(const gchar *token) { &err ); + print_gvariant_content("Start", response); + if (err) { DEBUG_SCREENCAST("Failed to start session: %s\n", err->message); ERR_HANDLE(err); } else { - while (!helper.isDone) { - gtk->g_main_context_iteration(NULL, TRUE); - } + waitForCallback(&helper); } unregisterScreenCastCallback(&helper); @@ -784,9 +918,9 @@ void portalScreenCastCleanup() { if (portal->screenCastSessionHandle) { gtk->g_dbus_connection_call_sync( portal->connection, - "org.freedesktop.portal.Desktop", + PORTAL_DESKTOP_BUS_NAME, portal->screenCastSessionHandle, - "org.freedesktop.portal.Session", + PORTAL_IFACE_SESSION, "Close", NULL, NULL, @@ -865,33 +999,140 @@ gboolean checkCanCaptureAllRequiredScreens(GdkRectangle *affectedBounds, return true; } +gboolean remoteDesktopSelectDevicesIfNeeded(const gchar* token) { + if (!isRemoteDesktop || !portal->remoteDesktopProxy) { + DEBUG_SCREENCAST("Skipping, remote desktop is not selected \n", NULL); + return TRUE; + } + + GError* err = NULL; + + gchar *requestPath = NULL; + gchar *requestToken = NULL; + + struct DBusCallbackHelper helper = {0}; + + + updateRequestPath( + &requestPath, + &requestToken + ); + + registerScreenCastCallback( + requestPath, + &helper, + callbackRemoteDesktopSelectDevices + ); + + GVariantBuilder builder; + + gtk->g_variant_builder_init( + &builder, + G_VARIANT_TYPE_VARDICT + ); + + gtk->g_variant_builder_add( + &builder, + "{sv}", "handle_token", + gtk->g_variant_new_string(requestToken) + ); + + // 1: KEYBOARD + // 2: POINTER + // 4: TOUCHSCREEN + gtk->g_variant_builder_add( + &builder, "{sv}", "types", + gtk->g_variant_new_uint32(1 | 2) + ); + + // 0: Do not persist (default) + // 1: Permissions persist as long as the application is running + // 2: Permissions persist until explicitly revoked + gtk->g_variant_builder_add( + &builder, + "{sv}", + "persist_mode", + gtk->g_variant_new_uint32(2) + ); + + if (validateToken(token)) { + gtk->g_variant_builder_add( + &builder, + "{sv}", + "restore_token", + gtk->g_variant_new_string(token) + ); + } + + GVariant *response = gtk->g_dbus_proxy_call_sync( + portal->remoteDesktopProxy, + "SelectDevices", + gtk->g_variant_new("(oa{sv})", portal->screenCastSessionHandle, &builder), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &err + ); + + print_gvariant_content("SelectDevices", response); + + if (err) { + DEBUG_SCREENCAST("Failed to call SelectDevices: %s\n", err->message); + ERR_HANDLE(err); + } else { + waitForCallback(&helper); + } + + unregisterScreenCastCallback(&helper); + if (response) { + gtk->g_variant_unref(response); + } + + free(requestPath); + free(requestToken); + + return helper.data != NULL; +} + +gboolean initAndStartSession(const gchar *token, int *retVal) { + + *retVal = RESULT_ERROR; -int getPipewireFd(const gchar *token, - GdkRectangle *affectedBounds, - gint affectedBoundsLength) { if (!portalScreenCastCreateSession()) { DEBUG_SCREENCAST("Failed to create ScreenCast session\n", NULL); - return RESULT_ERROR; + return FALSE; } if (!portalScreenCastSelectSources(token)) { DEBUG_SCREENCAST("Failed to select sources\n", NULL); - return RESULT_ERROR; + return FALSE; + } + + if (!remoteDesktopSelectDevicesIfNeeded(token)) { + return FALSE; } ScreenCastResult startResult = portalScreenCastStart(token); DEBUG_SCREENCAST("portalScreenCastStart result |%i|\n", startResult); + if (startResult != RESULT_OK) { - DEBUG_SCREENCAST("Failed to start\n", NULL); - return startResult; - } else { - if (!checkCanCaptureAllRequiredScreens(affectedBounds, - affectedBoundsLength)) { - DEBUG_SCREENCAST("The location of the screens has changed, " - "the capture area is outside the allowed " - "area.\n", NULL) - return RESULT_OUT_OF_BOUNDS; - } + DEBUG_SCREENCAST("Failed to start %d\n", startResult); + *retVal = startResult; + return FALSE; + } + + *retVal = RESULT_OK; + return TRUE; +} + +int getPipewireFd(GdkRectangle *affectedBounds, + gint affectedBoundsLength) { + if (!checkCanCaptureAllRequiredScreens(affectedBounds, + affectedBoundsLength)) { + DEBUG_SCREENCAST("The location of the screens has changed, " + "the capture area is outside the allowed " + "area.\n", NULL) + return RESULT_OUT_OF_BOUNDS; } DEBUG_SCREENCAST("--- portalScreenCastStart\n", NULL); @@ -904,3 +1145,182 @@ int getPipewireFd(const gchar *token, DEBUG_SCREENCAST("pwFd %i\n", pipewireFd); return pipewireFd; } + + +void print_gvariant_content(gchar *caption, GVariant *response) { + if (!DEBUG_SCREENCAST_ENABLED) { + return; + } + + gchar *str = NULL; + if (response != NULL) { + str = gtk->g_variant_print(response, TRUE); + } + + DEBUG_SCREENCAST("%s response:\n\t%s\n", + caption, str); + + gtk->g_free(str); +} + +static gboolean callRemoteDesktop(const gchar* methodName, GVariant *params) { + GError *err = NULL; + GVariantBuilder builder; + gtk->g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); + + GVariant *response = gtk->g_dbus_proxy_call_sync( + portal->remoteDesktopProxy, + methodName, + params, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &err + ); + + gchar * caption = gtk->g_strconcat("callRemoteDesktop ", methodName, NULL); + print_gvariant_content(caption, response); + gtk->g_free(caption); + + DEBUG_SCREENCAST("%s: response %p err %p\n", methodName, response, err); + + if (err) { + DEBUG_SCREENCAST("Failed to call %s: %s\n", methodName, err->message); + ERR_HANDLE(err); + + // e.g. user denied mouse keyboard/interaction + return FALSE; + } + + return TRUE; +} + +void clampCoordsIfNeeded(int *x, int *y) { + if (screenSpace.screenCount <= 0 || x == NULL || y == NULL) { + return; + } + + GdkRectangle s0 = screenSpace.screens[0].bounds; + int minX = s0.x; + int minY = s0.y; + int maxX = s0.x + s0.width; + int maxY = s0.y + s0.height; + + for (int i = 1; i < screenSpace.screenCount; ++i) { + GdkRectangle s = screenSpace.screens[i].bounds; + if (s.x < minX) minX = s.x; + if (s.y < minY) minY = s.y; + if (s.x + s.width > maxX) maxX = s.x + s.width; + if (s.y + s.height > maxY) maxY = s.y + s.height; + } + + if (*x < minX) { + *x = minX; + } else if (*x > maxX) { + *x = maxX - 1; + } + + if (*y < minY) { + *y = minY; + } else if (*y > maxY) { + *y = maxY - 1; + } +} + +gboolean remoteDesktopMouseMove(int x, int y) { + guint32 streamId = 0; + int relX = -1; + int relY = -1; + + DEBUG_SCREENCAST("mouseMove %d %d\n", x, y); + clampCoordsIfNeeded(&x, &y); + DEBUG_SCREENCAST("after clamping %d %d\n", x, y); + + for (int i = 0; i < screenSpace.screenCount; ++i) { + struct ScreenProps *screenProps = &screenSpace.screens[i]; + GdkRectangle rect = screenProps->bounds; + + if (x >= rect.x && + y >= rect.y && + x < rect.x + rect.width && + y < rect.y + rect.height) { + streamId = screenProps->id; + relX = x - rect.x; + relY = y - rect.y; + + DEBUG_SCREENCAST("screenId#%i point %dx%d (rel %i %i) inside of screen (%d, %d, %d, %d)\n", + streamId, + x, y, relX, relY, + rect.x, rect.y, rect.width, rect.height); + + break; + } + } + + if (streamId == 0) { + DEBUG_SCREENCAST("outside of available screens\n", NULL); + return TRUE; + } + + GVariantBuilder builder; + gtk->g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); + GVariant *params = gtk->g_variant_new("(oa{sv}udd)", portal->screenCastSessionHandle, &builder, + streamId, (double) relX, (double) relY); + return callRemoteDesktop("NotifyPointerMotionAbsolute", params); +} + +gboolean callRemoteDesktopNotifyPointerButton(gboolean isPress, int evdevButton) { + DEBUG_SCREENCAST("isPress %d evdevButton %d\n", isPress, evdevButton); + + GVariantBuilder builder; + gtk->g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + GVariant *params = gtk->g_variant_new("(oa{sv}iu)", + portal->screenCastSessionHandle, &builder, evdevButton, isPress); + return callRemoteDesktop("NotifyPointerButton", params); +} + +gboolean remoteDesktopMouse(gboolean isPress, int buttons) { + DEBUG_SCREENCAST("isPress %d awt buttons mask %d\n", isPress, buttons); + + if (buttons & java_awt_event_InputEvent_BUTTON1_MASK + || buttons & java_awt_event_InputEvent_BUTTON1_DOWN_MASK) { + if (!callRemoteDesktopNotifyPointerButton(isPress, 0x110)) { // BTN_LEFT + return FALSE; + } + } + if (buttons & java_awt_event_InputEvent_BUTTON2_MASK + || buttons & java_awt_event_InputEvent_BUTTON2_DOWN_MASK) { + if (!callRemoteDesktopNotifyPointerButton(isPress, 0x112)) { // BTN_MIDDLE + return FALSE; + } + + } + if (buttons & java_awt_event_InputEvent_BUTTON3_MASK + || buttons & java_awt_event_InputEvent_BUTTON3_DOWN_MASK) { + if (!callRemoteDesktopNotifyPointerButton(isPress, 0x111)) { // BTN_RIGHT + return FALSE; + } + } + + return TRUE; +} + +gboolean remoteDesktopMouseWheel(int wheelAmt) { + DEBUG_SCREENCAST("MouseWheel %d\n", wheelAmt); + + GVariantBuilder builder; + gtk->g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + GVariant *params = gtk->g_variant_new("(oa{sv}ui)", portal->screenCastSessionHandle, &builder, 0, wheelAmt); + return callRemoteDesktop("NotifyPointerAxisDiscrete", params); +} + +gboolean remoteDesktopKey(gboolean isPress, int key) { + DEBUG_SCREENCAST("Key%s key %d -> \n", isPress ? "Press" : "Release", key); + + GVariantBuilder builder; + gtk->g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + GVariant *params = gtk->g_variant_new ("(oa{sv}iu)", portal->screenCastSessionHandle, &builder, key, isPress); + return callRemoteDesktop("NotifyKeyboardKeysym", params); +} + +#endif diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/screencast_portal.h b/src/java.desktop/unix/native/libawt_xawt/awt/screencast_portal.h index 9ac210217fff3..706337610f188 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/screencast_portal.h +++ b/src/java.desktop/unix/native/libawt_xawt/awt/screencast_portal.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,11 +36,21 @@ #define PORTAL_REQUEST_TEMPLATE "/org/freedesktop/portal/desktop/" \ "request/%s/awtPipewire%lu" +#define PORTAL_DESKTOP_BUS_NAME "org.freedesktop.portal.Desktop" +#define PORTAL_DESKTOP_OBJECT_PATH "/org/freedesktop/portal/desktop" + +#define PORTAL_IFACE_REQUEST "org.freedesktop.portal.Request" +#define PORTAL_IFACE_SESSION "org.freedesktop.portal.Session" +#define PORTAL_IFACE_SCREENCAST "org.freedesktop.portal.ScreenCast" +#define PORTAL_IFACE_REMOTE_DESKTOP "org.freedesktop.portal.RemoteDesktop" + +#define PORTAL_MIN_VERSION_SCREENCAST 4 +#define PORTAL_MIN_VERSION_REMOTE_DESKTOP 2 + void debug_screencast(const char *__restrict fmt, ...); -int getPipewireFd(const gchar *token, - GdkRectangle *affectedBounds, - gint affectedBoundsLength); +gboolean initAndStartSession(const gchar *token, int *retVal); +int getPipewireFd(GdkRectangle *affectedBounds, gint affectedBoundsLength); void portalScreenCastCleanup(); @@ -48,8 +58,14 @@ gboolean initXdgDesktopPortal(); void errHandle(GError *error, const gchar *functionName, int lineNum); +gboolean remoteDesktopMouseMove(int x, int y); +gboolean remoteDesktopMouseWheel(int wheelAmt); +gboolean remoteDesktopMouse(gboolean isPress, int buttons); +gboolean remoteDesktopKey(gboolean isPress, int key); + struct XdgDesktopPortalApi { GDBusConnection *connection; + GDBusProxy *remoteDesktopProxy; GDBusProxy *screenCastProxy; gchar *senderName; char *screenCastSessionHandle; @@ -66,8 +82,15 @@ typedef enum { RESULT_ERROR = -1, RESULT_DENIED = -11, RESULT_OUT_OF_BOUNDS = -12, + RESULT_NO_STREAMS = -13, } ScreenCastResult; +typedef enum { + XDG_METHOD_SCREENCAST = 0, + XDG_METHOD_REMOTE_DESKTOP = 1, +} XdgPortalMethod; + + struct StartHelper { const gchar *token; ScreenCastResult result; diff --git a/src/java.desktop/unix/native/libpipewire/include/pipewire/context.h b/src/java.desktop/unix/native/libpipewire/include/pipewire/context.h index b02baf6c4c55f..7e1a2b29b9498 100644 --- a/src/java.desktop/unix/native/libpipewire/include/pipewire/context.h +++ b/src/java.desktop/unix/native/libpipewire/include/pipewire/context.h @@ -43,6 +43,7 @@ struct pw_context; struct pw_global; struct pw_impl_client; +struct pw_impl_node; #include #include @@ -50,7 +51,7 @@ struct pw_impl_client; /** context events emitted by the context object added with \ref pw_context_add_listener */ struct pw_context_events { -#define PW_VERSION_CONTEXT_EVENTS 0 +#define PW_VERSION_CONTEXT_EVENTS 1 uint32_t version; /** The context is being destroyed */ @@ -63,12 +64,24 @@ struct pw_context_events { void (*global_added) (void *data, struct pw_global *global); /** a global object was removed */ void (*global_removed) (void *data, struct pw_global *global); + + /** a driver was added, since 0.3.75 version:1 */ + void (*driver_added) (void *data, struct pw_impl_node *node); + /** a driver was removed, since 0.3.75 version:1 */ + void (*driver_removed) (void *data, struct pw_impl_node *node); }; -/** Make a new context object for a given main_loop. Ownership of the properties is taken */ -struct pw_context * pw_context_new(struct pw_loop *main_loop, /**< a main loop to run in */ - struct pw_properties *props, /**< extra properties */ - size_t user_data_size /**< extra user data size */); +/** Make a new context object for a given main_loop. Ownership of the properties is taken, even + * if the function returns NULL. + * + * \param main_loop A main loop to run in. This must stay alive unil pw_context_destroy() is called. + * \param props extra properties + * \param user_data_size extra user data size + * \return The context object on success, or NULL on failure, in which case errno is set. + * */ +struct pw_context * pw_context_new(struct pw_loop *main_loop, + struct pw_properties *props, + size_t user_data_size); /** destroy a context object, all resources except the main_loop will be destroyed */ void pw_context_destroy(struct pw_context *context); @@ -113,15 +126,27 @@ int pw_context_conf_section_match_rules(struct pw_context *context, const char * /** Get the context support objects */ const struct spa_support *pw_context_get_support(struct pw_context *context, uint32_t *n_support); -/** get the context main loop */ +/** Get the context main loop. Returns the value passed to pw_context_new(). */ struct pw_loop *pw_context_get_main_loop(struct pw_context *context); -/** get the context data loop. Since 0.3.56 */ +/** Get the context data loop. This loop runs on the realtime thread. This + * acquires a loop from the generic data.rt class. Use pw_context_acquire_loop() instead. + * Since 0.3.56 */ struct pw_data_loop *pw_context_get_data_loop(struct pw_context *context); +/** Get a data-loop. + * Since 1.1.0 */ +struct pw_loop *pw_context_acquire_loop(struct pw_context *context, const struct spa_dict *props); +/** Release a data-loop. + * Since 1.1.0 */ +void pw_context_release_loop(struct pw_context *context, struct pw_loop *loop); + /** Get the work queue from the context: Since 0.3.26 */ struct pw_work_queue *pw_context_get_work_queue(struct pw_context *context); +/** Get the memory pool from the context: Since 0.3.74 */ +struct pw_mempool *pw_context_get_mempool(struct pw_context *context); + /** Iterate the globals of the context. The callback should return * 0 to fetch the next item, any other value stops the iteration and returns * the value. When all callbacks return 0, this function returns 0 when all @@ -130,7 +155,10 @@ int pw_context_for_each_global(struct pw_context *context, int (*callback) (void *data, struct pw_global *global), void *data); -/** Find a context global by id */ +/** Find a context global by id. + * + * \return The global on success, or NULL on failure. If id is \ref PW_ID_CORE, + * this function will always return a non-NULL value. */ struct pw_global *pw_context_find_global(struct pw_context *context, /**< the context */ uint32_t id /**< the global id */); @@ -140,6 +168,7 @@ int pw_context_add_spa_lib(struct pw_context *context, const char *factory_regex /** find the library name for a spa factory */ const char * pw_context_find_spa_lib(struct pw_context *context, const char *factory_name); +/** Load a SPA handle from a context. On failure returns NULL and sets errno. */ struct spa_handle *pw_context_load_spa_handle(struct pw_context *context, const char *factory_name, const struct spa_dict *info); @@ -160,9 +189,21 @@ int pw_context_register_export_type(struct pw_context *context, struct pw_export /** find information about registered export type */ const struct pw_export_type *pw_context_find_export_type(struct pw_context *context, const char *type); -/** add an object to the context */ +/** add an object to the context + * + * \param context The context. + * \param type The type of the object, usually a `TYPE_INTERFACE_` value. + * \param value The object value. Must last as long as the context and must + * be of the type corresponding to the type. + * \return A negative number on failure (out of memory). + * */ int pw_context_set_object(struct pw_context *context, const char *type, void *value); -/** get an object from the context */ +/** get an object from the context + * + * \param context The context. + * \param type The string corresponding to the object's interface. + * \return The object, or NULL if the object does not exist. + * */ void *pw_context_get_object(struct pw_context *context, const char *type); /** diff --git a/src/java.desktop/unix/native/libpipewire/include/pipewire/core.h b/src/java.desktop/unix/native/libpipewire/include/pipewire/core.h index 23a9e16dfbf86..1acfded4b07ad 100644 --- a/src/java.desktop/unix/native/libpipewire/include/pipewire/core.h +++ b/src/java.desktop/unix/native/libpipewire/include/pipewire/core.h @@ -14,6 +14,8 @@ extern "C" { #include +#include + /** \defgroup pw_core Core * * \brief The core global object. @@ -34,11 +36,20 @@ extern "C" { #define PW_TYPE_INTERFACE_Core PW_TYPE_INFO_INTERFACE_BASE "Core" #define PW_TYPE_INTERFACE_Registry PW_TYPE_INFO_INTERFACE_BASE "Registry" +#define PW_CORE_PERM_MASK PW_PERM_R|PW_PERM_X|PW_PERM_M + #define PW_VERSION_CORE 4 struct pw_core; #define PW_VERSION_REGISTRY 3 struct pw_registry; +#ifndef PW_API_CORE_IMPL +#define PW_API_CORE_IMPL static inline +#endif +#ifndef PW_API_REGISTRY_IMPL +#define PW_API_REGISTRY_IMPL static inline +#endif + /** The default remote name to connect to */ #define PW_DEFAULT_REMOTE "pipewire-0" @@ -67,11 +78,13 @@ struct pw_core_info { #include #include -/** Update an existing \ref pw_core_info with \a update with reset */ +/** Update an existing \ref pw_core_info with \a update with reset. When info is NULL, + * a new one will be allocated. Returns NULL on failure. */ struct pw_core_info * pw_core_info_update(struct pw_core_info *info, const struct pw_core_info *update); -/** Update an existing \ref pw_core_info with \a update */ +/** Update an existing \ref pw_core_info with \a update. When info is NULL, a new one + * will be allocated. Returns NULL on failure */ struct pw_core_info * pw_core_info_merge(struct pw_core_info *info, const struct pw_core_info *update, bool reset); @@ -162,6 +175,9 @@ struct pw_core_events { * global ID. It is emitted before the global becomes visible in the * registry. * + * The bound_props event is an enhanced version of this event that + * also contains the extra global properties. + * * \param id bound object ID * \param global_id the global id bound to */ @@ -190,6 +206,21 @@ struct pw_core_events { */ void (*remove_mem) (void *data, uint32_t id); + /** + * Notify an object binding + * + * This event is emitted when a local object ID is bound to a + * global ID. It is emitted before the global becomes visible in the + * registry. + * + * This is an enhanced version of the bound_id event. + * + * \param id bound object ID + * \param global_id the global id bound to + * \param props The properties of the new global object. + * + * Since version 4:1 + */ void (*bound_props) (void *data, uint32_t id, uint32_t global_id, const struct spa_dict *props); }; @@ -223,6 +254,8 @@ struct pw_core_methods { * Start a conversation with the server. This will send * the core info and will destroy all resources for the client * (except the core and client resource). + * + * This requires X permissions on the core. */ int (*hello) (void *object, uint32_t version); /** @@ -235,6 +268,8 @@ struct pw_core_methods { * methods and the resulting events have been handled. * * \param seq the seq number passed to the done event + * + * This requires X permissions on the core. */ int (*sync) (void *object, uint32_t id, int seq); /** @@ -243,6 +278,8 @@ struct pw_core_methods { * Reply to the server ping event with the same seq. * * \param seq the seq number received in the ping event + * + * This requires X permissions on the core. */ int (*pong) (void *object, uint32_t id, int seq); /** @@ -257,9 +294,11 @@ struct pw_core_methods { * This method is usually also emitted on the resource object with * \a id. * - * \param id object where the error occurred + * \param id resource id where the error occurred * \param res error code * \param message error description + * + * This requires X permissions on the core. */ int (*error) (void *object, uint32_t id, int seq, int res, const char *message); /** @@ -269,6 +308,8 @@ struct pw_core_methods { * the global objects available from the PipeWire server * \param version the client version * \param user_data_size extra size + * + * This requires X permissions on the core. */ struct pw_registry * (*get_registry) (void *object, uint32_t version, size_t user_data_size); @@ -281,6 +322,8 @@ struct pw_core_methods { * \param version the version of the interface * \param props extra properties * \param user_data_size extra size + * + * This requires X permissions on the core. */ void * (*create_object) (void *object, const char *factory_name, @@ -294,27 +337,57 @@ struct pw_core_methods { * Destroy the server resource for the given proxy. * * \param obj the proxy to destroy + * + * This requires X permissions on the core. */ int (*destroy) (void *object, void *proxy); }; -#define pw_core_method(o,method,version,...) \ -({ \ - int _res = -ENOTSUP; \ - spa_interface_call_res((struct spa_interface*)o, \ - struct pw_core_methods, _res, \ - method, version, ##__VA_ARGS__); \ - _res; \ -}) -#define pw_core_add_listener(c,...) pw_core_method(c,add_listener,0,__VA_ARGS__) -#define pw_core_hello(c,...) pw_core_method(c,hello,0,__VA_ARGS__) -#define pw_core_sync(c,...) pw_core_method(c,sync,0,__VA_ARGS__) -#define pw_core_pong(c,...) pw_core_method(c,pong,0,__VA_ARGS__) -#define pw_core_error(c,...) pw_core_method(c,error,0,__VA_ARGS__) - - -static inline +/** \copydoc pw_core_methods.add_listener + * \sa pw_core_methods.add_listener */ +PW_API_CORE_IMPL int pw_core_add_listener(struct pw_core *object, + struct spa_hook *listener, + const struct pw_core_events *events, + void *data) +{ + return spa_api_method_r(int, -ENOTSUP, + pw_core, (struct spa_interface*)object, add_listener, 0, + listener, events, data); +} +/** \copydoc pw_core_methods.hello + * \sa pw_core_methods.hello */ +PW_API_CORE_IMPL int pw_core_hello(struct pw_core *object, uint32_t version) +{ + return spa_api_method_r(int, -ENOTSUP, + pw_core, (struct spa_interface*)object, hello, 0, + version); +} +/** \copydoc pw_core_methods.sync + * \sa pw_core_methods.sync */ +PW_API_CORE_IMPL int pw_core_sync(struct pw_core *object, uint32_t id, int seq) +{ + return spa_api_method_r(int, -ENOTSUP, + pw_core, (struct spa_interface*)object, sync, 0, + id, seq); +} +/** \copydoc pw_core_methods.pong + * \sa pw_core_methods.pong */ +PW_API_CORE_IMPL int pw_core_pong(struct pw_core *object, uint32_t id, int seq) +{ + return spa_api_method_r(int, -ENOTSUP, + pw_core, (struct spa_interface*)object, pong, 0, + id, seq); +} +/** \copydoc pw_core_methods.error + * \sa pw_core_methods.error */ +PW_API_CORE_IMPL int pw_core_error(struct pw_core *object, uint32_t id, int seq, int res, const char *message) +{ + return spa_api_method_r(int, -ENOTSUP, + pw_core, (struct spa_interface*)object, error, 0, + id, seq, res, message); +} +PW_API_CORE_IMPL SPA_PRINTF_FUNC(5, 0) int pw_core_errorv(struct pw_core *core, uint32_t id, int seq, int res, const char *message, va_list args) @@ -325,7 +398,7 @@ pw_core_errorv(struct pw_core *core, uint32_t id, int seq, return pw_core_error(core, id, seq, res, buffer); } -static inline +PW_API_CORE_IMPL SPA_PRINTF_FUNC(5, 6) int pw_core_errorf(struct pw_core *core, uint32_t id, int seq, int res, const char *message, ...) @@ -338,17 +411,18 @@ pw_core_errorf(struct pw_core *core, uint32_t id, int seq, return r; } -static inline struct pw_registry * +/** \copydoc pw_core_methods.get_registry + * \sa pw_core_methods.get_registry */ +PW_API_CORE_IMPL struct pw_registry * pw_core_get_registry(struct pw_core *core, uint32_t version, size_t user_data_size) { - struct pw_registry *res = NULL; - spa_interface_call_res((struct spa_interface*)core, - struct pw_core_methods, res, - get_registry, 0, version, user_data_size); - return res; + return spa_api_method_r(struct pw_registry*, NULL, + pw_core, (struct spa_interface*)core, get_registry, 0, + version, user_data_size); } - -static inline void * +/** \copydoc pw_core_methods.create_object + * \sa pw_core_methods.create_object */ +PW_API_CORE_IMPL void * pw_core_create_object(struct pw_core *core, const char *factory_name, const char *type, @@ -356,15 +430,18 @@ pw_core_create_object(struct pw_core *core, const struct spa_dict *props, size_t user_data_size) { - void *res = NULL; - spa_interface_call_res((struct spa_interface*)core, - struct pw_core_methods, res, - create_object, 0, factory_name, - type, version, props, user_data_size); - return res; + return spa_api_method_r(void*, NULL, + pw_core, (struct spa_interface*)core, create_object, 0, + factory_name, type, version, props, user_data_size); +} +/** \copydoc pw_core_methods.destroy + * \sa pw_core_methods.destroy */ +PW_API_CORE_IMPL void +pw_core_destroy(struct pw_core *core, void *proxy) +{ + spa_api_method_v(pw_core, (struct spa_interface*)core, destroy, 0, + proxy); } - -#define pw_core_destroy(c,...) pw_core_method(c,destroy,0,__VA_ARGS__) /** * \} @@ -474,36 +551,44 @@ struct pw_registry_methods { * * Try to destroy the global object. * - * \param id the global id to destroy + * \param id the global id to destroy. The client needs X permissions + * on the global. */ int (*destroy) (void *object, uint32_t id); }; -#define pw_registry_method(o,method,version,...) \ -({ \ - int _res = -ENOTSUP; \ - spa_interface_call_res((struct spa_interface*)o, \ - struct pw_registry_methods, _res, \ - method, version, ##__VA_ARGS__); \ - _res; \ -}) /** Registry */ -#define pw_registry_add_listener(p,...) pw_registry_method(p,add_listener,0,__VA_ARGS__) - -static inline void * +/** \copydoc pw_registry_methods.add_listener + * \sa pw_registry_methods.add_listener */ +PW_API_REGISTRY_IMPL int pw_registry_add_listener(struct pw_registry *registry, + struct spa_hook *listener, + const struct pw_registry_events *events, + void *data) +{ + return spa_api_method_r(int, -ENOTSUP, + pw_registry, (struct spa_interface*)registry, add_listener, 0, + listener, events, data); +} +/** \copydoc pw_registry_methods.bind + * \sa pw_registry_methods.bind */ +PW_API_REGISTRY_IMPL void * pw_registry_bind(struct pw_registry *registry, uint32_t id, const char *type, uint32_t version, size_t user_data_size) { - void *res = NULL; - spa_interface_call_res((struct spa_interface*)registry, - struct pw_registry_methods, res, - bind, 0, id, type, version, user_data_size); - return res; + return spa_api_method_r(void*, NULL, + pw_registry, (struct spa_interface*)registry, bind, 0, + id, type, version, user_data_size); +} +/** \copydoc pw_registry_methods.destroy + * \sa pw_registry_methods.destroy */ +PW_API_REGISTRY_IMPL int +pw_registry_destroy(struct pw_registry *registry, uint32_t id) +{ + return spa_api_method_r(int, -ENOTSUP, + pw_registry, (struct spa_interface*)registry, destroy, 0, id); } - -#define pw_registry_destroy(p,...) pw_registry_method(p,destroy,0,__VA_ARGS__) /** * \} diff --git a/src/java.desktop/unix/native/libpipewire/include/pipewire/keys.h b/src/java.desktop/unix/native/libpipewire/include/pipewire/keys.h index b7de764657a94..67ea3f9cce229 100644 --- a/src/java.desktop/unix/native/libpipewire/include/pipewire/keys.h +++ b/src/java.desktop/unix/native/libpipewire/include/pipewire/keys.h @@ -38,6 +38,14 @@ extern "C" { #define PW_KEY_SEC_GID "pipewire.sec.gid" /**< client gid, set by protocol*/ #define PW_KEY_SEC_LABEL "pipewire.sec.label" /**< client security label, set by protocol*/ +#define PW_KEY_SEC_SOCKET "pipewire.sec.socket" /**< client socket name, set by protocol */ + +#define PW_KEY_SEC_ENGINE "pipewire.sec.engine" /**< client secure context engine, set by protocol. + * This can also be set by a client when making a + * new security context. */ +#define PW_KEY_SEC_APP_ID "pipewire.sec.app-id" /**< client secure application id */ +#define PW_KEY_SEC_INSTANCE_ID "pipewire.sec.instance-id" /**< client secure instance id */ + #define PW_KEY_LIBRARY_NAME_SYSTEM "library.name.system" /**< name of the system library to use */ #define PW_KEY_LIBRARY_NAME_LOOP "library.name.loop" /**< name of the loop library to use */ #define PW_KEY_LIBRARY_NAME_DBUS "library.name.dbus" /**< name of the dbus library to use */ @@ -52,7 +60,8 @@ extern "C" { #define PW_KEY_OBJECT_LINGER "object.linger" /**< the object lives on even after the client * that created it has been destroyed */ #define PW_KEY_OBJECT_REGISTER "object.register" /**< If the object should be registered. */ - +#define PW_KEY_OBJECT_EXPORT "object.export" /**< If the object should be exported, + * since 0.3.72 */ /* config */ #define PW_KEY_CONFIG_PREFIX "config.prefix" /**< a config prefix directory */ @@ -60,6 +69,12 @@ extern "C" { #define PW_KEY_CONFIG_OVERRIDE_PREFIX "config.override.prefix" /**< a config override prefix directory */ #define PW_KEY_CONFIG_OVERRIDE_NAME "config.override.name" /**< a config override file name */ +/* loop */ +#define PW_KEY_LOOP_NAME "loop.name" /**< the name of a loop */ +#define PW_KEY_LOOP_CLASS "loop.class" /**< the classes this loop handles, array of strings */ +#define PW_KEY_LOOP_RT_PRIO "loop.rt-prio" /**< realtime priority of the loop */ +#define PW_KEY_LOOP_CANCEL "loop.cancel" /**< if the loop can be canceled */ + /* context */ #define PW_KEY_CONTEXT_PROFILE_MODULES "context.profile.modules" /**< a context profile for modules, deprecated */ #define PW_KEY_USER_NAME "context.user-name" /**< The user name that runs pipewire */ @@ -87,7 +102,9 @@ extern "C" { /* remote keys */ #define PW_KEY_REMOTE_NAME "remote.name" /**< The name of the remote to connect to, * default pipewire-0, overwritten by - * env(PIPEWIRE_REMOTE) */ + * env(PIPEWIRE_REMOTE). May also be + * a SPA-JSON array of sockets, to be tried + * in order. */ #define PW_KEY_REMOTE_INTENTION "remote.intention" /**< The intention of the remote connection, * "generic", "screencast" */ @@ -132,7 +149,14 @@ extern "C" { #define PW_KEY_NODE_SESSION "node.session" /**< the session id this node is part of */ #define PW_KEY_NODE_GROUP "node.group" /**< the group id this node is part of. Nodes * in the same group are always scheduled - * with the same driver. */ + * with the same driver. Can be an array of + * group names. */ +#define PW_KEY_NODE_SYNC_GROUP "node.sync-group" /**< the sync group this node is part of. Nodes + * in the same sync group are always scheduled + * together with the same driver when the sync + * is active. Can be an array of sync names. */ +#define PW_KEY_NODE_SYNC "node.sync" /**< if the sync-group is active or not */ +#define PW_KEY_NODE_TRANSPORT "node.transport" /**< if the transport is active or not */ #define PW_KEY_NODE_EXCLUSIVE "node.exclusive" /**< node wants exclusive access to resources */ #define PW_KEY_NODE_AUTOCONNECT "node.autoconnect" /**< node wants to be automatically connected * to a compatible node */ @@ -163,7 +187,23 @@ extern "C" { #define PW_KEY_NODE_SUSPEND_ON_IDLE "node.suspend-on-idle" /**< suspend the node when idle */ #define PW_KEY_NODE_CACHE_PARAMS "node.cache-params" /**< cache the node params */ #define PW_KEY_NODE_TRANSPORT_SYNC "node.transport.sync" /**< the node handles transport sync */ -#define PW_KEY_NODE_DRIVER "node.driver" /**< node can drive the graph */ +#define PW_KEY_NODE_DRIVER "node.driver" /**< node can drive the graph. When the node is + * selected as the driver, it needs to start + * the graph periodically. */ +#define PW_KEY_NODE_SUPPORTS_LAZY "node.supports-lazy" /**< the node can be a lazy driver. It will listen + * to RequestProcess commands and take them into + * account when deciding to start the graph. + * A value of 0 disables support, a value of > 0 + * enables with increasing preference. */ +#define PW_KEY_NODE_SUPPORTS_REQUEST "node.supports-request" /**< The node supports emiting RequestProcess events + * when it wants the graph to be scheduled. + * A value of 0 disables support, a value of > 0 + * enables with increasing preference. */ +#define PW_KEY_NODE_DRIVER_ID "node.driver-id" /**< the node id of the node assigned as driver + * for this node */ +#define PW_KEY_NODE_ASYNC "node.async" /**< the node wants async scheduling */ +#define PW_KEY_NODE_LOOP_NAME "node.loop.name" /**< the loop name fnmatch pattern to run in */ +#define PW_KEY_NODE_LOOP_CLASS "node.loop.class" /**< the loop class fnmatch pattern to run in */ #define PW_KEY_NODE_STREAM "node.stream" /**< node is a stream, the server side should * add a converter */ #define PW_KEY_NODE_VIRTUAL "node.virtual" /**< the node is some sort of virtual @@ -172,17 +212,20 @@ extern "C" { * on output/input/all ports when the value is * "out"/"in"/"true" respectively */ #define PW_KEY_NODE_LINK_GROUP "node.link-group" /**< the node is internally linked to - * nodes with the same link-group */ + * nodes with the same link-group. Can be an + * array of group names. */ #define PW_KEY_NODE_NETWORK "node.network" /**< the node is on a network */ #define PW_KEY_NODE_TRIGGER "node.trigger" /**< the node is not scheduled automatically * based on the dependencies in the graph * but it will be triggered explicitly. */ -#define PW_KEY_NODE_CHANNELNAMES "node.channel-names" /**< names of node's - * channels (unrelated to positions) */ -#define PW_KEY_NODE_DEVICE_PORT_NAME_PREFIX "node.device-port-name-prefix" /** override - * port name prefix for device ports, like capture and playback - * or disable the prefix completely if an empty string is provided */ - +#define PW_KEY_NODE_CHANNELNAMES "node.channel-names" /**< names of node's + * channels (unrelated to positions) */ +#define PW_KEY_NODE_DEVICE_PORT_NAME_PREFIX \ + "node.device-port-name-prefix" /**< override port name prefix for + * device ports, like capture and + * playback or disable the prefix + * completely if an empty string + * is provided */ /** Port keys */ #define PW_KEY_PORT_ID "port.id" /**< port id */ #define PW_KEY_PORT_NAME "port.name" /**< port name */ @@ -197,6 +240,8 @@ extern "C" { #define PW_KEY_PORT_EXTRA "port.extra" /**< api specific extra port info, API name * should be prefixed. "jack:flags:56" */ #define PW_KEY_PORT_PASSIVE "port.passive" /**< the ports wants passive links, since 0.3.67 */ +#define PW_KEY_PORT_IGNORE_LATENCY "port.ignore-latency" /**< latency ignored by peers, since 0.3.71 */ +#define PW_KEY_PORT_GROUP "port.group" /**< the port group of the port 1.2.0 */ /** link properties */ #define PW_KEY_LINK_ID "link.id" /**< a link id */ @@ -210,6 +255,7 @@ extern "C" { #define PW_KEY_LINK_FEEDBACK "link.feedback" /**< indicate that a link is a feedback * link and the target will receive data * in the next cycle */ +#define PW_KEY_LINK_ASYNC "link.async" /**< the link is using async io */ /** device properties */ #define PW_KEY_DEVICE_ID "device.id" /**< device id */ @@ -260,6 +306,7 @@ extern "C" { #define PW_KEY_MODULE_USAGE "module.usage" /**< a human readable usage description of * the module's arguments. */ #define PW_KEY_MODULE_VERSION "module.version" /**< a version string for the module. */ +#define PW_KEY_MODULE_DEPRECATED "module.deprecated" /**< the module is deprecated with this message */ /** Factory properties */ #define PW_KEY_FACTORY_ID "factory.id" /**< the factory id */ @@ -274,7 +321,10 @@ extern "C" { #define PW_KEY_STREAM_LATENCY_MAX "stream.latency.max" /**< The maximum latency of the stream */ #define PW_KEY_STREAM_MONITOR "stream.monitor" /**< Indicates that the stream is monitoring * and might select a less accurate but faster - * conversion algorithm. */ + * conversion algorithm. Monitor streams are also + * ignored when calculating the latency of their peer + * ports (since 0.3.71). + */ #define PW_KEY_STREAM_DONT_REMIX "stream.dont-remix" /**< don't remix channels */ #define PW_KEY_STREAM_CAPTURE_SINK "stream.capture.sink" /**< Try to capture the sink output instead of * source output */ @@ -292,6 +342,7 @@ extern "C" { #define PW_KEY_MEDIA_NAME "media.name" /**< media name. Ex: "Pink Floyd: Time" */ #define PW_KEY_MEDIA_TITLE "media.title" /**< title. Ex: "Time" */ #define PW_KEY_MEDIA_ARTIST "media.artist" /**< artist. Ex: "Pink Floyd" */ +#define PW_KEY_MEDIA_ALBUM "media.album" /**< album. Ex: "Dark Side of the Moon" */ #define PW_KEY_MEDIA_COPYRIGHT "media.copyright" /**< copyright string */ #define PW_KEY_MEDIA_SOFTWARE "media.software" /**< generator software */ #define PW_KEY_MEDIA_LANGUAGE "media.language" /**< language in POSIX format. Ex: en_GB */ @@ -327,9 +378,11 @@ extern "C" { # ifdef PW_ENABLE_DEPRECATED # define PW_KEY_PRIORITY_MASTER "priority.master" /**< deprecated, use priority.driver */ # define PW_KEY_NODE_TARGET "node.target" /**< deprecated since 0.3.64, use target.object. */ +# define PW_KEY_LOOP_RETRY_TIMEOUT "loop.retry-timeout" /**< deprecated since 1.3.0 */ # else # define PW_KEY_PRIORITY_MASTER PW_DEPRECATED("priority.master") # define PW_KEY_NODE_TARGET PW_DEPRECATED("node.target") +# define PW_KEY_LOOP_RETRY_TIMEOUT PW_DEPRECATED("loop.retry-timeout") # endif /* PW_ENABLE_DEPRECATED */ #endif /* PW_REMOVE_DEPRECATED */ diff --git a/src/java.desktop/unix/native/libpipewire/include/pipewire/loop.h b/src/java.desktop/unix/native/libpipewire/include/pipewire/loop.h index b3a5c55b83edb..94b2dc2780b3b 100644 --- a/src/java.desktop/unix/native/libpipewire/include/pipewire/loop.h +++ b/src/java.desktop/unix/native/libpipewire/include/pipewire/loop.h @@ -17,6 +17,8 @@ extern "C" { * PipeWire loop object provides an implementation of * the spa loop interfaces. It can be used to implement various * event loops. + * + * The members of \ref pw_loop are read-only. */ /** @@ -29,35 +31,118 @@ struct pw_loop { struct spa_loop *loop; /**< wrapped loop */ struct spa_loop_control *control; /**< loop control */ struct spa_loop_utils *utils; /**< loop utils */ + const char *name; }; +#ifndef PW_API_LOOP_IMPL +#define PW_API_LOOP_IMPL static inline +#endif + struct pw_loop * pw_loop_new(const struct spa_dict *props); void pw_loop_destroy(struct pw_loop *loop); -#define pw_loop_add_source(l,...) spa_loop_add_source((l)->loop,__VA_ARGS__) -#define pw_loop_update_source(l,...) spa_loop_update_source((l)->loop,__VA_ARGS__) -#define pw_loop_remove_source(l,...) spa_loop_remove_source((l)->loop,__VA_ARGS__) -#define pw_loop_invoke(l,...) spa_loop_invoke((l)->loop,__VA_ARGS__) - -#define pw_loop_get_fd(l) spa_loop_control_get_fd((l)->control) -#define pw_loop_add_hook(l,...) spa_loop_control_add_hook((l)->control,__VA_ARGS__) -#define pw_loop_enter(l) spa_loop_control_enter((l)->control) -#define pw_loop_iterate(l,...) spa_loop_control_iterate((l)->control,__VA_ARGS__) -#define pw_loop_leave(l) spa_loop_control_leave((l)->control) - -#define pw_loop_add_io(l,...) spa_loop_utils_add_io((l)->utils,__VA_ARGS__) -#define pw_loop_update_io(l,...) spa_loop_utils_update_io((l)->utils,__VA_ARGS__) -#define pw_loop_add_idle(l,...) spa_loop_utils_add_idle((l)->utils,__VA_ARGS__) -#define pw_loop_enable_idle(l,...) spa_loop_utils_enable_idle((l)->utils,__VA_ARGS__) -#define pw_loop_add_event(l,...) spa_loop_utils_add_event((l)->utils,__VA_ARGS__) -#define pw_loop_signal_event(l,...) spa_loop_utils_signal_event((l)->utils,__VA_ARGS__) -#define pw_loop_add_timer(l,...) spa_loop_utils_add_timer((l)->utils,__VA_ARGS__) -#define pw_loop_update_timer(l,...) spa_loop_utils_update_timer((l)->utils,__VA_ARGS__) -#define pw_loop_add_signal(l,...) spa_loop_utils_add_signal((l)->utils,__VA_ARGS__) -#define pw_loop_destroy_source(l,...) spa_loop_utils_destroy_source((l)->utils,__VA_ARGS__) +int pw_loop_set_name(struct pw_loop *loop, const char *name); + +PW_API_LOOP_IMPL int pw_loop_add_source(struct pw_loop *object, struct spa_source *source) +{ + return spa_loop_add_source(object->loop, source); +} +PW_API_LOOP_IMPL int pw_loop_update_source(struct pw_loop *object, struct spa_source *source) +{ + return spa_loop_update_source(object->loop, source); +} +PW_API_LOOP_IMPL int pw_loop_remove_source(struct pw_loop *object, struct spa_source *source) +{ + return spa_loop_remove_source(object->loop, source); +} +PW_API_LOOP_IMPL int pw_loop_invoke(struct pw_loop *object, + spa_invoke_func_t func, uint32_t seq, const void *data, + size_t size, bool block, void *user_data) +{ + return spa_loop_invoke(object->loop, func, seq, data, size, block, user_data); +} + +PW_API_LOOP_IMPL int pw_loop_get_fd(struct pw_loop *object) +{ + return spa_loop_control_get_fd(object->control); +} +PW_API_LOOP_IMPL void pw_loop_add_hook(struct pw_loop *object, + struct spa_hook *hook, const struct spa_loop_control_hooks *hooks, + void *data) +{ + spa_loop_control_add_hook(object->control, hook, hooks, data); +} +PW_API_LOOP_IMPL void pw_loop_enter(struct pw_loop *object) +{ + spa_loop_control_enter(object->control); +} +PW_API_LOOP_IMPL void pw_loop_leave(struct pw_loop *object) +{ + spa_loop_control_leave(object->control); +} +PW_API_LOOP_IMPL int pw_loop_iterate(struct pw_loop *object, + int timeout) +{ + return spa_loop_control_iterate_fast(object->control, timeout); +} + +PW_API_LOOP_IMPL struct spa_source * +pw_loop_add_io(struct pw_loop *object, int fd, uint32_t mask, + bool close, spa_source_io_func_t func, void *data) +{ + return spa_loop_utils_add_io(object->utils, fd, mask, close, func, data); +} +PW_API_LOOP_IMPL int pw_loop_update_io(struct pw_loop *object, + struct spa_source *source, uint32_t mask) +{ + return spa_loop_utils_update_io(object->utils, source, mask); +} +PW_API_LOOP_IMPL struct spa_source * +pw_loop_add_idle(struct pw_loop *object, bool enabled, + spa_source_idle_func_t func, void *data) +{ + return spa_loop_utils_add_idle(object->utils, enabled, func, data); +} +PW_API_LOOP_IMPL int pw_loop_enable_idle(struct pw_loop *object, + struct spa_source *source, bool enabled) +{ + return spa_loop_utils_enable_idle(object->utils, source, enabled); +} +PW_API_LOOP_IMPL struct spa_source * +pw_loop_add_event(struct pw_loop *object, spa_source_event_func_t func, void *data) +{ + return spa_loop_utils_add_event(object->utils, func, data); +} +PW_API_LOOP_IMPL int pw_loop_signal_event(struct pw_loop *object, + struct spa_source *source) +{ + return spa_loop_utils_signal_event(object->utils, source); +} +PW_API_LOOP_IMPL struct spa_source * +pw_loop_add_timer(struct pw_loop *object, spa_source_timer_func_t func, void *data) +{ + return spa_loop_utils_add_timer(object->utils, func, data); +} +PW_API_LOOP_IMPL int pw_loop_update_timer(struct pw_loop *object, + struct spa_source *source, struct timespec *value, + struct timespec *interval, bool absolute) +{ + return spa_loop_utils_update_timer(object->utils, source, value, interval, absolute); +} +PW_API_LOOP_IMPL struct spa_source * +pw_loop_add_signal(struct pw_loop *object, int signal_number, + spa_source_signal_func_t func, void *data) +{ + return spa_loop_utils_add_signal(object->utils, signal_number, func, data); +} +PW_API_LOOP_IMPL void pw_loop_destroy_source(struct pw_loop *object, + struct spa_source *source) +{ + return spa_loop_utils_destroy_source(object->utils, source); +} /** * \} diff --git a/src/java.desktop/unix/native/libpipewire/include/pipewire/port.h b/src/java.desktop/unix/native/libpipewire/include/pipewire/port.h index bb58cab97f53b..ef48c67c004d5 100644 --- a/src/java.desktop/unix/native/libpipewire/include/pipewire/port.h +++ b/src/java.desktop/unix/native/libpipewire/include/pipewire/port.h @@ -29,9 +29,15 @@ extern "C" { #define PW_TYPE_INTERFACE_Port PW_TYPE_INFO_INTERFACE_BASE "Port" +#define PW_PORT_PERM_MASK PW_PERM_R|PW_PERM_X|PW_PERM_M + #define PW_VERSION_PORT 3 struct pw_port; +#ifndef PW_API_PORT_IMPL +#define PW_API_PORT_IMPL static inline +#endif + /** The direction of a port */ #define pw_direction spa_direction #define PW_DIRECTION_INPUT SPA_DIRECTION_INPUT @@ -115,6 +121,8 @@ struct pw_port_methods { * * \param ids an array of param ids * \param n_ids the number of ids in \a ids + * + * This requires X permissions on the port. */ int (*subscribe_params) (void *object, uint32_t *ids, uint32_t n_ids); @@ -129,24 +137,43 @@ struct pw_port_methods { * \param start the start index or 0 for the first param * \param num the maximum number of params to retrieve * \param filter a param filter or NULL + * + * This requires X permissions on the port. */ int (*enum_params) (void *object, int seq, uint32_t id, uint32_t start, uint32_t num, const struct spa_pod *filter); }; -#define pw_port_method(o,method,version,...) \ -({ \ - int _res = -ENOTSUP; \ - spa_interface_call_res((struct spa_interface*)o, \ - struct pw_port_methods, _res, \ - method, version, ##__VA_ARGS__); \ - _res; \ -}) - -#define pw_port_add_listener(c,...) pw_port_method(c,add_listener,0,__VA_ARGS__) -#define pw_port_subscribe_params(c,...) pw_port_method(c,subscribe_params,0,__VA_ARGS__) -#define pw_port_enum_params(c,...) pw_port_method(c,enum_params,0,__VA_ARGS__) +/** \copydoc pw_port_methods.add_listener + * \sa pw_port_methods.add_listener */ +PW_API_PORT_IMPL int pw_port_add_listener(struct pw_port *object, + struct spa_hook *listener, + const struct pw_port_events *events, + void *data) +{ + return spa_api_method_r(int, -ENOTSUP, + pw_port, (struct spa_interface*)object, add_listener, 0, + listener, events, data); +} +/** \copydoc pw_port_methods.subscribe_params + * \sa pw_port_methods.subscribe_params */ +PW_API_PORT_IMPL int pw_port_subscribe_params(struct pw_port *object, uint32_t *ids, uint32_t n_ids) +{ + return spa_api_method_r(int, -ENOTSUP, + pw_port, (struct spa_interface*)object, subscribe_params, 0, + ids, n_ids); +} +/** \copydoc pw_port_methods.enum_params + * \sa pw_port_methods.enum_params */ +PW_API_PORT_IMPL int pw_port_enum_params(struct pw_port *object, + int seq, uint32_t id, uint32_t start, uint32_t num, + const struct spa_pod *filter) +{ + return spa_api_method_r(int, -ENOTSUP, + pw_port, (struct spa_interface*)object, enum_params, 0, + seq, id, start, num, filter); +} /** * \} diff --git a/src/java.desktop/unix/native/libpipewire/include/pipewire/properties.h b/src/java.desktop/unix/native/libpipewire/include/pipewire/properties.h index 3966be417bcb7..de67f33a50174 100644 --- a/src/java.desktop/unix/native/libpipewire/include/pipewire/properties.h +++ b/src/java.desktop/unix/native/libpipewire/include/pipewire/properties.h @@ -11,9 +11,14 @@ extern "C" { #include +#include #include #include +#ifndef PW_API_PROPERTIES +#define PW_API_PROPERTIES static inline +#endif + /** \defgroup pw_properties Properties * * Properties are used to pass around arbitrary key/value pairs. @@ -40,6 +45,10 @@ pw_properties_new_dict(const struct spa_dict *dict); struct pw_properties * pw_properties_new_string(const char *args); +struct pw_properties * +pw_properties_new_string_checked(const char *args, size_t size, + struct spa_error_location *loc); + struct pw_properties * pw_properties_copy(const struct pw_properties *properties); @@ -51,10 +60,14 @@ int pw_properties_update_ignore(struct pw_properties *props, /* Update props with all key/value pairs from dict */ int pw_properties_update(struct pw_properties *props, const struct spa_dict *dict); + /* Update props with all key/value pairs from str */ int pw_properties_update_string(struct pw_properties *props, const char *str, size_t size); +int pw_properties_update_string_checked(struct pw_properties *props, + const char *str, size_t size, struct spa_error_location *loc); + int pw_properties_add(struct pw_properties *oldprops, const struct spa_dict *dict); int pw_properties_add_keys(struct pw_properties *oldprops, @@ -92,7 +105,7 @@ pw_properties_fetch_int64(const struct pw_properties *properties, const char *ke int pw_properties_fetch_bool(const struct pw_properties *properties, const char *key, bool *value); -static inline uint32_t +PW_API_PROPERTIES uint32_t pw_properties_get_uint32(const struct pw_properties *properties, const char *key, uint32_t deflt) { uint32_t val = deflt; @@ -100,7 +113,7 @@ pw_properties_get_uint32(const struct pw_properties *properties, const char *key return val; } -static inline int32_t +PW_API_PROPERTIES int32_t pw_properties_get_int32(const struct pw_properties *properties, const char *key, int32_t deflt) { int32_t val = deflt; @@ -108,7 +121,7 @@ pw_properties_get_int32(const struct pw_properties *properties, const char *key, return val; } -static inline uint64_t +PW_API_PROPERTIES uint64_t pw_properties_get_uint64(const struct pw_properties *properties, const char *key, uint64_t deflt) { uint64_t val = deflt; @@ -116,7 +129,7 @@ pw_properties_get_uint64(const struct pw_properties *properties, const char *key return val; } -static inline int64_t +PW_API_PROPERTIES int64_t pw_properties_get_int64(const struct pw_properties *properties, const char *key, int64_t deflt) { int64_t val = deflt; @@ -125,7 +138,7 @@ pw_properties_get_int64(const struct pw_properties *properties, const char *key, } -static inline bool +PW_API_PROPERTIES bool pw_properties_get_bool(const struct pw_properties *properties, const char *key, bool deflt) { bool val = deflt; @@ -136,34 +149,38 @@ pw_properties_get_bool(const struct pw_properties *properties, const char *key, const char * pw_properties_iterate(const struct pw_properties *properties, void **state); -#define PW_PROPERTIES_FLAG_NL (1<<0) +#define PW_PROPERTIES_FLAG_NL (1<<0) +#define PW_PROPERTIES_FLAG_RECURSE (1<<1) +#define PW_PROPERTIES_FLAG_ENCLOSE (1<<2) +#define PW_PROPERTIES_FLAG_ARRAY (1<<3) +#define PW_PROPERTIES_FLAG_COLORS (1<<4) int pw_properties_serialize_dict(FILE *f, const struct spa_dict *dict, uint32_t flags); -static inline bool pw_properties_parse_bool(const char *value) { +PW_API_PROPERTIES bool pw_properties_parse_bool(const char *value) { return spa_atob(value); } -static inline int pw_properties_parse_int(const char *value) { +PW_API_PROPERTIES int pw_properties_parse_int(const char *value) { int v; return spa_atoi32(value, &v, 0) ? v: 0; } -static inline int64_t pw_properties_parse_int64(const char *value) { +PW_API_PROPERTIES int64_t pw_properties_parse_int64(const char *value) { int64_t v; return spa_atoi64(value, &v, 0) ? v : 0; } -static inline uint64_t pw_properties_parse_uint64(const char *value) { +PW_API_PROPERTIES uint64_t pw_properties_parse_uint64(const char *value) { uint64_t v; return spa_atou64(value, &v, 0) ? v : 0; } -static inline float pw_properties_parse_float(const char *value) { +PW_API_PROPERTIES float pw_properties_parse_float(const char *value) { float v; return spa_atof(value, &v) ? v : 0.0f; } -static inline double pw_properties_parse_double(const char *value) { +PW_API_PROPERTIES double pw_properties_parse_double(const char *value) { double v; return spa_atod(value, &v) ? v : 0.0; } @@ -172,6 +189,10 @@ static inline double pw_properties_parse_double(const char *value) { * \} */ +SPA_DEFINE_AUTOPTR_CLEANUP(pw_properties, struct pw_properties, { + spa_clear_ptr(*thing, pw_properties_free); +}) + #ifdef __cplusplus } #endif diff --git a/src/java.desktop/unix/native/libpipewire/include/pipewire/protocol.h b/src/java.desktop/unix/native/libpipewire/include/pipewire/protocol.h index f119a31b5db46..f306979877a11 100644 --- a/src/java.desktop/unix/native/libpipewire/include/pipewire/protocol.h +++ b/src/java.desktop/unix/native/libpipewire/include/pipewire/protocol.h @@ -81,7 +81,7 @@ struct pw_protocol_marshal { }; struct pw_protocol_implementation { -#define PW_VERSION_PROTOCOL_IMPLEMENTATION 0 +#define PW_VERSION_PROTOCOL_IMPLEMENTATION 1 uint32_t version; struct pw_protocol_client * (*new_client) (struct pw_protocol *protocol, @@ -90,6 +90,10 @@ struct pw_protocol_implementation { struct pw_protocol_server * (*add_server) (struct pw_protocol *protocol, struct pw_impl_core *core, const struct spa_dict *props); + struct pw_protocol_server * (*add_fd_server) (struct pw_protocol *protocol, + struct pw_impl_core *core, + int listen_fd, int close_fd, + const struct spa_dict *props); }; struct pw_protocol_events { @@ -101,6 +105,7 @@ struct pw_protocol_events { #define pw_protocol_new_client(p,...) (pw_protocol_get_implementation(p)->new_client(p,__VA_ARGS__)) #define pw_protocol_add_server(p,...) (pw_protocol_get_implementation(p)->add_server(p,__VA_ARGS__)) +#define pw_protocol_add_fd_server(p,...) (pw_protocol_get_implementation(p)->add_fd_server(p,__VA_ARGS__)) #define pw_protocol_ext(p,type,method,...) (((type*)pw_protocol_get_extension(p))->method( __VA_ARGS__)) struct pw_protocol *pw_protocol_new(struct pw_context *context, const char *name, size_t user_data_size); diff --git a/src/java.desktop/unix/native/libpipewire/include/pipewire/proxy.h b/src/java.desktop/unix/native/libpipewire/include/pipewire/proxy.h index ecd8038a2c0f6..ca7f324c285c1 100644 --- a/src/java.desktop/unix/native/libpipewire/include/pipewire/proxy.h +++ b/src/java.desktop/unix/native/libpipewire/include/pipewire/proxy.h @@ -12,6 +12,8 @@ extern "C" { #include /** \page page_proxy Proxy + * + * \see \ref pw_proxy * * \section sec_page_proxy_overview Overview * @@ -76,7 +78,7 @@ extern "C" { * invoked by the client to PipeWire messages. Events will call the handlers * set in listener. * - * See \ref page_proxy + * \see \ref page_proxy */ /** diff --git a/src/java.desktop/unix/native/libpipewire/include/pipewire/stream.h b/src/java.desktop/unix/native/libpipewire/include/pipewire/stream.h index 90096056a0c57..dca13d22669c4 100644 --- a/src/java.desktop/unix/native/libpipewire/include/pipewire/stream.h +++ b/src/java.desktop/unix/native/libpipewire/include/pipewire/stream.h @@ -10,6 +10,8 @@ extern "C" { #endif /** \page page_streams Streams + * + * \see \ref pw_stream * * \section sec_overview Overview * @@ -56,11 +58,15 @@ extern "C" { * \li PW_DIRECTION_INPUT for a stream that *consumes* data. This can be a * stream that captures from a Source or a when the stream is used to - * implement a Sink. + * implement a Sink. An application will use a \ref PW_DIRECTION_INPUT + * stream to record data. A virtual sound card will use a + * \ref PW_DIRECTION_INPUT stream to implement audio playback. * * \li PW_DIRECTION_OUTPUT for a stream that *produces* data. This can be a * stream that plays to a Sink or when the stream is used to implement - * a Source. + * a Source. An application will use a \ref PW_DIRECTION_OUTPUT + * stream to produce data. A virtual sound card or camera will use a + * \ref PW_DIRECTION_OUTPUT stream to implement audio or video recording. * * \subsection ssec_stream_target Stream target * @@ -127,8 +133,25 @@ extern "C" { * When the buffer has been processed, call \ref pw_stream_queue_buffer() * to let PipeWire reuse the buffer. * + * Although not strictly required, it is recommended to call \ref + * pw_stream_dequeue_buffer() and pw_stream_queue_buffer() from the + * process() callback to minimize the amount of buffering and + * maximize the amount of buffer reuse in the stream. + * + * It is also possible to dequeue the buffer from the process event, + * then process and queue the buffer from a helper thread. It is also + * possible to dequeue, process and queue a buffer from a helper thread + * after receiving the process event. + * * \subsection ssec_produce Produce data * + * The process event is emitted when a new buffer should be queued. + * + * When the PW_STREAM_FLAG_RT_PROCESS flag was given, this function will be + * called from a realtime thread and it is not safe to call non-reatime + * functions such as doing file operations, blocking operations or any of + * the PipeWire functions that are not explicitly marked as being RT safe. + * * \ref pw_stream_dequeue_buffer() gives an empty buffer that can be filled. * * The buffer is owned by the stream and stays alive until the @@ -136,8 +159,45 @@ extern "C" { * * Filled buffers should be queued with \ref pw_stream_queue_buffer(). * - * The process event is emitted when PipeWire has emptied a buffer that - * can now be refilled. + * Although not strictly required, it is recommended to call \ref + * pw_stream_dequeue_buffer() and pw_stream_queue_buffer() from the + * process() callback to minimize the amount of buffering and + * maximize the amount of buffer reuse in the stream. + * + * Buffers that are queued after the process event completes will be delayed + * to the next processing cycle. + * + * \section sec_stream_driving Driving the graph + * + * Starting in 0.3.34, it is possible for a stream to drive the graph. + * This allows interrupt-driven scheduling for drivers implemented as + * PipeWire streams, without having to reimplement the stream as a SPA + * plugin. + * + * A stream cannot drive the graph unless it is in the + * \ref PW_STREAM_STATE_STREAMING state and \ref pw_stream_is_driving() returns + * true. It must then use pw_stream_trigger_process() to start the graph + * cycle. + * + * \ref pw_stream_trigger_process() will result in a process event, where a buffer + * should be dequeued, and queued again. This is the recommended behaviour that + * minimizes buffering and maximized buffer reuse. + * + * Producers of data that drive the graph can also dequeue a buffer in a helper + * thread, fill it with data and then call \ref pw_stream_trigger_process() to + * start the graph cycle. In the process event they will then queue the filled + * buffer and dequeue a new empty buffer to fill again in the helper thread, + * + * Consumers of data that drive the graph (pull based scheduling) will use + * \ref pw_stream_trigger_process() to start the graph and will dequeue, process + * and queue the buffers in the process event. + * + * \section sec_stream_process_requests Request processing + * + * A stream that is not driving the graph can request a new graph cycle by doing + * \ref pw_stream_trigger_process(). This will result in a RequestProcess command + * in the driver stream. If the driver supports this, it can then perform + * \ref pw_stream_trigger_process() to start the actual graph cycle. * * \section sec_stream_disconnect Disconnect * @@ -151,6 +211,9 @@ extern "C" { * * \section sec_stream_environment Environment Variables * + * The environment variable PIPEWIRE_AUTOCONNECT can be used to override the + * flag and force apps to autoconnect or not. + * */ /** \defgroup pw_stream Stream * @@ -159,7 +222,7 @@ extern "C" { * The stream object provides a convenient way to send and * receive data streams from/to PipeWire. * - * See also \ref page_streams and \ref api_pw_core + * \see \ref page_streams, \ref api_pw_core */ /** @@ -171,6 +234,7 @@ struct pw_stream; #include #include #include +#include /** \enum pw_stream_state The state of a stream */ enum pw_stream_state { @@ -182,19 +246,29 @@ enum pw_stream_state { }; /** a buffer structure obtained from pw_stream_dequeue_buffer(). The size of this - * structure can grow as more field are added in the future */ + * structure can grow as more fields are added in the future */ struct pw_buffer { struct spa_buffer *buffer; /**< the spa buffer */ - void *user_data; /**< user data attached to the buffer */ + void *user_data; /**< user data attached to the buffer. The user of + * the stream can set custom data associated with the + * buffer, typically in the add_buffer event. Any + * cleanup should be performed in the remove_buffer + * event. The user data is returned unmodified each + * time a buffer is dequeued. */ uint64_t size; /**< This field is set by the user and the sum of - * all queued buffer is returned in the time info. + * all queued buffers is returned in the time info. * For audio, it is advised to use the number of - * samples in the buffer for this field. */ + * frames in the buffer for this field. */ uint64_t requested; /**< For playback streams, this field contains the * suggested amount of data to provide. For audio - * streams this will be the amount of samples + * streams this will be the amount of frames * required by the resampler. This field is 0 * when no suggestion is provided. Since 0.3.49 */ + uint64_t time; /**< For capture streams, this field contains the + * cycle time in nanoseconds when this buffer was + * queued in the stream. It can be compared against + * the pw_time values or pw_stream_get_nsec() + * Since 1.0.5 */ }; struct pw_stream_control { @@ -223,25 +297,33 @@ struct pw_stream_control { * value, and pw_time.ticks, were captured at pw_time.now and can be extrapolated * to the current time like this: * - * struct timespec ts; - * clock_gettime(CLOCK_MONOTONIC, &ts); - * int64_t diff = SPA_TIMESPEC_TO_NSEC(&ts) - pw_time.now; + *\code{.c} + * uint64_t now = pw_stream_get_nsec(stream); + * int64_t diff = now - pw_time.now; * int64_t elapsed = (pw_time.rate.denom * diff) / (pw_time.rate.num * SPA_NSEC_PER_SEC); + *\endcode * * pw_time.delay contains the total delay that a signal will travel through the * graph. This includes the delay caused by filters in the graph as well as delays * caused by the hardware. The delay is usually quite stable and should only change when * the topology, quantum or samplerate of the graph changes. * + * The delay requires the application to send the stream early relative to other synchronized + * streams in order to arrive at the edge of the graph in time. This is usually done by + * delaying the other streams with the given delay. + * + * Note that the delay can be negative. A negative delay means that this stream should be + * delayed with the (positive) delay relative to other streams. + * * pw_time.queued and pw_time.buffered is expressed in the time domain of the stream, * or the format that is used for the buffers of this stream. * * pw_time.queued is the sum of all the pw_buffer.size fields of the buffers that are * currently queued in the stream but not yet processed. The application can choose - * the units of this value, for example, time, samples or bytes (below expressed - * as app.rate). + * the units of this value, for example, time, samples, frames or bytes (below + * expressed as app.rate). * - * pw_time.buffered is format dependent, for audio/raw it contains the number of samples + * pw_time.buffered is format dependent, for audio/raw it contains the number of frames * that are buffered inside the resampler/converter. * * The total delay of data in a stream is the sum of the queued and buffered data @@ -252,15 +334,21 @@ struct pw_stream_control { * in milliseconds for the first sample in the newly queued buffer to be played * by the hardware can be calculated as: * + *\code{.unparsed} * (pw_time.buffered * 1000 / stream.samplerate) + * (pw_time.queued * 1000 / app.rate) + * ((pw_time.delay - elapsed) * 1000 * pw_time.rate.num / pw_time.rate.denom) + *\endcode * * The current extrapolated time (in ms) in the source or sink can be calculated as: * + *\code{.unparsed} * (pw_time.ticks + elapsed) * 1000 * pw_time.rate.num / pw_time.rate.denom + *\endcode * + * Below is an overview of the different timing values: * + *\code{.unparsed} * stream time domain graph time domain * /-----------------------\/-----------------------------\ * @@ -272,14 +360,15 @@ struct pw_stream_control { * latency latency * \--------/\-------------/\-----------------------------/ * queued buffered delay + *\endcode */ struct pw_time { - int64_t now; /**< the monotonic time in nanoseconds. This is the time - * when this time report was updated. It is usually - * updated every graph cycle. You can use the current - * monotonic time to calculate the elapsed time between - * this report and the current state and calculate - * updated ticks and delay values. */ + int64_t now; /**< the time in nanoseconds. This is the time when this + * time report was updated. It is usually updated every + * graph cycle. You can use pw_stream_get_nsec() to + * calculate the elapsed time between this report and + * the current time and calculate updated ticks and delay + * values. */ struct spa_fraction rate; /**< the rate of \a ticks and delay. This is usually * expressed in 1/. */ uint64_t ticks; /**< the ticks at \a now. This is the current time that @@ -298,10 +387,14 @@ struct pw_time { * of the size fields in the pw_buffer that are * currently queued */ uint64_t buffered; /**< for audio/raw streams, this contains the extra - * number of samples buffered in the resampler. + * number of frames buffered in the resampler. * Since 0.3.50. */ - uint32_t queued_buffers; /**< The number of buffers that are queued. Since 0.3.50 */ - uint32_t avail_buffers; /**< The number of buffers that can be dequeued. Since 0.3.50 */ + uint32_t queued_buffers; /**< the number of buffers that are queued. Since 0.3.50 */ + uint32_t avail_buffers; /**< the number of buffers that can be dequeued. Since 0.3.50 */ + uint64_t size; /**< for audio/raw playback streams, this contains the number of + * samples requested by the resampler for the current + * quantum. for audio/raw capture streams this will be the number + * of samples available for the current quantum. Since 1.1.0 */ }; #include @@ -342,7 +435,10 @@ struct pw_stream_events { /** A command notify, Since 0.3.39:1 */ void (*command) (void *data, const struct spa_command *command); - /** a trigger_process completed. Since version 0.3.40:2 */ + /** a trigger_process completed. Since version 0.3.40:2. + * This is normally called from the mainloop but since 1.1.0 it + * can also be called directly from the realtime data + * thread if the user is prepared to deal with this. */ void (*trigger_done) (void *data); }; @@ -357,7 +453,8 @@ enum pw_stream_flags { PW_STREAM_FLAG_INACTIVE = (1 << 1), /**< start the stream inactive, * pw_stream_set_active() needs to be * called explicitly */ - PW_STREAM_FLAG_MAP_BUFFERS = (1 << 2), /**< mmap the buffers except DmaBuf */ + PW_STREAM_FLAG_MAP_BUFFERS = (1 << 2), /**< mmap the buffers except DmaBuf that is not + * explicitly marked as mappable. */ PW_STREAM_FLAG_DRIVER = (1 << 3), /**< be a driver */ PW_STREAM_FLAG_RT_PROCESS = (1 << 4), /**< call process from the realtime * thread. You MUST use RT safe functions @@ -375,9 +472,24 @@ enum pw_stream_flags { * needs to be called. This can be used * when the output of the stream depends * on input from other streams. */ + PW_STREAM_FLAG_ASYNC = (1 << 10), /**< Buffers will not be dequeued/queued from + * the realtime process() function. This is + * assumed when RT_PROCESS is unset but can + * also be the case when the process() function + * does a trigger_process() that will then + * dequeue/queue a buffer from another process() + * function. since 0.3.73 */ + PW_STREAM_FLAG_EARLY_PROCESS = (1 << 11), /**< Call process as soon as there is a buffer + * to dequeue. This is only relevant for + * playback and when not using RT_PROCESS. It + * can be used to keep the maximum number of + * buffers queued. Since 0.3.81 */ + PW_STREAM_FLAG_RT_TRIGGER_DONE = (1 << 12), /**< Call trigger_done from the realtime + * thread. You MUST use RT safe functions + * in the trigger_done callback. Since 1.1.0 */ }; -/** Create a new unconneced \ref pw_stream +/** Create a new unconnected \ref pw_stream * \return a newly allocated \ref pw_stream */ struct pw_stream * pw_stream_new(struct pw_core *core, /**< a \ref pw_core */ @@ -385,7 +497,7 @@ pw_stream_new(struct pw_core *core, /**< a \ref pw_core */ struct pw_properties *props /**< stream properties, ownership is taken */); struct pw_stream * -pw_stream_new_simple(struct pw_loop *loop, /**< a \ref pw_loop to use */ +pw_stream_new_simple(struct pw_loop *loop, /**< a \ref pw_loop to use as the main loop */ const char *name, /**< a stream media name */ struct pw_properties *props,/**< stream properties, ownership is taken */ const struct pw_stream_events *events, /**< stream events */ @@ -447,45 +559,62 @@ int pw_stream_set_error(struct pw_stream *stream, /**< a \ref pw_stream */ const char *error, /**< an error message */ ...) SPA_PRINTF_FUNC(3, 4); -/** Complete the negotiation process with result code \a res - * - * This function should be called after notification of the format. - - * When \a res indicates success, \a params contain the parameters for the - * allocation state. */ +/** Update the param exposed on the stream. */ int pw_stream_update_params(struct pw_stream *stream, /**< a \ref pw_stream */ - const struct spa_pod **params, /**< an array of params. The params should - * ideally contain parameters for doing - * buffer allocation. */ + const struct spa_pod **params, /**< an array of params. */ uint32_t n_params /**< number of elements in \a params */); +/** + * Set a parameter on the stream. This is like pw_stream_set_control() but with + * a complete spa_pod param. It can also be called from the param_changed event handler + * to intercept and modify the param for the adapter. Since 0.3.70 */ +int pw_stream_set_param(struct pw_stream *stream, /**< a \ref pw_stream */ + uint32_t id, /**< the id of the param */ + const struct spa_pod *param /**< the params to set */); + /** Get control values */ const struct pw_stream_control *pw_stream_get_control(struct pw_stream *stream, uint32_t id); /** Set control values */ int pw_stream_set_control(struct pw_stream *stream, uint32_t id, uint32_t n_values, float *values, ...); -/** Query the time on the stream */ +/** Query the time on the stream, RT safe */ int pw_stream_get_time_n(struct pw_stream *stream, struct pw_time *time, size_t size); +/** Get the current time in nanoseconds. This value can be compared with + * the \ref pw_time.now value. RT safe. Since 1.1.0 */ +uint64_t pw_stream_get_nsec(struct pw_stream *stream); + +/** Get the data loop that is doing the processing of this stream. This loop + * is assigned after pw_stream_connect(). * Since 1.1.0 */ +struct pw_loop *pw_stream_get_data_loop(struct pw_stream *stream); + /** Query the time on the stream, deprecated since 0.3.50, - * use pw_stream_get_time_n() to get the fields added since 0.3.50. */ + * use pw_stream_get_time_n() to get the fields added since 0.3.50. RT safe. */ SPA_DEPRECATED int pw_stream_get_time(struct pw_stream *stream, struct pw_time *time); /** Get a buffer that can be filled for playback streams or consumed - * for capture streams. */ + * for capture streams. RT safe. */ struct pw_buffer *pw_stream_dequeue_buffer(struct pw_stream *stream); -/** Submit a buffer for playback or recycle a buffer for capture. */ +/** Submit a buffer for playback or recycle a buffer for capture. RT safe. */ int pw_stream_queue_buffer(struct pw_stream *stream, struct pw_buffer *buffer); +/** Return a buffer to the queue without using it. This makes the buffer + * immediately available to dequeue again. RT safe. */ +int pw_stream_return_buffer(struct pw_stream *stream, struct pw_buffer *buffer); + /** Activate or deactivate the stream */ int pw_stream_set_active(struct pw_stream *stream, bool active); /** Flush a stream. When \a drain is true, the drained callback will - * be called when all data is played or recorded */ + * be called when all data is played or recorded. The stream can be resumed + * after the drain by setting it active again with + * \ref pw_stream_set_active(). A flush without a drain is mostly useful afer + * a state change to PAUSED, to flush any remaining data from the queues and + * the converters. RT safe. */ int pw_stream_flush(struct pw_stream *stream, bool drain); /** Check if the stream is driving. The stream needs to have the @@ -494,10 +623,64 @@ int pw_stream_flush(struct pw_stream *stream, bool drain); * available (output) or needed (input). Since 0.3.34 */ bool pw_stream_is_driving(struct pw_stream *stream); +/** Check if the graph is using lazy scheduling. If the stream is + * driving according to \ref pw_stream_is_driving(), then it should + * consider taking into account the RequestProcess commands when + * driving the graph. + * + * If the stream is not driving, it should send out RequestProcess + * events with \ref pw_stream_emit_event() or indirectly with + * \ref pw_stream_trigger_process() to suggest a new graph cycle + * to the driver. + * + * It is not a requirement that all RequestProcess events/commands + * need to start a graph cycle. + * Since 1.4.0 */ +bool pw_stream_is_lazy(struct pw_stream *stream); + /** Trigger a push/pull on the stream. One iteration of the graph will - * scheduled and process() will be called. Since 0.3.34 */ + * be scheduled when the stream is driving according to + * \ref pw_stream_is_driving(). If it successfully finishes, process() + * will be called and the trigger_done event will be emitted. It is + * possible for the graph iteration to not finish, so + * pw_stream_trigger_process() needs to be called again even if process() + * and trigger_done is not called. + * + * If there is a deadline after which the stream will have xrun, + * pw_stream_trigger_process() should be called then, whether or not + * process()/trigger_done has been called. Sound hardware will xrun if + * there is any delay in audio processing, so the ALSA plugin triggers the + * graph every quantum to ensure audio keeps flowing. Drivers that + * do not have a deadline, such as the freewheel driver, should + * use a timeout to ensure that forward progress keeps being made. + * A reasonable choice of deadline is three times the quantum: if + * the graph is taking 3x longer than normal, it is likely that it + * is hung and should be retriggered. + * + * Streams that are not drivers according to \ref pw_stream_is_driving() + * can also call this method. The result is that a RequestProcess event + * is sent to the driver. If the graph is lazy scheduling according to + * \ref pw_stream_is_lazy(), this might result in a graph cycle by the + * driver. If the graph is not lazy scheduling and the stream is not a + * driver, this method will have no effect. + * + * RT safe. + * + * Since 0.3.34 */ int pw_stream_trigger_process(struct pw_stream *stream); +/** Emit an event from this stream. RT safe. + * Since 1.2.6 */ +int pw_stream_emit_event(struct pw_stream *stream, const struct spa_event *event); + +/** Adjust the rate of the stream. + * When the stream is using an adaptive resampler, adjust the resampler rate. + * When there is no resampler, -ENOTSUP is returned. Activating the adaptive + * resampler will add a small amount of delay to the samples, you can deactivate + * it again by setting a value <= 0.0. RT safe. + * Since 1.4.0 */ +int pw_stream_set_rate(struct pw_stream *stream, double rate); + /** * \} */ diff --git a/src/java.desktop/unix/native/libpipewire/include/pipewire/type.h b/src/java.desktop/unix/native/libpipewire/include/pipewire/type.h new file mode 100644 index 0000000000000..2035811666ea3 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/pipewire/type.h @@ -0,0 +1,45 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef PIPEWIRE_TYPE_H +#define PIPEWIRE_TYPE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** \defgroup pw_type Type info + * Type information + */ + +/** + * \addtogroup pw_type + * \{ + */ + +enum { + PW_TYPE_FIRST = SPA_TYPE_VENDOR_PipeWire, +}; + +#define PW_TYPE_INFO_BASE "PipeWire:" + +#define PW_TYPE_INFO_Object PW_TYPE_INFO_BASE "Object" +#define PW_TYPE_INFO_OBJECT_BASE PW_TYPE_INFO_Object ":" + +#define PW_TYPE_INFO_Interface PW_TYPE_INFO_BASE "Interface" +#define PW_TYPE_INFO_INTERFACE_BASE PW_TYPE_INFO_Interface ":" + +const struct spa_type_info * pw_type_info(void); + +/** + * \} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* PIPEWIRE_TYPE_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/pipewire/utils.h b/src/java.desktop/unix/native/libpipewire/include/pipewire/utils.h index 19110db81a4a8..c584320487549 100644 --- a/src/java.desktop/unix/native/libpipewire/include/pipewire/utils.h +++ b/src/java.desktop/unix/native/libpipewire/include/pipewire/utils.h @@ -15,11 +15,13 @@ extern "C" { #ifndef _POSIX_C_SOURCE # include #endif +#include #ifndef ENODATA #define ENODATA 9919 #endif +#include #include #include @@ -45,6 +47,12 @@ pw_split_strv(const char *str, const char *delimiter, int max_tokens, int *n_tok int pw_split_ip(char *str, const char *delimiter, int max_tokens, char *tokens[]); +char **pw_strv_parse(const char *val, size_t len, int max_tokens, int *n_tokens); + +int pw_strv_find(char **a, const char *b); + +int pw_strv_find_common(char **a, char **b); + void pw_free_strv(char **str); @@ -92,6 +100,10 @@ void* pw_reallocarray(void *ptr, size_t nmemb, size_t size); * \} */ +SPA_DEFINE_AUTO_CLEANUP(pw_strv, char **, { + spa_clear_ptr(*thing, pw_free_strv); +}) + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/buffer/buffer.h b/src/java.desktop/unix/native/libpipewire/include/spa/buffer/buffer.h index a5e2ac7ffff40..b287a76dec138 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/buffer/buffer.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/buffer/buffer.h @@ -12,6 +12,14 @@ extern "C" { #include #include +#ifndef SPA_API_BUFFER + #ifdef SPA_API_IMPL + #define SPA_API_BUFFER SPA_API_IMPL + #else + #define SPA_API_BUFFER static inline + #endif +#endif + /** \defgroup spa_buffer Buffers * * Buffers describe the data and metadata that is exchanged between @@ -27,9 +35,15 @@ enum spa_data_type { SPA_DATA_Invalid, SPA_DATA_MemPtr, /**< pointer to memory, the data field in * struct spa_data is set. */ - SPA_DATA_MemFd, /**< generic fd, mmap to get to memory */ - SPA_DATA_DmaBuf, /**< fd to dmabuf memory */ - SPA_DATA_MemId, /**< memory is identified with an id */ + SPA_DATA_MemFd, /**< memfd, mmap to get to memory. */ + SPA_DATA_DmaBuf, /**< fd to dmabuf memory. This might not be readily + * mappable (unless the MAPPABLE flag is set) and should + * normally be handled with DMABUF apis. */ + SPA_DATA_MemId, /**< memory is identified with an id. The actual memory + * can be obtained in some other way and can be identified + * with this id. */ + SPA_DATA_SyncObj, /**< a syncobj, usually requires a spa_meta_sync_timeline metadata + * with timeline points. */ _SPA_DATA_LAST, /**< not part of ABI */ }; @@ -65,9 +79,12 @@ struct spa_data { #define SPA_DATA_FLAG_WRITABLE (1u<<1) /**< data is writable */ #define SPA_DATA_FLAG_DYNAMIC (1u<<2) /**< data pointer can be changed */ #define SPA_DATA_FLAG_READWRITE (SPA_DATA_FLAG_READABLE|SPA_DATA_FLAG_WRITABLE) +#define SPA_DATA_FLAG_MAPPABLE (1u<<3) /**< data is mappable with simple mmap/munmap. Some memory + * types are not simply mappable (DmaBuf) unless explicitly + * specified with this flag. */ uint32_t flags; /**< data flags */ int64_t fd; /**< optional fd for data */ - uint32_t mapoffset; /**< offset to map fd at */ + uint32_t mapoffset; /**< offset to map fd at, this is page aligned */ uint32_t maxsize; /**< max size of data */ void *data; /**< optional data pointer */ struct spa_chunk *chunk; /**< valid chunk of memory */ @@ -82,7 +99,7 @@ struct spa_buffer { }; /** Find metadata in a buffer */ -static inline struct spa_meta *spa_buffer_find_meta(const struct spa_buffer *b, uint32_t type) +SPA_API_BUFFER struct spa_meta *spa_buffer_find_meta(const struct spa_buffer *b, uint32_t type) { uint32_t i; @@ -93,7 +110,7 @@ static inline struct spa_meta *spa_buffer_find_meta(const struct spa_buffer *b, return NULL; } -static inline void *spa_buffer_find_meta_data(const struct spa_buffer *b, uint32_t type, size_t size) +SPA_API_BUFFER void *spa_buffer_find_meta_data(const struct spa_buffer *b, uint32_t type, size_t size) { struct spa_meta *m; if ((m = spa_buffer_find_meta(b, type)) && m->size >= size) diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/buffer/meta.h b/src/java.desktop/unix/native/libpipewire/include/spa/buffer/meta.h index bf67f12b7f156..dbb452b56d70b 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/buffer/meta.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/buffer/meta.h @@ -12,6 +12,14 @@ extern "C" { #include #include +#ifndef SPA_API_META + #ifdef SPA_API_IMPL + #define SPA_API_META SPA_API_IMPL + #else + #define SPA_API_META static inline + #endif +#endif + /** * \addtogroup spa_buffer * \{ @@ -28,6 +36,7 @@ enum spa_meta_type { * associated with the data */ SPA_META_Busy, /**< don't write to buffer when count > 0 */ SPA_META_VideoTransform, /**< struct spa_meta_transform */ + SPA_META_SyncTimeline, /**< struct spa_meta_sync_timeline */ _SPA_META_LAST, /**< not part of ABI/API */ }; @@ -45,14 +54,13 @@ struct spa_meta { void *data; /**< pointer to metadata */ }; -static inline void *spa_meta_first(const struct spa_meta *m) { +SPA_API_META void *spa_meta_first(const struct spa_meta *m) { return m->data; } -#define spa_meta_first spa_meta_first -static inline void *spa_meta_end(const struct spa_meta *m) { + +SPA_API_META void *spa_meta_end(const struct spa_meta *m) { return SPA_PTROFF(m->data,m->size,void); } -#define spa_meta_end spa_meta_end #define spa_meta_check(p,m) (SPA_PTROFF(p,sizeof(*(p)),void) <= spa_meta_end(m)) /** @@ -79,19 +87,16 @@ struct spa_meta_region { struct spa_region region; }; -static inline bool spa_meta_region_is_valid(const struct spa_meta_region *m) { +SPA_API_META bool spa_meta_region_is_valid(const struct spa_meta_region *m) { return m->region.size.width != 0 && m->region.size.height != 0; } -#define spa_meta_region_is_valid spa_meta_region_is_valid /** iterate all the items in a metadata */ #define spa_meta_for_each(pos,meta) \ - for ((pos) = (__typeof(pos))spa_meta_first(meta); \ + for ((pos) = (__typeof(pos))spa_meta_first(meta); \ spa_meta_check(pos, meta); \ (pos)++) -#define spa_meta_bitmap_is_valid(m) ((m)->format != 0) - /** * Bitmap information * @@ -111,7 +116,9 @@ struct spa_meta_bitmap { * info. */ }; -#define spa_meta_cursor_is_valid(m) ((m)->id != 0) +SPA_API_META bool spa_meta_bitmap_is_valid(const struct spa_meta_bitmap *m) { + return m->format != 0; +} /** * Cursor information @@ -131,6 +138,10 @@ struct spa_meta_cursor { * struct spa_meta_bitmap at the offset. */ }; +SPA_API_META bool spa_meta_cursor_is_valid(const struct spa_meta_cursor *m) { + return m->id != 0; +} + /** a timed set of events associated with the buffer */ struct spa_meta_control { struct spa_pod_sequence sequence; @@ -149,7 +160,7 @@ enum spa_meta_videotransform_value { SPA_META_TRANSFORMATION_270, /**< 270 degree counter-clockwise */ SPA_META_TRANSFORMATION_Flipped, /**< 180 degree flipped around the vertical axis. Equivalent * to a reflexion through the vertical line splitting the - * bufffer in two equal sized parts */ + * buffer in two equal sized parts */ SPA_META_TRANSFORMATION_Flipped90, /**< flip then rotate around 90 degree counter-clockwise */ SPA_META_TRANSFORMATION_Flipped180, /**< flip then rotate around 180 degree counter-clockwise */ SPA_META_TRANSFORMATION_Flipped270, /**< flip then rotate around 270 degree counter-clockwise */ @@ -161,6 +172,26 @@ struct spa_meta_videotransform { * one of enum spa_meta_videotransform_value */ }; +/** + * A timeline point for explicit sync + * + * Metadata to describe the time on the timeline when the buffer + * can be acquired and when it can be reused. + * + * This metadata will require negotiation of 2 extra fds for the acquire + * and release timelines respectively. One way to achieve this is to place + * this metadata as SPA_PARAM_BUFFERS_metaType when negotiating a buffer + * layout with 2 extra fds. + */ +struct spa_meta_sync_timeline { + uint32_t flags; + uint32_t padding; + uint64_t acquire_point; /**< the timeline acquire point, this is when the data + * can be accessed. */ + uint64_t release_point; /**< the timeline release point, this timeline point should + * be signaled when the data is no longer accessed. */ +}; + /** * \} */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/buffer/type-info.h b/src/java.desktop/unix/native/libpipewire/include/spa/buffer/type-info.h index a5208c0b944da..2a9c43c3a421d 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/buffer/type-info.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/buffer/type-info.h @@ -35,6 +35,7 @@ static const struct spa_type_info spa_type_data_type[] = { { SPA_DATA_MemFd, SPA_TYPE_Int, SPA_TYPE_INFO_DATA_FD_BASE "MemFd", NULL }, { SPA_DATA_DmaBuf, SPA_TYPE_Int, SPA_TYPE_INFO_DATA_FD_BASE "DmaBuf", NULL }, { SPA_DATA_MemId, SPA_TYPE_Int, SPA_TYPE_INFO_DATA_BASE "MemId", NULL }, + { SPA_DATA_SyncObj, SPA_TYPE_Int, SPA_TYPE_INFO_DATA_BASE "SyncObj", NULL }, { 0, 0, NULL, NULL }, }; @@ -50,6 +51,22 @@ static const struct spa_type_info spa_type_data_type[] = { #define SPA_TYPE_INFO_META_ARRAY_Region SPA_TYPE_INFO_META_ARRAY_BASE "Region" #define SPA_TYPE_INFO_META_ARRAY_REGION_BASE SPA_TYPE_INFO_META_ARRAY_Region ":" +/* VideoTransform meta */ +#define SPA_TYPE_INFO_META_Transformation SPA_TYPE_INFO_ENUM_BASE "Meta:Transformation" +#define SPA_TYPE_INFO_META_TRANSFORMATION_BASE SPA_TYPE_INFO_META_Transformation ":" + +static const struct spa_type_info spa_type_meta_videotransform_type[] = { + { SPA_META_TRANSFORMATION_None, SPA_TYPE_Int, SPA_TYPE_INFO_META_TRANSFORMATION_BASE "None", NULL }, + { SPA_META_TRANSFORMATION_90, SPA_TYPE_Int, SPA_TYPE_INFO_META_TRANSFORMATION_BASE "90", NULL }, + { SPA_META_TRANSFORMATION_180, SPA_TYPE_Int, SPA_TYPE_INFO_META_TRANSFORMATION_BASE "180", NULL }, + { SPA_META_TRANSFORMATION_270, SPA_TYPE_Int, SPA_TYPE_INFO_META_TRANSFORMATION_BASE "270", NULL }, + { SPA_META_TRANSFORMATION_Flipped, SPA_TYPE_Int, SPA_TYPE_INFO_META_TRANSFORMATION_BASE "Flipped", NULL }, + { SPA_META_TRANSFORMATION_Flipped90, SPA_TYPE_Int, SPA_TYPE_INFO_META_TRANSFORMATION_BASE "Flipped90", NULL }, + { SPA_META_TRANSFORMATION_Flipped180, SPA_TYPE_Int, SPA_TYPE_INFO_META_TRANSFORMATION_BASE "Flipped180", NULL }, + { SPA_META_TRANSFORMATION_Flipped270, SPA_TYPE_Int, SPA_TYPE_INFO_META_TRANSFORMATION_BASE "Flipped270", NULL }, + { 0, 0, NULL, NULL }, +}; + static const struct spa_type_info spa_type_meta_type[] = { { SPA_META_Invalid, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "Invalid", NULL }, { SPA_META_Header, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "Header", NULL }, @@ -60,6 +77,7 @@ static const struct spa_type_info spa_type_meta_type[] = { { SPA_META_Control, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "Control", NULL }, { SPA_META_Busy, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "Busy", NULL }, { SPA_META_VideoTransform, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "VideoTransform", NULL }, + { SPA_META_SyncTimeline, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "SyncTimeline", NULL }, { 0, 0, NULL, NULL }, }; diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/control/control.h b/src/java.desktop/unix/native/libpipewire/include/spa/control/control.h index ed04c7e5b9683..43877eef9cc08 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/control/control.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/control/control.h @@ -24,9 +24,12 @@ extern "C" { /** Different Control types */ enum spa_control_type { SPA_CONTROL_Invalid, - SPA_CONTROL_Properties, /**< data contains a SPA_TYPE_OBJECT_Props */ - SPA_CONTROL_Midi, /**< data contains a spa_pod_bytes with raw midi data */ - SPA_CONTROL_OSC, /**< data contains a spa_pod_bytes with an OSC packet */ + SPA_CONTROL_Properties, /**< SPA_TYPE_OBJECT_Props */ + SPA_CONTROL_Midi, /**< spa_pod_bytes with raw midi data (deprecated, use SPA_CONTROL_UMP) */ + SPA_CONTROL_OSC, /**< spa_pod_bytes with an OSC packet */ + SPA_CONTROL_UMP, /**< spa_pod_bytes with raw UMP (universal MIDI packet) + * data. The UMP 32 bit words are stored in native endian + * format. */ _SPA_CONTROL_LAST, /**< not part of ABI */ }; diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/control/type-info.h b/src/java.desktop/unix/native/libpipewire/include/spa/control/type-info.h index a3a61a153c7b2..3db937ec616f0 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/control/type-info.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/control/type-info.h @@ -27,6 +27,7 @@ static const struct spa_type_info spa_type_control[] = { { SPA_CONTROL_Properties, SPA_TYPE_Int, SPA_TYPE_INFO_CONTROL_BASE "Properties", NULL }, { SPA_CONTROL_Midi, SPA_TYPE_Int, SPA_TYPE_INFO_CONTROL_BASE "Midi", NULL }, { SPA_CONTROL_OSC, SPA_TYPE_Int, SPA_TYPE_INFO_CONTROL_BASE "OSC", NULL }, + { SPA_CONTROL_UMP, SPA_TYPE_Int, SPA_TYPE_INFO_CONTROL_BASE "UMP", NULL }, { 0, 0, NULL, NULL }, }; diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/debug/types.h b/src/java.desktop/unix/native/libpipewire/include/spa/debug/types.h index dda912e7c04e0..b32fd28ed9c7e 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/debug/types.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/debug/types.h @@ -18,7 +18,16 @@ extern "C" { #include -static inline const struct spa_type_info *spa_debug_type_find(const struct spa_type_info *info, uint32_t type) +#ifndef SPA_API_DEBUG_TYPES + #ifdef SPA_API_IMPL + #define SPA_API_DEBUG_TYPES SPA_API_IMPL + #else + #define SPA_API_DEBUG_TYPES static inline + #endif +#endif + + +SPA_API_DEBUG_TYPES const struct spa_type_info *spa_debug_type_find(const struct spa_type_info *info, uint32_t type) { const struct spa_type_info *res; @@ -37,22 +46,19 @@ static inline const struct spa_type_info *spa_debug_type_find(const struct spa_t return NULL; } -static inline const char *spa_debug_type_short_name(const char *name) +SPA_API_DEBUG_TYPES const char *spa_debug_type_short_name(const char *name) { - const char *h; - if ((h = strrchr(name, ':')) != NULL) - name = h + 1; - return name; + return spa_type_short_name(name); } -static inline const char *spa_debug_type_find_name(const struct spa_type_info *info, uint32_t type) +SPA_API_DEBUG_TYPES const char *spa_debug_type_find_name(const struct spa_type_info *info, uint32_t type) { if ((info = spa_debug_type_find(info, type)) == NULL) return NULL; return info->name; } -static inline const char *spa_debug_type_find_short_name(const struct spa_type_info *info, uint32_t type) +SPA_API_DEBUG_TYPES const char *spa_debug_type_find_short_name(const struct spa_type_info *info, uint32_t type) { const char *str; if ((str = spa_debug_type_find_name(info, type)) == NULL) @@ -60,7 +66,7 @@ static inline const char *spa_debug_type_find_short_name(const struct spa_type_i return spa_debug_type_short_name(str); } -static inline uint32_t spa_debug_type_find_type(const struct spa_type_info *info, const char *name) +SPA_API_DEBUG_TYPES uint32_t spa_debug_type_find_type(const struct spa_type_info *info, const char *name) { if (info == NULL) info = SPA_TYPE_ROOT; @@ -76,7 +82,7 @@ static inline uint32_t spa_debug_type_find_type(const struct spa_type_info *info return SPA_ID_INVALID; } -static inline const struct spa_type_info *spa_debug_type_find_short(const struct spa_type_info *info, const char *name) +SPA_API_DEBUG_TYPES const struct spa_type_info *spa_debug_type_find_short(const struct spa_type_info *info, const char *name) { while (info && info->name) { if (strcmp(spa_debug_type_short_name(info->name), name) == 0) @@ -90,7 +96,7 @@ static inline const struct spa_type_info *spa_debug_type_find_short(const struct return NULL; } -static inline uint32_t spa_debug_type_find_type_short(const struct spa_type_info *info, const char *name) +SPA_API_DEBUG_TYPES uint32_t spa_debug_type_find_type_short(const struct spa_type_info *info, const char *name) { if ((info = spa_debug_type_find_short(info, name)) == NULL) return SPA_ID_INVALID; diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/node/io.h b/src/java.desktop/unix/native/libpipewire/include/spa/node/io.h index a25c8fd11c5a5..fffde5bb27bbd 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/node/io.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/node/io.h @@ -31,14 +31,16 @@ extern "C" { enum spa_io_type { SPA_IO_Invalid, SPA_IO_Buffers, /**< area to exchange buffers, struct spa_io_buffers */ - SPA_IO_Range, /**< expected byte range, struct spa_io_range */ + SPA_IO_Range, /**< expected byte range, struct spa_io_range (currently not used in PipeWire) */ SPA_IO_Clock, /**< area to update clock information, struct spa_io_clock */ - SPA_IO_Latency, /**< latency reporting, struct spa_io_latency */ + SPA_IO_Latency, /**< latency reporting, struct spa_io_latency (currently not used in + * PipeWire). \see spa_param_latency */ SPA_IO_Control, /**< area for control messages, struct spa_io_sequence */ SPA_IO_Notify, /**< area for notify messages, struct spa_io_sequence */ SPA_IO_Position, /**< position information in the graph, struct spa_io_position */ SPA_IO_RateMatch, /**< rate matching between nodes, struct spa_io_rate_match */ - SPA_IO_Memory, /**< memory pointer, struct spa_io_memory */ + SPA_IO_Memory, /**< memory pointer, struct spa_io_memory (currently not used in PipeWire) */ + SPA_IO_AsyncBuffers, /**< async area to exchange buffers, struct spa_io_async_buffers */ }; /** @@ -108,29 +110,52 @@ struct spa_io_range { * * The clock counts the elapsed time according to the clock provider * since the provider was last started. + * + * Driver nodes are supposed to update the contents of \ref SPA_IO_Clock before + * signaling the start of a graph cycle. These updated clock values become + * visible to other nodes in \ref SPA_IO_Position. Non-driver nodes do + * not need to update the contents of their \ref SPA_IO_Clock. + * + * The host generally gives each node a separate \ref spa_io_clock in \ref + * SPA_IO_Clock, so that updates made by the driver are not visible in the + * contents of \ref SPA_IO_Clock of other nodes. Instead, \ref SPA_IO_Position + * is used to look up the current graph time. + * + * A node is a driver when \ref spa_io_clock.id in \ref SPA_IO_Clock and + * \ref spa_io_position.clock.id in \ref SPA_IO_Position are the same. */ struct spa_io_clock { -#define SPA_IO_CLOCK_FLAG_FREEWHEEL (1u<<0) - uint32_t flags; /**< clock flags */ - uint32_t id; /**< unique clock id, set by application */ - char name[64]; /**< clock name prefixed with API, set by node. The clock name - * is unique per clock and can be used to check if nodes - * share the same clock. */ - uint64_t nsec; /**< time in nanoseconds against monotonic clock */ - struct spa_fraction rate; /**< rate for position/duration/delay */ - uint64_t position; /**< current position */ - uint64_t duration; /**< duration of current cycle */ - int64_t delay; /**< delay between position and hardware, - * positive for capture, negative for playback */ - double rate_diff; /**< rate difference between clock and monotonic time */ - uint64_t next_nsec; /**< estimated next wakeup time in nanoseconds */ +#define SPA_IO_CLOCK_FLAG_FREEWHEEL (1u<<0) /* graph is freewheeling */ +#define SPA_IO_CLOCK_FLAG_XRUN_RECOVER (1u<<1) /* recovering from xrun */ +#define SPA_IO_CLOCK_FLAG_LAZY (1u<<2) /* lazy scheduling */ +#define SPA_IO_CLOCK_FLAG_NO_RATE (1u<<3) /* the rate of the clock is only approximately. + * it is recommended to use the nsec as a clock source. + * The rate_diff contains the measured inaccuracy. */ + uint32_t flags; /**< Clock flags */ + uint32_t id; /**< Unique clock id, set by host application */ + char name[64]; /**< Clock name prefixed with API, set by node when it receives + * \ref SPA_IO_Clock. The clock name is unique per clock and + * can be used to check if nodes share the same clock. */ + uint64_t nsec; /**< Time in nanoseconds against monotonic clock + * (CLOCK_MONOTONIC). This fields reflects a real time instant + * in the past. The value may have jitter. */ + struct spa_fraction rate; /**< Rate for position/duration/delay/xrun */ + uint64_t position; /**< Current position, in samples @ \ref rate */ + uint64_t duration; /**< Duration of current cycle, in samples @ \ref rate */ + int64_t delay; /**< Delay between position and hardware, in samples @ \ref rate */ + double rate_diff; /**< Rate difference between clock and monotonic time, as a ratio of + * clock speeds. */ + uint64_t next_nsec; /**< Estimated next wakeup time in nanoseconds. + * This time is a logical start time of the next cycle, and + * is not necessarily in the future. + */ - struct spa_fraction target_rate; /**< target rate of next cycle */ - uint64_t target_duration; /**< target duration of next cycle */ - uint32_t target_seq; /**< seq counter. must be equal at start and + struct spa_fraction target_rate; /**< Target rate of next cycle */ + uint64_t target_duration; /**< Target duration of next cycle */ + uint32_t target_seq; /**< Seq counter. must be equal at start and * end of read and lower bit must be 0 */ - - uint32_t padding[3]; + uint32_t cycle; /**< incremented each time the graph is started */ + uint64_t xrun; /**< Estimated accumulated xrun duration */ }; /* the size of the video in this cycle */ @@ -145,7 +170,11 @@ struct spa_io_video_size { uint32_t padding[4]; }; -/** latency reporting */ +/** + * Latency reporting + * + * Currently not used in PipeWire. Instead, \see spa_param_latency + */ struct spa_io_latency { struct spa_fraction rate; /**< rate for min/max */ uint64_t min; /**< min latency */ @@ -166,7 +195,9 @@ struct spa_io_segment_bar { float signature_denom; /**< time signature denominator */ double bpm; /**< beats per minute */ double beat; /**< current beat in segment */ - uint32_t padding[8]; + double bar_start_tick; + double ticks_per_beat; + uint32_t padding[4]; }; /** video frame segment */ @@ -245,8 +276,13 @@ enum spa_io_position_state { /** * The position information adds extra meaning to the raw clock times. * - * It is set on all nodes and the clock id will contain the clock of the - * driving node in the graph. + * It is set on all nodes in \ref SPA_IO_Position, and the contents of \ref + * spa_io_position.clock contain the clock updates made by the driving node in + * the graph in its \ref SPA_IO_Clock. Also, \ref spa_io_position.clock.id + * will contain the clock id of the driving node in the graph. + * + * The position clock indicates the logical start time of the current graph + * cycle. * * The position information contains 1 or more segments that convert the * raw clock times to a stream time. They are sorted based on their @@ -269,14 +305,56 @@ struct spa_io_position { struct spa_io_segment segments[SPA_IO_POSITION_MAX_SEGMENTS]; /**< segments */ }; -/** rate matching */ +/** + * Rate matching. + * + * It is usually set on the nodes that process resampled data, by + * the component (audioadapter) that handles resampling between graph + * and node rates. The \a flags and \a rate fields may be modified by the node. + * + * The node can request a correction to the resampling rate in its process(), by setting + * \ref SPA_IO_RATE_MATCH_ACTIVE on \a flags, and setting \a rate to the desired rate + * correction. Usually the rate is obtained from DLL or other adaptive mechanism that + * e.g. drives the node buffer fill level toward a specific value. + * + * When resampling to (graph->node) direction, the number of samples produced + * by the resampler varies on each cycle, as the rates are not commensurate. + * + * When resampling to (node->graph) direction, the number of samples consumed by the + * resampler varies. Node output ports in process() should produce \a size number of + * samples to match what the resampler needs to produce one graph quantum of output + * samples. + * + * Resampling filters introduce processing delay, given by \a delay and \a delay_frac, in + * samples at node rate. The delay varies on each cycle e.g. when resampling between + * noncommensurate rates. + * + * The first sample output (graph->node) or consumed (node->graph) by the resampler is + * offset by \a delay + \a delay_frac / 1e9 node samples relative to the nominal graph + * cycle start position: + * + * \code{.unparsed} + * first_resampled_sample_nsec = + * first_original_sample_nsec + * - (rate_match->delay * SPA_NSEC_PER_SEC + rate_match->delay_frac) / node_rate + * \endcode + */ struct spa_io_rate_match { - uint32_t delay; /**< extra delay in samples for resampler */ + uint32_t delay; /**< resampling delay, in samples at + * node rate */ uint32_t size; /**< requested input size for resampler */ - double rate; /**< rate for resampler */ + double rate; /**< rate for resampler (set by node) */ #define SPA_IO_RATE_MATCH_FLAG_ACTIVE (1 << 0) - uint32_t flags; /**< extra flags */ - uint32_t padding[7]; + uint32_t flags; /**< extra flags (set by node) */ + int32_t delay_frac; /**< resampling delay fractional part, + * in units of nanosamples (1/10^9 sample) at node rate */ + uint32_t padding[6]; +}; + +/** async buffers */ +struct spa_io_async_buffers { + struct spa_io_buffers buffers[2]; /**< async buffers, writers write to current (cycle+1)&1, + * readers read from (cycle)&1 */ }; /** diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/node/type-info.h b/src/java.desktop/unix/native/libpipewire/include/spa/node/type-info.h index 3d3d7908519c0..ab4dbdedfb767 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/node/type-info.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/node/type-info.h @@ -34,6 +34,7 @@ static const struct spa_type_info spa_type_io[] = { { SPA_IO_Position, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Position", NULL }, { SPA_IO_RateMatch, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "RateMatch", NULL }, { SPA_IO_Memory, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Memory", NULL }, + { SPA_IO_AsyncBuffers, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "AsyncBuffers", NULL }, { 0, 0, NULL, NULL }, }; diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/aac-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/aac-types.h index 26c80bfa9db1c..e941250ff96b5 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/aac-types.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/aac-types.h @@ -12,6 +12,11 @@ extern "C" { #include #include +/** + * \addtogroup spa_param + * \{ + */ + #define SPA_TYPE_INFO_AudioAACStreamFormat SPA_TYPE_INFO_ENUM_BASE "AudioAACStreamFormat" #define SPA_TYPE_INFO_AUDIO_AAC_STREAM_FORMAT_BASE SPA_TYPE_INFO_AudioAACStreamFormat ":" diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/aac.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/aac.h index dc5257c2dfd3d..164eaf7de8bd2 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/aac.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/aac.h @@ -11,6 +11,11 @@ extern "C" { #include +/** + * \addtogroup spa_param + * \{ + */ + enum spa_audio_aac_stream_format { SPA_AUDIO_AAC_STREAM_FORMAT_UNKNOWN, /* Raw AAC frames */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/amr-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/amr-types.h index 5e07a784524c0..34281c85763ab 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/amr-types.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/amr-types.h @@ -12,6 +12,11 @@ extern "C" { #include #include +/** + * \addtogroup spa_param + * \{ + */ + #define SPA_TYPE_INFO_AudioAMRBandMode SPA_TYPE_INFO_ENUM_BASE "AudioAMRBandMode" #define SPA_TYPE_INFO_AUDIO_AMR_BAND_MODE_BASE SPA_TYPE_INFO_AudioAMRBandMode ":" diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/amr.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/amr.h index 88b2c4cbcf3ef..aece0ab119eea 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/amr.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/amr.h @@ -11,6 +11,11 @@ extern "C" { #include +/** + * \addtogroup spa_param + * \{ + */ + enum spa_audio_amr_band_mode { SPA_AUDIO_AMR_BAND_MODE_UNKNOWN, SPA_AUDIO_AMR_BAND_MODE_NB, diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/iec958-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/iec958-types.h index fc8243a769b78..6e2e3f028a5bd 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/iec958-types.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/iec958-types.h @@ -12,6 +12,19 @@ extern "C" { #include #include +/** + * \addtogroup spa_param + * \{ + */ + +#ifndef SPA_API_AUDIO_IEC958_TYPES + #ifdef SPA_API_IMPL + #define SPA_API_AUDIO_IEC958_TYPES SPA_API_IMPL + #else + #define SPA_API_AUDIO_IEC958_TYPES static inline + #endif +#endif + #define SPA_TYPE_INFO_AudioIEC958Codec SPA_TYPE_INFO_ENUM_BASE "AudioIEC958Codec" #define SPA_TYPE_INFO_AUDIO_IEC958_CODEC_BASE SPA_TYPE_INFO_AudioIEC958Codec ":" @@ -28,6 +41,14 @@ static const struct spa_type_info spa_type_audio_iec958_codec[] = { { 0, 0, NULL, NULL }, }; +SPA_API_AUDIO_IEC958_TYPES uint32_t spa_type_audio_iec958_codec_from_short_name(const char *name) +{ + return spa_type_from_short_name(name, spa_type_audio_iec958_codec, SPA_AUDIO_IEC958_CODEC_UNKNOWN); +} +SPA_API_AUDIO_IEC958_TYPES const char * spa_type_audio_iec958_codec_to_short_name(uint32_t type) +{ + return spa_type_to_short_name(type, spa_type_audio_iec958_codec, "UNKNOWN"); +} /** * \} */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/mp3-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/mp3-types.h index 6907090faaf6a..8974f1efb336b 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/mp3-types.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/mp3-types.h @@ -12,6 +12,11 @@ extern "C" { #include #include +/** + * \addtogroup spa_param + * \{ + */ + #define SPA_TYPE_INFO_AudioMP3ChannelMode SPA_TYPE_INFO_ENUM_BASE "AudioMP3ChannelMode" #define SPA_TYPE_INFO_AUDIO_MP3_CHANNEL_MODE_BASE SPA_TYPE_INFO_AudioMP3ChannelMode ":" diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/mp3.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/mp3.h index 51f4c2eaf75e5..bfe61aaea54ae 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/mp3.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/mp3.h @@ -11,6 +11,11 @@ extern "C" { #include +/** + * \addtogroup spa_param + * \{ + */ + enum spa_audio_mp3_channel_mode { SPA_AUDIO_MP3_CHANNEL_MODE_UNKNOWN, SPA_AUDIO_MP3_CHANNEL_MODE_MONO, diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/raw-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/raw-types.h index 50a42157602d9..06b8cc4a7d38d 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/raw-types.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/raw-types.h @@ -15,8 +15,17 @@ extern "C" { */ #include +#include #include +#ifndef SPA_API_AUDIO_RAW_TYPES + #ifdef SPA_API_IMPL + #define SPA_API_AUDIO_RAW_TYPES SPA_API_IMPL + #else + #define SPA_API_AUDIO_RAW_TYPES static inline + #endif +#endif + #define SPA_TYPE_INFO_AudioFormat SPA_TYPE_INFO_ENUM_BASE "AudioFormat" #define SPA_TYPE_INFO_AUDIO_FORMAT_BASE SPA_TYPE_INFO_AudioFormat ":" @@ -128,6 +137,15 @@ static const struct spa_type_info spa_type_audio_format[] = { { 0, 0, NULL, NULL }, }; +SPA_API_AUDIO_RAW_TYPES uint32_t spa_type_audio_format_from_short_name(const char *name) +{ + return spa_type_from_short_name(name, spa_type_audio_format, SPA_AUDIO_FORMAT_UNKNOWN); +} +SPA_API_AUDIO_RAW_TYPES const char * spa_type_audio_format_to_short_name(uint32_t type) +{ + return spa_type_to_short_name(type, spa_type_audio_format, "UNKNOWN"); +} + #define SPA_TYPE_INFO_AudioFlags SPA_TYPE_INFO_FLAGS_BASE "AudioFlags" #define SPA_TYPE_INFO_AUDIO_FLAGS_BASE SPA_TYPE_INFO_AudioFlags ":" @@ -247,6 +265,16 @@ static const struct spa_type_info spa_type_audio_channel[] = { { 0, 0, NULL, NULL }, }; +SPA_API_AUDIO_RAW_TYPES uint32_t spa_type_audio_channel_from_short_name(const char *name) +{ + return spa_type_from_short_name(name, spa_type_audio_channel, SPA_AUDIO_CHANNEL_UNKNOWN); +} +SPA_API_AUDIO_RAW_TYPES const char * spa_type_audio_channel_to_short_name(uint32_t type) +{ + return spa_type_to_short_name(type, spa_type_audio_channel, "UNK"); +} + + /** * \} */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/raw.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/raw.h index 54052f75311a9..462cf58a13ca6 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/raw.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/raw.h @@ -11,15 +11,7 @@ extern "C" { #include -#if !defined(__FreeBSD__) && !defined(__MidnightBSD__) && !defined(AIX) -#include -#endif - -#if defined(AIX) -#include -#define __BIG_ENDIAN BIG_ENDIAN -#define __BYTE_ORDER BIG_ENDIAN -#endif +#include /** * \addtogroup spa_param diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/wma-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/wma-types.h index 0309223ac7008..78ce1b987738d 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/wma-types.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/wma-types.h @@ -12,6 +12,11 @@ extern "C" { #include #include +/** + * \addtogroup spa_param + * \{ + */ + #define SPA_TYPE_INFO_AudioWMAProfile SPA_TYPE_INFO_ENUM_BASE "AudioWMAProfile" #define SPA_TYPE_INFO_AUDIO_WMA_PROFILE_BASE SPA_TYPE_INFO_AudioWMAProfile ":" diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/wma.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/wma.h index 84a78a7e12d05..4f86efe84b70e 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/wma.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/wma.h @@ -11,6 +11,11 @@ extern "C" { #include +/** + * \addtogroup spa_param + * \{ + */ + enum spa_audio_wma_profile { SPA_AUDIO_WMA_PROFILE_UNKNOWN, diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/bluetooth/audio.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/bluetooth/audio.h index 8561a00aebdbe..13590545ed73d 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/bluetooth/audio.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/bluetooth/audio.h @@ -21,6 +21,7 @@ enum spa_bluetooth_audio_codec { SPA_BLUETOOTH_AUDIO_CODEC_SBC_XQ, SPA_BLUETOOTH_AUDIO_CODEC_MPEG, SPA_BLUETOOTH_AUDIO_CODEC_AAC, + SPA_BLUETOOTH_AUDIO_CODEC_AAC_ELD, SPA_BLUETOOTH_AUDIO_CODEC_APTX, SPA_BLUETOOTH_AUDIO_CODEC_APTX_HD, SPA_BLUETOOTH_AUDIO_CODEC_LDAC, @@ -34,13 +35,18 @@ enum spa_bluetooth_audio_codec { SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_71, SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_DUPLEX, SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_PRO, + SPA_BLUETOOTH_AUDIO_CODEC_OPUS_G, /* HFP */ SPA_BLUETOOTH_AUDIO_CODEC_CVSD = 0x100, SPA_BLUETOOTH_AUDIO_CODEC_MSBC, + SPA_BLUETOOTH_AUDIO_CODEC_LC3_SWB, /* BAP */ SPA_BLUETOOTH_AUDIO_CODEC_LC3 = 0x200, + + /* ASHA */ + SPA_BLUETOOTH_AUDIO_CODEC_G722 = 0x300, }; /** diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/bluetooth/type-info.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/bluetooth/type-info.h index a7ce08246c27b..2affa465b458e 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/bluetooth/type-info.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/bluetooth/type-info.h @@ -25,6 +25,7 @@ static const struct spa_type_info spa_type_bluetooth_audio_codec[] = { { SPA_BLUETOOTH_AUDIO_CODEC_SBC_XQ, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "sbc_xq", NULL }, { SPA_BLUETOOTH_AUDIO_CODEC_MPEG, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "mpeg", NULL }, { SPA_BLUETOOTH_AUDIO_CODEC_AAC, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "aac", NULL }, + { SPA_BLUETOOTH_AUDIO_CODEC_AAC_ELD, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "aac_eld", NULL }, { SPA_BLUETOOTH_AUDIO_CODEC_APTX, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "aptx", NULL }, { SPA_BLUETOOTH_AUDIO_CODEC_APTX_HD, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "aptx_hd", NULL }, { SPA_BLUETOOTH_AUDIO_CODEC_LDAC, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "ldac", NULL }, @@ -38,12 +39,16 @@ static const struct spa_type_info spa_type_bluetooth_audio_codec[] = { { SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_71, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "opus_05_71", NULL }, { SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_DUPLEX, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "opus_05_duplex", NULL }, { SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_PRO, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "opus_05_pro", NULL }, + { SPA_BLUETOOTH_AUDIO_CODEC_OPUS_G, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "opus_g", NULL }, { SPA_BLUETOOTH_AUDIO_CODEC_CVSD, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "cvsd", NULL }, { SPA_BLUETOOTH_AUDIO_CODEC_MSBC, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "msbc", NULL }, + { SPA_BLUETOOTH_AUDIO_CODEC_LC3_SWB, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "lc3_swb", NULL }, { SPA_BLUETOOTH_AUDIO_CODEC_LC3, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "lc3", NULL }, + { SPA_BLUETOOTH_AUDIO_CODEC_G722, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "g722", NULL }, + { 0, 0, NULL, NULL }, }; diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/buffers-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/buffers-types.h index 987d75a1669f6..a963193a60c01 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/buffers-types.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/buffers-types.h @@ -56,6 +56,7 @@ static const struct spa_type_info spa_type_param_buffers[] = { { SPA_PARAM_BUFFERS_stride, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_BLOCK_INFO_BASE "stride", NULL }, { SPA_PARAM_BUFFERS_align, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_BLOCK_INFO_BASE "align", NULL }, { SPA_PARAM_BUFFERS_dataType, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_BLOCK_INFO_BASE "dataType", NULL }, + { SPA_PARAM_BUFFERS_metaType, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_BLOCK_INFO_BASE "metaType", NULL }, { 0, 0, NULL, NULL }, }; diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/buffers.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/buffers.h index 6834c6e5e390a..eb8c107337ebd 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/buffers.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/buffers.h @@ -24,14 +24,15 @@ enum spa_param_buffers { SPA_PARAM_BUFFERS_size, /**< size of a data block memory (Int)*/ SPA_PARAM_BUFFERS_stride, /**< stride of data block memory (Int) */ SPA_PARAM_BUFFERS_align, /**< alignment of data block memory (Int) */ - SPA_PARAM_BUFFERS_dataType, /**< possible memory types (Int, mask of enum spa_data_type) */ + SPA_PARAM_BUFFERS_dataType, /**< possible memory types (flags choice Int, mask of enum spa_data_type) */ + SPA_PARAM_BUFFERS_metaType, /**< required meta data types (Int, mask of enum spa_meta_type) */ }; /** properties for SPA_TYPE_OBJECT_ParamMeta */ enum spa_param_meta { SPA_PARAM_META_START, - SPA_PARAM_META_type, /**< the metadata, one of enum spa_meta_type (Id enum spa_meta_type) */ - SPA_PARAM_META_size, /**< the expected maximum size the meta (Int) */ + SPA_PARAM_META_type, /**< the metadata, one of enum spa_meta_type (Id enum spa_meta_type) */ + SPA_PARAM_META_size, /**< the expected maximum size the meta (Int) */ }; /** properties for SPA_TYPE_OBJECT_ParamIO */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/format-utils.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/format-utils.h index b4edb6f6efe4c..b829209351d1e 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/format-utils.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/format-utils.h @@ -18,7 +18,15 @@ extern "C" { #include #include -static inline int +#ifndef SPA_API_FORMAT_UTILS + #ifdef SPA_API_IMPL + #define SPA_API_FORMAT_UTILS SPA_API_IMPL + #else + #define SPA_API_FORMAT_UTILS static inline + #endif +#endif + +SPA_API_FORMAT_UTILS int spa_format_parse(const struct spa_pod *format, uint32_t *media_type, uint32_t *media_subtype) { return spa_pod_parse_object(format, diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/format.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/format.h index a7b425465239a..7de2775ca4417 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/format.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/format.h @@ -141,6 +141,8 @@ enum spa_format { SPA_FORMAT_START_Stream = 0x50000, /* Application Format keys */ SPA_FORMAT_START_Application = 0x60000, + SPA_FORMAT_CONTROL_types, /**< possible control types (flags choice Int, + * mask of enum spa_control_type) */ }; #define SPA_KEY_FORMAT_DSP "format.dsp" /**< a predefined DSP format, diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/latency.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/latency.h index 5fa40b59b9f65..2d567f79eb21b 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/latency.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/latency.h @@ -16,14 +16,31 @@ extern "C" { #include -/** properties for SPA_TYPE_OBJECT_ParamLatency */ +/** + * Properties for SPA_TYPE_OBJECT_ParamLatency + * + * The latency indicates: + * + * - for playback: time delay between start of a graph cycle, and the rendering of + * the first sample of that cycle in audio output. + * + * - for capture: time delay between start of a graph cycle, and the first sample + * of that cycle having occurred in audio input. + * + * For physical output/input, the latency is intended to correspond to the + * rendering/capture of physical audio, including hardware internal rendering delay. + * + * The latency values are adjusted by \ref SPA_PROP_latencyOffsetNsec or + * SPA_PARAM_ProcessLatency, if present. (e.g. for ALSA this is used to adjust for + * the internal hardware latency). + */ enum spa_param_latency { SPA_PARAM_LATENCY_START, SPA_PARAM_LATENCY_direction, /**< direction, input/output (Id enum spa_direction) */ SPA_PARAM_LATENCY_minQuantum, /**< min latency relative to quantum (Float) */ SPA_PARAM_LATENCY_maxQuantum, /**< max latency relative to quantum (Float) */ - SPA_PARAM_LATENCY_minRate, /**< min latency (Int) relative to rate */ - SPA_PARAM_LATENCY_maxRate, /**< max latency (Int) relative to rate */ + SPA_PARAM_LATENCY_minRate, /**< min latency (Int) relative to graph rate */ + SPA_PARAM_LATENCY_maxRate, /**< max latency (Int) relative to graph rate */ SPA_PARAM_LATENCY_minNs, /**< min latency (Long) in nanoseconds */ SPA_PARAM_LATENCY_maxNs, /**< max latency (Long) in nanoseconds */ }; @@ -33,27 +50,32 @@ struct spa_latency_info { enum spa_direction direction; float min_quantum; float max_quantum; - uint32_t min_rate; - uint32_t max_rate; - uint64_t min_ns; - uint64_t max_ns; + int32_t min_rate; + int32_t max_rate; + int64_t min_ns; + int64_t max_ns; }; #define SPA_LATENCY_INFO(dir,...) ((struct spa_latency_info) { .direction = (dir), ## __VA_ARGS__ }) -/** properties for SPA_TYPE_OBJECT_ParamProcessLatency */ +/** + * Properties for SPA_TYPE_OBJECT_ParamProcessLatency + * + * The processing latency indicates logical time delay between a sample in an input port, + * and a corresponding sample in an output port, relative to the graph time. + */ enum spa_param_process_latency { SPA_PARAM_PROCESS_LATENCY_START, SPA_PARAM_PROCESS_LATENCY_quantum, /**< latency relative to quantum (Float) */ - SPA_PARAM_PROCESS_LATENCY_rate, /**< latency (Int) relative to rate */ + SPA_PARAM_PROCESS_LATENCY_rate, /**< latency (Int) relative to graph rate */ SPA_PARAM_PROCESS_LATENCY_ns, /**< latency (Long) in nanoseconds */ }; /** Helper structure for managing process latency objects */ struct spa_process_latency_info { float quantum; - uint32_t rate; - uint64_t ns; + int32_t rate; + int64_t ns; }; #define SPA_PROCESS_LATENCY_INFO_INIT(...) ((struct spa_process_latency_info) { __VA_ARGS__ }) diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/param-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/param-types.h index ff2ddde1cd953..b996666d2b2c5 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/param-types.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/param-types.h @@ -40,6 +40,7 @@ static const struct spa_type_info spa_type_param[] = { { SPA_PARAM_Control, SPA_TYPE_Sequence, SPA_TYPE_INFO_PARAM_ID_BASE "Control", NULL }, { SPA_PARAM_Latency, SPA_TYPE_OBJECT_ParamLatency, SPA_TYPE_INFO_PARAM_ID_BASE "Latency", NULL }, { SPA_PARAM_ProcessLatency, SPA_TYPE_OBJECT_ParamProcessLatency, SPA_TYPE_INFO_PARAM_ID_BASE "ProcessLatency", NULL }, + { SPA_PARAM_Tag, SPA_TYPE_OBJECT_ParamTag, SPA_TYPE_INFO_PARAM_ID_BASE "Tag", NULL }, { 0, 0, NULL, NULL }, }; @@ -54,6 +55,11 @@ static const struct spa_type_info spa_type_prop_float_array[] = { { 0, 0, NULL, NULL }, }; +static const struct spa_type_info spa_type_prop_int_array[] = { + { SPA_PROP_START, SPA_TYPE_Int, SPA_TYPE_INFO_BASE "intArray", NULL, }, + { 0, 0, NULL, NULL }, +}; + static const struct spa_type_info spa_type_prop_channel_map[] = { { SPA_PROP_START, SPA_TYPE_Id, SPA_TYPE_INFO_BASE "channelMap", spa_type_audio_channel, }, { 0, 0, NULL, NULL }, diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/param.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/param.h index 48e7d9349d58d..aac55b7359163 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/param.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/param.h @@ -39,6 +39,7 @@ enum spa_param_type { SPA_PARAM_Control, /**< Control parameter, a SPA_TYPE_Sequence */ SPA_PARAM_Latency, /**< latency reporting, a SPA_TYPE_OBJECT_ParamLatency */ SPA_PARAM_ProcessLatency, /**< processing latency, a SPA_TYPE_OBJECT_ParamProcessLatency */ + SPA_PARAM_Tag, /**< tag reporting, a SPA_TYPE_OBJECT_ParamTag. Since 0.3.79 */ }; /** information about a parameter */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/profiler-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/profiler-types.h index a1abc02a130c4..b87a125a41692 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/profiler-types.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/profiler-types.h @@ -26,6 +26,7 @@ static const struct spa_type_info spa_type_profiler[] = { { SPA_PROFILER_clock, SPA_TYPE_Struct, SPA_TYPE_INFO_PROFILER_BASE "clock", NULL, }, { SPA_PROFILER_driverBlock, SPA_TYPE_Struct, SPA_TYPE_INFO_PROFILER_BASE "driverBlock", NULL, }, { SPA_PROFILER_followerBlock, SPA_TYPE_Struct, SPA_TYPE_INFO_PROFILER_BASE "followerBlock", NULL, }, + { SPA_PROFILER_followerClock, SPA_TYPE_Struct, SPA_TYPE_INFO_PROFILER_BASE "followerClock", NULL, }, { 0, 0, NULL, NULL }, }; diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/profiler.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/profiler.h index e65835a1a8525..97e5431a7de58 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/profiler.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/profiler.h @@ -39,7 +39,10 @@ enum spa_profiler { * Long : clock duration, * Long : clock delay, * Double : clock rate_diff, - * Long : clock next_nsec)) */ + * Long : clock next_nsec, + * Int : transport_state, + * Int : clock cycle, + * Long : xrun duration)) */ SPA_PROFILER_driverBlock, /**< generic driver info block * (Struct( * Int : driver_id, @@ -48,8 +51,9 @@ enum spa_profiler { * Long : driver signal, * Long : driver awake, * Long : driver finish, - * Int : driver status), - * Fraction : latency)) */ + * Int : driver status, + * Fraction : latency, + * Int : xrun_count)) */ SPA_PROFILER_START_Follower = 0x20000, /**< follower related profiler properties */ SPA_PROFILER_followerBlock, /**< generic follower info block @@ -61,8 +65,20 @@ enum spa_profiler { * Long : awake, * Long : finish, * Int : status, - * Fraction : latency)) */ - + * Fraction : latency, + * Int : xrun_count)) */ + SPA_PROFILER_followerClock, /**< follower clock information + * (Struct( + * Int : clock id, + * String: clock name, + * Long : clock nsec, + * Fraction : clock rate, + * Long : clock position, + * Long : clock duration, + * Long : clock delay, + * Double : clock rate_diff, + * Long : clock next_nsec, + * Long : xrun duration)) */ SPA_PROFILER_START_CUSTOM = 0x1000000, }; diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/props-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/props-types.h index 5e4e0a0c9d6f1..e52f6c9a228ec 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/props-types.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/props-types.h @@ -64,14 +64,14 @@ static const struct spa_type_info spa_type_props[] = { { SPA_PROP_volumeRampStepTime, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "volumeRampStepTime", NULL }, { SPA_PROP_volumeRampScale, SPA_TYPE_Id, SPA_TYPE_INFO_PROPS_BASE "volumeRampScale", NULL }, - { SPA_PROP_brightness, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "brightness", NULL }, - { SPA_PROP_contrast, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "contrast", NULL }, - { SPA_PROP_saturation, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "saturation", NULL }, + { SPA_PROP_brightness, SPA_TYPE_Float, SPA_TYPE_INFO_PROPS_BASE "brightness", NULL }, + { SPA_PROP_contrast, SPA_TYPE_Float, SPA_TYPE_INFO_PROPS_BASE "contrast", NULL }, + { SPA_PROP_saturation, SPA_TYPE_Float, SPA_TYPE_INFO_PROPS_BASE "saturation", NULL }, { SPA_PROP_hue, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "hue", NULL }, { SPA_PROP_gamma, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "gamma", NULL }, { SPA_PROP_exposure, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "exposure", NULL }, - { SPA_PROP_gain, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "gain", NULL }, - { SPA_PROP_sharpness, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "sharpness", NULL }, + { SPA_PROP_gain, SPA_TYPE_Float, SPA_TYPE_INFO_PROPS_BASE "gain", NULL }, + { SPA_PROP_sharpness, SPA_TYPE_Float, SPA_TYPE_INFO_PROPS_BASE "sharpness", NULL }, { SPA_PROP_params, SPA_TYPE_Struct, SPA_TYPE_INFO_PROPS_BASE "params", NULL }, { 0, 0, NULL, NULL }, diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/props.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/props.h index 8ecf6f69bc49c..2656c1087c7d7 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/props.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/props.h @@ -59,24 +59,31 @@ enum spa_prop { SPA_PROP_START_Audio = 0x10000, /**< audio related properties */ SPA_PROP_waveType, SPA_PROP_frequency, - SPA_PROP_volume, /**< a volume (Float), 0.0 silence, 1.0 normal */ + SPA_PROP_volume, /**< a volume (Float), 0.0 silence, 1.0 no attenutation */ SPA_PROP_mute, /**< mute (Bool) */ SPA_PROP_patternType, SPA_PROP_ditherType, SPA_PROP_truncate, - SPA_PROP_channelVolumes, /**< a volume array, one volume per - * channel (Array of Float) */ + SPA_PROP_channelVolumes, /**< a volume array, one (linear) volume per channel + * (Array of Float). 0.0 is silence, 1.0 is + * without attenuation. This is the effective + * volume that is applied. It can result + * in a hardware volume and software volume + * (see softVolumes) */ SPA_PROP_volumeBase, /**< a volume base (Float) */ SPA_PROP_volumeStep, /**< a volume step (Float) */ SPA_PROP_channelMap, /**< a channelmap array * (Array (Id enum spa_audio_channel)) */ SPA_PROP_monitorMute, /**< mute (Bool) */ - SPA_PROP_monitorVolumes, /**< a volume array, one volume per + SPA_PROP_monitorVolumes, /**< a volume array, one (linear) volume per * channel (Array of Float) */ SPA_PROP_latencyOffsetNsec, /**< delay adjustment */ - SPA_PROP_softMute, /**< mute (Bool) */ - SPA_PROP_softVolumes, /**< a volume array, one volume per - * channel (Array of Float) */ + SPA_PROP_softMute, /**< mute (Bool) applied in software */ + SPA_PROP_softVolumes, /**< a volume array, one (linear) volume per channel + * (Array of Float). 0.0 is silence, 1.0 is without + * attenuation. This is the volume applied in + * software, there might be a part applied in + * hardware. */ SPA_PROP_iec958Codecs, /**< enabled IEC958 (S/PDIF) codecs, * (Array (Id enum spa_audio_iec958_codec) */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/route-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/route-types.h index 8eb839d3f7b5a..d7f2bf96b47e7 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/route-types.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/route-types.h @@ -32,9 +32,9 @@ static const struct spa_type_info spa_type_param_route[] = { { SPA_PARAM_ROUTE_priority, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_ROUTE_BASE "priority", NULL, }, { SPA_PARAM_ROUTE_available, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_ROUTE_BASE "available", spa_type_param_availability, }, { SPA_PARAM_ROUTE_info, SPA_TYPE_Struct, SPA_TYPE_INFO_PARAM_ROUTE_BASE "info", NULL, }, - { SPA_PARAM_ROUTE_profiles, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_ROUTE_BASE "profiles", NULL, }, + { SPA_PARAM_ROUTE_profiles, SPA_TYPE_Array, SPA_TYPE_INFO_PARAM_ROUTE_BASE "profiles", spa_type_prop_int_array, }, { SPA_PARAM_ROUTE_props, SPA_TYPE_OBJECT_Props, SPA_TYPE_INFO_PARAM_ROUTE_BASE "props", NULL, }, - { SPA_PARAM_ROUTE_devices, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_ROUTE_BASE "devices", NULL, }, + { SPA_PARAM_ROUTE_devices, SPA_TYPE_Array, SPA_TYPE_INFO_PARAM_ROUTE_BASE "devices", spa_type_prop_int_array, }, { SPA_PARAM_ROUTE_profile, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_ROUTE_BASE "profile", NULL, }, { SPA_PARAM_ROUTE_save, SPA_TYPE_Bool, SPA_TYPE_INFO_PARAM_ROUTE_BASE "save", NULL, }, { 0, 0, NULL, NULL }, diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/tag-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/tag-types.h new file mode 100644 index 0000000000000..0a473ef90996f --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/tag-types.h @@ -0,0 +1,39 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_TAG_TYPES_H +#define SPA_PARAM_TAG_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include +#include +#include + +#define SPA_TYPE_INFO_PARAM_Tag SPA_TYPE_INFO_PARAM_BASE "Tag" +#define SPA_TYPE_INFO_PARAM_TAG_BASE SPA_TYPE_INFO_PARAM_Tag ":" + +static const struct spa_type_info spa_type_param_tag[] = { + { SPA_PARAM_TAG_START, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_TAG_BASE, spa_type_param, }, + { SPA_PARAM_TAG_direction, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_TAG_BASE "direction", spa_type_direction, }, + { SPA_PARAM_TAG_info, SPA_TYPE_Struct, SPA_TYPE_INFO_PARAM_TAG_BASE "info", NULL, }, + { 0, 0, NULL, NULL }, +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_TAG_TYPES_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/tag.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/tag.h new file mode 100644 index 0000000000000..558da087dc7aa --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/tag.h @@ -0,0 +1,46 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_TAG_H +#define SPA_PARAM_TAG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include + +/** properties for SPA_TYPE_OBJECT_ParamTag */ +enum spa_param_tag { + SPA_PARAM_TAG_START, + SPA_PARAM_TAG_direction, /**< direction, input/output (Id enum spa_direction) */ + SPA_PARAM_TAG_info, /**< Struct( + * Int: n_items + * (String: key + * String: value)* + * ) */ +}; + +/** helper structure for managing tag objects */ +struct spa_tag_info { + enum spa_direction direction; + const struct spa_pod *info; +}; + +#define SPA_TAG_INFO(dir,...) ((struct spa_tag_info) { .direction = (dir), ## __VA_ARGS__ }) + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_TAG_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/type-info.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/type-info.h index 730a1f53607f2..fee2d030a0a42 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/type-info.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/type-info.h @@ -14,5 +14,6 @@ #include #include #include +#include #endif /* SPA_PARAM_TYPE_INFO_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/dsp-utils.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/dsp-utils.h index 2d4a83c2928b5..d8f075901e343 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/dsp-utils.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/dsp-utils.h @@ -18,13 +18,24 @@ extern "C" { #include #include -static inline int +#ifndef SPA_API_VIDEO_DSP_UTILS + #ifdef SPA_API_IMPL + #define SPA_API_VIDEO_DSP_UTILS SPA_API_IMPL + #else + #define SPA_API_VIDEO_DSP_UTILS static inline + #endif +#endif + +SPA_API_VIDEO_DSP_UTILS int spa_format_video_dsp_parse(const struct spa_pod *format, struct spa_video_info_dsp *info) { info->flags = SPA_VIDEO_FLAG_NONE; - if (spa_pod_find_prop (format, NULL, SPA_FORMAT_VIDEO_modifier)) { + const struct spa_pod_prop *mod_prop; + if ((mod_prop = spa_pod_find_prop (format, NULL, SPA_FORMAT_VIDEO_modifier)) != NULL) { info->flags |= SPA_VIDEO_FLAG_MODIFIER; + if ((mod_prop->flags & SPA_POD_PROP_FLAG_DONT_FIXATE) == SPA_POD_PROP_FLAG_DONT_FIXATE) + info->flags |= SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED; } return spa_pod_parse_object(format, @@ -33,9 +44,9 @@ spa_format_video_dsp_parse(const struct spa_pod *format, SPA_FORMAT_VIDEO_modifier, SPA_POD_OPT_Long(&info->modifier)); } -static inline struct spa_pod * +SPA_API_VIDEO_DSP_UTILS struct spa_pod * spa_format_video_dsp_build(struct spa_pod_builder *builder, uint32_t id, - struct spa_video_info_dsp *info) + const struct spa_video_info_dsp *info) { struct spa_pod_frame f; spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id); @@ -46,9 +57,11 @@ spa_format_video_dsp_build(struct spa_pod_builder *builder, uint32_t id, if (info->format != SPA_VIDEO_FORMAT_UNKNOWN) spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(info->format), 0); - if (info->modifier != 0 || info->flags & SPA_VIDEO_FLAG_MODIFIER) - spa_pod_builder_add(builder, - SPA_FORMAT_VIDEO_modifier, SPA_POD_Long(info->modifier), 0); + if (info->modifier != 0 || info->flags & SPA_VIDEO_FLAG_MODIFIER) { + spa_pod_builder_prop(builder, + SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY); + spa_pod_builder_long(builder, info->modifier); + } return (struct spa_pod*)spa_pod_builder_pop(builder, &f); } diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/format-utils.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/format-utils.h index 31d33101774bc..13495a473a330 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/format-utils.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/format-utils.h @@ -16,7 +16,15 @@ extern "C" { #include #include -static inline int +#ifndef SPA_API_VIDEO_FORMAT_UTILS + #ifdef SPA_API_IMPL + #define SPA_API_VIDEO_FORMAT_UTILS SPA_API_IMPL + #else + #define SPA_API_VIDEO_FORMAT_UTILS static inline + #endif +#endif + +SPA_API_VIDEO_FORMAT_UTILS int spa_format_video_parse(const struct spa_pod *format, struct spa_video_info *info) { int res; @@ -40,8 +48,9 @@ spa_format_video_parse(const struct spa_pod *format, struct spa_video_info *info return -ENOTSUP; } -static inline struct spa_pod * -spa_format_video_build(struct spa_pod_builder *builder, uint32_t id, struct spa_video_info *info) +SPA_API_VIDEO_FORMAT_UTILS struct spa_pod * +spa_format_video_build(struct spa_pod_builder *builder, uint32_t id, + const struct spa_video_info *info) { switch (info->media_subtype) { case SPA_MEDIA_SUBTYPE_raw: diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/h264-utils.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/h264-utils.h index 89c73c98f7ef0..d15b4a0ee68df 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/h264-utils.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/h264-utils.h @@ -18,7 +18,15 @@ extern "C" { #include #include -static inline int +#ifndef SPA_API_VIDEO_H264_UTILS + #ifdef SPA_API_IMPL + #define SPA_API_VIDEO_H264_UTILS SPA_API_IMPL + #else + #define SPA_API_VIDEO_H264_UTILS static inline + #endif +#endif + +SPA_API_VIDEO_H264_UTILS int spa_format_video_h264_parse(const struct spa_pod *format, struct spa_video_info_h264 *info) { @@ -31,9 +39,9 @@ spa_format_video_h264_parse(const struct spa_pod *format, SPA_FORMAT_VIDEO_H264_alignment, SPA_POD_OPT_Id(&info->alignment)); } -static inline struct spa_pod * +SPA_API_VIDEO_H264_UTILS struct spa_pod * spa_format_video_h264_build(struct spa_pod_builder *builder, uint32_t id, - struct spa_video_info_h264 *info) + const struct spa_video_info_h264 *info) { struct spa_pod_frame f; spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id); @@ -49,7 +57,7 @@ spa_format_video_h264_build(struct spa_pod_builder *builder, uint32_t id, SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&info->framerate), 0); if (info->max_framerate.denom != 0) spa_pod_builder_add(builder, - SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_Fraction(info->max_framerate), 0); + SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_Fraction(&info->max_framerate), 0); if (info->stream_format != 0) spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_H264_streamFormat, SPA_POD_Id(info->stream_format), 0); diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/mjpg-utils.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/mjpg-utils.h index 5cb323e8077d6..af362360755c8 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/mjpg-utils.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/mjpg-utils.h @@ -18,7 +18,15 @@ extern "C" { #include #include -static inline int +#ifndef SPA_API_VIDEO_MJPG_UTILS + #ifdef SPA_API_IMPL + #define SPA_API_VIDEO_MJPG_UTILS SPA_API_IMPL + #else + #define SPA_API_VIDEO_MJPG_UTILS static inline + #endif +#endif + +SPA_API_VIDEO_MJPG_UTILS int spa_format_video_mjpg_parse(const struct spa_pod *format, struct spa_video_info_mjpg *info) { @@ -29,9 +37,9 @@ spa_format_video_mjpg_parse(const struct spa_pod *format, SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_OPT_Fraction(&info->max_framerate)); } -static inline struct spa_pod * +SPA_API_VIDEO_MJPG_UTILS struct spa_pod * spa_format_video_mjpg_build(struct spa_pod_builder *builder, uint32_t id, - struct spa_video_info_mjpg *info) + const struct spa_video_info_mjpg *info) { struct spa_pod_frame f; spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id); @@ -47,7 +55,7 @@ spa_format_video_mjpg_build(struct spa_pod_builder *builder, uint32_t id, SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&info->framerate), 0); if (info->max_framerate.denom != 0) spa_pod_builder_add(builder, - SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_Fraction(info->max_framerate), 0); + SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_Fraction(&info->max_framerate), 0); return (struct spa_pod*)spa_pod_builder_pop(builder, &f); } diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/multiview.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/multiview.h index 10a19323f4407..7ee6a152f64ce 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/multiview.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/multiview.h @@ -59,12 +59,11 @@ enum spa_video_multiview_mode { * sequence. This method only applies to * raw video buffers at the moment. * Specific view identification is via - * \ref spa_video_multiview_meta on raw - * video buffers. */ + * metadata on raw video buffers. */ SPA_VIDEO_MULTIVIEW_MODE_SEPARATED, /**< Multiple views are provided as separate * \ref spa_data framebuffers attached * to each \ref spa_buffer, described - * by the \ref spa_video_multiview_meta */ + * by the metadata */ /* future expansion for annotated modes */ }; @@ -97,9 +96,7 @@ enum spa_video_multiview_flags { SPA_VIDEO_MULTIVIEW_FLAGS_MIXED_MONO = (1 << 15), /**< The video stream contains both * mono and multiview portions, * signalled on each buffer by the - * absence or presence of the - * \ref SPA_VIDEO_BUFFER_FLAG_MULTIPLE_VIEW - * buffer flag. */ + * absence or presence of a buffer flag. */ }; diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/raw-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/raw-types.h index 743dd4cd1a5ce..b216250eda6b3 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/raw-types.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/raw-types.h @@ -16,11 +16,20 @@ extern "C" { #include #include +#ifndef SPA_API_VIDEO_RAW_TYPES + #ifdef SPA_API_IMPL + #define SPA_API_VIDEO_RAW_TYPES SPA_API_IMPL + #else + #define SPA_API_VIDEO_RAW_TYPES static inline + #endif +#endif + #define SPA_TYPE_INFO_VideoFormat SPA_TYPE_INFO_ENUM_BASE "VideoFormat" #define SPA_TYPE_INFO_VIDEO_FORMAT_BASE SPA_TYPE_INFO_VideoFormat ":" static const struct spa_type_info spa_type_video_format[] = { - { SPA_VIDEO_FORMAT_ENCODED, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "encoded", NULL }, + { SPA_VIDEO_FORMAT_UNKNOWN, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "UNKNOWN", NULL }, + { SPA_VIDEO_FORMAT_ENCODED, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "ENCODED", NULL }, { SPA_VIDEO_FORMAT_I420, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", NULL }, { SPA_VIDEO_FORMAT_YV12, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "YV12", NULL }, { SPA_VIDEO_FORMAT_YUY2, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "YUY2", NULL }, @@ -110,6 +119,15 @@ static const struct spa_type_info spa_type_video_format[] = { { 0, 0, NULL, NULL }, }; +SPA_API_VIDEO_RAW_TYPES uint32_t spa_type_video_format_from_short_name(const char *name) +{ + return spa_type_from_short_name(name, spa_type_video_format, SPA_VIDEO_FORMAT_UNKNOWN); +} +SPA_API_VIDEO_RAW_TYPES const char * spa_type_video_format_to_short_name(uint32_t type) +{ + return spa_type_to_short_name(type, spa_type_video_format, "UNKNOWN"); +} + #define SPA_TYPE_INFO_VideoFlags SPA_TYPE_INFO_FLAGS_BASE "VideoFlags" #define SPA_TYPE_INFO_VIDEO_FLAGS_BASE SPA_TYPE_INFO_VideoFlags ":" diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/raw-utils.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/raw-utils.h index e39c430136d1d..424c04004cc06 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/raw-utils.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/raw-utils.h @@ -18,13 +18,24 @@ extern "C" { #include #include -static inline int +#ifndef SPA_API_VIDEO_RAW_UTILS + #ifdef SPA_API_IMPL + #define SPA_API_VIDEO_RAW_UTILS SPA_API_IMPL + #else + #define SPA_API_VIDEO_RAW_UTILS static inline + #endif +#endif + +SPA_API_VIDEO_RAW_UTILS int spa_format_video_raw_parse(const struct spa_pod *format, struct spa_video_info_raw *info) { info->flags = SPA_VIDEO_FLAG_NONE; - if (spa_pod_find_prop (format, NULL, SPA_FORMAT_VIDEO_modifier)) { + const struct spa_pod_prop *mod_prop; + if ((mod_prop = spa_pod_find_prop (format, NULL, SPA_FORMAT_VIDEO_modifier)) != NULL) { info->flags |= SPA_VIDEO_FLAG_MODIFIER; + if ((mod_prop->flags & SPA_POD_PROP_FLAG_DONT_FIXATE) == SPA_POD_PROP_FLAG_DONT_FIXATE) + info->flags |= SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED; } return spa_pod_parse_object(format, @@ -46,9 +57,9 @@ spa_format_video_raw_parse(const struct spa_pod *format, SPA_FORMAT_VIDEO_colorPrimaries, SPA_POD_OPT_Id(&info->color_primaries)); } -static inline struct spa_pod * +SPA_API_VIDEO_RAW_UTILS struct spa_pod * spa_format_video_raw_build(struct spa_pod_builder *builder, uint32_t id, - struct spa_video_info_raw *info) + const struct spa_video_info_raw *info) { struct spa_pod_frame f; spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id); @@ -65,12 +76,14 @@ spa_format_video_raw_build(struct spa_pod_builder *builder, uint32_t id, if (info->framerate.denom != 0) spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&info->framerate), 0); - if (info->modifier != 0 || info->flags & SPA_VIDEO_FLAG_MODIFIER) - spa_pod_builder_add(builder, - SPA_FORMAT_VIDEO_modifier, SPA_POD_Long(info->modifier), 0); + if (info->modifier != 0 || info->flags & SPA_VIDEO_FLAG_MODIFIER) { + spa_pod_builder_prop(builder, + SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY); + spa_pod_builder_long(builder, info->modifier); + } if (info->max_framerate.denom != 0) spa_pod_builder_add(builder, - SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_Fraction(info->max_framerate), 0); + SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_Fraction(&info->max_framerate), 0); if (info->views != 0) spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_views, SPA_POD_Int(info->views), 0); @@ -79,7 +92,7 @@ spa_format_video_raw_build(struct spa_pod_builder *builder, uint32_t id, SPA_FORMAT_VIDEO_interlaceMode, SPA_POD_Id(info->interlace_mode), 0); if (info->pixel_aspect_ratio.denom != 0) spa_pod_builder_add(builder, - SPA_FORMAT_VIDEO_pixelAspectRatio,SPA_POD_Fraction(info->pixel_aspect_ratio), 0); + SPA_FORMAT_VIDEO_pixelAspectRatio, SPA_POD_Fraction(&info->pixel_aspect_ratio), 0); if (info->multiview_mode != 0) spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_multiviewMode, SPA_POD_Id(info->multiview_mode), 0); diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/raw.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/raw.h index 5f5a5b64a5192..c2d369569d888 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/raw.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/raw.h @@ -134,11 +134,12 @@ enum spa_video_format { * Extra video flags */ enum spa_video_flags { - SPA_VIDEO_FLAG_NONE = 0, /**< no flags */ - SPA_VIDEO_FLAG_VARIABLE_FPS = (1 << 0), /**< a variable fps is selected, fps_n and fps_d - * denote the maximum fps of the video */ - SPA_VIDEO_FLAG_PREMULTIPLIED_ALPHA = (1 << 1), /**< Each color has been scaled by the alpha value. */ - SPA_VIDEO_FLAG_MODIFIER = (1 << 2), /**< use the format modifier */ + SPA_VIDEO_FLAG_NONE = 0, /**< no flags */ + SPA_VIDEO_FLAG_VARIABLE_FPS = (1 << 0), /**< a variable fps is selected, fps_n and fps_d + * denote the maximum fps of the video */ + SPA_VIDEO_FLAG_PREMULTIPLIED_ALPHA = (1 << 1), /**< Each color has been scaled by the alpha value. */ + SPA_VIDEO_FLAG_MODIFIER = (1 << 2), /**< use the format modifier */ + SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED = (1 << 3), /**< format modifier was not fixated yet */ }; /** diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/pod/builder.h b/src/java.desktop/unix/native/libpipewire/include/spa/pod/builder.h index d9283baa036c4..745e0d671a56f 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/pod/builder.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/pod/builder.h @@ -24,6 +24,14 @@ extern "C" { #include #include +#ifndef SPA_API_POD_BUILDER + #ifdef SPA_API_IMPL + #define SPA_API_POD_BUILDER SPA_API_IMPL + #else + #define SPA_API_POD_BUILDER static inline + #endif +#endif + struct spa_pod_builder_state { uint32_t offset; #define SPA_POD_BUILDER_FLAG_BODY (1<<0) @@ -49,22 +57,22 @@ struct spa_pod_builder { struct spa_callbacks callbacks; }; -#define SPA_POD_BUILDER_INIT(buffer,size) ((struct spa_pod_builder){ (buffer), (size), 0, {}, {} }) +#define SPA_POD_BUILDER_INIT(buffer,size) ((struct spa_pod_builder){ (buffer), (size), 0, {0,0,NULL},{NULL,NULL}}) -static inline void +SPA_API_POD_BUILDER void spa_pod_builder_get_state(struct spa_pod_builder *builder, struct spa_pod_builder_state *state) { *state = builder->state; } -static inline void +SPA_API_POD_BUILDER void spa_pod_builder_set_callbacks(struct spa_pod_builder *builder, const struct spa_pod_builder_callbacks *callbacks, void *data) { builder->callbacks = SPA_CALLBACKS_INIT(callbacks, data); } -static inline void +SPA_API_POD_BUILDER void spa_pod_builder_reset(struct spa_pod_builder *builder, struct spa_pod_builder_state *state) { struct spa_pod_frame *f; @@ -74,12 +82,12 @@ spa_pod_builder_reset(struct spa_pod_builder *builder, struct spa_pod_builder_st f->pod.size -= size; } -static inline void spa_pod_builder_init(struct spa_pod_builder *builder, void *data, uint32_t size) +SPA_API_POD_BUILDER void spa_pod_builder_init(struct spa_pod_builder *builder, void *data, uint32_t size) { *builder = SPA_POD_BUILDER_INIT(data, size); } -static inline struct spa_pod * +SPA_API_POD_BUILDER struct spa_pod * spa_pod_builder_deref(struct spa_pod_builder *builder, uint32_t offset) { uint32_t size = builder->size; @@ -91,7 +99,7 @@ spa_pod_builder_deref(struct spa_pod_builder *builder, uint32_t offset) return NULL; } -static inline struct spa_pod * +SPA_API_POD_BUILDER struct spa_pod * spa_pod_builder_frame(struct spa_pod_builder *builder, struct spa_pod_frame *frame) { if (frame->offset + SPA_POD_SIZE(&frame->pod) <= builder->size) @@ -99,7 +107,7 @@ spa_pod_builder_frame(struct spa_pod_builder *builder, struct spa_pod_frame *fra return NULL; } -static inline void +SPA_API_POD_BUILDER void spa_pod_builder_push(struct spa_pod_builder *builder, struct spa_pod_frame *frame, const struct spa_pod *pod, @@ -115,7 +123,7 @@ spa_pod_builder_push(struct spa_pod_builder *builder, builder->state.flags = SPA_POD_BUILDER_FLAG_FIRST | SPA_POD_BUILDER_FLAG_BODY; } -static inline int spa_pod_builder_raw(struct spa_pod_builder *builder, const void *data, uint32_t size) +SPA_API_POD_BUILDER int spa_pod_builder_raw(struct spa_pod_builder *builder, const void *data, uint32_t size) { int res = 0; struct spa_pod_frame *f; @@ -139,14 +147,14 @@ static inline int spa_pod_builder_raw(struct spa_pod_builder *builder, const voi return res; } -static inline int spa_pod_builder_pad(struct spa_pod_builder *builder, uint32_t size) +SPA_API_POD_BUILDER int spa_pod_builder_pad(struct spa_pod_builder *builder, uint32_t size) { uint64_t zeroes = 0; size = SPA_ROUND_UP_N(size, 8) - size; return size ? spa_pod_builder_raw(builder, &zeroes, size) : 0; } -static inline int +SPA_API_POD_BUILDER int spa_pod_builder_raw_padded(struct spa_pod_builder *builder, const void *data, uint32_t size) { int r, res = spa_pod_builder_raw(builder, data, size); @@ -155,7 +163,7 @@ spa_pod_builder_raw_padded(struct spa_pod_builder *builder, const void *data, ui return res; } -static inline void *spa_pod_builder_pop(struct spa_pod_builder *builder, struct spa_pod_frame *frame) +SPA_API_POD_BUILDER void *spa_pod_builder_pop(struct spa_pod_builder *builder, struct spa_pod_frame *frame) { struct spa_pod *pod; @@ -172,7 +180,7 @@ static inline void *spa_pod_builder_pop(struct spa_pod_builder *builder, struct return pod; } -static inline int +SPA_API_POD_BUILDER int spa_pod_builder_primitive(struct spa_pod_builder *builder, const struct spa_pod *p) { const void *data; @@ -198,13 +206,13 @@ spa_pod_builder_primitive(struct spa_pod_builder *builder, const struct spa_pod #define SPA_POD_INIT_None() SPA_POD_INIT(0, SPA_TYPE_None) -static inline int spa_pod_builder_none(struct spa_pod_builder *builder) +SPA_API_POD_BUILDER int spa_pod_builder_none(struct spa_pod_builder *builder) { const struct spa_pod p = SPA_POD_INIT_None(); return spa_pod_builder_primitive(builder, &p); } -static inline int spa_pod_builder_child(struct spa_pod_builder *builder, uint32_t size, uint32_t type) +SPA_API_POD_BUILDER int spa_pod_builder_child(struct spa_pod_builder *builder, uint32_t size, uint32_t type) { const struct spa_pod p = SPA_POD_INIT(size,type); SPA_FLAG_CLEAR(builder->state.flags, SPA_POD_BUILDER_FLAG_FIRST); @@ -213,7 +221,7 @@ static inline int spa_pod_builder_child(struct spa_pod_builder *builder, uint32_ #define SPA_POD_INIT_Bool(val) ((struct spa_pod_bool){ { sizeof(uint32_t), SPA_TYPE_Bool }, (val) ? 1 : 0, 0 }) -static inline int spa_pod_builder_bool(struct spa_pod_builder *builder, bool val) +SPA_API_POD_BUILDER int spa_pod_builder_bool(struct spa_pod_builder *builder, bool val) { const struct spa_pod_bool p = SPA_POD_INIT_Bool(val); return spa_pod_builder_primitive(builder, &p.pod); @@ -221,7 +229,7 @@ static inline int spa_pod_builder_bool(struct spa_pod_builder *builder, bool val #define SPA_POD_INIT_Id(val) ((struct spa_pod_id){ { sizeof(uint32_t), SPA_TYPE_Id }, (val), 0 }) -static inline int spa_pod_builder_id(struct spa_pod_builder *builder, uint32_t val) +SPA_API_POD_BUILDER int spa_pod_builder_id(struct spa_pod_builder *builder, uint32_t val) { const struct spa_pod_id p = SPA_POD_INIT_Id(val); return spa_pod_builder_primitive(builder, &p.pod); @@ -229,7 +237,7 @@ static inline int spa_pod_builder_id(struct spa_pod_builder *builder, uint32_t v #define SPA_POD_INIT_Int(val) ((struct spa_pod_int){ { sizeof(int32_t), SPA_TYPE_Int }, (val), 0 }) -static inline int spa_pod_builder_int(struct spa_pod_builder *builder, int32_t val) +SPA_API_POD_BUILDER int spa_pod_builder_int(struct spa_pod_builder *builder, int32_t val) { const struct spa_pod_int p = SPA_POD_INIT_Int(val); return spa_pod_builder_primitive(builder, &p.pod); @@ -237,7 +245,7 @@ static inline int spa_pod_builder_int(struct spa_pod_builder *builder, int32_t v #define SPA_POD_INIT_Long(val) ((struct spa_pod_long){ { sizeof(int64_t), SPA_TYPE_Long }, (val) }) -static inline int spa_pod_builder_long(struct spa_pod_builder *builder, int64_t val) +SPA_API_POD_BUILDER int spa_pod_builder_long(struct spa_pod_builder *builder, int64_t val) { const struct spa_pod_long p = SPA_POD_INIT_Long(val); return spa_pod_builder_primitive(builder, &p.pod); @@ -245,7 +253,7 @@ static inline int spa_pod_builder_long(struct spa_pod_builder *builder, int64_t #define SPA_POD_INIT_Float(val) ((struct spa_pod_float){ { sizeof(float), SPA_TYPE_Float }, (val), 0 }) -static inline int spa_pod_builder_float(struct spa_pod_builder *builder, float val) +SPA_API_POD_BUILDER int spa_pod_builder_float(struct spa_pod_builder *builder, float val) { const struct spa_pod_float p = SPA_POD_INIT_Float(val); return spa_pod_builder_primitive(builder, &p.pod); @@ -253,7 +261,7 @@ static inline int spa_pod_builder_float(struct spa_pod_builder *builder, float v #define SPA_POD_INIT_Double(val) ((struct spa_pod_double){ { sizeof(double), SPA_TYPE_Double }, (val) }) -static inline int spa_pod_builder_double(struct spa_pod_builder *builder, double val) +SPA_API_POD_BUILDER int spa_pod_builder_double(struct spa_pod_builder *builder, double val) { const struct spa_pod_double p = SPA_POD_INIT_Double(val); return spa_pod_builder_primitive(builder, &p.pod); @@ -261,7 +269,7 @@ static inline int spa_pod_builder_double(struct spa_pod_builder *builder, double #define SPA_POD_INIT_String(len) ((struct spa_pod_string){ { (len), SPA_TYPE_String } }) -static inline int +SPA_API_POD_BUILDER int spa_pod_builder_write_string(struct spa_pod_builder *builder, const char *str, uint32_t len) { int r, res; @@ -273,7 +281,7 @@ spa_pod_builder_write_string(struct spa_pod_builder *builder, const char *str, u return res; } -static inline int +SPA_API_POD_BUILDER int spa_pod_builder_string_len(struct spa_pod_builder *builder, const char *str, uint32_t len) { const struct spa_pod_string p = SPA_POD_INIT_String(len+1); @@ -283,7 +291,7 @@ spa_pod_builder_string_len(struct spa_pod_builder *builder, const char *str, uin return res; } -static inline int spa_pod_builder_string(struct spa_pod_builder *builder, const char *str) +SPA_API_POD_BUILDER int spa_pod_builder_string(struct spa_pod_builder *builder, const char *str) { uint32_t len = str ? strlen(str) : 0; return spa_pod_builder_string_len(builder, str ? str : "", len); @@ -291,7 +299,7 @@ static inline int spa_pod_builder_string(struct spa_pod_builder *builder, const #define SPA_POD_INIT_Bytes(len) ((struct spa_pod_bytes){ { (len), SPA_TYPE_Bytes } }) -static inline int +SPA_API_POD_BUILDER int spa_pod_builder_bytes(struct spa_pod_builder *builder, const void *bytes, uint32_t len) { const struct spa_pod_bytes p = SPA_POD_INIT_Bytes(len); @@ -300,7 +308,7 @@ spa_pod_builder_bytes(struct spa_pod_builder *builder, const void *bytes, uint32 res = r; return res; } -static inline void * +SPA_API_POD_BUILDER void * spa_pod_builder_reserve_bytes(struct spa_pod_builder *builder, uint32_t len) { uint32_t offset = builder->state.offset; @@ -311,7 +319,7 @@ spa_pod_builder_reserve_bytes(struct spa_pod_builder *builder, uint32_t len) #define SPA_POD_INIT_Pointer(type,value) ((struct spa_pod_pointer){ { sizeof(struct spa_pod_pointer_body), SPA_TYPE_Pointer }, { (type), 0, (value) } }) -static inline int +SPA_API_POD_BUILDER int spa_pod_builder_pointer(struct spa_pod_builder *builder, uint32_t type, const void *val) { const struct spa_pod_pointer p = SPA_POD_INIT_Pointer(type, val); @@ -320,7 +328,7 @@ spa_pod_builder_pointer(struct spa_pod_builder *builder, uint32_t type, const vo #define SPA_POD_INIT_Fd(fd) ((struct spa_pod_fd){ { sizeof(int64_t), SPA_TYPE_Fd }, (fd) }) -static inline int spa_pod_builder_fd(struct spa_pod_builder *builder, int64_t fd) +SPA_API_POD_BUILDER int spa_pod_builder_fd(struct spa_pod_builder *builder, int64_t fd) { const struct spa_pod_fd p = SPA_POD_INIT_Fd(fd); return spa_pod_builder_primitive(builder, &p.pod); @@ -328,7 +336,7 @@ static inline int spa_pod_builder_fd(struct spa_pod_builder *builder, int64_t fd #define SPA_POD_INIT_Rectangle(val) ((struct spa_pod_rectangle){ { sizeof(struct spa_rectangle), SPA_TYPE_Rectangle }, (val) }) -static inline int +SPA_API_POD_BUILDER int spa_pod_builder_rectangle(struct spa_pod_builder *builder, uint32_t width, uint32_t height) { const struct spa_pod_rectangle p = SPA_POD_INIT_Rectangle(SPA_RECTANGLE(width, height)); @@ -337,14 +345,14 @@ spa_pod_builder_rectangle(struct spa_pod_builder *builder, uint32_t width, uint3 #define SPA_POD_INIT_Fraction(val) ((struct spa_pod_fraction){ { sizeof(struct spa_fraction), SPA_TYPE_Fraction }, (val) }) -static inline int +SPA_API_POD_BUILDER int spa_pod_builder_fraction(struct spa_pod_builder *builder, uint32_t num, uint32_t denom) { const struct spa_pod_fraction p = SPA_POD_INIT_Fraction(SPA_FRACTION(num, denom)); return spa_pod_builder_primitive(builder, &p.pod); } -static inline int +SPA_API_POD_BUILDER int spa_pod_builder_push_array(struct spa_pod_builder *builder, struct spa_pod_frame *frame) { const struct spa_pod_array p = @@ -356,7 +364,7 @@ spa_pod_builder_push_array(struct spa_pod_builder *builder, struct spa_pod_frame return res; } -static inline int +SPA_API_POD_BUILDER int spa_pod_builder_array(struct spa_pod_builder *builder, uint32_t child_size, uint32_t child_type, uint32_t n_elems, const void *elems) { @@ -378,7 +386,7 @@ spa_pod_builder_array(struct spa_pod_builder *builder, { { { (n_vals) * sizeof(ctype) + sizeof(struct spa_pod_choice_body), SPA_TYPE_Choice }, \ { (type), 0, { sizeof(ctype), (child_type) } } }, { __VA_ARGS__ } }) -static inline int +SPA_API_POD_BUILDER int spa_pod_builder_push_choice(struct spa_pod_builder *builder, struct spa_pod_frame *frame, uint32_t type, uint32_t flags) { @@ -393,7 +401,7 @@ spa_pod_builder_push_choice(struct spa_pod_builder *builder, struct spa_pod_fram #define SPA_POD_INIT_Struct(size) ((struct spa_pod_struct){ { (size), SPA_TYPE_Struct } }) -static inline int +SPA_API_POD_BUILDER int spa_pod_builder_push_struct(struct spa_pod_builder *builder, struct spa_pod_frame *frame) { const struct spa_pod_struct p = SPA_POD_INIT_Struct(0); @@ -405,7 +413,7 @@ spa_pod_builder_push_struct(struct spa_pod_builder *builder, struct spa_pod_fram #define SPA_POD_INIT_Object(size,type,id,...) ((struct spa_pod_object){ { (size), SPA_TYPE_Object }, { (type), (id) }, ##__VA_ARGS__ }) -static inline int +SPA_API_POD_BUILDER int spa_pod_builder_push_object(struct spa_pod_builder *builder, struct spa_pod_frame *frame, uint32_t type, uint32_t id) { @@ -420,7 +428,7 @@ spa_pod_builder_push_object(struct spa_pod_builder *builder, struct spa_pod_fram #define SPA_POD_INIT_Prop(key,flags,size,type) \ ((struct spa_pod_prop){ (key), (flags), { (size), (type) } }) -static inline int +SPA_API_POD_BUILDER int spa_pod_builder_prop(struct spa_pod_builder *builder, uint32_t key, uint32_t flags) { const struct { uint32_t key; uint32_t flags; } p = { key, flags }; @@ -430,7 +438,7 @@ spa_pod_builder_prop(struct spa_pod_builder *builder, uint32_t key, uint32_t fla #define SPA_POD_INIT_Sequence(size,unit) \ ((struct spa_pod_sequence){ { (size), SPA_TYPE_Sequence}, {(unit), 0 } }) -static inline int +SPA_API_POD_BUILDER int spa_pod_builder_push_sequence(struct spa_pod_builder *builder, struct spa_pod_frame *frame, uint32_t unit) { const struct spa_pod_sequence p = @@ -441,14 +449,14 @@ spa_pod_builder_push_sequence(struct spa_pod_builder *builder, struct spa_pod_fr return res; } -static inline uint32_t +SPA_API_POD_BUILDER int spa_pod_builder_control(struct spa_pod_builder *builder, uint32_t offset, uint32_t type) { const struct { uint32_t offset; uint32_t type; } p = { offset, type }; return spa_pod_builder_raw(builder, &p, sizeof(p)); } -static inline uint32_t spa_choice_from_id(char id) +SPA_API_POD_BUILDER uint32_t spa_choice_from_id(char id) { switch (id) { case 'r': @@ -481,7 +489,7 @@ do { \ spa_pod_builder_long(builder, va_arg(args, int64_t)); \ break; \ case 'f': \ - spa_pod_builder_float(builder, va_arg(args, double)); \ + spa_pod_builder_float(builder, (float)va_arg(args, double)); \ break; \ case 'd': \ spa_pod_builder_double(builder, va_arg(args, double)); \ @@ -560,7 +568,7 @@ do { \ } \ } while(false) -static inline int +SPA_API_POD_BUILDER int spa_pod_builder_addv(struct spa_pod_builder *builder, va_list args) { int res = 0; @@ -618,7 +626,7 @@ spa_pod_builder_addv(struct spa_pod_builder *builder, va_list args) return res; } -static inline int spa_pod_builder_add(struct spa_pod_builder *builder, ...) +SPA_API_POD_BUILDER int spa_pod_builder_add(struct spa_pod_builder *builder, ...) { int res; va_list args; @@ -658,7 +666,7 @@ static inline int spa_pod_builder_add(struct spa_pod_builder *builder, ...) }) /** Copy a pod structure */ -static inline struct spa_pod * +SPA_API_POD_BUILDER struct spa_pod * spa_pod_copy(const struct spa_pod *pod) { size_t size; diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/pod/event.h b/src/java.desktop/unix/native/libpipewire/include/spa/pod/event.h index 23a75a2fd439d..0fa02f698f57f 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/pod/event.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/pod/event.h @@ -30,7 +30,7 @@ struct spa_event { (ev)->body.body.id : SPA_ID_INVALID) #define SPA_EVENT_INIT_FULL(t,size,type,id,...) ((t) \ - { { (size), SPA_TYPE_OBJECT }, \ + { { (size), SPA_TYPE_Object }, \ { { (type), (id) }, ##__VA_ARGS__ } }) \ #define SPA_EVENT_INIT(type,id) \ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/pod/iter.h b/src/java.desktop/unix/native/libpipewire/include/spa/pod/iter.h index 3dd24a8eb4425..cb9b7b898848e 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/pod/iter.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/pod/iter.h @@ -14,6 +14,14 @@ extern "C" { #include +#ifndef SPA_API_POD_ITER + #ifdef SPA_API_IMPL + #define SPA_API_POD_ITER SPA_API_IMPL + #else + #define SPA_API_POD_ITER static inline + #endif +#endif + /** * \addtogroup spa_pod * \{ @@ -26,54 +34,60 @@ struct spa_pod_frame { uint32_t flags; }; -static inline bool spa_pod_is_inside(const void *pod, uint32_t size, const void *iter) +SPA_API_POD_ITER bool spa_pod_is_inside(const void *pod, uint32_t size, const void *iter) { - return SPA_POD_BODY(iter) <= SPA_PTROFF(pod, size, void) && - SPA_PTROFF(iter, SPA_POD_SIZE(iter), void) <= SPA_PTROFF(pod, size, void); + size_t remaining; + + return spa_ptr_type_inside(pod, size, iter, struct spa_pod, &remaining) && + remaining >= SPA_POD_BODY_SIZE(iter); } -static inline void *spa_pod_next(const void *iter) +SPA_API_POD_ITER void *spa_pod_next(const void *iter) { return SPA_PTROFF(iter, SPA_ROUND_UP_N(SPA_POD_SIZE(iter), 8), void); } -static inline struct spa_pod_prop *spa_pod_prop_first(const struct spa_pod_object_body *body) +SPA_API_POD_ITER struct spa_pod_prop *spa_pod_prop_first(const struct spa_pod_object_body *body) { return SPA_PTROFF(body, sizeof(struct spa_pod_object_body), struct spa_pod_prop); } -static inline bool spa_pod_prop_is_inside(const struct spa_pod_object_body *body, +SPA_API_POD_ITER bool spa_pod_prop_is_inside(const struct spa_pod_object_body *body, uint32_t size, const struct spa_pod_prop *iter) { - return SPA_POD_CONTENTS(struct spa_pod_prop, iter) <= SPA_PTROFF(body, size, void) && - SPA_PTROFF(iter, SPA_POD_PROP_SIZE(iter), void) <= SPA_PTROFF(body, size, void); + size_t remaining; + + return spa_ptr_type_inside(body, size, iter, struct spa_pod_prop, &remaining) && + remaining >= iter->value.size; } -static inline struct spa_pod_prop *spa_pod_prop_next(const struct spa_pod_prop *iter) +SPA_API_POD_ITER struct spa_pod_prop *spa_pod_prop_next(const struct spa_pod_prop *iter) { return SPA_PTROFF(iter, SPA_ROUND_UP_N(SPA_POD_PROP_SIZE(iter), 8), struct spa_pod_prop); } -static inline struct spa_pod_control *spa_pod_control_first(const struct spa_pod_sequence_body *body) +SPA_API_POD_ITER struct spa_pod_control *spa_pod_control_first(const struct spa_pod_sequence_body *body) { return SPA_PTROFF(body, sizeof(struct spa_pod_sequence_body), struct spa_pod_control); } -static inline bool spa_pod_control_is_inside(const struct spa_pod_sequence_body *body, +SPA_API_POD_ITER bool spa_pod_control_is_inside(const struct spa_pod_sequence_body *body, uint32_t size, const struct spa_pod_control *iter) { - return SPA_POD_CONTENTS(struct spa_pod_control, iter) <= SPA_PTROFF(body, size, void) && - SPA_PTROFF(iter, SPA_POD_CONTROL_SIZE(iter), void) <= SPA_PTROFF(body, size, void); + size_t remaining; + + return spa_ptr_type_inside(body, size, iter, struct spa_pod_control, &remaining) && + remaining >= iter->value.size; } -static inline struct spa_pod_control *spa_pod_control_next(const struct spa_pod_control *iter) +SPA_API_POD_ITER struct spa_pod_control *spa_pod_control_next(const struct spa_pod_control *iter) { return SPA_PTROFF(iter, SPA_ROUND_UP_N(SPA_POD_CONTROL_SIZE(iter), 8), struct spa_pod_control); } #define SPA_POD_ARRAY_BODY_FOREACH(body, _size, iter) \ for ((iter) = (__typeof__(iter))SPA_PTROFF((body), sizeof(struct spa_pod_array_body), void); \ - (iter) < (__typeof__(iter))SPA_PTROFF((body), (_size), void); \ + (body)->child.size > 0 && spa_ptrinside(body, _size, iter, (body)->child.size, NULL); \ (iter) = (__typeof__(iter))SPA_PTROFF((iter), (body)->child.size, void)) #define SPA_POD_ARRAY_FOREACH(obj, iter) \ @@ -81,7 +95,7 @@ static inline struct spa_pod_control *spa_pod_control_next(const struct spa_pod_ #define SPA_POD_CHOICE_BODY_FOREACH(body, _size, iter) \ for ((iter) = (__typeof__(iter))SPA_PTROFF((body), sizeof(struct spa_pod_choice_body), void); \ - (iter) < (__typeof__(iter))SPA_PTROFF((body), (_size), void); \ + (body)->child.size > 0 && spa_ptrinside(body, _size, iter, (body)->child.size, NULL); \ (iter) = (__typeof__(iter))SPA_PTROFF((iter), (body)->child.size, void)) #define SPA_POD_CHOICE_FOREACH(obj, iter) \ @@ -112,7 +126,7 @@ static inline struct spa_pod_control *spa_pod_control_next(const struct spa_pod_ SPA_POD_SEQUENCE_BODY_FOREACH(&(seq)->body, SPA_POD_BODY_SIZE(seq), iter) -static inline void *spa_pod_from_data(void *data, size_t maxsize, off_t offset, size_t size) +SPA_API_POD_ITER void *spa_pod_from_data(void *data, size_t maxsize, off_t offset, size_t size) { void *pod; if (size < sizeof(struct spa_pod) || offset + size > maxsize) @@ -123,17 +137,17 @@ static inline void *spa_pod_from_data(void *data, size_t maxsize, off_t offset, return pod; } -static inline int spa_pod_is_none(const struct spa_pod *pod) +SPA_API_POD_ITER int spa_pod_is_none(const struct spa_pod *pod) { return (SPA_POD_TYPE(pod) == SPA_TYPE_None); } -static inline int spa_pod_is_bool(const struct spa_pod *pod) +SPA_API_POD_ITER int spa_pod_is_bool(const struct spa_pod *pod) { return (SPA_POD_TYPE(pod) == SPA_TYPE_Bool && SPA_POD_BODY_SIZE(pod) >= sizeof(int32_t)); } -static inline int spa_pod_get_bool(const struct spa_pod *pod, bool *value) +SPA_API_POD_ITER int spa_pod_get_bool(const struct spa_pod *pod, bool *value) { if (!spa_pod_is_bool(pod)) return -EINVAL; @@ -141,12 +155,12 @@ static inline int spa_pod_get_bool(const struct spa_pod *pod, bool *value) return 0; } -static inline int spa_pod_is_id(const struct spa_pod *pod) +SPA_API_POD_ITER int spa_pod_is_id(const struct spa_pod *pod) { return (SPA_POD_TYPE(pod) == SPA_TYPE_Id && SPA_POD_BODY_SIZE(pod) >= sizeof(uint32_t)); } -static inline int spa_pod_get_id(const struct spa_pod *pod, uint32_t *value) +SPA_API_POD_ITER int spa_pod_get_id(const struct spa_pod *pod, uint32_t *value) { if (!spa_pod_is_id(pod)) return -EINVAL; @@ -154,12 +168,12 @@ static inline int spa_pod_get_id(const struct spa_pod *pod, uint32_t *value) return 0; } -static inline int spa_pod_is_int(const struct spa_pod *pod) +SPA_API_POD_ITER int spa_pod_is_int(const struct spa_pod *pod) { return (SPA_POD_TYPE(pod) == SPA_TYPE_Int && SPA_POD_BODY_SIZE(pod) >= sizeof(int32_t)); } -static inline int spa_pod_get_int(const struct spa_pod *pod, int32_t *value) +SPA_API_POD_ITER int spa_pod_get_int(const struct spa_pod *pod, int32_t *value) { if (!spa_pod_is_int(pod)) return -EINVAL; @@ -167,12 +181,12 @@ static inline int spa_pod_get_int(const struct spa_pod *pod, int32_t *value) return 0; } -static inline int spa_pod_is_long(const struct spa_pod *pod) +SPA_API_POD_ITER int spa_pod_is_long(const struct spa_pod *pod) { return (SPA_POD_TYPE(pod) == SPA_TYPE_Long && SPA_POD_BODY_SIZE(pod) >= sizeof(int64_t)); } -static inline int spa_pod_get_long(const struct spa_pod *pod, int64_t *value) +SPA_API_POD_ITER int spa_pod_get_long(const struct spa_pod *pod, int64_t *value) { if (!spa_pod_is_long(pod)) return -EINVAL; @@ -180,12 +194,12 @@ static inline int spa_pod_get_long(const struct spa_pod *pod, int64_t *value) return 0; } -static inline int spa_pod_is_float(const struct spa_pod *pod) +SPA_API_POD_ITER int spa_pod_is_float(const struct spa_pod *pod) { return (SPA_POD_TYPE(pod) == SPA_TYPE_Float && SPA_POD_BODY_SIZE(pod) >= sizeof(float)); } -static inline int spa_pod_get_float(const struct spa_pod *pod, float *value) +SPA_API_POD_ITER int spa_pod_get_float(const struct spa_pod *pod, float *value) { if (!spa_pod_is_float(pod)) return -EINVAL; @@ -193,12 +207,12 @@ static inline int spa_pod_get_float(const struct spa_pod *pod, float *value) return 0; } -static inline int spa_pod_is_double(const struct spa_pod *pod) +SPA_API_POD_ITER int spa_pod_is_double(const struct spa_pod *pod) { return (SPA_POD_TYPE(pod) == SPA_TYPE_Double && SPA_POD_BODY_SIZE(pod) >= sizeof(double)); } -static inline int spa_pod_get_double(const struct spa_pod *pod, double *value) +SPA_API_POD_ITER int spa_pod_get_double(const struct spa_pod *pod, double *value) { if (!spa_pod_is_double(pod)) return -EINVAL; @@ -206,7 +220,7 @@ static inline int spa_pod_get_double(const struct spa_pod *pod, double *value) return 0; } -static inline int spa_pod_is_string(const struct spa_pod *pod) +SPA_API_POD_ITER int spa_pod_is_string(const struct spa_pod *pod) { const char *s = (const char *)SPA_POD_CONTENTS(struct spa_pod_string, pod); return (SPA_POD_TYPE(pod) == SPA_TYPE_String && @@ -214,7 +228,7 @@ static inline int spa_pod_is_string(const struct spa_pod *pod) s[SPA_POD_BODY_SIZE(pod)-1] == '\0'); } -static inline int spa_pod_get_string(const struct spa_pod *pod, const char **value) +SPA_API_POD_ITER int spa_pod_get_string(const struct spa_pod *pod, const char **value) { if (!spa_pod_is_string(pod)) return -EINVAL; @@ -222,7 +236,7 @@ static inline int spa_pod_get_string(const struct spa_pod *pod, const char **val return 0; } -static inline int spa_pod_copy_string(const struct spa_pod *pod, size_t maxlen, char *dest) +SPA_API_POD_ITER int spa_pod_copy_string(const struct spa_pod *pod, size_t maxlen, char *dest) { const char *s = (const char *)SPA_POD_CONTENTS(struct spa_pod_string, pod); if (!spa_pod_is_string(pod) || maxlen < 1) @@ -232,12 +246,12 @@ static inline int spa_pod_copy_string(const struct spa_pod *pod, size_t maxlen, return 0; } -static inline int spa_pod_is_bytes(const struct spa_pod *pod) +SPA_API_POD_ITER int spa_pod_is_bytes(const struct spa_pod *pod) { return SPA_POD_TYPE(pod) == SPA_TYPE_Bytes; } -static inline int spa_pod_get_bytes(const struct spa_pod *pod, const void **value, uint32_t *len) +SPA_API_POD_ITER int spa_pod_get_bytes(const struct spa_pod *pod, const void **value, uint32_t *len) { if (!spa_pod_is_bytes(pod)) return -EINVAL; @@ -246,13 +260,13 @@ static inline int spa_pod_get_bytes(const struct spa_pod *pod, const void **valu return 0; } -static inline int spa_pod_is_pointer(const struct spa_pod *pod) +SPA_API_POD_ITER int spa_pod_is_pointer(const struct spa_pod *pod) { return (SPA_POD_TYPE(pod) == SPA_TYPE_Pointer && SPA_POD_BODY_SIZE(pod) >= sizeof(struct spa_pod_pointer_body)); } -static inline int spa_pod_get_pointer(const struct spa_pod *pod, uint32_t *type, const void **value) +SPA_API_POD_ITER int spa_pod_get_pointer(const struct spa_pod *pod, uint32_t *type, const void **value) { if (!spa_pod_is_pointer(pod)) return -EINVAL; @@ -261,13 +275,13 @@ static inline int spa_pod_get_pointer(const struct spa_pod *pod, uint32_t *type, return 0; } -static inline int spa_pod_is_fd(const struct spa_pod *pod) +SPA_API_POD_ITER int spa_pod_is_fd(const struct spa_pod *pod) { return (SPA_POD_TYPE(pod) == SPA_TYPE_Fd && SPA_POD_BODY_SIZE(pod) >= sizeof(int64_t)); } -static inline int spa_pod_get_fd(const struct spa_pod *pod, int64_t *value) +SPA_API_POD_ITER int spa_pod_get_fd(const struct spa_pod *pod, int64_t *value) { if (!spa_pod_is_fd(pod)) return -EINVAL; @@ -275,13 +289,13 @@ static inline int spa_pod_get_fd(const struct spa_pod *pod, int64_t *value) return 0; } -static inline int spa_pod_is_rectangle(const struct spa_pod *pod) +SPA_API_POD_ITER int spa_pod_is_rectangle(const struct spa_pod *pod) { return (SPA_POD_TYPE(pod) == SPA_TYPE_Rectangle && SPA_POD_BODY_SIZE(pod) >= sizeof(struct spa_rectangle)); } -static inline int spa_pod_get_rectangle(const struct spa_pod *pod, struct spa_rectangle *value) +SPA_API_POD_ITER int spa_pod_get_rectangle(const struct spa_pod *pod, struct spa_rectangle *value) { if (!spa_pod_is_rectangle(pod)) return -EINVAL; @@ -289,39 +303,39 @@ static inline int spa_pod_get_rectangle(const struct spa_pod *pod, struct spa_re return 0; } -static inline int spa_pod_is_fraction(const struct spa_pod *pod) +SPA_API_POD_ITER int spa_pod_is_fraction(const struct spa_pod *pod) { return (SPA_POD_TYPE(pod) == SPA_TYPE_Fraction && SPA_POD_BODY_SIZE(pod) >= sizeof(struct spa_fraction)); } -static inline int spa_pod_get_fraction(const struct spa_pod *pod, struct spa_fraction *value) +SPA_API_POD_ITER int spa_pod_get_fraction(const struct spa_pod *pod, struct spa_fraction *value) { spa_return_val_if_fail(spa_pod_is_fraction(pod), -EINVAL); *value = SPA_POD_VALUE(struct spa_pod_fraction, pod); return 0; } -static inline int spa_pod_is_bitmap(const struct spa_pod *pod) +SPA_API_POD_ITER int spa_pod_is_bitmap(const struct spa_pod *pod) { return (SPA_POD_TYPE(pod) == SPA_TYPE_Bitmap && SPA_POD_BODY_SIZE(pod) >= sizeof(uint8_t)); } -static inline int spa_pod_is_array(const struct spa_pod *pod) +SPA_API_POD_ITER int spa_pod_is_array(const struct spa_pod *pod) { return (SPA_POD_TYPE(pod) == SPA_TYPE_Array && SPA_POD_BODY_SIZE(pod) >= sizeof(struct spa_pod_array_body)); } -static inline void *spa_pod_get_array(const struct spa_pod *pod, uint32_t *n_values) +SPA_API_POD_ITER void *spa_pod_get_array(const struct spa_pod *pod, uint32_t *n_values) { spa_return_val_if_fail(spa_pod_is_array(pod), NULL); *n_values = SPA_POD_ARRAY_N_VALUES(pod); return SPA_POD_ARRAY_VALUES(pod); } -static inline uint32_t spa_pod_copy_array(const struct spa_pod *pod, uint32_t type, +SPA_API_POD_ITER uint32_t spa_pod_copy_array(const struct spa_pod *pod, uint32_t type, void *values, uint32_t max_values) { uint32_t n_values; @@ -333,13 +347,13 @@ static inline uint32_t spa_pod_copy_array(const struct spa_pod *pod, uint32_t ty return n_values; } -static inline int spa_pod_is_choice(const struct spa_pod *pod) +SPA_API_POD_ITER int spa_pod_is_choice(const struct spa_pod *pod) { return (SPA_POD_TYPE(pod) == SPA_TYPE_Choice && SPA_POD_BODY_SIZE(pod) >= sizeof(struct spa_pod_choice_body)); } -static inline struct spa_pod *spa_pod_get_values(const struct spa_pod *pod, uint32_t *n_vals, uint32_t *choice) +SPA_API_POD_ITER struct spa_pod *spa_pod_get_values(const struct spa_pod *pod, uint32_t *n_vals, uint32_t *choice) { if (pod->type == SPA_TYPE_Choice) { *n_vals = SPA_POD_CHOICE_N_VALUES(pod); @@ -353,34 +367,34 @@ static inline struct spa_pod *spa_pod_get_values(const struct spa_pod *pod, uint } } -static inline int spa_pod_is_struct(const struct spa_pod *pod) +SPA_API_POD_ITER int spa_pod_is_struct(const struct spa_pod *pod) { return (SPA_POD_TYPE(pod) == SPA_TYPE_Struct); } -static inline int spa_pod_is_object(const struct spa_pod *pod) +SPA_API_POD_ITER int spa_pod_is_object(const struct spa_pod *pod) { return (SPA_POD_TYPE(pod) == SPA_TYPE_Object && SPA_POD_BODY_SIZE(pod) >= sizeof(struct spa_pod_object_body)); } -static inline bool spa_pod_is_object_type(const struct spa_pod *pod, uint32_t type) +SPA_API_POD_ITER bool spa_pod_is_object_type(const struct spa_pod *pod, uint32_t type) { return (pod && spa_pod_is_object(pod) && SPA_POD_OBJECT_TYPE(pod) == type); } -static inline bool spa_pod_is_object_id(const struct spa_pod *pod, uint32_t id) +SPA_API_POD_ITER bool spa_pod_is_object_id(const struct spa_pod *pod, uint32_t id) { return (pod && spa_pod_is_object(pod) && SPA_POD_OBJECT_ID(pod) == id); } -static inline int spa_pod_is_sequence(const struct spa_pod *pod) +SPA_API_POD_ITER int spa_pod_is_sequence(const struct spa_pod *pod) { return (SPA_POD_TYPE(pod) == SPA_TYPE_Sequence && SPA_POD_BODY_SIZE(pod) >= sizeof(struct spa_pod_sequence_body)); } -static inline const struct spa_pod_prop *spa_pod_object_find_prop(const struct spa_pod_object *pod, +SPA_API_POD_ITER const struct spa_pod_prop *spa_pod_object_find_prop(const struct spa_pod_object *pod, const struct spa_pod_prop *start, uint32_t key) { const struct spa_pod_prop *first, *res; @@ -400,7 +414,7 @@ static inline const struct spa_pod_prop *spa_pod_object_find_prop(const struct s return NULL; } -static inline const struct spa_pod_prop *spa_pod_find_prop(const struct spa_pod *pod, +SPA_API_POD_ITER const struct spa_pod_prop *spa_pod_find_prop(const struct spa_pod *pod, const struct spa_pod_prop *start, uint32_t key) { if (!spa_pod_is_object(pod)) @@ -408,7 +422,7 @@ static inline const struct spa_pod_prop *spa_pod_find_prop(const struct spa_pod return spa_pod_object_find_prop((const struct spa_pod_object *)pod, start, key); } -static inline int spa_pod_object_fixate(struct spa_pod_object *pod) +SPA_API_POD_ITER int spa_pod_object_fixate(struct spa_pod_object *pod) { struct spa_pod_prop *res; SPA_POD_OBJECT_FOREACH(pod, res) { @@ -419,14 +433,14 @@ static inline int spa_pod_object_fixate(struct spa_pod_object *pod) return 0; } -static inline int spa_pod_fixate(struct spa_pod *pod) +SPA_API_POD_ITER int spa_pod_fixate(struct spa_pod *pod) { if (!spa_pod_is_object(pod)) return -EINVAL; return spa_pod_object_fixate((struct spa_pod_object *)pod); } -static inline int spa_pod_object_is_fixated(const struct spa_pod_object *pod) +SPA_API_POD_ITER int spa_pod_object_is_fixated(const struct spa_pod_object *pod) { struct spa_pod_prop *res; SPA_POD_OBJECT_FOREACH(pod, res) { @@ -437,7 +451,15 @@ static inline int spa_pod_object_is_fixated(const struct spa_pod_object *pod) return 1; } -static inline int spa_pod_is_fixated(const struct spa_pod *pod) +SPA_API_POD_ITER int spa_pod_object_has_props(const struct spa_pod_object *pod) +{ + struct spa_pod_prop *res; + SPA_POD_OBJECT_FOREACH(pod, res) + return 1; + return 0; +} + +SPA_API_POD_ITER int spa_pod_is_fixated(const struct spa_pod *pod) { if (!spa_pod_is_object(pod)) return -EINVAL; diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/pod/parser.h b/src/java.desktop/unix/native/libpipewire/include/spa/pod/parser.h index 76c73e2e6cae9..bfec7b88c805c 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/pod/parser.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/pod/parser.h @@ -15,6 +15,14 @@ extern "C" { #include #include +#ifndef SPA_API_POD_PARSER + #ifdef SPA_API_IMPL + #define SPA_API_POD_PARSER SPA_API_IMPL + #else + #define SPA_API_POD_PARSER static inline + #endif +#endif + /** * \addtogroup spa_pod * \{ @@ -33,33 +41,33 @@ struct spa_pod_parser { struct spa_pod_parser_state state; }; -#define SPA_POD_PARSER_INIT(buffer,size) ((struct spa_pod_parser){ (buffer), (size), 0, {} }) +#define SPA_POD_PARSER_INIT(buffer,size) ((struct spa_pod_parser){ (buffer), (size), 0, {0,0,NULL}}) -static inline void spa_pod_parser_init(struct spa_pod_parser *parser, +SPA_API_POD_PARSER void spa_pod_parser_init(struct spa_pod_parser *parser, const void *data, uint32_t size) { *parser = SPA_POD_PARSER_INIT(data, size); } -static inline void spa_pod_parser_pod(struct spa_pod_parser *parser, +SPA_API_POD_PARSER void spa_pod_parser_pod(struct spa_pod_parser *parser, const struct spa_pod *pod) { spa_pod_parser_init(parser, pod, SPA_POD_SIZE(pod)); } -static inline void +SPA_API_POD_PARSER void spa_pod_parser_get_state(struct spa_pod_parser *parser, struct spa_pod_parser_state *state) { *state = parser->state; } -static inline void +SPA_API_POD_PARSER void spa_pod_parser_reset(struct spa_pod_parser *parser, struct spa_pod_parser_state *state) { parser->state = *state; } -static inline struct spa_pod * +SPA_API_POD_PARSER struct spa_pod * spa_pod_parser_deref(struct spa_pod_parser *parser, uint32_t offset, uint32_t size) { /* Cast to uint64_t to avoid wraparound. Add 8 for the pod itself. */ @@ -78,12 +86,12 @@ spa_pod_parser_deref(struct spa_pod_parser *parser, uint32_t offset, uint32_t si return NULL; } -static inline struct spa_pod *spa_pod_parser_frame(struct spa_pod_parser *parser, struct spa_pod_frame *frame) +SPA_API_POD_PARSER struct spa_pod *spa_pod_parser_frame(struct spa_pod_parser *parser, struct spa_pod_frame *frame) { return SPA_PTROFF(parser->data, frame->offset, struct spa_pod); } -static inline void spa_pod_parser_push(struct spa_pod_parser *parser, +SPA_API_POD_PARSER void spa_pod_parser_push(struct spa_pod_parser *parser, struct spa_pod_frame *frame, const struct spa_pod *pod, uint32_t offset) { frame->pod = *pod; @@ -93,19 +101,19 @@ static inline void spa_pod_parser_push(struct spa_pod_parser *parser, parser->state.frame = frame; } -static inline struct spa_pod *spa_pod_parser_current(struct spa_pod_parser *parser) +SPA_API_POD_PARSER struct spa_pod *spa_pod_parser_current(struct spa_pod_parser *parser) { struct spa_pod_frame *f = parser->state.frame; uint32_t size = f ? f->offset + SPA_POD_SIZE(&f->pod) : parser->size; return spa_pod_parser_deref(parser, parser->state.offset, size); } -static inline void spa_pod_parser_advance(struct spa_pod_parser *parser, const struct spa_pod *pod) +SPA_API_POD_PARSER void spa_pod_parser_advance(struct spa_pod_parser *parser, const struct spa_pod *pod) { parser->state.offset += SPA_ROUND_UP_N(SPA_POD_SIZE(pod), 8); } -static inline struct spa_pod *spa_pod_parser_next(struct spa_pod_parser *parser) +SPA_API_POD_PARSER struct spa_pod *spa_pod_parser_next(struct spa_pod_parser *parser) { struct spa_pod *pod = spa_pod_parser_current(parser); if (pod) @@ -113,7 +121,7 @@ static inline struct spa_pod *spa_pod_parser_next(struct spa_pod_parser *parser) return pod; } -static inline int spa_pod_parser_pop(struct spa_pod_parser *parser, +SPA_API_POD_PARSER int spa_pod_parser_pop(struct spa_pod_parser *parser, struct spa_pod_frame *frame) { parser->state.frame = frame->parent; @@ -121,7 +129,7 @@ static inline int spa_pod_parser_pop(struct spa_pod_parser *parser, return 0; } -static inline int spa_pod_parser_get_bool(struct spa_pod_parser *parser, bool *value) +SPA_API_POD_PARSER int spa_pod_parser_get_bool(struct spa_pod_parser *parser, bool *value) { int res = -EPIPE; const struct spa_pod *pod = spa_pod_parser_current(parser); @@ -130,7 +138,7 @@ static inline int spa_pod_parser_get_bool(struct spa_pod_parser *parser, bool *v return res; } -static inline int spa_pod_parser_get_id(struct spa_pod_parser *parser, uint32_t *value) +SPA_API_POD_PARSER int spa_pod_parser_get_id(struct spa_pod_parser *parser, uint32_t *value) { int res = -EPIPE; const struct spa_pod *pod = spa_pod_parser_current(parser); @@ -139,7 +147,7 @@ static inline int spa_pod_parser_get_id(struct spa_pod_parser *parser, uint32_t return res; } -static inline int spa_pod_parser_get_int(struct spa_pod_parser *parser, int32_t *value) +SPA_API_POD_PARSER int spa_pod_parser_get_int(struct spa_pod_parser *parser, int32_t *value) { int res = -EPIPE; const struct spa_pod *pod = spa_pod_parser_current(parser); @@ -148,7 +156,7 @@ static inline int spa_pod_parser_get_int(struct spa_pod_parser *parser, int32_t return res; } -static inline int spa_pod_parser_get_long(struct spa_pod_parser *parser, int64_t *value) +SPA_API_POD_PARSER int spa_pod_parser_get_long(struct spa_pod_parser *parser, int64_t *value) { int res = -EPIPE; const struct spa_pod *pod = spa_pod_parser_current(parser); @@ -157,7 +165,7 @@ static inline int spa_pod_parser_get_long(struct spa_pod_parser *parser, int64_t return res; } -static inline int spa_pod_parser_get_float(struct spa_pod_parser *parser, float *value) +SPA_API_POD_PARSER int spa_pod_parser_get_float(struct spa_pod_parser *parser, float *value) { int res = -EPIPE; const struct spa_pod *pod = spa_pod_parser_current(parser); @@ -166,7 +174,7 @@ static inline int spa_pod_parser_get_float(struct spa_pod_parser *parser, float return res; } -static inline int spa_pod_parser_get_double(struct spa_pod_parser *parser, double *value) +SPA_API_POD_PARSER int spa_pod_parser_get_double(struct spa_pod_parser *parser, double *value) { int res = -EPIPE; const struct spa_pod *pod = spa_pod_parser_current(parser); @@ -175,7 +183,7 @@ static inline int spa_pod_parser_get_double(struct spa_pod_parser *parser, doubl return res; } -static inline int spa_pod_parser_get_string(struct spa_pod_parser *parser, const char **value) +SPA_API_POD_PARSER int spa_pod_parser_get_string(struct spa_pod_parser *parser, const char **value) { int res = -EPIPE; const struct spa_pod *pod = spa_pod_parser_current(parser); @@ -184,7 +192,7 @@ static inline int spa_pod_parser_get_string(struct spa_pod_parser *parser, const return res; } -static inline int spa_pod_parser_get_bytes(struct spa_pod_parser *parser, const void **value, uint32_t *len) +SPA_API_POD_PARSER int spa_pod_parser_get_bytes(struct spa_pod_parser *parser, const void **value, uint32_t *len) { int res = -EPIPE; const struct spa_pod *pod = spa_pod_parser_current(parser); @@ -193,7 +201,7 @@ static inline int spa_pod_parser_get_bytes(struct spa_pod_parser *parser, const return res; } -static inline int spa_pod_parser_get_pointer(struct spa_pod_parser *parser, uint32_t *type, const void **value) +SPA_API_POD_PARSER int spa_pod_parser_get_pointer(struct spa_pod_parser *parser, uint32_t *type, const void **value) { int res = -EPIPE; const struct spa_pod *pod = spa_pod_parser_current(parser); @@ -202,7 +210,7 @@ static inline int spa_pod_parser_get_pointer(struct spa_pod_parser *parser, uint return res; } -static inline int spa_pod_parser_get_fd(struct spa_pod_parser *parser, int64_t *value) +SPA_API_POD_PARSER int spa_pod_parser_get_fd(struct spa_pod_parser *parser, int64_t *value) { int res = -EPIPE; const struct spa_pod *pod = spa_pod_parser_current(parser); @@ -211,7 +219,7 @@ static inline int spa_pod_parser_get_fd(struct spa_pod_parser *parser, int64_t * return res; } -static inline int spa_pod_parser_get_rectangle(struct spa_pod_parser *parser, struct spa_rectangle *value) +SPA_API_POD_PARSER int spa_pod_parser_get_rectangle(struct spa_pod_parser *parser, struct spa_rectangle *value) { int res = -EPIPE; const struct spa_pod *pod = spa_pod_parser_current(parser); @@ -220,7 +228,7 @@ static inline int spa_pod_parser_get_rectangle(struct spa_pod_parser *parser, st return res; } -static inline int spa_pod_parser_get_fraction(struct spa_pod_parser *parser, struct spa_fraction *value) +SPA_API_POD_PARSER int spa_pod_parser_get_fraction(struct spa_pod_parser *parser, struct spa_fraction *value) { int res = -EPIPE; const struct spa_pod *pod = spa_pod_parser_current(parser); @@ -229,7 +237,7 @@ static inline int spa_pod_parser_get_fraction(struct spa_pod_parser *parser, str return res; } -static inline int spa_pod_parser_get_pod(struct spa_pod_parser *parser, struct spa_pod **value) +SPA_API_POD_PARSER int spa_pod_parser_get_pod(struct spa_pod_parser *parser, struct spa_pod **value) { struct spa_pod *pod = spa_pod_parser_current(parser); if (pod == NULL) @@ -238,7 +246,7 @@ static inline int spa_pod_parser_get_pod(struct spa_pod_parser *parser, struct s spa_pod_parser_advance(parser, pod); return 0; } -static inline int spa_pod_parser_push_struct(struct spa_pod_parser *parser, +SPA_API_POD_PARSER int spa_pod_parser_push_struct(struct spa_pod_parser *parser, struct spa_pod_frame *frame) { const struct spa_pod *pod = spa_pod_parser_current(parser); @@ -251,7 +259,7 @@ static inline int spa_pod_parser_push_struct(struct spa_pod_parser *parser, return 0; } -static inline int spa_pod_parser_push_object(struct spa_pod_parser *parser, +SPA_API_POD_PARSER int spa_pod_parser_push_object(struct spa_pod_parser *parser, struct spa_pod_frame *frame, uint32_t type, uint32_t *id) { const struct spa_pod *pod = spa_pod_parser_current(parser); @@ -268,7 +276,7 @@ static inline int spa_pod_parser_push_object(struct spa_pod_parser *parser, return 0; } -static inline bool spa_pod_parser_can_collect(const struct spa_pod *pod, char type) +SPA_API_POD_PARSER bool spa_pod_parser_can_collect(const struct spa_pod *pod, char type) { if (pod == NULL) return false; @@ -443,7 +451,7 @@ do { \ } \ } while(false) -static inline int spa_pod_parser_getv(struct spa_pod_parser *parser, va_list args) +SPA_API_POD_PARSER int spa_pod_parser_getv(struct spa_pod_parser *parser, va_list args) { struct spa_pod_frame *f = parser->state.frame; uint32_t ftype = f ? f->pod.type : (uint32_t)SPA_TYPE_Struct; @@ -455,7 +463,7 @@ static inline int spa_pod_parser_getv(struct spa_pod_parser *parser, va_list arg const struct spa_pod *pod = NULL; const char *format; - if (ftype == SPA_TYPE_Object) { + if (f && ftype == SPA_TYPE_Object) { uint32_t key = va_arg(args, uint32_t); const struct spa_pod_object *object; @@ -496,7 +504,7 @@ static inline int spa_pod_parser_getv(struct spa_pod_parser *parser, va_list arg return count; } -static inline int spa_pod_parser_get(struct spa_pod_parser *parser, ...) +SPA_API_POD_PARSER int spa_pod_parser_get(struct spa_pod_parser *parser, ...) { int res; va_list args; diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/support/loop.h b/src/java.desktop/unix/native/libpipewire/include/spa/support/loop.h index 4beac560b250b..376ab559de314 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/support/loop.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/support/loop.h @@ -9,10 +9,20 @@ extern "C" { #endif +#include + #include #include #include +#ifndef SPA_API_LOOP + #ifdef SPA_API_IMPL + #define SPA_API_LOOP SPA_API_IMPL + #else + #define SPA_API_LOOP static inline + #endif +#endif + /** \defgroup spa_loop Loop * Event loop interface */ @@ -66,19 +76,56 @@ struct spa_loop_methods { #define SPA_VERSION_LOOP_METHODS 0 uint32_t version; - /** add a source to the loop */ + /** Add a source to the loop. Must be called from the loop's own thread. + * + * \param[in] object The callbacks data. + * \param[in] source The source. + * \return 0 on success, negative errno-style value on failure. + */ int (*add_source) (void *object, struct spa_source *source); - /** update the source io mask */ + /** Update the source io mask. Must be called from the loop's own thread. + * + * \param[in] object The callbacks data. + * \param[in] source The source. + * \return 0 on success, negative errno-style value on failure. + */ int (*update_source) (void *object, struct spa_source *source); - /** remove a source from the loop */ + /** Remove a source from the loop. Must be called from the loop's own thread. + * + * \param[in] object The callbacks data. + * \param[in] source The source. + * \return 0 on success, negative errno-style value on failure. + */ int (*remove_source) (void *object, struct spa_source *source); - /** invoke a function in the context of this loop */ + /** Invoke a function in the context of this loop. + * May be called from any thread and multiple threads at the same time. + * If called from the loop's thread, all callbacks previously queued with + * invoke() will be run synchronously, which might cause unexpected + * reentrancy problems. + * + * \param[in] object The callbacks data. + * \param func The function to be invoked. + * \param seq An opaque sequence number. This will be made + * available to func. + * \param[in] data Data that will be copied into the internal ring buffer and made + * available to func. Because this data is copied, it is okay to + * pass a pointer to a local variable, but do not pass a pointer to + * an object that has identity. + * \param size The size of data to copy. + * \param block If \true, do not return until func has been called. Otherwise, + * returns immediately. Passing \true does not risk a deadlock because + * the data thread is never allowed to wait on any other thread. + * \param user_data An opaque pointer passed to func. + * \return `-EPIPE` if the internal ring buffer filled up, + * if block is \false, 0 if seq was SPA_ID_INVALID or + * seq with the ASYNC flag set + * or the return value of func otherwise. */ int (*invoke) (void *object, spa_invoke_func_t func, uint32_t seq, @@ -88,21 +135,29 @@ struct spa_loop_methods { void *user_data); }; -#define spa_loop_method(o,method,version,...) \ -({ \ - int _res = -ENOTSUP; \ - struct spa_loop *_o = o; \ - spa_interface_call_res(&_o->iface, \ - struct spa_loop_methods, _res, \ - method, version, ##__VA_ARGS__); \ - _res; \ -}) - -#define spa_loop_add_source(l,...) spa_loop_method(l,add_source,0,##__VA_ARGS__) -#define spa_loop_update_source(l,...) spa_loop_method(l,update_source,0,##__VA_ARGS__) -#define spa_loop_remove_source(l,...) spa_loop_method(l,remove_source,0,##__VA_ARGS__) -#define spa_loop_invoke(l,...) spa_loop_method(l,invoke,0,##__VA_ARGS__) - +SPA_API_LOOP int spa_loop_add_source(struct spa_loop *object, struct spa_source *source) +{ + return spa_api_method_r(int, -ENOTSUP, + spa_loop, &object->iface, add_source, 0, source); +} +SPA_API_LOOP int spa_loop_update_source(struct spa_loop *object, struct spa_source *source) +{ + return spa_api_method_r(int, -ENOTSUP, + spa_loop, &object->iface, update_source, 0, source); +} +SPA_API_LOOP int spa_loop_remove_source(struct spa_loop *object, struct spa_source *source) +{ + return spa_api_method_r(int, -ENOTSUP, + spa_loop, &object->iface, remove_source, 0, source); +} +SPA_API_LOOP int spa_loop_invoke(struct spa_loop *object, + spa_invoke_func_t func, uint32_t seq, const void *data, + size_t size, bool block, void *user_data) +{ + return spa_api_method_r(int, -ENOTSUP, + spa_loop, &object->iface, invoke, 0, func, seq, data, + size, block, user_data); +} /** Control hooks. These hooks can't be removed from their * callbacks and must be removed from a safe place (when the loop @@ -118,24 +173,42 @@ struct spa_loop_control_hooks { void (*after) (void *data); }; -#define spa_loop_control_hook_before(l) \ -({ \ - struct spa_hook_list *_l = l; \ - struct spa_hook *_h; \ - spa_list_for_each_reverse(_h, &_l->list, link) \ - spa_callbacks_call(&_h->cb, struct spa_loop_control_hooks, before, 0); \ -}) - -#define spa_loop_control_hook_after(l) \ -({ \ - struct spa_hook_list *_l = l; \ - struct spa_hook *_h; \ - spa_list_for_each(_h, &_l->list, link) \ - spa_callbacks_call(&_h->cb, struct spa_loop_control_hooks, after, 0); \ -}) +SPA_API_LOOP void spa_loop_control_hook_before(struct spa_hook_list *l) +{ + struct spa_hook *h; + spa_list_for_each_reverse(h, &l->list, link) + spa_callbacks_call_fast(&h->cb, struct spa_loop_control_hooks, before, 0); +} + +SPA_API_LOOP void spa_loop_control_hook_after(struct spa_hook_list *l) +{ + struct spa_hook *h; + spa_list_for_each(h, &l->list, link) + spa_callbacks_call_fast(&h->cb, struct spa_loop_control_hooks, after, 0); +} /** * Control an event loop + * + * The event loop control function provide API to run the event loop. + * + * The below (pseudo)code is a minimal example outlining the use of the loop + * control: + * \code{.c} + * spa_loop_control_enter(loop); + * while (running) { + * spa_loop_control_iterate(loop, -1); + * } + * spa_loop_control_leave(loop); + * \endcode + * + * It is also possible to add the loop to an existing event loop by using the + * spa_loop_control_get_fd() call. This fd will become readable when activity + * has been detected on the sources in the loop. spa_loop_control_iterate() with + * a 0 timeout should be called to process the pending sources. + * + * spa_loop_control_enter() and spa_loop_control_leave() should be called once + * from the thread that will run the iterate() function. */ struct spa_loop_control_methods { /* the version of this structure. This can be used to expand this @@ -143,10 +216,19 @@ struct spa_loop_control_methods { #define SPA_VERSION_LOOP_CONTROL_METHODS 1 uint32_t version; + /** get the loop fd + * \param object the control to query + * + * Get the fd of this loop control. This fd will be readable when a + * source in the loop has activity. The user should call iterate() + * with a 0 timeout to schedule one iteration of the loop and dispatch + * the sources. + * \return the fd of the loop + */ int (*get_fd) (void *object); /** Add a hook - * \param ctrl the control to change + * \param object the control to change * \param hooks the hooks to add * * Adds hooks to the loop controlled by \a ctrl. @@ -157,18 +239,19 @@ struct spa_loop_control_methods { void *data); /** Enter a loop - * \param ctrl the control + * \param object the control * - * Start an iteration of the loop. This function should be called - * before calling iterate and is typically used to capture the thread - * that this loop will run in. + * This function should be called before calling iterate and is + * typically used to capture the thread that this loop will run in. + * It should ideally be called once from the thread that will run + * the loop. */ void (*enter) (void *object); /** Leave a loop - * \param ctrl the control + * \param object the control * - * Ends the iteration of a loop. This should be called after calling - * iterate. + * It should ideally be called once after calling iterate when the loop + * will no longer be iterated from the thread that called enter(). */ void (*leave) (void *object); @@ -194,30 +277,43 @@ struct spa_loop_control_methods { int (*check) (void *object); }; -#define spa_loop_control_method_v(o,method,version,...) \ -({ \ - struct spa_loop_control *_o = o; \ - spa_interface_call(&_o->iface, \ - struct spa_loop_control_methods, \ - method, version, ##__VA_ARGS__); \ -}) - -#define spa_loop_control_method_r(o,method,version,...) \ -({ \ - int _res = -ENOTSUP; \ - struct spa_loop_control *_o = o; \ - spa_interface_call_res(&_o->iface, \ - struct spa_loop_control_methods, _res, \ - method, version, ##__VA_ARGS__); \ - _res; \ -}) - -#define spa_loop_control_get_fd(l) spa_loop_control_method_r(l,get_fd,0) -#define spa_loop_control_add_hook(l,...) spa_loop_control_method_v(l,add_hook,0,__VA_ARGS__) -#define spa_loop_control_enter(l) spa_loop_control_method_v(l,enter,0) -#define spa_loop_control_leave(l) spa_loop_control_method_v(l,leave,0) -#define spa_loop_control_iterate(l,...) spa_loop_control_method_r(l,iterate,0,__VA_ARGS__) -#define spa_loop_control_check(l) spa_loop_control_method_r(l,check,1) +SPA_API_LOOP int spa_loop_control_get_fd(struct spa_loop_control *object) +{ + return spa_api_method_r(int, -ENOTSUP, + spa_loop_control, &object->iface, get_fd, 0); +} +SPA_API_LOOP void spa_loop_control_add_hook(struct spa_loop_control *object, + struct spa_hook *hook, const struct spa_loop_control_hooks *hooks, + void *data) +{ + spa_api_method_v(spa_loop_control, &object->iface, add_hook, 0, + hook, hooks, data); +} +SPA_API_LOOP void spa_loop_control_enter(struct spa_loop_control *object) +{ + spa_api_method_v(spa_loop_control, &object->iface, enter, 0); +} +SPA_API_LOOP void spa_loop_control_leave(struct spa_loop_control *object) +{ + spa_api_method_v(spa_loop_control, &object->iface, leave, 0); +} +SPA_API_LOOP int spa_loop_control_iterate(struct spa_loop_control *object, + int timeout) +{ + return spa_api_method_r(int, -ENOTSUP, + spa_loop_control, &object->iface, iterate, 0, timeout); +} +SPA_API_LOOP int spa_loop_control_iterate_fast(struct spa_loop_control *object, + int timeout) +{ + return spa_api_method_fast_r(int, -ENOTSUP, + spa_loop_control, &object->iface, iterate, 0, timeout); +} +SPA_API_LOOP int spa_loop_control_check(struct spa_loop_control *object) +{ + return spa_api_method_r(int, -ENOTSUP, + spa_loop_control, &object->iface, check, 1); +} typedef void (*spa_source_io_func_t) (void *data, int fd, uint32_t mask); typedef void (*spa_source_idle_func_t) (void *data); @@ -268,44 +364,71 @@ struct spa_loop_utils_methods { void (*destroy_source) (void *object, struct spa_source *source); }; -#define spa_loop_utils_method_v(o,method,version,...) \ -({ \ - struct spa_loop_utils *_o = o; \ - spa_interface_call(&_o->iface, \ - struct spa_loop_utils_methods, \ - method, version, ##__VA_ARGS__); \ -}) - -#define spa_loop_utils_method_r(o,method,version,...) \ -({ \ - int _res = -ENOTSUP; \ - struct spa_loop_utils *_o = o; \ - spa_interface_call_res(&_o->iface, \ - struct spa_loop_utils_methods, _res, \ - method, version, ##__VA_ARGS__); \ - _res; \ -}) -#define spa_loop_utils_method_s(o,method,version,...) \ -({ \ - struct spa_source *_res = NULL; \ - struct spa_loop_utils *_o = o; \ - spa_interface_call_res(&_o->iface, \ - struct spa_loop_utils_methods, _res, \ - method, version, ##__VA_ARGS__); \ - _res; \ -}) - - -#define spa_loop_utils_add_io(l,...) spa_loop_utils_method_s(l,add_io,0,__VA_ARGS__) -#define spa_loop_utils_update_io(l,...) spa_loop_utils_method_r(l,update_io,0,__VA_ARGS__) -#define spa_loop_utils_add_idle(l,...) spa_loop_utils_method_s(l,add_idle,0,__VA_ARGS__) -#define spa_loop_utils_enable_idle(l,...) spa_loop_utils_method_r(l,enable_idle,0,__VA_ARGS__) -#define spa_loop_utils_add_event(l,...) spa_loop_utils_method_s(l,add_event,0,__VA_ARGS__) -#define spa_loop_utils_signal_event(l,...) spa_loop_utils_method_r(l,signal_event,0,__VA_ARGS__) -#define spa_loop_utils_add_timer(l,...) spa_loop_utils_method_s(l,add_timer,0,__VA_ARGS__) -#define spa_loop_utils_update_timer(l,...) spa_loop_utils_method_r(l,update_timer,0,__VA_ARGS__) -#define spa_loop_utils_add_signal(l,...) spa_loop_utils_method_s(l,add_signal,0,__VA_ARGS__) -#define spa_loop_utils_destroy_source(l,...) spa_loop_utils_method_v(l,destroy_source,0,__VA_ARGS__) +SPA_API_LOOP struct spa_source * +spa_loop_utils_add_io(struct spa_loop_utils *object, int fd, uint32_t mask, + bool close, spa_source_io_func_t func, void *data) +{ + return spa_api_method_r(struct spa_source *, NULL, + spa_loop_utils, &object->iface, add_io, 0, fd, mask, close, func, data); +} +SPA_API_LOOP int spa_loop_utils_update_io(struct spa_loop_utils *object, + struct spa_source *source, uint32_t mask) +{ + return spa_api_method_r(int, -ENOTSUP, + spa_loop_utils, &object->iface, update_io, 0, source, mask); +} +SPA_API_LOOP struct spa_source * +spa_loop_utils_add_idle(struct spa_loop_utils *object, bool enabled, + spa_source_idle_func_t func, void *data) +{ + return spa_api_method_r(struct spa_source *, NULL, + spa_loop_utils, &object->iface, add_idle, 0, enabled, func, data); +} +SPA_API_LOOP int spa_loop_utils_enable_idle(struct spa_loop_utils *object, + struct spa_source *source, bool enabled) +{ + return spa_api_method_r(int, -ENOTSUP, + spa_loop_utils, &object->iface, enable_idle, 0, source, enabled); +} +SPA_API_LOOP struct spa_source * +spa_loop_utils_add_event(struct spa_loop_utils *object, spa_source_event_func_t func, void *data) +{ + return spa_api_method_r(struct spa_source *, NULL, + spa_loop_utils, &object->iface, add_event, 0, func, data); +} +SPA_API_LOOP int spa_loop_utils_signal_event(struct spa_loop_utils *object, + struct spa_source *source) +{ + return spa_api_method_r(int, -ENOTSUP, + spa_loop_utils, &object->iface, signal_event, 0, source); +} +SPA_API_LOOP struct spa_source * +spa_loop_utils_add_timer(struct spa_loop_utils *object, spa_source_timer_func_t func, void *data) +{ + return spa_api_method_r(struct spa_source *, NULL, + spa_loop_utils, &object->iface, add_timer, 0, func, data); +} +SPA_API_LOOP int spa_loop_utils_update_timer(struct spa_loop_utils *object, + struct spa_source *source, struct timespec *value, + struct timespec *interval, bool absolute) +{ + return spa_api_method_r(int, -ENOTSUP, + spa_loop_utils, &object->iface, update_timer, 0, source, + value, interval, absolute); +} +SPA_API_LOOP struct spa_source * +spa_loop_utils_add_signal(struct spa_loop_utils *object, int signal_number, + spa_source_signal_func_t func, void *data) +{ + return spa_api_method_r(struct spa_source *, NULL, + spa_loop_utils, &object->iface, add_signal, 0, + signal_number, func, data); +} +SPA_API_LOOP void spa_loop_utils_destroy_source(struct spa_loop_utils *object, + struct spa_source *source) +{ + spa_api_method_v(spa_loop_utils, &object->iface, destroy_source, 0, source); +} /** * \} diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/support/system.h b/src/java.desktop/unix/native/libpipewire/include/spa/support/system.h index 6bd9dcd015985..fb58dc337b636 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/support/system.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/support/system.h @@ -12,11 +12,20 @@ extern "C" { struct itimerspec; #include +#include #include #include #include +#ifndef SPA_API_SYSTEM + #ifdef SPA_API_IMPL + #define SPA_API_SYSTEM SPA_API_IMPL + #else + #define SPA_API_SYSTEM static inline + #endif +#endif + /** \defgroup spa_system System * I/O, clock, polling, timer, and signal interfaces */ @@ -97,42 +106,106 @@ struct spa_system_methods { int (*signalfd_read) (void *object, int fd, int *signal); }; -#define spa_system_method_r(o,method,version,...) \ -({ \ - volatile int _res = -ENOTSUP; \ - struct spa_system *_o = o; \ - spa_interface_call_res(&_o->iface, \ - struct spa_system_methods, _res, \ - method, version, ##__VA_ARGS__); \ - _res; \ -}) - - -#define spa_system_read(s,...) spa_system_method_r(s,read,0,__VA_ARGS__) -#define spa_system_write(s,...) spa_system_method_r(s,write,0,__VA_ARGS__) -#define spa_system_ioctl(s,...) spa_system_method_r(s,ioctl,0,__VA_ARGS__) -#define spa_system_close(s,...) spa_system_method_r(s,close,0,__VA_ARGS__) - -#define spa_system_clock_gettime(s,...) spa_system_method_r(s,clock_gettime,0,__VA_ARGS__) -#define spa_system_clock_getres(s,...) spa_system_method_r(s,clock_getres,0,__VA_ARGS__) - -#define spa_system_pollfd_create(s,...) spa_system_method_r(s,pollfd_create,0,__VA_ARGS__) -#define spa_system_pollfd_add(s,...) spa_system_method_r(s,pollfd_add,0,__VA_ARGS__) -#define spa_system_pollfd_mod(s,...) spa_system_method_r(s,pollfd_mod,0,__VA_ARGS__) -#define spa_system_pollfd_del(s,...) spa_system_method_r(s,pollfd_del,0,__VA_ARGS__) -#define spa_system_pollfd_wait(s,...) spa_system_method_r(s,pollfd_wait,0,__VA_ARGS__) - -#define spa_system_timerfd_create(s,...) spa_system_method_r(s,timerfd_create,0,__VA_ARGS__) -#define spa_system_timerfd_settime(s,...) spa_system_method_r(s,timerfd_settime,0,__VA_ARGS__) -#define spa_system_timerfd_gettime(s,...) spa_system_method_r(s,timerfd_gettime,0,__VA_ARGS__) -#define spa_system_timerfd_read(s,...) spa_system_method_r(s,timerfd_read,0,__VA_ARGS__) - -#define spa_system_eventfd_create(s,...) spa_system_method_r(s,eventfd_create,0,__VA_ARGS__) -#define spa_system_eventfd_write(s,...) spa_system_method_r(s,eventfd_write,0,__VA_ARGS__) -#define spa_system_eventfd_read(s,...) spa_system_method_r(s,eventfd_read,0,__VA_ARGS__) - -#define spa_system_signalfd_create(s,...) spa_system_method_r(s,signalfd_create,0,__VA_ARGS__) -#define spa_system_signalfd_read(s,...) spa_system_method_r(s,signalfd_read,0,__VA_ARGS__) +SPA_API_SYSTEM ssize_t spa_system_read(struct spa_system *object, int fd, void *buf, size_t count) +{ + return spa_api_method_fast_r(ssize_t, -ENOTSUP, spa_system, &object->iface, read, 0, fd, buf, count); +} +SPA_API_SYSTEM ssize_t spa_system_write(struct spa_system *object, int fd, const void *buf, size_t count) +{ + return spa_api_method_fast_r(ssize_t, -ENOTSUP, spa_system, &object->iface, write, 0, fd, buf, count); +} +#define spa_system_ioctl(object,fd,request,...) \ + spa_api_method_fast_r(int, -ENOTSUP, spa_system, &object->iface, ioctl, 0, fd, request, ##__VA_ARGS__) + +SPA_API_SYSTEM int spa_system_close(struct spa_system *object, int fd) +{ + return spa_api_method_fast_r(int, -ENOTSUP, spa_system, &object->iface, close, 0, fd); +} +SPA_API_SYSTEM int spa_system_clock_gettime(struct spa_system *object, + int clockid, struct timespec *value) +{ + return spa_api_method_fast_r(int, -ENOTSUP, spa_system, &object->iface, clock_gettime, 0, clockid, value); +} +SPA_API_SYSTEM int spa_system_clock_getres(struct spa_system *object, + int clockid, struct timespec *res) +{ + return spa_api_method_fast_r(int, -ENOTSUP, spa_system, &object->iface, clock_getres, 0, clockid, res); +} + +SPA_API_SYSTEM int spa_system_pollfd_create(struct spa_system *object, int flags) +{ + return spa_api_method_fast_r(int, -ENOTSUP, spa_system, &object->iface, pollfd_create, 0, flags); +} +SPA_API_SYSTEM int spa_system_pollfd_add(struct spa_system *object, int pfd, int fd, uint32_t events, void *data) +{ + return spa_api_method_fast_r(int, -ENOTSUP, spa_system, &object->iface, pollfd_add, 0, pfd, fd, events, data); +} +SPA_API_SYSTEM int spa_system_pollfd_mod(struct spa_system *object, int pfd, int fd, uint32_t events, void *data) +{ + return spa_api_method_fast_r(int, -ENOTSUP, spa_system, &object->iface, pollfd_mod, 0, pfd, fd, events, data); +} +SPA_API_SYSTEM int spa_system_pollfd_del(struct spa_system *object, int pfd, int fd) +{ + return spa_api_method_fast_r(int, -ENOTSUP, spa_system, &object->iface, pollfd_del, 0, pfd, fd); +} +SPA_API_SYSTEM int spa_system_pollfd_wait(struct spa_system *object, int pfd, + struct spa_poll_event *ev, int n_ev, int timeout) +{ + return spa_api_method_fast_r(int, -ENOTSUP, spa_system, &object->iface, pollfd_wait, 0, pfd, ev, n_ev, timeout); +} + +SPA_API_SYSTEM int spa_system_timerfd_create(struct spa_system *object, int clockid, int flags) +{ + return spa_api_method_fast_r(int, -ENOTSUP, spa_system, &object->iface, timerfd_create, 0, clockid, flags); +} + +SPA_API_SYSTEM int spa_system_timerfd_settime(struct spa_system *object, + int fd, int flags, + const struct itimerspec *new_value, + struct itimerspec *old_value) +{ + return spa_api_method_fast_r(int, -ENOTSUP, spa_system, &object->iface, timerfd_settime, 0, + fd, flags, new_value, old_value); +} + +SPA_API_SYSTEM int spa_system_timerfd_gettime(struct spa_system *object, + int fd, struct itimerspec *curr_value) +{ + return spa_api_method_fast_r(int, -ENOTSUP, spa_system, &object->iface, timerfd_gettime, 0, + fd, curr_value); +} +SPA_API_SYSTEM int spa_system_timerfd_read(struct spa_system *object, int fd, uint64_t *expirations) +{ + return spa_api_method_fast_r(int, -ENOTSUP, spa_system, &object->iface, timerfd_read, 0, + fd, expirations); +} + +SPA_API_SYSTEM int spa_system_eventfd_create(struct spa_system *object, int flags) +{ + return spa_api_method_fast_r(int, -ENOTSUP, spa_system, &object->iface, eventfd_create, 0, flags); +} +SPA_API_SYSTEM int spa_system_eventfd_write(struct spa_system *object, int fd, uint64_t count) +{ + return spa_api_method_fast_r(int, -ENOTSUP, spa_system, &object->iface, eventfd_write, 0, + fd, count); +} +SPA_API_SYSTEM int spa_system_eventfd_read(struct spa_system *object, int fd, uint64_t *count) +{ + return spa_api_method_fast_r(int, -ENOTSUP, spa_system, &object->iface, eventfd_read, 0, + fd, count); +} + +SPA_API_SYSTEM int spa_system_signalfd_create(struct spa_system *object, int signal, int flags) +{ + return spa_api_method_fast_r(int, -ENOTSUP, spa_system, &object->iface, signalfd_create, 0, + signal, flags); +} + +SPA_API_SYSTEM int spa_system_signalfd_read(struct spa_system *object, int fd, int *signal) +{ + return spa_api_method_fast_r(int, -ENOTSUP, spa_system, &object->iface, signalfd_read, 0, + fd, signal); +} /** * \} diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/utils/cleanup.h b/src/java.desktop/unix/native/libpipewire/include/spa/utils/cleanup.h new file mode 100644 index 0000000000000..944907ccffbd2 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/utils/cleanup.h @@ -0,0 +1,124 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2023 PipeWire authors */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_UTILS_CLEANUP_H +#define SPA_UTILS_CLEANUP_H + +#define spa_exchange(var, new_value) \ +__extension__ ({ \ + __typeof__(var) *_ptr_ = &(var); \ + __typeof__(var) _old_value_ = *_ptr_; \ + *_ptr_ = (new_value); \ + _old_value_; \ +}) + +/* ========================================================================== */ + +#if __GNUC__ >= 10 || defined(__clang__) +#define spa_steal_ptr(ptr) ((__typeof__(*(ptr)) *) spa_exchange((ptr), NULL)) +#else +#define spa_steal_ptr(ptr) spa_exchange((ptr), NULL) +#endif + +#define spa_clear_ptr(ptr, destructor) \ +__extension__ ({ \ + __typeof__(ptr) _old_value = spa_steal_ptr(ptr); \ + if (_old_value) \ + destructor(_old_value); \ + (void) 0; \ +}) + +/* ========================================================================== */ + +#include +#include + +#define spa_steal_fd(fd) spa_exchange((fd), -1) + +#define spa_clear_fd(fd) \ +__extension__ ({ \ + int _old_value = spa_steal_fd(fd), _res = 0; \ + if (_old_value >= 0) \ + _res = close(_old_value); \ + _res; \ +}) + +/* ========================================================================== */ + +#if defined(__has_attribute) && __has_attribute(__cleanup__) + +#define spa_cleanup(func) __attribute__((__cleanup__(func))) + +#define SPA_DEFINE_AUTO_CLEANUP(name, type, ...) \ +typedef __typeof__(type) _spa_auto_cleanup_type_ ## name; \ +static inline void _spa_auto_cleanup_func_ ## name (__typeof__(type) *thing) \ +{ \ + int _save_errno = errno; \ + __VA_ARGS__ \ + errno = _save_errno; \ +} + +#define spa_auto(name) \ + spa_cleanup(_spa_auto_cleanup_func_ ## name) \ + _spa_auto_cleanup_type_ ## name + +#define SPA_DEFINE_AUTOPTR_CLEANUP(name, type, ...) \ +typedef __typeof__(type) * _spa_autoptr_cleanup_type_ ## name; \ +static inline void _spa_autoptr_cleanup_func_ ## name (__typeof__(type) **thing) \ +{ \ + int _save_errno = errno; \ + __VA_ARGS__ \ + errno = _save_errno; \ +} + +#define spa_autoptr(name) \ + spa_cleanup(_spa_autoptr_cleanup_func_ ## name) \ + _spa_autoptr_cleanup_type_ ## name + +/* ========================================================================== */ + +#include + +static inline void _spa_autofree_cleanup_func(void *p) +{ + int save_errno = errno; + free(*(void **) p); + errno = save_errno; +} +#define spa_autofree spa_cleanup(_spa_autofree_cleanup_func) + +/* ========================================================================== */ + +static inline void _spa_autoclose_cleanup_func(int *fd) +{ + int save_errno = errno; + spa_clear_fd(*fd); + errno = save_errno; +} +#define spa_autoclose spa_cleanup(_spa_autoclose_cleanup_func) + +/* ========================================================================== */ + +#include + +SPA_DEFINE_AUTOPTR_CLEANUP(FILE, FILE, { + spa_clear_ptr(*thing, fclose); +}) + +/* ========================================================================== */ + +#include + +SPA_DEFINE_AUTOPTR_CLEANUP(DIR, DIR, { + spa_clear_ptr(*thing, closedir); +}) + +#else + +#define SPA_DEFINE_AUTO_CLEANUP(name, type, ...) +#define SPA_DEFINE_AUTOPTR_CLEANUP(name, type, ...) + +#endif + +#endif /* SPA_UTILS_CLEANUP_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/utils/defs.h b/src/java.desktop/unix/native/libpipewire/include/spa/utils/defs.h index 66c238987d0ac..ace677015cad2 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/utils/defs.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/utils/defs.h @@ -9,20 +9,31 @@ extern "C" { # if __cplusplus >= 201103L # define SPA_STATIC_ASSERT_IMPL(expr, msg, ...) static_assert(expr, msg) +# define SPA_ALIGNOF alignof # endif +#elif __STDC_VERSION__ >= 202311L +# define SPA_STATIC_ASSERT_IMPL(expr, msg, ...) static_assert(expr, msg) +# define SPA_ALIGNOF alignof #else # include # if __STDC_VERSION__ >= 201112L # define SPA_STATIC_ASSERT_IMPL(expr, msg, ...) _Static_assert(expr, msg) +# define SPA_ALIGNOF _Alignof # endif #endif #ifndef SPA_STATIC_ASSERT_IMPL #define SPA_STATIC_ASSERT_IMPL(expr, ...) \ ((void)sizeof(struct { int spa_static_assertion_failed : 2 * !!(expr) - 1; })) #endif +#ifndef SPA_ALIGNOF +#define SPA_ALIGNOF __alignof__ +#endif #define SPA_STATIC_ASSERT(expr, ...) SPA_STATIC_ASSERT_IMPL(expr, ## __VA_ARGS__, "`" #expr "` evaluated to false") +#define SPA_CONCAT_NOEXPAND(a, b) a ## b +#define SPA_CONCAT(a, b) SPA_CONCAT_NOEXPAND(a, b) + #include #include #include @@ -122,10 +133,10 @@ struct spa_fraction { * ``` */ #define SPA_FOR_EACH_ELEMENT(arr, ptr) \ - for ((ptr) = arr; (void*)(ptr) < SPA_PTROFF(arr, sizeof(arr), void); (ptr)++) + for ((ptr) = arr; (ptr) < (arr) + SPA_N_ELEMENTS(arr); (ptr)++) #define SPA_FOR_EACH_ELEMENT_VAR(arr, var) \ - for (__typeof__((arr)[0])* var = arr; (void*)(var) < SPA_PTROFF(arr, sizeof(arr), void); (var)++) + for (__typeof__((arr)[0])* var = arr; (var) < (arr) + SPA_N_ELEMENTS(arr); (var)++) #define SPA_ABS(a) \ ({ \ @@ -156,6 +167,10 @@ struct spa_fraction { ({ \ fminf(fmaxf(v, low), high); \ }) +#define SPA_CLAMPD(v,low,high) \ +({ \ + fmin(fmax(v, low), high); \ +}) #define SPA_SWAP(a,b) \ @@ -171,6 +186,16 @@ struct spa_fraction { x; \ }) +/** 3-way comparison. NaN > NaN and NaN > finite numbers */ +#define SPA_CMP(a, b) \ +({ \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + (_a > _b) ? 1 : (_a == _b) ? 0 : (_a < _b) ? -1 \ + : (_a == _a) ? -1 : (_b == _b) ? 1 \ + : 1; \ +}) + /** * Return the address (buffer + offset) as pointer of \a type */ @@ -178,7 +203,6 @@ struct spa_fraction { #define SPA_PTROFF_ALIGN(ptr_,offset_,alignment_,type_) \ SPA_PTR_ALIGN(SPA_PTROFF(ptr_,offset_,type_),alignment_,type_) - /** * Deprecated, use SPA_PTROFF and SPA_PTROFF_ALIGN instead */ @@ -189,9 +213,6 @@ struct spa_fraction { #define SPA_PTRDIFF(p1,p2) ((intptr_t)(p1) - (intptr_t)(p2)) -#define SPA_PTR_TO_INT(p) ((int) ((intptr_t) (p))) -#define SPA_INT_TO_PTR(u) ((void*) ((intptr_t) (u))) - #define SPA_PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p))) #define SPA_UINT32_TO_PTR(u) ((void*) ((uintptr_t) (u))) @@ -233,6 +254,20 @@ struct spa_fraction { #define SPA_WARN_UNUSED_RESULT #endif +#ifndef SPA_API_IMPL +#define SPA_API_PROTO static inline +#define SPA_API_IMPL static inline +#endif + +#ifndef SPA_API_UTILS_DEFS + #ifdef SPA_API_IMPL + #define SPA_API_UTILS_DEFS SPA_API_IMPL + #else + #define SPA_API_UTILS_DEFS static inline + #endif +#endif + + #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L #define SPA_RESTRICT restrict #elif defined(__GNUC__) && __GNUC__ >= 4 @@ -265,7 +300,7 @@ struct spa_fraction { }) -#define SPA_PTR_ALIGNMENT(p,align) ((intptr_t)(p) & ((align)-1)) +#define SPA_PTR_ALIGNMENT(p,align) ((uintptr_t)(p) & ((align)-1)) #define SPA_IS_ALIGNED(p,align) (SPA_PTR_ALIGNMENT(p,align) == 0) #define SPA_PTR_ALIGN(p,align,type) ((type*)SPA_ROUND_UP_N((intptr_t)(p), (intptr_t)(align))) @@ -279,9 +314,51 @@ struct spa_fraction { #endif #endif +SPA_API_UTILS_DEFS bool spa_ptrinside(const void *p1, size_t s1, const void *p2, size_t s2, + size_t *remaining) +{ + if (SPA_LIKELY((uintptr_t)p1 <= (uintptr_t)p2 && s2 <= s1 && + (uintptr_t)p2 - (uintptr_t)p1 <= s1 - s2)) { + if (remaining != NULL) + *remaining = ((uintptr_t)p1 + s1) - ((uintptr_t)p2 + s2); + return true; + } else { + if (remaining != NULL) + *remaining = 0; + return false; + } +} + +SPA_API_UTILS_DEFS bool spa_ptr_inside_and_aligned(const void *p1, size_t s1, + const void *p2, size_t s2, size_t align, + size_t *remaining) +{ + if (SPA_IS_ALIGNED(p2, align)) { + return spa_ptrinside(p1, s1, p2, s2, remaining); + } else { + if (remaining != NULL) + *remaining = 0; + return false; + } +} + +#define spa_ptr_type_inside(p1, s1, p2, type, remaining) \ + spa_ptr_inside_and_aligned(p1, s1, p2, sizeof(type), SPA_ALIGNOF(type), remaining) + +#define SPA_PTR_TO_INT(p) ((int) ((intptr_t) (p))) +#define SPA_INT_TO_PTR(u) ((void*) ((intptr_t) (u))) + #define SPA_STRINGIFY_1(...) #__VA_ARGS__ #define SPA_STRINGIFY(...) SPA_STRINGIFY_1(__VA_ARGS__) +struct spa_error_location { + int line; + int col; + size_t len; + const char *location; + const char *reason; +}; + #define spa_return_if_fail(expr) \ do { \ if (SPA_UNLIKELY(!(expr))) { \ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/utils/dict.h b/src/java.desktop/unix/native/libpipewire/include/spa/utils/dict.h index f34cd21f84078..d9258a5fe1c4d 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/utils/dict.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/utils/dict.h @@ -13,6 +13,14 @@ extern "C" { #include +#ifndef SPA_API_DICT + #ifdef SPA_API_IMPL + #define SPA_API_DICT SPA_API_IMPL + #else + #define SPA_API_DICT static inline + #endif +#endif + /** * \defgroup spa_dict Dictionary * Dictionary data structure @@ -28,7 +36,8 @@ struct spa_dict_item { const char *value; }; -#define SPA_DICT_ITEM_INIT(key,value) ((struct spa_dict_item) { (key), (value) }) +#define SPA_DICT_ITEM(key,value) ((struct spa_dict_item) { (key), (value) }) +#define SPA_DICT_ITEM_INIT(key,value) SPA_DICT_ITEM(key,value) struct spa_dict { #define SPA_DICT_FLAG_SORTED (1<<0) /**< items are sorted */ @@ -37,22 +46,26 @@ struct spa_dict { const struct spa_dict_item *items; }; -#define SPA_DICT_INIT(items,n_items) ((struct spa_dict) { 0, (n_items), (items) }) -#define SPA_DICT_INIT_ARRAY(items) ((struct spa_dict) { 0, SPA_N_ELEMENTS(items), (items) }) +#define SPA_DICT(items,n_items) ((struct spa_dict) { 0, (n_items), (items) }) +#define SPA_DICT_ARRAY(items) SPA_DICT((items),SPA_N_ELEMENTS(items)) +#define SPA_DICT_ITEMS(...) SPA_DICT_ARRAY(((struct spa_dict_item[]) { __VA_ARGS__})) + +#define SPA_DICT_INIT(items,n_items) SPA_DICT(items,n_items) +#define SPA_DICT_INIT_ARRAY(items) SPA_DICT_ARRAY(items) #define spa_dict_for_each(item, dict) \ for ((item) = (dict)->items; \ (item) < &(dict)->items[(dict)->n_items]; \ (item)++) -static inline int spa_dict_item_compare(const void *i1, const void *i2) +SPA_API_DICT int spa_dict_item_compare(const void *i1, const void *i2) { const struct spa_dict_item *it1 = (const struct spa_dict_item *)i1, *it2 = (const struct spa_dict_item *)i2; return strcmp(it1->key, it2->key); } -static inline void spa_dict_qsort(struct spa_dict *dict) +SPA_API_DICT void spa_dict_qsort(struct spa_dict *dict) { if (dict->n_items > 0) qsort((void*)dict->items, dict->n_items, sizeof(struct spa_dict_item), @@ -60,7 +73,7 @@ static inline void spa_dict_qsort(struct spa_dict *dict) SPA_FLAG_SET(dict->flags, SPA_DICT_FLAG_SORTED); } -static inline const struct spa_dict_item *spa_dict_lookup_item(const struct spa_dict *dict, +SPA_API_DICT const struct spa_dict_item *spa_dict_lookup_item(const struct spa_dict *dict, const char *key) { const struct spa_dict_item *item; @@ -83,7 +96,7 @@ static inline const struct spa_dict_item *spa_dict_lookup_item(const struct spa_ return NULL; } -static inline const char *spa_dict_lookup(const struct spa_dict *dict, const char *key) +SPA_API_DICT const char *spa_dict_lookup(const struct spa_dict *dict, const char *key) { const struct spa_dict_item *item = spa_dict_lookup_item(dict, key); return item ? item->value : NULL; diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/utils/endian.h b/src/java.desktop/unix/native/libpipewire/include/spa/utils/endian.h new file mode 100644 index 0000000000000..2d002d4538c30 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/utils/endian.h @@ -0,0 +1,26 @@ +/* Spa */ +/* SPDX-FileCopyrightText: Copyright © 2019 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_ENDIAN_H +#define SPA_ENDIAN_H + +#if defined(__FreeBSD__) || defined(__MidnightBSD__) +#include +#define bswap_16 bswap16 +#define bswap_32 bswap32 +#define bswap_64 bswap64 +#elif defined(_MSC_VER) && defined(_WIN32) +#include +#define __LITTLE_ENDIAN 1234 +#define __BIG_ENDIAN 4321 +#define __BYTE_ORDER __LITTLE_ENDIAN +#define bswap_16 _byteswap_ushort +#define bswap_32 _byteswap_ulong +#define bswap_64 _byteswap_uint64 +#else +#include +#include +#endif + +#endif /* SPA_ENDIAN_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/utils/enum-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/utils/enum-types.h index b374995b5cf1f..c1313f0390ef5 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/utils/enum-types.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/utils/enum-types.h @@ -10,6 +10,12 @@ extern "C" { #endif #include +#include + +/** + * \addtogroup spa_types + * \{ + */ #define SPA_TYPE_INFO_Direction SPA_TYPE_INFO_ENUM_BASE "Direction" #define SPA_TYPE_INFO_DIRECTION_BASE SPA_TYPE_INFO_Direction ":" @@ -20,8 +26,6 @@ static const struct spa_type_info spa_type_direction[] = { { 0, 0, NULL, NULL } }; -#include - #define SPA_TYPE_INFO_Choice SPA_TYPE_INFO_ENUM_BASE "Choice" #define SPA_TYPE_INFO_CHOICE_BASE SPA_TYPE_INFO_Choice ":" diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/utils/hook.h b/src/java.desktop/unix/native/libpipewire/include/spa/utils/hook.h index 81fc07b8a05d9..566433bf8de29 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/utils/hook.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/utils/hook.h @@ -12,6 +12,14 @@ extern "C" { #include #include +#ifndef SPA_API_HOOK + #ifdef SPA_API_IMPL + #define SPA_API_HOOK SPA_API_IMPL + #else + #define SPA_API_HOOK static inline + #endif +#endif + /** \defgroup spa_interfaces Interfaces * * \brief Generic implementation of implementation-independent interfaces @@ -158,10 +166,18 @@ struct spa_interface { const type *_f = (const type *) (callbacks)->funcs; \ bool _res = SPA_CALLBACK_CHECK(_f,method,vers); \ if (SPA_LIKELY(_res)) \ - _f->method((callbacks)->data, ## __VA_ARGS__); \ + (_f->method)((callbacks)->data, ## __VA_ARGS__); \ _res; \ }) +#define spa_callbacks_call_fast(callbacks,type,method,vers,...) \ +({ \ + const type *_f = (const type *) (callbacks)->funcs; \ + (_f->method)((callbacks)->data, ## __VA_ARGS__); \ + true; \ +}) + + /** * True if the \a callbacks are of version \a vers, false otherwise */ @@ -191,9 +207,14 @@ struct spa_interface { ({ \ const type *_f = (const type *) (callbacks)->funcs; \ if (SPA_LIKELY(SPA_CALLBACK_CHECK(_f,method,vers))) \ - res = _f->method((callbacks)->data, ## __VA_ARGS__); \ + res = (_f->method)((callbacks)->data, ## __VA_ARGS__); \ res; \ }) +#define spa_callbacks_call_fast_res(callbacks,type,res,method,vers,...) \ +({ \ + const type *_f = (const type *) (callbacks)->funcs; \ + res = (_f->method)((callbacks)->data, ## __VA_ARGS__); \ +}) /** * True if the \a iface's callbacks are of version \a vers, false otherwise @@ -216,6 +237,9 @@ struct spa_interface { #define spa_interface_call(iface,method_type,method,vers,...) \ spa_callbacks_call(&(iface)->cb,method_type,method,vers,##__VA_ARGS__) +#define spa_interface_call_fast(iface,method_type,method,vers,...) \ + spa_callbacks_call_fast(&(iface)->cb,method_type,method,vers,##__VA_ARGS__) + /** * Invoke method named \a method in the callbacks on the given interface object. * The \a method_type defines the type of the method struct, not the interface @@ -226,6 +250,76 @@ struct spa_interface { #define spa_interface_call_res(iface,method_type,res,method,vers,...) \ spa_callbacks_call_res(&(iface)->cb,method_type,res,method,vers,##__VA_ARGS__) +#define spa_interface_call_fast_res(iface,method_type,res,method,vers,...) \ + spa_callbacks_call_fast_res(&(iface)->cb,method_type,res,method,vers,##__VA_ARGS__) + + +#define spa_api_func_v(o,method,version,...) \ +({ \ + if (SPA_LIKELY(SPA_CALLBACK_CHECK(o,method,version))) \ + ((o)->method)(o, ##__VA_ARGS__); \ +}) +#define spa_api_func_r(rtype,def,o,method,version,...) \ +({ \ + rtype _res = def; \ + if (SPA_LIKELY(SPA_CALLBACK_CHECK(o,method,version))) \ + _res = ((o)->method)(o, ##__VA_ARGS__); \ + _res; \ +}) +#define spa_api_func_fast(o,method,...) \ +({ \ + ((o)->method)(o, ##__VA_ARGS__); \ +}) + +#define spa_api_method_v(type,o,method,version,...) \ +({ \ + struct spa_interface *_i = o; \ + spa_interface_call(_i, struct type ##_methods, \ + method, version, ##__VA_ARGS__); \ +}) +#define spa_api_method_r(rtype,def,type,o,method,version,...) \ +({ \ + rtype _res = def; \ + struct spa_interface *_i = o; \ + spa_interface_call_res(_i, struct type ##_methods, \ + _res, method, version, ##__VA_ARGS__); \ + _res; \ +}) +#define spa_api_method_null_v(type,co,o,method,version,...) \ +({ \ + struct type *_co = co; \ + if (SPA_LIKELY(_co != NULL)) { \ + struct spa_interface *_i = o; \ + spa_interface_call(_i, struct type ##_methods, \ + method, version, ##__VA_ARGS__); \ + } \ +}) +#define spa_api_method_null_r(rtype,def,type,co,o,method,version,...) \ +({ \ + rtype _res = def; \ + struct type *_co = co; \ + if (SPA_LIKELY(_co != NULL)) { \ + struct spa_interface *_i = o; \ + spa_interface_call_res(_i, struct type ##_methods, \ + _res, method, version, ##__VA_ARGS__); \ + } \ + _res; \ +}) +#define spa_api_method_fast_v(type,o,method,version,...) \ +({ \ + struct spa_interface *_i = o; \ + spa_interface_call_fast(_i, struct type ##_methods, \ + method, version, ##__VA_ARGS__); \ +}) +#define spa_api_method_fast_r(rtype,def,type,o,method,version,...) \ +({ \ + rtype _res = def; \ + struct spa_interface *_i = o; \ + spa_interface_call_fast_res(_i, struct type ##_methods, \ + _res, method, version, ##__VA_ARGS__); \ + _res; \ +}) + /** * \} */ @@ -329,18 +423,18 @@ struct spa_hook { }; /** Initialize a hook list to the empty list*/ -static inline void spa_hook_list_init(struct spa_hook_list *list) +SPA_API_HOOK void spa_hook_list_init(struct spa_hook_list *list) { spa_list_init(&list->list); } -static inline bool spa_hook_list_is_empty(struct spa_hook_list *list) +SPA_API_HOOK bool spa_hook_list_is_empty(struct spa_hook_list *list) { return spa_list_is_empty(&list->list); } /** Append a hook. */ -static inline void spa_hook_list_append(struct spa_hook_list *list, +SPA_API_HOOK void spa_hook_list_append(struct spa_hook_list *list, struct spa_hook *hook, const void *funcs, void *data) { @@ -350,7 +444,7 @@ static inline void spa_hook_list_append(struct spa_hook_list *list, } /** Prepend a hook */ -static inline void spa_hook_list_prepend(struct spa_hook_list *list, +SPA_API_HOOK void spa_hook_list_prepend(struct spa_hook_list *list, struct spa_hook *hook, const void *funcs, void *data) { @@ -360,7 +454,7 @@ static inline void spa_hook_list_prepend(struct spa_hook_list *list, } /** Remove a hook */ -static inline void spa_hook_remove(struct spa_hook *hook) +SPA_API_HOOK void spa_hook_remove(struct spa_hook *hook) { if (spa_list_is_initialized(&hook->link)) spa_list_remove(&hook->link); @@ -369,14 +463,14 @@ static inline void spa_hook_remove(struct spa_hook *hook) } /** Remove all hooks from the list */ -static inline void spa_hook_list_clean(struct spa_hook_list *list) +SPA_API_HOOK void spa_hook_list_clean(struct spa_hook_list *list) { struct spa_hook *h; spa_list_consume(h, &list->list, link) spa_hook_remove(h); } -static inline void +SPA_API_HOOK void spa_hook_list_isolate(struct spa_hook_list *list, struct spa_hook_list *save, struct spa_hook *hook, @@ -390,7 +484,7 @@ spa_hook_list_isolate(struct spa_hook_list *list, spa_hook_list_append(list, hook, funcs, data); } -static inline void +SPA_API_HOOK void spa_hook_list_join(struct spa_hook_list *list, struct spa_hook_list *save) { diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/utils/list.h b/src/java.desktop/unix/native/libpipewire/include/spa/utils/list.h index 5d4169217b52f..6bcc0e2328b53 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/utils/list.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/utils/list.h @@ -9,6 +9,16 @@ extern "C" { #endif +#include + +#ifndef SPA_API_LIST + #ifdef SPA_API_IMPL + #define SPA_API_LIST SPA_API_IMPL + #else + #define SPA_API_LIST static inline + #endif +#endif + /** * \defgroup spa_list List * Doubly linked list data structure @@ -26,19 +36,19 @@ struct spa_list { #define SPA_LIST_INIT(list) ((struct spa_list){ (list), (list) }) -static inline void spa_list_init(struct spa_list *list) +SPA_API_LIST void spa_list_init(struct spa_list *list) { *list = SPA_LIST_INIT(list); } -static inline int spa_list_is_initialized(struct spa_list *list) +SPA_API_LIST int spa_list_is_initialized(struct spa_list *list) { return !!list->prev; } #define spa_list_is_empty(l) ((l)->next == (l)) -static inline void spa_list_insert(struct spa_list *list, struct spa_list *elem) +SPA_API_LIST void spa_list_insert(struct spa_list *list, struct spa_list *elem) { elem->prev = list; elem->next = list->next; @@ -46,7 +56,7 @@ static inline void spa_list_insert(struct spa_list *list, struct spa_list *elem) elem->next->prev = elem; } -static inline void spa_list_insert_list(struct spa_list *list, struct spa_list *other) +SPA_API_LIST void spa_list_insert_list(struct spa_list *list, struct spa_list *other) { if (spa_list_is_empty(other)) return; @@ -56,7 +66,7 @@ static inline void spa_list_insert_list(struct spa_list *list, struct spa_list * list->next = other->next; } -static inline void spa_list_remove(struct spa_list *elem) +SPA_API_LIST void spa_list_remove(struct spa_list *elem) { elem->prev->next = elem->next; elem->next->prev = elem->prev; diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/utils/string.h b/src/java.desktop/unix/native/libpipewire/include/spa/utils/string.h index 529b8fa38cdc6..d225a3c87c3d2 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/utils/string.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/utils/string.h @@ -17,6 +17,14 @@ extern "C" { #include +#ifndef SPA_API_STRING + #ifdef SPA_API_IMPL + #define SPA_API_STRING SPA_API_IMPL + #else + #define SPA_API_STRING static inline + #endif +#endif + /** * \defgroup spa_string String handling * String handling utilities @@ -33,7 +41,7 @@ extern "C" { * If both \a a and \a b are NULL, the two are considered equal. * */ -static inline bool spa_streq(const char *s1, const char *s2) +SPA_API_STRING bool spa_streq(const char *s1, const char *s2) { return SPA_LIKELY(s1 && s2) ? strcmp(s1, s2) == 0 : s1 == s2; } @@ -43,7 +51,7 @@ static inline bool spa_streq(const char *s1, const char *s2) * * If both \a a and \a b are NULL, the two are considered equal. */ -static inline bool spa_strneq(const char *s1, const char *s2, size_t len) +SPA_API_STRING bool spa_strneq(const char *s1, const char *s2, size_t len) { return SPA_LIKELY(s1 && s2) ? strncmp(s1, s2, len) == 0 : s1 == s2; } @@ -54,7 +62,7 @@ static inline bool spa_strneq(const char *s1, const char *s2, size_t len) * A \a s is NULL, it never starts with the given \a prefix. A \a prefix of * NULL is a bug in the caller. */ -static inline bool spa_strstartswith(const char *s, const char *prefix) +SPA_API_STRING bool spa_strstartswith(const char *s, const char *prefix) { if (SPA_UNLIKELY(s == NULL)) return false; @@ -70,7 +78,7 @@ static inline bool spa_strstartswith(const char *s, const char *prefix) * A \a s is NULL, it never ends with the given \a suffix. A \a suffix of * NULL is a bug in the caller. */ -static inline bool spa_strendswith(const char *s, const char *suffix) +SPA_API_STRING bool spa_strendswith(const char *s, const char *suffix) { size_t l1, l2; @@ -92,7 +100,7 @@ static inline bool spa_strendswith(const char *s, const char *suffix) * * \return true on success, false otherwise */ -static inline bool spa_atoi32(const char *str, int32_t *val, int base) +SPA_API_STRING bool spa_atoi32(const char *str, int32_t *val, int base) { char *endptr; long v; @@ -120,7 +128,7 @@ static inline bool spa_atoi32(const char *str, int32_t *val, int base) * * \return true on success, false otherwise */ -static inline bool spa_atou32(const char *str, uint32_t *val, int base) +SPA_API_STRING bool spa_atou32(const char *str, uint32_t *val, int base) { char *endptr; unsigned long long v; @@ -148,7 +156,7 @@ static inline bool spa_atou32(const char *str, uint32_t *val, int base) * * \return true on success, false otherwise */ -static inline bool spa_atoi64(const char *str, int64_t *val, int base) +SPA_API_STRING bool spa_atoi64(const char *str, int64_t *val, int base) { char *endptr; long long v; @@ -173,7 +181,7 @@ static inline bool spa_atoi64(const char *str, int64_t *val, int base) * * \return true on success, false otherwise */ -static inline bool spa_atou64(const char *str, uint64_t *val, int base) +SPA_API_STRING bool spa_atou64(const char *str, uint64_t *val, int base) { char *endptr; unsigned long long v; @@ -196,7 +204,7 @@ static inline bool spa_atou64(const char *str, uint64_t *val, int base) * * \return true on success, false otherwise */ -static inline bool spa_atob(const char *str) +SPA_API_STRING bool spa_atob(const char *str) { return spa_streq(str, "true") || spa_streq(str, "1"); } @@ -210,7 +218,7 @@ static inline bool spa_atob(const char *str) * number on error. */ SPA_PRINTF_FUNC(3, 0) -static inline int spa_vscnprintf(char *buffer, size_t size, const char *format, va_list args) +SPA_API_STRING int spa_vscnprintf(char *buffer, size_t size, const char *format, va_list args) { int r; @@ -233,7 +241,7 @@ static inline int spa_vscnprintf(char *buffer, size_t size, const char *format, * number on error. */ SPA_PRINTF_FUNC(3, 4) -static inline int spa_scnprintf(char *buffer, size_t size, const char *format, ...) +SPA_API_STRING int spa_scnprintf(char *buffer, size_t size, const char *format, ...) { int r; va_list args; @@ -253,7 +261,7 @@ static inline int spa_scnprintf(char *buffer, size_t size, const char *format, . * * \return the result float. */ -static inline float spa_strtof(const char *str, char **endptr) +SPA_API_STRING float spa_strtof(const char *str, char **endptr) { #ifndef __LOCALE_C_ONLY static locale_t locale = NULL; @@ -279,7 +287,7 @@ static inline float spa_strtof(const char *str, char **endptr) * * \return true on success, false otherwise */ -static inline bool spa_atof(const char *str, float *val) +SPA_API_STRING bool spa_atof(const char *str, float *val) { char *endptr; float v; @@ -303,7 +311,7 @@ static inline bool spa_atof(const char *str, float *val) * * \return the result float. */ -static inline double spa_strtod(const char *str, char **endptr) +SPA_API_STRING double spa_strtod(const char *str, char **endptr) { #ifndef __LOCALE_C_ONLY static locale_t locale = NULL; @@ -329,7 +337,7 @@ static inline double spa_strtod(const char *str, char **endptr) * * \return true on success, false otherwise */ -static inline bool spa_atod(const char *str, double *val) +SPA_API_STRING bool spa_atod(const char *str, double *val) { char *endptr; double v; @@ -346,7 +354,7 @@ static inline bool spa_atod(const char *str, double *val) return true; } -static inline char *spa_dtoa(char *str, size_t size, double val) +SPA_API_STRING char *spa_dtoa(char *str, size_t size, double val) { int i, l; l = spa_scnprintf(str, size, "%f", val); @@ -362,15 +370,17 @@ struct spa_strbuf { size_t pos; }; -static inline void spa_strbuf_init(struct spa_strbuf *buf, char *buffer, size_t maxsize) +SPA_API_STRING void spa_strbuf_init(struct spa_strbuf *buf, char *buffer, size_t maxsize) { buf->buffer = buffer; buf->maxsize = maxsize; buf->pos = 0; + if (maxsize > 0) + buf->buffer[0] = '\0'; } SPA_PRINTF_FUNC(2, 3) -static inline int spa_strbuf_append(struct spa_strbuf *buf, const char *fmt, ...) +SPA_API_STRING int spa_strbuf_append(struct spa_strbuf *buf, const char *fmt, ...) { size_t remain = buf->maxsize - buf->pos; ssize_t written; diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/utils/type-info.h b/src/java.desktop/unix/native/libpipewire/include/spa/utils/type-info.h index 86a6b87bc49c8..85f831daede10 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/utils/type-info.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/utils/type-info.h @@ -20,10 +20,6 @@ extern "C" { #define SPA_TYPE_ROOT spa_types #endif -static inline bool spa_type_is_a(const char *type, const char *parent) -{ - return type != NULL && parent != NULL && strncmp(type, parent, strlen(parent)) == 0; -} #include #include @@ -83,6 +79,7 @@ static const struct spa_type_info spa_types[] = { { SPA_TYPE_OBJECT_Profiler, SPA_TYPE_Object, SPA_TYPE_INFO_Profiler, spa_type_profiler }, { SPA_TYPE_OBJECT_ParamLatency, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Latency, spa_type_param_latency }, { SPA_TYPE_OBJECT_ParamProcessLatency, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_ProcessLatency, spa_type_param_process_latency }, + { SPA_TYPE_OBJECT_ParamTag, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Tag, spa_type_param_tag }, { 0, 0, NULL, NULL } }; diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/utils/type.h b/src/java.desktop/unix/native/libpipewire/include/spa/utils/type.h index fcadda93101a3..79802d2f07eb8 100644 --- a/src/java.desktop/unix/native/libpipewire/include/spa/utils/type.h +++ b/src/java.desktop/unix/native/libpipewire/include/spa/utils/type.h @@ -10,6 +10,15 @@ extern "C" { #endif #include +#include + +#ifndef SPA_API_TYPE + #ifdef SPA_API_IMPL + #define SPA_API_TYPE SPA_API_IMPL + #else + #define SPA_API_TYPE static inline + #endif +#endif /** \defgroup spa_types Types * Data type information enumerations @@ -78,6 +87,7 @@ enum { SPA_TYPE_OBJECT_Profiler, SPA_TYPE_OBJECT_ParamLatency, SPA_TYPE_OBJECT_ParamProcessLatency, + SPA_TYPE_OBJECT_ParamTag, _SPA_TYPE_OBJECT_LAST, /**< not part of ABI */ /* vendor extensions */ @@ -122,6 +132,47 @@ struct spa_type_info { const struct spa_type_info *values; }; +SPA_API_TYPE bool spa_type_is_a(const char *type, const char *parent) +{ + return type != NULL && parent != NULL && strncmp(type, parent, strlen(parent)) == 0; +} + +SPA_API_TYPE const char *spa_type_short_name(const char *name) +{ + const char *h; + if ((h = strrchr(name, ':')) != NULL) + name = h + 1; + return name; +} + +SPA_API_TYPE uint32_t spa_type_from_short_name(const char *name, + const struct spa_type_info *info, uint32_t unknown) +{ + int i; + for (i = 0; info[i].name; i++) { + if (spa_streq(name, spa_type_short_name(info[i].name))) + return info[i].type; + } + return unknown; +} +SPA_API_TYPE const char * spa_type_to_name(uint32_t type, + const struct spa_type_info *info, const char *unknown) +{ + int i; + for (i = 0; info[i].name; i++) { + if (info[i].type == type) + return info[i].name; + } + return unknown; +} + +SPA_API_TYPE const char * spa_type_to_short_name(uint32_t type, + const struct spa_type_info *info, const char *unknown) +{ + const char *n = spa_type_to_name(type, info, unknown); + return n ? spa_type_short_name(n) : NULL; +} + /** * \} */ diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/AnimationController.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/AnimationController.java index 05c2f5c0a40cb..b50a5ad583ec5 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/AnimationController.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/AnimationController.java @@ -59,7 +59,7 @@ *

  • It tracks the animation state for every UI component involved in the * animation and paints {@code Skin} in new {@code State} over the * {@code Skin} in last {@code State} using - * {@code AlphaComposite.SrcOver.derive(alpha)} where {code alpha} + * {@code AlphaComposite.SrcOver.derive(alpha)} where {@code alpha} * depends on the state of animation * * diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java index c7007bec3db8e..dbc913582f2ad 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,7 @@ import java.awt.Rectangle; import javax.swing.ButtonModel; +import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.JMenuItem; import javax.swing.plaf.ComponentUI; @@ -71,6 +72,26 @@ protected void paintBackground(Graphics g, JMenuItem menuItem, } super.paintBackground(g, menuItem, bgColor); } + + /** + * Paint MenuItem. + */ + protected void paintMenuItem(Graphics g, JComponent c, + Icon checkIcon, Icon arrowIcon, + Color background, Color foreground, + int defaultTextIconGap) { + if (WindowsMenuItemUI.isVistaPainting()) { + WindowsMenuItemUI.paintMenuItem(accessor, g, c, checkIcon, + arrowIcon, background, foreground, + disabledForeground, acceleratorSelectionForeground, + acceleratorForeground, defaultTextIconGap, + menuItem, getPropertyPrefix()); + return; + } + super.paintMenuItem(g, c, checkIcon, arrowIcon, background, + foreground, defaultTextIconGap); + } + /** * Method which renders the text of the current menu item. * diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsFileChooserUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsFileChooserUI.java index 87f5f5dccb891..c0a354b52877b 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsFileChooserUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsFileChooserUI.java @@ -1048,6 +1048,8 @@ public Component getListCellRendererComponent(JList list, Object value, ii.depth = directoryComboBoxModel.getDepth(index); setIcon(ii); + putClientProperty("html.disable", getFileChooser().getClientProperty("html.disable")); + return this; } } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java index 86a587aabd3b6..25ef9a99fbe56 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,6 @@ import java.awt.BasicStroke; import java.awt.Color; import java.awt.Component; -import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; @@ -175,7 +174,7 @@ public static Icon createFrameResizeIcon() { @SuppressWarnings("serial") // Same-version serialization only private static class FrameButtonIcon implements Icon, Serializable { - private Part part; + private final Part part; private FrameButtonIcon(Part part) { this.part = part; @@ -286,18 +285,10 @@ public int getIconWidth() { int width; if (XPStyle.getXP() != null) { // Fix for XP bug where sometimes these sizes aren't updated properly - // Assume for now that height is correct and derive width using the - // ratio from the uxtheme part - width = UIManager.getInt("InternalFrame.titleButtonHeight") -2; - Dimension d = XPStyle.getPartSize(Part.WP_CLOSEBUTTON, State.NORMAL); - if (d != null && d.width != 0 && d.height != 0) { - width = (int) ((float) width * d.width / d.height); - } + // Assume for now that height is correct and derive width from height + width = UIManager.getInt("InternalFrame.titleButtonHeight") + 10; } else { - width = UIManager.getInt("InternalFrame.titleButtonWidth") -2; - } - if (XPStyle.getXP() != null) { - width -= 2; + width = UIManager.getInt("InternalFrame.titleButtonHeight") - 2; } return width; } @@ -857,6 +848,7 @@ public void paintIcon(Component c, Graphics g, int x, int y) { } assert menuItem == null || c == menuItem; Icon icon = getIcon(); + if (type == JCheckBoxMenuItem.class || type == JRadioButtonMenuItem.class) { AbstractButton b = (AbstractButton) c; @@ -880,19 +872,25 @@ public void paintIcon(Component c, Graphics g, int x, int y) { } XPStyle xp = XPStyle.getXP(); if (xp != null) { - Skin skin; - skin = xp.getSkin(c, backgroundPart); - skin.paintSkin(g, x, y, - getIconWidth(), getIconHeight(), backgroundState); - if (icon == null) { - skin = xp.getSkin(c, part); + Skin skin = xp.getSkin(c, part); + if (icon == null || icon.getIconHeight() <= 16) { skin.paintSkin(g, x + OFFSET, y + OFFSET, state); + } else { + skin.paintSkin(g, x + OFFSET, y + icon.getIconHeight() / 2, state); } } } } if (icon != null) { - icon.paintIcon(c, g, x + OFFSET, y + OFFSET); + if (WindowsGraphicsUtils.isLeftToRight(c)) { + icon.paintIcon(c, g, + x + VistaMenuItemCheckIconFactory.getIconWidth(), + y + OFFSET); + } else { + icon.paintIcon(c, g, + x - VistaMenuItemCheckIconFactory.getIconWidth() + 2 * OFFSET, + y + OFFSET); + } } } private static WindowsMenuItemUIAccessor getAccessor( diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameTitlePane.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameTitlePane.java index 83717d6bf937e..cd0987a907991 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameTitlePane.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameTitlePane.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,20 +25,46 @@ package com.sun.java.swing.plaf.windows; -import sun.swing.SwingUtilities2; - -import javax.swing.*; -import javax.swing.border.*; -import javax.swing.UIManager; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.BasicInternalFrameTitlePane; -import java.awt.*; -import java.awt.event.*; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.LayoutManager; +import java.awt.Paint; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyVetoException; -import static com.sun.java.swing.plaf.windows.TMSchema.*; +import javax.swing.BorderFactory; +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.JInternalFrame; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.JSeparator; +import javax.swing.LookAndFeel; +import javax.swing.UIDefaults; +import javax.swing.UIManager; +import javax.swing.border.Border; +import javax.swing.plaf.UIResource; +import javax.swing.plaf.basic.BasicInternalFrameTitlePane; + +import sun.swing.SwingUtilities2; + +import static com.sun.java.swing.plaf.windows.TMSchema.Part; +import static com.sun.java.swing.plaf.windows.TMSchema.Prop; +import static com.sun.java.swing.plaf.windows.TMSchema.State; import static com.sun.java.swing.plaf.windows.XPStyle.Skin; @SuppressWarnings("serial") // Superclass is not serializable across versions @@ -68,7 +94,6 @@ protected void installDefaults() { super.installDefaults(); titlePaneHeight = UIManager.getInt("InternalFrame.titlePaneHeight"); - buttonWidth = UIManager.getInt("InternalFrame.titleButtonWidth") - 4; buttonHeight = UIManager.getInt("InternalFrame.titleButtonHeight") - 4; Object obj = UIManager.get("InternalFrame.titleButtonToolTipsOn"); @@ -77,15 +102,10 @@ protected void installDefaults() { if (XPStyle.getXP() != null) { // Fix for XP bug where sometimes these sizes aren't updated properly - // Assume for now that height is correct and derive width using the - // ratio from the uxtheme part - buttonWidth = buttonHeight; - Dimension d = XPStyle.getPartSize(Part.WP_CLOSEBUTTON, State.NORMAL); - if (d != null && d.width != 0 && d.height != 0) { - buttonWidth = (int) ((float) buttonWidth * d.width / d.height); - } + // Assume for now that height is correct and derive width from height + buttonWidth = buttonHeight + 14; } else { - buttonWidth += 2; + buttonWidth = buttonHeight + 2; Color activeBorderColor = UIManager.getColor("InternalFrame.activeBorderColor"); setBorder(BorderFactory.createLineBorder(activeBorderColor, 1)); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java index 09fad9c98a6de..0fa284294e27e 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,21 +26,30 @@ package com.sun.java.swing.plaf.windows; import java.awt.Color; +import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; +import java.awt.Insets; import java.awt.Rectangle; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.util.Enumeration; +import javax.swing.AbstractButton; +import javax.swing.ButtonGroup; import javax.swing.ButtonModel; +import javax.swing.DefaultButtonModel; +import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.JMenu; import javax.swing.JMenuItem; +import javax.swing.SwingConstants; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.BasicMenuItemUI; +import com.sun.java.swing.SwingUtilities3; import com.sun.java.swing.plaf.windows.TMSchema.Part; import com.sun.java.swing.plaf.windows.TMSchema.State; import com.sun.java.swing.plaf.windows.XPStyle.Skin; @@ -120,6 +129,27 @@ public void propertyChange(PropertyChangeEvent e) { menuItem.addPropertyChangeListener(changeListener); } + protected void installDefaults() { + super.installDefaults(); + String prefix = getPropertyPrefix(); + + if (acceleratorSelectionForeground == null || + acceleratorSelectionForeground instanceof UIResource) { + acceleratorSelectionForeground = + UIManager.getColor(prefix + ".acceleratorSelectionForeground"); + } + if (acceleratorForeground == null || + acceleratorForeground instanceof UIResource) { + acceleratorForeground = + UIManager.getColor(prefix + ".acceleratorForeground"); + } + if (disabledForeground == null || + disabledForeground instanceof UIResource) { + disabledForeground = + UIManager.getColor(prefix + ".disabledForeground"); + } + } + /** * {@inheritDoc} */ @@ -132,6 +162,103 @@ protected void uninstallListeners() { changeListener = null; } + protected void paintMenuItem(Graphics g, JComponent c, + Icon checkIcon, Icon arrowIcon, + Color background, Color foreground, + int defaultTextIconGap) { + if (WindowsMenuItemUI.isVistaPainting()) { + WindowsMenuItemUI.paintMenuItem(accessor, g, c, checkIcon, + arrowIcon, background, foreground, + disabledForeground, acceleratorSelectionForeground, + acceleratorForeground, defaultTextIconGap, menuItem, + getPropertyPrefix()); + return; + } + super.paintMenuItem(g, c, checkIcon, arrowIcon, background, + foreground, defaultTextIconGap); + } + + static void paintMenuItem(WindowsMenuItemUIAccessor accessor, Graphics g, + JComponent c, Icon checkIcon, Icon arrowIcon, + Color background, Color foreground, + Color disabledForeground, + Color acceleratorSelectionForeground, + Color acceleratorForeground, + int defaultTextIconGap, JMenuItem menuItem, String prefix) { + // Save original graphics font and color + Font holdf = g.getFont(); + Color holdc = g.getColor(); + + JMenuItem mi = (JMenuItem) c; + g.setFont(mi.getFont()); + + Rectangle viewRect = new Rectangle(0, 0, mi.getWidth(), mi.getHeight()); + SwingUtilities3.applyInsets(viewRect, mi.getInsets()); + + String acceleratorDelimiter = + UIManager.getString("MenuItem.acceleratorDelimiter"); + if (acceleratorDelimiter == null) { acceleratorDelimiter = "+"; } + Font acceleratorFont = UIManager.getFont("MenuItem.acceleratorFont"); + if (acceleratorFont == null) { + acceleratorFont = UIManager.getFont("MenuItem.font"); + } + + MenuItemLayoutHelper lh = new MenuItemLayoutHelper(mi, checkIcon, + arrowIcon, viewRect, defaultTextIconGap, acceleratorDelimiter, + mi.getComponentOrientation().isLeftToRight(), mi.getFont(), + acceleratorFont, MenuItemLayoutHelper.useCheckAndArrow(menuItem), + prefix); + MenuItemLayoutHelper.LayoutResult lr = lh.layoutMenuItem(); + + paintBackground(accessor, g, mi, background); + SwingUtilities3.paintCheckIcon(g, lh, lr, holdc, foreground); + SwingUtilities3.paintIcon(g, lh, lr, holdc); + + if (lh.getCheckIcon() != null && lh.useCheckAndArrow()) { + Rectangle rect = lr.getTextRect(); + if (menuItem.getComponentOrientation().isLeftToRight()) { + if (menuItem.getHorizontalTextPosition() != SwingConstants.LEADING + && menuItem.getHorizontalTextPosition() != SwingConstants.LEFT) { + rect.x += lh.getAfterCheckIconGap(); + } + } else { + if (menuItem.getHorizontalTextPosition() != SwingConstants.LEADING + && menuItem.getHorizontalTextPosition() != SwingConstants.RIGHT) { + rect.x -= lh.getAfterCheckIconGap(); + } + } + + lr.setTextRect(rect); + } + if (!lh.getText().isEmpty()) { + if (lh.getHtmlView() != null) { + // Text is HTML + lh.getHtmlView().paint(g, lr.getTextRect()); + } else { + // Text isn't HTML + paintText(accessor, g, lh.getMenuItem(), + lr.getTextRect(), lh.getText()); + } + } + if (lh.getCheckIcon() != null && lh.useCheckAndArrow()) { + Rectangle rect = lr.getAccRect(); + if (menuItem.getComponentOrientation().isLeftToRight()) { + rect.x += lh.getAfterCheckIconGap(); + } else { + rect.x -= lh.getAfterCheckIconGap(); + } + lr.setAccRect(rect); + } + SwingUtilities3.paintAccText(g, lh, lr, disabledForeground, + acceleratorSelectionForeground, + acceleratorForeground); + SwingUtilities3.paintArrowIcon(g, lh, lr, foreground); + + // Restore original graphics font and color + g.setColor(holdc); + g.setFont(holdf); + } + /** * Method which renders the text of the current menu item. * diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java index 4195b4e85cace..d2d07fd79502d 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -127,6 +127,26 @@ protected void installDefaults() { hotTrackingOn = (obj instanceof Boolean) ? (Boolean)obj : true; } + /** + * Paint MenuItem. + */ + protected void paintMenuItem(Graphics g, JComponent c, + Icon checkIcon, Icon arrowIcon, + Color background, Color foreground, + int defaultTextIconGap) { + if (WindowsMenuItemUI.isVistaPainting()) { + WindowsMenuItemUI.paintMenuItem(accessor, g, c, checkIcon, arrowIcon, + background, foreground, + disabledForeground, acceleratorSelectionForeground, + acceleratorForeground, defaultTextIconGap, menuItem, + getPropertyPrefix()); + return; + } + super.paintMenuItem(g, c, checkIcon, arrowIcon, background, + foreground, defaultTextIconGap); + } + + /** * Draws the background of the menu. * @since 1.4 diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java index 584a0f1622b1a..4ca4517638a1d 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,7 @@ import java.awt.Rectangle; import javax.swing.ButtonModel; +import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.JMenuItem; import javax.swing.plaf.ComponentUI; @@ -72,6 +73,25 @@ protected void paintBackground(Graphics g, JMenuItem menuItem, super.paintBackground(g, menuItem, bgColor); } + /** + * Paint MenuItem. + */ + protected void paintMenuItem(Graphics g, JComponent c, + Icon checkIcon, Icon arrowIcon, + Color background, Color foreground, + int defaultTextIconGap) { + if (WindowsMenuItemUI.isVistaPainting()) { + WindowsMenuItemUI.paintMenuItem(accessor, g, c, checkIcon, + arrowIcon, background, foreground, + disabledForeground, acceleratorSelectionForeground, + acceleratorForeground, defaultTextIconGap, + menuItem, getPropertyPrefix()); + return; + } + super.paintMenuItem(g, c, checkIcon, arrowIcon, background, + foreground, defaultTextIconGap); + } + /** * Method which renders the text of the current menu item. * diff --git a/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java b/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java index d2b0a6764f105..b4d307c646d09 100644 --- a/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java +++ b/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java @@ -47,6 +47,8 @@ import sun.java2d.opengl.WGLGraphicsConfig; import sun.java2d.windows.WindowsFlags; +import static java.awt.peer.ComponentPeer.SET_BOUNDS; + import static sun.awt.Win32GraphicsEnvironment.debugScaleX; import static sun.awt.Win32GraphicsEnvironment.debugScaleY; @@ -443,6 +445,21 @@ public synchronized void setFullScreenWindow(Window w) { protected native void enterFullScreenExclusive(int screen, WindowPeer w); protected native void exitFullScreenExclusive(int screen, WindowPeer w); + /** + * Reapplies the size of this graphics device to + * the given full-screen window. + * @param w a Window that needs resizing + * @param b new full-screen window bounds + */ + private static void resizeFSWindow(final Window w, final Rectangle b) { + if (w != null) { + WindowPeer peer = AWTAccessor.getComponentAccessor().getPeer(w); + if (peer != null) { + peer.setBounds(b.x, b.y, b.width, b.height, SET_BOUNDS); + } + } + } + @Override public boolean isDisplayChangeSupported() { return (isFullScreenSupported() && getFullScreenWindow() != null); @@ -465,13 +482,9 @@ public synchronized void setDisplayMode(DisplayMode dm) { WWindowPeer peer = AWTAccessor.getComponentAccessor().getPeer(w); configDisplayMode(screen, peer, dm.getWidth(), dm.getHeight(), dm.getBitDepth(), dm.getRefreshRate()); - // resize the fullscreen window to the dimensions of the new - // display mode - Rectangle screenBounds = getDefaultConfiguration().getBounds(); - w.setBounds(screenBounds.x, screenBounds.y, - screenBounds.width, screenBounds.height); - // Note: no call to replaceSurfaceData is required here since - // replacement will be caused by an upcoming display change event + // Note: the full-screen window will get resized to the dimensions of the new + // display mode in the upcoming display change event, when the DPI scales + // would already be correctly set etc. } else { throw new IllegalStateException("Must be in fullscreen mode " + "in order to set display mode"); @@ -531,6 +544,10 @@ public void displayChanged() { defaultConfig = null; configs = null; initScaleFactors(); + + Rectangle screenBounds = getDefaultConfiguration().getBounds(); + resizeFSWindow(getFullScreenWindow(), screenBounds); + // pass on to all top-level windows on this display topLevels.notifyListeners(); } diff --git a/src/java.desktop/windows/classes/sun/awt/windows/ThemeReader.java b/src/java.desktop/windows/classes/sun/awt/windows/ThemeReader.java index e33deb5b46702..dedfe22dd76d0 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/ThemeReader.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/ThemeReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -100,21 +100,30 @@ public static boolean isXPStyleEnabled() { return xpStyleEnabled; } - private static Long openThemeImpl(String widget, int dpi) { - Long theme; + private static long openThemeImpl(String widget, int dpi) { + long theme; int i = widget.indexOf("::"); if (i > 0) { // We're using the syntax "subAppName::controlName" here, as used by msstyles. // See documentation for SetWindowTheme on MSDN. setWindowTheme(widget.substring(0, i)); - theme = openTheme(widget.substring(i + 2), dpi); + theme = getOpenThemeValue(widget.substring(i + 2), dpi); setWindowTheme(null); } else { - theme = openTheme(widget, dpi); + theme = getOpenThemeValue(widget, dpi); } return theme; } + private static long getOpenThemeValue(String widget, int dpi) { + long theme; + theme = openTheme(widget, dpi); + if (theme == 0) { + theme = openTheme(widget, defaultDPI); + } + return theme; + } + // this should be called only with writeLock held private static Long getThemeImpl(String widget, int dpi) { return dpiAwareWidgetToTheme.computeIfAbsent(dpi, key -> new HashMap<>()) diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WClipboard.java b/src/java.desktop/windows/classes/sun/awt/windows/WClipboard.java index e16b18052955b..110dfb8d63763 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WClipboard.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WClipboard.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,9 @@ import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.io.IOException; +import java.lang.System.Logger.Level; import java.util.Map; +import java.util.concurrent.locks.ReentrantLock; import sun.awt.datatransfer.DataTransferer; import sun.awt.datatransfer.SunClipboard; @@ -51,8 +53,12 @@ final class WClipboard extends SunClipboard { private boolean isClipboardViewerRegistered; + private final ReentrantLock clipboardLocked = new ReentrantLock(); + WClipboard() { super("System"); + // Register java side of the clipboard with the native side + registerClipboard(); } @Override @@ -104,18 +110,42 @@ protected void clearNativeContext() {} /** * Call the Win32 OpenClipboard function. If newOwner is non-null, - * we also call EmptyClipboard and take ownership. + * we also call EmptyClipboard and take ownership. If this method call + * succeeds, it must be followed by a call to {@link #closeClipboard()}. * * @throws IllegalStateException if the clipboard has not been opened */ @Override - public native void openClipboard(SunClipboard newOwner) throws IllegalStateException; + public void openClipboard(SunClipboard newOwner) throws IllegalStateException { + if (!clipboardLocked.tryLock()) { + throw new IllegalStateException("Failed to acquire clipboard lock"); + } + try { + openClipboard0(newOwner); + } catch (IllegalStateException ex) { + clipboardLocked.unlock(); + throw ex; + } + } + /** * Call the Win32 CloseClipboard function if we have clipboard ownership, * does nothing if we have not ownership. */ @Override - public native void closeClipboard(); + public void closeClipboard() { + if (clipboardLocked.isLocked()) { + try { + closeClipboard0(); + } finally { + clipboardLocked.unlock(); + } + } + } + + private native void openClipboard0(SunClipboard newOwner) throws IllegalStateException; + private native void closeClipboard0(); + /** * Call the Win32 SetClipboardData function. */ @@ -157,16 +187,12 @@ private void handleContentsChanged() { return; } - long[] formats = null; try { - openClipboard(null); - formats = getClipboardFormats(); - } catch (IllegalStateException exc) { - // do nothing to handle the exception, call checkChange(null) - } finally { - closeClipboard(); + long[] formats = getClipboardFormats(); + checkChange(formats); + } catch (Throwable ex) { + System.getLogger(WClipboard.class.getName()).log(Level.DEBUG, "Failed to process handleContentsChanged", ex); } - checkChange(formats); } /** @@ -214,4 +240,6 @@ public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorExcepti } }; } + + private native void registerClipboard(); } diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WInputMethod.java b/src/java.desktop/windows/classes/sun/awt/windows/WInputMethod.java index 9ad52ba7111c0..18630726857ca 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WInputMethod.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WInputMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -319,6 +319,10 @@ public void activate() { isLastFocussedActiveClient = isAc; } isActive = true; + + // Sync currentLocale with the Windows keyboard layout which could be changed + // while the component was inactive. + getLocale(); if (currentLocale != null) { setLocale(currentLocale, true); } diff --git a/src/java.desktop/windows/native/libawt/java2d/windows/GDIRenderer.cpp b/src/java.desktop/windows/native/libawt/java2d/windows/GDIRenderer.cpp index 5e35ac7af344f..08ff3214632ba 100644 --- a/src/java.desktop/windows/native/libawt/java2d/windows/GDIRenderer.cpp +++ b/src/java.desktop/windows/native/libawt/java2d/windows/GDIRenderer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -49,7 +49,7 @@ extern "C" { #define POLYTEMPSIZE (512 / sizeof(POINT)) -static void AngleToCoord(jint angle, jint w, jint h, jint *x, jint *y) +static void AngleToCoord(int angle, int w, int h, int *x, int *y) { const double pi = 3.1415926535; const double toRadians = 2 * pi / 360; @@ -322,7 +322,7 @@ Java_sun_java2d_windows_GDIRenderer_doDrawArc return; } - long sx, sy, ex, ey; + int sx, sy, ex, ey; if (angleExtent >= 360 || angleExtent <= -360) { sx = ex = x + w; sy = ey = y + h/2; @@ -602,7 +602,7 @@ Java_sun_java2d_windows_GDIRenderer_doFillArc if (wsdo == NULL) { return; } - long sx, sy, ex, ey; + int sx, sy, ex, ey; int angleEnd; if (angleExtent < 0) { angleEnd = angleStart; diff --git a/src/java.desktop/windows/native/libawt/java2d/windows/GDIWindowSurfaceData.cpp b/src/java.desktop/windows/native/libawt/java2d/windows/GDIWindowSurfaceData.cpp index 804a8efb8e926..77528dc333069 100644 --- a/src/java.desktop/windows/native/libawt/java2d/windows/GDIWindowSurfaceData.cpp +++ b/src/java.desktop/windows/native/libawt/java2d/windows/GDIWindowSurfaceData.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -897,7 +897,7 @@ static void GDIWinSD_GetRasInfo(JNIEnv *env, } if (wsdo->lockFlags & SD_LOCK_LUT) { pRasInfo->lutBase = - (long *) wsdo->device->GetSystemPaletteEntries(); + (jint *) wsdo->device->GetSystemPaletteEntries(); pRasInfo->lutSize = 256; } else { pRasInfo->lutBase = NULL; diff --git a/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp b/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp index 2d0745f763fb8..b3f13a5bf8260 100644 --- a/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp +++ b/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp @@ -1080,12 +1080,12 @@ JNIEXPORT jintArray JNICALL Java_sun_awt_shell_Win32ShellFolder2_getIconBits // Extract the color bitmap int nBits = iconSize * iconSize; - long *colorBits = NULL; - long *maskBits = NULL; + jint *colorBits = NULL; + int *maskBits = NULL; try { entry_point(); - colorBits = (long*)safe_Malloc(MAX_ICON_SIZE * MAX_ICON_SIZE * sizeof(long)); + colorBits = (jint*)safe_Malloc(MAX_ICON_SIZE * MAX_ICON_SIZE * sizeof(jint)); GetDIBits(dc, iconInfo.hbmColor, 0, iconSize, colorBits, &bmi, DIB_RGB_COLORS); // XP supports alpha in some icons, and depending on device. // This should take precedence over the icon mask bits. @@ -1100,7 +1100,7 @@ JNIEXPORT jintArray JNICALL Java_sun_awt_shell_Win32ShellFolder2_getIconBits } if (!hasAlpha) { // Extract the mask bitmap - maskBits = (long*)safe_Malloc(MAX_ICON_SIZE * MAX_ICON_SIZE * sizeof(long)); + maskBits = (int*)safe_Malloc(MAX_ICON_SIZE * MAX_ICON_SIZE * sizeof(int)); GetDIBits(dc, iconInfo.hbmMask, 0, iconSize, maskBits, &bmi, DIB_RGB_COLORS); // Copy the mask alphas into the color bits for (int i = 0; i < nBits; i++) { diff --git a/src/java.desktop/windows/native/libawt/windows/WPrinterJob.cpp b/src/java.desktop/windows/native/libawt/windows/WPrinterJob.cpp index 812d0df3bbb32..16b20afd03072 100644 --- a/src/java.desktop/windows/native/libawt/windows/WPrinterJob.cpp +++ b/src/java.desktop/windows/native/libawt/windows/WPrinterJob.cpp @@ -729,7 +729,7 @@ Java_sun_print_Win32PrintService_getPrinterPort(JNIEnv *env, } jstring jPort; - LPTSTR printerName = NULL, printerPort = TEXT("LPT1"); + LPTSTR printerName = NULL, printerPort = (LPTSTR)TEXT("LPT1"); LPBYTE buffer = NULL; DWORD cbBuf = 0; @@ -745,7 +745,7 @@ Java_sun_print_Win32PrintService_getPrinterPort(JNIEnv *env, } if (printerPort == NULL) { - printerPort = TEXT("LPT1"); + printerPort = (LPTSTR)TEXT("LPT1"); } jPort = JNU_NewStringPlatform(env, printerPort); delete [] buffer; @@ -1110,7 +1110,7 @@ Java_sun_print_Win32PrintJob_startPrintRawData(JNIEnv *env, // Fill in the structure with info about this "document." DocInfo.pDocName = jname; DocInfo.pOutputFile = NULL; - DocInfo.pDatatype = TEXT("RAW"); + DocInfo.pDatatype = (LPTSTR)TEXT("RAW"); // Inform the spooler the document is beginning. if( (::StartDocPrinter(hPrinter, 1, (LPBYTE)&DocInfo)) == 0 ) { diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Clipboard.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Clipboard.cpp index 2aadcd5a08c7f..1232f189ca26e 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Clipboard.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Clipboard.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -69,9 +69,8 @@ void AwtClipboard::RegisterClipboardViewer(JNIEnv *env, jobject jclipboard) { return; } - if (theCurrentClipboard == NULL) { - theCurrentClipboard = env->NewGlobalRef(jclipboard); - } + DASSERT(AwtClipboard::theCurrentClipboard != NULL); + DASSERT(env->IsSameObject(AwtClipboard::theCurrentClipboard, jclipboard)); jclass cls = env->GetObjectClass(jclipboard); AwtClipboard::handleContentsChangedMID = @@ -128,11 +127,13 @@ Java_sun_awt_windows_WClipboard_init(JNIEnv *env, jclass cls) * Signature: (Lsun/awt/windows/WClipboard;)V */ JNIEXPORT void JNICALL -Java_sun_awt_windows_WClipboard_openClipboard(JNIEnv *env, jobject self, +Java_sun_awt_windows_WClipboard_openClipboard0(JNIEnv *env, jobject self, jobject newOwner) { TRY; + DASSERT(AwtClipboard::theCurrentClipboard != NULL); + DASSERT(newOwner == NULL || env->IsSameObject(AwtClipboard::theCurrentClipboard, newOwner)); DASSERT(::GetOpenClipboardWindow() != AwtToolkit::GetInstance().GetHWnd()); if (!::OpenClipboard(AwtToolkit::GetInstance().GetHWnd())) { @@ -142,10 +143,6 @@ Java_sun_awt_windows_WClipboard_openClipboard(JNIEnv *env, jobject self, } if (newOwner != NULL) { AwtClipboard::GetOwnership(); - if (AwtClipboard::theCurrentClipboard != NULL) { - env->DeleteGlobalRef(AwtClipboard::theCurrentClipboard); - } - AwtClipboard::theCurrentClipboard = env->NewGlobalRef(newOwner); } CATCH_BAD_ALLOC; @@ -157,7 +154,7 @@ Java_sun_awt_windows_WClipboard_openClipboard(JNIEnv *env, jobject self, * Signature: ()V */ JNIEXPORT void JNICALL -Java_sun_awt_windows_WClipboard_closeClipboard(JNIEnv *env, jobject self) +Java_sun_awt_windows_WClipboard_closeClipboard0(JNIEnv *env, jobject self) { TRY; @@ -297,23 +294,25 @@ Java_sun_awt_windows_WClipboard_getClipboardFormats { TRY; - DASSERT(::GetOpenClipboardWindow() == AwtToolkit::GetInstance().GetHWnd()); + unsigned int cFormats = 128; // Allocate enough space to hold all + unsigned int pcFormatsOut = 0; + unsigned int lpuiFormats[128] = { 0 }; - jsize nFormats = ::CountClipboardFormats(); - jlongArray formats = env->NewLongArray(nFormats); + VERIFY(::GetUpdatedClipboardFormats(lpuiFormats, 128, &pcFormatsOut)); + + jlongArray formats = env->NewLongArray(pcFormatsOut); if (formats == NULL) { throw std::bad_alloc(); } - if (nFormats == 0) { + if (pcFormatsOut == 0) { return formats; } jboolean isCopy; jlong *lFormats = env->GetLongArrayElements(formats, &isCopy), *saveFormats = lFormats; - UINT num = 0; - for (jsize i = 0; i < nFormats; i++, lFormats++) { - *lFormats = num = ::EnumClipboardFormats(num); + for (unsigned int i = 0; i < pcFormatsOut; i++, lFormats++) { + *lFormats = lpuiFormats[i]; } env->ReleaseLongArrayElements(formats, saveFormats, 0); @@ -478,4 +477,16 @@ Java_sun_awt_windows_WClipboard_getClipboardData CATCH_BAD_ALLOC_RET(NULL); } +/* + * Class: sun_awt_windows_WClipboard + * Method: registerClipboard + * Signature: ()V + */ +JNIEXPORT void JNICALL +Java_sun_awt_windows_WClipboard_registerClipboard(JNIEnv *env, jobject self) +{ + DASSERT(AwtClipboard::theCurrentClipboard == NULL); + AwtClipboard::theCurrentClipboard = env->NewGlobalRef(self); +} + } /* extern "C" */ diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Debug.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Debug.cpp index 5faa5f3a63412..88dc0f17abec7 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Debug.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Debug.cpp @@ -193,7 +193,7 @@ void AwtDebugSupport::AssertCallback(const char * expr, const char * file, int l NULL); if (msgBuffer == NULL) { - msgBuffer = ""; + msgBuffer = (LPSTR)""; } // format the assertion message _snprintf(assertMsg, ASSERT_MSG_SIZE, AssertFmt, expr, file, line, lastError, msgBuffer); diff --git a/src/java.desktop/windows/native/libawt/windows/awt_DesktopProperties.cpp b/src/java.desktop/windows/native/libawt/windows/awt_DesktopProperties.cpp index 608138fb2eb97..4ee5ca0f93684 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_DesktopProperties.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_DesktopProperties.cpp @@ -138,7 +138,7 @@ void AwtDesktopProperties::GetSystemProperties() { // Note that it uses malloc() and returns the pointer to allocated // memory, so remember to use free() when you are done with its // result. -static LPTSTR resolveShellDialogFont(LPTSTR fontName, HKEY handle) { +static LPTSTR resolveShellDialogFont(LPCTSTR fontName, HKEY handle) { DWORD valueType, valueSize; if (RegQueryValueEx((HKEY)handle, fontName, NULL, &valueType, NULL, &valueSize) != 0) { @@ -164,7 +164,7 @@ static LPTSTR resolveShellDialogFont(LPTSTR fontName, HKEY handle) { // memory, so remember to use free() when you are done with its // result. static LPTSTR resolveShellDialogFont() { - LPTSTR subKey = TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes"); + LPCTSTR subKey = TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes"); HKEY handle; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, subKey, 0, KEY_READ, &handle) != 0) { @@ -183,7 +183,7 @@ static LPTSTR resolveShellDialogFont() { // Note that it uses malloc() and returns the pointer to allocated // memory, so remember to use free() when you are done with its // result. -static LPTSTR getWindowsPropFromReg(LPTSTR subKey, LPTSTR valueName, DWORD *valueType) { +static LPTSTR getWindowsPropFromReg(LPCTSTR subKey, LPCTSTR valueName, DWORD *valueType) { HKEY handle; if (RegOpenKeyEx(HKEY_CURRENT_USER, subKey, 0, KEY_READ, &handle) != 0) { return NULL; @@ -221,7 +221,7 @@ static LPTSTR getWindowsPropFromReg(LPTSTR subKey, LPTSTR valueName, DWORD *valu } } -static LPTSTR getXPStylePropFromReg(LPTSTR valueName) { +static LPTSTR getXPStylePropFromReg(LPCTSTR valueName) { DWORD valueType; return getWindowsPropFromReg(TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\ThemeManager"), valueName, &valueType); @@ -651,11 +651,11 @@ void AwtDesktopProperties::GetOtherParameters() { throw; } - LPTSTR valueName = TEXT("PlaceN"); + LPCTSTR valueName = TEXT("PlaceN"); LPTSTR valueNameBuf = (LPTSTR)SAFE_SIZE_ARRAY_ALLOC(safe_Malloc, (lstrlen(valueName) + 1), sizeof(TCHAR)); lstrcpy(valueNameBuf, valueName); - LPTSTR propKey = TEXT("win.comdlg.placesBarPlaceN"); + LPCTSTR propKey = TEXT("win.comdlg.placesBarPlaceN"); LPTSTR propKeyBuf; try { @@ -672,7 +672,7 @@ void AwtDesktopProperties::GetOtherParameters() { valueNameBuf[5] = _T('0' + i++); propKeyBuf[25] = valueNameBuf[5]; - LPTSTR key = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\comdlg32\\PlacesBar"); + LPCTSTR key = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\comdlg32\\PlacesBar"); try { value = NULL; if ((value = getWindowsPropFromReg(key, valueNameBuf, &valueType)) != NULL) { diff --git a/src/java.desktop/windows/native/libawt/windows/awt_DrawingSurface.cpp b/src/java.desktop/windows/native/libawt/windows/awt_DrawingSurface.cpp index 019295ac2b5be..de7f7fd895cf0 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_DrawingSurface.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_DrawingSurface.cpp @@ -275,7 +275,7 @@ extern "C" JNIEXPORT void JNICALL DSUnlockAWT(JNIEnv* env) // EmbeddedFrame support -static char *const embeddedClassName = "sun/awt/windows/WEmbeddedFrame"; +static const char *const embeddedClassName = "sun/awt/windows/WEmbeddedFrame"; JNIEXPORT jobject JNICALL awt_CreateEmbeddedFrame (JNIEnv* env, void* platformInfo) diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Font.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Font.cpp index a7eedd888f37f..57f17031e6486 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Font.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Font.cpp @@ -345,7 +345,7 @@ AwtFont* AwtFont::Create(JNIEnv *env, jobject font, jint angle, jfloat awScale) wName = L"Arial"; } - WCHAR* wEName; + LPCWSTR wEName; if (!wcscmp(wName, L"Helvetica") || !wcscmp(wName, L"SansSerif")) { wEName = L"Arial"; } else if (!wcscmp(wName, L"TimesRoman") || @@ -388,7 +388,7 @@ AwtFont* AwtFont::Create(JNIEnv *env, jobject font, jint angle, jfloat awScale) return awtFont; } -static void strip_tail(wchar_t* text, wchar_t* tail) { // strips tail and any possible whitespace before it from the end of text +static void strip_tail(wchar_t* text, const wchar_t* tail) { // strips tail and any possible whitespace before it from the end of text if (wcslen(text)<=wcslen(tail)) { return; } @@ -495,7 +495,7 @@ static HFONT CreateHFont_sub(LPCWSTR name, int style, int height, return hFont; } -HFONT AwtFont::CreateHFont(WCHAR* name, int style, int height, +HFONT AwtFont::CreateHFont(LPCWSTR name, int style, int height, int angle, float awScale) { WCHAR longName[80]; diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Font.h b/src/java.desktop/windows/native/libawt/windows/awt_Font.h index 3f4d8e6a67db6..aa1d7241f1067 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Font.h +++ b/src/java.desktop/windows/native/libawt/windows/awt_Font.h @@ -155,7 +155,7 @@ class AwtFont : public AwtObject { */ static AwtFont* Create(JNIEnv *env, jobject font, jint angle = 0, jfloat awScale=1.0f); - static HFONT CreateHFont(WCHAR* name, int style, int height, + static HFONT CreateHFont(LPCWSTR name, int style, int height, int angle = 0, float awScale=1.0f); static void Cleanup(); diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Menu.h b/src/java.desktop/windows/native/libawt/windows/awt_Menu.h index 4392f293678c1..9ee61f8b433be 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Menu.h +++ b/src/java.desktop/windows/native/libawt/windows/awt_Menu.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -73,7 +73,7 @@ class AwtMenu : public AwtMenuItem { /*for multifont menu */ BOOL IsTopMenu(); - virtual AwtMenuItem* GetItem(jobject target, long index); + virtual AwtMenuItem* GetItem(jobject target, jint index); virtual int CountItem(jobject target); diff --git a/src/java.desktop/windows/native/libawt/windows/awt_MenuBar.cpp b/src/java.desktop/windows/native/libawt/windows/awt_MenuBar.cpp index cf12bf59607a5..24a9a24281b3b 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_MenuBar.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_MenuBar.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -145,7 +145,7 @@ int AwtMenuBar::CountItem(jobject menuBar) return nCount; } -AwtMenuItem* AwtMenuBar::GetItem(jobject target, long index) +AwtMenuItem* AwtMenuBar::GetItem(jobject target, jint index) { JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); if (env->EnsureLocalCapacity(2) < 0) { diff --git a/src/java.desktop/windows/native/libawt/windows/awt_MenuBar.h b/src/java.desktop/windows/native/libawt/windows/awt_MenuBar.h index 3e60a9b399aa4..d55f656b5396b 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_MenuBar.h +++ b/src/java.desktop/windows/native/libawt/windows/awt_MenuBar.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -66,7 +66,7 @@ class AwtMenuBar : public AwtMenu { virtual HWND GetOwnerHWnd(); virtual void RedrawMenuBar(); - AwtMenuItem* GetItem(jobject target, long index); + AwtMenuItem* GetItem(jobject target, jint index); int CountItem(jobject menuBar); void DrawItem(DRAWITEMSTRUCT& drawInfo); diff --git a/src/java.desktop/windows/native/libawt/windows/awt_PrintControl.cpp b/src/java.desktop/windows/native/libawt/windows/awt_PrintControl.cpp index c29d4c4e7ac85..5064bb4fb4529 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_PrintControl.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_PrintControl.cpp @@ -1045,7 +1045,7 @@ BOOL AwtPrintControl::UpdateAttributes(JNIEnv *env, DEVNAMES *devnames = (DEVNAMES*)::GlobalLock(pd.hDevNames); DASSERT(!IsBadReadPtr(devnames, sizeof(DEVNAMES))); LPTSTR lpcNames = (LPTSTR)devnames; - LPTSTR pbuf = (_tcslen(lpcNames + devnames->wDeviceOffset) == 0 ? + LPCTSTR pbuf = (_tcslen(lpcNames + devnames->wDeviceOffset) == 0 ? TEXT("") : lpcNames + devnames->wDeviceOffset); if (pbuf != NULL) { jstring jstr = JNU_NewStringPlatform(env, pbuf); diff --git a/src/java.desktop/windows/native/libawt/windows/awt_PrintJob.cpp b/src/java.desktop/windows/native/libawt/windows/awt_PrintJob.cpp index f8899037ea547..fa9dc5dc90f59 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_PrintJob.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_PrintJob.cpp @@ -65,8 +65,8 @@ extern "C" { /*** Private Constants ***/ -static char *kJavaIntStr = "I"; -static char *kJavaLongStr = "J"; +static const char *kJavaIntStr = "I"; +static const char *kJavaLongStr = "J"; /* 2D printing uses 3 byte BGR pixels in Raster printing */ static int J2DRasterBPP = 3; @@ -1354,7 +1354,7 @@ Java_sun_awt_windows_WPrinterJob__1startDoc(JNIEnv *env, jobject self, } else { destination = VerifyDestination(env, self); } - LPTSTR docname = NULL; + LPCTSTR docname = NULL; if (jobname != NULL) { LPTSTR tmp = (LPTSTR)JNU_GetStringPlatformChars(env, jobname, NULL); if (tmp == NULL) { diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Toolkit.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Toolkit.cpp index 69e4aea78a631..c8378fd22e5bd 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Toolkit.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Toolkit.cpp @@ -530,7 +530,7 @@ void ToolkitThreadProc(void *param) JNIEnv *env; JavaVMAttachArgs attachArgs; attachArgs.version = JNI_VERSION_1_2; - attachArgs.name = "AWT-Windows"; + attachArgs.name = (char*)"AWT-Windows"; attachArgs.group = data->threadGroup; jint res = jvm->AttachCurrentThreadAsDaemon((void **)&env, &attachArgs); diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsEnv.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsEnv.cpp index 92a7720fe7809..d65eeeaf44414 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsEnv.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsEnv.cpp @@ -232,7 +232,7 @@ Java_sun_awt_Win32FontManager_getEUDCFontFile(JNIEnv *env, jclass cl) { unsigned long fontPathLen = MAX_PATH + 1; WCHAR tmpPath[MAX_PATH + 1]; LPWSTR fontPath = fontPathBuf; - LPWSTR eudcKey = NULL; + LPCWSTR eudcKey = NULL; LANGID langID = GetSystemDefaultLangID(); //lookup for encoding ID, EUDC only supported in diff --git a/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_MidiIn.cpp b/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_MidiIn.cpp index 4d1f4fee19e9e..1b4fcb6608642 100644 --- a/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_MidiIn.cpp +++ b/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_MidiIn.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -47,6 +47,8 @@ extern "C" { #define MIDIIN_CHECK_ERROR #endif +#include + /* * Callback from the MIDI device for all messages. */ @@ -55,8 +57,8 @@ void CALLBACK MIDI_IN_PutMessage( HMIDIIN hMidiIn, UINT wMsg, UINT_PTR dwInstanc MidiDeviceHandle* handle = (MidiDeviceHandle*) dwInstance; - TRACE3("> MIDI_IN_PutMessage, hMidiIn: %x, wMsg: %x, dwInstance: %x\n", hMidiIn, wMsg, dwInstance); - TRACE2(" dwParam1: %x, dwParam2: %x\n", dwParam1, dwParam2); + TRACE3("> MIDI_IN_PutMessage, hMidiIn: 0x%" PRIxPTR ", wMsg: %x, dwInstance: 0x%" PRIxPTR "\n", (uintptr_t)hMidiIn, wMsg, (uintptr_t)dwInstance); + TRACE2(" dwParam1: 0x%" PRIxPTR ", dwParam2: 0x%" PRIxPTR "\n", (uintptr_t)dwParam1, (uintptr_t)dwParam2); switch(wMsg) { @@ -70,8 +72,8 @@ void CALLBACK MIDI_IN_PutMessage( HMIDIIN hMidiIn, UINT wMsg, UINT_PTR dwInstanc case MIM_MOREDATA: case MIM_DATA: - TRACE3(" MIDI_IN_PutMessage: MIM_MOREDATA or MIM_DATA. status=%x data1=%x data2=%x\n", - dwParam1 & 0xFF, (dwParam1 & 0xFF00)>>8, (dwParam1 & 0xFF0000)>>16); + TRACE3(" MIDI_IN_PutMessage: MIM_MOREDATA or MIM_DATA. status=%x data1=%x data2=%x\n", + (int)(dwParam1 & 0xFF), (int)((dwParam1 & 0xFF00)>>8), (int)((dwParam1 & 0xFF0000)>>16)); if (handle!=NULL && handle->queue!=NULL && handle->platformData) { MIDI_QueueAddShort(handle->queue, // queue stores packedMsg in big endian diff --git a/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_Ports.c b/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_Ports.c index 55dcb6e28827a..237aa89c12502 100644 --- a/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_Ports.c +++ b/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_Ports.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -251,7 +251,7 @@ void printInfo(PortInfo* info) { TRACE5(" PortInfo %p: handle=%p, mixerIndex=%d, dstLineCount=%d, dstLines=%p, ", info, (void*) info->handle, info->mixerIndex, info->dstLineCount, info->dstLines); TRACE5("srcLineCount=%d, srcLines=%p, targetPortCount=%d, sourcePortCount=%d, ports=%p, ", info->srcLineCount, info->srcLines, info->targetPortCount, info->sourcePortCount, info->ports); TRACE3("maxControlCount=%d, usedControlIDs=%d, controlIDs=%p \n", info->maxControlCount, info->usedControlIDs, info->controlIDs); - TRACE2("usedMuxData=%d, muxData=%p, controlIDs=%p \n", info->usedMuxData, info->muxData); + TRACE3("usedMuxData=%d, muxData=%p, controlIDs=%p \n", info->usedMuxData, info->muxData, info->controlIDs); } #endif // USE_TRACE diff --git a/src/java.management/share/classes/javax/management/modelmbean/RequiredModelMBean.java b/src/java.management/share/classes/javax/management/modelmbean/RequiredModelMBean.java index c706ababeca75..9b865081d4392 100644 --- a/src/java.management/share/classes/javax/management/modelmbean/RequiredModelMBean.java +++ b/src/java.management/share/classes/javax/management/modelmbean/RequiredModelMBean.java @@ -192,7 +192,7 @@ public RequiredModelMBean() * * @exception MBeanException Wraps a distributed communication Exception. * @exception RuntimeOperationsException Wraps an - * {link java.lang.IllegalArgumentException}: + * {@link java.lang.IllegalArgumentException}: * The MBeanInfo passed in parameter is null. * **/ diff --git a/src/java.management/share/classes/javax/management/remote/package.html b/src/java.management/share/classes/javax/management/remote/package.html index 3673d8dd8ed03..6beea83d143fc 100644 --- a/src/java.management/share/classes/javax/management/remote/package.html +++ b/src/java.management/share/classes/javax/management/remote/package.html @@ -2,7 +2,7 @@ JMX Remote API. - - - - - UpdatingBootTime - - - -

    UpdatingBootTime
    Bug ID:

    - -

    See the dialog box (usually in upper left corner) for instructions

    - - - - diff --git a/test/jdk/java/awt/Component/UpdatingBootTime/UpdatingBootTime.java b/test/jdk/java/awt/Component/UpdatingBootTime/UpdatingBootTime.java deleted file mode 100644 index 3a8479a5db794..0000000000000 --- a/test/jdk/java/awt/Component/UpdatingBootTime/UpdatingBootTime.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - test - @bug 6461933 7194219 - @summary adjust system boot time in nowMillisUTC() frequently - @author Andrei Dmitriev : area=awt.component - @run applet/manual=yesno UpdatingBootTime.html - */ - -/* - * verifies that time updated by the system is picked up by the Java App. - */ - -import java.applet.Applet; -import java.awt.*; -import java.awt.event.*; -import java.util.Date; - -public class UpdatingBootTime extends Applet -{ - //Declare things used in the test, like buttons and labels here - - public void init() - { - this.setLayout (new BorderLayout ()); - - String[] instructions = - { - "1) Press the mouse over the button.", - "2) A two timestamps would be printed.", - "3) Verify that they are not differ a lot: it is okay to observe a 1 or 2 seconds difference.", - "4) Change the system time significantly(by a month or a year) by using the OS abilities.", - "5) Click on the button once again.", - "6) Printed times should still be the same. Pay attention to the Month/Year if they were changed.", - "7) It is okay to observe a 1 or 2 seconds difference and this is not a fail.", - "8) If the difference is more then 1-2 seconds noticed then the test fail; otherwise pass." - }; - Sysout.createDialogWithInstructions( instructions ); - - }//End init() - - public void start () - { - Button b = new Button("Press me"); - b.addMouseListener(new MouseAdapter(){ - public void mousePressed(MouseEvent e){ - Sysout.println("Event occured : "+e); - Sysout.println("The event time is : "+ (new Date(e.getWhen()))); - Sysout.println("The system time is : "+ (new Date())); - } - }); - add(b); - setSize (200,200); - setVisible(true); - validate(); - }// start() -}// class UpdatingBootTime - -/* Place other classes related to the test after this line */ - - - - - -/**************************************************** - Standard Test Machinery - DO NOT modify anything below -- it's a standard - chunk of code whose purpose is to make user - interaction uniform, and thereby make it simpler - to read and understand someone else's test. - ****************************************************/ - -/** - This is part of the standard test machinery. - It creates a dialog (with the instructions), and is the interface - for sending text messages to the user. - To print the instructions, send an array of strings to Sysout.createDialog - WithInstructions method. Put one line of instructions per array entry. - To display a message for the tester to see, simply call Sysout.println - with the string to be displayed. - This mimics System.out.println but works within the test harness as well - as standalone. - */ - -class Sysout -{ - private static TestDialog dialog; - - public static void createDialogWithInstructions( String[] instructions ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - dialog.printInstructions( instructions ); - dialog.setVisible(true); - println( "Any messages for the tester will display here." ); - } - - public static void createDialog( ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - String[] defInstr = { "Instructions will appear here. ", "" } ; - dialog.printInstructions( defInstr ); - dialog.setVisible(true); - println( "Any messages for the tester will display here." ); - } - - - public static void printInstructions( String[] instructions ) - { - dialog.printInstructions( instructions ); - } - - - public static void println( String messageIn ) - { - dialog.displayMessage( messageIn ); - } - -}// Sysout class - -/** - This is part of the standard test machinery. It provides a place for the - test instructions to be displayed, and a place for interactive messages - to the user to be displayed. - To have the test instructions displayed, see Sysout. - To have a message to the user be displayed, see Sysout. - Do not call anything in this dialog directly. - */ -class TestDialog extends Dialog -{ - - TextArea instructionsText; - TextArea messageText; - int maxStringLength = 80; - - //DO NOT call this directly, go through Sysout - public TestDialog( Frame frame, String name ) - { - super( frame, name ); - int scrollBoth = TextArea.SCROLLBARS_BOTH; - instructionsText = new TextArea( "", 15, maxStringLength, scrollBoth ); - add( "North", instructionsText ); - - messageText = new TextArea( "", 5, maxStringLength, scrollBoth ); - add("Center", messageText); - - pack(); - - setVisible(true); - }// TestDialog() - - //DO NOT call this directly, go through Sysout - public void printInstructions( String[] instructions ) - { - //Clear out any current instructions - instructionsText.setText( "" ); - - //Go down array of instruction strings - - String printStr, remainingStr; - for( int i=0; i < instructions.length; i++ ) - { - //chop up each into pieces maxSringLength long - remainingStr = instructions[ i ]; - while( remainingStr.length() > 0 ) - { - //if longer than max then chop off first max chars to print - if( remainingStr.length() >= maxStringLength ) - { - //Try to chop on a word boundary - int posOfSpace = remainingStr. - lastIndexOf( ' ', maxStringLength - 1 ); - - if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1; - - printStr = remainingStr.substring( 0, posOfSpace + 1 ); - remainingStr = remainingStr.substring( posOfSpace + 1 ); - } - //else just print - else - { - printStr = remainingStr; - remainingStr = ""; - } - - instructionsText.append( printStr + "\n" ); - - }// while - - }// for - - }//printInstructions() - - //DO NOT call this directly, go through Sysout - public void displayMessage( String messageIn ) - { - messageText.append( messageIn + "\n" ); - System.out.println(messageIn); - } - -}// TestDialog class diff --git a/test/jdk/java/awt/Component/ZOrderTest/ZOrderTest.java b/test/jdk/java/awt/Component/ZOrderTest/ZOrderTest.java new file mode 100644 index 0000000000000..15003f753a6fe --- /dev/null +++ b/test/jdk/java/awt/Component/ZOrderTest/ZOrderTest.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4059430 + * @summary Test for component z-ordering consistency + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ZOrderTest + */ + +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Insets; +import java.awt.Label; +import java.awt.Panel; +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +public class ZOrderTest { + public final static String INSTRUCTIONS = """ + The ZOrderTest creates two frames. + - Frame 1 has components added to an intermediate panel + - Frame 2 has components added directly to the frame itself + Verify that the components are in the correct z-order. Lower numbered + components should overlap higher numbered ones (e.g. component zero should + appear on top of component one). + Both frames should have the same component ordering, and this ordering should + be the same on all supported operating systems. + """; + + public static void main(String [] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Component ZOrder Test") + .testUI(ZOrderTest::makeFrames) + .instructions(INSTRUCTIONS) + .columns(40) + .logArea() + .build() + .awaitAndCheck(); + } + + private static List makeFrames() { + Frame frame, frame2; + + // test adding components to panel on a frame + frame = new Frame("ZOrderTest(1) for 4059430"); + frame.pack(); + frame.show(); + Panel panel = new ZOrderPanel(); + frame.setBounds(0, 0, 500, 350); + frame.setLayout(new GridLayout()); + frame.add(panel); + doTest(panel); + + // test adding components directly to frame + frame2 = new ZOrderTestFrame("ZOrderTest(2) for 4059430"); + frame2.pack(); + frame2.show(); + frame2.setBounds(80, 80, 500, 350); + doTest(frame2); + + return List.of(frame, frame2); + } + + /* + * This tests various boundary conditions with z-ordering + * - inserting at the top of the z-order + * - inserting at the bottom of the z-order + * - inserting in the middle of the z-order + */ + private static void doTest(Container cont) { + Component compZero, compOne, compTwo, compThree, compFour; + + compZero = makeBox(cont, "Comp One", Color.blue, -1); + // insert on top + compOne = makeBox(cont, "Comp Zero", Color.yellow, 0); + // put at the back + compThree = makeBox(cont, "Comp Three", Color.red, 2); + // insert in last position + compTwo = makeBox(cont, "Comp Two", Color.green, 3); + // swap compTwo and compThree to correct positions + cont.remove(compTwo); + cont.add(compTwo, 2); + // one more test of adding to the end + compFour = makeBox(cont, "Comp Four", Color.magenta, -1); + // re-validate so components cascade into proper place + cont.validate(); + } + + private static Component makeBox(Container cont, String s, Color c, int index) { + Label l = new Label(s); + l.setBackground(c); + l.setAlignment(Label.RIGHT); + if (index == -1) { + cont.add(l); // semantically equivalent to -1, but why not test this too + } else { + cont.add(l, index); + } + cont.validate(); + return l; + } + + /** + * Cascades components across the container so + * that they overlap, demonstrating their z-ordering + */ + static void doCascadeLayout(Container cont) { + int i, n; + Insets ins = cont.insets(); + n = cont.countComponents(); + for (i = n - 1; i >= 0; i--) { + Component comp = cont.getComponent(i); + comp.reshape(ins.left + 75 * i, ins.top + 30 * i, 100, 100); + } + } +} + +class ZOrderPanel extends Panel { + public void layout() { + ZOrderTest.doCascadeLayout(this); + } +} + +class ZOrderTestFrame extends Frame +{ + public ZOrderTestFrame(String title) { + super(title); + } + + public void layout() { + ZOrderTest.doCascadeLayout(this); + } +} diff --git a/test/jdk/java/awt/Cursor/BlockedWindowTest/BlockedWindowTest.java b/test/jdk/java/awt/Cursor/BlockedWindowTest/BlockedWindowTest.java new file mode 100644 index 0000000000000..f1313dbb74246 --- /dev/null +++ b/test/jdk/java/awt/Cursor/BlockedWindowTest/BlockedWindowTest.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2006, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6391547 + * @summary Test if the JTextField's cursor is changed when there is a modal dialog + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual BlockedWindowTest + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Cursor; +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.TextField; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +class MyDialog extends Dialog implements ActionListener { + MyDialog(Frame owner) { + super(owner, "Modal dialog", true); + setBounds(owner.getX() + 150, owner.getY() + 150, 100, 100); + setLayout(new BorderLayout()); + Button b = new Button("Close"); + add(b, "South"); + b.addActionListener(this); + setVisible(true); + } + + public void actionPerformed(ActionEvent ae) { + setVisible(false); + this.dispose(); + } +} + +class MyFrame extends Frame implements ActionListener { + Dialog d; + + public MyFrame() { + super("ManualYesNoTest"); + Button b = new Button("Click here"); + TextField tf = new TextField("A text field"); + b.addActionListener(this); + setLayout(new BorderLayout()); + setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + add(b, "South"); + add(tf, "North"); + setSize(300, 300); + } + + public void actionPerformed(ActionEvent ae) { + d = new MyDialog(this); + } +} + +public class BlockedWindowTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Verify that the hand cursor is displayed over the window and + text cursor over TextField. + Click the button in the window to display a modal dialog. + Verify that default cursor is displayed over the window + and over TextField now. + Then close modal dialog and verify that hand cursor is + displayed over window and text cursor over TextField. + If so, press PASS, else press FAIL. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(BlockedWindowTest::createUI) + .build() + .awaitAndCheck(); + } + + public static MyFrame createUI() { + MyFrame f = new MyFrame(); + return f; + } +} diff --git a/test/jdk/java/awt/Cursor/CursorDragTest/ListDragCursor.java b/test/jdk/java/awt/Cursor/CursorDragTest/ListDragCursor.java new file mode 100644 index 0000000000000..da0394a13928b --- /dev/null +++ b/test/jdk/java/awt/Cursor/CursorDragTest/ListDragCursor.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4313052 + * @summary Test cursor changes after mouse dragging ends + * @run main/manual ListDragCursor + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Cursor; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.List; +import java.awt.Panel; +import java.awt.TextArea; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class ListDragCursor { + private static final String INSTRUCTIONS = """ + 1. Move mouse to the TextArea. + 2. Press the left mouse button. + 3. Drag mouse to the list. + 4. Release the left mouse button. + + The mouse cursor should appear as an I-beam cursor + and should stay the same while dragging across the + components. Once you reach the list, release the + left mouse button. As soon as you release the left + mouse button, the cursor should change to a Hand + cursor. If true, this test passes. + + The test fails if the cursor updates while dragging + over the components before releasing the left + mouse button. + """; + + private static Frame testFrame; + private static Frame instructionsFrame; + + private static final CountDownLatch countDownLatch = new CountDownLatch(1); + + public static void main(String[] args) throws Exception { + try { + EventQueue.invokeAndWait(() -> { + instructionsFrame = createInstructionsFrame(); + testFrame = createTestFrame(); + }); + if (!countDownLatch.await(5, TimeUnit.MINUTES)) { + throw new RuntimeException("Test timeout : No action was" + + " taken on the test."); + } + } finally { + EventQueue.invokeAndWait(ListDragCursor::disposeFrames); + } + } + + static Frame createTestFrame() { + Frame frame = new Frame("Cursor change after drag"); + Panel panel = new Panel(); + + List list = new List(2); + list.add("List1"); + list.add("List2"); + list.add("List3"); + list.add("List4"); + list.setCursor(new Cursor(Cursor.HAND_CURSOR)); + + TextArea textArea = new TextArea(3, 5); + textArea.setCursor(new Cursor(Cursor.TEXT_CURSOR)); + + panel.add(textArea); + panel.add(list); + + frame.add(panel); + frame.setSize(300, 150); + frame.setLocation(instructionsFrame.getX() + + instructionsFrame.getWidth(), + instructionsFrame.getY()); + frame.setVisible(true); + return frame; + } + + static Frame createInstructionsFrame() { + Frame frame = new Frame("Test Instructions"); + Panel mainPanel = new Panel(new BorderLayout()); + TextArea textArea = new TextArea(INSTRUCTIONS, + 15, 35, TextArea.SCROLLBARS_NONE); + + Panel btnPanel = new Panel(); + Button passBtn = new Button("Pass"); + Button failBtn = new Button("Fail"); + btnPanel.add(passBtn); + btnPanel.add(failBtn); + + passBtn.addActionListener(e -> { + countDownLatch.countDown(); + System.out.println("Test passed."); + }); + failBtn.addActionListener(e -> { + countDownLatch.countDown(); + throw new RuntimeException("Test Failed."); + }); + + mainPanel.add(textArea, BorderLayout.CENTER); + mainPanel.add(btnPanel, BorderLayout.SOUTH); + + frame.add(mainPanel); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + return frame; + } + + static void disposeFrames() { + if (testFrame != null) { + testFrame.dispose(); + } + if (instructionsFrame != null) { + instructionsFrame.dispose(); + } + } +} diff --git a/test/jdk/java/awt/Cursor/CursorOverlappedPanelsTest.java b/test/jdk/java/awt/Cursor/CursorOverlappedPanelsTest.java new file mode 100644 index 0000000000000..067ccdd4b9f4f --- /dev/null +++ b/test/jdk/java/awt/Cursor/CursorOverlappedPanelsTest.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Point; +import java.lang.reflect.InvocationTargetException; +import javax.swing.BorderFactory; +import javax.swing.JFrame; +import javax.swing.JLayeredPane; +import javax.swing.JPanel; + +/* + * @test + * @bug 8007155 + * @summary [macosx] Disabled panel takes mouse input in JLayeredPane + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual CursorOverlappedPanelsTest + */ + +public class CursorOverlappedPanelsTest extends Frame { + public static JFrame initialize() { + final JFrame frame = new JFrame("Overlapping Panels Cursor Test"); + + JLayeredPane layeredPane = new JLayeredPane(); + layeredPane.setPreferredSize(new Dimension(400, 400)); + JPanel enabledPanel = createPanel(new Point(10, 10), true); + JPanel disabledPanel = createPanel(new Point(100, 100), false); + layeredPane.add(disabledPanel, JLayeredPane.PALETTE_LAYER); + layeredPane.add(enabledPanel, JLayeredPane.DEFAULT_LAYER); + + frame.getContentPane().add(layeredPane); + frame.pack(); + return frame; + } + + private static JPanel createPanel(Point location, boolean enabled) { + final JPanel panel = new JPanel(); + panel.setOpaque(false); + panel.setEnabled(enabled); + panel.setSize(new Dimension(200, 200)); + panel.setLocation(location); + panel.setBorder(BorderFactory.createTitledBorder( + enabled ? "Enabled" : "Disabled")); + panel.setCursor(Cursor.getPredefinedCursor( + enabled ? Cursor.CROSSHAIR_CURSOR : Cursor.DEFAULT_CURSOR)); + return panel; + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + String instructions = """ + 1) Move the mouse cursor into the area + of Enabled and Disabled panels intersection; + 2) Check that the crosshair cursor is displayed. + If so, press PASS, otherwise press FAIL. + """; + + PassFailJFrame.builder() + .title("Overlapping Panels Cursor Test Instructions") + .instructions(instructions) + .rows((int) instructions.lines().count() + 1) + .columns(30) + .testUI(CursorOverlappedPanelsTest::initialize) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/Cursor/CursorOverlappedPanelsTest/CursorOverlappedPanelsTest.html b/test/jdk/java/awt/Cursor/CursorOverlappedPanelsTest/CursorOverlappedPanelsTest.html deleted file mode 100644 index d7e6157e927da..0000000000000 --- a/test/jdk/java/awt/Cursor/CursorOverlappedPanelsTest/CursorOverlappedPanelsTest.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - - CursorOverlappedPanelsTest, bug ID 8007155 - - - -

    See the dialog box (usually in upper left corner) for instructions

    - - diff --git a/test/jdk/java/awt/Cursor/CursorOverlappedPanelsTest/CursorOverlappedPanelsTest.java b/test/jdk/java/awt/Cursor/CursorOverlappedPanelsTest/CursorOverlappedPanelsTest.java deleted file mode 100644 index 40f1ae6c49938..0000000000000 --- a/test/jdk/java/awt/Cursor/CursorOverlappedPanelsTest/CursorOverlappedPanelsTest.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.awt.*; -//import java.applet.Applet; -import javax.swing.BorderFactory; -import javax.swing.JApplet; -import javax.swing.JFrame; -import javax.swing.JLayeredPane; -import javax.swing.JPanel; - -/** - * @test - * @bug 8007155 - * @summary [macosx] Disabled panel takes mouse input in JLayeredPane - * @author Alexander Scherbatiy: area=java.awt.Cursor - * @run applet/manual=yesno CursorOverlappedPanelsTest.html - */ -public class CursorOverlappedPanelsTest extends JApplet { - //Declare things used in the test, like buttons and labels here - - public void init() { - //Create instructions for the user here, as well as set up - // the environment -- set the layout manager, add buttons, - // etc. - this.setLayout(new BorderLayout()); - - String[] instructions = { - "Verify that the Crosshair cursor from enabled panel" - + " is displayed on the panels intersection", - "1) Move the mosue cursor on the Enabled and Disabled panels" - + " intersection", - "2) Check that the Crosshair cursor is displayed ", - "If so, press PASS, else press FAIL." - }; - Sysout.createDialogWithInstructions(instructions); - - }//End init() - - public void start() { - //Get things going. Request focus, set size, et cetera - setSize(200, 200); - setVisible(true); - validate(); - try { - EventQueue.invokeAndWait(new Runnable() { - public void run() { - createAndShowGUI(); - } - }); - } catch (Exception e) { - throw new RuntimeException(e); - } - }// start() - - //The rest of this class is the actions which perform the test... - //Use Sysout.println to communicate with the user NOT System.out!! - //Sysout.println ("Something Happened!"); - private static JPanel createPanel(Point location, boolean enabled) { - final JPanel panel = new JPanel(); - panel.setOpaque(false); - panel.setEnabled(enabled); - panel.setSize(new Dimension(200, 200)); - panel.setLocation(location); - panel.setBorder(BorderFactory.createTitledBorder( - enabled ? "Enabled" : "Disabled")); - panel.setCursor(Cursor.getPredefinedCursor( - enabled ? Cursor.CROSSHAIR_CURSOR : Cursor.DEFAULT_CURSOR)); - System.out.println("cursor: " + Cursor.getPredefinedCursor(enabled ? Cursor.CROSSHAIR_CURSOR : Cursor.DEFAULT_CURSOR)); - return panel; - } - - private static void createAndShowGUI() { - final JFrame frame = new JFrame("Test"); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - JLayeredPane layeredPane = new JLayeredPane(); - layeredPane.setPreferredSize(new Dimension(400, 400)); - JPanel enabledPanel = createPanel(new Point(10, 10), true); - JPanel disabledPanel = createPanel(new Point(100, 100), false); - layeredPane.add(disabledPanel, JLayeredPane.PALETTE_LAYER); - layeredPane.add(enabledPanel, JLayeredPane.DEFAULT_LAYER); - - frame.getContentPane().add(layeredPane); - frame.pack(); - frame.setVisible(true); - } -}// class BlockedWindowTest - -/* Place other classes related to the test after this line */ -/** - * ************************************************** - * Standard Test Machinery DO NOT modify anything below -- it's a standard chunk - * of code whose purpose is to make user interaction uniform, and thereby make - * it simpler to read and understand someone else's test. - * ************************************************** - */ -/** - * This is part of the standard test machinery. It creates a dialog (with the - * instructions), and is the interface for sending text messages to the user. To - * print the instructions, send an array of strings to Sysout.createDialog - * WithInstructions method. Put one line of instructions per array entry. To - * display a message for the tester to see, simply call Sysout.println with the - * string to be displayed. This mimics System.out.println but works within the - * test harness as well as standalone. - */ -class Sysout { - - private static TestDialog dialog; - - public static void createDialogWithInstructions(String[] instructions) { - dialog = new TestDialog(new Frame(), "Instructions"); - dialog.printInstructions(instructions); - dialog.setVisible(true); - println("Any messages for the tester will display here."); - } - - public static void createDialog() { - dialog = new TestDialog(new Frame(), "Instructions"); - String[] defInstr = {"Instructions will appear here. ", ""}; - dialog.printInstructions(defInstr); - dialog.setVisible(true); - println("Any messages for the tester will display here."); - } - - public static void printInstructions(String[] instructions) { - dialog.printInstructions(instructions); - } - - public static void println(String messageIn) { - dialog.displayMessage(messageIn); - } -}// Sysout class - -/** - * This is part of the standard test machinery. It provides a place for the test - * instructions to be displayed, and a place for interactive messages to the - * user to be displayed. To have the test instructions displayed, see Sysout. To - * have a message to the user be displayed, see Sysout. Do not call anything in - * this dialog directly. - */ -class TestDialog extends Dialog { - - TextArea instructionsText; - TextArea messageText; - int maxStringLength = 80; - - //DO NOT call this directly, go through Sysout - public TestDialog(Frame frame, String name) { - super(frame, name); - int scrollBoth = TextArea.SCROLLBARS_BOTH; - instructionsText = new TextArea("", 15, maxStringLength, scrollBoth); - add("North", instructionsText); - - messageText = new TextArea("", 5, maxStringLength, scrollBoth); - add("Center", messageText); - - pack(); - - setVisible(true); - }// TestDialog() - - //DO NOT call this directly, go through Sysout - public void printInstructions(String[] instructions) { - //Clear out any current instructions - instructionsText.setText(""); - - //Go down array of instruction strings - - String printStr, remainingStr; - for (int i = 0; i < instructions.length; i++) { - //chop up each into pieces maxSringLength long - remainingStr = instructions[ i]; - while (remainingStr.length() > 0) { - //if longer than max then chop off first max chars to print - if (remainingStr.length() >= maxStringLength) { - //Try to chop on a word boundary - int posOfSpace = remainingStr. - lastIndexOf(' ', maxStringLength - 1); - - if (posOfSpace <= 0) { - posOfSpace = maxStringLength - 1; - } - - printStr = remainingStr.substring(0, posOfSpace + 1); - remainingStr = remainingStr.substring(posOfSpace + 1); - } //else just print - else { - printStr = remainingStr; - remainingStr = ""; - } - - instructionsText.append(printStr + "\n"); - - }// while - - }// for - - }//printInstructions() - - //DO NOT call this directly, go through Sysout - public void displayMessage(String messageIn) { - messageText.append(messageIn + "\n"); - System.out.println(messageIn); - } -}// TestDialog class diff --git a/test/jdk/java/awt/Cursor/CursorUpdateTest/CursorUpdateTest.java b/test/jdk/java/awt/Cursor/CursorUpdateTest/CursorUpdateTest.java new file mode 100644 index 0000000000000..33e2a3cb166a1 --- /dev/null +++ b/test/jdk/java/awt/Cursor/CursorUpdateTest/CursorUpdateTest.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 5097531 + * @summary Make sure the cursor updates correctly under certain + * circumstances even when the EDT is busy + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual CursorUpdateTest + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Graphics; + +public class CursorUpdateTest { + final static String progress = "|/-\\"; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Check the following: + 1. Cursor must be crosshair when hovering the mouse over the + blue square. + 2. Crosshair cursor must not flicker. + 3. The cursor must be "I-beam" when hovering the mouse over the + button. + 4. Click the button - it will display "Busy" message and a + rotating bar for 5 seconds. The cursor must change to + hourglass. + 5. (Windows only) While the cursor is on the button, press Alt. + The cursor must change to normal shape. Pressing Alt again + must revert it back to I-beam. + 6. Move the mouse out of the button and back onto it. The cursor + must update correctly (hourglass over the button, normal + over the frame) even when the button displays "busy". + Do not try to check (1) or (5) when the button displays + "Busy" - this is not required. + Pass if all the steps are as behave as described. Otherwise, + fail this test. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(CursorUpdateTest::createUI) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame f = new Frame(); + f.setLayout(new FlowLayout()); + Button b = new Button("Button"); + f.add(b); + b.setCursor(new Cursor(Cursor.TEXT_CURSOR)); + Component c = new MyComponent(); + f.add(c); + c.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR)); + b.addActionListener(e -> { + String oldLabel = b.getLabel(); + Cursor oldCursor = b.getCursor(); + b.setCursor(new Cursor(Cursor.WAIT_CURSOR)); + try { + for (int i = 0; i < 50; i++) { + b.setLabel("Busy " + progress.charAt(i % 4)); + Thread.sleep(100); + } + } catch (InterruptedException ex) { + } + b.setCursor(oldCursor); + b.setLabel(oldLabel); + }); + return f; + } +} + +class MyComponent extends Component { + public void paint(Graphics g) { + g.setColor(getBackground()); + g.fillRect(0, 0, getSize().width, getSize().height); + } + + public MyComponent() { + setBackground(Color.blue); + } + + public Dimension getPreferredSize() { + return new Dimension(100, 100); + } +} diff --git a/test/jdk/java/awt/Cursor/CustomCursorTest/CustomCursorTest.java b/test/jdk/java/awt/Cursor/CustomCursorTest/CustomCursorTest.java new file mode 100644 index 0000000000000..32c1fdc150622 --- /dev/null +++ b/test/jdk/java/awt/Cursor/CustomCursorTest/CustomCursorTest.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4174035 4106384 4205805 + * @summary Test for functionality of Custom Cursor + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual CustomCursorTest + */ + +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Frame; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Toolkit; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; + +import javax.imageio.ImageIO; +import javax.swing.JFrame; +import javax.swing.JLabel; + +import static java.awt.image.BufferedImage.TYPE_INT_ARGB; + +public class CustomCursorTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This test is for switching between a custom cursor and the + system cursor. + + 1. Click on the test window panel to change from the default + system cursor to the custom red square cursor + 2. Verify that the square cursor shows when the panel is clicked + 3. Verify that the square cursor is colored red + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(CustomCursorTest::createUI) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + JFrame f = new JFrame("Custom Cursor Test"); + CustomCursorPanel c = null; + try { + c = new CustomCursorPanel(); + } catch (IOException e) { + e.printStackTrace(); + } + + f.setIconImage(c.getImage()); + f.getContentPane().add(c); + f.setSize(400, 400); + return f; + } +} + +class CustomCursorPanel extends Panel { + Toolkit toolkit = Toolkit.getDefaultToolkit(); + Image image; + Cursor cursor; + boolean flip = false; + + public CustomCursorPanel() throws IOException { + generateRedSquareCursor(); + + image = toolkit.getImage(System.getProperty("test.classes", ".") + + java.io.File.separator + "square_cursor.gif"); + + setBackground(Color.green); + cursor = toolkit.createCustomCursor(image, new Point(0, 0), "custom"); + + JLabel c = (JLabel) add(new JLabel("click to switch between " + + "red square and default cursors")); + c.setBackground(Color.white); + c.setForeground(Color.red); + + addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent me) { + if (!flip) { + setCursor(cursor); + flip = true; + } else { + setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + flip = false; + } + } + }); + } + + public Image getImage() { + return image; + } + + private static void generateRedSquareCursor() throws IOException { + Path p = Path.of(System.getProperty("test.classes", ".")); + BufferedImage bImg = new BufferedImage(35, 34, TYPE_INT_ARGB); + Graphics2D cg = bImg.createGraphics(); + cg.setColor(Color.RED); + cg.fillRect(0, 0, 35, 34); + ImageIO.write(bImg, "png", new File(p + java.io.File.separator + + "square_cursor.gif")); + } +} diff --git a/test/jdk/java/awt/Cursor/HiddenDialogParentTest/HiddenDialogParentTest.java b/test/jdk/java/awt/Cursor/HiddenDialogParentTest/HiddenDialogParentTest.java new file mode 100644 index 0000000000000..7450f4ec3bb77 --- /dev/null +++ b/test/jdk/java/awt/Cursor/HiddenDialogParentTest/HiddenDialogParentTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 5079694 + * @summary Test if JDialog respects setCursor + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual HiddenDialogParentTest + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Cursor; + +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.border.LineBorder; + +public class HiddenDialogParentTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + You can see a label area in the center of JDialog. + Verify that the cursor is a hand cursor in this area. + If so, press pass, else press fail. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(HiddenDialogParentTest::createUI) + .build() + .awaitAndCheck(); + } + + public static JDialog createUI() { + JDialog dialog = new JDialog(); + dialog.setTitle("JDialog Cursor Test"); + dialog.setLayout(new BorderLayout()); + JLabel centerLabel = new JLabel("Cursor should be a hand in this " + + "label area"); + centerLabel.setBorder(new LineBorder(Color.BLACK)); + centerLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + dialog.add(centerLabel, BorderLayout.CENTER); + dialog.setSize(300, 200); + + return dialog; + } +} diff --git a/test/jdk/java/awt/Cursor/InvalidImageCustomCursorTest/InvalidImageCustomCursorTest.java b/test/jdk/java/awt/Cursor/InvalidImageCustomCursorTest/InvalidImageCustomCursorTest.java new file mode 100644 index 0000000000000..9877df342ec60 --- /dev/null +++ b/test/jdk/java/awt/Cursor/InvalidImageCustomCursorTest/InvalidImageCustomCursorTest.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4212593 + * @summary The Toolkit.createCustomCursor does not check absence of the + * image of cursor + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual InvalidImageCustomCursorTest + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Frame; +import java.awt.Image; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Toolkit; + +public class InvalidImageCustomCursorTest { + static Cursor cursor; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Press 'Hide' button to hide (set transparent) cursor for the + green panel. Move the pointer over the green panel - pointer + should disappear. Press 'Default' button to set default cursor + for the green panel. + + If you see any exceptions or cursor is not transparent, + test failed, otherwise it passed. + """; + + Toolkit tk = Toolkit.getDefaultToolkit(); + Image image = tk.getImage("NON_EXISTING_FILE.gif"); + Point p = new Point(0, 0); + + cursor = tk.createCustomCursor(image, p, "Test"); + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(InvalidImageCustomCursorTest::createUI) + .logArea(5) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame f = new Frame("Invalid Cursor Image Test"); + f.setLayout(new BorderLayout()); + f.setSize(200, 200); + + Button def = new Button("Default"); + Button hide = new Button("Hide"); + Panel panel = new Panel(); + + def.addActionListener(e -> panel.setCursor(Cursor.getDefaultCursor())); + hide.addActionListener(e -> panel.setCursor(cursor)); + + panel.setBackground(Color.green); + panel.setSize(100, 100); + f.add("Center", panel); + f.add("North", hide); + f.add("South", def); + + return f; + } +} diff --git a/test/jdk/java/awt/Cursor/JPanelCursorTest/JPanelCursorTest.java b/test/jdk/java/awt/Cursor/JPanelCursorTest/JPanelCursorTest.java new file mode 100644 index 0000000000000..33437a6545edc --- /dev/null +++ b/test/jdk/java/awt/Cursor/JPanelCursorTest/JPanelCursorTest.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4114073 + * @summary Test for setCursor in a JPanel when added to a JFrame's contentPane + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual JPanelCursorTest + */ + +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Graphics; + +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JSplitPane; +import javax.swing.border.BevelBorder; + +public class JPanelCursorTest { + public static void main(String[] args) throws Exception { + final String INSTRUCTIONS = """ + This test checks for setCursor in a JPanel when added to a + JFrame's contentPane. + + 1. Verify that the cursor in the left side of the test window + is a text cursor. + 2. Verify that the cursor changes to the crosshair cursor when + pointing over the button. + 3. Verify that the cursor changes to the hand cursor when in + the right side of the splitpane (and not on the button). + 4. Verify that the cursor changes to the wait cursor when in + the empty bottom section of the test window. + + If true, then pass the test. Otherwise, fail this test. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(37) + .testUI(JPanelCursorTest::createUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createUI() { + JFrame frame = new JFrame("Cursor Test Frame"); + + JSplitPane j = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); + ExtJComponent pane = new ExtJComponent(); + + CursorBugPanel panel = new CursorBugPanel(); + + j.setLeftComponent(pane); + j.setRightComponent(panel); + j.setContinuousLayout(true); + j.setSize(200, 200); + + frame.getContentPane().add("North", j); + pane.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); + frame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + frame.setSize(300, 200); + return frame; + } +} + +class ExtJComponent extends JComponent { + public ExtJComponent() { + super(); + setOpaque(true); + setBackground(Color.green); + setForeground(Color.red); + setBorder(new BevelBorder(BevelBorder.RAISED)); + } + public void paintComponent(Graphics g) { + g.drawString("Text", 20, 30); + } + public Dimension getPreferredSize() { + return new Dimension(100, 100); + } +} + +class CursorBugPanel extends JPanel { + public CursorBugPanel() { + // BUG: fails to set cursor for panel + setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + + // Create a button + JButton button = new JButton("Crosshair"); + + // Sets cursor for button, no problem + button.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); + add(button); + } + + public void paintComponent(Graphics g) { + g.drawString("Hand", 20, 60); + } +} diff --git a/test/jdk/java/awt/Cursor/MultiResolutionCursorTest.java b/test/jdk/java/awt/Cursor/MultiResolutionCursorTest.java new file mode 100644 index 0000000000000..cfcaaa256a92b --- /dev/null +++ b/test/jdk/java/awt/Cursor/MultiResolutionCursorTest.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Label; +import java.awt.Point; +import java.awt.Toolkit; +import java.awt.image.BaseMultiResolutionImage; +import java.awt.image.BufferedImage; +import java.lang.reflect.InvocationTargetException; +import javax.swing.JFrame; + +/* + * @test + * @bug 8028212 + * @summary [macosx] Custom Cursor HiDPI support + * @requires (os.family == "mac") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MultiResolutionCursorTest + */ +public class MultiResolutionCursorTest { + static final int sizes[] = {8, 16, 32, 128}; + + private static JFrame initialize() { + final Image image = new BaseMultiResolutionImage( + createResolutionVariant(0), + createResolutionVariant(1), + createResolutionVariant(2), + createResolutionVariant(3) + ); + + int center = sizes[0] / 2; + Cursor cursor = Toolkit.getDefaultToolkit().createCustomCursor( + image, new Point(center, center), "multi-resolution cursor"); + + JFrame frame = new JFrame("Multi-resolution Cursor Test Frame"); + frame.setSize(300, 300); + frame.add(new Label("Move cursor here")); + frame.setCursor(cursor); + return frame; + } + + private static BufferedImage createResolutionVariant(int i) { + BufferedImage resolutionVariant = new BufferedImage(sizes[i], sizes[i], + BufferedImage.TYPE_INT_RGB); + Graphics2D g2 = resolutionVariant.createGraphics(); + Color colors[] = {Color.WHITE, Color.RED, Color.GREEN, Color.BLUE}; + g2.setColor(colors[i]); + g2.fillRect(0, 0, sizes[i], sizes[i]); + g2.dispose(); + return resolutionVariant; + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + String instructions = """ + Verify that high resolution custom cursor is used + on HiDPI displays. + 1) Run the test on Retina display or enable the Quartz Debug + and select the screen resolution with (HiDPI) label + 2) Move the cursor to the Test Frame + 3) Check that cursor has red, green or blue color + If so, press Pass, else press Fail. + """; + + PassFailJFrame.builder() + .title("Multi-resolution Cursor Test Instructions") + .instructions(instructions) + .rows((int) instructions.lines().count() + 1) + .columns(40) + .testUI(MultiResolutionCursorTest::initialize) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/Cursor/MultiResolutionCursorTest/MultiResolutionCursorTest.html b/test/jdk/java/awt/Cursor/MultiResolutionCursorTest/MultiResolutionCursorTest.html deleted file mode 100644 index b02bf2be73e8e..0000000000000 --- a/test/jdk/java/awt/Cursor/MultiResolutionCursorTest/MultiResolutionCursorTest.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - - High resolution custom cursor test, bug ID 8028212 - - - -

    See the dialog box (usually in upper left corner) for instructions

    - - diff --git a/test/jdk/java/awt/Cursor/MultiResolutionCursorTest/MultiResolutionCursorTest.java b/test/jdk/java/awt/Cursor/MultiResolutionCursorTest/MultiResolutionCursorTest.java deleted file mode 100644 index 6821f9c55e657..0000000000000 --- a/test/jdk/java/awt/Cursor/MultiResolutionCursorTest/MultiResolutionCursorTest.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Cursor; -import java.awt.Dialog; -import java.awt.Frame; -import java.awt.Graphics2D; -import java.awt.Image; -import java.awt.Label; -import java.awt.Point; -import java.awt.TextArea; -import java.awt.Toolkit; -import java.awt.image.BaseMultiResolutionImage; -import java.awt.image.BufferedImage; -import javax.swing.JApplet; - -import jdk.test.lib.Platform; - -/** - * @test - * @bug 8028212 - * @summary [macosx] Custom Cursor HiDPI support - * @author Alexander Scherbatiy - * @library /test/lib - * @modules java.desktop/sun.awt.image - * @build jdk.test.lib.Platform - * @run applet/manual=yesno MultiResolutionCursorTest.html - */ -public class MultiResolutionCursorTest extends JApplet { - //Declare things used in the test, like buttons and labels here - - static final int sizes[] = {8, 16, 32, 128}; - static final Color colors[] = {Color.WHITE, Color.RED, Color.GREEN, Color.BLUE}; - - public void init() { - //Create instructions for the user here, as well as set up - // the environment -- set the layout manager, add buttons, - // etc. - this.setLayout(new BorderLayout()); - - if (Platform.isOSX()) { - String[] instructions = { - "Verify that high resolution custom cursor is used" - + " on HiDPI displays.", - "1) Run the test on Retina display or enable the Quartz Debug" - + " and select the screen resolution with (HiDPI) label", - "2) Move the cursor to the Test Frame", - "3) Check that cursor has red, green or blue color", - "If so, press PASS, else press FAIL." - }; - Sysout.createDialogWithInstructions(instructions); - - } else { - String[] instructions = { - "This test is not applicable to the current platform. Press PASS." - }; - Sysout.createDialogWithInstructions(instructions); - } - }//End init() - - public void start() { - //Get things going. Request focus, set size, et cetera - setSize(200, 200); - setVisible(true); - validate(); - - final Image image = new BaseMultiResolutionImage( - createResolutionVariant(0), - createResolutionVariant(1), - createResolutionVariant(2), - createResolutionVariant(3) - ); - - int center = sizes[0] / 2; - Cursor cursor = Toolkit.getDefaultToolkit().createCustomCursor( - image, new Point(center, center), "multi-resolution cursor"); - - Frame frame = new Frame("Test Frame"); - frame.setSize(300, 300); - frame.setLocation(300, 50); - frame.add(new Label("Move cursor here")); - frame.setCursor(cursor); - frame.setVisible(true); - }// start() - - static BufferedImage createResolutionVariant(int i) { - BufferedImage resolutionVariant = new BufferedImage(sizes[i], sizes[i], - BufferedImage.TYPE_INT_RGB); - Graphics2D g2 = resolutionVariant.createGraphics(); - g2.setColor(colors[i]); - g2.fillRect(0, 0, sizes[i], sizes[i]); - g2.dispose(); - return resolutionVariant; - } -}// class BlockedWindowTest - -/* Place other classes related to the test after this line */ -/** - * ************************************************** - * Standard Test Machinery DO NOT modify anything below -- it's a standard chunk - * of code whose purpose is to make user interaction uniform, and thereby make - * it simpler to read and understand someone else's test. - * ************************************************** - */ -/** - * This is part of the standard test machinery. It creates a dialog (with the - * instructions), and is the interface for sending text messages to the user. To - * print the instructions, send an array of strings to Sysout.createDialog - * WithInstructions method. Put one line of instructions per array entry. To - * display a message for the tester to see, simply call Sysout.println with the - * string to be displayed. This mimics System.out.println but works within the - * test harness as well as standalone. - */ -class Sysout { - - private static TestDialog dialog; - - public static void createDialogWithInstructions(String[] instructions) { - dialog = new TestDialog(new Frame(), "Instructions"); - dialog.printInstructions(instructions); - dialog.setVisible(true); - println("Any messages for the tester will display here."); - } - - public static void createDialog() { - dialog = new TestDialog(new Frame(), "Instructions"); - String[] defInstr = {"Instructions will appear here. ", ""}; - dialog.printInstructions(defInstr); - dialog.setVisible(true); - println("Any messages for the tester will display here."); - } - - public static void printInstructions(String[] instructions) { - dialog.printInstructions(instructions); - } - - public static void println(String messageIn) { - dialog.displayMessage(messageIn); - } -}// Sysout class - -/** - * This is part of the standard test machinery. It provides a place for the test - * instructions to be displayed, and a place for interactive messages to the - * user to be displayed. To have the test instructions displayed, see Sysout. To - * have a message to the user be displayed, see Sysout. Do not call anything in - * this dialog directly. - */ -class TestDialog extends Dialog { - - TextArea instructionsText; - TextArea messageText; - int maxStringLength = 80; - - //DO NOT call this directly, go through Sysout - public TestDialog(Frame frame, String name) { - super(frame, name); - int scrollBoth = TextArea.SCROLLBARS_BOTH; - instructionsText = new TextArea("", 15, maxStringLength, scrollBoth); - add("North", instructionsText); - - messageText = new TextArea("", 5, maxStringLength, scrollBoth); - add("Center", messageText); - - pack(); - - setVisible(true); - }// TestDialog() - - //DO NOT call this directly, go through Sysout - public void printInstructions(String[] instructions) { - //Clear out any current instructions - instructionsText.setText(""); - - //Go down array of instruction strings - - String printStr, remainingStr; - for (int i = 0; i < instructions.length; i++) { - //chop up each into pieces maxSringLength long - remainingStr = instructions[ i]; - while (remainingStr.length() > 0) { - //if longer than max then chop off first max chars to print - if (remainingStr.length() >= maxStringLength) { - //Try to chop on a word boundary - int posOfSpace = remainingStr.lastIndexOf(' ', maxStringLength - 1); - - if (posOfSpace <= 0) { - posOfSpace = maxStringLength - 1; - } - - printStr = remainingStr.substring(0, posOfSpace + 1); - remainingStr = remainingStr.substring(posOfSpace + 1); - } //else just print - else { - printStr = remainingStr; - remainingStr = ""; - } - - instructionsText.append(printStr + "\n"); - - }// while - - }// for - - }//printInstructions() - - //DO NOT call this directly, go through Sysout - public void displayMessage(String messageIn) { - messageText.append(messageIn + "\n"); - System.out.println(messageIn); - } -}// Te diff --git a/test/jdk/java/awt/Cursor/NullCursorTest/NullCursorTest.java b/test/jdk/java/awt/Cursor/NullCursorTest/NullCursorTest.java new file mode 100644 index 0000000000000..c2398a80eb2b2 --- /dev/null +++ b/test/jdk/java/awt/Cursor/NullCursorTest/NullCursorTest.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4111379 + * @summary Test for setting cursor to null for lightweight components + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual NullCursorTest + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Cursor; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +public class NullCursorTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Hover over each colored area as described: + Green area shows a CrossCursor. + Red area shows a TextCursor. + Yellow area shows a HandCursor. + 2. Click once in red area, then: + Green area shows a HandCursor. + Red area shows a BusyCursor. + Yellow area shows a HandCursor. + 3. Click again in red area, then: + Green area shows a CrossCursor. + Red area shows a HandCursor. + Yellow area shows a HandCursor. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(NullCursorTest::createUI) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame f = new Frame("Null Cursor Test Frame"); + f.setSize(200, 200); + final Container p = f; + p.setName("parent"); + p.setLayout(null); + + final Component green = p.add(new Component() { + public void paint(Graphics g) { + Rectangle r = getBounds(); + g.setColor(Color.green); + g.fillRect(0, 0, r.width, r.height); + } + }); + green.setName("green"); + green.setBackground(Color.red); + green.setBounds(50, 50, 75, 75); + green.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); + + Container h = new Container() { + public void paint(Graphics g) { + Rectangle r = getBounds(); + g.setColor(Color.yellow); + g.fillRect(0, 0, r.width, r.height); + super.paint(g); + } + }; + h.setBounds(15, 25, 150, 150); + h.setName("container"); + h.setBackground(Color.yellow); + h.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + final Component red = new Component() { + public void paint(Graphics g) { + Rectangle r = getBounds(); + Color c = getBackground(); + g.setColor(c); + g.fillRect(0, 0, r.width, r.height); + super.paint(g); + } + }; + red.setName("red"); + red.setBackground(Color.red); + red.setBounds(10, 10, 120, 120); + red.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); + + final Button b = (Button)h.add(new Button("Test")); + b.setBounds(10, 10, 40, 20); + h.add(red); + p.add(h); + + b.addActionListener(new ActionListener() { + boolean f = false; + public void actionPerformed(ActionEvent e) { + if (f) { + b.setCursor(null); + } else { + b.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + } + f = !f; + } + }); + red.addMouseListener(new MouseAdapter() { + boolean f = true; + + public void mouseClicked(MouseEvent e) { + Component c = (Component)e.getSource(); + if (f) { + c.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + p.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); + green.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + f = false; + } else { + c.setCursor(null); + p.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + green.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); + f = true; + } + } + }); + return f; + } +} diff --git a/test/jdk/java/awt/Cursor/SetCursorTest/SetCursorTest.java b/test/jdk/java/awt/Cursor/SetCursorTest/SetCursorTest.java new file mode 100644 index 0000000000000..87f028eb4f617 --- /dev/null +++ b/test/jdk/java/awt/Cursor/SetCursorTest/SetCursorTest.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4160080 + * @summary Test setCursor() on lightweight components when event is generated + * by a button + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual SetCursorTest + */ + +import java.awt.Cursor; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; + + +public class SetCursorTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This test checks for the behavior of setCursor() when called in + a JFrame's JButton action event. + + 1. Click the "OK" button in the test window. + 2. Verify that the cursor changes to the waiting cursor instead + of the default system cursor. + + If true, then pass the test. Otherwise, fail this test. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(SetCursorTest::createUI) + .build() + .awaitAndCheck(); + } + + public static myFrame createUI() { + myFrame f = new myFrame(); + return f; + } +} + +class myFrame extends JFrame { + public myFrame() { + super("SetCursor With Button Test"); + setSize(200, 200); + + final JPanel p = new JPanel(); + final JButton okButton = new JButton("OK"); + okButton.addActionListener(e -> + setCursor(new Cursor(Cursor.WAIT_CURSOR))); + + p.add(okButton); + getContentPane().add(p); + } +} diff --git a/test/jdk/java/awt/Debug/DumpOnKey/DumpOnKey.java b/test/jdk/java/awt/Debug/DumpOnKey/DumpOnKey.java index 5dd99d9e6ea7d..67b88b0d9550d 100644 --- a/test/jdk/java/awt/Debug/DumpOnKey/DumpOnKey.java +++ b/test/jdk/java/awt/Debug/DumpOnKey/DumpOnKey.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,6 +22,7 @@ */ import java.awt.AWTException; +import java.awt.EventQueue; import java.awt.Frame; import java.awt.Robot; import java.awt.Window; @@ -58,9 +59,11 @@ public void list(final PrintStream out, final int indent) { w.setSize(200, 200); w.setLocationRelativeTo(null); w.setVisible(true); + w.toFront(); + w.requestFocus(); final Robot robot = new Robot(); - robot.setAutoDelay(50); + robot.setAutoDelay(100); robot.setAutoWaitForIdle(true); robot.mouseMove(w.getX() + w.getWidth() / 2, w.getY() + w.getHeight() / 2); @@ -74,7 +77,14 @@ public void list(final PrintStream out, final int indent) { robot.keyRelease(KeyEvent.VK_SHIFT); robot.keyRelease(KeyEvent.VK_CONTROL); - w.dispose(); + try { + EventQueue.invokeAndWait(() -> { + w.dispose(); + }); + } catch (Exception e) {} + + robot.delay(2000); + if (dumped != dump) { throw new RuntimeException("Exp:" + dump + ", actual:" + dumped); } diff --git a/test/jdk/java/awt/Desktop/ActionSupportTest.java b/test/jdk/java/awt/Desktop/ActionSupportTest.java new file mode 100644 index 0000000000000..e5cdec0e42246 --- /dev/null +++ b/test/jdk/java/awt/Desktop/ActionSupportTest.java @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6255196 + * @key headful + * @summary Verifies the supported actions on different platforms. + * @library /test/lib + * @run main/othervm ActionSupportTest + */ + +import java.awt.Desktop; +import java.io.File; +import java.net.URI; +import javax.swing.JMenuBar; +import jtreg.SkippedException; + +import static java.awt.desktop.QuitStrategy.NORMAL_EXIT; + +public class ActionSupportTest { + + public static void main(String[] args) { + final File file = new File("nonExistentFile"); + final URI uri = URI.create("nonExistentSchema:anything"); + final StringBuilder error = new StringBuilder(); + + if (!Desktop.isDesktopSupported()) { + throw new SkippedException("Class java.awt.Desktop is not supported on " + + "current platform. Farther testing will not be performed"); + } + + Desktop desktop = Desktop.getDesktop(); + for (Desktop.Action action : Desktop.Action.values()) { + boolean supported = desktop.isSupported(action); + + try { + switch (action) { + case OPEN: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.open(file); + break; + case EDIT: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.edit(file); + break; + case PRINT: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.print(file); + break; + case MAIL: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.mail(uri); + break; + case BROWSE: + if (supported) { + continue; // prevent native message about strange schema + } + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.browse(uri); + break; + case APP_EVENT_FOREGROUND: + case APP_EVENT_HIDDEN: + case APP_EVENT_REOPENED: + case APP_EVENT_SCREEN_SLEEP: + case APP_EVENT_SYSTEM_SLEEP: + case APP_EVENT_USER_SESSION: + continue; // Has no effect if SystemEventListener's sub-type + // is unsupported on the current platform. + case APP_ABOUT: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.setAboutHandler(e -> { + }); + break; + case APP_PREFERENCES: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.setPreferencesHandler(e -> { + }); + break; + case APP_OPEN_FILE: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.setOpenFileHandler(e -> { + }); + break; + case APP_PRINT_FILE: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.setPrintFileHandler(e -> { + }); + break; + case APP_OPEN_URI: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.setOpenURIHandler(e -> { + }); + break; + case APP_QUIT_HANDLER: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.setQuitHandler((e, response) -> { + }); + break; + case APP_QUIT_STRATEGY: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.setQuitStrategy(NORMAL_EXIT); + break; + case APP_SUDDEN_TERMINATION: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.enableSuddenTermination(); + break; + case APP_REQUEST_FOREGROUND: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.requestForeground(true); + break; + case APP_HELP_VIEWER: + if (supported) { + continue; // prevent open separate window + } + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.openHelpViewer(); + break; + case APP_MENU_BAR: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.setDefaultMenuBar(new JMenuBar()); + break; + case BROWSE_FILE_DIR: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.browseFileDirectory(file); + break; + case MOVE_TO_TRASH: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.moveToTrash(file); + break; + } + // no exception has been thrown. + if (!supported) { + error.append("Action " + action.name() + " is an " + + "unsupported operation, but no exception has been thrown\n"); + } + } catch (UnsupportedOperationException uoe) { + if (!supported) { + System.out.println("Action " + action.name() + "is not supported."); + } else { + error.append("Action " + action.name() + " is a " + + "supported operation, " + + "but UnsupportedOperationException has been thrown\n"); + } + } catch (Exception e) { + if (supported) { + System.out.println("Action " + action.name() + "supported."); + } else { + error.append("Action " + action.name() + " is an " + + "unsupported operation, but " + + "UnsupportedOperationException has not been thrown\n"); + } + } + } + + if (!error.isEmpty()) { + System.err.println(error); + throw new RuntimeException("One or more tests failed. " + + "Look at the error output for details"); + } + System.out.println("Test completed"); + } +} diff --git a/test/jdk/java/awt/Desktop/BrowseTest.java b/test/jdk/java/awt/Desktop/BrowseTest.java new file mode 100644 index 0000000000000..33de1ecdca74c --- /dev/null +++ b/test/jdk/java/awt/Desktop/BrowseTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6255196 + * @summary Verifies the function of method browse(java.net.URI uri). + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame jtreg.SkippedException + * @run main/manual BrowseTest + */ + +import java.awt.Desktop; +import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.net.URI; +import javax.swing.JPanel; + +import jtreg.SkippedException; + +public class BrowseTest extends JPanel { + static final String INSTRUCTIONS = """ + This test could launch default file manager to open user's home + directory, and default web browser to show the URL of java vendor. + After test execution close the native file manager and web browser + windows if they were launched by test. + Also check output for any unexpected EXCEPTIONS, + if you see any failure messages press Fail otherwise press Pass. + """; + + public BrowseTest() { + Desktop desktop = Desktop.getDesktop(); + + URI dirURI = new File(System.getProperty("user.home")).toURI(); + URI webURI = URI.create(System.getProperty("java.vendor.url", "http://www.java.com")); + boolean failed = false; + try { + PassFailJFrame.log("Try to browse " + dirURI + " ..."); + desktop.browse(dirURI); + PassFailJFrame.log("Succeed.\n"); + } catch (Exception e) { + PassFailJFrame.log("EXCEPTION: " + e.getMessage()); + } + + try { + PassFailJFrame.log("Try to browse " + webURI + " ..."); + desktop.browse(webURI); + PassFailJFrame.log("Succeed.\n"); + } catch (Exception e) { + PassFailJFrame.log("EXCEPTION: " + e.getMessage()); + } + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + if (!Desktop.isDesktopSupported()) { + throw new SkippedException("Class java.awt.Desktop is not supported " + + "on current platform. Further testing will not be performed"); + } + + PassFailJFrame.builder() + .title("Browser Test") + .splitUI(BrowseTest::new) + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 1) + .columns(40) + .logArea() + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/Desktop/DesktopEventsExceptions/DesktopEventsExceptions.java b/test/jdk/java/awt/Desktop/DesktopEventsExceptions/DesktopEventsExceptions.java index 2a1b88d7d7336..83a35b71a120f 100644 --- a/test/jdk/java/awt/Desktop/DesktopEventsExceptions/DesktopEventsExceptions.java +++ b/test/jdk/java/awt/Desktop/DesktopEventsExceptions/DesktopEventsExceptions.java @@ -45,7 +45,7 @@ * @test * @bug 8203224 * @summary tests that the correct exceptions are thrown by the events classes - * in {code java.awt.desktop} package + * in {@code java.awt.desktop} package * @run main/othervm DesktopEventsExceptions * @run main/othervm -Djava.awt.headless=true DesktopEventsExceptions */ diff --git a/test/jdk/java/awt/Desktop/DesktopSupportTest.java b/test/jdk/java/awt/Desktop/DesktopSupportTest.java new file mode 100644 index 0000000000000..ec8b82ba5ef21 --- /dev/null +++ b/test/jdk/java/awt/Desktop/DesktopSupportTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6255196 + * @key headful + * @summary Verifies if class Desktop is supported on current platform. + * @run main DesktopSupportTest + */ + +import java.awt.Desktop; + +public class DesktopSupportTest { + public static void main(String[] args) { + boolean supported = Desktop.isDesktopSupported(); + try { + Desktop desktop = Desktop.getDesktop(); + if (!supported) { + throw new RuntimeException("UnsupportedOperationException " + + "should be thrown, as this class is not supported " + + "on current platform."); + } + } catch (UnsupportedOperationException uoe) { + if (supported) { + throw new RuntimeException("UnsupportedOperationException " + + "should NOT be thrown, as this class is supported " + + "on current platform."); + } + } catch (Exception e) { + if (!supported) { + throw new RuntimeException("UnsupportedOperationException " + + "should be thrown, as this class is not supported " + + "on current platform. But " + e.getClass().getName() + + " has been thrown instead."); + } + } + } +} diff --git a/test/jdk/java/awt/Desktop/EditAndPrintTest/EditAndPrintTest.java b/test/jdk/java/awt/Desktop/EditAndPrintTest/EditAndPrintTest.java new file mode 100644 index 0000000000000..b2d7ef28df125 --- /dev/null +++ b/test/jdk/java/awt/Desktop/EditAndPrintTest/EditAndPrintTest.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @key printer + * @bug 6255196 + * @summary Verifies the function of methods edit(java.io.File file) and + * print(java.io.File file) + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame jtreg.SkippedException + * @run main/manual EditAndPrintTest + */ + +import java.awt.Desktop; +import java.awt.Desktop.Action; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import javax.swing.JPanel; + +import jtreg.SkippedException; + +public class EditAndPrintTest extends JPanel { + + static final String INSTRUCTIONS = """ + This test tries to edit and print a directory, which will expectedly raise IOException. + Then this test would edit and print a .txt file, which should be successful. + After test execution close the editor if it was launched by test. + If you see any EXCEPTION messages in the output press FAIL. + """; + + static Desktop desktop; + + public EditAndPrintTest() { + /* + * Part 1: print or edit a directory, which should throw an IOException. + */ + File userHome = new File(System.getProperty("user.home")); + try { + if (desktop.isSupported(Action.EDIT)) { + PassFailJFrame.log("Trying to edit " + userHome); + desktop.edit(userHome); + PassFailJFrame.log("No exception has been thrown for editing " + + "directory " + userHome.getPath()); + PassFailJFrame.log("Test failed."); + } else { + PassFailJFrame.log("Action EDIT is unsupported."); + } + } catch (IOException e) { + PassFailJFrame.log("Expected IOException is caught."); + } + + try { + if (desktop.isSupported(Action.PRINT)) { + PassFailJFrame.log("Trying to print " + userHome); + desktop.print(userHome); + PassFailJFrame.log("No exception has been thrown for printing " + + "directory " + userHome.getPath()); + PassFailJFrame.log("Test failed."); + } else { + PassFailJFrame.log("Action PRINT is unsupported.\n"); + } + } catch (IOException e) { + PassFailJFrame.log("Expected IOException is caught."); + } + + /* + * Part 2: print or edit a normal .txt file, which may succeed if there + * is associated application to print or edit the given file. It fails + * otherwise. + */ + // Create a temp .txt file for test. + String testFilePath = System.getProperty("java.io.tmpdir") + File.separator + "JDIC-test.txt"; + File testFile = null; + try { + PassFailJFrame.log("Creating temporary file."); + testFile = File.createTempFile("JDIC-test", ".txt", new File(System.getProperty("java.io.tmpdir"))); + testFile.deleteOnExit(); + FileWriter writer = new FileWriter(testFile); + writer.write("This is a temp file used to test print() method of Desktop."); + writer.flush(); + writer.close(); + } catch (IOException ioe){ + PassFailJFrame.log("EXCEPTION: " + ioe.getMessage()); + PassFailJFrame.forceFail("Failed to create temp file for testing."); + } + + try { + if (desktop.isSupported(Action.EDIT)) { + PassFailJFrame.log("Try to edit " + testFile); + desktop.edit(testFile); + PassFailJFrame.log("Succeed."); + } + } catch (IOException e) { + PassFailJFrame.log("EXCEPTION: " + e.getMessage()); + } + + try { + if (desktop.isSupported(Action.PRINT)) { + PassFailJFrame.log("Trying to print " + testFile); + desktop.print(testFile); + PassFailJFrame.log("Succeed."); + } + } catch (IOException e) { + PassFailJFrame.log("EXCEPTION: " + e.getMessage()); + } + } + + public static void main(String args[]) throws InterruptedException, + InvocationTargetException { + if (!Desktop.isDesktopSupported()) { + throw new SkippedException("Class java.awt.Desktop is not supported " + + "on current platform. Further testing will not be performed"); + } + + desktop = Desktop.getDesktop(); + if (!desktop.isSupported(Action.PRINT) && !desktop.isSupported(Action.EDIT)) { + throw new SkippedException("Neither EDIT nor PRINT actions are supported. Nothing to test."); + } + + PassFailJFrame.builder() + .title("Edit and Print test") + .splitUI(EditAndPrintTest::new) + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 1) + .columns(60) + .logArea() + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/Desktop/MailTest.java b/test/jdk/java/awt/Desktop/MailTest.java new file mode 100644 index 0000000000000..2775a671310ee --- /dev/null +++ b/test/jdk/java/awt/Desktop/MailTest.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Desktop; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.net.URI; +import javax.swing.JPanel; + +import jtreg.SkippedException; + +/* + * @test + * @bug 6255196 + * @summary Verifies the function of methods mail() and mail(java.net.URI uri). + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame jtreg.SkippedException + * @run main/manual MailTest + */ + +public class MailTest extends JPanel { + + static final String INSTRUCTIONS = """ + This test could launch the mail client to compose mail + with and without filling the message fields. + After test execution close the mail composing windows if they + were launched by test. + If you see any unexpected EXCEPTION messages in the output + press Fail. Otherwise press Pass. + """; + + private MailTest() { + Desktop desktop = Desktop.getDesktop(); + /* + * Part 1: launch the mail composing window without a mailto URI. + */ + try { + desktop.mail(); + } catch (IOException e) { + PassFailJFrame.log("EXCEPTION: " + e.getMessage()); + } + + /* + * Part 2: launch the mail composing window with a mailto URI. + */ + URI testURI = null; + try { + testURI = new URI("mailto", "foo@bar.com?subject=test subject" + + "&cc=foocc@bar.com&body=test body", null); + desktop.mail(testURI); + } catch (IOException e) { + PassFailJFrame.log("EXCEPTION: " + e.getMessage()); + } catch (java.net.URISyntaxException use) { + // Should not reach here. + PassFailJFrame.log("EXCEPTION: " + use.getMessage()); + } + + /* + * Part 3: try to launch the mail composing window with a URI with a + * scheme which is not "mailto": + * http://java.net. + * An IOException should be thrown in this case. + */ + try { + testURI = URI.create("http://java.com"); + PassFailJFrame.log("Try to mail: " + testURI); + desktop.mail(testURI); + } catch (IllegalArgumentException e) { + PassFailJFrame.log("Caught expected IllegalArgumentException"); + } catch (IOException ioe) { + PassFailJFrame.log("EXCEPTION: " + ioe.getMessage()); + } + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + if (!Desktop.isDesktopSupported()) { + throw new SkippedException("Class java.awt.Desktop is not supported " + + "on current platform. Further testing will not be performed"); + } + + if (!Desktop.getDesktop().isSupported(Desktop.Action.MAIL)) { + throw new SkippedException("Action.MAIL is not supported."); + } + + PassFailJFrame.builder() + .title("Mail Test") + .splitUI(MailTest::new) + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 1) + .columns(40) + .logArea() + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/Desktop/OpenTest.java b/test/jdk/java/awt/Desktop/OpenTest.java new file mode 100644 index 0000000000000..c5f2a6939e45b --- /dev/null +++ b/test/jdk/java/awt/Desktop/OpenTest.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6255196 + * @summary Verifies the function of method open(java.io.File file). + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame jtreg.SkippedException + * @run main/manual/othervm OpenTest + */ + +import java.awt.Desktop; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import javax.swing.JPanel; + +import jtreg.SkippedException; + +public class OpenTest extends JPanel { + + static final String INSTRUCTIONS = """ + This test could open the user's home directory and a .txt file. + After test execution, close the native application windows that + are used to open the directory and .txt file if they were launched + by the test. + If you see any unexpected EXCEPTION messages in the output press Fail. + Otherwise press Pass. + """; + + public OpenTest() { + Desktop desktop = Desktop.getDesktop(); + + /* + * Part 1: open a directory, which should launch the system default + * file explorer. + * + * On Windows platforms, the default file explorer is explorer; + * on UNIX platforms with Gnome installed and running, the default + * file explorer is Nautilus. + */ + File userHome = new File(System.getProperty("user.home")); + + try { + PassFailJFrame.log("Try to open " + userHome); + desktop.open(userHome); + PassFailJFrame.log("Succeed."); + } catch (IOException e) { + PassFailJFrame.log("EXCEPTION: " + e.getMessage()); + } + + /* + * Part 2: open a normal .txt file, which should launch the registered + * application for .txt files. + */ + // Create a temp .txt file for test. + File testFile = null; + try { + PassFailJFrame.log("Creating temporary file"); + testFile = File.createTempFile("JDIC-test", ".txt", + new File(System.getProperty("java.io.tmpdir"))); + testFile.deleteOnExit(); + } catch (IOException ioe) { + PassFailJFrame.log("EXCEPTION: " + ioe.getMessage()); + PassFailJFrame.log("Failed to create test file"); + } + + try { + PassFailJFrame.log("Try to open " + testFile); + desktop.open(testFile); + PassFailJFrame.log("Succeed."); + } catch (IOException e) { + PassFailJFrame.log("EXCEPTION: " + e.getMessage()); + } + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + if (!Desktop.isDesktopSupported()) { + throw new SkippedException("Class java.awt.Desktop is not supported " + + "on current platform. Further testing will not be performed"); + } + + PassFailJFrame.builder() + .title("Mail Test") + .splitUI(OpenTest::new) + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 1) + .columns(40) + .logArea() + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/DesktopProperties/FontSmoothing.java b/test/jdk/java/awt/DesktopProperties/FontSmoothing.java new file mode 100644 index 0000000000000..d3879d4893ff2 --- /dev/null +++ b/test/jdk/java/awt/DesktopProperties/FontSmoothing.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Frame; +import java.awt.Toolkit; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +/* + * @test + * @bug 4808569 + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary add desktop property for the Windows XP or later font smoothing settings + * @run main/manual FontSmoothing + */ + +public class FontSmoothing { + + private static final String PROP_NAME = "win.text.fontSmoothingType"; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This test should be run on Windows XP or later. + + On Windows 11: + 1. Open Run dialog by typing 'run' in search bar. + 2. Type 'cttune' and press Ok. + 3. Uncheck the "Turn On ClearType" checkbox and follow next instructions on screen. + 4. Repeat Step 1-2. + 5. Check the "Turn On ClearType" checkbox and follow next instructions on screen. + 6. Take a look at the output window to determine if the test passed or failed. + """; + + PassFailJFrame.builder() + .title("FontSmoothing Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testTimeOut(5) + .testUI(FontSmoothing::createUI) + .logArea(8) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame f = new Frame("FontSmoothing Test"); + f.setSize(50, 50); + + Object value = Toolkit.getDefaultToolkit().getDesktopProperty(PROP_NAME); + PassFailJFrame.log("toolkit.getDesktopProperty: " + PROP_NAME + " = " + value + "\n"); + + Toolkit.getDefaultToolkit().addPropertyChangeListener(PROP_NAME, new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent e) { + PassFailJFrame.log("PropertyChangeEvent: " + e.getPropertyName() + + "\n old value=" + e.getOldValue() + + "\n new value=" + e.getNewValue()); + + Integer value = (Integer) Toolkit.getDefaultToolkit().getDesktopProperty(PROP_NAME); + PassFailJFrame.log("toolkit.getDesktopProperty:" + PROP_NAME + "=" + value); + + if (value.equals((Integer) e.getNewValue())) { + PassFailJFrame.log("test PASSED"); + } else { + PassFailJFrame.log("test FAILED"); + } + } + }); + return f; + } +} diff --git a/test/jdk/java/awt/DesktopProperties/ThreeDBackgroundColor.java b/test/jdk/java/awt/DesktopProperties/ThreeDBackgroundColor.java new file mode 100644 index 0000000000000..26792e79a92d1 --- /dev/null +++ b/test/jdk/java/awt/DesktopProperties/ThreeDBackgroundColor.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.Frame; +import java.awt.Toolkit; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +/* + * @test + * @bug 4368193 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @requires (os.family == "windows") + * @summary Toolkit's getDesktopProperty returns stale values on Microsoft Windows + * @run main/manual ThreeDBackgroundColor + */ + +public class ThreeDBackgroundColor { + + private static final String PROP_NAME = "win.3d.backgroundColor"; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + On Windows 10: + 1. Open Windows Settings, in the search bar type + 'high contrast', in the list of suggestions choose option + 'Turn high contrast on or off' + 2. In the High contrast control panel click on the on/off switch + to initialize High contrast mode + 3. Wait for the High contrast mode to finish initialization + 4. Click on the same switch again to turn off High contrast mode + + On Windows 11: + 1. Open Windows settings, in the search bar type + 'Contrast Theme'. + 2. Select any value from 'Contrast themes' dropdown menu and press 'Apply'. + 3. Wait for the High contrast mode to finish initialization + 4. Select 'None' from 'Contrast themes' dropdown menu to revert the changes. + + Take a look at the output window to determine if the test passed or failed."""; + + PassFailJFrame.builder() + .title("ThreeDBackgroundColor Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testTimeOut(5) + .testUI(ThreeDBackgroundColor::createUI) + .logArea(8) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame f = new Frame("ThreeDBackgroundColor Test"); + f.setSize(50, 50); + + Object value = Toolkit.getDefaultToolkit().getDesktopProperty(PROP_NAME); + PassFailJFrame.log("toolkit.getDesktopProperty:" + PROP_NAME + "=" + value); + + Toolkit.getDefaultToolkit().addPropertyChangeListener(PROP_NAME, new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent e) { + PassFailJFrame.log("PropertyChangeEvent: " + e.getPropertyName() + + "\n old value=" + e.getOldValue() + + "\n new value=" + e.getNewValue()); + + Color value = (Color) Toolkit.getDefaultToolkit().getDesktopProperty(PROP_NAME); + PassFailJFrame.log("toolkit.getDesktopProperty:" + PROP_NAME + "=" + value); + if (value.equals((Color) e.getNewValue())) { + PassFailJFrame.log("test PASSED"); + } else { + PassFailJFrame.log("test FAILED"); + } + } + }); + return f; + } +} diff --git a/test/jdk/java/awt/Dialog/ChoiceModalDialogTest.java b/test/jdk/java/awt/Dialog/ChoiceModalDialogTest.java new file mode 100644 index 0000000000000..97ce5a83a96b7 --- /dev/null +++ b/test/jdk/java/awt/Dialog/ChoiceModalDialogTest.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6213128 + * @key headful + * @summary Tests that choice is releasing input capture when a modal + * dialog is shown + * @run main ChoiceModalDialogTest + */ + +import java.awt.Choice; +import java.awt.Dialog; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Robot; +import java.awt.TextField; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +public class ChoiceModalDialogTest { + static Frame f; + static Dialog d; + static volatile boolean keyOK; + static volatile boolean mouseOK; + static TextField tf; + static Choice c; + + public static void main(String[] args) throws Exception { + Robot r; + try { + r = new Robot(); + r.setAutoDelay(100); + EventQueue.invokeAndWait(() -> { + f = new Frame("Frame"); + c = new Choice(); + f.setBounds(100, 300, 300, 200); + f.setLayout(new FlowLayout()); + tf = new TextField(3); + f.add(tf); + + c.add("1"); + c.add("2"); + c.add("3"); + c.add("4"); + f.add(c); + + tf.addFocusListener(new FocusAdapter() { + public void focusLost(FocusEvent ev) { + d = new Dialog(f, "Dialog", true); + d.setBounds(300, 300, 200, 150); + d.addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent ev) { + keyOK = true; + } + }); + d.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent ev) { + mouseOK = true; + } + }); + d.setVisible(true); + } + }); + + f.setVisible(true); + f.toFront(); + }); + r.waitForIdle(); + r.delay(1000); + EventQueue.invokeAndWait(() -> { + r.mouseMove(tf.getLocationOnScreen().x + tf.getSize().width / 2, + tf.getLocationOnScreen().y + tf.getSize().height / 2); + }); + r.waitForIdle(); + r.delay(500); + EventQueue.invokeAndWait(() -> { + r.mouseMove(c.getLocationOnScreen().x + c.getSize().width - 4, + c.getLocationOnScreen().y + c.getSize().height / 2); + r.mousePress(InputEvent.BUTTON1_DOWN_MASK); + r.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + }); + r.waitForIdle(); + r.delay(500); + EventQueue.invokeAndWait(() -> { + r.mouseMove(d.getLocationOnScreen().x + d.getSize().width / 2, + d.getLocationOnScreen().y + d.getSize().height / 2); + r.mousePress(InputEvent.BUTTON1_DOWN_MASK); + r.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + r.keyPress(KeyEvent.VK_A); + r.keyRelease(KeyEvent.VK_A); + }); + r.waitForIdle(); + r.delay(500); + if (!mouseOK) { + throw new RuntimeException("Test Failed due to Mouse release failure!"); + } + if (!keyOK) { + throw new RuntimeException("Test Failed due to Key release failure!"); + } + System.out.println("Test Passed!"); + } finally { + EventQueue.invokeAndWait(() -> { + if (d != null) { + d.dispose(); + } + if (f != null) { + f.dispose(); + } + }); + } + } +} diff --git a/test/jdk/java/awt/Dialog/DialogBackgroundTest.java b/test/jdk/java/awt/Dialog/DialogBackgroundTest.java new file mode 100644 index 0000000000000..793782fc43be8 --- /dev/null +++ b/test/jdk/java/awt/Dialog/DialogBackgroundTest.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4255230 4191946 + * @summary Tests to verify Dialog inherits background from its owner + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DialogBackgroundTest + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Dialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.TextField; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +public class DialogBackgroundTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Perform the following steps: + 1) Select "New Frame" from the "File" menu of the + "TreeCopy Frame #1" frame. + 2) Select "Configure" from the "File" menu in the + *new* frame. + If label text "This is a label:" in the appeared + "Configuration Dialog" dialog has a grey background + test PASSES, otherwise it FAILS + """; + TreeCopy treeCopy = new TreeCopy(++TreeCopy.windowCount, null); + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(treeCopy) + .logArea(8) + .build() + .awaitAndCheck(); + } +} + +class TreeCopy extends Frame implements ActionListener { + TextField tfRoot; + ConfigDialog configDlg; + MenuItem miConfigure = new MenuItem("Configure..."); + MenuItem miNewWindow = new MenuItem("New Frame"); + static int windowCount = 0; + Window parent; + + public TreeCopy(int windowNum, Window myParent) { + super(); + setTitle("TreeCopy Frame #" + windowNum); + MenuBar mb = new MenuBar(); + Menu m = new Menu("File"); + configDlg = new ConfigDialog(this); + parent = myParent; + + m.add(miConfigure); + m.add(miNewWindow); + miConfigure.addActionListener(this); + miNewWindow.addActionListener(this); + mb.add(m); + setMenuBar(mb); + m.addActionListener(this); + + tfRoot = new TextField(); + tfRoot.setEditable(false); + add(tfRoot); + + addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent we) { + dispose(); + } + }); + + setSize(200, 100); + setLocationRelativeTo(parent); + } + + public void actionPerformed(ActionEvent ae) { + Object source = ae.getSource(); + + if (source == miConfigure) { + configDlg.setVisible(true); + if (configDlg.getBackground() != configDlg.labelColor) + PassFailJFrame.log("FAIL: Test failed!!!"); + } else if (source == miNewWindow) { + new TreeCopy(++windowCount, this).setVisible(true); + } + } +} + +class ConfigDialog extends Dialog implements ActionListener { + public Button okButton; + public Button cancelButton; + public Label l2; + public Color labelColor; + + public ConfigDialog(Frame parent) { + super(parent, "Configuration Dialog"); + okButton = new Button("OK"); + cancelButton = new Button("Cancel"); + l2 = new Label("This is a label:"); + + setLayout(new FlowLayout()); + add(l2); + add(okButton); + add(cancelButton); + + okButton.addActionListener(this); + cancelButton.addActionListener(this); + + pack(); + labelColor = l2.getBackground(); + } + + public void actionPerformed(ActionEvent ae) { + dispose(); + } +} diff --git a/test/jdk/java/awt/Dialog/DialogIconTest/DialogIconTest.java b/test/jdk/java/awt/Dialog/DialogIconTest/DialogIconTest.java new file mode 100644 index 0000000000000..06debe28fc551 --- /dev/null +++ b/test/jdk/java/awt/Dialog/DialogIconTest/DialogIconTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.Image; +import java.awt.Label; +import java.awt.MediaTracker; +import java.awt.Toolkit; +import java.awt.Window; +import java.util.List; + +/* + * @test + * @bug 4779641 + * @summary Test to verify that Non-resizable dialogs should not show icons + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DialogIconTest + */ + +public class DialogIconTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. This is a Windows-only test of Dialog icons + 2. You can see a frame with a swing icon and two dialogs that it + owns. The resizable dialog should have the same icon as the + frame. The non-resizable dialog should have no icon at all + 3. Press PASS if this is true, press FAIL otherwise + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .build() + .awaitAndCheck(); + } + + public static List initialize() { + Frame f = new Frame("Parent frame"); + f.setBounds(50, 50, 200, 200); + + Dialog dr = new Dialog(f, "Resizable Dialog"); + dr.setLocation(100, 100); + dr.add(new Label("Should inherit icon from parent")); + dr.pack(); + + Dialog dn = new Dialog(f, "NON Resizable Dialog"); + dn.setLocation(150, 150); + dn.add(new Label("Should have no icon")); + dn.pack(); + dn.setResizable(false); + + String fileName = System.getProperty("test.src") + + System.getProperty("file.separator") + "swing.small.gif"; + + Image icon = Toolkit.getDefaultToolkit().createImage(fileName); + MediaTracker tracker = new MediaTracker(f); + tracker.addImage(icon, 0); + try { + tracker.waitForAll(); + } catch (InterruptedException ie) { + throw new RuntimeException("MediaTracker addImage Interrupted!"); + } + f.setIconImage(icon); + return List.of(f, dn, dr); + } +} diff --git a/test/jdk/java/awt/Dialog/DialogIconTest/swing.small.gif b/test/jdk/java/awt/Dialog/DialogIconTest/swing.small.gif new file mode 100644 index 0000000000000..14a489ff4e7df Binary files /dev/null and b/test/jdk/java/awt/Dialog/DialogIconTest/swing.small.gif differ diff --git a/test/jdk/java/awt/Dialog/DialogModalityTest.java b/test/jdk/java/awt/Dialog/DialogModalityTest.java new file mode 100644 index 0000000000000..d8ac9e4620b42 --- /dev/null +++ b/test/jdk/java/awt/Dialog/DialogModalityTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Component; +import java.awt.Dialog; +import java.awt.Event; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.Window; +import java.util.List; + +/* + * @test + * @bug 4058370 + * @summary Test to verify Modality of Dialog + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DialogModalityTest + */ + +public class DialogModalityTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. When the test is running, there will be a Frame, a Modal Dialog + and a Window that is Modal Dialog's parent. + 2. Verify that it is impossible to bring up the menu in Frame before + closing the Modal Dialog. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .build() + .awaitAndCheck(); + } + + public static List initialize() { + Frame f = new Frame("Parent Frame"); + DialogTest dlg = new DialogTest(f, "Modal Dialog"); + f.add(new Button("push me")); + f.setSize(200, 200); + f.setLocation(210, 1); + dlg.setBounds(210, 203, 200, 200); + return List.of(f, dlg); + } +} + +class DialogTest extends Dialog { + Button closeButton; + Frame parent; + + public DialogTest(Frame parent, String title) { + this(parent, title, true); + } + + public DialogTest(Frame parent, String title, boolean modal) { + super(parent, title, modal); + this.parent = parent; + setLayout(new BorderLayout()); + Panel buttonPanel = new Panel(); + closeButton = new Button("Close"); + buttonPanel.add(closeButton); + add("Center", buttonPanel); + pack(); + } + + public boolean action(Event e, Object arg) { + if (e.target == closeButton) { + Dialog dialog = null; + Component c = (Component) e.target; + + while (c != null && !(c instanceof Dialog)) { + c = c.getParent(); + } + + if (c != null) { + dialog = (Dialog) c; + } + + if (dialog == null) { + return false; + } + + dialog.setVisible(false); + dialog.dispose(); + return true; + } + return false; + } +} diff --git a/test/jdk/java/awt/Dialog/DialogResizeTest.java b/test/jdk/java/awt/Dialog/DialogResizeTest.java new file mode 100644 index 0000000000000..e57a529806872 --- /dev/null +++ b/test/jdk/java/awt/Dialog/DialogResizeTest.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Checkbox; +import java.awt.Dialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.TextArea; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.lang.Exception; +import java.lang.String; +import java.lang.System; + +/* + * @test + * @bug 4115213 + * @summary Test to verify Checks that with resizable set to false, + * dialog can not be resized + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DialogResizeTest + */ + +public class DialogResizeTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. When this test is run a dialog will display (setResizable Test) + Click on the checkbox to change the dialog resizable state + 2. For both dialog resizable states (resizable, non-resizable) try to + change the size of the dialog. When isResizable is true the dialog + is resizable. When isResizable is false the dialog is non-resizable + 3. If this is the behavior that you observe, the test has passed, Press + the Pass button. Otherwise the test has failed, Press the Fail button + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testUI(initialize()) + .logArea(8) + .build() + .awaitAndCheck(); + } + + public static Dialog initialize() { + Frame f = new Frame("Owner Frame"); + MyDialog ld = new MyDialog(f); + ld.setBounds(100, 100, 400, 150); + ld.setResizable(false); + System.out.println("isResizable is set to: " + ld.isResizable()); + return ld; + } +} + +class MyDialog extends Dialog implements ItemListener { + String sText = "Tests java.awt.Dialog.setResizable method"; + TextArea ta = new TextArea(sText, 2, 40, TextArea.SCROLLBARS_NONE); + + public MyDialog(Frame f) { + + super(f, "setResizable test", false); + + Panel cbPanel = new Panel(); + cbPanel.setLayout(new FlowLayout()); + + Panel taPanel = new Panel(); + taPanel.setLayout(new FlowLayout()); + taPanel.add(ta); + + Checkbox cb = new Checkbox("Check this box to change the dialog's " + + "resizable state", null, isResizable()); + cb.setState(false); + cb.addItemListener(this); + cbPanel.add(cb); + + add("North", taPanel); + add("South", cbPanel); + pack(); + } + + public void itemStateChanged(ItemEvent evt) { + setResizable(evt.getStateChange() == ItemEvent.SELECTED); + + boolean bResizeState = isResizable(); + PassFailJFrame.log("isResizable is set to: " + bResizeState); + + if (isResizable()) { + ta.setText("dialog is resizable (isResizable = " + bResizeState + ")"); + } else { + ta.setText("dialog is NOT resizable (isResizable = " + bResizeState + ")"); + } + } +} diff --git a/test/jdk/java/awt/Dialog/DialogResizeTest2.java b/test/jdk/java/awt/Dialog/DialogResizeTest2.java new file mode 100644 index 0000000000000..3124719637a1d --- /dev/null +++ b/test/jdk/java/awt/Dialog/DialogResizeTest2.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.GridLayout; + +/* + * @test + * @bug 4172302 + * @summary Test to make sure non-resizable Dialogs can be resized with the + * setSize() method. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DialogResizeTest2 + */ + +public class DialogResizeTest2 { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This tests the programmatic resizability of non-resizable Dialogs + Even when a Dialog is set to be non-resizable, it should be + programmatically resizable using the setSize() method. + + 1. Initially the Dialog will be resizable. Try using the \\"Smaller\\" + and \\"Larger\\" buttons to verify that the Dialog resizes correctly + 2. Then, click the \\"Toggle\\" button to make the Dialog non-resizable + 3. Again, verify that clicking the \\"Larger\\" and \\"Smaller\\" buttons + causes the Dialog to get larger and smaller. If the Dialog does + not change size, or does not re-layout correctly, the test FAILS + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .logArea(8) + .build() + .awaitAndCheck(); + } + + public static Frame initialize() { + Frame frame = new Frame("Parent Frame"); + frame.add(new Button("Button")); + frame.setSize(100, 100); + new dlg(frame).setVisible(true); + return frame; + } + + static class dlg extends Dialog { + public dlg(Frame f_) { + super(f_, "Dialog", false); + setSize(200, 200); + Button bLarger = new Button("Larger"); + bLarger.addActionListener(e -> setSize(400, 400)); + Button bSmaller = new Button("Smaller"); + bSmaller.addActionListener(e -> setSize(200, 100)); + Button bCheck = new Button("Resizable?"); + bCheck.addActionListener(e -> { + if (isResizable()) { + PassFailJFrame.log("Dialog is resizable"); + } else { + PassFailJFrame.log("Dialog is not resizable"); + } + }); + Button bToggle = new Button("Toggle"); + bToggle.addActionListener(e -> { + if (isResizable()) { + setResizable(false); + PassFailJFrame.log("Dialog is now not resizable"); + } else { + setResizable(true); + PassFailJFrame.log("Dialog is now resizable"); + } + }); + setLayout(new GridLayout(1, 4)); + add(bSmaller); + add(bLarger); + add(bCheck); + add(bToggle); + } + } +} diff --git a/test/jdk/java/awt/Dialog/DialogSystemMenu/DialogSystemMenu.java b/test/jdk/java/awt/Dialog/DialogSystemMenu/DialogSystemMenu.java new file mode 100644 index 0000000000000..3f1639e90a41e --- /dev/null +++ b/test/jdk/java/awt/Dialog/DialogSystemMenu/DialogSystemMenu.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.event.WindowListener; +import java.util.List; + +/* + * @test + * @bug 4058953 4094035 + * @summary Test to verify system menu of a dialog on win32 + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DialogSystemMenu + */ + +public class DialogSystemMenu { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Check the following on the first dialog window: + Right-clicking on the title bar + should bring up a system menu. + The system menu should not allow any + of the Maximize, Minimize and + Restore actions + + 2. The second dialog should be non-resizable + and have no application icon. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .build() + .awaitAndCheck(); + } + + public static List initialize() { + Frame frame = new java.awt.Frame("Parent Frame"); + String txt = """ + This is a resizable dialog + Right-clicking on the title bar + should bring up a system menu + The system menu should not + allow any + of the Maximize, Minimize and + Restore actions + """; + String txt_non = """ + This is a non-resizable dialog + It should be really non-resizable + and have no application icon + """; + TestApp resizable = new TestApp(frame, "Test for 4058953", txt, true); + resizable.setLocation(0, 0); + + TestApp non_resizable = new TestApp(frame, "Test for 4094035", txt_non, false); + non_resizable.setLocation(320, 0); + return List.of(resizable, non_resizable); + } +} + + +class TestApp extends Dialog implements WindowListener { + public TestApp(java.awt.Frame parent, String title, String txt, boolean resize) { + super(parent, title, false); + + java.awt.TextArea ta = new java.awt.TextArea(txt); + ta.setEditable(false); + this.add(ta, "Center"); + this.addWindowListener(this); + this.setSize(300, 200); + this.setResizable(resize); + } + + + public void windowOpened(java.awt.event.WindowEvent myEvent) { + } + + public void windowClosed(java.awt.event.WindowEvent myEvent) { + } + + public void windowIconified(java.awt.event.WindowEvent myEvent) { + } + + public void windowDeiconified(java.awt.event.WindowEvent myEvent) { + } + + public void windowActivated(java.awt.event.WindowEvent myEvent) { + } + + public void windowDeactivated(java.awt.event.WindowEvent myEvent) { + } + + public void windowClosing(java.awt.event.WindowEvent myEvent) { + this.dispose(); + } +} diff --git a/test/hotspot/jtreg/runtime/LoadClass/dummy.jar b/test/jdk/java/awt/Dialog/DialogSystemMenu/icon24x24.gif similarity index 100% rename from test/hotspot/jtreg/runtime/LoadClass/dummy.jar rename to test/jdk/java/awt/Dialog/DialogSystemMenu/icon24x24.gif diff --git a/test/jdk/java/awt/Dialog/DialogSystemMenu/iconone.gif b/test/jdk/java/awt/Dialog/DialogSystemMenu/iconone.gif new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/test/jdk/java/awt/Dialog/DialogSystemMenu/icontwo.gif b/test/jdk/java/awt/Dialog/DialogSystemMenu/icontwo.gif new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/test/jdk/java/awt/Dialog/EnabledResetTest.java b/test/jdk/java/awt/Dialog/EnabledResetTest.java new file mode 100644 index 0000000000000..d71c9b1801b22 --- /dev/null +++ b/test/jdk/java/awt/Dialog/EnabledResetTest.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4232374 + * @summary Tests that dismissing a modal dialog does not enable + * disabled components + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual EnabledResetTest + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Dialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class EnabledResetTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Press "Create Child" twice to create three windows + Verify that the parent windows are disabled + 2. Press "Create Modal Dialog" + Verify that the parent windows are disabled + 3. Press "enable" + Verify that no windows accept mouse events + 4. Press "ok" + Verify that the first window is still disabled + If all the verifications are done, then test is + PASSED, else test fails. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(new ChildDialog(1, null)) + .build() + .awaitAndCheck(); + } +} + +class ChildDialog extends Frame implements ActionListener { + Window parent; + int id; + Button b, c, d; + + public ChildDialog(int frameNumber, Window myParent) { + super(); + id = frameNumber; + parent = myParent; + + setTitle("Frame Number " + id); + + b = new Button("Dismiss me"); + c = new Button("Create Child"); + d = new Button("Create Modal Dialog"); + + setLayout(new BorderLayout()); + add("North", c); + add("Center", d); + add("South", b); + pack(); + + b.addActionListener(this); + c.addActionListener(this); + d.addActionListener(this); + } + + public void setVisible(boolean b) { + if (parent != null) { + if (b) { + parent.setEnabled(false); + } else { + parent.setEnabled(true); + parent.requestFocus(); + } + } + + super.setVisible(b); + } + + public void dispose() { + if (parent != null) { + parent.setEnabled(true); + parent.requestFocus(); + } + super.dispose(); + } + + + public void actionPerformed(ActionEvent evt) { + if (evt.getSource() == c) { + (new ChildDialog(id + 1, this)).setVisible(true); + } else if (evt.getSource() == d) { + Dialog D = new Dialog(this, "Modal Dialog "); + D.setLayout(new FlowLayout()); + Button b = new Button("ok"); + Button e = new Button("enable"); + D.add(b); + D.add(e); + D.setModal(true); + D.pack(); + b.addActionListener(this); + e.addActionListener(this); + D.setVisible(true); + } else if (evt.getSource() == b) { + dispose(); + } else if (evt.getSource() instanceof Button) { + if ("ok".equals(evt.getActionCommand())) { + Button target = (Button) evt.getSource(); + Window w = (Window) target.getParent(); + w.dispose(); + } + if ("enable".equals(evt.getActionCommand())) { + parent.setEnabled(true); + } + } + } +} diff --git a/test/jdk/java/awt/Dialog/FileDialogFilterTest.java b/test/jdk/java/awt/Dialog/FileDialogFilterTest.java new file mode 100644 index 0000000000000..e30d8ea58a2d6 --- /dev/null +++ b/test/jdk/java/awt/Dialog/FileDialogFilterTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.FileDialog; +import java.awt.Frame; +import java.io.File; +import java.io.FilenameFilter; + +/* + * @test + * @bug 4364256 + * @summary Test to File Dialog filter + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FileDialogFilterTest + */ + +public class FileDialogFilterTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Run the test, make sure a file dialog + comes up with no crash. If the file dialog + comes up successfully then press PASS, else FAIL. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .build() + .awaitAndCheck(); + } + + public static FileDialog initialize() { + FileDialog fDlg = new FileDialog(new Frame()); + fDlg.addNotify(); + fDlg.setFilenameFilter(new MyFilter()); + return fDlg; + } +} + +class MyFilter implements FilenameFilter { + public boolean accept(File dir, String name) { + return true; + } +} diff --git a/test/jdk/java/awt/Dialog/FileDialogGetFileTest.java b/test/jdk/java/awt/Dialog/FileDialogGetFileTest.java new file mode 100644 index 0000000000000..d4670cceb601e --- /dev/null +++ b/test/jdk/java/awt/Dialog/FileDialogGetFileTest.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4414105 + * @summary Tests that FileDialog returns null when cancelled + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FileDialogGetFileTest + */ + +import java.awt.Button; +import java.awt.FileDialog; +import java.awt.Frame; + +public class FileDialogGetFileTest { + static FileDialog fd; + static Frame frame; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Open FileDialog from "Show File Dialog" button. + 2. Click cancel button without selecting any file/folder. + 3. If FileDialog.getFile return null then test PASSES, + else test FAILS automatically. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .logArea(4) + .build() + .awaitAndCheck(); + } + + public static Frame initialize() { + frame = new Frame("FileDialog GetFile test"); + fd = new FileDialog(frame); + fd.setFile("FileDialogGetFileTest.html"); + fd.setBounds(100, 100, 400, 400); + Button showBtn = new Button("Show File Dialog"); + frame.add(showBtn); + frame.pack(); + showBtn.addActionListener(e -> { + fd.setVisible(true); + if (fd.getFile() != null) { + PassFailJFrame.forceFail("Test failed: FileDialog returned non-null value"); + } else { + PassFailJFrame.log("Test Passed!"); + } + }); + return frame; + } +} diff --git a/test/jdk/java/awt/Dialog/FileDialogIconTest/FileDialogIconTest.java b/test/jdk/java/awt/Dialog/FileDialogIconTest/FileDialogIconTest.java new file mode 100644 index 0000000000000..e8505681a82a5 --- /dev/null +++ b/test/jdk/java/awt/Dialog/FileDialogIconTest/FileDialogIconTest.java @@ -0,0 +1,258 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.FileDialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Image; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/* + * @test + * @bug 4035189 + * @summary Test to verify that PIT File Dialog icon not matching with + * the new java icon (frame Icon) - PIT build + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FileDialogIconTest + */ + +public class FileDialogIconTest { + public static Frame frame; + public static Image image; + public static List images; + static String fileBase; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Select the Image for a Dialog and Frame using either + Load/Save/Just Dialog. + 2. Set the Icon Image/s to Frame and Dialog. Verify that the + Icon is set for the respective Frame and Dialog. + If selected Icon is set to Frame and Dialog press PASS + else FAIL. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .logArea(8) + .build() + .awaitAndCheck(); + } + + public static void setImagesToFD(java.util.List listIcon) { + FileDialogIconTest.images = listIcon; + } + + public static void setImagesToFrame(java.util.List listIcon) { + frame.setIconImages(listIcon); + } + + public static void setImageToFD(Image img) { + FileDialogIconTest.image = img; + } + + public static void setImageToFrame(Image img) { + frame.setIconImage(img); + } + + public static Frame initialize() { + frame = new Frame("FileDialogIconTest"); + Button setImageButton1 = new Button("setIconImageToFrame"); + Button setImageButton2 = new Button("setIconImageToDialog"); + Button setImageButton3 = new Button("setIconImagesToFrame"); + Button setImageButton4 = new Button("setIconImagesToDialog"); + Button setImageButton5 = new Button("setIconBufferedImagesToDialog"); + Button setImageButton6 = new Button("setIconBufferedImagesToFrame"); + + if (System.getProperty("test.src") == null) { + fileBase = ""; + } else { + fileBase = System.getProperty("test.src") + System.getProperty("file.separator"); + } + + final String fileName = fileBase + "loading-msg.gif"; + + setImageButton1.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent event) { + try { + Image image = Toolkit.getDefaultToolkit().getImage(fileName); + setImageToFrame(image); + PassFailJFrame.log("Loaded image . setting to frame"); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + + setImageButton2.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent event) { + try { + Image image = Toolkit.getDefaultToolkit().getImage(fileName); + setImageToFD(image); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + + setImageButton3.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent event) { + try { + Image image; + java.util.List list = new java.util.ArrayList(); + for (int i = 1; i <= 4; i++) { + String fileName = fileBase + "T" + i + ".gif"; + image = Toolkit.getDefaultToolkit().getImage(fileName); + PassFailJFrame.log("Loaded image " + fileName + ". setting to the list for frame"); + list.add(image); + } + setImagesToFrame(list); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + + + setImageButton4.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent event) { + try { + Image image; + List list = new ArrayList<>(); + for (int i = 1; i <= 4; i++) { + String fileName = fileBase + "T" + i + ".gif"; + image = Toolkit.getDefaultToolkit().getImage(fileName); + PassFailJFrame.log("Loaded image " + fileName + ". setting to the list for dialog"); + list.add(image); + } + setImagesToFD(list); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + + + setImageButton5.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent event) { + List list = new ArrayList<>(); + try { + Robot robot = new Robot(); + Rectangle rectangle; + for (int i = 1; i <= 4; i++) { + rectangle = new Rectangle(i * 10, i * 10, i * 10 + 40, i * 10 + 40); + java.awt.image.BufferedImage image = robot.createScreenCapture(rectangle); + robot.delay(100); + list.add(image); + } + } catch (Throwable t) { + t.printStackTrace(); + } + PassFailJFrame.log("Captured images and set to the list for dialog"); + } + }); + + setImageButton6.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent event) { + List list = new ArrayList<>(); + try { + Robot robot = new Robot(); + Rectangle rectangle; + for (int i = 1; i <= 4; i++) { + rectangle = new Rectangle(i * 10, i * 10, i * 10 + 40, i * 10 + 40); + java.awt.image.BufferedImage image = robot.createScreenCapture(rectangle); + robot.delay(100); + list.add(image); + } + } catch (Throwable t) { + t.printStackTrace(); + } + PassFailJFrame.log("Captured images and set to the list for frame"); + } + }); + + Button buttonLoad = new Button("Load Dialog"); + Button buttonSave = new Button("Save Dialog"); + Button buttonSimple = new Button("Just Dialog"); + buttonLoad.addActionListener(new MyActionListener(FileDialog.LOAD, "LOAD")); + buttonSave.addActionListener(new MyActionListener(FileDialog.SAVE, "SAVE")); + buttonSimple.addActionListener(new MyActionListener(-1, "")); + + frame.setSize(400, 400); + frame.setLayout(new FlowLayout()); + frame.add(buttonLoad); + frame.add(buttonSave); + frame.add(buttonSimple); + frame.add(setImageButton1); + frame.add(setImageButton2); + frame.add(setImageButton3); + frame.add(setImageButton4); + frame.pack(); + return frame; + } +} + +class MyActionListener implements ActionListener { + int id; + String name; + + public MyActionListener(int id, String name) { + this.id = id; + this.name = name; + } + + public void actionPerformed(ActionEvent ae) { + try { + FileDialog filedialog; + if (id == -1 && Objects.equals(name, "")) { + filedialog = new FileDialog(FileDialogIconTest.frame); + } else { + filedialog = new FileDialog(FileDialogIconTest.frame, name, id); + } + if (FileDialogIconTest.image != null) { + filedialog.setIconImage(FileDialogIconTest.image); + } + + if (FileDialogIconTest.images != null) { + filedialog.setIconImages(FileDialogIconTest.images); + } + filedialog.setVisible(true); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/test/jdk/java/awt/Dialog/FileDialogIconTest/T1.gif b/test/jdk/java/awt/Dialog/FileDialogIconTest/T1.gif new file mode 100644 index 0000000000000..24a7dea299fd1 Binary files /dev/null and b/test/jdk/java/awt/Dialog/FileDialogIconTest/T1.gif differ diff --git a/test/jdk/java/awt/Dialog/FileDialogIconTest/T2.gif b/test/jdk/java/awt/Dialog/FileDialogIconTest/T2.gif new file mode 100644 index 0000000000000..1f3fd66328ba6 Binary files /dev/null and b/test/jdk/java/awt/Dialog/FileDialogIconTest/T2.gif differ diff --git a/test/jdk/java/awt/Dialog/FileDialogIconTest/T3.gif b/test/jdk/java/awt/Dialog/FileDialogIconTest/T3.gif new file mode 100644 index 0000000000000..af6d626058a67 Binary files /dev/null and b/test/jdk/java/awt/Dialog/FileDialogIconTest/T3.gif differ diff --git a/test/jdk/java/awt/Dialog/FileDialogIconTest/T4.gif b/test/jdk/java/awt/Dialog/FileDialogIconTest/T4.gif new file mode 100644 index 0000000000000..67876264d8f7f Binary files /dev/null and b/test/jdk/java/awt/Dialog/FileDialogIconTest/T4.gif differ diff --git a/test/jdk/java/awt/Dialog/FileDialogIconTest/loading-msg.gif b/test/jdk/java/awt/Dialog/FileDialogIconTest/loading-msg.gif new file mode 100644 index 0000000000000..6c7570a6d8435 Binary files /dev/null and b/test/jdk/java/awt/Dialog/FileDialogIconTest/loading-msg.gif differ diff --git a/test/jdk/java/awt/Dialog/FileDialogUserFilterTest.java b/test/jdk/java/awt/Dialog/FileDialogUserFilterTest.java new file mode 100644 index 0000000000000..80ff9ed0bde60 --- /dev/null +++ b/test/jdk/java/awt/Dialog/FileDialogUserFilterTest.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Checkbox; +import java.awt.Component; +import java.awt.Container; +import java.awt.Event; +import java.awt.FileDialog; +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Label; +import java.awt.Panel; +import java.awt.TextField; +import java.io.File; +import java.io.FilenameFilter; + +/* + * @test + * @bug 4293697 4416433 4417139 4409600 + * @summary Test to verify that user filter always gets called on changing the + * directory in FileDialog + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FileDialogUserFilterTest + */ + +public class FileDialogUserFilterTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Enter a mask into the field, a directory into + the field (or leave the default values). + 2. Then click the button, file dialog will appear. + Output of the user filter will be shown in the output + area. Enter several different directories to the file dialog + via double-clicking on the directory list. The output + area should show some filtering output on each directory + change. If any output was only given on dialog startup, + the test is FAILED. + 3. Look at the list of files accepted by the filter. + If some files do not match the filter, + the test is FAILED. + 4. Open dialog with an empty filter. + Enter some directories with a lot of files (like /usr/bin). + If dialog crashes the test is FAILED. + Enter the directory that contain files and other directories. + If the directories are shown in the files box along with files + then the test is FAILED. + 5. Click in checkbox 'do not use filter', make it checked. + Open dialog, enter the directory with some files. + If no files is shown in the File list box (while you are sure + there are some files there) the test is FAILED + Otherwise it is PASSED." + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(new DialogFilterTest()) + .build() + .awaitAndCheck(); + } +} + +class DialogFilterTest extends Frame implements FilenameFilter { + FileDialog fd; + static TextField tfDirectory = new TextField(); + static TextField tfFile = new TextField(); + static TextField tfFilter = new TextField(); + static Checkbox useFilterCheck = new Checkbox("do not use filter"); + + public DialogFilterTest() { + setTitle("File Dialog User Filter test"); + add("North", new Button("Load")); + Panel p = new Panel(); + p.setLayout(new GridBagLayout()); + addRow(p, new Label("directory:", Label.RIGHT), tfDirectory); + addRow(p, new Label("file:", Label.RIGHT), tfFile); + addRow(p, new Label("filter:", Label.RIGHT), tfFilter); + addRow(p, new Label(""), useFilterCheck); + tfFilter.setText(".java"); + tfDirectory.setText("."); + add("Center", p); + setSize(300, 200); + } + + static void addRow(Container cont, Component c1, Component c2) { + GridBagLayout gbl = (GridBagLayout) cont.getLayout(); + GridBagConstraints c = new GridBagConstraints(); + c.fill = GridBagConstraints.BOTH; + cont.add(c1); + gbl.setConstraints(c1, c); + + c.gridwidth = GridBagConstraints.REMAINDER; + c.weightx = 1.0; + cont.add(c2); + gbl.setConstraints(c2, c); + } + + public boolean accept(File dir, String name) { + System.out.println("File " + dir + " String " + name); + if (fd.getMode() == FileDialog.LOAD) { + return name.lastIndexOf(tfFilter.getText()) > 0; + } + return true; + } + + public boolean action(Event evt, Object what) { + boolean load = "Load".equals(what); + + if (load || "Save".equals(what)) { + fd = new FileDialog(new Frame(), null, + load ? FileDialog.LOAD : FileDialog.SAVE); + fd.setDirectory(tfDirectory.getText()); + fd.setFile(tfFile.getText()); + if (!useFilterCheck.getState()) { + fd.setFilenameFilter(this); + } + fd.setVisible(true); + tfDirectory.setText(fd.getDirectory()); + tfFile.setText(fd.getFile()); + + return true; + } + return false; + } +} diff --git a/test/jdk/java/awt/Dialog/FileDialogWrongNameCrash.java b/test/jdk/java/awt/Dialog/FileDialogWrongNameCrash.java new file mode 100644 index 0000000000000..f3d83a6849465 --- /dev/null +++ b/test/jdk/java/awt/Dialog/FileDialogWrongNameCrash.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Frame; + +/* + * @test + * @bug 4779118 + * @summary Tests that FileDialog with wrong initial file name + * doesn't crash when Open button is pressed. + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FileDialogWrongNameCrash + */ + +public class FileDialogWrongNameCrash { + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + (This is Windows only test) + 1. You should see a frame 'Frame' with button 'Load'. Press button.", + 2. You should see 'Load file' dialog, select any file and press 'Open'", + (not 'Cancel'!!!). If Java doesn't crash - press PASS, else FAIL + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .build() + .awaitAndCheck(); + } + + private static Frame initialize() { + Frame frame = new Frame("File Dialog Wrong Name Crash Test"); + Button fileButton = new Button("Load"); + fileButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent e) { + final java.awt.FileDialog selector = + new java.awt.FileDialog(frame); + selector.setFile("Z:\\O2 XDA\\LogiTest\\\\Testcase.xml"); + selector.setVisible(true); + } + }); + frame.add(fileButton); + frame.setSize(100, 60); + return frame; + } +} diff --git a/test/jdk/java/awt/Dialog/GetLocationTest_1.java b/test/jdk/java/awt/Dialog/GetLocationTest_1.java new file mode 100644 index 0000000000000..a373f931cf72a --- /dev/null +++ b/test/jdk/java/awt/Dialog/GetLocationTest_1.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Window; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +/* + * @test + * @bug 4168481 + * @summary Test to verify Dialog getLocation() regression on Solaris + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual GetLocationTest_1 + */ + +public class GetLocationTest_1 { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Click in in the blue square and the yellow window should come + up with the top left by the cursor + 2. If you see this correct behavior press PASS. If you see that + the yellow window location is offset by some inset, press FAIL + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .logArea(8) + .build() + .awaitAndCheck(); + } + + public static Dialog initialize() { + Frame f = new Frame("Owner Frame"); + ColorComponent blue = new ColorComponent(); + blue.setBackground(Color.blue); + blue.setSize(50, 50); + + final Dialog dialog = new Dialog(f, "GetLocation test"); + dialog.setLocation(300, 300); + System.out.println("Dialog location = " + dialog.getLocation()); + blue.setLocation(50, 50); + dialog.setLayout(null); + dialog.add(blue); + dialog.setSize(200, 200); + + final ColorWindow w = new ColorWindow(f); + w.setSize(50, 50); + w.setBackground(Color.yellow); + + blue.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent e) { + PassFailJFrame.log("Dialog location = " + dialog.getLocation()); + Point p = e.getPoint(); + Component c = e.getComponent(); + PassFailJFrame.log("Position = " + p); + convertPointToScreen(p, c); + PassFailJFrame.log("Converted to = " + p); + w.setLocation(p.x, p.y); + w.setVisible(true); + } + }); + return dialog; + } + + static class ColorComponent extends Component { + public void paint(Graphics g) { + g.setColor(getBackground()); + Rectangle bounds = getBounds(); + g.fillRect(0, 0, bounds.width, bounds.height); + } + } + + static class ColorWindow extends Window { + ColorWindow(Frame f) { + super(f); + } + + public void paint(Graphics g) { + g.setColor(getBackground()); + Rectangle bounds = getBounds(); + g.fillRect(0, 0, bounds.width, bounds.height); + } + } + + public static void convertPointToScreen(Point p, Component c) { + do { + Point b = c.getLocation(); + PassFailJFrame.log("Adding " + b + " for " + c); + p.x += b.x; + p.y += b.y; + + if (c instanceof java.awt.Window) { + break; + } + c = c.getParent(); + } while (c != null); + } +} diff --git a/test/jdk/java/awt/Dialog/HideDialogTest.java b/test/jdk/java/awt/Dialog/HideDialogTest.java new file mode 100644 index 0000000000000..78d0344a6cf8e --- /dev/null +++ b/test/jdk/java/awt/Dialog/HideDialogTest.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Color; +import java.awt.Dialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Panel; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 4048664 4065506 4122094 4171979 + * @summary Test if Dialog can be successfully hidden, see that no other app + * comes to front, see if hide + dispose causes assertion failure + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual HideDialogTest + */ + +public class HideDialogTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. A Frame should appear with a "test" button in it + 2. Click on the "test" button. A Dialog will appear with a "dismiss" button + and a "dismiss-with-dispose" button + 3. First, click on the "dismiss-with-dispose" button. Verify that + no assertion failure appears. + 4. Now, click on the "dismiss" button. The Dialog should go away. + 5. Repeat from (2) 10-20 times. + 6. When the dialog goes away check that the frame window does not briefly + get obscured by another app or repaint it's entire area. There should be + no flicker at all in areas obscured by the dialog. (4065506 4122094) + If there is the test fails. + 7. If the Dialog is successfully hidden each time, the test passed. If the + Dialog did not hide, the test failed (4048664). + + NOTE: When the dialog does not go away (meaning the bug has manifested itself), + the "dismiss-with-dispose" button can be used to get rid of it. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testUI(new MyFrame()) + .build() + .awaitAndCheck(); + } +} + +class MyDialog extends Dialog { + public MyDialog(Frame f) { + super(f, "foobar", true); + setSize(200, 200); + setLayout(new BorderLayout()); + Panel p = new Panel(); + p.setLayout(new FlowLayout(FlowLayout.CENTER)); + Button okButton; + okButton = new Button("dismiss"); + p.add(okButton); + okButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + System.out.println("Calling setVisible(false)"); + setVisible(false); + } + }); + Button newButton; + p.add(newButton = new Button("dismiss-with-dispose")); + newButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + System.out.println("Calling setVisible(false) + dispose()"); + setVisible(false); + dispose(); + } + }); + add("South", p); + pack(); + } +} + +class MyFrame extends Frame implements ActionListener { + public MyFrame() { + super(); + setSize(600, 400); + setTitle("HideDialogTest"); + setLayout(new BorderLayout()); + Panel toolbar = new Panel(); + toolbar.setLayout(new FlowLayout(FlowLayout.LEFT)); + Button testButton = new Button("test"); + testButton.addActionListener(this); + toolbar.add(testButton); + add("North", toolbar); + } + + public void actionPerformed(ActionEvent e) { + String s = e.getActionCommand(); + if (s.equals("test")) { + System.out.println("Begin test"); + MyDialog d = new MyDialog(this); + d.setVisible(true); + System.out.println("End test"); + } + } + + public void paint(Graphics g) { + for (int i = 0; i < 10; i++) { + g.setColor(Color.red); + g.fillRect(0, 0, 2000, 2000); + g.setColor(Color.blue); + g.fillRect(0, 0, 2000, 2000); + } + } +} diff --git a/test/jdk/java/awt/Dialog/ModalDialogTest.java b/test/jdk/java/awt/Dialog/ModalDialogTest.java new file mode 100644 index 0000000000000..aceacc9209a80 --- /dev/null +++ b/test/jdk/java/awt/Dialog/ModalDialogTest.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Checkbox; +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +/* + * @test + * @bug 4078176 + * @summary Test to verify Modal dialogs don't act modal if addNotify() + * is called before setModal(true). + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ModalDialogTest + */ + +public class ModalDialogTest implements ActionListener { + public boolean modal = true; + Button closeBtn = new Button("Close me"); + Button createBtn = new Button("Create Dialog"); + Button createNewBtn = new Button("Create Modal Dialog"); + Button lastBtn = new Button("Show Last Dialog"); + Dialog dialog; + Dialog newDialog; + Frame testFrame; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Use 'Modal' checkbox to select which dialog you're + going to create - modal or non-modal. + (this checkbox affects only new created dialog but + not existing one) + 2. Use 'Create Dialog' button to create a dialog. + If you have selected 'Modal' checkbox then dialog has to + be created modal - you can make sure of that clicking + on any other control (i.e. 'Modal' checkbox) - they + should not work. + 3. Use 'Show Last Dialog' button to bring up last + created dialog - to make sure that if you show/hide + modal dialog several times it stays modal. + 4. On the appearing dialog there are two buttons: + 'Close Me' which closes the dialog, + and 'Create Modal Dialog' which creates one more + MODAL dialog just to make sure that + in situation with two modal dialogs all is fine. + 5. If created modal dialogs are really modal + (which means that they blocks the calling app) + then test is PASSED, otherwise it's FAILED." + """; + ModalDialogTest test = new ModalDialogTest(); + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(test.initialize()) + .build() + .awaitAndCheck(); + } + + public Frame initialize() { + testFrame = new Frame("Parent Frame"); + Frame frame = new Frame("Modal Dialog test"); + Panel panel = new Panel(); + panel.setLayout(new BorderLayout()); + + createBtn.addActionListener(this); + createNewBtn.addActionListener(this); + closeBtn.addActionListener(this); + lastBtn.addActionListener(this); + panel.add("Center", createBtn); + panel.add("South", lastBtn); + Checkbox cb = new Checkbox("Modal", modal); + cb.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + modal = ((Checkbox) e.getSource()).getState(); + } + }); + panel.add("North", cb); + panel.setSize(200, 100); + + frame.add(panel); + frame.pack(); + return frame; + } + + public void actionPerformed(ActionEvent e) { + if (e.getSource() == createBtn) { + if (dialog != null) { + dialog.dispose(); + } + dialog = new Dialog(testFrame, "Modal Dialog"); + dialog.add("North", closeBtn); + dialog.add("South", createNewBtn); + createBtn.setEnabled(false); + dialog.pack(); + dialog.setModal(modal); + dialog.setVisible(true); + } else if (e.getSource() == closeBtn && dialog != null) { + createBtn.setEnabled(true); + dialog.setVisible(false); + } else if (e.getSource() == lastBtn && dialog != null) { + dialog.setVisible(true); + } else if (e.getSource() == createNewBtn && newDialog == null) { + newDialog = new Dialog(testFrame, "New Modal Dialog"); + Button clsBtn = new Button("Close Me"); + clsBtn.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + newDialog.dispose(); + newDialog = null; + } + }); + newDialog.add("North", clsBtn); + newDialog.pack(); + newDialog.setModal(true); + newDialog.setVisible(true); + } + } +} diff --git a/test/jdk/java/awt/Dialog/PrintToFileTest/PrintToFileFrame.java b/test/jdk/java/awt/Dialog/PrintToFileTest/PrintToFileFrame.java new file mode 100644 index 0000000000000..a117622d57003 --- /dev/null +++ b/test/jdk/java/awt/Dialog/PrintToFileTest/PrintToFileFrame.java @@ -0,0 +1,40 @@ +import java.awt.Button; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.PrintJob; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +class PrintToFileFrame extends Frame implements ActionListener { + Button nativeDlg = new Button("Show print dialog"); + + public PrintToFileFrame() { + this.setLayout(new FlowLayout()); + add(nativeDlg); + nativeDlg.addActionListener(this); + + setSize(300, 300); + } + + @SuppressWarnings("removal") + public void actionPerformed(ActionEvent ae) { + if (System.getSecurityManager() == null) { + throw new RuntimeException("Security manager isn't installed."); + } + + try { + System.getSecurityManager().checkPrintJobAccess(); + System.out.println("checkPrintJobAccess - OK"); + } catch (SecurityException e) { + System.out.println("checkPrintJobAccess - ERROR " + e); + } + + PrintJob printJob = getToolkit().getPrintJob(this, null, null); + + if (printJob != null) { + System.out.println("Print Job: " + printJob); + } else { + System.out.println("Print Job is null."); + } + } +} diff --git a/test/jdk/java/awt/Dialog/PrintToFileTest/PrintToFileGranted.java b/test/jdk/java/awt/Dialog/PrintToFileTest/PrintToFileGranted.java new file mode 100644 index 0000000000000..05d73123d983a --- /dev/null +++ b/test/jdk/java/awt/Dialog/PrintToFileTest/PrintToFileGranted.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.print.PrinterJob; + +/* + * @test + * @bug 6275359 + * @summary Test to verify system menu of a dialog on win32 + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @compile PrintToFileFrame.java + * @compile PrintToFileGranted.java + * @run main/manual/policy=granted/othervm PrintToFileGranted + */ + +public class PrintToFileGranted { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS; + if (isPrintSupport()) { + INSTRUCTIONS = """ + 1. Click on 'Show file dialog' button A print dialog will come up + 2. If checkbox 'Print to file' is enabled then the test passed + else the test failed + 3. Close the print dialog before pressing PASS or FAIL buttons + """; + } else { + INSTRUCTIONS = """ + 1. The test requires printer installed in your system, + but there is no printers found + Please install one and re-run the test + """; + } + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(new PrintToFileFrame()) + .build() + .awaitAndCheck(); + } + + public static boolean isPrintSupport() { + PrinterJob pj = PrinterJob.getPrinterJob(); + return pj.getPrintService() != null; + } +} diff --git a/test/jdk/java/awt/Dialog/PrintToFileTest/PrintToFileRevoked.java b/test/jdk/java/awt/Dialog/PrintToFileTest/PrintToFileRevoked.java new file mode 100644 index 0000000000000..7c724e97bed53 --- /dev/null +++ b/test/jdk/java/awt/Dialog/PrintToFileTest/PrintToFileRevoked.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.print.PrinterJob; + +/* + * @test + * @bug 6275359 + * @summary Test to verify Printing ignores Security permissions + * using native dialog + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @compile PrintToFileRevoked.java + * @run main/manual/policy=revoked/othervm PrintToFileRevoked + */ + +public class PrintToFileRevoked { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS; + if (isPrintSupport()) { + INSTRUCTIONS = """ + 1. Click on 'Show file dialog' button A print dialog will come up + 2. If checkbox 'Print to file' is disabled then the test passed + else the test failed + 3. Close the print dialog before pressing PASS or FAIL buttons + """; + } else { + INSTRUCTIONS = """ + 1. The test requires printer installed in your system, + but there is no printers found + Please install one and re-run the test + """; + } + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(new PrintToFileFrame()) + .build() + .awaitAndCheck(); + } + + public static boolean isPrintSupport() { + PrinterJob pj = PrinterJob.getPrinterJob(); + return pj.getPrintService() != null; + } +} diff --git a/test/jdk/java/awt/Dialog/PrintToFileTest/granted b/test/jdk/java/awt/Dialog/PrintToFileTest/granted new file mode 100644 index 0000000000000..e73b0fdf3cdef --- /dev/null +++ b/test/jdk/java/awt/Dialog/PrintToFileTest/granted @@ -0,0 +1,10 @@ +/* AUTOMATICALLY GENERATED ON Thu Jan 03 15:48:39 PST 2002*/ +/* DO NOT EDIT */ + +grant { + permission java.lang.RuntimePermission "queuePrintJob"; + permission java.util.PropertyPermission "*", "read"; + permission java.io.FilePermission "<>", "read"; + permission java.io.FilePermission "<>", "write"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.util.locale.provider"; +}; diff --git a/test/jdk/java/awt/Dialog/PrintToFileTest/revoked b/test/jdk/java/awt/Dialog/PrintToFileTest/revoked new file mode 100644 index 0000000000000..d2545e15e1155 --- /dev/null +++ b/test/jdk/java/awt/Dialog/PrintToFileTest/revoked @@ -0,0 +1,9 @@ +/* AUTOMATICALLY GENERATED ON Thu Jan 03 15:48:39 PST 2002*/ +/* DO NOT EDIT */ + +grant { + permission java.lang.RuntimePermission "queuePrintJob"; + permission java.util.PropertyPermission "*", "read"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.util.locale.provider"; +}; + diff --git a/test/jdk/java/awt/Dialog/TopmostModalDialogTest.java b/test/jdk/java/awt/Dialog/TopmostModalDialogTest.java new file mode 100644 index 0000000000000..7b91d47e248c4 --- /dev/null +++ b/test/jdk/java/awt/Dialog/TopmostModalDialogTest.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +/* + * @test + * @bug 4940645 + * @summary Test to verify setAlwaysOnTop(true) does + * work in modal dialog in Windows + * @requires (os.family == "windows" | os.family == "linux" ) + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual TopmostModalDialogTest + */ + +public class TopmostModalDialogTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + (This test verifies that modal dialog can be made always on top + This test should only be run on the platforms which support always-on-top windows + Such platforms are: Windows, Linux with GNOME2/Metacity window manager, + Solaris with GNOME2/Metacity window manager + If you are not running on any of these platforms, please select 'Pass' to skip testing + If you are unsure on which platform you are running please select 'Pass') + + 1. After test started you see a frame with \\"Main Frame\\" title + It contains three buttons. Every button starts one of test stage + You should test all three stages + 2. After you press button to start the stage. It shows modal dialog + This modal dialog should be always-on-top window + 3. Since it's a modal the only way to test this is try to cover it + using some native window + 4. If you will able to cover it be native window - test FAILS, otherwise - PASS + + Note: in stages #2 and #3 dialog is initially shown as regular modal dialogs + You will see \\"Let's wait\\" message in the message area below + Please wait until message \\"Let's make it topmost\\" will be printed in the area + After that you can continue testing. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .logArea(8) + .build() + .awaitAndCheck(); + } + + public static Frame initialize() { + final Tester tester = new Tester(); + Frame frame = new Frame("Main Frame"); + frame.setLayout(new GridLayout(3, 1)); + for (int i = 0; i < 3; i++) { + Button btn = new Button("Stage #" + i); + frame.add(btn); + btn.addActionListener(tester); + } + frame.pack(); + return frame; + } +} + +class Tester implements ActionListener { + public void actionPerformed(ActionEvent e) { + String command = e.getActionCommand(); + PassFailJFrame.log(command); + int cmd = Integer.parseInt(command.substring(command.length() - 1)); + PassFailJFrame.log("" + cmd); + Dialog dlg = new Dialog(new Frame(""), "Modal Dialog", true); + dlg.setBounds(100, 100, 100, 100); + dlg.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent we) { + Window self = we.getWindow(); + Window owner = self.getOwner(); + if (owner != null) { + owner.dispose(); + } else { + self.dispose(); + } + } + }); + + switch (cmd) { + case 0: + dlg.setAlwaysOnTop(true); + dlg.setVisible(true); + break; + case 1: + (new Thread(new TopmostMaker(dlg))).start(); + dlg.setVisible(true); + break; + case 2: + dlg.setFocusableWindowState(false); + (new Thread(new TopmostMaker(dlg))).start(); + dlg.setVisible(true); + break; + default: + PassFailJFrame.log("Unsupported operation :("); + } + } +} + +class TopmostMaker implements Runnable { + final Window wnd; + + public TopmostMaker(Window wnd) { + this.wnd = wnd; + } + + public void run() { + PassFailJFrame.log("Let's wait"); + try { + Thread.sleep(1000); + } catch (InterruptedException ie) { + PassFailJFrame.log("Test was interrupted. " + ie); + ie.printStackTrace(); + } + PassFailJFrame.log("Let's make it topmost"); + wnd.setAlwaysOnTop(true); + } +} diff --git a/test/jdk/java/awt/EventQueue/PushPopDeadlock/PushPopDeadlock.java b/test/jdk/java/awt/EventQueue/PushPopDeadlock/PushPopDeadlock.java new file mode 100644 index 0000000000000..82ca54871848e --- /dev/null +++ b/test/jdk/java/awt/EventQueue/PushPopDeadlock/PushPopDeadlock.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4212687 + * @summary Verifies that calling EventQueue.push() and EventQueue.pop() + * does not deadlock. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual PushPopDeadlock + */ + +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Robot; +import java.awt.Toolkit; + +public class PushPopDeadlock { + static int counter = 0; + static Robot robot; + static Frame f; + static Label l; + + public static void main(String[] args) throws Exception { + robot = new Robot(); + String INSTRUCTIONS = """ + Click rapidly in the Frame labeled 'Click Here!'. + The number in the Frame should continue to increase. If the number + stops increasing (remains at a constant value), the test fails. + """; + + PassFailJFrame pfJFrame = PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(PushPopDeadlock::createUI) + .build(); + PushPopDeadlock.test(); + pfJFrame.awaitAndCheck(); + } + + public static Frame createUI() { + f = new Frame("Click Here!"); + l = new Label("Counter: " + counter); + f.add(l); + f.setSize(200, 200); + return f; + } + + public static void test() { + EventQueue q = new EventQueue() { + public void push(EventQueue queue) { + super.push(queue); + pop(); + } + }; + EventQueue q2 = new EventQueue(); + + Toolkit.getDefaultToolkit().getSystemEventQueue().push(q); + + new Thread(() -> { + while (true) { + robot.delay(500); + l.setText("Counter: " + ++counter); + q.push(q2); + try { + Thread.currentThread().sleep(500); + } catch (InterruptedException e) { + return; + } + } + }).start(); + } +} diff --git a/test/jdk/java/awt/FileDialog/DoubleActionCloseX.java b/test/jdk/java/awt/FileDialog/DoubleActionCloseX.java new file mode 100644 index 0000000000000..5d3feaa42ed4b --- /dev/null +++ b/test/jdk/java/awt/FileDialog/DoubleActionCloseX.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.FileDialog; +import java.awt.Frame; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 6227750 + * @summary Tests that FileDialog can be closed by clicking the 'close' (X) button + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DoubleActionCloseX + */ + +public class DoubleActionCloseX { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + NOTE: On Linux and Mac, there is no 'close'(X) button + when file dialog is visible, press Pass. + + Click the 'Open File Dialog' button to open FileDialog. + A file dialog will appear on the screen. + Click on the 'close'(X) button. + The dialog should be closed. + If not, the test failed, press Fail otherwise press Pass. + """; + + PassFailJFrame.builder() + .title("DoubleActionCloseX Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(DoubleActionCloseX::createUI) + .build() + .awaitAndCheck(); + } + public static Frame createUI() { + Frame f = new Frame("DoubleActionCloseX Test"); + Button b = new Button("Open File Dialog"); + FileDialog fd = new FileDialog(f); + b.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + fd.setVisible(true); + } + }); + f.add(b); + f.setSize(300, 200); + return f; + } +} diff --git a/test/jdk/java/awt/FileDialog/DoubleActionESC.java b/test/jdk/java/awt/FileDialog/DoubleActionESC.java new file mode 100644 index 0000000000000..e8bb4963aa002 --- /dev/null +++ b/test/jdk/java/awt/FileDialog/DoubleActionESC.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.FileDialog; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.util.concurrent.CountDownLatch; + +import static java.util.concurrent.TimeUnit.SECONDS; + +/* + * @test + * @bug 5097243 + * @summary Tests that FileDialog can be closed by ESC any time + * @key headful + * @run main DoubleActionESC + * @run main/othervm -Dsun.awt.disableGtkFileDialogs=true DoubleActionESC + */ + +public class DoubleActionESC { + private static Frame f; + private static Button showBtn; + private static FileDialog fd; + private static Robot robot; + private static volatile Point p; + private static volatile Dimension d; + private static final int REPEAT_COUNT = 2; + private static final long LATCH_TIMEOUT = 10; + + private static final CountDownLatch latch = new CountDownLatch(REPEAT_COUNT); + + public static void main(String[] args) throws Exception { + robot = new Robot(); + robot.setAutoDelay(50); + try { + EventQueue.invokeAndWait(() -> { + createAndShowUI(); + }); + + robot.waitForIdle(); + robot.delay(1000); + + EventQueue.invokeAndWait(() -> { + p = showBtn.getLocationOnScreen(); + d = showBtn.getSize(); + }); + + for (int i = 0; i < REPEAT_COUNT; ++i) { + robot.mouseMove(p.x + d.width / 2, p.y + d.height / 2); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(1000); + + robot.keyPress(KeyEvent.VK_ESCAPE); + robot.keyRelease(KeyEvent.VK_ESCAPE); + robot.waitForIdle(); + robot.delay(1000); + } + + if (!latch.await(LATCH_TIMEOUT, SECONDS)) { + throw new RuntimeException("Test failed: Latch timeout reached"); + } + EventQueue.invokeAndWait(() -> { + if (fd.isVisible()) { + throw new RuntimeException("File Dialog is not closed"); + } + }); + } finally { + EventQueue.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + } + } + + public static void createAndShowUI() { + f = new Frame("DoubleActionESC Test"); + showBtn = new Button("Show File Dialog"); + fd = new FileDialog(f); + showBtn.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == showBtn) { + fd.setSize(200, 200); + fd.setLocation(200, 200); + fd.setVisible(true); + latch.countDown(); + } + } + }); + f.add(showBtn); + f.setSize(300, 200); + f.setLocationRelativeTo(null); + f.setVisible(true); + } +} diff --git a/test/jdk/java/awt/FileDialog/FileDialogForDirectories.java b/test/jdk/java/awt/FileDialog/FileDialogForDirectories.java new file mode 100644 index 0000000000000..6e31adb44473b --- /dev/null +++ b/test/jdk/java/awt/FileDialog/FileDialogForDirectories.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.FileDialog; +import java.awt.Frame; +import java.lang.reflect.InvocationTargetException; +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; + +/* + * @test + * @bug 7161437 + * @summary We should support "apple.awt.fileDialogForDirectories" property. + * @requires (os.family == "mac") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FileDialogForDirectories + */ + +public class FileDialogForDirectories { + + private static JFrame initialize() { + System.setProperty("apple.awt.fileDialogForDirectories", "true"); + + JFrame frame = new JFrame("Directory File Dialog Test Frame"); + frame.setLayout(new BorderLayout()); + JTextArea textOutput = new JTextArea(8, 30); + textOutput.setLineWrap(true); + JScrollPane textScrollPane = new JScrollPane(textOutput); + frame.add(textScrollPane, BorderLayout.CENTER); + + FileDialog fd = new FileDialog(new Frame(), "Open"); + + Button showBtn = new Button("Show File Dialog"); + showBtn.addActionListener(e -> { + fd.setVisible(true); + String output = fd.getFile(); + if (output != null) { + textOutput.append(output + " is selected\n"); + textOutput.setCaretPosition(textOutput.getText().length()); + } + }); + frame.add(showBtn, BorderLayout.NORTH); + frame.pack(); + return frame; + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + String instructions = """ + 1) Click on 'Show File Dialog' button. A file dialog will come up. + 2) Check that files can't be selected. + 3) Check that directories can be selected. + 4) Repeat steps 1 - 3 a few times for different files and directories. + 5) If it's true then the press Pass, otherwise press Fail. + """; + + PassFailJFrame.builder() + .title("Directory File Dialog Test Instructions") + .instructions(instructions) + .rows((int) instructions.lines().count() + 1) + .columns(40) + .testUI(FileDialogForDirectories::initialize) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/FileDialog/FileDialogForDirectories/FileDialogForDirectories.html b/test/jdk/java/awt/FileDialog/FileDialogForDirectories/FileDialogForDirectories.html deleted file mode 100644 index fbce46be30215..0000000000000 --- a/test/jdk/java/awt/FileDialog/FileDialogForDirectories/FileDialogForDirectories.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - FileDialogForDirectories - - - -

    FileDialogForDirectories
    Bug ID: 7161437

    - -

    See the dialog box (usually in upper left corner) for instructions

    - - - - diff --git a/test/jdk/java/awt/FileDialog/FileDialogForDirectories/FileDialogForDirectories.java b/test/jdk/java/awt/FileDialog/FileDialogForDirectories/FileDialogForDirectories.java deleted file mode 100644 index 2c1e833ebf9aa..0000000000000 --- a/test/jdk/java/awt/FileDialog/FileDialogForDirectories/FileDialogForDirectories.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - -import jdk.test.lib.Platform; -import test.java.awt.regtesthelpers.Sysout; - -import java.applet.Applet; -import java.awt.Button; -import java.awt.FileDialog; -import java.awt.Frame; -import java.awt.GridLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -public class FileDialogForDirectories extends Applet implements ActionListener { - private volatile Button showBtn; - private volatile FileDialog fd; - - @Override - public void init() { - if (!Platform.isOSX()) { - Sysout.createDialogWithInstructions(new String[]{ - "Press PASS, this test is for MacOS X only."}); - return; - } - - System.setProperty("apple.awt.fileDialogForDirectories", "true"); - - setLayout(new GridLayout(1, 1)); - - fd = new FileDialog(new Frame(), "Open"); - - showBtn = new Button("Show File Dialog"); - showBtn.addActionListener(this); - add(showBtn); - String[] instructions = { - "1) Click on 'Show File Dialog' button. A file dialog will come up.", - "2) Check that files can't be selected.", - "3) Check that directories can be selected.", - "4) Repeat steps 1 - 3 a few times for different files and directories.", - "5) If it's true then the test passed, otherwise it failed."}; - Sysout.createDialogWithInstructions(instructions); - }//End init() - - @Override - public void start() { - setSize(200, 200); - show(); - }// start() - - @Override - public void actionPerformed(ActionEvent e) { - if (e.getSource() == showBtn) { - fd.setVisible(true); - String output = fd.getFile(); - if (output != null) { - Sysout.println(output + " is selected"); - } - } - } -}// class ManualYesNoTest diff --git a/test/jdk/java/awt/FileDialog/FileDialogForPackages.java b/test/jdk/java/awt/FileDialog/FileDialogForPackages.java new file mode 100644 index 0000000000000..878b7316b5bbe --- /dev/null +++ b/test/jdk/java/awt/FileDialog/FileDialogForPackages.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.FileDialog; +import java.awt.Frame; +import java.lang.reflect.InvocationTargetException; +import javax.swing.JButton; + +/* + * @test + * @bug 8026869 + * @summary Support apple.awt.use-file-dialog-packages property. + * @requires (os.family == "mac") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FileDialogForPackages +*/ + +public class FileDialogForPackages { + + private static JButton initialize() { + System.setProperty("apple.awt.use-file-dialog-packages", "true"); + + FileDialog fd = new FileDialog((Frame) null, "Open"); + String APPLICATIONS_FOLDER = "/Applications"; + fd.setDirectory(APPLICATIONS_FOLDER); + + JButton showBtn = new JButton("Show File Dialog"); + showBtn.addActionListener(e -> { + fd.setVisible(true); + String output = fd.getFile(); + if (output != null) { + PassFailJFrame.log(output + " is selected\n"); + } + }); + return showBtn; + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + String instructions = """ + 1) Click on 'Show File Dialog' button. A file dialog will come up. + 2) Navigate to the Applications folder if not already there. + 3) Check that the application bundles can be selected + but can not be navigated. + 4) If it's true then press Pass, otherwise press Fail. + """; + + PassFailJFrame.builder() + .title("Directory File Dialog Test Instructions") + .instructions(instructions) + .rows((int) instructions.lines().count() + 1) + .columns(40) + .logArea(8) + .splitUIBottom(FileDialogForPackages::initialize) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/FileDialog/FileDialogForPackages/FileDialogForPackages.html b/test/jdk/java/awt/FileDialog/FileDialogForPackages/FileDialogForPackages.html deleted file mode 100644 index 0e5a939c6c331..0000000000000 --- a/test/jdk/java/awt/FileDialog/FileDialogForPackages/FileDialogForPackages.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - FileDialogForPackages - - - -

    FileDialogForPackages
    Bug ID: 8026869

    - -

    See the dialog box (usually in upper left corner) for instructions

    - - - - diff --git a/test/jdk/java/awt/FileDialog/FileDialogForPackages/FileDialogForPackages.java b/test/jdk/java/awt/FileDialog/FileDialogForPackages/FileDialogForPackages.java deleted file mode 100644 index d624607f33312..0000000000000 --- a/test/jdk/java/awt/FileDialog/FileDialogForPackages/FileDialogForPackages.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - -import jdk.test.lib.Platform; -import test.java.awt.regtesthelpers.Sysout; - -import java.applet.Applet; -import java.awt.Button; -import java.awt.FileDialog; -import java.awt.Frame; -import java.awt.GridLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -public class FileDialogForPackages extends Applet implements ActionListener { - private static final String APPLICATIONS_FOLDER = "/Applications"; - - private volatile Button showBtn; - private volatile FileDialog fd; - - @Override - public void init() { - if (!Platform.isOSX()) { - Sysout.createDialogWithInstructions(new String[]{ - "Press PASS, this test is for MacOS X only."}); - return; - } - - System.setProperty("apple.awt.use-file-dialog-packages", "true"); - - setLayout(new GridLayout(1, 1)); - - fd = new FileDialog(new Frame(), "Open"); - fd.setDirectory(APPLICATIONS_FOLDER); - - showBtn = new Button("Show File Dialog"); - showBtn.addActionListener(this); - add(showBtn); - String[] instructions = { - "1) Click on 'Show File Dialog' button. A file dialog will come up.", - "2) Navigate to the Applications folder if not already there", - "3) Check that the application bundles can be selected and can not be navigated", - "4) If it's true then the test passed, otherwise it failed."}; - Sysout.createDialogWithInstructions(instructions); - } - - @Override - public void start() { - setSize(200, 200); - show(); - } - - @Override - public void actionPerformed(ActionEvent e) { - if (e.getSource() == showBtn) { - fd.setVisible(true); - String output = fd.getFile(); - if (output != null) { - Sysout.println(output + " is selected"); - } - } - } -} diff --git a/test/jdk/java/awt/FileDialog/FileDialogOpenDirTest.java b/test/jdk/java/awt/FileDialog/FileDialogOpenDirTest.java new file mode 100644 index 0000000000000..efd5148010f86 --- /dev/null +++ b/test/jdk/java/awt/FileDialog/FileDialogOpenDirTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.FileDialog; +import java.awt.Frame; +import java.awt.Toolkit; +import java.lang.reflect.InvocationTargetException; +import javax.swing.JButton; +import jtreg.SkippedException; + +/* + * @test + * @bug 4974135 + * @summary FileDialog should open current directory by default. + * @requires (os.family == "linux") + * @library /java/awt/regtesthelpers + * @library /test/lib + * @build PassFailJFrame + * @build jtreg.SkippedException + * @run main/manual FileDialogOpenDirTest + */ + +public class FileDialogOpenDirTest { + private static JButton initialize() { + System.setProperty("sun.awt.disableGtkFileDialogs", "true"); + + JButton open = new JButton("Open File Dialog"); + open.addActionListener(e -> { + new FileDialog((Frame) null).setVisible(true); + }); + + return open; + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + String toolkit = Toolkit.getDefaultToolkit().getClass().getName(); + if (!toolkit.equals("sun.awt.X11.XToolkit")) { + throw new SkippedException("Test is not designed for toolkit " + toolkit); + } + + String curdir = System.getProperty("user.dir"); + String instructions = """ + Click the \"Open File Dialog\" button below to open FileDialog. + Verify that the directory opened is current directory, that is: + $curdir, + If so press Pass, otherwise press Fail + """.replace("$curdir", curdir); + + PassFailJFrame.builder() + .title("Directory File Dialog Test Instructions") + .instructions(instructions) + .rows((int) instructions.lines().count() + 1) + .columns(40) + .splitUIBottom(FileDialogOpenDirTest::initialize) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/FileDialog/FileDialogOpenDirTest/FileDialogOpenDirTest.html b/test/jdk/java/awt/FileDialog/FileDialogOpenDirTest/FileDialogOpenDirTest.html deleted file mode 100644 index a36a20661ba40..0000000000000 --- a/test/jdk/java/awt/FileDialog/FileDialogOpenDirTest/FileDialogOpenDirTest.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - -FileDialogOpenDirTest - - - -

    FileDialogOpenDirTest
    Bug ID: 4974135

    - -

    See the dialog box (usually in upper left corner) for instructions

    - - - - diff --git a/test/jdk/java/awt/FileDialog/FileDialogOpenDirTest/FileDialogOpenDirTest.java b/test/jdk/java/awt/FileDialog/FileDialogOpenDirTest/FileDialogOpenDirTest.java deleted file mode 100644 index 2461a0b7db294..0000000000000 --- a/test/jdk/java/awt/FileDialog/FileDialogOpenDirTest/FileDialogOpenDirTest.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - test - @bug 4974135 - @summary FileDialog should open current directory by default. - @author tav@sparc.spb.su area=awt.filedialog - @run applet/manual=yesno FileDialogOpenDirTest.html -*/ - -import java.awt.*; -import java.awt.event.*; -import java.applet.*; - -public class FileDialogOpenDirTest extends Applet { - - public static void main(String[] args) { - Applet a = new FileDialogOpenDirTest(); - a.init(); - a.start(); - } - - public void init() - { - System.setProperty("sun.awt.disableGtkFileDialogs","true"); - //Create instructions for the user here, as well as set up - // the environment -- set the layout manager, add buttons, - // etc. - this.setLayout (new BorderLayout ()); - - String curdir = System.getProperty("user.dir"); - - String[] instructions1 = - { - "After test started you will see 'Test Frame' with a button inside.", - "Click the button to open FileDialog.", - "Verify that the directory opened is current directory, that is:", - curdir, - "If so press PASSED, otherwise FAILED." - }; - - String[] instructions2 = - { - "The test is not applicable for current platform. Press PASSED." - }; - - Sysout.createDialogWithInstructions(Toolkit.getDefaultToolkit().getClass().getName(). - equals("sun.awt.X11.XToolkit") ? - instructions1 : instructions2); - } - - public void start() { - Frame frame = new Frame("Test Frame"); - Button open = new Button("Open File Dialog"); - - open.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - new FileDialog(new Frame()).show(); - } - }); - - frame.setLayout(new FlowLayout()); - frame.add(open); - - int x = 0; - int y = 0; - Component dlg = null; - - if ((dlg = Sysout.getDialog()) != null) { - x = dlg.getBounds().x + dlg.getBounds().width; - y = dlg.getBounds().y; - } - frame.setBounds(x, y, 150, 70); - frame.setVisible(true); - } -} - - -/**************************************************** - Standard Test Machinery - DO NOT modify anything below -- it's a standard - chunk of code whose purpose is to make user - interaction uniform, and thereby make it simpler - to read and understand someone else's test. - ****************************************************/ - -/** - This is part of the standard test machinery. - It creates a dialog (with the instructions), and is the interface - for sending text messages to the user. - To print the instructions, send an array of strings to Sysout.createDialog - WithInstructions method. Put one line of instructions per array entry. - To display a message for the tester to see, simply call Sysout.println - with the string to be displayed. - This mimics System.out.println but works within the test harness as well - as standalone. - */ - -class Sysout -{ - private static TestDialog dialog; - - public static void createDialogWithInstructions( String[] instructions ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - dialog.printInstructions( instructions ); - dialog.setVisible(true); - println( "Any messages for the tester will display here." ); - } - - public static void createDialog( ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - String[] defInstr = { "Instructions will appear here. ", "" } ; - dialog.printInstructions( defInstr ); - dialog.setVisible(true); - println( "Any messages for the tester will display here." ); - } - - - public static void printInstructions( String[] instructions ) - { - dialog.printInstructions( instructions ); - } - - - public static void println( String messageIn ) - { - dialog.displayMessage( messageIn ); - } - - public static Component getDialog() { - return dialog; - } - -}// Sysout class - -/** - This is part of the standard test machinery. It provides a place for the - test instructions to be displayed, and a place for interactive messages - to the user to be displayed. - To have the test instructions displayed, see Sysout. - To have a message to the user be displayed, see Sysout. - Do not call anything in this dialog directly. - */ -class TestDialog extends Dialog -{ - - TextArea instructionsText; - TextArea messageText; - int maxStringLength = 80; - - //DO NOT call this directly, go through Sysout - public TestDialog( Frame frame, String name ) - { - super( frame, name ); - int scrollBoth = TextArea.SCROLLBARS_BOTH; - instructionsText = new TextArea( "", 15, maxStringLength, scrollBoth ); - add( "North", instructionsText ); - - messageText = new TextArea( "", 5, maxStringLength, scrollBoth ); - add("Center", messageText); - - pack(); - - setVisible(true); - }// TestDialog() - - //DO NOT call this directly, go through Sysout - public void printInstructions( String[] instructions ) - { - //Clear out any current instructions - instructionsText.setText( "" ); - - //Go down array of instruction strings - - String printStr, remainingStr; - for( int i=0; i < instructions.length; i++ ) - { - //chop up each into pieces maxSringLength long - remainingStr = instructions[ i ]; - while( remainingStr.length() > 0 ) - { - //if longer than max then chop off first max chars to print - if( remainingStr.length() >= maxStringLength ) - { - //Try to chop on a word boundary - int posOfSpace = remainingStr. - lastIndexOf( ' ', maxStringLength - 1 ); - - if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1; - - printStr = remainingStr.substring( 0, posOfSpace + 1 ); - remainingStr = remainingStr.substring( posOfSpace + 1 ); - } - //else just print - else - { - printStr = remainingStr; - remainingStr = ""; - } - - instructionsText.append( printStr + "\n" ); - - }// while - - }// for - - }//printInstructions() - - //DO NOT call this directly, go through Sysout - public void displayMessage( String messageIn ) - { - messageText.append( messageIn + "\n" ); - System.out.println(messageIn); - } - -}// TestDialog class diff --git a/test/jdk/java/awt/FileDialog/FileDialogReturnTest.java b/test/jdk/java/awt/FileDialog/FileDialogReturnTest.java new file mode 100644 index 0000000000000..0b78dfb2a2151 --- /dev/null +++ b/test/jdk/java/awt/FileDialog/FileDialogReturnTest.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.FileDialog; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Label; +import java.awt.TextField; +import java.awt.Toolkit; +import java.lang.reflect.InvocationTargetException; +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import jtreg.SkippedException; + +/* + * @test + * @bug 6260676 + * @summary FileDialog.setDirectory() does not work properly, XToolkit + * @requires (os.family == "linux") + * @library /java/awt/regtesthelpers + * @library /test/lib + * @build PassFailJFrame + * @build jtreg.SkippedException + * @run main/manual FileDialogReturnTest + */ + +public class FileDialogReturnTest { + + private static JFrame initialize() { + JFrame frame = new JFrame("File Dialog Return Test Frame"); + GridBagConstraints gbc = new GridBagConstraints(); + GridBagLayout grid = new GridBagLayout(); + frame.setLayout(grid); + JTextArea textOutput = new JTextArea(8, 30); + textOutput.setLineWrap(true); + JScrollPane textScrollPane = new JScrollPane(textOutput); + gbc.insets = new Insets(5, 5, 5, 5); + + gbc.gridx = 0; + gbc.gridy = 0; + gbc.gridwidth = 1; + gbc.fill = GridBagConstraints.WEST; + frame.add(new Label("File:"), gbc); + + TextField fileField = new TextField("", 20); + gbc.gridx = 1; + gbc.gridy = 0; + gbc.fill = GridBagConstraints.HORIZONTAL; + frame.add(fileField, gbc); + + gbc.gridx = 0; + gbc.gridy = 1; + gbc.fill = GridBagConstraints.WEST; + frame.add(new Label("Dir:"), gbc); + + TextField dirField = new TextField("", 20); + gbc.gridx = 1; + gbc.gridy = 1; + gbc.fill = GridBagConstraints.HORIZONTAL; + frame.add(dirField, gbc); + + Button button = new Button("Show"); + gbc.gridx = 0; + gbc.gridy = 2; + gbc.gridwidth = 2; + gbc.fill = GridBagConstraints.CENTER; + frame.add(button, gbc); + + gbc.gridx = 0; + gbc.gridy = 3; + gbc.gridwidth = 2; + frame.add(textScrollPane, gbc); + + button.addActionListener(e -> { + FileDialog fd = new FileDialog(frame); + fd.setFile(fileField.getText()); + fd.setDirectory(dirField.getText()); + fd.setVisible(true); + + textOutput.append("[file=" + fd.getFile()+"]\n"); + textOutput.append("[dir=" + fd.getDirectory()+"]\n"); + textOutput.setCaretPosition(textOutput.getText().length()); + + }); + frame.pack(); + return frame; + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + String instructions = """ + 1) The test shows the 'File Dialog Return Test Frame' frame + that contains two text fields, one button and an output area. + 2) Input something into the 'File:' text field or just keep the field empty. + 3) Input something into the 'Dir:' text field or just keep the field empty. + 4) Press the 'Show' button and a file dialog will appear. + 5-1) Cancel the file dialog, e.g. by selecting the 'close' menu item. + If the output window shows that 'file'/'dir' values are null + then the test passes, otherwise the test fails. + 5-2) Select any file by double clicking on it. + If the output window shows that 'file'/'dir' values are not-null + then the test passes, otherwise the test fails. + """; + + String toolkit = Toolkit.getDefaultToolkit().getClass().getName(); + if (!toolkit.equals("sun.awt.X11.XToolkit")) { + throw new SkippedException("Test is not designed for toolkit " + toolkit); + } + + PassFailJFrame.builder() + .title("File Dialog Return Test Instructions") + .instructions(instructions) + .rows((int) instructions.lines().count() + 1) + .columns(50) + .testUI(FileDialogReturnTest::initialize) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/FileDialog/FileDialogReturnTest/FileDialogReturnTest.html b/test/jdk/java/awt/FileDialog/FileDialogReturnTest/FileDialogReturnTest.html deleted file mode 100644 index 68b19739a675b..0000000000000 --- a/test/jdk/java/awt/FileDialog/FileDialogReturnTest/FileDialogReturnTest.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - FileDialogReturnTest - - - -

    FileDialogReturnTest
    Bug ID:

    - -

    See the dialog box (usually in upper left corner) for instructions

    - - - - diff --git a/test/jdk/java/awt/FileDialog/FileDialogReturnTest/FileDialogReturnTest.java b/test/jdk/java/awt/FileDialog/FileDialogReturnTest/FileDialogReturnTest.java deleted file mode 100644 index 8853f76fc8d5f..0000000000000 --- a/test/jdk/java/awt/FileDialog/FileDialogReturnTest/FileDialogReturnTest.java +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - test - @bug 6260676 - @summary FileDialog.setDirectory() does not work properly, XToolkit - @author Dmitry.Cherepanov area=awt.filedialog - @run applet/manual=yesno FileDialogReturnTest.html -*/ - -import java.applet.Applet; -import java.awt.*; -import java.awt.event.*; - -/* - * Current implementation of the FileDialog class doesn't provide - * any explicit method to get the return value after the user closes - * the dialog. The only way to detect whether the user cancels the - * dialog or the user selects any file is to use the getFile() method. - * The getFile() method should return null value if the user cancels - * the dialog or non-null value if the user selects any file. - */ -public class FileDialogReturnTest extends Applet -{ - - public static void main(String[] args) { - Applet a = new FileDialogReturnTest(); - a.init(); - a.start(); - } - - public void init() - { - this.setLayout (new BorderLayout ()); - - String[] instructions = - { - " 1. The test shows the 'FileDialogReturnTest' applet which contains two text fields and one button, ", - " 2. Input something into the 'File:' text field or just keep the field empty, ", - " 3. Input something into the 'Dir:' text field or just keep the field empty, ", - " 4. Press the 'Show' button and a file dialog will appear, ", - " 5-1. Cancel the file dialog, e.g. by selecting the 'close' menu item, ", - " If the output window shows that 'file'/'dir' values is null then the test passes, otherwise the test fails, ", - " 5-2. Select any file, e.g. by pressing the 'OK' button, ", - " If the output window shows that 'file'/'dir' values is not-null then the test passes, otherwise the test fails. " - }; - Sysout.createDialogWithInstructions( instructions ); - - }//End init() - - final TextField fileField = new TextField("", 20); - final TextField dirField = new TextField("", 20); - final Button button = new Button("Show"); - - public void start () - { - setLayout(new FlowLayout()); - - add(new Label("File:")); - add(fileField); - add(new Label("Dir:")); - add(dirField); - add(button); - - button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - showDialog(); - } - }); - - setSize (200,200); - setVisible(true); - validate(); - } - - void showDialog() - { - FileDialog fd = new FileDialog(new Frame()); - fd.setFile(fileField.getText()); - fd.setDirectory(dirField.getText()); - fd.setVisible(true); - - Sysout.println("[file=" + fd.getFile()+"]"); - Sysout.println("[dir=" + fd.getDirectory()+"]"); - } - -} - - -/**************************************************** - Standard Test Machinery - DO NOT modify anything below -- it's a standard - chunk of code whose purpose is to make user - interaction uniform, and thereby make it simpler - to read and understand someone else's test. - ****************************************************/ - -/** - This is part of the standard test machinery. - It creates a dialog (with the instructions), and is the interface - for sending text messages to the user. - To print the instructions, send an array of strings to Sysout.createDialog - WithInstructions method. Put one line of instructions per array entry. - To display a message for the tester to see, simply call Sysout.println - with the string to be displayed. - This mimics System.out.println but works within the test harness as well - as standalone. - */ - -class Sysout -{ - private static TestDialog dialog; - - public static void createDialogWithInstructions( String[] instructions ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - dialog.printInstructions( instructions ); - dialog.setVisible(true); - println( "Any messages for the tester will display here." ); - } - - public static void createDialog( ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - String[] defInstr = { "Instructions will appear here. ", "" } ; - dialog.printInstructions( defInstr ); - dialog.setVisible(true); - println( "Any messages for the tester will display here." ); - } - - - public static void printInstructions( String[] instructions ) - { - dialog.printInstructions( instructions ); - } - - - public static void println( String messageIn ) - { - dialog.displayMessage( messageIn ); - } - -}// Sysout class - -/** - This is part of the standard test machinery. It provides a place for the - test instructions to be displayed, and a place for interactive messages - to the user to be displayed. - To have the test instructions displayed, see Sysout. - To have a message to the user be displayed, see Sysout. - Do not call anything in this dialog directly. - */ -class TestDialog extends Dialog -{ - - TextArea instructionsText; - TextArea messageText; - int maxStringLength = 100; - - //DO NOT call this directly, go through Sysout - public TestDialog( Frame frame, String name ) - { - super( frame, name ); - int scrollBoth = TextArea.SCROLLBARS_BOTH; - instructionsText = new TextArea( "", 15, maxStringLength, scrollBoth ); - add( "North", instructionsText ); - - messageText = new TextArea( "", 5, maxStringLength, scrollBoth ); - add("Center", messageText); - - pack(); - - setVisible(true); - }// TestDialog() - - //DO NOT call this directly, go through Sysout - public void printInstructions( String[] instructions ) - { - //Clear out any current instructions - instructionsText.setText( "" ); - - //Go down array of instruction strings - - String printStr, remainingStr; - for( int i=0; i < instructions.length; i++ ) - { - //chop up each into pieces maxSringLength long - remainingStr = instructions[ i ]; - while( remainingStr.length() > 0 ) - { - //if longer than max then chop off first max chars to print - if( remainingStr.length() >= maxStringLength ) - { - //Try to chop on a word boundary - int posOfSpace = remainingStr. - lastIndexOf( ' ', maxStringLength - 1 ); - - if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1; - - printStr = remainingStr.substring( 0, posOfSpace + 1 ); - remainingStr = remainingStr.substring( posOfSpace + 1 ); - } - //else just print - else - { - printStr = remainingStr; - remainingStr = ""; - } - - instructionsText.append( printStr + "\n" ); - - }// while - - }// for - - }//printInstructions() - - //DO NOT call this directly, go through Sysout - public void displayMessage( String messageIn ) - { - messageText.append( messageIn + "\n" ); - System.out.println(messageIn); - } - -}// TestDialog class diff --git a/test/jdk/java/awt/FileDialog/FileNameOverrideTest.java b/test/jdk/java/awt/FileDialog/FileNameOverrideTest.java new file mode 100644 index 0000000000000..3042abc257c73 --- /dev/null +++ b/test/jdk/java/awt/FileDialog/FileNameOverrideTest.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.FileDialog; +import java.awt.Frame; +import java.awt.Toolkit; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import javax.swing.JButton; +import jtreg.SkippedException; + +/* + * @test + * @bug 6260659 + * @summary File Name set programmatically in FileDialog is overridden during navigation, XToolkit + * @requires (os.family == "linux") + * @library /java/awt/regtesthelpers + * @library /test/lib + * @build PassFailJFrame + * @build jtreg.SkippedException + * @run main/manual FileNameOverrideTest + */ + +public class FileNameOverrideTest { + private final static String clickDirName = "Directory for double click"; + + private static JButton initialize() { + final String fileName = "input"; + final String dirPath = System.getProperty("user.dir");; + + JButton showBtn = new JButton("Show File Dialog"); + showBtn.addActionListener(w -> { + FileDialog fd = new FileDialog((Frame) null, "Open"); + fd.setFile(fileName); + fd.setDirectory(dirPath); + fd.setVisible(true); + String output = fd.getFile(); + fd.dispose(); + if (fileName.equals(output)) { + PassFailJFrame.forcePass(); + } else { + PassFailJFrame.forceFail("File name mismatch: " + + fileName + " vs " + output); + } + }); + + try { + File tmpFileUp = new File(dirPath + File.separator + fileName); + File tmpDir = new File(dirPath + File.separator + clickDirName); + File tmpFileIn = new File(tmpDir.getAbsolutePath() + File.separator + fileName); + tmpDir.mkdir(); + tmpFileUp.createNewFile(); + tmpFileIn.createNewFile(); + } catch (IOException ex) { + throw new RuntimeException("Cannot create test folder", ex); + } + + return showBtn; + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + String instructions = """ + 1) Click on 'Show File Dialog' button. A file dialog will come up. + 2) Double-click on '$clickDirName' and click OK or Open + to confirm selection (label on the button depends on the current window manager). + 3) Test will pass or fail automatically. + """.replace("$clickDirName", clickDirName); + + String toolkit = Toolkit.getDefaultToolkit().getClass().getName(); + if (!toolkit.equals("sun.awt.X11.XToolkit")) { + throw new SkippedException("Test is not designed for toolkit " + toolkit); + } + + PassFailJFrame.builder() + .title("File Dialog Return Test Instructions") + .instructions(instructions) + .rows((int) instructions.lines().count() + 1) + .columns(50) + .splitUIBottom(FileNameOverrideTest::initialize) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/FileDialog/FileNameOverrideTest/FileNameOverrideTest.html b/test/jdk/java/awt/FileDialog/FileNameOverrideTest/FileNameOverrideTest.html deleted file mode 100644 index a9c256e0dca2a..0000000000000 --- a/test/jdk/java/awt/FileDialog/FileNameOverrideTest/FileNameOverrideTest.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - FileNameOverrideTest - - - -

    FileNameOverrideTest
    Bug ID: 6260659

    - -

    See the dialog box (usually in upper left corner) for instructions

    - - - - diff --git a/test/jdk/java/awt/FileDialog/FileNameOverrideTest/FileNameOverrideTest.java b/test/jdk/java/awt/FileDialog/FileNameOverrideTest/FileNameOverrideTest.java deleted file mode 100644 index e3240fd3429a5..0000000000000 --- a/test/jdk/java/awt/FileDialog/FileNameOverrideTest/FileNameOverrideTest.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - test - @bug 6260659 - @summary File Name set programmatically in FileDialog is overridden during navigation, XToolkit - @author Dmitry.Cherepanov@SUN.COM area=awt.filedialog - @library ../../regtesthelpers - @build Sysout - @run applet/manual=yesno FileNameOverrideTest.html -*/ - -import test.java.awt.regtesthelpers.Sysout; - -import java.applet.Applet; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.File; -import java.io.IOException; - -public class FileNameOverrideTest extends Applet implements ActionListener { - private final static String fileName = "input"; - private final static String clickDirName = "Directory for double click"; - private final static String dirPath = "."; - private Button showBtn; - private FileDialog fd; - - public void init() { - this.setLayout(new GridLayout(1, 1)); - - fd = new FileDialog(new Frame(), "Open"); - - showBtn = new Button("Show File Dialog"); - showBtn.addActionListener(this); - add(showBtn); - - try { - File tmpFileUp = new File(dirPath + File.separator + fileName); - File tmpDir = new File(dirPath + File.separator + clickDirName); - File tmpFileIn = new File(tmpDir.getAbsolutePath() + File.separator + fileName); - tmpDir.mkdir(); - tmpFileUp.createNewFile(); - tmpFileIn.createNewFile(); - } catch (IOException ex) { - throw new RuntimeException("Cannot create test folder", ex); - } - - String[] instructions = { - "1) Click on 'Show File Dialog' button. A file dialog will come up.", - "2) Double-click on '" + clickDirName + "' and click OK.", - "3) See result of the test below" - }; - Sysout.createDialogWithInstructions(instructions); - }//End init() - - public void start() { - setSize(200, 200); - show(); - }// start() - - public void actionPerformed(ActionEvent e) { - if (e.getSource() == showBtn) { - fd.setFile(fileName); - fd.setDirectory(dirPath); - fd.setVisible(true); - String output = fd.getFile(); - if (fileName.equals(output)) { - Sysout.println("TEST PASSED"); - } else { - Sysout.println("TEST FAILED (output file - " + output + ")"); - } - } - } -}// class ManualYesNoTest diff --git a/test/jdk/java/awt/FileDialog/KeyboardInteractionTest.java b/test/jdk/java/awt/FileDialog/KeyboardInteractionTest.java new file mode 100644 index 0000000000000..f919a8a338af8 --- /dev/null +++ b/test/jdk/java/awt/FileDialog/KeyboardInteractionTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.FileDialog; +import java.awt.Frame; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 6259434 + * @summary PIT: Choice in FileDialog is not responding to keyboard interactions, XToolkit + * @requires (os.family == "linux") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual KeyboardInteractionTest + */ + +public class KeyboardInteractionTest { + public static void main(String[] args) throws Exception { + System.setProperty("sun.awt.disableGtkFileDialogs", "true"); + String INSTRUCTIONS = """ + 1) Click on 'Show File Dialog' button to bring up the FileDialog window. + A file dialog will come up. + 2) You will see a text field 'Enter full path or filename'. + Right next to it, you will see a button. + Transfer the focus on this button using 'TAB'. + Make sure that the popup choice is not shown. + 3) Press 'ESC'. If file dialog isn't disposed, then the test failed. + 4) Again, click on 'Show File Dialog' to bring up the file dialog. + A file dialog will come up. + 5) You will see a text field 'Enter full path or filename'. + Right next to it, you will see a button. + Click on this button. The popup choice will appear. + 6) Look at the popup choice. Change the current item in the popup + choice by the arrow keys. + If the text in the 'Enter full path or filename' text field isn't + changed, then the test failed. + 7) The test passed. + """; + + PassFailJFrame.builder() + .title("KeyboardInteractionTest Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(KeyboardInteractionTest::createUI) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame f = new Frame("KeyboardInteractionTest Test"); + Button b = new Button("Show File Dialog"); + FileDialog fd = new FileDialog(f); + b.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + fd.setVisible(true); + } + }); + f.add(b); + f.setSize(300, 200); + return f; + } +} diff --git a/test/jdk/java/awt/FileDialog/MultipleMode.java b/test/jdk/java/awt/FileDialog/MultipleMode.java new file mode 100644 index 0000000000000..da26f4a20d918 --- /dev/null +++ b/test/jdk/java/awt/FileDialog/MultipleMode.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Checkbox; +import java.awt.FileDialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.TextArea; +import java.io.File; + +/* + * @test + * @bug 6467204 + * @summary Need to implement "extended" native FileDialog for JFileChooser + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MultipleMode +*/ + +public class MultipleMode { + + private static final String INSTRUCTIONS = + """ + 1. Verify that the 'multiple' checkbox is off and press the 'open' button + 2. Verify that the file dialog doesn't allow the multiple file selection + 3. Select any file and close the file dialog + 4. The results will be displayed, verify the results + 5. Turn the 'multiple' checkbox on and press the 'open' button + 6. Verify that the file dialog allows the multiple file selection + 7. Select several files and close the file dialog + 8. The results will be displayed, verify the results. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame + .builder() + .title("MultipleMode test instructions") + .instructions(INSTRUCTIONS) + .rows(15) + .columns(40) + .position(PassFailJFrame.Position.TOP_LEFT_CORNER) + .testUI(MultipleMode::init) + .build() + .awaitAndCheck(); + } + + private static Frame init() { + Frame frame = new Frame("MultipleMode"); + TextArea sysout = new TextArea("", 20, 70); + sysout.setEditable(false); + + final Checkbox mode = new Checkbox("multiple", false); + + Button open = new Button("open"); + open.addActionListener(e -> { + FileDialog d = new FileDialog(frame); + d.setMultipleMode(mode.getState()); + d.setVisible(true); + + // print the results + sysout.append("DIR:\n"); + sysout.append(" %s\n".formatted(d.getDirectory())); + sysout.append("FILE:\n"); + sysout.append(" %s\n".formatted(d.getFile())); + sysout.append("FILES:\n"); + for (File f : d.getFiles()) { + sysout.append(" %s\n".formatted(f)); + } + }); + + Panel panel = new Panel(new FlowLayout()); + panel.add(mode); + panel.add(open); + + frame.setLayout(new BorderLayout()); + frame.add(panel, BorderLayout.NORTH); + frame.add(sysout, BorderLayout.CENTER); + + frame.pack(); + + return frame; + } +} diff --git a/test/jdk/java/awt/FileDialog/MultipleMode/MultipleMode.html b/test/jdk/java/awt/FileDialog/MultipleMode/MultipleMode.html deleted file mode 100644 index 16118014e23b2..0000000000000 --- a/test/jdk/java/awt/FileDialog/MultipleMode/MultipleMode.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - MultipleMode - - - -

    MultipleMode
    Bug ID: 6467204

    - -

    See the dialog box (usually in upper left corner) for instructions

    - - - - diff --git a/test/jdk/java/awt/FileDialog/MultipleMode/MultipleMode.java b/test/jdk/java/awt/FileDialog/MultipleMode/MultipleMode.java deleted file mode 100644 index e0e7bdf83c774..0000000000000 --- a/test/jdk/java/awt/FileDialog/MultipleMode/MultipleMode.java +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - test - @bug 6467204 - @summary Need to implement "extended" native FileDialog for JFileChooser - @author dmitry.cherepanov@sun.com area=awt.filedialog - @run applet/manual=yesno MultipleMode.html -*/ - -// Note there is no @ in front of test above. This is so that the -// harness will not mistake this file as a test file. It should -// only see the html file as a test file. (the harness runs all -// valid test files, so it would run this test twice if this file -// were valid as well as the html file.) -// Also, note the area= after Your Name in the author tag. Here, you -// should put which functional area the test falls in. See the -// AWT-core home page -> test areas and/or -> AWT team for a list of -// areas. -// There are several places where ManualYesNoTest appear. It is -// recommended that these be changed by a global search and replace, -// such as ESC-% in xemacs. - - - -/** - * MultipleMode.java - * - * summary: - */ - -import java.applet.Applet; -import java.awt.*; -import java.awt.event.*; -import java.io.File; - - -//Manual tests should run as applet tests if possible because they -// get their environments cleaned up, including AWT threads, any -// test created threads, and any system resources used by the test -// such as file descriptors. (This is normally not a problem as -// main tests usually run in a separate VM, however on some platforms -// such as the Mac, separate VMs are not possible and non-applet -// tests will cause problems). Also, you don't have to worry about -// synchronisation stuff in Applet tests the way you do in main -// tests... - - -public class MultipleMode extends Applet -{ - //Declare things used in the test, like buttons and labels here - - public void init() - { - //Create instructions for the user here, as well as set up - // the environment -- set the layout manager, add buttons, - // etc. - this.setLayout (new BorderLayout ()); - - String[] instructions = - { - " 1. Turn the 'multiple' checkbox off and press the 'open' button ", - " 2. Verify that the file dialog doesn't allow the multiple file selection ", - " 3. Select any file and close the file dialog ", - " 4. The results will be displayed, verify the results ", - " 5. Turn the 'multiple' checkbox on and press the 'open' button ", - " 6. Verify that the file dialog allows the multiple file selection ", - " 7. Select several files and close the file dialog ", - " 8. The results will be displayed, verify the results " - }; - Sysout.createDialogWithInstructions( instructions ); - - }//End init() - - public void start () - { - final Checkbox mode = new Checkbox("multiple", true); - Button open = new Button("open"); - open.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - FileDialog d = new FileDialog((Frame)null); - d.setMultipleMode(mode.getState()); - d.setVisible(true); - - // print the results - Sysout.println("DIR:"); - Sysout.println(d.getDirectory()); - Sysout.println("FILE:"); - Sysout.println(d.getFile()); - Sysout.println("FILES:"); - File files[] = d.getFiles(); - for (File f : files) { - Sysout.println(String.valueOf(f)); - } - } - }); - - setLayout(new FlowLayout()); - add(mode); - add(open); - - //Get things going. Request focus, set size, et cetera - setSize (200,200); - setVisible(true); - validate(); - - }// start() - - //The rest of this class is the actions which perform the test... - - //Use Sysout.println to communicate with the user NOT System.out!! - //Sysout.println ("Something Happened!"); - -}// class ManualYesNoTest - -/* Place other classes related to the test after this line */ - - - - - -/**************************************************** - Standard Test Machinery - DO NOT modify anything below -- it's a standard - chunk of code whose purpose is to make user - interaction uniform, and thereby make it simpler - to read and understand someone else's test. - ****************************************************/ - -/** - This is part of the standard test machinery. - It creates a dialog (with the instructions), and is the interface - for sending text messages to the user. - To print the instructions, send an array of strings to Sysout.createDialog - WithInstructions method. Put one line of instructions per array entry. - To display a message for the tester to see, simply call Sysout.println - with the string to be displayed. - This mimics System.out.println but works within the test harness as well - as standalone. - */ - -class Sysout -{ - private static TestDialog dialog; - private static boolean numbering = false; - private static int messageNumber = 0; - - public static void createDialogWithInstructions( String[] instructions ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - dialog.printInstructions( instructions ); - dialog.setVisible(true); - println( "Any messages for the tester will display here." ); - } - - public static void createDialog( ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - String[] defInstr = { "Instructions will appear here. ", "" } ; - dialog.printInstructions( defInstr ); - dialog.setVisible(true); - println( "Any messages for the tester will display here." ); - } - - /* Enables message counting for the tester. */ - public static void enableNumbering(boolean enable){ - numbering = enable; - } - - public static void printInstructions( String[] instructions ) - { - dialog.printInstructions( instructions ); - } - - - public static void println( String messageIn ) - { - if (numbering) { - messageIn = "" + messageNumber + " " + messageIn; - messageNumber++; - } - dialog.displayMessage( messageIn ); - } - -}// Sysout class - -/** - This is part of the standard test machinery. It provides a place for the - test instructions to be displayed, and a place for interactive messages - to the user to be displayed. - To have the test instructions displayed, see Sysout. - To have a message to the user be displayed, see Sysout. - Do not call anything in this dialog directly. - */ -class TestDialog extends Dialog -{ - - TextArea instructionsText; - TextArea messageText; - int maxStringLength = 80; - - //DO NOT call this directly, go through Sysout - public TestDialog( Frame frame, String name ) - { - super( frame, name ); - int scrollBoth = TextArea.SCROLLBARS_BOTH; - instructionsText = new TextArea( "", 15, maxStringLength, scrollBoth ); - add( "North", instructionsText ); - - messageText = new TextArea( "", 5, maxStringLength, scrollBoth ); - add("Center", messageText); - - pack(); - - setVisible(true); - }// TestDialog() - - //DO NOT call this directly, go through Sysout - public void printInstructions( String[] instructions ) - { - //Clear out any current instructions - instructionsText.setText( "" ); - - //Go down array of instruction strings - - String printStr, remainingStr; - for( int i=0; i < instructions.length; i++ ) - { - //chop up each into pieces maxSringLength long - remainingStr = instructions[ i ]; - while( remainingStr.length() > 0 ) - { - //if longer than max then chop off first max chars to print - if( remainingStr.length() >= maxStringLength ) - { - //Try to chop on a word boundary - int posOfSpace = remainingStr. - lastIndexOf( ' ', maxStringLength - 1 ); - - if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1; - - printStr = remainingStr.substring( 0, posOfSpace + 1 ); - remainingStr = remainingStr.substring( posOfSpace + 1 ); - } - //else just print - else - { - printStr = remainingStr; - remainingStr = ""; - } - - instructionsText.append( printStr + "\n" ); - - }// while - - }// for - - }//printInstructions() - - //DO NOT call this directly, go through Sysout - public void displayMessage( String messageIn ) - { - messageText.append( messageIn + "\n" ); - System.out.println(messageIn); - } - -}// TestDialog class diff --git a/test/jdk/java/awt/FileDialog/PathChoiceDisposeTest.java b/test/jdk/java/awt/FileDialog/PathChoiceDisposeTest.java new file mode 100644 index 0000000000000..267b2a807cff8 --- /dev/null +++ b/test/jdk/java/awt/FileDialog/PathChoiceDisposeTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.FileDialog; +import java.awt.Frame; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 6240084 + * @summary Test that disposing unfurled list by the pressing ESC + * in FileDialog is working properly on XToolkit + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual PathChoiceDisposeTest + */ + +public class PathChoiceDisposeTest { + public static void main(String[] args) throws Exception { + System.setProperty("sun.awt.disableGtkFileDialogs", "true"); + String INSTRUCTIONS = """ + 1) Click on 'Show File Dialog' button to bring up the FileDialog window. + 2) Open the directory selection choice by clicking button next to + 'Enter Path or Folder Name'. A drop-down will appear. + 3) Press 'ESC'. + 4) If you see that the dialog gets disposed and the popup + still remains on the screen, the test failed, otherwise passed. + """; + + PassFailJFrame.builder() + .title("PathChoiceDisposeTest Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(PathChoiceDisposeTest::createUI) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame f = new Frame("PathChoiceDisposeTest Test"); + Button b = new Button("Show File Dialog"); + FileDialog fd = new FileDialog(f); + b.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + fd.setVisible(true); + } + }); + f.add(b); + f.setSize(300, 200); + return f; + } +} diff --git a/test/jdk/java/awt/FileDialog/PathChoiceWorkArrowsTest.java b/test/jdk/java/awt/FileDialog/PathChoiceWorkArrowsTest.java new file mode 100644 index 0000000000000..17aee8abb800a --- /dev/null +++ b/test/jdk/java/awt/FileDialog/PathChoiceWorkArrowsTest.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.FileDialog; +import java.awt.Frame; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 6240074 + * @summary Test that file drop-down field in FileDialog is working properly on XToolkit + * @requires (os.family == "linux") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual PathChoiceWorkArrowsTest + */ + +public class PathChoiceWorkArrowsTest { + public static void main(String[] args) throws Exception { + System.setProperty("sun.awt.disableGtkFileDialogs", "true"); + String INSTRUCTIONS = """ + This is only XAWT test. + + 1) Click on 'Show File Dialog' to bring up the FileDialog window. + A file dialog would come up. + 2) Click on the button next to 'Enter folder name' field. + A drop-down will appear. After this, there are 2 scenarios. + 3) Press the down arrow one by one. You will see a '/' being + appended as soon as the current entry is removed. + Keep pressing till the last entry is reached. Now the drop-down + will stop responding to arrow keys. If yes, the test failed. + 4) Press the up arrow. The cursor will directly go to the last + entry ('/') and navigation will stop there. You will see 2 + entries being selected at the same time. + If yes, the test failed. + """; + + PassFailJFrame.builder() + .title("PathChoiceWorkArrowsTest Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(PathChoiceWorkArrowsTest::createUI) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame f = new Frame("PathChoiceWorkArrowsTest Test"); + Button b = new Button("Show File Dialog"); + FileDialog fd = new FileDialog(f); + b.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + fd.setSize(200, 200); + fd.setLocation(200, 200); + fd.setVisible(true); + } + }); + f.add(b); + f.setSize(300, 200); + return f; + } +} diff --git a/test/jdk/java/awt/FileDialog/RegexpFilterTest.java b/test/jdk/java/awt/FileDialog/RegexpFilterTest.java new file mode 100644 index 0000000000000..af539735b7764 --- /dev/null +++ b/test/jdk/java/awt/FileDialog/RegexpFilterTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.JButton; +import java.awt.Frame; +import java.awt.FileDialog; + +/* + * Motif file dialogs let the user specify a filter that controls the files that + * are displayed in the dialog. This filter is generally specified as a regular + * expression. The test verifies that Motif-like filtering works fine using + * XAWT-toolkit also. + */ + +/* + * @test + * @bug 4934185 + * @summary JCK1.5-runtime-interactive: XToolkit FileDialog does not work as expected + * @requires (os.family == "linux") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/othervm/manual -Dsun.awt.disableGtkFileDialogs=true RegexpFilterTest +*/ +public class RegexpFilterTest { + + private static final String INSTRUCTIONS = + """ + 0. The test is only for X platforms + 1. Press the 'Show' button and a file dialog will appear, + 2. Input any string into the 'Filter' text field, + This filter is generally specified as a regular expression + (e.g., * matches all files, while *.c matches all files that end in .c) + 3. Press 'Enter' to refresh the displayed files with the filter, + 4. If the list of the files contains all actual files matched the filter, + then the test passed; otherwise it failed. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame + .builder() + .title("RegexpFilterTest Instructions") + .instructions(INSTRUCTIONS) + .splitUIRight(() -> { + JButton show = new JButton("show"); + show.addActionListener(e -> + new FileDialog((Frame) null).setVisible(true)); + return show; + }) + .rows(15) + .columns(40) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/FileDialog/RegexpFilterTest/RegexpFilterTest.html b/test/jdk/java/awt/FileDialog/RegexpFilterTest/RegexpFilterTest.html deleted file mode 100644 index 9b1adabfa9b87..0000000000000 --- a/test/jdk/java/awt/FileDialog/RegexpFilterTest/RegexpFilterTest.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - RegexpFilterTest - - - -


    Bug ID:

    - -

    See the dialog box (usually in upper left corner) for instructions

    - - - - diff --git a/test/jdk/java/awt/FileDialog/RegexpFilterTest/RegexpFilterTest.java b/test/jdk/java/awt/FileDialog/RegexpFilterTest/RegexpFilterTest.java deleted file mode 100644 index 01b726bac8850..0000000000000 --- a/test/jdk/java/awt/FileDialog/RegexpFilterTest/RegexpFilterTest.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - test - @bug 4934185 - @summary JCK1.5-runtime-interactive: XToolkit FileDialog does not work as expected - @author Dmitry.Cherepanov area=awt.filedialog - @run applet/manual=yesno RegexpFilterTest.html -*/ - -import java.applet.Applet; -import java.awt.*; -import java.awt.event.*; - -/* - * Motif file dialogs let the user specify a filter that controls the files that - * are displayed in the dialog. This filter is generally specified as a regular - * expression. The test verifies that Motif-like filtering works fine using - * XAWT-toolkit also. - */ -public class RegexpFilterTest extends Applet -{ - - public static void main(String[] args) { - Applet a = new RegexpFilterTest(); - a.init(); - a.start(); - } - - public void init() - { - this.setLayout (new BorderLayout ()); - - String[] instructions = - { - " 0. The test is only for X platforms", - " 1. Press the 'Show' button and a file dialog will appear, ", - " 2. Input any string into the 'Filter' text field, ", - " This filter is generally specified as a regular expression ", - " (e.g., * matches all files, while *.c matches all files that end in .c) ", - " 3. Press 'Enter' to refresh the displayed files with the filter, ", - " 4. If the list of the files contains all actual files matched the filter, ", - " then the test passed; otherwise it failed. " - }; - Sysout.createDialogWithInstructions( instructions ); - - }//End init() - - public void start () - { - setLayout(new FlowLayout()); - Button button = new Button("Show"); - add(button); - - button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - FileDialog fd = new FileDialog(new Frame()); - fd.setVisible(true); - } - }); - - setSize (200,200); - setVisible(true); - validate(); - } - -} - - -/**************************************************** - Standard Test Machinery - DO NOT modify anything below -- it's a standard - chunk of code whose purpose is to make user - interaction uniform, and thereby make it simpler - to read and understand someone else's test. - ****************************************************/ - -/** - This is part of the standard test machinery. - It creates a dialog (with the instructions), and is the interface - for sending text messages to the user. - To print the instructions, send an array of strings to Sysout.createDialog - WithInstructions method. Put one line of instructions per array entry. - To display a message for the tester to see, simply call Sysout.println - with the string to be displayed. - This mimics System.out.println but works within the test harness as well - as standalone. - */ - -class Sysout -{ - private static TestDialog dialog; - - public static void createDialogWithInstructions( String[] instructions ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - dialog.printInstructions( instructions ); - dialog.setVisible(true); - println( "Any messages for the tester will display here." ); - } - - public static void createDialog( ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - String[] defInstr = { "Instructions will appear here. ", "" } ; - dialog.printInstructions( defInstr ); - dialog.setVisible(true); - println( "Any messages for the tester will display here." ); - } - - - public static void printInstructions( String[] instructions ) - { - dialog.printInstructions( instructions ); - } - - - public static void println( String messageIn ) - { - dialog.displayMessage( messageIn ); - } - -}// Sysout class - -/** - This is part of the standard test machinery. It provides a place for the - test instructions to be displayed, and a place for interactive messages - to the user to be displayed. - To have the test instructions displayed, see Sysout. - To have a message to the user be displayed, see Sysout. - Do not call anything in this dialog directly. - */ -class TestDialog extends Dialog -{ - - TextArea instructionsText; - TextArea messageText; - int maxStringLength = 100; - - //DO NOT call this directly, go through Sysout - public TestDialog( Frame frame, String name ) - { - super( frame, name ); - int scrollBoth = TextArea.SCROLLBARS_BOTH; - instructionsText = new TextArea( "", 15, maxStringLength, scrollBoth ); - add( "North", instructionsText ); - - messageText = new TextArea( "", 5, maxStringLength, scrollBoth ); - add("Center", messageText); - - pack(); - - setVisible(true); - }// TestDialog() - - //DO NOT call this directly, go through Sysout - public void printInstructions( String[] instructions ) - { - //Clear out any current instructions - instructionsText.setText( "" ); - - //Go down array of instruction strings - - String printStr, remainingStr; - for( int i=0; i < instructions.length; i++ ) - { - //chop up each into pieces maxSringLength long - remainingStr = instructions[ i ]; - while( remainingStr.length() > 0 ) - { - //if longer than max then chop off first max chars to print - if( remainingStr.length() >= maxStringLength ) - { - //Try to chop on a word boundary - int posOfSpace = remainingStr. - lastIndexOf( ' ', maxStringLength - 1 ); - - if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1; - - printStr = remainingStr.substring( 0, posOfSpace + 1 ); - remainingStr = remainingStr.substring( posOfSpace + 1 ); - } - //else just print - else - { - printStr = remainingStr; - remainingStr = ""; - } - - instructionsText.append( printStr + "\n" ); - - }// while - - }// for - - }//printInstructions() - - //DO NOT call this directly, go through Sysout - public void displayMessage( String messageIn ) - { - messageText.append( messageIn + "\n" ); - System.out.println(messageIn); - } - -}// TestDialog class diff --git a/test/jdk/java/awt/FileDialog/SaveFileNameOverrideTest.java b/test/jdk/java/awt/FileDialog/SaveFileNameOverrideTest.java new file mode 100644 index 0000000000000..fc88ffa879ecc --- /dev/null +++ b/test/jdk/java/awt/FileDialog/SaveFileNameOverrideTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.JButton; +import java.awt.FileDialog; +import java.awt.Frame; +import java.io.File; + +/* + * @test + * @bug 6998877 8022531 + * @summary After double-click on the folder names, FileNameOverrideTest FAILED + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual SaveFileNameOverrideTest + */ + +public class SaveFileNameOverrideTest { + + private final static String clickDirName = "Directory for double click"; + private static final String INSTRUCTIONS = + """ + 1) Click on the 'Show File Dialog' button. A file dialog will appear. + 2) Double-click on '%s' + 3) Click on a confirmation button. + (It can be 'OK', 'Save' or any other name depending on the platform). + 3) The test will automatically pass or fail. + """ + .formatted(clickDirName); + private final static String dirPath = "."; + + public static void main(String[] args) throws Exception { + System.out.println(System.getProperties()); + System.out.println(new File(dirPath).getAbsolutePath()); + File tmpDir = new File(dirPath + File.separator + clickDirName); + if (!tmpDir.mkdir()) { + throw new RuntimeException("Cannot create directory."); + } + tmpDir.deleteOnExit(); + + PassFailJFrame + .builder() + .title("SaveFileNameOverrideTest Instructions") + .instructions(INSTRUCTIONS) + .splitUIRight(SaveFileNameOverrideTest::getButton) + .rows(8) + .columns(40) + .build() + .awaitAndCheck(); + } + + public static JButton getButton() { + JButton showBtn = new JButton("Show File Dialog"); + showBtn.addActionListener(e -> { + FileDialog fd = + new FileDialog((Frame) null, "Save", FileDialog.SAVE); + + fd.setFile("input"); + fd.setDirectory(new File(dirPath).getAbsolutePath()); + fd.setVisible(true); + + String output = fd.getFile(); + if ("input".equals(output)) { + PassFailJFrame.forcePass(); + } else { + PassFailJFrame.forceFail("TEST FAILED (output file - " + output + ")"); + } + }); + return showBtn; + } +} diff --git a/test/jdk/java/awt/FileDialog/SaveFileNameOverrideTest/SaveFileNameOverrideTest.html b/test/jdk/java/awt/FileDialog/SaveFileNameOverrideTest/SaveFileNameOverrideTest.html deleted file mode 100644 index 0a0d48e48c65a..0000000000000 --- a/test/jdk/java/awt/FileDialog/SaveFileNameOverrideTest/SaveFileNameOverrideTest.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - SaveFileNameOverrideTest - - - -

    SaveFileNameOverrideTest
    Bug ID: 6260659

    - -

    See the dialog box (usually in upper left corner) for instructions

    - - - - diff --git a/test/jdk/java/awt/FileDialog/SaveFileNameOverrideTest/SaveFileNameOverrideTest.java b/test/jdk/java/awt/FileDialog/SaveFileNameOverrideTest/SaveFileNameOverrideTest.java deleted file mode 100644 index 79b5a3a6de0d1..0000000000000 --- a/test/jdk/java/awt/FileDialog/SaveFileNameOverrideTest/SaveFileNameOverrideTest.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - test - @bug 6998877 8022531 - @summary After double-click on the folder names, FileNameOverrideTest FAILED - @author Sergey.Bylokhov@oracle.com area=awt.filedialog - @library ../../regtesthelpers - @build Sysout - @run applet/manual=yesno SaveFileNameOverrideTest.html -*/ - -import test.java.awt.regtesthelpers.Sysout; - -import java.applet.Applet; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.File; - -public class SaveFileNameOverrideTest extends Applet implements ActionListener { - private final static String clickDirName = "Directory for double click"; - private final static String dirPath = "."; - private Button showBtn; - private FileDialog fd; - - public void init() { - this.setLayout(new GridLayout(1, 1)); - - fd = new FileDialog(new Frame(), "Save", FileDialog.SAVE); - - showBtn = new Button("Show File Dialog"); - showBtn.addActionListener(this); - add(showBtn); - - File tmpDir = new File(dirPath + File.separator + clickDirName); - tmpDir.mkdir(); - - String[] instructions = { - "1) Click on 'Show File Dialog' button. A file dialog will come up.", - "2) Double-click on '" + clickDirName + "' and click a confirmation", - " button, it can be 'OK', 'Save' or any other platform-dependent name.", - "3) See result of the test below" - }; - - Sysout.createDialogWithInstructions(instructions); - - }//End init() - - public void start() { - setSize(200, 200); - show(); - }// start() - - public void actionPerformed(ActionEvent e) { - if (e.getSource() == showBtn) { - fd.setFile("input"); - fd.setDirectory(dirPath); - fd.setVisible(true); - String output = fd.getFile(); - if ("input".equals(output)) { - Sysout.println("TEST PASSED"); - } else { - Sysout.println("TEST FAILED (output file - " + output + ")"); - } - } - } -}// class ManualYesNoTest diff --git a/test/jdk/java/awt/FileDialog/SavedDirInitTest.java b/test/jdk/java/awt/FileDialog/SavedDirInitTest.java new file mode 100644 index 0000000000000..7a3b33f55fe16 --- /dev/null +++ b/test/jdk/java/awt/FileDialog/SavedDirInitTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.FileDialog; +import java.awt.Frame; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 6260650 + * @summary FileDialog.getDirectory() does not return null when file dialog is cancelled + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual SavedDirInitTest + */ + +public class SavedDirInitTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Click on 'Show File Dialog' button to bring up the FileDialog window. + 1) A file dialog will come up. + 2) Press 'Cancel' button to cancel the file dialog. + 3) The result (passed or failed) will be shown in the message window below. + """; + + PassFailJFrame.builder() + .title("SavedDirInitTest Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(SavedDirInitTest::createUI) + .logArea(2) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame f = new Frame("SavedDirInitTest Test"); + Button b = new Button("Show File Dialog"); + FileDialog fd = new FileDialog(f); + b.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + fd.setVisible(true); + if (fd.getDirectory() == null && fd.getFile() == null) { + PassFailJFrame.log("TEST PASSED"); + } else { + PassFailJFrame.log("TEST FAILED. dir = " + fd.getDirectory() + + " , file = " + fd.getFile()); + } + } + }); + f.add(b); + f.setSize(300, 200); + return f; + } +} diff --git a/test/jdk/java/awt/FileDialog/TestFileDialogDupJNIRef.java b/test/jdk/java/awt/FileDialog/TestFileDialogDupJNIRef.java new file mode 100644 index 0000000000000..56b6c49214488 --- /dev/null +++ b/test/jdk/java/awt/FileDialog/TestFileDialogDupJNIRef.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Dialog; +import java.awt.FileDialog; +import java.awt.FlowLayout; +import java.awt.Frame; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 4906972 + * @summary Tests using of JNI reference to peer object. + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual TestFileDialogDupJNIRef + */ + +public class TestFileDialogDupJNIRef { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This is a crash test. + After test started you will see 'Test Frame' with one button. + 1. Click the button to open FileDialog. + 2. Go to the dialog and choose any directory with some files in it.. + 3. Click on any file to highlight it. + 4. Click on the file again to rename. + 5. Leave the file in edit mode and click Open button + + If there was no crash the test passed, Press Pass. + """; + + PassFailJFrame.builder() + .title("TestFileDialogDupJNIRef Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(TestFileDialogDupJNIRef::createUI) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame frame = new Frame("TestFileDialogDupJNIRef Test Frame"); + Button open = new Button("Open File Dialog"); + + open.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + FileDialog fd = new FileDialog(frame); + fd.setVisible(true); + } + }); + + frame.setLayout(new FlowLayout()); + frame.add(open); + frame.setSize(250, 70); + return frame; + } +} diff --git a/test/jdk/java/awt/Focus/ActivateFocusTest.java b/test/jdk/java/awt/Focus/ActivateFocusTest.java new file mode 100644 index 0000000000000..09f5bbb172ca8 --- /dev/null +++ b/test/jdk/java/awt/Focus/ActivateFocusTest.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4369903 + * @summary Focus on window activation does not work correctly + * @key headful + * @run main ActivateFocusTest + */ + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Toolkit; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +public class ActivateFocusTest { + + public static void main(final String[] args) { + ActivateFocusTest app = new ActivateFocusTest(); + app.doTest(); + } + + public void doTest() { + ActivateFocus[] af = new ActivateFocus[2]; + boolean testFailed = false; + Dimension scrSize = Toolkit.getDefaultToolkit().getScreenSize(); + for (int i = 0; i < 2; i++) { + af[i] = new ActivateFocus(i); + af[i].setLocation(i * 160 + scrSize.width / 2, scrSize.height / 2); + af[i].setVisible(true); + } + try { + Thread.sleep(5000); + } catch (InterruptedException ie) { + throw new RuntimeException("TEST FAILED - thread was interrupted"); + } + for (int i = 0; i < 2; i++) { + testFailed = (af[i].lw.focusCounter > 1); + } + if (testFailed) { + throw new RuntimeException("TEST FAILED - focus is gained more than one time"); + } else { + System.out.println("TEST PASSED"); + } + } + + } + +class ActivateFocus extends Frame { + + public LightWeight lw = null; + int num; + + public String toString() { + return ("Window " + num); + } + + public ActivateFocus(int i) { + setTitle("Window " + i); + lw = new LightWeight(i); + num=i; + addWindowListener(new WindowAdapter() { + public void windowActivated(WindowEvent e) { + if(lw != null) { + lw.requestFocus(); + } + } + }); + add(lw); + pack(); + } + + // A very simple lightweight component + class LightWeight extends Component implements FocusListener { + + boolean focused = false; + int num; + public int focusCounter = 0; + + public LightWeight(int num) { + this.num = num; + addFocusListener(this); + } + + public void paint(Graphics g) { + Dimension size = getSize(); + int w = size.width; + int h = size.height; + g.setColor(getBackground()); + g.fillRect(0, 0, w, h); + g.setColor(Color.black); + g.drawOval(0, 0, w-1, h-1); + if (focused) { + g.drawLine(w/2, 0, w/2, h); + g.drawLine(0, h/2, w, h/2); + } + + } + + public Dimension getPreferredSize() { + return new Dimension(150, 150); + } + + public void focusGained(FocusEvent e) { + focused = true; + focusCounter++; + System.out.println("focusGained on " + e.getComponent()); + repaint(); + } + + public void focusLost(FocusEvent e) { + focused = false; + System.out.println("focusLost on " + e.getComponent()); + repaint(); + } + + public String toString() { + return ("Component " + num); + } + } +} diff --git a/test/jdk/java/awt/Focus/ActivateOnProperAppContextTest.java b/test/jdk/java/awt/Focus/ActivateOnProperAppContextTest.java new file mode 100644 index 0000000000000..ab08398332147 --- /dev/null +++ b/test/jdk/java/awt/Focus/ActivateOnProperAppContextTest.java @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2006, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* +* @test +* @bug 6385277 +* @key headful +* @summary Tests that activation happens on correct AppContext. +* @modules java.desktop/sun.awt +* @run main ActivateOnProperAppContextTest +*/ + +import sun.awt.AppContext; +import sun.awt.SunToolkit; + +import java.awt.Button; +import java.awt.Component; +import java.awt.Container; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Point; +import java.awt.Robot; +import java.awt.Toolkit; +import java.awt.Window; +import java.awt.event.InputEvent; +import java.util.concurrent.atomic.AtomicBoolean; + +public class ActivateOnProperAppContextTest { + static Robot robot; + SunToolkit toolkit; + + ThreadGroup threadGroup = new ThreadGroup("Test_Thread_Group"); + AppContext appContext; + Frame frame; + volatile boolean passed = true; + AtomicBoolean cond = new AtomicBoolean(false); + + public static void main(String[] args) throws Exception { + ActivateOnProperAppContextTest app = new ActivateOnProperAppContextTest(); + robot = new Robot(); + app.start(); + } + + public void start() { + toolkit = (SunToolkit)Toolkit.getDefaultToolkit(); + + Runnable runnable = new Runnable() { + public void run() { + test(); + + synchronized (cond) { + cond.set(true); + cond.notifyAll(); + } + } + }; + + Thread thread = new Thread(threadGroup, runnable, "Test Thread"); + + synchronized (cond) { + + thread.start(); + + while (!cond.get()) { + try { + cond.wait(); + } catch (InterruptedException ie) { + ie.printStackTrace(); + } + } + } + + if (passed) { + System.out.println("Test passed."); + } else { + throw new TestFailedException("Test failed!"); + } + } + + void test() { + appContext = SunToolkit.createNewAppContext(); + System.out.println("Created new AppContext: " + appContext); + + frame = new Frame("ActivateOnProperAppContextTest Frame") { + public boolean isActive() { + verifyAppContext("Frame.isActive()"); + return super.isActive(); + } + public boolean isFocused() { + verifyAppContext("Frame.isFocused()"); + return super.isFocused(); + } + public boolean isFocusable() { + verifyAppContext("Frame.isFocusable()"); + return super.isFocusable(); + } + public Window getOwner() { + verifyAppContext("Frame.getOwner()"); + return super.getOwner(); + } + public boolean isEnabled() { + verifyAppContext("Frame.isEnabled()"); + return super.isEnabled(); + } + public boolean isVisible() { + verifyAppContext("Frame.isVisible()"); + return super.isVisible(); + } + public Container getParent() { + verifyAppContext("Frame.getParent()"); + return super.getParent(); + } + public Cursor getCursor() { + verifyAppContext("Frame.getCursor()"); + return super.getCursor(); + } + public Point getLocation() { + verifyAppContext("Frame.getLocation()"); + return super.getLocation(); + } + public Point getLocationOnScreen() { + verifyAppContext("Frame.getLocationOnScreen()"); + return super.getLocationOnScreen(); + } + }; + Window window = new Window(frame) { + public boolean isFocused() { + verifyAppContext("Window.isFocused()"); + return super.isFocused(); + } + public boolean isFocusable() { + verifyAppContext("Window.isFocusable()"); + return super.isFocusable(); + } + public Window getOwner() { + verifyAppContext("Window.getOwner()"); + return super.getOwner(); + } + public boolean isEnabled() { + verifyAppContext("Window.isEnabled()"); + return super.isEnabled(); + } + public boolean isVisible() { + verifyAppContext("Window.isVisible()"); + return super.isVisible(); + } + public Container getParent() { + verifyAppContext("Window.getParent()"); + return super.getParent(); + } + public Cursor getCursor() { + verifyAppContext("Window.getCursor()"); + return super.getCursor(); + } + public Point getLocation() { + verifyAppContext("Window.getLocation()"); + return super.getLocation(); + } + public Point getLocationOnScreen() { + verifyAppContext("Window.getLocationOnScreen()"); + return super.getLocationOnScreen(); + } + }; + Button button = new Button("button"); + Label label = new Label("label"); + + window.setLayout(new FlowLayout()); + window.add(button); + window.add(label); + window.setLocation(800, 0); + window.pack(); + window.setVisible(true); + + frame.setBounds(800, 100, 100, 50); + frame.setVisible(true); + + toolkit.realSync(); + + /* + * When the label is clicked in the window some of + * the owner's public method get called. + */ + clickOn(label); + } + + void verifyAppContext(String methodName) { + AppContext ac = AppContext.getAppContext(); + println(methodName + " called on AppContext: " + ac); + + if (ac != appContext) { + passed = false; + System.err.println("Test failed: " + methodName + " is called on wrong AppContext!"); + Thread.dumpStack(); + } + } + + void clickOn(Component c) { + Point p = c.getLocationOnScreen(); + Dimension d = c.getSize(); + + robot.mouseMove(p.x + (int)(d.getWidth()/2), p.y + (int)(d.getHeight()/2)); + + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.delay(20); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + + toolkit.realSync(); + } + + void println(final String msg) { + SunToolkit.executeOnEventHandlerThread(frame, new Runnable() { + public void run() { + System.out.println(msg); + } + }); + } +} + +class TestFailedException extends RuntimeException { + TestFailedException(String msg) { + super(msg); + } +} diff --git a/test/jdk/java/awt/Focus/AltTabEventsTest.java b/test/jdk/java/awt/Focus/AltTabEventsTest.java new file mode 100644 index 0000000000000..15d679ce7295a --- /dev/null +++ b/test/jdk/java/awt/Focus/AltTabEventsTest.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4524015 + * @summary Tests that when user switches between windows using Alt-tab then the appropriate events are generated + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual AltTabEventsTest + */ + +import java.awt.Button; +import java.awt.Choice; +import java.awt.Component; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.PopupMenu; +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +public class AltTabEventsTest { + + private static final String INSTRUCTIONS = """ + This test verifies that when user switches between windows using Alt-tab + key combination then appropriate window events are generated. Also, when + user interacts with Menu bar, Popup menu, Choice then no excessive window + event is generated. + + After test started you will see Frame('Test for 4524015')-F1 with some + components and Frame('Another frame')-F2 with no components. + 1. Make F1 active by clicking on it. + 2. Press Alt-tab. + In the messqge dialog area you should see that + WINDOW_DEACTIVATED, WINDOW_LOST_FOCUS event were generated. + If you switched to F2 then also WINDOW_ACTIVATED, WINDOW_GAINED_FOCUS + were generated. + If no events were generated the test FAILED. + Repeat the 2) with different circumstances. + + 3. Make F1 active by clicking on it. + 4. Click on Menu bar/Button 'popup'/Choice and select some item from + the list shown. If any of the window events appeared in the output then + the test FAILED. + + else the test PASSED."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("AltTabEventsTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 5) + .columns(35) + .testUI(Test::new) + .logArea() + .build() + .awaitAndCheck(); + } + +} + + +class Test extends Frame { + PopupMenu pop; + Frame f; + + void println(String messageIn) { + PassFailJFrame.log(messageIn); + } + + public Test() { + super("Test for 4524015"); + WindowAdapter wa = new WindowAdapter() { + public void windowActivated(WindowEvent e) { + println(e.toString()); + } + public void windowDeactivated(WindowEvent e) { + println(e.toString()); + } + public void windowGainedFocus(WindowEvent e) { + println(e.toString()); + } + public void windowLostFocus(WindowEvent e) { + println(e.toString()); + } + }; + addWindowListener(wa); + addWindowFocusListener(wa); + + f = new Frame("Another frame"); + f.addWindowListener(wa); + f.addWindowFocusListener(wa); + f.setBounds(800, 300, 300, 100); + f.setVisible(true); + + setLayout(new FlowLayout()); + Button b = new Button("popup"); + add(b); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + pop.show((Component)e.getSource(), 10, 10); + } + }); + Choice cho = new Choice(); + add(cho); + cho.addItem("1"); + cho.addItem("2"); + cho.addItem("3"); + + MenuBar bar = new MenuBar(); + Menu menu = new Menu("menu"); + MenuItem item = new MenuItem("first"); + menu.add(item); + item = new MenuItem("second"); + menu.add(item); + bar.add(menu); + setMenuBar(bar); + + pop = new PopupMenu(); + pop.add("1"); + pop.add("@"); + add(pop); + setSize(300, 100); + } +} + diff --git a/test/jdk/java/awt/Focus/CanvasPanelFocusOnClickTest.java b/test/jdk/java/awt/Focus/CanvasPanelFocusOnClickTest.java new file mode 100644 index 0000000000000..8df21ba0f0a67 --- /dev/null +++ b/test/jdk/java/awt/Focus/CanvasPanelFocusOnClickTest.java @@ -0,0 +1,212 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4041703 4096228 4032657 4066152 4149866 4025223 + * @summary Ensures that an Panel/Canvas without heavyweight children + receives focus on mouse click + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual CanvasPanelFocusOnClickTest + */ + +import java.awt.Button; +import java.awt.Canvas; +import java.awt.Color; +import java.awt.Component; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Panel; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; + +public class CanvasPanelFocusOnClickTest { + + private static final String INSTRUCTIONS = """ + + Click on the red Canvas. Verify that it has focus by key pressing. + Click on the yellow Panel. Verify that it has focus by key pressing. + Click on the blue heavyweight Panel (NOT ON THE BUTTON!). + Verify that it doesn't have focus by key pressing. + If two empty containers are able to the get focus by a mouse click + and the container with heavyweight children are unable to get + the focus by a mouse click which can be verified through messages in message dialog + the test passes."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("CanvasPanelFocusOnClickTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testUI(CanvasPanelFocusOnClickTest::createTestUI) + .logArea() + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + Canvas canvas = new Canvas();; + Panel emptyPanel = new Panel(); + Panel panel = new Panel(); + Button buttonInPanel = new Button("BUTTON ON PANEL"); + + Frame frame = new Frame("CanvasPanelFocusOnClickTest Frame"); + frame.setLayout(new GridLayout(3, 1)); + canvas.setBackground(Color.red); + canvas.setName("RED CANVAS"); + canvas.addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent e) { + println(e.toString()); + } + public void focusLost(FocusEvent e) { + println(e.toString()); + } + }); + canvas.addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent e) { + printKey(e); + } + + public void keyTyped(KeyEvent e) { + printKey(e); + } + + public void keyReleased(KeyEvent e) { + printKey(e); + } + }); + frame.add(canvas); + + emptyPanel.setBackground(Color.yellow); + emptyPanel.setName("YELLOW PANEL"); + emptyPanel.addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent e) { + println(e.toString()); + } + public void focusLost(FocusEvent e) { + println(e.toString()); + } + }); + emptyPanel.addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent e) { + printKey(e); + } + + public void keyTyped(KeyEvent e) { + printKey(e); + } + + public void keyReleased(KeyEvent e) { + printKey(e); + } + }); + frame.add(emptyPanel); + + panel.setBackground(Color.blue); + panel.setName("BLUE PANEL"); + buttonInPanel.setName("BUTTON ON PANEL"); + buttonInPanel.addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent e) { + println(e.toString()); + } + public void focusLost(FocusEvent e) { + println(e.toString()); + } + }); + buttonInPanel.addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent e) { + printKey(e); + } + + public void keyTyped(KeyEvent e) { + printKey(e); + } + + public void keyReleased(KeyEvent e) { + printKey(e); + } + }); + panel.add(buttonInPanel); + panel.addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent e) { + println(e.toString()); + } + public void focusLost(FocusEvent e) { + println(e.toString()); + } + }); + panel.addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent e) { + printKey(e); + } + + public void keyTyped(KeyEvent e) { + printKey(e); + } + + public void keyReleased(KeyEvent e) { + printKey(e); + } + }); + frame.add(panel); + + frame.setSize(200, 200); + + return frame; + + } + + static void printKey(KeyEvent e) { + String typeStr; + switch(e.getID()) { + case KeyEvent.KEY_PRESSED: + typeStr = "KEY_PRESSED"; + break; + case KeyEvent.KEY_RELEASED: + typeStr = "KEY_RELEASED"; + break; + case KeyEvent.KEY_TYPED: + typeStr = "KEY_TYPED"; + break; + default: + typeStr = "unknown type"; + } + + Object source = e.getSource(); + if (source instanceof Component) { + typeStr += " on " + ((Component)source).getName(); + } else { + typeStr += " on " + source; + } + + println(typeStr); + } + + static void println(String messageIn) { + PassFailJFrame.log(messageIn); + } +} diff --git a/test/jdk/java/awt/Focus/ComponentLostFocusTest.java b/test/jdk/java/awt/Focus/ComponentLostFocusTest.java new file mode 100644 index 0000000000000..8045ace43d5b5 --- /dev/null +++ b/test/jdk/java/awt/Focus/ComponentLostFocusTest.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4982943 + * @key headful + * @summary focus lost in text fields or text areas, unable to enter characters from keyboard + * @run main ComponentLostFocusTest + */ + +import java.awt.Dialog; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.KeyboardFocusManager; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.TextField; +import java.awt.Toolkit; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.InputEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.File; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import javax.imageio.ImageIO; + +public class ComponentLostFocusTest { + + static Frame frame; + static TextField tf; + static Robot r; + static Dialog dialog = null; + static volatile Point loc; + static volatile int width; + static volatile int top; + static final CountDownLatch focusGainedLatch = new CountDownLatch(1); + + private static void createTestUI() { + + dialog = new Dialog(frame, "Dialog", true); + + frame = new Frame("ComponentLostFocusTest Frame"); + frame.setLayout(new FlowLayout()); + frame.addWindowFocusListener(new WindowAdapter() { + public void windowGainedFocus(WindowEvent e) { + System.out.println("Frame gained focus: "+e); + } + }); + tf = new TextField("Text Field"); + frame.add(tf); + frame.setSize(400,300); + frame.setVisible(true); + frame.setLocationRelativeTo(null); + frame.validate(); + } + + public static void doTest() { + System.out.println("dialog.setVisible.... "); + new Thread(() -> dialog.setVisible(true)).start(); + + // The bug is that this construction leads to the redundant xRequestFocus + // By the way, the requestFocusInWindow() works fine before the fix + System.out.println("requesting.... "); + frame.requestFocus(); + + r.delay(1000); + + // Returning the focus to the initial frame will work correctly after the fix + System.out.println("disposing.... "); + dialog.dispose(); + + r.delay(1000); + + // We want to track the GAIN_FOCUS from this time + tf.addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent e) { + System.out.println("TextField gained focus: " + e); + focusGainedLatch.countDown(); + } + }); + + } + + private static void doRequestFocusToTextField() { + // do activation using press title + r.mouseMove(loc.x + width / 2, loc.y + top / 2); + r.waitForIdle(); + r.mousePress(InputEvent.BUTTON1_DOWN_MASK); + r.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + r.waitForIdle(); + + // request focus to the text field + tf.requestFocus(); + } + + private static void captureScreen() { + try { + final Rectangle screenBounds = new Rectangle( + Toolkit.getDefaultToolkit().getScreenSize()); + ImageIO.write(r.createScreenCapture(screenBounds), + "png", new File("ComponentLostFocusTest.png")); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static final void main(String args[]) throws Exception { + r = new Robot(); + r.setAutoDelay(100); + + EventQueue.invokeAndWait(() -> createTestUI()); + r.waitForIdle(); + r.delay(1000); + try { + EventQueue.invokeAndWait(() -> { + doTest(); + loc = frame.getLocationOnScreen(); + width = frame.getWidth(); + top = frame.getInsets().top; + }); + doRequestFocusToTextField(); + + System.out.println("Focused window: " + + KeyboardFocusManager.getCurrentKeyboardFocusManager(). + getFocusedWindow()); + System.out.println("Focus owner: " + + KeyboardFocusManager.getCurrentKeyboardFocusManager(). + getFocusOwner()); + if (!focusGainedLatch.await(5, TimeUnit.SECONDS)) { + captureScreen(); + throw new RuntimeException("Waited too long, " + + "TextField got no focus! Test failed."); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } +} diff --git a/test/jdk/java/awt/Focus/ConsumedKeyEventTest.java b/test/jdk/java/awt/Focus/ConsumedKeyEventTest.java new file mode 100644 index 0000000000000..dda82adeec587 --- /dev/null +++ b/test/jdk/java/awt/Focus/ConsumedKeyEventTest.java @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4700276 + * @summary Peers process KeyEvents before KeyEventPostProcessors + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ConsumedKeyEventTest +*/ + +import java.awt.Canvas; +import java.awt.Component; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.KeyboardFocusManager; +import java.awt.KeyEventPostProcessor; +import java.awt.event.KeyEvent; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +public class ConsumedKeyEventTest implements KeyEventPostProcessor { + + private static final String INSTRUCTIONS = """ + This is a Windows-only test. + When the test starts, you will see a Frame with two components in it, + components look like colored rectangles, one of them is lightweight, one is heavyweight. + Do the following: + 1. Click the mouse on the left component. + If it isn't yellow after the click (that means it doesn't have focus), the test fails. + 2. Press and release ALT key. + In the output window, the text should appear stating that those key events were consumed. + If no output appears, the test fails. + 3. Press space bar. If system menu drops down, the test fails. + 4. Click the right rectangle. + It should become red after the click. If it doesn't, it means that it didn't get the focus, and the test fails. + 5. Repeat steps 2. and 3. + 6. If the test didn't fail on any of the previous steps, the test passes."""; + + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("ConsumedKeyEventTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 5) + .columns(35) + .testUI(ConsumedKeyEventTest::createTestUI) + .logArea() + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + KeyboardFocusManager.getCurrentKeyboardFocusManager(). + addKeyEventPostProcessor((e) -> { + System.out.println("postProcessor(" + e + ")"); + // consumes all ALT-events + if (e.getKeyCode() == KeyEvent.VK_ALT) { + println("consumed " + e); + e.consume(); + return true; + } + return false; + }); + FocusRequestor requestor = new FocusRequestor(); + Frame frame = new Frame("Main Frame"); + frame.setLayout(new FlowLayout()); + + Canvas canvas = new CustomCanvas(); + canvas.addMouseListener(requestor); + frame.add(canvas); + canvas.requestFocus(); + + Component lwComp = new LWComponent(); + lwComp.addMouseListener(requestor); + frame.add(lwComp); + + frame.pack(); + + return frame; + } + + public boolean postProcessKeyEvent(KeyEvent e) { + System.out.println("postProcessor(" + e + ")"); + // consumes all ALT-events + if (e.getKeyCode() == KeyEvent.VK_ALT) { + println("consumed " + e); + e.consume(); + return true; + } + return false; + } + + static void println(String messageIn) { + PassFailJFrame.log(messageIn); + } +}// class ConsumedKeyEventTest + +class CustomCanvas extends Canvas { + CustomCanvas() { + super(); + setName("HWComponent"); + setSize(100, 100); + addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent fe) { + repaint(); + } + + public void focusLost(FocusEvent fe) { + repaint(); + } + }); + } + + public void paint(Graphics g) { + if (isFocusOwner()) { + g.setColor(Color.YELLOW); + } else { + g.setColor(Color.GREEN); + } + g.fillRect(0, 0, 100, 100); + } + +} + +class LWComponent extends Component { + LWComponent() { + super(); + setName("LWComponent"); + addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent fe) { + repaint(); + } + + public void focusLost(FocusEvent fe) { + repaint(); + } + }); + } + + public Dimension getPreferredSize() { + return new Dimension(100, 100); + } + + public void paint(Graphics g) { + if (isFocusOwner()) { + g.setColor(Color.RED); + } else { + g.setColor(Color.BLACK); + } + g.fillRect(0, 0, 100, 100); + } + +} + +class FocusRequestor extends MouseAdapter { + static int counter = 0; + public void mouseClicked(MouseEvent me) { + System.out.println("mouseClicked on " + me.getComponent().getName()); + } + public void mousePressed(MouseEvent me) { + System.out.println("mousePressed on " + me.getComponent().getName()); + me.getComponent().requestFocus(); + } + public void mouseReleased(MouseEvent me) { + System.out.println("mouseReleased on " + me.getComponent().getName()); + } +} + diff --git a/test/jdk/java/awt/Focus/DeiconifyTest.java b/test/jdk/java/awt/Focus/DeiconifyTest.java new file mode 100644 index 0000000000000..e87b13b8e65fa --- /dev/null +++ b/test/jdk/java/awt/Focus/DeiconifyTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4380809 + * @summary Focus disappears after deiconifying frame + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DeiconifyTest +*/ + +import java.awt.Button; +import java.awt.Frame; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; + +public class DeiconifyTest { + + private static final String INSTRUCTIONS = """ + 1. Activate frame \"Main frame\" + be sure that button has focus + 2. Minimize frame and then restore it. + If the button has focus then test passed, else failed"""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("DeiconifyTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int)INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(DeiconifyTest::createTestUI) + .logArea() + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + Frame frame = new Frame("Main frame"); + Button button = new Button("button"); + button.addFocusListener(new FocusListener() { + public void focusGained(FocusEvent fe) { + println("focus gained"); + } + public void focusLost(FocusEvent fe) { + println("focus lost"); + } + }); + frame.add(button); + frame.setSize(300, 100); + + return frame; + } + + static void println(String messageIn) { + PassFailJFrame.log(messageIn); + } +} + diff --git a/test/jdk/java/awt/Focus/EmptyWindowKeyTest.java b/test/jdk/java/awt/Focus/EmptyWindowKeyTest.java new file mode 100644 index 0000000000000..bbdf8ce4f383b --- /dev/null +++ b/test/jdk/java/awt/Focus/EmptyWindowKeyTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4464723 + * @summary Tests simple KeyAdapter / KeyListener on an empty, focusable window + * @key headful + * @run main EmptyWindowKeyTest +*/ + +import java.awt.AWTEvent; +import java.awt.Frame; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.Robot; + +public class EmptyWindowKeyTest { + + static volatile boolean passed1, passed2; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + robot.setAutoDelay(100); + MainFrame mainFrame = new MainFrame(); + mainFrame.setSize(50,50); + mainFrame.addKeyListener(new KeyboardTracker()); + robot.waitForIdle(); + robot.delay(1000); + robot.keyPress(KeyEvent.VK_A); + robot.keyRelease(KeyEvent.VK_A); + robot.waitForIdle(); + robot.delay(1000); + if (!passed1 || !passed2) { + throw new RuntimeException("KeyPress/keyRelease not seen," + + "passed1 " + passed1 + " passed2 " + passed2); + } + } + + static public class KeyboardTracker extends KeyAdapter { + public KeyboardTracker() { } + public void keyTyped(KeyEvent e) {} + + public void keyPressed(KeyEvent e) { + if (e.getKeyText(e.getKeyCode()).equals("A")) { + passed1 = true; + } + } + public void keyReleased(KeyEvent e) { + if (e.getKeyText(e.getKeyCode()).equals("A")) { + passed2 = true; + } + } + } + + static public class MainFrame extends Frame { + + public MainFrame() { + super(); + enableEvents(AWTEvent.KEY_EVENT_MASK); + setVisible(true); + } + + } + +} + diff --git a/test/jdk/java/awt/Focus/FocusChangeOnResizeTest.java b/test/jdk/java/awt/Focus/FocusChangeOnResizeTest.java new file mode 100644 index 0000000000000..a9fa9e5edfd07 --- /dev/null +++ b/test/jdk/java/awt/Focus/FocusChangeOnResizeTest.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* + * @test + * @bug 4060975 + * @summary tests that a component doesn't lose focus on resize + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FocusChangeOnResizeTest +*/ + +import java.util.List; + +import java.awt.Button; +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Panel; +import java.awt.TextField; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; + +public class FocusChangeOnResizeTest { + + private static final String INSTRUCTIONS = """ + For the Frame and the Dialog: + Press the LOWER BUTTON to resize the Frame or Dialog programmatically. + Give the focus to the LOWER BUTTON and resize the Frame or Dialog manually. + If the LOWER BUTTON always has focus after resize + (for both frame and dialog) the test passes."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("PopupMenu Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(FocusChangeOnResizeTest::createTestUI) + .logArea() + .build() + .awaitAndCheck(); + } + + private static List createTestUI() { + Frame frame = new Frame("FocusChangeOnResizeTest frame"); + Dialog dialog = new Dialog(frame, "Test dialog"); + frame.add(new TestPanel(frame)); + dialog.add(new TestPanel(dialog)); + frame.setBounds (150, 200, 200, 200); + dialog.setBounds (150, 500, 200, 200); + return List.of(frame, dialog); + } + + static FocusListener eventLogger = new FocusListener() { + public void focusGained(FocusEvent e) { + PassFailJFrame.log(e.toString()); + } + public void focusLost(FocusEvent e) { + PassFailJFrame.log(e.toString()); + } + }; +} + +class TestPanel extends Panel { + + TextField textField = new TextField("TEXT FIELD"); + Button button1 = new Button("UPPER BUTTON"); + Button button2 = new Button("LOWER BUTTON"); + + public TestPanel(Window parent) { + setLayout(new GridLayout(3, 1)); + add(textField); + add(button1); + add(button2); + textField.setName("TEXT FIELD"); + button1.setName("UPPER BUTTON"); + button2.setName("LOWER BUTTON"); + textField.addFocusListener(FocusChangeOnResizeTest.eventLogger); + button1.addFocusListener(FocusChangeOnResizeTest.eventLogger); + button2.addFocusListener(FocusChangeOnResizeTest.eventLogger); + + button2.addActionListener(new Resizer(parent)); + } +} + +class Resizer implements ActionListener { + Window target; + + public Resizer(Window window) { + target = window; + } + + public void actionPerformed(ActionEvent e) { + target.setSize(200, 100); + target.doLayout(); + target.pack(); + } +} + diff --git a/test/jdk/java/awt/Focus/FocusKeepTest.java b/test/jdk/java/awt/Focus/FocusKeepTest.java new file mode 100644 index 0000000000000..0adc463f5d9ae --- /dev/null +++ b/test/jdk/java/awt/Focus/FocusKeepTest.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4128659 + * @summary Tests whether a focus request will work on a focus lost event. + * @key headful + * @run main FocusKeepTest + */ + +import java.awt.BorderLayout; +import java.awt.KeyboardFocusManager; +import java.awt.Robot; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.KeyEvent; +import javax.swing.JFrame; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; + +public class FocusKeepTest { + + static JFrame frame; + static JTextField tf; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + robot.setAutoDelay(100); + try { + SwingUtilities.invokeAndWait(() -> createTestUI()); + robot.waitForIdle(); + robot.delay(1000); + robot.keyPress(KeyEvent.VK_TAB); + robot.keyRelease(KeyEvent.VK_TAB); + if (KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() instanceof JTextField tf1) { + if (!tf1.getText().equals("TextField 1")) { + throw new RuntimeException("Focus on wrong textfield"); + } + } else { + throw new RuntimeException("Focus not on correct component"); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void createTestUI() { + frame = new JFrame("FocusKeepTest"); + tf = new JTextField("TextField 1"); + tf.addFocusListener(new MyFocusAdapter("TextField 1")); + frame.add(tf, BorderLayout.NORTH); + + tf = new JTextField("TextField 2"); + tf.addFocusListener(new MyFocusAdapter("TextField 2")); + frame.add(tf, BorderLayout.SOUTH); + + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + static class MyFocusAdapter extends FocusAdapter { + private String myName; + + public MyFocusAdapter (String name) { + myName = name; + } + + public void focusLost (FocusEvent e) { + if (myName.equals ("TextField 1")) { + e.getComponent().requestFocus (); + } + } + + public void focusGained (FocusEvent e) { + } + } +} diff --git a/test/jdk/java/awt/Focus/FocusPolicyTest.java b/test/jdk/java/awt/Focus/FocusPolicyTest.java new file mode 100644 index 0000000000000..3ec362acaf085 --- /dev/null +++ b/test/jdk/java/awt/Focus/FocusPolicyTest.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4897459 + * @key headful + * @summary The key does not switches focus in the internal frames in Swing apps. + * @run main FocusPolicyTest + */ + +import java.awt.Container; +import java.awt.Component; +import java.awt.DefaultFocusTraversalPolicy; +import java.awt.Dialog; +import java.awt.FocusTraversalPolicy; +import java.awt.Frame; +import java.awt.KeyboardFocusManager; +import java.awt.Toolkit; +import java.awt.Window; +import javax.swing.JDialog; +import javax.swing.JInternalFrame; +import javax.swing.JFrame; +import javax.swing.JWindow; +import javax.swing.LayoutFocusTraversalPolicy; + +public class FocusPolicyTest { + static int stageNum; + static FocusTraversalPolicy customPolicy = new CustomPolicy(); + final static Class awtDefaultPolicy = DefaultFocusTraversalPolicy.class; + final static Class swingDefaultPolicy = LayoutFocusTraversalPolicy.class; + + public static void main(String[] args) { + final boolean isXawt = "sun.awt.X11.XToolkit".equals(Toolkit.getDefaultToolkit().getClass().getName()); + + System.err.println("isXawt = " + isXawt); + + // 1. Check default policy + if (KeyboardFocusManager.getCurrentKeyboardFocusManager(). + getDefaultFocusTraversalPolicy().getClass() != awtDefaultPolicy) { + throw new RuntimeException("Error: stage 1: default policy is not DefaultFocusTraversalPolicy"); + } + + // 2. Check AWT top-levels policies + stageNum = 2; + checkAWTPoliciesFor(awtDefaultPolicy); + + // 3. Check Swing top-levels policies + stageNum = 3; + checkSwingPoliciesFor(swingDefaultPolicy); + + // 4. Check default policy if not XToolkit + if (!isXawt) { + if (KeyboardFocusManager.getCurrentKeyboardFocusManager(). + getDefaultFocusTraversalPolicy().getClass() != swingDefaultPolicy) { + throw new RuntimeException("Error: stage 4: default policy is not LayoutFocusTraversalPolicy"); + } + } + + // 5. Check AWT top-levels policies + // this is a bug in XAWT we should change the test as soon as + // we will be able to fix this bug. + stageNum = 5; + Class defaultPolicy = swingDefaultPolicy; + if (isXawt) { + defaultPolicy = awtDefaultPolicy; + } + checkAWTPoliciesFor(defaultPolicy); + + // Set custom policy as default + KeyboardFocusManager.getCurrentKeyboardFocusManager().setDefaultFocusTraversalPolicy(customPolicy); + + // 6. Check AWT top-levels policies for custom + stageNum = 6; + checkAWTPoliciesFor(customPolicy.getClass()); + + // 7. Check Swing top-levels policies for custom + stageNum = 7; + checkSwingPoliciesFor(customPolicy.getClass()); + + return; + } + + public static void checkAWTPoliciesFor(Class expectedPolicyClass) { + Window[] tlvs = new Window[7]; + + tlvs[0] = new Frame(""); + tlvs[1] = new Frame("", tlvs[0].getGraphicsConfiguration()); + tlvs[2] = new Window((Frame)tlvs[0]); + tlvs[3] = new Dialog((Frame)tlvs[0], "", false); + tlvs[4] = new Dialog((Frame)tlvs[0], "", false, tlvs[0].getGraphicsConfiguration()); + tlvs[5] = new Dialog((Dialog)tlvs[3], "", false); + tlvs[6] = new Dialog((Dialog)tlvs[3], "", false, tlvs[0].getGraphicsConfiguration()); + + for (int i = 0; i < 7; i++) { + Class policyClass = tlvs[i].getFocusTraversalPolicy().getClass(); + if (policyClass != expectedPolicyClass) { + throw new RuntimeException("Error: stage " + stageNum + ": " + + tlvs[i].getClass().getName() + + "'s policy is " + policyClass.getName() + + " but not " + expectedPolicyClass.getName()); + } + } + } + + public static void checkSwingPoliciesFor(Class expectedPolicyClass) { + Container[] tlvs = new Container[12]; + + tlvs[0] = new JFrame(); + tlvs[1] = new JFrame(tlvs[0].getGraphicsConfiguration()); + tlvs[2] = new JFrame(""); + tlvs[3] = new JFrame("", tlvs[0].getGraphicsConfiguration()); + tlvs[4] = new JWindow((Frame)tlvs[0]); + tlvs[5] = new JWindow((Window)tlvs[4]); + tlvs[6] = new JWindow((Window)tlvs[4], tlvs[0].getGraphicsConfiguration()); + tlvs[7] = new JDialog((Frame)tlvs[0], "", false); + tlvs[8] = new JDialog((Frame)tlvs[0], "", false, tlvs[0].getGraphicsConfiguration()); + tlvs[9] = new JDialog((Dialog)tlvs[7], "", false); + tlvs[10] = new JDialog((Dialog)tlvs[7], "", false, tlvs[0].getGraphicsConfiguration()); + tlvs[11] = new JInternalFrame("", false, false, false, false); + + for (int i = 0; i < tlvs.length; i++) { + Class policyClass = tlvs[i].getFocusTraversalPolicy().getClass(); + if (policyClass != expectedPolicyClass) { + throw new RuntimeException("Error: stage " + stageNum + + ": " + tlvs[i].getClass().getName() + + "'s policy is " + policyClass.getName() + " but not " + + expectedPolicyClass.getName()); + } + } + } + + // Dummy policy. + static class CustomPolicy extends FocusTraversalPolicy { + public Component getComponentAfter(Container focusCycleRoot, + Component aComponent) { + return null; + } + + public Component getComponentBefore(Container focusCycleRoot, + Component aComponent) { + return null; + } + + public Component getFirstComponent(Container focusCycleRoot) { + return null; + } + + public Component getLastComponent(Container focusCycleRoot) { + return null; + } + + public Component getDefaultComponent(Container focusCycleRoot) { + return null; + } + } +} diff --git a/test/jdk/java/awt/Focus/HiddenTraversalTest.java b/test/jdk/java/awt/Focus/HiddenTraversalTest.java new file mode 100644 index 0000000000000..59e7f477945e5 --- /dev/null +++ b/test/jdk/java/awt/Focus/HiddenTraversalTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* + * @test + * @bug 4157017 + * @summary Checks whether focus can be traversed when component not visible + within parent container. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual HiddenTraversalTest +*/ + +import java.awt.Button; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Panel; + +public class HiddenTraversalTest { + + private static final String INSTRUCTIONS = """ + Examine the Frame. If six buttons are visible, resize the frame + so that only four are visible. If fewer than six buttons are + visible, do nothing.\n + Now, repeatedly press the tab key. Focus should cycle through the + visible and invisible buttons. If after six presses of the tab + button 'Button 0' has focus, the test passes. If focus is instead + stuck at 'Button 3', the test fails."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("HiddenTraversalTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(HiddenTraversalTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + Frame f = new Frame("Focus test"); + Panel p = new Panel(new FlowLayout()); + for (int i = 0; i < 6; i++) { + p.add(new Button("Button " + i)); + } + f.add(p); + f.setSize(200, 100); + return f; + } + +} + diff --git a/test/jdk/java/awt/Focus/InactiveFocusRace.java b/test/jdk/java/awt/Focus/InactiveFocusRace.java new file mode 100644 index 0000000000000..efca2dbf53a13 --- /dev/null +++ b/test/jdk/java/awt/Focus/InactiveFocusRace.java @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4697451 + * @summary Test that there is no race between focus component in inactive window and window activation + * @key headful + * @run main InactiveFocusRace +*/ + +import java.awt.Button; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.KeyboardFocusManager; +import java.awt.Point; +import java.awt.Robot; +import java.awt.Toolkit; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.event.InputEvent; + +public class InactiveFocusRace { + + static Frame activeFrame, inactiveFrame; + Button activeButton, inactiveButton1, inactiveButton2; + Semaphore sema; + final static int TIMEOUT = 10000; + + public static void main(String[] args) throws Exception { + try { + InactiveFocusRace test = new InactiveFocusRace(); + test.init(); + test.start(); + } finally { + if (activeFrame != null) { + activeFrame.dispose(); + } + if (inactiveFrame != null) { + inactiveFrame.dispose(); + } + } + } + + public void init() { + activeButton = new Button("active button"); + inactiveButton1 = new Button("inactive button1"); + inactiveButton2 = new Button("inactive button2"); + activeFrame = new Frame("Active frame"); + inactiveFrame = new Frame("Inactive frame"); + inactiveFrame.setLayout(new FlowLayout()); + activeFrame.add(activeButton); + inactiveFrame.add(inactiveButton1); + inactiveFrame.add(inactiveButton2); + activeFrame.pack(); + activeFrame.setLocation(300, 10); + inactiveFrame.pack(); + inactiveFrame.setLocation(300, 300); + sema = new Semaphore(); + + inactiveButton1.addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent e) { + System.err.println("Button 1 got focus"); + } + }); + inactiveButton2.addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent e) { + System.err.println("Button2 got focus"); + sema.raise(); + } + }); + activeFrame.addWindowListener(new WindowAdapter() { + public void windowActivated(WindowEvent e) { + System.err.println("Window activated"); + sema.raise(); + } + }); + } + + public void start() { + Robot robot = null; + try { + robot = new Robot(); + } catch (Exception e) { + throw new RuntimeException("Unable to create robot"); + } + + inactiveFrame.setVisible(true); + activeFrame.setVisible(true); + + // Wait for active frame to become active + try { + sema.doWait(TIMEOUT); + } catch (InterruptedException ie) { + throw new RuntimeException("Wait was interrupted"); + } + if (!sema.getState()) { + throw new RuntimeException("Frame doesn't become active on show"); + } + sema.setState(false); + + // press on second button in inactive frame + Point loc = inactiveButton2.getLocationOnScreen(); + robot.mouseMove(loc.x+5, loc.y+5); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + + // after all second button should be focus owner. + try { + sema.doWait(TIMEOUT); + } catch (InterruptedException ie) { + throw new RuntimeException("Wait was interrupted"); + } + if (!sema.getState()) { + throw new RuntimeException("Button2 didn't become focus owner"); + } + Toolkit.getDefaultToolkit().sync(); + robot.waitForIdle(); + if (!(KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() == inactiveButton2)) { + throw new RuntimeException("Button2 should be the focus owner after all"); + } + + } + +} + +class Semaphore { + boolean state = false; + int waiting = 0; + public Semaphore() { + } + public void doWait() throws InterruptedException { + synchronized(this) { + if (state) return; + waiting++; + wait(); + waiting--; + } + } + public void doWait(int timeout) throws InterruptedException { + synchronized(this) { + if (state) return; + waiting++; + wait(timeout); + waiting--; + } + } + public void raise() { + synchronized(this) { + state = true; + if (waiting > 0) { + notifyAll(); + } + } + } + public boolean getState() { + synchronized(this) { + return state; + } + } + public void setState(boolean newState) { + synchronized(this) { + state = newState; + } + } +} diff --git a/test/jdk/java/awt/Focus/InitialPrintDlgFocusTest.java b/test/jdk/java/awt/Focus/InitialPrintDlgFocusTest.java new file mode 100644 index 0000000000000..cc9d0c0371182 --- /dev/null +++ b/test/jdk/java/awt/Focus/InitialPrintDlgFocusTest.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4688591 + * @summary Tab key hangs in Native Print Dialog on win32 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual InitialPrintDlgFocusTest + */ + +import java.awt.BorderLayout; +import java.awt.Dialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.JobAttributes; +import java.awt.PageAttributes; +import java.awt.PrintJob; +import java.awt.Toolkit; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JFrame; + +public class InitialPrintDlgFocusTest { + + private static final String INSTRUCTIONS = """ + After the tests starts you will see a frame titled "PrintTest". + Press the "Print" button and the print dialog should appear. + If you are able to transfer focus between components of the Print dialog + using the TAB key, then the test passes else the test fails. + + Note: close the Print dialog before clicking on "Pass" or "Fail" buttons."""; + + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("InitialPrintDlgFocusTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(InitialPrintDlgFocusTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + return new PrintTest(); + + } +} + +class PrintTest extends JFrame implements ActionListener { + + JButton b; + JobAttributes jbattrib; + Toolkit tk ; + PageAttributes pgattrib; + + public PrintTest() { + setTitle("PrintTest"); + setSize(500, 400); + + b = new JButton("Print"); + jbattrib = new JobAttributes(); + tk = Toolkit.getDefaultToolkit(); + pgattrib = new PageAttributes(); + getContentPane().setLayout(new FlowLayout()); + getContentPane().add(b); + + b.addActionListener(this); + + } + + public void actionPerformed(ActionEvent ae) { + if(ae.getSource()==b) + jbattrib.setDialog(JobAttributes.DialogType.NATIVE); + + PrintJob pjob = tk.getPrintJob(this, "Printing Test", + jbattrib, pgattrib); + + } +} + diff --git a/test/jdk/java/awt/Focus/KeyStrokeTest.java b/test/jdk/java/awt/Focus/KeyStrokeTest.java new file mode 100644 index 0000000000000..7c462ce8f22d6 --- /dev/null +++ b/test/jdk/java/awt/Focus/KeyStrokeTest.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4845868 + * @summary REGRESSION: First keystroke after JDialog is closed is lost + * @key headful + * @run main KeyStrokeTest + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.Robot; +import java.awt.TextField; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; + +public class KeyStrokeTest { + static boolean keyTyped; + static Frame frame; + + public static void main(String[] args) throws Exception { + try { + KeyStrokeTest test = new KeyStrokeTest(); + test.doTest(); + } finally { + if (frame != null) { + frame.dispose(); + } + } + } + + private static void doTest() throws Exception { + final Object monitor = new Object(); + frame = new Frame(); + TextField textField = new TextField() { + public void transferFocus() { + System.err.println("transferFocus()"); + final Dialog dialog = new Dialog(frame, true); + Button btn = new Button("Close It"); + btn.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + System.err.println("action performed"); + dialog.setVisible(false); + } + }); + dialog.add(btn); + dialog.setSize(200, 200); + dialog.setVisible(true); + } + }; + + textField.addKeyListener(new KeyAdapter() { + public void keyTyped(KeyEvent e) { + System.err.println(e); + if (e.getKeyChar() == 'a') { + keyTyped = true; + } + + synchronized (monitor) { + monitor.notifyAll(); + } + } + }); + frame.add(textField); + frame.setSize(400, 400); + frame.setVisible(true); + + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(1000); + robot.keyPress(KeyEvent.VK_TAB); + robot.keyRelease(KeyEvent.VK_TAB); + + robot.delay(1000); + robot.keyPress(KeyEvent.VK_SPACE); + robot.keyRelease(KeyEvent.VK_SPACE); + + robot.delay(1000); + synchronized (monitor) { + robot.keyPress(KeyEvent.VK_A); + robot.keyRelease(KeyEvent.VK_A); + monitor.wait(3000); + } + + if (!keyTyped) { + throw new RuntimeException("TEST FAILED"); + } + + System.out.println("Test passed"); + } + +} diff --git a/test/jdk/java/awt/Focus/KillFocusTest.java b/test/jdk/java/awt/Focus/KillFocusTest.java new file mode 100644 index 0000000000000..f61c18971f6f9 --- /dev/null +++ b/test/jdk/java/awt/Focus/KillFocusTest.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* + * @test + * @bug 4402942 + * @summary After deactivation and activation of frame, focus should be restored correctlty + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual KillFocusTest +*/ + +import java.awt.Frame; +import java.awt.TextField; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; + +public class KillFocusTest { + + private static final String INSTRUCTIONS = """ + After starting the test you should see \"Test Frame\" + with the \"Click me\" text field. + Click on this text field and try to type something in it. + Make sure that the field receives focus and you can enter text in it. + Click on any non-java window. + Click on \"Click me\" text field to return focus to it + If the caret is in the text field and you are able to type + in it then press pass else press fail."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("KillFocusTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(KillFocusTest::createTestUI) + .logArea() + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + + Frame frame = new Frame("KillFocusTest Frame"); + TextField textField = new TextField("Click me", 10); + textField.addFocusListener(new FocusListener() { + public void focusGained(FocusEvent fe) { + PassFailJFrame.log("Focus gained"); + } + public void focusLost(FocusEvent fe) { + PassFailJFrame.log("Focus lost"); + } + }); + frame.add(textField); + frame.setSize(200, 100); + return frame; + } + + +} + diff --git a/test/jdk/java/awt/Focus/LightweightFocusLostTest.java b/test/jdk/java/awt/Focus/LightweightFocusLostTest.java new file mode 100644 index 0000000000000..aff2e5e3b2eb8 --- /dev/null +++ b/test/jdk/java/awt/Focus/LightweightFocusLostTest.java @@ -0,0 +1,261 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* + * @test + * @bug 4124119 + * @summary Checks that lightweight components do not lose focus after + dragging the frame by using the mouse. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual LightweightFocusLostTest +*/ + +import java.awt.AWTEvent; +import java.awt.AWTEventMulticaster; +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Label; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.SystemColor; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + + +public class LightweightFocusLostTest { + + private static final String INSTRUCTIONS = """ + Steps to try to reproduce this problem: + When this test is run a window will display (Test Focus). + Click in the text field to give it the focus (a blinking cursor + will appear) and then move the frame with the mouse. The text field + (component which uses a lightweight component) should still have focus. + You should still see the blinking cursor in the text field after the + frame has been moved. If this is the behavior that you observe, the + test has passed, Press the Pass button. Otherwise the test has failed, + Press the Fail button."""; + + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("LightweightFocusLostTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 5) + .columns(35) + .testUI(LightweightFocusLostTest::createTestUI) + .logArea() + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + Frame f = new Frame("LightweightFocusLostTest"); + f.setLayout(new BorderLayout()); + String sLabel = "Lightweight component below (text field)"; + Label TFL = new Label(sLabel, Label.LEFT); + f.add(TFL, BorderLayout.NORTH); + SimpleTextField canvas = new SimpleTextField(30, 5); + f.add(canvas, BorderLayout.CENTER); + f.setSize(new Dimension(300,125)); + f.requestFocus(); + return f; + + } + +} + +class SimpleTextField extends Component implements Runnable { + int border; + int length; + Font font; + FontMetrics fontM; + char[] buffer; + int bufferIx; + + boolean hasFocus; + boolean cursorOn; + + SimpleTextField(int len, int bor) { + super(); + border = bor; + length = len; + buffer = new char[len]; + font = getFont(); + if (font == null) { + font = new Font("Dialog", Font.PLAIN, 20); + } + fontM = getFontMetrics(font); + + // Listen for key and mouse events. + this.addMouseListener(new MouseEventHandler()); + this.addFocusListener(new FocusEventHandler()); + this.addKeyListener(new KeyEventHandler()); + + // Set text cursor. + setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); + + // Start the thread that blinks the cursor. + (new Thread(this)).start(); + } + + public Dimension getMinimumSize() { + // The minimum height depends on the point size. + int w = fontM.charWidth('m') * length; + return new Dimension(w + 2 * border, fontM.getHeight() + 2 * border); + } + public Dimension getPreferredSize() { + return getMinimumSize(); + } + public Dimension getMaximumSize() { + return new Dimension(Short.MAX_VALUE, getPreferredSize().height); + } + + public boolean isFocusTraversable() { + return true; + } + + public void paint(Graphics g) { + int y = (getSize().height-fontM.getHeight())/2; + + // Clear the background using the text background color. + g.setColor(SystemColor.text); + g.fillRect(0, 0, getSize().width, getSize().height); + + g.setFont(font); + g.setColor(SystemColor.textText); + g.drawChars(buffer, 0, bufferIx, border, y + fontM.getAscent()); + + // Draw blinking cursor. + int x = fontM.charsWidth(buffer, 0, bufferIx) + border; + int w = fontM.charWidth('c'); + if (hasFocus) { + g.setColor(getForeground()); + g.fillRect(x, y, w, fontM.getHeight()); + if (cursorOn) { + if (bufferIx < buffer.length) { + g.setColor(SystemColor.text); + g.fillRect(x + 2, y + 2, w - 4, fontM.getHeight() - 4); + } + } + } + } + + // Event handlers + class MouseEventHandler extends MouseAdapter { + public void mousePressed(MouseEvent evt) { + requestFocus(); + } + } + class FocusEventHandler extends FocusAdapter { + public void focusGained(FocusEvent evt) { + PassFailJFrame.log("Focus gained: " + evt); + + hasFocus = true; + repaint(); + } + public void focusLost(FocusEvent evt) { + PassFailJFrame.log("Focus lost: " + evt); + hasFocus = false; + repaint(); + } + } + class KeyEventHandler extends KeyAdapter { + public void keyPressed(KeyEvent evt) { + switch (evt.getKeyCode()) { + case KeyEvent.VK_DELETE: + case KeyEvent.VK_BACK_SPACE: + if (bufferIx > 0) { + bufferIx--; + repaint(); + } + break; + case KeyEvent.VK_ENTER: + ActionEvent action = + new ActionEvent(SimpleTextField.this, + ActionEvent.ACTION_PERFORMED, + String.valueOf(buffer, 0, bufferIx)); + // Send contents of buffer to listeners + processEvent(action); + break; + default: + repaint(); + } + } + public void keyTyped(KeyEvent evt) { + if (bufferIx < buffer.length + && !evt.isActionKey() + && !Character.isISOControl(evt.getKeyChar())) { + buffer[bufferIx++] = evt.getKeyChar(); + } + } + } + + // Support for Action Listener. + ActionListener actionListener; + + public void addActionListener(ActionListener l) { + actionListener = AWTEventMulticaster.add(actionListener, l); + } + + // Override processEvent() to deal with ActionEvent. + protected void processEvent(AWTEvent evt) { + if (evt instanceof ActionEvent) { + processActionEvent((ActionEvent)evt); + } else { + super.processEvent(evt); + } + } + + // Supply method to process Action event. + protected void processActionEvent(ActionEvent evt) { + if (actionListener != null) { + actionListener.actionPerformed(evt); + } + } + + public void run() { + while (true) { + try { + // If component has focus, blink the cursor every 1/2 second. + Thread.sleep(500); + cursorOn = !cursorOn; + if (hasFocus) { + repaint(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } +} + diff --git a/test/jdk/java/awt/Focus/LightweightPopupTest.java b/test/jdk/java/awt/Focus/LightweightPopupTest.java new file mode 100644 index 0000000000000..bc3dd0d038b9b --- /dev/null +++ b/test/jdk/java/awt/Focus/LightweightPopupTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4472032 + * @summary Switching between lightweight menus by horizontal arrow key works incorrect + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual LightweightPopupTest +*/ + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; + +public class LightweightPopupTest { + + private static final String INSTRUCTIONS = """ + When the test starts, you will see a frame titled + 'Lightweight Popup Test', which contains a button + (titled 'JButton') and two menus ('Menu 1' and 'Menu 2'). + Make sure that both menus, when expanded, fit entirely + into the frame. Now take the following steps: + 1. Click on 'JButton' to focus it. + 2. Click 'Menu 1' to expand it. + 3. Press right arrow to select 'Menu 2'. + Now check where the focus is. If it is on 'JButton' + (you can press space bar to see if it is there), then + the test failed. If 'JButton' is not focused, then + the test passed."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("LightweightPopupTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int)INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(LightweightPopupTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + + JFrame frame = new JFrame("Lightweight Popup Test"); + JButton button = new JButton("JButton"); + JMenuBar menuBar = new JMenuBar(); + JMenu menu1 = new JMenu("Menu 1"); + menu1.add(new JMenuItem("Menu Item 1")); + menu1.add(new JMenuItem("Menu Item 2")); + menuBar.add(menu1); + JMenu menu2 = new JMenu("Menu 2"); + menu2.add(new JMenuItem("Menu Item 3")); + menu2.add(new JMenuItem("Menu Item 4")); + menuBar.add(menu2); + + frame.add(button); + frame.setJMenuBar(menuBar); + frame.setSize(300, 200); + return frame; + } + +} + diff --git a/test/jdk/java/awt/Focus/MinimizeNonfocusableWindowTest.java b/test/jdk/java/awt/Focus/MinimizeNonfocusableWindowTest.java new file mode 100644 index 0000000000000..3114850dc7af3 --- /dev/null +++ b/test/jdk/java/awt/Focus/MinimizeNonfocusableWindowTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6399659 + * @summary When minimizing non-focusable window focus shouldn't jump out of the focused window. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MinimizeNonfocusableWindowTest +*/ + +import java.awt.Frame; +import java.awt.Window; +import java.util.List; + +public class MinimizeNonfocusableWindowTest { + + private static final String INSTRUCTIONS = """ + + You should see three frames: Frame-1, Frame-2 and Unfocusable. + + 1. Click Frame-1 to make it focused window, then click Frame-2. + Minimize Unfocusable frame with the mouse. If Frame-2 is still + the focused window continue testing, otherwise press FAIL. + + 2. Restore Unfocusable frame to normal state. Try to resize by dragging + its edge with left mouse button. It should be resizable. If not press + FAIL. Try the same with right mouse button. It shouldn't resize. + If it does, press FAIL, otherwise press PASS."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("MinimizeNonfocusableWindowTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 1) + .columns(40) + .testUI(MinimizeNonfocusableWindowTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static List createTestUI() { + Frame frame1 = new Frame("Frame-1"); + Frame frame2 = new Frame("Frame-2"); + Frame frame3 = new Frame("Unfocusable"); + frame1.setBounds(100, 0, 200, 100); + frame2.setBounds(100, 150, 200, 100); + frame3.setBounds(100, 300, 200, 100); + + frame3.setFocusableWindowState(false); + + return List.of(frame1, frame2, frame3); + } +} + diff --git a/test/jdk/java/awt/Focus/MixedWeightFocus.java b/test/jdk/java/awt/Focus/MixedWeightFocus.java new file mode 100644 index 0000000000000..4b434e4b66d7e --- /dev/null +++ b/test/jdk/java/awt/Focus/MixedWeightFocus.java @@ -0,0 +1,207 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4098290 4140890 + * @summary Using non-opaque windows - popups are initially not painted correctly + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MixedWeightFocus +*/ + +import java.util.List; +import java.awt.AWTEvent; +import java.awt.Button; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.FlowLayout; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Panel; +import java.awt.Toolkit; +import java.awt.Window; +import java.awt.event.FocusEvent; +import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +public class MixedWeightFocus { + + static FocusFrame f; + + private static final String INSTRUCTIONS = """ + This tests that permanent FOCUS_LOST messages are sent to lightweight + components when the focus shifts to a heavyweight component. It also + tests that components retain the focus when their parent window is + deactivated and activated. + + 1. Tab or mouse between the light and heavyweight buttons in this test + and verify that the focus rectangle on the lightweight components + disappears when focus shifts to a heavyweight button. + + 2. Activate another application then reactivate the test frame window. + Verify that the component that had the focus (light or heavy) when + the frame was deactivated regains the focus when it's reactivated. Do + the same thing for the modeless dialog. Also test this by moving the + activation between the dialog and the frame. + + 3. Verify that lightweight components with the focus in a deactivated + window draw their focus rectangles in gray instead of blue-- this indicates + they received temporary instead of permanent FOCUS_LOST events. + + NOTE: There is currently another problem with lightweight components + where if you click on one to activate its frame window, the lightweight + that previously had the focus will not get a FOCUS_LOST event. This + may manifest itself with a gray focus rectangle not getting erased. + Ignore this for now (Win32 only)."""; + + public static void main(String[] argv) throws Exception { + PassFailJFrame.builder() + .title("MixedWeightFocus Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 5) + .columns(45) + .testUI(MixedWeightFocus::createTestUI) + .build() + .awaitAndCheck(); + } + + private static List createTestUI() { + FocusFrame f = new FocusFrame(); + ModelessDialog dlg = new ModelessDialog(f); + + return List.of(f, dlg); + } +} + +class FocusFrame extends Frame { + public FocusFrame() { + super("FocusFrame"); + Panel p = new Panel(); + + p.add(new Button("button 1")); + p.add(new LightweightComp("lw 1")); + p.add(new Button("button 2")); + p.add(new LightweightComp("lw 2")); + add(p); + + pack(); + setLocation(100, 100); + + addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent ev) { + dispose(); + } + }); + } + +} + +class ModelessDialog extends Dialog { + public ModelessDialog(Frame frame) { + super(frame, "ModelessDialog", false); + setLayout( new FlowLayout() ); + add(new Button("button 1")); + add(new LightweightComp("lw 1")); + pack(); + setLocation(100, 400); + } +} + +// simple lightweight component, focus traversable, highlights upon focus +class LightweightComp extends Component { + FontMetrics fm; + String label; + private static final int FOCUS_GONE = 0; + private static final int FOCUS_TEMP = 1; + private static final int FOCUS_HAVE = 2; + int focusLevel = FOCUS_GONE; + public static int nameCounter = 0; + + public LightweightComp(String lwLabel ) { + label = lwLabel; + enableEvents(AWTEvent.FOCUS_EVENT_MASK|AWTEvent.MOUSE_EVENT_MASK); + setName("lw"+Integer.toString(nameCounter++)); + } + + public Dimension getPreferredSize() { + if (fm == null) { + fm = Toolkit.getDefaultToolkit().getFontMetrics(getFont()); + } + return new Dimension(fm.stringWidth(label) + 2, fm.getHeight() + 2); + } + + public void paint(Graphics g) { + Dimension s=getSize(); + + // erase the background + g.setColor(getBackground()); + g.fillRect(0, 0, s.width, s.height); + + g.setColor(getForeground()); + + // draw the string + g.drawString(label, 2, fm.getHeight()); + + // draw a focus rectangle + if (focusLevel > FOCUS_GONE) { + if (focusLevel == FOCUS_TEMP) { + g.setColor(Color.gray); + } else { + g.setColor(Color.blue); + } + g.drawRect(1,1,s.width-2,s.height-2); + } + } + + + public boolean isFocusTraversable() { + return true; + } + + protected void processFocusEvent(FocusEvent e) { + super.processFocusEvent(e); + if (e.getID() == FocusEvent.FOCUS_GAINED) { + focusLevel = FOCUS_HAVE; + } else { + if (e.isTemporary()) { + focusLevel = FOCUS_TEMP; + } else { + focusLevel = FOCUS_GONE; + } + } + repaint(); + } + + protected void processMouseEvent(MouseEvent e) { + + if (e.getID()==MouseEvent.MOUSE_PRESSED) { + requestFocus(); + } + super.processMouseEvent(e); + } +} diff --git a/test/jdk/java/awt/Focus/NextFocusHelperTest.java b/test/jdk/java/awt/Focus/NextFocusHelperTest.java new file mode 100644 index 0000000000000..5b3015da63017 --- /dev/null +++ b/test/jdk/java/awt/Focus/NextFocusHelperTest.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* + * @test + * @bug 4474893 + * @summary Component.nextFocusHelper should search for first visible focus cycle root ancst + * @key headful + * @run main NextFocusHelperTest +*/ + +import java.awt.Button; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.FlowLayout; +import java.awt.KeyboardFocusManager; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.InputEvent; + +public class NextFocusHelperTest { + static Panel panel; + static Frame frame; + static Button btn1; + static Button btn3; + static Button hideButton; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + robot.setAutoDelay(100); + try { + EventQueue.invokeAndWait(() -> createTestUI()); + robot.waitForIdle(); + robot.delay(1000); + + Point loc = btn1.getLocationOnScreen(); + Dimension dim = btn1.getSize(); + robot.mouseMove(loc.x + dim.width/2, loc.y + dim.height/2); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(500); + + Point loc1 = hideButton.getLocationOnScreen(); + Dimension dim1 = hideButton.getSize(); + robot.mouseMove(loc1.x + dim1.width/2, loc1.y + dim1.height/2); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + + if (KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() + instanceof Button btn) { + if (!btn.getLabel().equals("Button 3")) { + throw new RuntimeException("Wrong button has focus"); + } + } + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void createTestUI() { + frame = new Frame("NextFocusHelperTest Frame"); + frame.setLayout(new FlowLayout()); + + panel = new Panel(); + panel.setFocusCycleRoot(true); + btn1 = new Button("Button In Panel"); + panel.add(btn1); + + hideButton = new Button("Hide Panel"); + hideButton.setFocusable(false); + hideButton.addActionListener(e -> { + panel.setVisible(false); + }); + + frame.add(new Button("Button 1")); + frame.add(panel); + frame.add(new Button("Button 3")); + frame.add(hideButton); + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } +} + diff --git a/test/jdk/java/awt/Focus/ProxiedWindowHideTest.java b/test/jdk/java/awt/Focus/ProxiedWindowHideTest.java new file mode 100644 index 0000000000000..a99ec4f0a0d90 --- /dev/null +++ b/test/jdk/java/awt/Focus/ProxiedWindowHideTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4396407 + * @summary Tests that after a proxied window is hidden, focus is being restored correctly + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ProxiedWindowHideTest +*/ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Container; +import javax.swing.Box; +import javax.swing.JComboBox; +import javax.swing.JFrame; + +public class ProxiedWindowHideTest { + + private static final String INSTRUCTIONS = """ + You will see a JFrame. + Click on JComboBox, list will expand then select any item in it. + After selection, list should collapse. + Click on Button('Push'). + If you are able to make it focused by mouse click, + (black rectangle will appear around it) the test is PASSED, + otherwise the test is FAILED."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("ProxiedWindowHideTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(ProxiedWindowHideTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + JFrame frame = new JFrame("ProxiedWindowHideTest frame"); + String[] petStrings = { "Bird", "Cat", "Dog", "Rabbit", "Pig" }; + JComboBox cb = new JComboBox(petStrings); + + cb.setLightWeightPopupEnabled(false); + Container parent = Box.createVerticalBox(); + parent.add(new Button("Push")); + parent.add(cb); + frame.add(parent, BorderLayout.CENTER); + frame.pack(); + return frame; + } + +} + diff --git a/test/jdk/java/awt/Focus/RequestInInactiveFrame.java b/test/jdk/java/awt/Focus/RequestInInactiveFrame.java new file mode 100644 index 0000000000000..3297fe17501a1 --- /dev/null +++ b/test/jdk/java/awt/Focus/RequestInInactiveFrame.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6458497 + * @summary check focus requests in inactive frames + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual RequestInInactiveFrame + */ + +import java.util.ArrayList; + +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.Window; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +public class RequestInInactiveFrame { + + private static final String INSTRUCTIONS = """ + After the tests starts you will see two frames: \"test frame\" and \"opposite frame\" + activate the former by click on its title + Focus should be on \"press me\" button (if it's not, the test fails) + press on \"press me\" button and activate \"opposite frame\" + wait for several seconds. + Focus should either remain on button in the \"opposite frame\" + or goes to \"focus target\" button (in this case \"test frame\" should be activated + if it's not, the test failed. + Activate \"test frame\" one more time, press on \"press me\" button and switch focus + to some native window. Wait for several seconds, + If you see focus border around + \"focus target\" and \"test frame\" is not active then the test failed. + if focus transfered to that button and the frame is activated, or if there is no focus + in java - tests passed."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("RequestInInactiveFrame Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testUI(RequestInInactiveFrame::createTestUI) + .build() + .awaitAndCheck(); + } + + private static ArrayList createTestUI() { + JFrame frame = new JFrame("test frame"); + final JButton btn2 = new JButton("focus target"); + JButton btn1 = new JButton("press me"); + btn1.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + System.out.println("waiting..."); + try { + Thread.sleep(3000); + } catch (InterruptedException ie) { + ie.printStackTrace(); + } + System.out.println("requesting focus"); + btn2.requestFocus(); + } + }); + frame.setLayout(new FlowLayout()); + frame.add(btn1); + frame.add(btn2); + frame.pack(); + frame.setLocation(200, 100); + + JFrame frame2 = new JFrame("opposite frame"); + JButton btn3 = new JButton("just a button"); + frame2.add(btn3); + frame2.pack(); + frame2.setLocation(200, 200); + + ArrayList list = new ArrayList<>(); + list.add(frame); + list.add(frame2); + return list; + } + +} diff --git a/test/jdk/java/awt/Focus/TestDisabledAutoTransfer.java b/test/jdk/java/awt/Focus/TestDisabledAutoTransfer.java new file mode 100644 index 0000000000000..d7928b7db1c70 --- /dev/null +++ b/test/jdk/java/awt/Focus/TestDisabledAutoTransfer.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* + * @test + * @bug 6180261 + * @summary Test that auto-transfer doesn't happen when there are pending focus requests + * @key headful + * @run main TestDisabledAutoTransfer +*/ + +import java.awt.Button; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.InputEvent; +import java.util.concurrent.atomic.AtomicBoolean; + +public class TestDisabledAutoTransfer { + static Frame frame; + static Robot robot; + Button b1; + Button desired; + AtomicBoolean focused = new AtomicBoolean(); + ActionListener mover; + volatile Point loc; + volatile Dimension dim; + + public static void main(String[] args) throws Exception { + robot = new Robot(); + try { + TestDisabledAutoTransfer test = new TestDisabledAutoTransfer(); + test.createTestUI(); + robot.waitForIdle(); + robot.delay(1000); + test.doTest(); + } finally { + if (frame != null) { + frame.dispose(); + } + } + } + + public void createTestUI() { + frame = new Frame("TestDisabledAutoTransfer"); + frame.setLayout(new FlowLayout()); + desired = new Button("Desired"); + FocusAdapter watcher = new FocusAdapter() { + public void focusGained(FocusEvent e) { + synchronized(focused) { + focused.set(true); + } + } + }; + b1 = new Button("Press to disable"); + mover = new ActionListener() { + public void actionPerformed(ActionEvent e) { + desired.requestFocus(); + ((Component)e.getSource()).setEnabled(false); + } + }; + b1.addFocusListener(watcher); + desired.addFocusListener(watcher); + frame.add(b1); + Button misc = new Button("Next"); + frame.add(misc); + misc.addFocusListener(watcher); + frame.add(desired); + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + frame.validate(); + + } + + public void doTest() { + + loc = b1.getLocationOnScreen(); + dim = b1.getSize(); + robot.mouseMove(loc.x + dim.width / 2, loc.y + dim.height / 2); + robot.waitForIdle(); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + b1.requestFocus(); + + try { + synchronized(focused) { + if (!focused.get()) { + focused.wait(1000); + } + } + } catch (InterruptedException ie) { + throw new RuntimeException("Test was interrupted"); + } + + if (!focused.get()) { + throw new RuntimeException("b1 didn't get focus"); + } + focused.set(false); + + b1.addActionListener(mover); + robot.mouseMove(loc.x + dim.width / 2, loc.y + dim.height / 2); + robot.waitForIdle(); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + + try { + synchronized(focused) { + if (!focused.get()) { + focused.wait(1000); + } + } + } catch (InterruptedException ie) { + throw new RuntimeException("Test was interrupted"); + } + + if (!focused.get()) { + throw new RuntimeException("none got focus"); + } + + if (!desired.isFocusOwner()) { + throw new RuntimeException("desired didn't get focus"); + } + } + +} + diff --git a/test/jdk/java/awt/Focus/TestDisabledAutoTransferSwing.java b/test/jdk/java/awt/Focus/TestDisabledAutoTransferSwing.java new file mode 100644 index 0000000000000..0793316a989cd --- /dev/null +++ b/test/jdk/java/awt/Focus/TestDisabledAutoTransferSwing.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* + * @test + * @bug 6180261 + * @summary Test that auto-transfer doesn't happen when there are pending focus requests + * @key headful + * @run main TestDisabledAutoTransferSwing +*/ + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Point; +import java.awt.Robot; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.util.concurrent.atomic.AtomicBoolean; + +public class TestDisabledAutoTransferSwing { + static JFrame frame; + static Robot robot; + JButton b1; + JButton desired; + AtomicBoolean focused = new AtomicBoolean(); + ActionListener mover; + volatile Point loc; + volatile Dimension dim; + + public static void main(String[] args) throws Exception { + robot = new Robot(); + try { + TestDisabledAutoTransferSwing test = new TestDisabledAutoTransferSwing(); + SwingUtilities.invokeAndWait(() -> { + test.createTestUI(); + }); + robot.waitForIdle(); + robot.delay(1000); + test.doTest(); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + public void createTestUI() { + frame = new JFrame("TestDisabledAutoTransferSwing"); + frame.setLayout (new FlowLayout ()); + desired = new JButton("Desired"); + FocusAdapter watcher = new FocusAdapter() { + public void focusGained(FocusEvent e) { + synchronized(focused) { + focused.set(true); + } + } + }; + b1 = new JButton("Press to disable"); + mover = new ActionListener() { + public void actionPerformed(ActionEvent e) { + desired.requestFocus(); + ((Component)e.getSource()).setEnabled(false); + } + }; + b1.addFocusListener(watcher); + desired.addFocusListener(watcher); + frame.add(b1); + JButton misc = new JButton("Next"); + frame.add(misc); + misc.addFocusListener(watcher); + frame.add(desired); + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + frame.validate(); + + } + + public void doTest() throws Exception { + + SwingUtilities.invokeAndWait(() -> { + loc = b1.getLocationOnScreen(); + dim = b1.getSize(); + }); + robot.mouseMove(loc.x + dim.width / 2, loc.y + dim.height / 2); + robot.waitForIdle(); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + SwingUtilities.invokeAndWait(() -> { + b1.requestFocus(); + }); + + try { + synchronized(focused) { + if (!focused.get()) { + focused.wait(2000); + } + } + } catch (InterruptedException ie) { + throw new RuntimeException("Test was interrupted"); + } + + if (!focused.get()) { + throw new RuntimeException("b1 didn't get focus"); + } + focused.set(false); + + SwingUtilities.invokeAndWait(() -> { + b1.addActionListener(mover); + }); + robot.mouseMove(loc.x + dim.width / 2, loc.y + dim.height / 2); + robot.waitForIdle(); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + + try { + synchronized(focused) { + if (!focused.get()) { + focused.wait(2000); + } + } + } catch (InterruptedException ie) { + throw new RuntimeException("Test was interrupted"); + } + + if (!focused.get()) { + throw new RuntimeException("none got focus"); + } + + if (!desired.isFocusOwner()) { + throw new RuntimeException("desired didn't get focus"); + } + } + +} diff --git a/test/jdk/java/awt/Focus/WindowDisposeFocusTest.java b/test/jdk/java/awt/Focus/WindowDisposeFocusTest.java new file mode 100644 index 0000000000000..5e2e49147e54d --- /dev/null +++ b/test/jdk/java/awt/Focus/WindowDisposeFocusTest.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4257071 4228379 + * @summary Ensures that focus lost is delivered to a lightweight component + in a disposed window + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual WindowDisposeFocusTest +*/ + +import java.awt.Window; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JPanel; + +public class WindowDisposeFocusTest { + + private static final String INSTRUCTIONS = """ + Click on "Second" + Click on close box + When dialog pops up, "Second" should no longer have focus."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("WindowDisposeFocusTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(WindowDisposeFocusTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Window createTestUI() { + return JFCFocusBug2.test(new String[]{}); + } +} + +class JFCFocusBug2 extends JPanel { + + static public Window test(String[] args) { + final JFrame frame = new JFrame("WindowDisposeFrame"); + frame.setSize(100, 100); + frame.setVisible(true); + + final JFCFocusBug2 bug = new JFCFocusBug2(); + final JDialog dialog = new JDialog(frame, false); + dialog.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + dialog.dispose(); + JDialog dialog2 = new JDialog(frame, false); + dialog2.setContentPane(bug); + dialog2.pack(); + dialog2.setVisible(true); + } + }); + dialog.setContentPane(bug); + dialog.pack(); + dialog.setVisible(true); + return frame; + } + + public JFCFocusBug2() { + _first = new JButton("First"); + _second = new JButton("Second"); + add(_first); + add(_second); + } + + private JButton _first; + private JButton _second; +} diff --git a/test/jdk/java/awt/Focus/bug6435715.java b/test/jdk/java/awt/Focus/bug6435715.java new file mode 100644 index 0000000000000..f13f731169ff7 --- /dev/null +++ b/test/jdk/java/awt/Focus/bug6435715.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2006, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6435715 + * @summary JButton stops receiving the focusGained event and eventually focus is lost altogether + * @modules java.desktop/sun.awt + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug6435715 + */ + +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; + +public class bug6435715 { + + private static final String INSTRUCTIONS = """ + 1. after test started you will see frame with three buttons. Notice that focus is on Button2. + 2. Click on Button 3. Focus goes to Button3. + 3. Click on Button1 and quickly switch to another window. Via either alt/tab or + clicking another window with the mouse. + 4. After a few seconds, come back to the frame. Notice that focus is around Button2 + 5. Click at Button3. If focus remains at Button2 test failed, if focus is on Button3 - test passed."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug6435715 Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 5) + .columns(35) + .testUI(bug6435715::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + JFrame fr = new JFrame("FocusIssue"); + sun.awt.SunToolkit.setLWRequestStatus(fr, true); + + JPanel panel = new JPanel(); + final JButton b1 = new JButton("Button 1"); + final JButton b2 = new JButton("Button 2"); + final JButton b3 = new JButton("Button 3"); + + panel.add(b1); + panel.add(b2); + panel.add(b3); + + b1.addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent event) { + synchronized (this) { + try { + wait(1000); + } catch (Exception ex) { + ex.printStackTrace(); + } + b2.requestFocus(); + } + } + }); + fr.getContentPane().add(panel); + fr.pack(); + return fr; + } + +} diff --git a/test/jdk/java/awt/FontClass/FontTransformAttributeTest.java b/test/jdk/java/awt/FontClass/FontTransformAttributeTest.java new file mode 100644 index 0000000000000..7bbf5d11d28b4 --- /dev/null +++ b/test/jdk/java/awt/FontClass/FontTransformAttributeTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.font.TextAttribute; +import java.awt.font.TransformAttribute; +import java.awt.geom.AffineTransform; +import java.text.AttributedCharacterIterator; +import java.text.AttributedString; + +import javax.swing.JPanel; + +/* + * @test + * @bug 4650042 + * @summary Draw text using a transform to simulate superscript, it should look like a superscript + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FontTransformAttributeTest + */ + +public class FontTransformAttributeTest extends JPanel { + AttributedCharacterIterator iter; + + public static void main(String[] args) throws Exception { + final String INSTRUCTIONS = """ + This test should display a string ending with the superscripted number '11'. + Pass the test if you see the superscript."""; + + PassFailJFrame.builder() + .title("FontTransformAttributeTest Instruction") + .instructions(INSTRUCTIONS) + .columns(35) + .splitUI(FontTransformAttributeTest::new) + .build() + .awaitAndCheck(); + } + + FontTransformAttributeTest() { + AffineTransform superTransform = AffineTransform.getScaleInstance(0.65, 0.65); + superTransform.translate(0, -7); + TransformAttribute superAttribute = new TransformAttribute(superTransform); + String s = "a big number 7 11"; + AttributedString as = new AttributedString(s); + as.addAttribute(TextAttribute.TRANSFORM, superAttribute, 15, 17); + iter = as.getIterator(); + setBackground(Color.WHITE); + } + + @Override + public Dimension getPreferredSize() { + return new Dimension(200, 100); + } + + @Override + public void paint(Graphics g) { + Graphics2D g2 = (Graphics2D) g; + Dimension d = getSize(); + g2.drawString(iter, 20, d.height / 2 + 8); + } +} diff --git a/test/jdk/java/awt/FontClass/FontUnderscoreTest.java b/test/jdk/java/awt/FontClass/FontUnderscoreTest.java new file mode 100644 index 0000000000000..101a0b4908867 --- /dev/null +++ b/test/jdk/java/awt/FontClass/FontUnderscoreTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Graphics; + +import javax.swing.JPanel; + +/* + * @test + * @bug 4248579 + * @summary Make sure the underscore glyph appears in the different strings + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FontUnderscoreTest + */ + +public class FontUnderscoreTest extends JPanel { + public static void main(String[] args) throws Exception { + final String INSTRUCTIONS = """ + Make sure all 8 underscore characters appear in each + of the 3 strings. + + Press PASS if all 8 are there, else FAIL."""; + + PassFailJFrame.builder() + .title("FontUnderscoreTest Instruction") + .instructions(INSTRUCTIONS) + .columns(35) + .splitUI(FontUnderscoreTest::new) + .build() + .awaitAndCheck(); + } + + @Override + public Dimension getPreferredSize() { + return new Dimension(550, 230); + } + + @Override + public void paint(Graphics g) { + Font f = new Font(Font.SANS_SERIF, Font.PLAIN, 24); + g.setFont(f); + g.drawString ("8 underscore characters appear in each string", 5, 200); + + g.drawString("J_A_V_A_2_j_a_v_a", 25, 50); + + f = new Font(Font.SERIF, Font.PLAIN, 24); + g.setFont(f); + g.drawString("J_A_V_A_2_j_a_v_a", 25, 100); + + f = new Font(Font.MONOSPACED, Font.PLAIN, 24); + g.setFont(f); + g.drawString("J_A_V_A_2_j_a_v_a", 25, 150); + } +} diff --git a/test/jdk/java/awt/FontMetrics/ExtremeFontSizeTest.java b/test/jdk/java/awt/FontMetrics/ExtremeFontSizeTest.java index caa365a3f219f..f596f710831c0 100644 --- a/test/jdk/java/awt/FontMetrics/ExtremeFontSizeTest.java +++ b/test/jdk/java/awt/FontMetrics/ExtremeFontSizeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,16 +24,19 @@ import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; +import java.awt.GraphicsEnvironment; import java.awt.Rectangle; import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; +import java.util.HashMap; +import java.util.Map; /* * @test - * @bug 8328896 + * @bug 8328896 8357672 * @summary test that using very large font sizes used don't break later uses */ @@ -49,34 +52,106 @@ public class ExtremeFontSizeTest { static double[] scales = { 1.0, 900.0}; static boolean[] fms = { false, true }; + static class Key { + int fontSize; + double scale; + boolean fm; + String str; + + + Key(int fs, double sc, boolean f, String s) { + fontSize = fs; + scale = sc; + fm = f; + str = s; + } + + public boolean equals(Object o) { + return + (o instanceof Key k) && + this.fontSize == k.fontSize && + this.scale == k.scale && + this.fm == k.fm && + this.str.equals(k.str); + } + + public int hashCode() { + return fontSize + (int)scale + (fm ? 1 : 0) + str.hashCode(); + } + } + + static class Value { + int height; + double strBounds; + Rectangle pixelBounds; + Rectangle2D visualBounds; + + Value(int h, double sb, Rectangle pb, Rectangle2D vb) { + height = h; + strBounds = sb; + pixelBounds = pb; + visualBounds = vb; + } + + public boolean equals(Object o) { + return + (o instanceof Value v) && + this.height == v.height && + this.strBounds == v.strBounds && + this.pixelBounds.equals(v.pixelBounds) && + this.visualBounds.equals(v.visualBounds); + } + + public int hashCode() { + return height + (int)strBounds + pixelBounds.hashCode() + visualBounds.hashCode(); + } + } + + static Map metricsMap = new HashMap(); + public static void main(String[] args) { + Font[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts(); + for (Font f : fonts) { + font = f.deriveFont(Font.PLAIN, 12); + System.out.println("Test font : " + font); + if (font.canDisplayUpTo(testString) != -1) { + System.out.println("Skipping since cannot display test string"); + continue; + } + metricsMap = new HashMap(); + testFont(); + } + } + + static void testFont() { /* run tests validating bounds etc are non-zero * then run with extreme scales for which zero is allowed - but not required * then run the first tests again to be sure they are still reasonable. */ - runTests(); - test(5_000_000, 10_000, false, testString, false); - test(5_000_000, 10_000, true, testString, false); - test(0, 0.00000001, false, testString, false); - runTests(); + runTests(true, false); + test(5_000_000, 10_000, false, testString, false, false, false); + test(5_000_000, 10_000, true, testString, false, false, false); + test(0, 0.00000001, false, testString, false, false, false); + runTests(false, true); if (failed) { throw new RuntimeException("Test failed. Check stdout log."); } } - static void runTests() { + static void runTests(boolean add, boolean check) { for (int fontSize : fontSizes) { for (double scale : scales) { for (boolean fm : fms) { - test(fontSize, scale, fm, testString, true); + test(fontSize, scale, fm, testString, true, add, check); } } } } - static void test(int size, double scale, boolean fm, String str, boolean checkAll) { + static void test(int size, double scale, boolean fm, String str, + boolean checkAll, boolean add, boolean check) { AffineTransform at = AffineTransform.getScaleInstance(scale, scale); FontRenderContext frc = new FontRenderContext(at, false, fm); @@ -114,5 +189,22 @@ static void test(int size, double scale, boolean fm, String str, boolean checkAl System.out.println(" *** RESULTS NOT AS EXPECTED *** "); } System.out.println(); + + Key k = null; + Value v = null; + if (add || check) { + k = new Key(size, scale, fm, str); + v = new Value(height, width, pixelBounds, visualBounds); + } + if (add) { + metricsMap.put(k, v); + } + if (check) { + Value vmap = metricsMap.get(k); + if (!v.equals(vmap)) { + failed = true; + System.out.println("Values differ"); + } + } } } diff --git a/test/jdk/java/awt/Frame/AddRemoveMenuBarTest_5.java b/test/jdk/java/awt/Frame/AddRemoveMenuBarTest_5.java new file mode 100644 index 0000000000000..f5f1018c26282 --- /dev/null +++ b/test/jdk/java/awt/Frame/AddRemoveMenuBarTest_5.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.Robot; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +/* + * @test + * @key headful + * @bug 4159883 + * @summary Adding/Removing a menu causes frame to unexpected small size + * @requires (os.family == "linux" | os.family == "windows") + */ + +public class AddRemoveMenuBarTest_5 { + + static Frame frame; + static MenuBar menu; + static Button btnAdd, btnRemove; + static Dimension oldSize; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + try { + EventQueue.invokeAndWait(AddRemoveMenuBarTest_5::initAndShowGui); + robot.waitForIdle(); + robot.delay(500); + + EventQueue.invokeAndWait(() -> { + oldSize = frame.getSize(); + changeMenubar(true); + }); + robot.waitForIdle(); + robot.delay(500); + + EventQueue.invokeAndWait(() -> { + checkSize(); + changeMenubar(false); + }); + robot.waitForIdle(); + robot.delay(500); + + EventQueue.invokeAndWait(AddRemoveMenuBarTest_5::checkSize); + } finally { + EventQueue.invokeAndWait(frame::dispose); + } + } + + public static void initAndShowGui() { + frame = new Frame(); + frame.setLocationRelativeTo(null); + frame.addWindowListener(new WindowAdapter() { + @Override + public void windowOpened(WindowEvent e) { + System.out.println("Frame size:" + frame.getSize().toString()); + System.out.println("Button size:" + btnAdd.getSize().toString()); + } + }); + frame.add("West", btnAdd = new Button("TRY:ADD")); + frame.add("East", btnRemove = new Button("TRY:REMOVE")); + + + btnAdd.addActionListener((e) -> changeMenubar(true)); + btnRemove.addActionListener((e) -> changeMenubar(false)); + frame.setSize(500, 100); + frame.setVisible(true); + } + + private static void changeMenubar(boolean enable) { + if (enable) { + menu = new MenuBar(); + menu.add(new Menu("BAAAAAAAAAAAAAAA")); + menu.add(new Menu("BZZZZZZZZZZZZZZZ")); + menu.add(new Menu("BXXXXXXXXXXXXXXX")); + } else { + menu = null; + } + frame.setMenuBar(menu); + frame.invalidate(); + frame.validate(); + + System.out.println("Frame size:" + frame.getSize().toString()); + System.out.println("Button size:" + btnAdd.getSize().toString()); + } + + private static void checkSize() { + Dimension newSize = frame.getSize(); + if (!oldSize.equals(newSize)) { + throw new RuntimeException("Frame size changed: old %s new %s" + .formatted(oldSize, newSize)); + } + } +} diff --git a/test/jdk/java/awt/Frame/DefaultLocationTest.java b/test/jdk/java/awt/Frame/DefaultLocationTest.java new file mode 100644 index 0000000000000..dfeb5e93e3c89 --- /dev/null +++ b/test/jdk/java/awt/Frame/DefaultLocationTest.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Label; + +/* + * @test + * @bug 4085599 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Test default location for frame + * @run main/manual DefaultLocationTest + */ + +public class DefaultLocationTest { + private static Frame f; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + A small frame containing the label 'Hello World' should + appear in the upper left hand corner of the screen. The + exact location is dependent upon the window manager. + + On Linux and Mac machines, the default location for frame + is below the taskbar or close to top-left corner. + + Upon test completion, click Pass or Fail appropriately."""; + + PassFailJFrame passFailJFrame = new PassFailJFrame("DefaultLocationTest " + + " Instructions", INSTRUCTIONS, 5, 10, 40); + EventQueue.invokeAndWait(DefaultLocationTest::createAndShowUI); + passFailJFrame.awaitAndCheck(); + } + + private static void createAndShowUI() { + f = new Frame("DefaultLocation"); + f.add("Center", new Label("Hello World")); + f.pack(); + PassFailJFrame.addTestWindow(f); + PassFailJFrame.positionTestWindow( + null, PassFailJFrame.Position.HORIZONTAL); + f.setVisible(true); + } +} diff --git a/test/jdk/java/awt/Frame/DeiconifyClipTest.java b/test/jdk/java/awt/Frame/DeiconifyClipTest.java new file mode 100644 index 0000000000000..c650355f3ec73 --- /dev/null +++ b/test/jdk/java/awt/Frame/DeiconifyClipTest.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * DeiconifyClipTest.java + * + * summary: + * + * What happens is that we call AwtWindow::UpdateInsets when + * processing WM_NCCALCSIZE delivered on programmatic deiconification. + * At this point IsIconic returns false (so UpdateInsets proceeds), + * but the rect sizes still seems to be those weird of the iconic + * state. Based on them we compute insets with top = left = 0 (and + * bottom and right that are completely bogus) and pass them to + * PaintUpdateRgn which results in incorrect clip origin. Immediately + * after that we do UpdateInsets again during WM_SIZE processing and + * get real values. + */ + +import javax.swing.BoxLayout; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Insets; + +/* + * @test + * @bug 4792958 + * @summary Incorrect clip region after programmatic restore + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DeiconifyClipTest +*/ + +public class DeiconifyClipTest { + private static final String INSTRUCTIONS = """ + This test creates a frame that is automatically iconified/deiconified + in a cycle. + + The test FAILS if after deiconfication the frame has a greyed-out area + in the lower-right corner. + If the frame contents is drawn completely - the test PASSES. + + Press PASS or FAIL button accordingly. + """; + + static TestFrame testFrame; + static volatile boolean shouldContinue = true; + + public static void main(String[] args) throws Exception { + PassFailJFrame passFailJFrame = PassFailJFrame.builder() + .title("DeiconifyClipTest Instructions") + .instructions(INSTRUCTIONS) + .columns(45) + .testUI(DeiconifyClipTest::createAndShowUI) + .build(); + try { + runThread(); + } finally { + passFailJFrame.awaitAndCheck(); + shouldContinue = false; + } + } + + private static void runThread() { + new Thread(() -> { + for (int i = 0; i < 1000 && shouldContinue; ++i) { + try { + Thread.sleep(3000); + SwingUtilities.invokeAndWait(() -> { + if ((testFrame.getExtendedState() & Frame.ICONIFIED) + != 0) { + testFrame.setExtendedState(Frame.NORMAL); + } else { + testFrame.setState(Frame.ICONIFIED); + } + }); + } catch (Exception ignored) { + } + } + }).start(); + } + + static Frame createAndShowUI() { + testFrame = new TestFrame(); + testFrame.getContentPane().setLayout(new BoxLayout(testFrame.getContentPane(), + BoxLayout.Y_AXIS)); + testFrame.getContentPane().setBackground(Color.yellow); + testFrame.setSize(300, 300); + return testFrame; + } + + static class TestFrame extends JFrame { + public TestFrame() { + super("DeiconifyClipTest"); + } + + // make it more visible if the clip is wrong. + public void paint(Graphics g) { + Insets b = getInsets(); + Dimension d = getSize(); + + int x = b.left; + int y = b.top; + int w = d.width - x - b.right; + int h = d.height - y - b.bottom; + + g.setColor(Color.white); + g.fillRect(0, 0, d.width, d.height); + + g.setColor(Color.green); + g.drawRect(x, y, w-1, h-1); + g.drawLine(x, y, x+w, y+h); + g.drawLine(x, y+h, x+w, y); + } + } +} diff --git a/test/jdk/java/awt/Frame/DisabledParentOfToplevel.java b/test/jdk/java/awt/Frame/DisabledParentOfToplevel.java new file mode 100644 index 0000000000000..878809749d35a --- /dev/null +++ b/test/jdk/java/awt/Frame/DisabledParentOfToplevel.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Robot; +import java.awt.Window; +import java.awt.event.InputEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +/* + * @test + * @bug 5062118 + * @key headful + * @summary Disabling of a parent should not disable Window. + * @run main DisabledParentOfToplevel + */ + +public class DisabledParentOfToplevel { + private static Button okBtn; + private static Window ww; + private static Frame parentFrame; + private static volatile Point p; + private static volatile Dimension d; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + robot.setAutoDelay(100); + try { + EventQueue.invokeAndWait(() -> { + createAndShowUI(); + }); + robot.delay(1000); + EventQueue.invokeAndWait(() -> { + p = okBtn.getLocationOnScreen(); + d = okBtn.getSize(); + }); + robot.mouseMove(p.x + d.width / 2, p.x + d.height / 2); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.delay(500); + if (ww.isVisible()) { + throw new RuntimeException("Window is visible but should be hidden: failure."); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (parentFrame != null) { + parentFrame.dispose(); + } + }); + } + } + + private static void createAndShowUI() { + parentFrame = new Frame("parentFrame"); + parentFrame.setSize(100, 100); + parentFrame.setEnabled(false); + ww = new Window(parentFrame); + ww.setLayout(new BorderLayout()); + okBtn = new Button("Click to Close Me"); + ww.add(okBtn); + ww.setSize(250, 250); + ww.setLocation(110, 110); + okBtn.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent me) { + System.out.println("Pressed: close"); + ww.setVisible(false); + } + }); + parentFrame.setVisible(true); + ww.setVisible(true); + okBtn.requestFocus(); + } +} diff --git a/test/jdk/java/awt/Frame/DisposeParentGC/DisposeParentGC.java b/test/jdk/java/awt/Frame/DisposeParentGC/DisposeParentGC.java index d6d7ae82b3a62..88b4beb09b75a 100644 --- a/test/jdk/java/awt/Frame/DisposeParentGC/DisposeParentGC.java +++ b/test/jdk/java/awt/Frame/DisposeParentGC/DisposeParentGC.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,7 +21,25 @@ * questions. */ -import java.awt.*; +import java.awt.Button; +import java.awt.Canvas; +import java.awt.Checkbox; +import java.awt.CheckboxGroup; +import java.awt.Choice; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Label; +import java.awt.List; +import java.awt.Point; +import java.awt.Scrollbar; +import java.awt.TextArea; +import java.awt.TextField; +import java.awt.Toolkit; +import java.awt.Frame; +import java.awt.FlowLayout; import java.awt.image.BufferedImage; import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue; @@ -34,7 +52,6 @@ * @summary Display a dialog with a parent, the dialog contains all awt components * added to it & each components are setted with different cursors types. * Dispose the parent & collect GC. Garbage collection should happen - * @author Dmitriy Ermashov (dmitriy.ermashov@oracle.com) * @library /lib/client * @build ExtendedRobot * @run main/othervm -Xmx20m DisposeParentGC @@ -100,7 +117,7 @@ public void createDialog(int number) { child.setLocation(20, 140 * number); Button button = new Button("Press Me") ; - TextArea textArea = new TextArea(5,5); + TextArea textArea = new TextArea(5, 5); TextField textField = new TextField(10); Choice choice = new Choice(); choice.add("One"); @@ -115,15 +132,15 @@ public void createDialog(int number) { list.add("Four"); list.add("Five"); Checkbox checkBox = new Checkbox("Hai"); - Scrollbar scrollBar = new Scrollbar(Scrollbar.VERTICAL,0,1,0,200); + Scrollbar scrollBar = new Scrollbar(Scrollbar.VERTICAL, 0, 1, 0, 200); CheckboxGroup checkboxGroup = new CheckboxGroup(); - Checkbox radioButton = new Checkbox("Hello" ,true, checkboxGroup); + Checkbox radioButton = new Checkbox("Hello", true, checkboxGroup); Canvas canvas = new Canvas(); Label label = new Label("I am label!"); Cursor customCursor = null; child.setLayout(new FlowLayout()); - canvas.setSize(100,100); + canvas.setSize(100, 100); canvas.setBackground(Color.red); button.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR)); @@ -138,13 +155,17 @@ public void createDialog(int number) { /* create a custom cursor */ Toolkit toolkit = Toolkit.getDefaultToolkit(); - Dimension d = toolkit.getBestCursorSize(32,32); + Dimension d = toolkit.getBestCursorSize(32, 32); int color = toolkit.getMaximumCursorColors(); - if(!d.equals(new Dimension(0,0)) && color != 0 ) - customCursor = toolkit.createCustomCursor(new BufferedImage( 16, 16, BufferedImage.TYPE_INT_RGB ), new Point(10, 10), "custom cursor."); - else + if (!d.equals(new Dimension(0,0)) && color != 0) { + customCursor = toolkit.createCustomCursor( + new BufferedImage(16, 16, BufferedImage.TYPE_INT_RGB), + new Point(10, 10), "custom cursor."); + } + else { System.err.println("Platform doesn't support to create a custom cursor."); + } textArea.setCursor(customCursor); child.add(label); diff --git a/test/jdk/java/awt/Frame/EmptyFrameTest.java b/test/jdk/java/awt/Frame/EmptyFrameTest.java new file mode 100644 index 0000000000000..7a324a3cbc994 --- /dev/null +++ b/test/jdk/java/awt/Frame/EmptyFrameTest.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import javax.imageio.ImageIO; + +/* + * @test + * @bug 4237529 + * @key headful + * @summary Test repainting of an empty frame + * @run main EmptyFrameTest + */ + +public class EmptyFrameTest { + private static Frame f; + private static Robot robot; + private static volatile Point p; + private static volatile Dimension d; + private static final int TOLERANCE = 5; + public static void main(String[] args) throws Exception { + robot = new Robot(); + robot.setAutoDelay(100); + try { + EventQueue.invokeAndWait(() -> { + createAndShowUI(); + }); + robot.delay(1000); + f.setSize(50, 50); + robot.delay(500); + EventQueue.invokeAndWait(() -> { + p = f.getLocation(); + d = f.getSize(); + }); + Rectangle rect = new Rectangle(p, d); + BufferedImage img = robot.createScreenCapture(rect); + if (chkImgBackgroundColor(img)) { + try { + ImageIO.write(img, "png", new File("Frame.png")); + } catch (IOException ignored) {} + throw new RuntimeException("Frame doesn't repaint itself on resize"); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + } + } + + private static void createAndShowUI() { + f = new Frame("EmptyFrameTest"); + f.setUndecorated(true); + f.setBackground(Color.RED); + f.setVisible(true); + } + + private static boolean chkImgBackgroundColor(BufferedImage img) { + for (int x = 1; x < img.getWidth() - 1; ++x) { + for (int y = 1; y < img.getHeight() - 1; ++y) { + Color c = new Color(img.getRGB(x, y)); + if ((c.getRed() - Color.RED.getRed()) > TOLERANCE) { + return true; + } + } + } + return false; + } +} diff --git a/test/jdk/java/awt/Frame/FocusTest.java b/test/jdk/java/awt/Frame/FocusTest.java new file mode 100644 index 0000000000000..14d194789fe94 --- /dev/null +++ b/test/jdk/java/awt/Frame/FocusTest.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Canvas; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.GridLayout; +import java.awt.Panel; +import java.awt.Window; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +/* + * @test + * @bug 4140293 + * @summary Tests that focus is returned to the correct Component when a Frame + * is reactivated. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FocusTest + */ + +public class FocusTest { + private static final String INSTRUCTIONS = """ + Click on the bottom rectangle. Move the mouse slightly. + A focus rectangle should appear around the bottom rectangle. + + Now, deactivate the window and then reactivate it. + (You would click on the caption bar of another window, + and then on the caption bar of the FocusTest Frame.) + + If the focus rectangle appears again, the test passes. + If it does not appear, or appears around the top rectangle, + the test fails. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("FocusTest Instructions") + .instructions(INSTRUCTIONS) + .columns(45) + .logArea(6) + .testUI(FocusTest::createAndShowUI) + .build() + .awaitAndCheck(); + } + + private static Window createAndShowUI() { + Frame frame = new Frame("FocusTest"); + + frame.add(new FocusTestPanel()); + frame.setSize(400, 400); + + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + frame.dispose(); + } + }); + + frame.validate(); + return frame; + } + + private static class FocusTestPanel extends Panel { + PassiveClient pc1 = new PassiveClient("pc1"); + PassiveClient pc2 = new PassiveClient("pc2"); + + public FocusTestPanel() { + super(); + setLayout(new GridLayout(2, 1, 10, 10)); + add(pc1); + add(pc2); + } + } + + private static class PassiveClient extends Canvas implements FocusListener { + boolean haveFocus = false; + final String name; + + PassiveClient(String name) { + super(); + this.name = name; + setSize(400, 100); + setBackground(Color.cyan); + setVisible(true); + setEnabled(true); + addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + requestFocus(); + } + }); + addFocusListener(this); + } + + public void paint(Graphics g) { + g.setColor(getBackground()); + Dimension size = getSize(); + g.fillRect(0, 0, size.width, size.height); + if (haveFocus) { + g.setColor(Color.black); + g.drawRect(0, 0, size.width - 1, size.height - 1); + g.drawRect(1, 1, size.width - 3, size.height - 3); + } + g.setColor(getForeground()); + } + + public void focusGained(FocusEvent event) { + haveFocus = true; + paint(getGraphics()); + PassFailJFrame.log("<<<< %s Got focus!! %s>>>>".formatted(this, event)); + } + + public void focusLost(FocusEvent event) { + haveFocus = false; + paint(getGraphics()); + PassFailJFrame.log("<<<< %s Lost focus!! %s>>>>".formatted(this, event)); + } + + @Override + public String toString() { + return "PassiveClient " + name; + } + } +} diff --git a/test/jdk/java/awt/Frame/FrameLayoutTest.java b/test/jdk/java/awt/Frame/FrameLayoutTest.java new file mode 100644 index 0000000000000..d6e994beaace0 --- /dev/null +++ b/test/jdk/java/awt/Frame/FrameLayoutTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Frame; + +/* + * @test + * @bug 4173503 + * @library /java/awt/regtesthelpers + * @requires (os.family == "windows") + * @build PassFailJFrame + * @summary Tests that frame layout is performed when frame is maximized from taskbar + * @run main/manual FrameLayoutTest + */ + +public class FrameLayoutTest { + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Right-click on the taskbar button for this test. In the menu appeared, + choose Maximize. The frame will be maximized. Check if buttons inside + the frame are laid out properly, i.e. they occupy the frame entirely. + + If so, test passes. If buttons occupy small rectangle in the top left + corner, test fails."""; + + PassFailJFrame.builder() + .title("Frame's Layout Test Instruction") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testUI(FrameLayoutTest::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame f = new Frame("Maximize Test"); + f.add(new Button("North"), BorderLayout.NORTH); + f.add(new Button("South"), BorderLayout.SOUTH); + f.add(new Button("East"), BorderLayout.EAST); + f.add(new Button("West"), BorderLayout.WEST); + f.add(new Button("Cent"), BorderLayout.CENTER); + f.pack(); + f.setState(Frame.ICONIFIED); + return f; + } +} diff --git a/test/jdk/java/awt/Frame/FrameMaximizedTest.java b/test/jdk/java/awt/Frame/FrameMaximizedTest.java new file mode 100644 index 0000000000000..368132937fb4b --- /dev/null +++ b/test/jdk/java/awt/Frame/FrameMaximizedTest.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Frame; +import java.awt.Label; + +/* + * @test + * @bug 4106068 + * @summary Test to verify maximized window is not too big + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FrameMaximizedTest + */ + +public class FrameMaximizedTest { + public static void main (String[] args) throws Exception { + String INSTRUCTIONS = """ + Maximize the frame window. Check that the right and bottom edges of the + window are not off the edge of the screen. If they are not, the test + is successful and the bug is fixed. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows(4) + .columns(40) + .testUI(new TestFrame()) + .build() + .awaitAndCheck(); + } +} + +class TestFrame extends Frame { + public TestFrame() { + setTitle("FrameMaximizedTest"); + setSize(500, 300); + add("North", new Label("Maximize me and check if my " + + "bottom and right edge are on screen.")); + } +} diff --git a/test/jdk/java/awt/Frame/FrameMenuPackTest.java b/test/jdk/java/awt/Frame/FrameMenuPackTest.java new file mode 100644 index 0000000000000..c034acd8eb710 --- /dev/null +++ b/test/jdk/java/awt/Frame/FrameMenuPackTest.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.ScrollPane; +import java.awt.Window; +import java.util.List; + +/* + * @test + * @bug 4084766 + * @summary Test for bug(s): 4084766 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FrameMenuPackTest + */ + +public class FrameMenuPackTest { + private static final String INSTRUCTIONS = """ + Check that both frames that appear are properly packed with + the scrollpane visible. + """; + + public static void main(String[] argv) throws Exception { + PassFailJFrame.builder() + .title("FrameMenuPackTest Instructions") + .instructions(INSTRUCTIONS) + .columns(45) + .testUI(FrameMenuPackTest::createAndShowUI) + .positionTestUIRightRow() + .build() + .awaitAndCheck(); + } + + private static List createAndShowUI() { + // Frame without menu, packs correctly + PackedFrame f1 = new PackedFrame(false); + f1.pack(); + + // Frame with menu, doesn't pack right + PackedFrame f2 = new PackedFrame(true); + f2.pack(); + + return List.of(f1, f2); + } + + private static class PackedFrame extends Frame { + public PackedFrame(boolean withMenu) { + super("PackedFrame"); + + MenuBar menubar; + Menu fileMenu; + MenuItem foo; + ScrollPane sp; + + sp = new ScrollPane(); + sp.add(new Label("Label in ScrollPane")); + System.out.println(sp.getMinimumSize()); + + this.setLayout(new BorderLayout()); + this.add(sp, "Center"); + this.add(new Label("Label in Frame"), "South"); + + if (withMenu) { + menubar = new MenuBar(); + fileMenu = new Menu("File"); + foo = new MenuItem("foo"); + fileMenu.add(foo); + menubar.add(fileMenu); + this.setMenuBar(menubar); + } + } + } +} diff --git a/test/jdk/java/awt/Frame/FrameMinimizeTest.java b/test/jdk/java/awt/Frame/FrameMinimizeTest.java new file mode 100644 index 0000000000000..cd3459a78418d --- /dev/null +++ b/test/jdk/java/awt/Frame/FrameMinimizeTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Frame; + +/* + * @test + * @bug 4172782 + * @summary Test if non-resizable frame is minimizable + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FrameMinimizeTest + */ + +public class FrameMinimizeTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + When the blank FrameMinimizeTest frame is shown, verify that + 1. It is not resizable; + 2. It is minimizable. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows(4) + .columns(35) + .testUI(FrameMinimizeTest::initialize) + .build() + .awaitAndCheck(); + } + + public static Frame initialize() { + Frame f = new Frame("FrameMinimizeTest"); + f.setSize(200, 200); + f.setResizable(false); + return f; + } +} diff --git a/test/jdk/java/awt/Frame/FrameResizableTest.java b/test/jdk/java/awt/Frame/FrameResizableTest.java new file mode 100644 index 0000000000000..4734ce71670aa --- /dev/null +++ b/test/jdk/java/awt/Frame/FrameResizableTest.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Panel; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 1231233 + * @summary Tests whether the resizable property of a Frame is + * respected after it is set. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FrameResizableTest + */ + +public class FrameResizableTest { + private static final String INSTRUCTIONS = """ + There is a frame with two buttons and a label. The label + reads 'true' or 'false' to indicate whether the frame can be + resized or not. + + When the first button, 'Set Resizable', is + clicked, you should be able to resize the frame. + When the second button, 'UnSet Resizable', is clicked, you should + not be able to resize the frame. + + A frame is resized in a way which depends upon the window manager (WM) running. + You may resize the frame by dragging the corner resize handles or the borders, + or you may use the title bar's resize menu items and buttons. + + Upon test completion, click Pass or Fail appropriately. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("FrameResizableTest Instructions") + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(FrameResizable::new) + .build() + .awaitAndCheck(); + } + + private static class FrameResizable extends Frame { + Label label; + Button buttonResizable; + Button buttonNotResizable; + + public FrameResizable() { + super("FrameResizable"); + setResizable(false); + Panel panel = new Panel(); + + add("North", panel); + ActionListener actionListener = (e) -> { + if (e.getSource() == buttonResizable) { + setResizable(true); + } else if (e.getSource() == buttonNotResizable) { + setResizable(false); + } + label.setText("Resizable: " + isResizable()); + }; + + panel.add(buttonResizable = new Button("Set Resizable")); + panel.add(buttonNotResizable = new Button("UnSet Resizable")); + panel.add(label = new Label("Resizable: " + isResizable())); + buttonResizable.addActionListener(actionListener); + buttonNotResizable.addActionListener(actionListener); + + setSize(400, 200); + } + } +} diff --git a/test/jdk/java/awt/Frame/FrameResizeTest/FrameResizeTest_3.java b/test/jdk/java/awt/Frame/FrameResizeTest/FrameResizeTest_3.java new file mode 100644 index 0000000000000..6fdef005775a5 --- /dev/null +++ b/test/jdk/java/awt/Frame/FrameResizeTest/FrameResizeTest_3.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Window; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 4097207 + * @summary setSize() on a Frame does not resize its content + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FrameResizeTest_3 +*/ + +public class FrameResizeTest_3 { + + private static final String INSTRUCTIONS = """ + 1. You would see a frame titled 'TestFrame' with 2 buttons + named 'setSize(500,500)' and 'setSize(400,400)' + 2. Click any button and you would see the frame resized + 3. If the buttons get resized along with the frame + (ie., to fit the frame), press Pass else press Fail. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("FrameResizeTest_3 Instructions") + .instructions(INSTRUCTIONS) + .columns(45) + .logArea(6) + .testUI(FrameResizeTest_3::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Window createTestUI() { + Frame frame = new Frame("TestFrame"); + frame.setLayout(new GridLayout(2, 1)); + + Button butSize500 = new Button("setSize(500,500)"); + Button butSize400 = new Button("setSize(400,400)"); + + ActionListener actionListener = e -> { + if (e.getSource() instanceof Button) { + if (e.getSource() == butSize500) { + frame.setSize(500, 500); + PassFailJFrame.log("New bounds: " + frame.getBounds()); + } else if (e.getSource() == butSize400) { + frame.setSize(400, 400); + PassFailJFrame.log("New bounds: " + frame.getBounds()); + } + } + }; + butSize500.addActionListener(actionListener); + butSize400.addActionListener(actionListener); + frame.add(butSize500); + frame.add(butSize400); + + frame.setSize(270, 200); + return frame; + } +} diff --git a/test/jdk/java/awt/Frame/FrameResizeTest/FrameResizeTest_4.java b/test/jdk/java/awt/Frame/FrameResizeTest/FrameResizeTest_4.java new file mode 100644 index 0000000000000..4428feee3355b --- /dev/null +++ b/test/jdk/java/awt/Frame/FrameResizeTest/FrameResizeTest_4.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* Note that although this test makes use of Swing classes, like JFrame and */ +/* JButton, it is really an AWT test, because it tests mechanism of sending */ +/* paint events. */ + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import java.awt.BorderLayout; + +/* + * @test + * @bug 4174831 + * @summary Tests that frame do not flicker on diagonal resize + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FrameResizeTest_4 + */ + +public class FrameResizeTest_4 { + private static final String INSTRUCTIONS = """ + Try enlarging the frame diagonally. + If buttons inside frame excessively repaint themselves and flicker + while you enlarge frame, the test fails. + Otherwise, it passes. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("FrameResizeTest_4 Instructions") + .instructions(INSTRUCTIONS) + .columns(45) + .testUI(FrameResizeTest_4::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + JFrame f = new JFrame("FrameResizeTest_4 Flickering Frame"); + + JPanel panel = new JPanel(new BorderLayout()); + panel.add(new JButton("West"), BorderLayout.WEST); + panel.add(new JButton("East"), BorderLayout.EAST); + panel.add(new JButton("North"), BorderLayout.NORTH); + panel.add(new JButton("South"), BorderLayout.SOUTH); + panel.add(new JButton("Center"), BorderLayout.CENTER); + f.setContentPane(panel); + + f.pack(); + f.setBounds(100, 50, 300, 200); + + return f; + } +} diff --git a/test/jdk/java/awt/Frame/FrameResizeTest/FrameResizeTest_5.java b/test/jdk/java/awt/Frame/FrameResizeTest/FrameResizeTest_5.java new file mode 100644 index 0000000000000..5a43b755a5212 --- /dev/null +++ b/test/jdk/java/awt/Frame/FrameResizeTest/FrameResizeTest_5.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Frame; +import java.awt.GridLayout; + +/* + * @test + * @summary Test to make sure non-resizable Frames can be resized with the + * setSize() method. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FrameResizeTest_5 +*/ + +public class FrameResizeTest_5 { + private static final String INSTRUCTIONS = """ + This tests the programmatic resizability of non-resizable Frames. + Even when a Frame is set to be non-resizable, it should still be + programmatically resizable using the setSize() method. + + Initially the Frame will be resizable. Try using the "Smaller" + and "Larger" buttons to verify that the Frame resizes correctly. + Then, click the "Toggle" button to make the Frame non-resizable. + Again, verify that clicking the "Larger" and "Smaller" buttons + causes the Frame to get larger and smaller. If the Frame does + not change size, or does not re-layout correctly, the test fails. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("FrameResizeTest_5 Instructions") + .instructions(INSTRUCTIONS) + .columns(45) + .logArea(6) + .testUI(TestFrame::new) + .build() + .awaitAndCheck(); + } + + private static class TestFrame extends Frame { + Button bLarger, bSmaller, bCheck, bToggle; + + public TestFrame() { + super("Frame Resize Test"); + setSize(200, 200); + bLarger = new Button("Larger"); + bLarger.addActionListener(e -> { + setSize(400, 400); + validate(); + }); + bSmaller = new Button("Smaller"); + bSmaller.addActionListener(e -> { + setSize(200, 100); + validate(); + }); + bCheck = new Button("Resizable?"); + bCheck.addActionListener(e -> { + if (isResizable()) { + PassFailJFrame.log("Frame is resizable"); + setResizable(true); + } else { + PassFailJFrame.log("Frame is not resizable"); + setResizable(false); + } + }); + bToggle = new Button("Toggle"); + bToggle.addActionListener(e -> { + if (isResizable()) { + PassFailJFrame.log("Frame is now not resizable"); + setResizable(false); + } else { + PassFailJFrame.log("Frame is now resizable"); + setResizable(true); + } + }); + setLayout(new GridLayout(4, 1)); + add(bSmaller); + add(bLarger); + add(bCheck); + add(bToggle); + } + } +} diff --git a/test/jdk/java/awt/Frame/FrameSetCursorTest.java b/test/jdk/java/awt/Frame/FrameSetCursorTest.java new file mode 100644 index 0000000000000..98968a8fbab82 --- /dev/null +++ b/test/jdk/java/awt/Frame/FrameSetCursorTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Cursor; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.event.ActionListener; +import java.lang.Exception; +import java.lang.InterruptedException; +import java.lang.Object; +import java.lang.String; +import java.lang.Thread; + +/* + * @test + * @bug 4097226 + * @summary Frame.setCursor() sometimes doesn't update the cursor until user moves the mouse + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FrameSetCursorTest + */ + +public class FrameSetCursorTest { + private static final String INSTRUCTIONS = """ + 1. Keep the instruction dialog and TestFrame side by side so that + you can read the instructions while doing the test + 2. Click on the 'Start Busy' button on the frame titled 'TestFrame' + and DO NOT MOVE THE MOUSE ANYWHERE till you complete the steps below + 3. The cursor on the TestFrame changes to busy cursor + 4. If you don't see the busy cursor press 'Fail' after + the `done sleeping` message + 5. If the busy cursor is seen, after 5 seconds the message + 'done sleeping' is displayed in the message window + 6. Check for the cursor type after the display of 'done sleeping' + 7. If the cursor on the TestFrame has changed back to default cursor + (without you touching or moving the mouse), then press 'Pass' + else if the frame still shows the busy cursor press 'Fail' + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("FrameSetCursorTest Instructions") + .instructions(INSTRUCTIONS) + .columns(45) + .testUI(FrameSetCursorTest::createAndShowUI) + .logArea(5) + .build() + .awaitAndCheck(); + + } + + static Frame createAndShowUI() { + Frame frame = new Frame("TestFrame"); + Panel panel = new Panel(); + Button busyButton = new Button("Start Busy"); + + ActionListener actionListener = event -> { + Object source = event.getSource(); + if (source == busyButton) { + frame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + try { + Thread.sleep(5000); + } catch (InterruptedException ignored) {} + PassFailJFrame.log("done sleeping"); + frame.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + }; + + busyButton.addActionListener(actionListener); + panel.setLayout(new BorderLayout()); + panel.add("North", busyButton); + + frame.add(panel); + frame.pack(); + frame.setSize(200, 200); + return frame; + } +} \ No newline at end of file diff --git a/test/jdk/java/awt/Frame/FrameSetMinimumSizeTest.java b/test/jdk/java/awt/Frame/FrameSetMinimumSizeTest.java new file mode 100644 index 0000000000000..929a36e773e40 --- /dev/null +++ b/test/jdk/java/awt/Frame/FrameSetMinimumSizeTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; + +/* + * @test + * @bug 4320050 + * @key headful + * @summary Minimum size for java.awt.Frame is not being enforced. + * @run main FrameSetMinimumSizeTest + */ + +public class FrameSetMinimumSizeTest { + private static Frame f; + private static volatile boolean passed; + + public static void main(String[] args) throws Exception { + EventQueue.invokeAndWait(() -> { + try { + createAndShowUI(); + + f.setSize(200, 200); + passed = verifyFrameSize(new Dimension(300, 300)); + isFrameSizeOk(passed); + + f.setSize(200, 400); + passed = verifyFrameSize(new Dimension(300, 400)); + isFrameSizeOk(passed); + + f.setSize(400, 200); + passed = verifyFrameSize(new Dimension(400, 300)); + isFrameSizeOk(passed); + + f.setMinimumSize(null); + + f.setSize(200, 200); + passed = verifyFrameSize(new Dimension(200, 200)); + isFrameSizeOk(passed); + } finally { + if (f != null) { + f.dispose(); + } + } + }); + } + + private static void createAndShowUI() { + f = new Frame("Minimum Size Test"); + f.setSize(300, 300); + f.setMinimumSize(new Dimension(300, 300)); + f.setVisible(true); + } + + private static boolean verifyFrameSize(Dimension expected) { + + if (f.getSize().width != expected.width || f.getSize().height != expected.height) { + return false; + } + return true; + } + + private static void isFrameSizeOk(boolean passed) { + if (!passed) { + throw new RuntimeException("Frame's setMinimumSize not honoured for the" + + " frame size: " + f.getSize()); + } + } +} diff --git a/test/jdk/java/awt/Frame/FrameVisualTest.java b/test/jdk/java/awt/Frame/FrameVisualTest.java new file mode 100644 index 0000000000000..767eb0a18965c --- /dev/null +++ b/test/jdk/java/awt/Frame/FrameVisualTest.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsEnvironment; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import javax.imageio.ImageIO; + +/* + * @test + * @bug 4328588 + * @key headful + * @summary Non-default visual on top-level Frame should work + * @run main FrameVisualTest + */ + +public class FrameVisualTest { + private static GraphicsConfiguration[] gcs; + private static volatile Frame[] frames; + private static volatile int index; + + private static Frame f; + private static Robot robot; + private static volatile Point p; + private static volatile Dimension d; + private static final int TOLERANCE = 5; + + public static void main(String[] args) throws Exception { + gcs = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getConfigurations(); + robot = new Robot(); + robot.setAutoDelay(100); + try { + EventQueue.invokeAndWait(() -> { + createAndShowUI(); + }); + robot.delay(1000); + System.out.println("frames.length: "+frames.length); + for (index = 0; index < frames.length; index++) { + EventQueue.invokeAndWait(() -> { + p = frames[index].getLocation(); + d = frames[index].getSize(); + }); + Rectangle rect = new Rectangle(p, d); + BufferedImage img = robot.createScreenCapture(rect); + if (chkImgBackgroundColor(img)) { + try { + ImageIO.write(img, "png", new File("Frame_" + index + ".png")); + } catch (IOException ignored) {} + throw new RuntimeException("Frame visual test failed with non-white background color"); + } + } + } finally { + for (index = 0; index < frames.length; index++) { + EventQueue.invokeAndWait(() -> { + if (frames[index] != null) { + frames[index].dispose(); + } + }); + } + } + } + + private static void createAndShowUI() { + frames = new Frame[gcs.length]; + for (int i = 0; i < frames.length; i++) { + frames[i] = new Frame("Frame w/ gc " + i, gcs[i]); + frames[i].setSize(100, 100); + frames[i].setUndecorated(true); + frames[i].setBackground(Color.WHITE); + frames[i].setVisible(true); + } + } + + private static boolean chkImgBackgroundColor(BufferedImage img) { + + // scan for mid-line and if it is non-white color then return true. + for (int x = 1; x < img.getWidth() - 1; ++x) { + Color c = new Color(img.getRGB(x, img.getHeight() / 2)); + if ((c.getRed() - Color.WHITE.getRed()) > TOLERANCE && + (c.getGreen() - Color.WHITE.getGreen()) > TOLERANCE && + (c.getBlue() - Color.WHITE.getBlue()) > TOLERANCE) { + return true; + } + } + return false; + } +} + diff --git a/test/jdk/java/awt/Frame/I18NTitle.java b/test/jdk/java/awt/Frame/I18NTitle.java new file mode 100644 index 0000000000000..7127fc3dfbf6b --- /dev/null +++ b/test/jdk/java/awt/Frame/I18NTitle.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Window; + +/* + * @test + * @bug 6269884 4929291 + * @summary Tests that title which contains mix of non-English characters is displayed correctly + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual I18NTitle + */ + +public class I18NTitle { + private static final String INSTRUCTIONS = """ + You will see a frame with some title (S. Chinese, Cyrillic and German). + Please check if non-English characters are visible and compare + the visible title with the same string shown in the label + (it should not look worse than the label). + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("I18NTitle Instructions") + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(I18NTitle::createAndShowGUI) + .build() + .awaitAndCheck(); + } + + private static Window createAndShowGUI() { + String s = "\u4e2d\u6587\u6d4b\u8bd5 \u0420\u0443\u0441\u0441\u043a\u0438\u0439 Zur\u00FCck"; + Frame frame = new Frame(s); + frame.setLayout(new BorderLayout()); + Label l = new Label(s); + frame.add(l); + frame.setSize(400, 100); + return frame; + } +} diff --git a/test/jdk/java/awt/Frame/IMStatusBar.java b/test/jdk/java/awt/Frame/IMStatusBar.java new file mode 100644 index 0000000000000..7e882d01c707c --- /dev/null +++ b/test/jdk/java/awt/Frame/IMStatusBar.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Panel; +import java.awt.TextField; + +/* + * @test + * @bug 4113040 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Checks that IMStatusBar does not affect Frame layout + * @run main/manual/othervm -Duser.language=ja -Duser.country=JP IMStatusBar + */ + +public class IMStatusBar { + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + If the window appears the right size, but then resizes so that the + status field overlaps the bottom label, press Fail; otherwise press Pass. + """; + + PassFailJFrame.builder() + .title("IMStatusBar Instruction") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testUI(IMStatusBar::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame f = new Frame(); + Panel centerPanel = new Panel(); + f.setSize(200, 200); + f.setLayout(new BorderLayout()); + f.add(new Label("Top"), BorderLayout.NORTH); + f.add(centerPanel, BorderLayout.CENTER); + f.add(new Label("Bottom"), BorderLayout.SOUTH); + centerPanel.setLayout(new BorderLayout()); + centerPanel.add(new TextField("Middle"), BorderLayout.CENTER); + centerPanel.validate(); + return f; + } +} diff --git a/test/jdk/java/awt/Frame/InitialIconifiedTest.java b/test/jdk/java/awt/Frame/InitialIconifiedTest.java new file mode 100644 index 0000000000000..4a8d959566575 --- /dev/null +++ b/test/jdk/java/awt/Frame/InitialIconifiedTest.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.imageio.ImageIO; +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +/* + * @test + * @key headful + * @bug 4851435 + * @summary Frame is not shown initially iconified after pack + */ + +public class InitialIconifiedTest { + + private static Frame backgroundFrame; + private static Frame testedFrame; + + private static final Rectangle backgroundFrameBounds = + new Rectangle(100, 100, 200, 200); + private static final Rectangle testedFrameBounds = + new Rectangle(150, 150, 100, 100); + + private static Robot robot; + + private static final StringBuilder FAILURES = new StringBuilder(); + + public static void main(String[] args) throws Exception { + robot = new Robot(); + try { + EventQueue.invokeAndWait(InitialIconifiedTest::initAndShowBackground); + robot.waitForIdle(); + robot.delay(500); + + test(false); + test(true); + } finally { + EventQueue.invokeAndWait(() -> { + backgroundFrame.dispose(); + testedFrame.dispose(); + }); + } + + if (!FAILURES.isEmpty()) { + throw new RuntimeException(FAILURES.toString()); + } + } + + private static void test(boolean isUndecorated) throws Exception { + String prefix = isUndecorated ? "undecorated" : "decorated"; + + EventQueue.invokeAndWait(() -> initAndShowTestedFrame(isUndecorated)); + // On macos, we can observe the animation of the window from the initial + // NORMAL state to the ICONIFIED state, + // even if the window was created in the ICONIFIED state. + // The following delay is commented out to capture this animation + // robot.waitForIdle(); + // robot.delay(500); + if (!testIfIconified(prefix + "_no_extra_delay")) { + FAILURES.append("Case %s frame with no extra delay failed\n" + .formatted(isUndecorated ? "undecorated" : "decorated")); + } + + EventQueue.invokeAndWait(() -> initAndShowTestedFrame(isUndecorated)); + robot.waitForIdle(); + robot.delay(500); + if (!testIfIconified(prefix + "_with_extra_delay")) { + FAILURES.append("Case %s frame with extra delay failed\n" + .formatted(isUndecorated ? "undecorated" : "decorated")); + } + } + + private static void initAndShowBackground() { + backgroundFrame = new Frame("DisposeTest background"); + backgroundFrame.setUndecorated(true); + backgroundFrame.setBackground(Color.RED); + backgroundFrame.setBounds(backgroundFrameBounds); + backgroundFrame.setVisible(true); + } + + private static void initAndShowTestedFrame(boolean isUndecorated) { + if (testedFrame != null) { + testedFrame.dispose(); + } + testedFrame = new Frame("Should have started ICONIC"); + if (isUndecorated) { + testedFrame.setUndecorated(true); + } + testedFrame.setExtendedState(Frame.ICONIFIED); + testedFrame.setBounds(testedFrameBounds); + testedFrame.setVisible(true); + } + + private static boolean testIfIconified(String prefix) { + BufferedImage bi = robot.createScreenCapture(backgroundFrameBounds); + int redPix = Color.RED.getRGB(); + + for (int x = 0; x < bi.getWidth(); x++) { + for (int y = 0; y < bi.getHeight(); y++) { + if (bi.getRGB(x, y) != redPix) { + try { + ImageIO.write(bi, "png", + new File(prefix + "_failure.png")); + } catch (IOException ignored) {} + return false; + } + } + } + return true; + } +} diff --git a/test/jdk/java/awt/Frame/InitialMaximizedTest/InitialMaximizedTest.html b/test/jdk/java/awt/Frame/InitialMaximizedTest/InitialMaximizedTest.html deleted file mode 100644 index 1966095a27c96..0000000000000 --- a/test/jdk/java/awt/Frame/InitialMaximizedTest/InitialMaximizedTest.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - InitialMaximizedTest - - - -

    InitialMaximizedTest
    Bug ID: 4464714

    - -

    See the dialog box (usually in upper left corner) for instructions

    - - - - diff --git a/test/jdk/java/awt/Frame/InitialMaximizedTest/InitialMaximizedTest.java b/test/jdk/java/awt/Frame/InitialMaximizedTest/InitialMaximizedTest.java index ccbdd16f7954f..bea52bf8f9247 100644 --- a/test/jdk/java/awt/Frame/InitialMaximizedTest/InitialMaximizedTest.java +++ b/test/jdk/java/awt/Frame/InitialMaximizedTest/InitialMaximizedTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,197 +21,82 @@ * questions. */ +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.GraphicsConfiguration; +import java.awt.Insets; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.Toolkit; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + /* - test - @bug 4464714 6365898 - @summary Frames cannot be shown initially maximized - @author Valeriy Ushakov: area=toplevel - @run applet/manual=yesno InitialMaximizedTest.html + * @test + * @bug 4464714 6365898 + * @key headful + * @summary Frames cannot be shown initially maximized */ - -/** - * InitialMaximizedTest.java - * - * summary: - */ - -import java.applet.Applet; -import java.awt.*; - - -public class InitialMaximizedTest extends Applet -{ - Frame f; - - public void init() - { - this.setLayout (new BorderLayout ()); - - String[] instructions = - { - "This test creates a frame that is initially maximized.", - "Press PASS if frame is shown initially maximized, else press FAIL" - }; - Sysout.createDialogWithInstructions( instructions ); - - }//End init() - - public void start () - { - //Get things going. Request focus, set size, et cetera - setSize (200,200); - setVisible(true); - validate(); - - f = new Frame("The frame SHOULD be shown MAXIMIZED"); - f.setSize(300, 300); - f.setLocation(50, 50); - f.setExtendedState(Frame.MAXIMIZED_BOTH); - f.setVisible(true); - }// start() - -}// class InitialMaximizedTest - -/* Place other classes related to the test after this line */ - - - - - -/**************************************************** - Standard Test Machinery - DO NOT modify anything below -- it's a standard - chunk of code whose purpose is to make user - interaction uniform, and thereby make it simpler - to read and understand someone else's test. - ****************************************************/ - -/** - This is part of the standard test machinery. - It creates a dialog (with the instructions), and is the interface - for sending text messages to the user. - To print the instructions, send an array of strings to Sysout.createDialog - WithInstructions method. Put one line of instructions per array entry. - To display a message for the tester to see, simply call Sysout.println - with the string to be displayed. - This mimics System.out.println but works within the test harness as well - as standalone. - */ - -class Sysout -{ - private static TestDialog dialog; - - public static void createDialogWithInstructions( String[] instructions ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - dialog.printInstructions( instructions ); - dialog.setVisible(true); - println( "Any messages for the tester will display here." ); +public class InitialMaximizedTest { + + static Frame frame; + + public static void main(String[] args) throws Exception { + if (!Toolkit.getDefaultToolkit() + .isFrameStateSupported(Frame.MAXIMIZED_BOTH)) { + return; + } + + Robot robot = new Robot(); + try { + EventQueue.invokeAndWait(InitialMaximizedTest::createAndShowFrame); + robot.waitForIdle(); + robot.delay(1000); + EventQueue.invokeAndWait(InitialMaximizedTest::checkMaximized); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } } - public static void createDialog( ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - String[] defInstr = { "Instructions will appear here. ", "" } ; - dialog.printInstructions( defInstr ); - dialog.setVisible(true); - println( "Any messages for the tester will display here." ); - } + private static void checkMaximized() { + Rectangle frameBounds = frame.getBounds(); + GraphicsConfiguration gc = frame.getGraphicsConfiguration(); + Rectangle workArea = gc.getBounds(); - public static void printInstructions( String[] instructions ) - { - dialog.printInstructions( instructions ); - } + Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets(gc); + workArea.x += screenInsets.left; + workArea.y += screenInsets.top; + workArea.width -= screenInsets.left + screenInsets.right; + workArea.height -= screenInsets.top + screenInsets.bottom; + System.out.println("Frame bounds " + frameBounds); + System.out.println("GraphicsConfiguration bounds " + gc.getBounds()); + System.out.println("Screen insets: " + screenInsets); + System.out.println("Work area: " + workArea); - public static void println( String messageIn ) - { - dialog.displayMessage( messageIn ); + //frame bounds can exceed screen size on Windows, see 8231043 + if (!frameBounds.contains(workArea)) { + throw new RuntimeException("Frame is not maximized"); + } } -}// Sysout class - -/** - This is part of the standard test machinery. It provides a place for the - test instructions to be displayed, and a place for interactive messages - to the user to be displayed. - To have the test instructions displayed, see Sysout. - To have a message to the user be displayed, see Sysout. - Do not call anything in this dialog directly. - */ -class TestDialog extends Dialog -{ - - TextArea instructionsText; - TextArea messageText; - int maxStringLength = 80; - - //DO NOT call this directly, go through Sysout - public TestDialog( Frame frame, String name ) - { - super( frame, name ); - int scrollBoth = TextArea.SCROLLBARS_BOTH; - instructionsText = new TextArea( "", 15, maxStringLength, scrollBoth ); - add( "North", instructionsText ); - - messageText = new TextArea( "", 5, maxStringLength, scrollBoth ); - add("Center", messageText); - - pack(); - - setVisible(true); - }// TestDialog() - - //DO NOT call this directly, go through Sysout - public void printInstructions( String[] instructions ) - { - //Clear out any current instructions - instructionsText.setText( "" ); - - //Go down array of instruction strings - - String printStr, remainingStr; - for( int i=0; i < instructions.length; i++ ) - { - //chop up each into pieces maxSringLength long - remainingStr = instructions[ i ]; - while( remainingStr.length() > 0 ) - { - //if longer than max then chop off first max chars to print - if( remainingStr.length() >= maxStringLength ) - { - //Try to chop on a word boundary - int posOfSpace = remainingStr. - lastIndexOf( ' ', maxStringLength - 1 ); - - if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1; - - printStr = remainingStr.substring( 0, posOfSpace + 1 ); - remainingStr = remainingStr.substring( posOfSpace + 1 ); - } - //else just print - else - { - printStr = remainingStr; - remainingStr = ""; - } - - instructionsText.append( printStr + "\n" ); - - }// while - - }// for - - }//printInstructions() - - //DO NOT call this directly, go through Sysout - public void displayMessage( String messageIn ) - { - messageText.append( messageIn + "\n" ); - System.out.println(messageIn); + private static void createAndShowFrame() { + frame = new Frame("The frame SHOULD be shown MAXIMIZED"); + frame.setSize(300, 300); + frame.setLocation(50, 50); + frame.setExtendedState(Frame.MAXIMIZED_BOTH); + frame.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + frame.dispose(); + } + }); + frame.setVisible(true); } - -}// TestDialog class +} diff --git a/test/jdk/java/awt/Frame/InsetCorrectionTest.java b/test/jdk/java/awt/Frame/InsetCorrectionTest.java new file mode 100644 index 0000000000000..1ea6f03b24b15 --- /dev/null +++ b/test/jdk/java/awt/Frame/InsetCorrectionTest.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 4091426 + * @key headful + * @summary Test inset correction when setVisible(true) BEFORE setSize(), setLocation() + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual InsetCorrectionTest + */ + +public class InsetCorrectionTest { + private static final String INSTRUCTIONS = """ + There is a frame of size 300x300 at location (100,100). + It has a menubar with one menu, 'File', but the frame + is otherwise empty. In particular, there should be no + part of the frame that is not shown in the background color. + Upon test completion, click Pass or Fail appropriately. + """; + + private static InsetCorrection testFrame; + + public static void main(String[] args) throws Exception { + EventQueue.invokeAndWait(() -> testFrame = new InsetCorrection()); + + try { + PassFailJFrame passFailJFrame = PassFailJFrame.builder() + .title("InsetCorrectionTest Instructions") + .instructions(INSTRUCTIONS) + .columns(45) + .logArea(3) + .build(); + EventQueue.invokeAndWait(() -> + PassFailJFrame.log("frame location: " + testFrame.getBounds())); + passFailJFrame.awaitAndCheck(); + } finally { + EventQueue.invokeAndWait(testFrame::dispose); + } + } + + static class InsetCorrection extends Frame + implements ActionListener { + MenuBar mb; + Menu file; + MenuItem cause_bug_b; + + public InsetCorrection() { + super("InsetCorrection"); + mb = new MenuBar(); + file = new Menu("File"); + mb.add(file); + cause_bug_b = new MenuItem("cause bug"); + file.add(cause_bug_b); + setMenuBar(mb); + cause_bug_b.addActionListener(this); + + // Making the frame visible before setSize and setLocation() + // are being called causes sometimes strange behaviour with + // JDK1.1.5G. The frame is then sometimes to large and the + // excess areas are drawn in black. This only happens + // sometimes. + setVisible(true); + setSize(300, 300); + setLocation(100, 100); + } + + public void actionPerformed(ActionEvent e) { + setVisible(false); + setVisible(true); + } + } +} diff --git a/test/jdk/java/awt/Frame/MegaIconTest/MegaIconTest.java b/test/jdk/java/awt/Frame/MegaIconTest/MegaIconTest.java new file mode 100644 index 0000000000000..3132d49df564a --- /dev/null +++ b/test/jdk/java/awt/Frame/MegaIconTest/MegaIconTest.java @@ -0,0 +1,273 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Canvas; +import java.awt.Color; +import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.Label; +import java.awt.MediaTracker; +import java.awt.Panel; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.image.ImageProducer; +import java.net.URL; + +/* + * @test + * @bug 4175560 + * @summary Test use of user-defined icons + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MegaIconTest + */ + +public class MegaIconTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Each of the buttons in the main window represents a test + of certain icon functionality - background transparency/opacity + of the icon, scaling etc. + Clicking on each button brings up a window displaying the graphic + that should appear in the corresponding icon. + Click on each button, minimize the resulting window, and check that + the icon is displayed as the test name indicates. + On Win32, icons should also be displayed correctly in the title bar. + If all the test pass, then this test passes, else fail. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows(10) + .columns(35) + .testUI(MegaIconTest::initialize) + .build() + .awaitAndCheck(); + } + + public static Frame initialize() { + //Create the iconTestFrames and add to IconTestButtons + IconTestButtons itb = new IconTestButtons(new IconTestFrame[]{ + new IconTestFrame("Opaque, Scaled Icon Test", + "duke_404.gif"), + + new IconTestFrame("Transparent Icon", + "dukeWave.gif"), + + new IconTestFrameBG("Transparent, Scaled Icon with bg", + "fight.gif", Color.red), + + new IconTestFrameDlg("Transparent icon w/ Dialog", + "dukeWave.gif") + }); + itb.pack(); + return itb; + } +} + +class IconTestButtons extends Frame { + public IconTestButtons(IconTestFrame[] iconTests) { + IconTestFrame tempTest; + Button newBtn; + Panel newPnl; + DoneLabel newLbl; + + setTitle("MegaIconTest"); + + setLayout(new GridLayout(iconTests.length, 1)); + + //For each icon test frame + //Get name, add button with name and action to + //display the window, and add label "done" after + + for (int i = 0; i < iconTests.length; i++) { + tempTest = iconTests[i]; + newBtn = new Button(tempTest.getTestName()); + newLbl = new DoneLabel(); + newBtn.addActionListener(new IconTestActionListener(tempTest, + newLbl)); + newPnl = new Panel(); + newPnl.add(newBtn); + newPnl.add(newLbl); + add(newPnl); + } + } + + protected class DoneLabel extends Label { + public DoneLabel() { + super("Done"); + setVisible(false); + } + } + + protected class IconTestActionListener implements ActionListener { + IconTestFrame f; + DoneLabel l; + + public IconTestActionListener(IconTestFrame frame, DoneLabel label) { + this.f = frame; + this.l = label; + } + + public void actionPerformed(ActionEvent e) { + f.pack(); + f.setVisible(true); + l.setVisible(true); + IconTestButtons.this.pack(); + } + } +} + +class IconTestFrame extends Frame { + private String testName; + int width, height; + Image iconImage; + MediaTracker tracker; + + public IconTestFrame(String testName, String iconFileName) { + super(testName); + this.testName = testName; + tracker = new MediaTracker(this); + + //Set icon image + URL url = MegaIconTest.class.getResource(iconFileName); + Toolkit tk = Toolkit.getDefaultToolkit(); + if (tk == null) { + System.out.println("Toolkit is null!"); + } + if (url == null) { + System.out.println("Can't load icon is null!"); + return; + } + try { + iconImage = tk.createImage((ImageProducer) url.getContent()); + } catch (java.io.IOException e) { + System.out.println("Unable to load icon image from url: " + url); + } + tracker.addImage(iconImage, 0); + try { + tracker.waitForAll(); + } catch (java.lang.InterruptedException e) { + System.err.println(e); + } + width = iconImage.getWidth(this); + height = iconImage.getHeight(this); + setIconImage(iconImage); + + addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + setVisible(false); + } + }); + + setLayout(new BorderLayout()); + setBackground(Color.YELLOW); + + //Add the icon graphic and instructions to the Frame + add(new IconCanvas(), "Center"); + pack(); + } + + class IconCanvas extends Canvas { + public void paint(Graphics g) { + if (IconTestFrame.this.iconImage == null) { + throw new NullPointerException(); + } + g.drawImage(IconTestFrame.this.iconImage, 0, 0, this); + } + + public Dimension getPreferredSize() { + return new Dimension(IconTestFrame.this.width, + IconTestFrame.this.height); + } + + public Dimension getMinimumSize() { + return getPreferredSize(); + } + + public Dimension getMaximumSize() { + return getPreferredSize(); + } + } + + public String getTestName() { + return testName; + } +} + +class IconTestFrameBG extends IconTestFrame { + public IconTestFrameBG(String testName, String iconFileName, Color bg) { + super(testName, iconFileName); + setBackground(bg); + Panel p = new Panel(); + p.setLayout(new GridLayout(3, 1)); + p.add(new Label("The background of this window has been set.")); + p.add(new Label("Unless the default icon background is the same color,")); + p.add(new Label("the icon background should NOT be this color.")); + add(p, "North"); + pack(); + } +} + +class IconTestFrameDlg extends IconTestFrame implements ActionListener { + Dialog dlg; + Button dlgBtn; + + public IconTestFrameDlg(String testName, String iconFilename) { + super(testName, iconFilename); + Panel p = new Panel(); + p.setLayout(new GridLayout(4, 1)); + p.add(new Label("Click on the button below to display a child dialog.")); + p.add(new Label("On Win32, the Dialog's titlebar icon should match")); + p.add(new Label("the titlebar icon of this window.")); + p.add(new Label("Minimizing this Frame should yield only one icon.")); + add(p, "North"); + + dlg = new Dialog(this); + dlg.setSize(200, 200); + dlg.add(new Label("Dialog stuff.")); + dlg.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + setVisible(false); + } + }); + + dlgBtn = new Button("Display Dialog"); + dlgBtn.addActionListener(this); + add(dlgBtn, "South"); + } + + public void actionPerformed(ActionEvent e) { + dlg.setVisible(true); + } +} diff --git a/test/jdk/java/awt/Frame/MegaIconTest/dukeWave.gif b/test/jdk/java/awt/Frame/MegaIconTest/dukeWave.gif new file mode 100644 index 0000000000000..52ada7979ef57 Binary files /dev/null and b/test/jdk/java/awt/Frame/MegaIconTest/dukeWave.gif differ diff --git a/test/jdk/java/awt/Frame/MegaIconTest/duke_404.gif b/test/jdk/java/awt/Frame/MegaIconTest/duke_404.gif new file mode 100644 index 0000000000000..4958e0d0dfa8c Binary files /dev/null and b/test/jdk/java/awt/Frame/MegaIconTest/duke_404.gif differ diff --git a/test/jdk/java/awt/Frame/MegaIconTest/fight.gif b/test/jdk/java/awt/Frame/MegaIconTest/fight.gif new file mode 100644 index 0000000000000..6be1b4972d0de Binary files /dev/null and b/test/jdk/java/awt/Frame/MegaIconTest/fight.gif differ diff --git a/test/jdk/java/awt/Frame/MenuBarOffsetTest.java b/test/jdk/java/awt/Frame/MenuBarOffsetTest.java new file mode 100644 index 0000000000000..65a4a8ef4bd59 --- /dev/null +++ b/test/jdk/java/awt/Frame/MenuBarOffsetTest.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Menu; +import java.awt.MenuBar; + +/* + * @test + * @bug 4180577 + * @summary offset problems with menus in frames: (2 * 1) should be (2 * menuBarBorderSize) + * @requires (os.family == "linux" | os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MenuBarOffsetTest +*/ + +public class MenuBarOffsetTest { + private static final String INSTRUCTIONS = """ + If a menubar containing a menubar item labeled Test appears. + in a frame, and fits within the frame, press Pass, else press Fail. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("MenuBarOffsetTest Instructions") + .instructions(INSTRUCTIONS) + .columns(45) + .testUI(FrameTest::new) + .build() + .awaitAndCheck(); + } + + private static class FrameTest extends Frame { + public FrameTest() { + super("MenuBarOffsetTest FrameTest"); + MenuBar m = new MenuBar(); + setMenuBar(m); + Menu test = m.add(new Menu("Test")); + test.add("1"); + test.add("2"); + setSize(100, 100); + } + + public void paint(Graphics g) { + setForeground(Color.red); + Insets i = getInsets(); + Dimension d = getSize(); + System.err.println(getBounds()); + System.err.println("" + i); + + g.drawRect(i.left, i.top, + d.width - i.left - i.right - 1, + d.height - i.top - i.bottom - 1); + } + } +} diff --git a/test/jdk/java/awt/Frame/MinimumSizeTest.java b/test/jdk/java/awt/Frame/MinimumSizeTest.java new file mode 100644 index 0000000000000..cfff77be5f93c --- /dev/null +++ b/test/jdk/java/awt/Frame/MinimumSizeTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.imageio.ImageIO; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.Toolkit; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +/* + * @test + * @key headful + * @bug 1256759 + * @summary Checks that Frames with a very small size don't cause Motif + * to generate VendorShells which consume the entire desktop. + */ + +public class MinimumSizeTest { + + private static final Color BG_COLOR = Color.RED; + private static Frame backgroundFrame; + private static Frame testedFrame; + + private static Robot robot; + private static final Point location = new Point(200, 200); + private static final Point[] testPointLocations = { + new Point(100, 200), + new Point(200, 100), + new Point(450, 210), + new Point(210, 350), + }; + + public static void main(String[] args) throws Exception { + robot = new Robot(); + + try { + EventQueue.invokeAndWait(MinimumSizeTest::initAndShowGui); + robot.waitForIdle(); + robot.delay(500); + test(); + System.out.println("Test passed."); + } finally { + EventQueue.invokeAndWait(() -> { + backgroundFrame.dispose(); + testedFrame.dispose(); + }); + } + } + + private static void test() { + for (Point testLocation : testPointLocations) { + Color pixelColor = robot.getPixelColor(testLocation.x, testLocation.y); + + if (!pixelColor.equals(BG_COLOR)) { + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + BufferedImage screenCapture = robot.createScreenCapture(new Rectangle(screenSize)); + try { + ImageIO.write(screenCapture, "png", new File("failure.png")); + } catch (IOException ignored) {} + throw new RuntimeException("Pixel color does not match expected color %s at %s" + .formatted(pixelColor, testLocation)); + } + } + } + + private static void initAndShowGui() { + backgroundFrame = new Frame("MinimumSizeTest background"); + backgroundFrame.setUndecorated(true); + backgroundFrame.setBackground(BG_COLOR); + backgroundFrame.setBounds(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize())); + backgroundFrame.setVisible(true); + + testedFrame = new MinimumSizeTestFrame(); + testedFrame.setVisible(true); + } + + private static class MinimumSizeTestFrame extends Frame { + public MinimumSizeTestFrame() { + super("MinimumSizeTest"); + setVisible(true); + setBackground(Color.BLUE); + setSize(0, 0); + setLocation(location); + } + } +} + diff --git a/test/jdk/java/awt/Frame/MiscUndecorated/ActiveAWTWindowTest.java b/test/jdk/java/awt/Frame/MiscUndecorated/ActiveAWTWindowTest.java index 7aa5b8c5132eb..c7ec6c6d969f3 100644 --- a/test/jdk/java/awt/Frame/MiscUndecorated/ActiveAWTWindowTest.java +++ b/test/jdk/java/awt/Frame/MiscUndecorated/ActiveAWTWindowTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,15 +25,24 @@ * @test * @key headful * @summary To check proper WINDOW_EVENTS are triggered when Frame gains or losses the focus - * @author Jitender(jitender.singh@eng.sun.com) area=AWT - * @author yan * @library /lib/client * @build ExtendedRobot * @run main ActiveAWTWindowTest */ -import java.awt.*; -import java.awt.event.*; +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.TextField; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.event.WindowFocusListener; public class ActiveAWTWindowTest { @@ -47,12 +56,12 @@ public class ActiveAWTWindowTest { private boolean passed = true; private final int delay = 150; - public static void main(String[] args) { + public static void main(String[] args) throws Exception { ActiveAWTWindowTest test = new ActiveAWTWindowTest(); try { test.doTest(); } finally { - EventQueue.invokeLater(() -> { + EventQueue.invokeAndWait(() -> { if (test.frame != null) { test.frame.dispose(); } diff --git a/test/jdk/java/awt/Frame/MultiScreenTest.java b/test/jdk/java/awt/Frame/MultiScreenTest.java new file mode 100644 index 0000000000000..845f601138b74 --- /dev/null +++ b/test/jdk/java/awt/Frame/MultiScreenTest.java @@ -0,0 +1,485 @@ +/* + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Canvas; +import java.awt.Choice; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.FontMetrics; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Image; +import java.awt.Label; +import java.awt.LayoutManager; +import java.awt.Panel; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.TextField; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import java.awt.image.ColorModel; +import java.awt.image.MemoryImageSource; + +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JFrame; + +import jtreg.SkippedException; + +/* + * @test + * @bug 4312921 + * @key multimon + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame + * @summary Tests that no garbage is painted on primary screen with DGA + * @run main/manual MultiScreenTest + */ + +public class MultiScreenTest { + static GraphicsEnvironment ge; + static GraphicsDevice[] gs; + + public static void main(String[] args) throws Exception { + ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + gs = ge.getScreenDevices(); + if (gs.length < 2) { + throw new SkippedException("You have only one monitor in your system - test passed"); + } + MultiScreenTest obj = new MultiScreenTest(); + String INSTRUCTIONS = + "This test is to be run only on multiscreen machine. " + + "You have " + gs.length + " monitors in your system.\n" + + "Actively drag the DitherTest frames on the secondary screen and " + + "if you see garbage appearing on your primary screen " + + "test failed otherwise it passed.";; + + PassFailJFrame.builder() + .title("MultiScreenTest Instruction") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testUI(obj::init) + .build() + .awaitAndCheck(); + } + + public List init() { + List list = new ArrayList<>(); + for (int j = 0; j < gs.length; j++) { + GraphicsConfiguration[] gc = gs[j].getConfigurations(); + if (gc.length > 0) { + for (int i = 0; i < gc.length / 2; i++) { + JFrame f = new JFrame(gc[i]); //test JFrame( gc ) + GCCanvas c = new GCCanvas(gc[i]);//test canvas( gc ) + Rectangle gcBounds = gc[i].getBounds(); //test getBounds() + int xoffs = gcBounds.x; + int yoffs = gcBounds.y; + + f.getContentPane().add(c); + f.setTitle("Screen# " + Integer.toString(j) + ", GC#" + Integer.toString(i)); + f.setSize(300, 200); + f.setLocation(400 + xoffs, (i * 150) + yoffs);//test displaying in right location + list.add(f); + + Frame ditherfs = new Frame("DitherTest GC#" + Integer.toString(i), gc[i]); + ditherfs.setLayout(new BorderLayout()); //showDitherTest + DitherTest ditherTest = new DitherTest(gc[i]); + ditherfs.add("Center", ditherTest); + ditherfs.setBounds(300, 200, 300, 200); + ditherfs.setLocation(750 + xoffs, (i * 50) + yoffs); + ditherfs.pack(); + ditherfs.show(); + ditherTest.start(); + } + } + } + return list; + } +} + +class GCCanvas extends Canvas { + + GraphicsConfiguration gc; + Rectangle bounds; + Graphics g = this.getGraphics(); + Dimension size = getSize(); + + public GCCanvas(GraphicsConfiguration gc) { + super(gc); + this.gc = gc; + bounds = gc.getBounds(); + } + + public void paint( Graphics _g ) { + + Graphics2D g = (Graphics2D) _g; + + g.drawRect(0, 0, size.width-1, size.height-1); + g.setColor(Color.lightGray); + g.draw3DRect(1, 1, size.width-3, size.height-3, true); + + g.setColor(Color.red); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + g.drawString("HELLO!", 110, 10); + + g.setColor(Color.blue); + g.drawString("ScreenSize="+Integer.toString(bounds.width)+"X"+ + Integer.toString(bounds.height), 10, 20); + g.setColor(Color.green); + g.drawString(gc.toString(), 10, 30); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); + + g.setColor(Color.orange); + g.fillRect(40, 20, 50, 50); + + g.setColor(Color.red); + g.drawRect(100, 20, 30, 30); + + g.setColor(Color.gray); + g.drawLine(220, 20, 280, 40); + + g.setColor(Color.cyan); + g.fillArc(150, 30, 30, 30, 0, 200); + } + + public Dimension getPreferredSize(){ + return new Dimension(300, 200); + } +} + +class DitherCanvas extends Canvas { + Image img; + static String calcString = "Calculating..."; + + GraphicsConfiguration mGC; + + public DitherCanvas(GraphicsConfiguration gc) { + super(gc); + mGC = gc; + } + + public GraphicsConfiguration getGraphicsConfig() { + return mGC; + } + + public void paint(Graphics g) { + int w = getSize().width; + int h = getSize().height; + if (img == null) { + super.paint(g); + g.setColor(Color.black); + FontMetrics fm = g.getFontMetrics(); + int x = (w - fm.stringWidth(calcString)) / 2; + int y = h / 2; + g.drawString(calcString, x, y); + } else { + g.drawImage(img, 0, 0, w, h, this); + } + } + + public void update(Graphics g) { + paint(g); + } + + public Dimension getMinimumSize() { + return new Dimension(20, 20); + } + + public Dimension getPreferredSize() { + return new Dimension(200, 200); + } + + public Image getImage() { + return img; + } + + public void setImage(Image img) { + this.img = img; + paint(getGraphics()); + } +} + +class DitherTest extends Panel implements Runnable { + final static int NOOP = 0; + final static int RED = 1; + final static int GREEN = 2; + final static int BLUE = 3; + final static int ALPHA = 4; + final static int SATURATION = 5; + + Thread runner; + + DitherControls XControls; + DitherControls YControls; + DitherCanvas canvas; + + public DitherTest(GraphicsConfiguration gc) { + String xspec, yspec; + int xvals[] = new int[2]; + int yvals[] = new int[2]; + + xspec = "red"; + yspec = "blue"; + int xmethod = colormethod(xspec, xvals); + int ymethod = colormethod(yspec, yvals); + + setLayout(new BorderLayout()); + XControls = new DitherControls(this, xvals[0], xvals[1], + xmethod, false); + YControls = new DitherControls(this, yvals[0], yvals[1], + ymethod, true); + YControls.addRenderButton(); + add("North", XControls); + add("South", YControls); + add("Center", canvas = new DitherCanvas(gc)); + } + + public void start() { + runner = new Thread(this); + runner.start(); + } + + int colormethod(String s, int vals[]) { + int method = NOOP; + + if (s == null) { + s = ""; + } + + String lower = s.toLowerCase(); + int len = 0; + if (lower.startsWith("red")) { + method = RED; + lower = lower.substring(3); + } else if (lower.startsWith("green")) { + method = GREEN; + lower = lower.substring(5); + } else if (lower.startsWith("blue")) { + method = BLUE; + lower = lower.substring(4); + } else if (lower.startsWith("alpha")) { + method = ALPHA; + lower = lower.substring(4); + } else if (lower.startsWith("saturation")) { + method = SATURATION; + lower = lower.substring(10); + } + + if (method == NOOP) { + vals[0] = 0; + vals[1] = 0; + return method; + } + + int begval = 0; + int endval = 255; + + try { + int dash = lower.indexOf('-'); + if (dash < 0) { + begval = endval = Integer.parseInt(lower); + } else { + begval = Integer.parseInt(lower.substring(0, dash)); + endval = Integer.parseInt(lower.substring(dash + 1)); + } + } catch (Exception e) { + } + + if (begval < 0) { + begval = 0; + } + if (endval < 0) { + endval = 0; + } + if (begval > 255) { + begval = 255; + } + if (endval > 255) { + endval = 255; + } + + vals[0] = begval; + vals[1] = endval; + + return method; + } + + void applymethod(int c[], int method, int step, int total, int vals[]) { + if (method == NOOP) + return; + int val = ((total < 2) + ? vals[0] + : vals[0] + ((vals[1] - vals[0]) * step / (total - 1))); + switch (method) { + case RED: + c[0] = val; + break; + case GREEN: + c[1] = val; + break; + case BLUE: + c[2] = val; + break; + case ALPHA: + c[3] = val; + break; + case SATURATION: + int max = Math.max(Math.max(c[0], c[1]), c[2]); + int min = max * (255 - val) / 255; + if (c[0] == 0) { + c[0] = min; + } + if (c[1] == 0) { + c[1] = min; + } + if (c[2] == 0) { + c[2] = min; + } + break; + } + } + + public void run() { + canvas.setImage(null); // Wipe previous image + Image img = calculateImage(); + synchronized (this) { + if (img != null && runner == Thread.currentThread()) { + canvas.setImage(img); + } + } + } + + /** + * Calculates and returns the image. Halts the calculation and returns + * null if stopped during the calculation. + */ + Image calculateImage() { + Thread me = Thread.currentThread(); + + int width = canvas.getSize().width; + int height = canvas.getSize().height; + int xvals[] = new int[2]; + int yvals[] = new int[2]; + int xmethod = XControls.getParams(xvals); + int ymethod = YControls.getParams(yvals); + int pixels[] = new int[width * height]; + int c[] = new int[4]; + int index = 0; + + for (int j = 0; j < height; j++) { + for (int i = 0; i < width; i++) { + c[0] = c[1] = c[2] = 0; + c[3] = 255; + if (xmethod < ymethod) { + applymethod(c, xmethod, i, width, xvals); + applymethod(c, ymethod, j, height, yvals); + } else { + applymethod(c, ymethod, j, height, yvals); + applymethod(c, xmethod, i, width, xvals); + } + pixels[index++] = ((c[3] << 24) | + (c[0] << 16) | + (c[1] << 8) | + (c[2] << 0)); + + } + // Poll once per row to see if we've been told to stop. + if (runner != me) { + return null; + } + } + + return createImage(new MemoryImageSource(width, height, + ColorModel.getRGBdefault(), pixels, 0, width)); + } + + public String getInfo() { + return "An interactive demonstration of dithering."; + } + + public String[][] getParameterInfo() { + String[][] info = { + {"xaxis", "{RED, GREEN, BLUE, PINK, ORANGE, MAGENTA, CYAN, WHITE, YELLOW, GRAY, DARKGRAY}", + "The color of the Y axis. Default is RED."}, + {"yaxis", "{RED, GREEN, BLUE, PINK, ORANGE, MAGENTA, CYAN, WHITE, YELLOW, GRAY, DARKGRAY}", + "The color of the X axis. Default is BLUE."} + }; + return info; + } +} + +class DitherControls extends Panel implements ActionListener { + TextField start; + TextField end; + Button button; + Choice choice; + DitherTest dt; + + static LayoutManager dcLayout = new FlowLayout(FlowLayout.CENTER, 10, 5); + + public DitherControls(DitherTest app, int s, int e, int type, + boolean vertical) { + dt = app; + setLayout(dcLayout); + add(new Label(vertical ? "Vertical" : "Horizontal")); + add(choice = new Choice()); + choice.addItem("Noop"); + choice.addItem("Red"); + choice.addItem("Green"); + choice.addItem("Blue"); + choice.addItem("Alpha"); + choice.addItem("Saturation"); + choice.select(type); + add(start = new TextField(Integer.toString(s), 4)); + add(end = new TextField(Integer.toString(e), 4)); + } + + public void addRenderButton() { + add(button = new Button("New Image")); + button.addActionListener(this); + } + + public int getParams(int vals[]) { + vals[0] = Integer.parseInt(start.getText()); + vals[1] = Integer.parseInt(end.getText()); + return choice.getSelectedIndex(); + } + + public void actionPerformed(ActionEvent e) { + if (e.getSource() == button) { + dt.start(); + } + } +} diff --git a/test/jdk/java/awt/Frame/PackTwiceTest.java b/test/jdk/java/awt/Frame/PackTwiceTest.java new file mode 100644 index 0000000000000..63cd20612f0d3 --- /dev/null +++ b/test/jdk/java/awt/Frame/PackTwiceTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Frame; +import java.awt.TextField; + +/* + * @test + * @bug 4097744 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary packing a frame twice stops it resizing + * @run main/manual PackTwiceTest + */ + +public class PackTwiceTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. You would see a Frame titled 'TestFrame' + 2. The Frame displays a text as below: + 'I am a lengthy sentence...can you see me?' + 3. If you can see the full text without resizing the frame + using mouse, press 'Pass' else press 'Fail'."""; + + PassFailJFrame.builder() + .title("PackTwiceTest Instruction") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testUI(PackTwiceTest::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame f = new Frame("PackTwiceTest TestFrame"); + TextField tf = new TextField(); + f.add(tf, "Center"); + tf.setText("I am a short sentence"); + f.pack(); + f.pack(); + tf.setText("I am a lengthy sentence...can you see me?"); + f.pack(); + f.requestFocus(); + return f; + } +} diff --git a/test/jdk/java/awt/Frame/ShapeNotSetSometimes/ShapeNotSetSometimes.java b/test/jdk/java/awt/Frame/ShapeNotSetSometimes/ShapeNotSetSometimes.java index 52a5754a9c3f4..fe3bf95f25ac1 100644 --- a/test/jdk/java/awt/Frame/ShapeNotSetSometimes/ShapeNotSetSometimes.java +++ b/test/jdk/java/awt/Frame/ShapeNotSetSometimes/ShapeNotSetSometimes.java @@ -33,6 +33,7 @@ import java.awt.geom.Area; import java.awt.geom.Ellipse2D; import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; @@ -42,7 +43,7 @@ * @key headful * @bug 6988428 * @summary Tests whether shape is always set - * @run main/othervm -Dsun.java2d.uiScale=1 ShapeNotSetSometimes + * @run main/othervm/timeout=300 -Dsun.java2d.uiScale=1 ShapeNotSetSometimes */ public class ShapeNotSetSometimes { @@ -147,15 +148,20 @@ private void doTest() throws Exception { robot.waitForIdle(); robot.delay(500); + Rectangle screenBounds = window.getGraphicsConfiguration().getBounds(); + BufferedImage screenCapture = robot.createScreenCapture(screenBounds); try { - colorCheck(innerPoint.x, innerPoint.y, SHAPE_COLOR, true); + colorCheck(innerPoint.x, innerPoint.y, SHAPE_COLOR, + true, screenCapture); for (Point point : pointsOutsideToCheck) { - colorCheck(point.x, point.y, BACKGROUND_COLOR, true); + colorCheck(point.x, point.y, BACKGROUND_COLOR, + true, screenCapture); } for (Point point : shadedPointsToCheck) { - colorCheck(point.x, point.y, SHAPE_COLOR, false); + colorCheck(point.x, point.y, SHAPE_COLOR, + false, screenCapture); } } finally { EventQueue.invokeAndWait(() -> { @@ -169,15 +175,12 @@ private void doTest() throws Exception { } } - private void colorCheck(int x, int y, Color expectedColor, boolean mustBeExpectedColor) { + private void colorCheck(int x, int y, Color expectedColor, + boolean mustBeExpectedColor, BufferedImage screenCapture) { int screenX = window.getX() + x; int screenY = window.getY() + y; - robot.mouseMove(screenX, screenY); - robot.waitForIdle(); - robot.delay(50); - - Color actualColor = robot.getPixelColor(screenX, screenY); + Color actualColor = new Color(screenCapture.getRGB(screenX, screenY)); System.out.printf( "Checking %3d, %3d, %35s should %sbe %35s\n", diff --git a/test/jdk/java/awt/Frame/ShownOnPack/ShownOnPack.html b/test/jdk/java/awt/Frame/ShownOnPack/ShownOnPack.html deleted file mode 100644 index 6dd732fcf6cdc..0000000000000 --- a/test/jdk/java/awt/Frame/ShownOnPack/ShownOnPack.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - ShownOnPack - - - -

    ShownOnPack
    Bug ID: 6525850

    - -

    See the dialog box (usually in upper left corner) for instructions

    - - - - diff --git a/test/jdk/java/awt/Frame/ShownOnPack/ShownOnPack.java b/test/jdk/java/awt/Frame/ShownOnPack/ShownOnPack.java index a5763d0bed260..5e591bfe0a6b5 100644 --- a/test/jdk/java/awt/Frame/ShownOnPack/ShownOnPack.java +++ b/test/jdk/java/awt/Frame/ShownOnPack/ShownOnPack.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,208 +21,48 @@ * questions. */ +import java.awt.EventQueue; +import java.awt.Frame; + /* - test - @bug 6525850 - @summary Iconified frame gets shown after pack() - @author anthony.petrov@...: area=awt.toplevel - @run applet/manual=yesno ShownOnPack.html + * @test + * @bug 6525850 + * @summary Iconified frame gets shown after pack() + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ShownOnPack */ - -/** - * ShownOnPack.java - * - * summary: - */ - -import java.applet.Applet; -import java.awt.*; - -public class ShownOnPack extends Applet -{ - //Declare things used in the test, like buttons and labels here - Frame f; - - public void init() - { - //Create instructions for the user here, as well as set up - // the environment -- set the layout manager, add buttons, - // etc. - this.setLayout (new BorderLayout ()); - - String[] instructions = - { - "This test creates an invisible and iconified frame that should not become visible.", - "If you observe the window titled 'Should NOT BE SHOWN' in the taskbar, press FAIL,", - "else press PASS" - }; - Sysout.createDialogWithInstructions( instructions ); - - }//End init() - - public void start () - { - //Get things going. Request focus, set size, et cetera - setSize (200,200); - setVisible(true); - validate(); - - //What would normally go into main() will probably go here. - //Use System.out.println for diagnostic messages that you want - // to read after the test is done. - //Use Sysout.println for messages you want the tester to read. - f = new Frame("Should NOT BE SHOWN"); - f.setExtendedState(Frame.ICONIFIED); - f.pack(); - }// start() - - //The rest of this class is the actions which perform the test... - - //Use Sysout.println to communicate with the user NOT System.out!! - //Sysout.println ("Something Happened!"); - -}// class ShownOnPack - -/* Place other classes related to the test after this line */ - - - - - -/**************************************************** - Standard Test Machinery - DO NOT modify anything below -- it's a standard - chunk of code whose purpose is to make user - interaction uniform, and thereby make it simpler - to read and understand someone else's test. - ****************************************************/ - -/** - This is part of the standard test machinery. - It creates a dialog (with the instructions), and is the interface - for sending text messages to the user. - To print the instructions, send an array of strings to Sysout.createDialog - WithInstructions method. Put one line of instructions per array entry. - To display a message for the tester to see, simply call Sysout.println - with the string to be displayed. - This mimics System.out.println but works within the test harness as well - as standalone. - */ - -class Sysout -{ - private static TestDialog dialog; - - public static void createDialogWithInstructions( String[] instructions ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - dialog.printInstructions( instructions ); - dialog.setVisible(true); - println( "Any messages for the tester will display here." ); - } - - public static void createDialog( ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - String[] defInstr = { "Instructions will appear here. ", "" } ; - dialog.printInstructions( defInstr ); - dialog.setVisible(true); - println( "Any messages for the tester will display here." ); +public class ShownOnPack { + + private static final String INSTRUCTIONS = """ + This test creates an invisible and iconified frame that should not become visible. + + If you observe the window titled 'Should NOT BE SHOWN' in the taskbar, + press FAIL, otherwise press PASS + """; + + static Frame frame; + + public static void main(String[] args) throws Exception { + PassFailJFrame shownOnPackInstructions = PassFailJFrame + .builder() + .title("ShownOnPack Instructions") + .instructions(INSTRUCTIONS) + .rows(5) + .columns(50) + .build(); + + EventQueue.invokeAndWait(() -> { + frame = new Frame("Should NOT BE SHOWN"); + frame.setExtendedState(Frame.ICONIFIED); + frame.pack(); + }); + + try { + shownOnPackInstructions.awaitAndCheck(); + } finally { + EventQueue.invokeAndWait(() -> frame.dispose()); + } } - - - public static void printInstructions( String[] instructions ) - { - dialog.printInstructions( instructions ); - } - - - public static void println( String messageIn ) - { - dialog.displayMessage( messageIn ); - } - -}// Sysout class - -/** - This is part of the standard test machinery. It provides a place for the - test instructions to be displayed, and a place for interactive messages - to the user to be displayed. - To have the test instructions displayed, see Sysout. - To have a message to the user be displayed, see Sysout. - Do not call anything in this dialog directly. - */ -class TestDialog extends Dialog -{ - - TextArea instructionsText; - TextArea messageText; - int maxStringLength = 80; - - //DO NOT call this directly, go through Sysout - public TestDialog( Frame frame, String name ) - { - super( frame, name ); - int scrollBoth = TextArea.SCROLLBARS_BOTH; - instructionsText = new TextArea( "", 15, maxStringLength, scrollBoth ); - add( "North", instructionsText ); - - messageText = new TextArea( "", 5, maxStringLength, scrollBoth ); - add("Center", messageText); - - pack(); - - setVisible(true); - }// TestDialog() - - //DO NOT call this directly, go through Sysout - public void printInstructions( String[] instructions ) - { - //Clear out any current instructions - instructionsText.setText( "" ); - - //Go down array of instruction strings - - String printStr, remainingStr; - for( int i=0; i < instructions.length; i++ ) - { - //chop up each into pieces maxSringLength long - remainingStr = instructions[ i ]; - while( remainingStr.length() > 0 ) - { - //if longer than max then chop off first max chars to print - if( remainingStr.length() >= maxStringLength ) - { - //Try to chop on a word boundary - int posOfSpace = remainingStr. - lastIndexOf( ' ', maxStringLength - 1 ); - - if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1; - - printStr = remainingStr.substring( 0, posOfSpace + 1 ); - remainingStr = remainingStr.substring( posOfSpace + 1 ); - } - //else just print - else - { - printStr = remainingStr; - remainingStr = ""; - } - - instructionsText.append( printStr + "\n" ); - - }// while - - }// for - - }//printInstructions() - - //DO NOT call this directly, go through Sysout - public void displayMessage( String messageIn ) - { - messageText.append( messageIn + "\n" ); - System.out.println(messageIn); - } - -}// TestDialog class +} diff --git a/test/jdk/java/awt/Frame/SizeMinimizedTest.java b/test/jdk/java/awt/Frame/SizeMinimizedTest.java new file mode 100644 index 0000000000000..2520cf42892f7 --- /dev/null +++ b/test/jdk/java/awt/Frame/SizeMinimizedTest.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +/* + * @test + * @key headful + * @bug 4065534 + * @summary Frame.setSize() doesn't change size if window is in an iconified state + * @run main SizeMinimizedTest + */ + +public class SizeMinimizedTest { + private static Frame frame; + private static final int INITIAL_SIZE = 100; + private static final int INITIAL_X = 150; + private static final int INITIAL_Y = 50; + private static final int RESET_SIZE = 200; + private static final int OFFSET = 10; + private static int iterationCnt = 0; + private static Dimension expectedSize; + private static Dimension frameSize; + private static Point expectedLoc; + private static Point frameLoc; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + try { + EventQueue.invokeAndWait(() -> { + createUI(); + }); + robot.waitForIdle(); + robot.delay(1000); + + EventQueue.invokeAndWait(() -> { + frame.setState(Frame.ICONIFIED); + }); + robot.waitForIdle(); + robot.delay(100); + + EventQueue.invokeAndWait(() -> { + frame.setSize(RESET_SIZE, RESET_SIZE); + }); + robot.waitForIdle(); + robot.delay(100); + + for (int i = 0; i < 5; i++) { + EventQueue.invokeAndWait(() -> { + Point pt = frame.getLocation(); + frame.setLocation(pt.x + OFFSET, pt.y); + }); + iterationCnt++; + robot.waitForIdle(); + robot.delay(100); + } + EventQueue.invokeAndWait(() -> { + frame.setState(Frame.NORMAL); + }); + robot.waitForIdle(); + robot.delay(100); + + System.out.println("Test Passed!"); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + public static void createUI() { + frame = new Frame("Frame size test"); + frame.setSize(INITIAL_SIZE, INITIAL_SIZE); + frame.setLocation(INITIAL_X, INITIAL_Y); + + frame.addWindowListener(new WindowAdapter() { + @Override + public void windowOpened(WindowEvent e) { + System.out.println("Initial Frame Size: " + frame.getSize()); + System.out.println("Initial Frame Location: " + + frame.getLocationOnScreen()); + } + }); + + frame.addWindowStateListener(new WindowAdapter() { + @Override + public void windowStateChanged(WindowEvent e) { + if (e.getNewState() == Frame.NORMAL) { + System.out.println("Frame Size: " + frame.getSize()); + System.out.println("Frame Location: " + + frame.getLocationOnScreen()); + expectedSize = new Dimension(RESET_SIZE, RESET_SIZE); + frameSize = frame.getSize(); + + if (!expectedSize.equals(frameSize)) { + throw new RuntimeException("Test Failed due to size mismatch."); + } + + expectedLoc = new Point(INITIAL_X + OFFSET * iterationCnt, + INITIAL_Y); + frameLoc = frame.getLocationOnScreen(); + + if (!expectedLoc.equals(frameLoc)) { + throw new RuntimeException("Test Failed due to " + + "location mismatch."); + } + } + } + }); + frame.setVisible(true); + } +} diff --git a/test/jdk/java/awt/FullScreen/FullscreenWindowProps/FullscreenWindowProps.java b/test/jdk/java/awt/FullScreen/FullscreenWindowProps/FullscreenWindowProps.java index 1160c54bf329c..66f358d64ce41 100644 --- a/test/jdk/java/awt/FullScreen/FullscreenWindowProps/FullscreenWindowProps.java +++ b/test/jdk/java/awt/FullScreen/FullscreenWindowProps/FullscreenWindowProps.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,7 +32,7 @@ /** * @test - * @bug 8211999 + * @bug 8211999 8282863 * @key headful * @summary verifies the full-screen window bounds and graphics configuration */ @@ -52,6 +52,10 @@ public void paint(Graphics g) { super.paint(g); g.setColor(Color.GREEN); g.fillRect(0, 0, getWidth(), getHeight()); + g.setColor(Color.RED); + DisplayMode displayMode = + getGraphicsConfiguration().getDevice().getDisplayMode(); + g.drawString(displayMode.toString(), 100, 100); } }; try { diff --git a/test/jdk/java/awt/FullScreen/NoResizeEventOnDMChangeTest/NoResizeEventOnDMChangeTest.java b/test/jdk/java/awt/FullScreen/NoResizeEventOnDMChangeTest/NoResizeEventOnDMChangeTest.java index 455ad2f2b0a3e..c7277e8b96caa 100644 --- a/test/jdk/java/awt/FullScreen/NoResizeEventOnDMChangeTest/NoResizeEventOnDMChangeTest.java +++ b/test/jdk/java/awt/FullScreen/NoResizeEventOnDMChangeTest/NoResizeEventOnDMChangeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,8 @@ * @bug 6646411 * @summary Tests that full screen window and its children receive resize event when display mode changes + * @library /test/lib + * @build jdk.test.lib.Platform jtreg.SkippedException * @run main/othervm NoResizeEventOnDMChangeTest * @run main/othervm -Dsun.java2d.d3d=false NoResizeEventOnDMChangeTest */ @@ -44,9 +46,21 @@ import java.awt.event.ComponentEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; +import java.io.BufferedReader; +import java.io.IOException; + +import static java.util.concurrent.TimeUnit.SECONDS; +import jdk.test.lib.Platform; +import jtreg.SkippedException; public class NoResizeEventOnDMChangeTest { + public static void main(String[] args) { + if (Platform.isOnWayland() && !isFixDelivered()) { + throw new SkippedException("Test skipped because fix was not" + + "delivered in current GnomeShell version"); + } + final GraphicsDevice gd = GraphicsEnvironment. getLocalGraphicsEnvironment().getDefaultScreenDevice(); @@ -231,4 +245,34 @@ public synchronized int getDmChanges() { return dmChanges; } } + + private static boolean isFixDelivered() { + try { + Process process = + new ProcessBuilder("/usr/bin/gnome-shell", "--version") + .start(); + + try (BufferedReader reader = process.inputReader()) { + if (process.waitFor(2, SECONDS) && process.exitValue() == 0) { + String line = reader.readLine(); + if (line != null) { + System.out.println("Gnome shell version: " + line); + String[] versionComponents = line + .replaceAll("[^\\d.]", "") + .split("\\."); + + if (versionComponents.length >= 1) { + return Integer.parseInt(versionComponents[0]) > 42; + } + } + } + } + } catch (IOException + | InterruptedException + | IllegalThreadStateException + | NumberFormatException ignored) { + } + + return false; + } } diff --git a/test/jdk/java/awt/FullScreen/NonfocusableFrameFullScreenTest.java b/test/jdk/java/awt/FullScreen/NonfocusableFrameFullScreenTest.java new file mode 100644 index 0000000000000..a273adff6ccc7 --- /dev/null +++ b/test/jdk/java/awt/FullScreen/NonfocusableFrameFullScreenTest.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.GraphicsEnvironment; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JPanel; + +/* + * @test + * @bug 6225472 6682536 + * @requires (os.family != "linux") + * @summary Tests that non-focusable Frame in full-screen mode overlaps the task bar. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual NonfocusableFrameFullScreenTest + */ + +public class NonfocusableFrameFullScreenTest extends JPanel { + boolean fullscreen = false; + + public static void main(String[] args) throws Exception { + final String INSTRUCTIONS = """ + 1. Press "Show Frame" button to show a Frame with two buttons. + + 2. Press the button "To Full Screen" to bring the frame to + full-screen mode: + + The frame should overlap the taskbar + + 3. Press "To Windowed" button: + The frame should return to its original size. + The frame shouldn't be alwaysOnTop. + + 4. Press "Set Always On Top" button and make sure the frame + is alwaysOnTop, then press "To Full Screen" button + and then "To Windowed" button: + + The frame should return to its original size keeping alwaysOnTop + state on. + + Press Pass if everything is as expected."""; + + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(45) + .testUI(NonfocusableFrameFullScreenTest::new) + .build() + .awaitAndCheck(); + } + + private NonfocusableFrameFullScreenTest() { + Button b = new Button("Show Frame"); + b.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + showFrame(); + } + }); + setLayout(new BorderLayout()); + add(b, BorderLayout.CENTER); + } + + @Override + public Dimension getPreferredSize() { + return new Dimension(200, 100); + } + + public void showFrame() { + Frame frame = new Frame("Test Frame"); + + Button button = new Button("To Full Screen"); + button.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (fullscreen) { + GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(). + setFullScreenWindow(null); + button.setLabel("To Full Screen"); + fullscreen = false; + } else { + GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(). + setFullScreenWindow(frame); + button.setLabel("To Windowed"); + fullscreen = true; + } + frame.validate(); + } + }); + + Button button2 = new Button("Set Always On Top"); + button2.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (frame.isAlwaysOnTop()) { + button2.setLabel("Set Always On Top"); + frame.setAlwaysOnTop(false); + } else { + button2.setLabel("Set Not Always On Top"); + frame.setAlwaysOnTop(true); + } + frame.validate(); + } + }); + + frame.setLayout(new BorderLayout()); + frame.add(button, BorderLayout.WEST); + frame.add(button2, BorderLayout.EAST); + frame.setBounds(400, 200, 350, 100); + frame.setFocusableWindowState(false); + frame.setVisible(true); + } +} diff --git a/test/jdk/java/awt/GradientPaint/JerkyGradient.java b/test/jdk/java/awt/GradientPaint/JerkyGradient.java new file mode 100644 index 0000000000000..dc33b9c185af8 --- /dev/null +++ b/test/jdk/java/awt/GradientPaint/JerkyGradient.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4221201 + * @summary Test where the gradient drawn should remain in sync with the + * rotating rectangle. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual JerkyGradient + */ + +import java.awt.Color; +import java.awt.Frame; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.Panel; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; + +public class JerkyGradient extends Panel implements Runnable { + protected static Shape mShape; + protected static Paint mPaint; + protected static double mTheta; + static Thread animatorThread; + static BufferedImage mImg; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Watch at least one full rotation of the rectangle. Check that + the gradient drawn remains in sync with the rotating + rectangle. If so, pass this test. Otherwise, fail this test. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(JerkyGradient::createUI) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame f = new Frame("Rotating Gradient Test"); + JerkyGradient jg = new JerkyGradient(); + f.add(jg); + f.setSize(200, 200); + return f; + } + + public JerkyGradient() { + mShape = new Rectangle2D.Double(60, 50, 80, 100); + mPaint = new GradientPaint(0, 0, Color.red, + 25, 25, Color.yellow, + true); + mImg = new BufferedImage(200, 200, BufferedImage.TYPE_INT_RGB); + + animatorThread = new Thread(this); + animatorThread.setPriority(Thread.MIN_PRIORITY); + animatorThread.start(); + } + + public synchronized void run() { + Thread me = Thread.currentThread(); + double increment = Math.PI / 36; + double twoPI = Math.PI * 2; + + while (animatorThread == me) { + mTheta = (mTheta + increment) % twoPI; + repaint(); + try { + wait(50); + } catch (InterruptedException ie) { + break; + } + } + } + + public void update(Graphics g) { + Graphics2D g2 = mImg.createGraphics(); + g2.setColor(getBackground()); + g2.fillRect(0, 0, 200, 200); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + g2.rotate(mTheta, 100, 100); + g2.setPaint(Color.black); + g2.drawLine(100, 30, 100, 55); + g2.setPaint(mPaint); + g2.fill(mShape); + g2.setPaint(Color.black); + g2.draw(mShape); + paint(g); + g2.dispose(); + } + + public void paint(Graphics g) { + g.drawImage(mImg, 0, 0, null); + } +} diff --git a/test/jdk/java/awt/GradientPaint/ShearTest.java b/test/jdk/java/awt/GradientPaint/ShearTest.java new file mode 100644 index 0000000000000..95a4e4a6dcd3e --- /dev/null +++ b/test/jdk/java/awt/GradientPaint/ShearTest.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4171820 + * @summary Checks that GradientPaint responds to shearing transforms correctly + * The gradients drawn should be parallel to the sides of the + * indicated anchor rectangle. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ShearTest + */ + +import java.awt.Canvas; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GridLayout; +import java.awt.Panel; +import java.awt.Rectangle; +import java.awt.geom.AffineTransform; + +public class ShearTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This test displays 2 rows each containing 4 gradient fills. Each + gradient fill is labeled depending on whether the line or lines + of the gradient should be truly vertical, truly horizontal, or + some slanted diagonal direction. The test passes if the direction + of each gradient matches its label. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(ShearTest::createUI) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame f = new Frame("Shear Gradient Test"); + f.setLayout(new GridLayout(0, 1)); + f.add(getPanelSet(false), "North"); + f.add(getPanelSet(true), "Center"); + f.setSize(500, 300); + return f; + } + + public static Panel getPanelSet(boolean horizontal) { + String direven = horizontal ? "Slanted" : "Vertical"; + String dirodd = horizontal ? "Horizontal" : "Slanted"; + + Panel p = new Panel(); + p.setLayout(new GridLayout(0, 4)); + p.add(new ShearCanvas(direven, false, horizontal, false, true)); + p.add(new ShearCanvas(dirodd, false, horizontal, true, false)); + p.add(new ShearCanvas(direven, true, horizontal, false, true)); + p.add(new ShearCanvas(dirodd, true, horizontal, true, false)); + + return p; + } + + public static class ShearCanvas extends Canvas { + public static final int GRADW = 30; + + public static final Rectangle anchor = + new Rectangle(-GRADW / 2, -GRADW / 2, GRADW, GRADW); + + public static final Color faintblue = new Color(0f, 0f, 1.0f, 0.35f); + + private AffineTransform txform; + private GradientPaint grad; + private String label; + + public ShearCanvas(String label, + boolean cyclic, boolean horizontal, + boolean shearx, boolean sheary) { + txform = new AffineTransform(); + if (shearx) { + txform.shear(-.5, 0); + } + if (sheary) { + txform.shear(0, -.5); + } + int relx = horizontal ? 0 : GRADW / 2; + int rely = horizontal ? GRADW / 2 : 0; + grad = new GradientPaint(-relx, -rely, Color.green, + relx, rely, Color.yellow, cyclic); + this.label = label; + } + + public void paint(Graphics g) { + Graphics2D g2d = (Graphics2D) g; + + AffineTransform at = g2d.getTransform(); + g2d.translate(75, 75); + g2d.transform(txform); + g2d.setPaint(grad); + g2d.fill(g.getClip()); + g2d.setColor(faintblue); + g2d.fill(anchor); + g2d.setTransform(at); + + Dimension d = getSize(); + g2d.setColor(Color.black); + g2d.drawRect(0, 0, d.width - 1, d.height - 1); + g2d.drawString(label, 5, d.height - 5); + g2d.dispose(); + } + } +} diff --git a/test/jdk/java/awt/Graphics/GDIResourceExhaustionTest.java b/test/jdk/java/awt/Graphics/GDIResourceExhaustionTest.java new file mode 100644 index 0000000000000..b8ba155da60ea --- /dev/null +++ b/test/jdk/java/awt/Graphics/GDIResourceExhaustionTest.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.imageio.ImageIO; +import java.awt.AWTException; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Label; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; + +/* + * @test + * @bug 4191297 + * @summary Tests that unreferenced GDI resources are correctly + * destroyed when no longer needed. + * @key headful + * @run main GDIResourceExhaustionTest + */ + +public class GDIResourceExhaustionTest extends Frame { + public void initUI() { + setSize(200, 200); + setUndecorated(true); + setLocationRelativeTo(null); + Panel labelPanel = new Panel(); + Label label = new Label("Red label"); + label.setBackground(Color.red); + labelPanel.add(label); + labelPanel.setLocation(20, 50); + add(labelPanel); + setVisible(true); + } + + public void paint(Graphics graphics) { + super.paint(graphics); + for (int rgb = 0; rgb <= 0xfff; rgb++) { + graphics.setColor(new Color(rgb)); + graphics.fillRect(0, 0, 5, 5); + } + } + + public void requestCoordinates(Rectangle r) { + Insets insets = getInsets(); + Point location = getLocationOnScreen(); + Dimension size = getSize(); + r.x = location.x + insets.left; + r.y = location.y + insets.top; + r.width = size.width - (insets.left + insets.right); + r.height = size.height - (insets.top + insets.bottom); + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, AWTException, IOException { + GDIResourceExhaustionTest test = new GDIResourceExhaustionTest(); + try { + EventQueue.invokeAndWait(test::initUI); + Robot robot = new Robot(); + robot.delay(2000); + Rectangle coords = new Rectangle(); + EventQueue.invokeAndWait(() -> { + test.requestCoordinates(coords); + }); + robot.mouseMove(coords.x - 50, coords.y - 50); + robot.waitForIdle(); + robot.delay(5000); + BufferedImage capture = robot.createScreenCapture(coords); + robot.delay(500); + boolean redFound = false; + int redRGB = Color.red.getRGB(); + for (int y = 0; y < capture.getHeight(); y++) { + for (int x = 0; x < capture.getWidth(); x++) { + if (capture.getRGB(x, y) == redRGB) { + redFound = true; + break; + } + if (redFound) { + break; + } + } + } + if (!redFound) { + File errorImage = new File("screenshot.png"); + ImageIO.write(capture, "png", errorImage); + throw new RuntimeException("Red label is not detected, possibly GDI resources exhausted"); + } + } finally { + EventQueue.invokeAndWait(test::dispose); + } + } +} diff --git a/test/jdk/java/awt/Graphics/LineLocationTest.java b/test/jdk/java/awt/Graphics/LineLocationTest.java new file mode 100644 index 0000000000000..5c382ceac959c --- /dev/null +++ b/test/jdk/java/awt/Graphics/LineLocationTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4094059 + * @summary drawing to a subclass of canvas didn't draw to the correct location. + * @key headful + * @run main LineLocationTest + */ + +import java.awt.AWTException; +import java.awt.Canvas; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.image.BufferedImage; +import java.lang.reflect.InvocationTargetException; + +public class LineLocationTest extends Frame { + private DrawScreen screen; + + public void initialize() { + setSize(400, 400); + setLocationRelativeTo(null); + setTitle("Line Location Test"); + Panel p = new Panel(); + screen = new DrawScreen(); + p.add(screen); + p.setLocation(50, 50); + p.setSize(300, 300); + add(p); + setBackground(Color.white); + setForeground(Color.blue); + setVisible(true); + } + + public void requestCoordinates(Rectangle r) { + Point location = screen.getLocationOnScreen(); + Dimension size = screen.getSize(); + r.setBounds(location.x, location.y, size.width, size.height); + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, AWTException { + LineLocationTest me = new LineLocationTest(); + EventQueue.invokeAndWait(me::initialize); + try { + Robot robot = new Robot(); + robot.delay(1000); + Rectangle coords = new Rectangle(); + EventQueue.invokeAndWait(() -> { + me.requestCoordinates(coords); + }); + BufferedImage capture = robot.createScreenCapture(coords); + robot.delay(2000); + for (int y = 0; y < capture.getHeight(); y++) { + for (int x = 0; x < capture.getWidth(); x++) { + int blue = Color.blue.getRGB(); + if (capture.getRGB(x, y) == blue) { + throw new RuntimeException("Blue detected at " + x + ", " + y); + } + } + } + } finally { + EventQueue.invokeAndWait(me::dispose); + } + } +} + +class DrawScreen extends Canvas { + public Dimension getPreferredSize() { + return new Dimension(300, 300); + } + + public void paint(Graphics g) { + g.setColor(Color.blue); + g.drawLine(5, -3145583, 50, -3145583); + } +} diff --git a/test/jdk/java/awt/Graphics/NativeWin32Clear.java b/test/jdk/java/awt/Graphics/NativeWin32Clear.java new file mode 100644 index 0000000000000..6cef4fa810aa3 --- /dev/null +++ b/test/jdk/java/awt/Graphics/NativeWin32Clear.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4216180 + * @summary This test verifies that Graphics2D.setBackground and clearRect + * performs correctly regardless of antialiasing hint. + * @key headful + * @run main NativeWin32Clear + */ + +import java.awt.AWTException; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Robot; +import java.awt.image.BufferedImage; +import java.lang.reflect.InvocationTargetException; + +public class NativeWin32Clear extends Frame { + + public void initialize() { + setLocationRelativeTo(null); + setSize(300, 200); + setBackground(Color.red); + setVisible(true); + } + + public void paint(Graphics g) { + Graphics2D g2 = (Graphics2D) g; + Dimension d = getSize(); + g2.setBackground(Color.green); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + g2.clearRect(0, 0, d.width / 2, d.height); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_OFF); + g2.clearRect(d.width / 2, 0, d.width / 2, d.height); + g2.setColor(Color.black); + } + + public void cleanup() { + setVisible(false); + dispose(); + } + + public void requestCoordinates(Rectangle r) { + Insets insets = getInsets(); + Point location = getLocationOnScreen(); + Dimension size = getSize(); + r.x = location.x + insets.left + 5; + r.y = location.y + insets.top + 5; + r.width = size.width - (insets.left + insets.right + 10); + r.height = size.height - (insets.top + insets.bottom + 10); + } + + /* + * Check color match within allowed deviation. + * Prints first non-matching pixel coordinates and actual and expected values. + * Returns true if image is filled with the provided color, false otherwise. + */ + private boolean checkColor(BufferedImage img, Color c, int delta) { + int cRed = c.getRed(); + int cGreen = c.getGreen(); + int cBlue = c.getBlue(); + for (int y = 0; y < img.getHeight(); y++) { + for (int x = 0; x < img.getWidth(); x++) { + int rgb = img.getRGB(x, y); + int red = (rgb & 0x00ff0000) >> 16; + int green = (rgb & 0x0000ff00) >> 8; + int blue = rgb & 0x000000ff; + if (cRed > (red + delta) || cRed < (red - delta) + || cGreen > (green + delta) || cGreen < (green - delta) + || cBlue > (blue + delta) || cBlue < (blue - delta)) { + System.err.println("Color at coordinates (" + x + ", " + y + ") does not match"); + System.err.println("Expected color: " + c.getRGB()); + System.err.println("Actual color: " + rgb); + System.err.println("Allowed deviation: " + delta); + return false; + } + } + } + return true; + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, AWTException { + NativeWin32Clear test = new NativeWin32Clear(); + try { + EventQueue.invokeAndWait(test::initialize); + Robot robot = new Robot(); + Rectangle coords = new Rectangle(); + EventQueue.invokeAndWait(() -> { + test.requestCoordinates(coords); + }); + robot.delay(2000); + robot.mouseMove(coords.x - 50, coords.y - 50); + robot.waitForIdle(); + BufferedImage capture = robot.createScreenCapture(coords); + robot.delay(2000); + if (!test.checkColor(capture, Color.green, 5)) { + throw new RuntimeException("Incorrect color encountered, check error log for details"); + } + } finally { + EventQueue.invokeAndWait(test::cleanup); + } + } +} diff --git a/test/jdk/java/awt/Graphics/PolygonFillTest.java b/test/jdk/java/awt/Graphics/PolygonFillTest.java new file mode 100644 index 0000000000000..72c216f9e4b56 --- /dev/null +++ b/test/jdk/java/awt/Graphics/PolygonFillTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4465509 4453725 4489667 + * @summary verify that fillPolygon completely fills area defined by drawPolygon + * @key headful + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual PolygonFillTest +*/ + +import java.awt.Color; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.Polygon; +import java.lang.reflect.InvocationTargetException; + +public class PolygonFillTest extends Frame { + Polygon poly; + static String INSTRUCTIONS = """ + There should be two hourglass shapes drawn inside the window + called "Polygon Fill Test". The outline should be blue + and the interior should be green and there should be no gaps + between the filled interior and the outline nor should the green + filler spill outside the blue outline. You may need + to use a screen magnifier to inspect the smaller shape + on the left to verify that there are no gaps. + + If both polygons painted correctly press "Pass" otherwise press "Fail". + """; + + public PolygonFillTest() { + poly = new Polygon(); + poly.addPoint(0, 0); + poly.addPoint(10, 10); + poly.addPoint(0, 10); + poly.addPoint(10, 0); + setSize(300, 300); + setTitle("Polygon Fill Test"); + } + + public void paint(Graphics g) { + int w = getWidth(); + int h = getHeight(); + Image img = createImage(20, 20); + Graphics g2 = img.getGraphics(); + drawPolys(g2, 20, 20, 5, 5); + g2.dispose(); + drawPolys(g, w, h, (w / 4) - 5, (h / 2) - 5); + g.drawImage(img, (3 * w / 4) - 40, (h / 2) - 40, 80, 80, null); + } + + public void drawPolys(Graphics g, int w, int h, int x, int y) { + g.setColor(Color.white); + g.fillRect(0, 0, w, h); + g.translate(x, y); + g.setColor(Color.green); + g.fillPolygon(poly); + g.setColor(Color.blue); + g.drawPolygon(poly); + g.translate(-x, -y); + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Polygon Fill Instructions") + .instructions(INSTRUCTIONS) + .testUI(PolygonFillTest::new) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/Graphics/RepeatedRepaintTest.java b/test/jdk/java/awt/Graphics/RepeatedRepaintTest.java new file mode 100644 index 0000000000000..140cb48630dbf --- /dev/null +++ b/test/jdk/java/awt/Graphics/RepeatedRepaintTest.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.image.BufferedImage; +import java.lang.reflect.InvocationTargetException; + +/* + * @test + * @bug 4081126 4129709 + * @summary Test for proper repainting on multiprocessor systems. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual RepeatedRepaintTest + */ +public class RepeatedRepaintTest extends Frame { + private Font font = null; + private Image background; + + static String INSTRUCTIONS = """ + The frame next to this window called "AWT Draw Test" has + some elements drawn on it. Move this window partially outside of the + screen bounds and then drag it back. Repeat it couple of times. + Drag the instructions window over the frame partially obscuring it. + If after number of attempts the frame content stops repainting + press "Fail", otherwise press "Pass". + """; + + public RepeatedRepaintTest() { + setTitle("AWT Draw Test"); + setSize(300, 300); + background = new BufferedImage(300, 300, BufferedImage.TYPE_INT_ARGB); + Graphics g = background.getGraphics(); + g.setColor(Color.black); + g.fillRect(0, 0, 300, 300); + g.dispose(); + } + + public void paint(Graphics g) { + Dimension dim = this.getSize(); + super.paint(g); + g.drawImage(background, 0, 0, dim.width, dim.height, null); + g.setColor(Color.white); + if (font == null) { + font = new Font("SansSerif", Font.PLAIN, 24); + } + g.setFont(font); + FontMetrics metrics = g.getFontMetrics(); + String message = "Draw Test"; + g.drawString(message, (dim.width / 2) - (metrics.stringWidth(message) / 2), + (dim.height / 2) + (metrics.getHeight() / 2)); + + int counter = 50; + for (int i = 0; i < 50; i++) { + counter += 4; + g.drawOval(counter, 50, i, i); + } + + counter = 20; + for (int i = 0; i < 100; i++) { + counter += 4; + g.drawOval(counter, 150, i, i); + } + g.setColor(Color.black); + g.drawLine(0, dim.height - 25, dim.width, dim.height - 25); + g.setColor(Color.gray); + g.drawLine(0, dim.height - 24, dim.width, dim.height - 24); + g.setColor(Color.lightGray); + g.drawLine(0, dim.height - 23, dim.width, dim.height - 23); + g.fillRect(0, dim.height - 22, dim.width, dim.height); + + + g.setXORMode(Color.blue); + g.fillRect(0, 0, 25, dim.height - 26); + g.setColor(Color.red); + g.fillRect(0, 0, 25, dim.height - 26); + g.setColor(Color.green); + g.fillRect(0, 0, 25, dim.height - 26); + g.setPaintMode(); + + Image img = createImage(50, 50); + Graphics imgGraphics = img.getGraphics(); + imgGraphics.setColor(Color.magenta); + imgGraphics.fillRect(0, 0, 50, 50); + imgGraphics.setColor(Color.yellow); + imgGraphics.drawString("offscreen", 0, 20); + imgGraphics.drawString("image", 0, 30); + + g.drawImage(img, dim.width - 100, dim.height - 100, Color.blue, null); + + g.setXORMode(Color.white); + drawAt(g, 100, 100, 50, 50); + drawAt(g, 105, 105, 50, 50); + drawAt(g, 110, 110, 50, 50); + } + + public void drawAt(Graphics g, int x, int y, int width, int height) { + g.setColor(Color.magenta); + g.fillRect(x, y, width, height); + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Repeated Repaint Test Instructions") + .instructions(INSTRUCTIONS) + .testUI(RepeatedRepaintTest::new) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/Graphics/SmallPrimitives.java b/test/jdk/java/awt/Graphics/SmallPrimitives.java new file mode 100644 index 0000000000000..7eb267cf0b944 --- /dev/null +++ b/test/jdk/java/awt/Graphics/SmallPrimitives.java @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Panel; +import java.awt.Polygon; +import java.awt.geom.GeneralPath; +import java.awt.geom.Line2D; +import java.lang.reflect.InvocationTargetException; + +/* + * @test + * @bug 4411814 4298688 4205762 4524760 4067534 + * @summary Check that Graphics rendering primitives function + * correctly when fed small and degenerate shapes + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual SmallPrimitives + */ + + +public class SmallPrimitives extends Panel { + + static String INSTRUCTIONS = """ + In the borderless frame next to this window there should be a + set of tiny narrow blue polygons painted next to the green rectangles. + If rectangle is vertical the corresponding polygon is painted to the right of it, + if rectangle is horizontal the polygon is painted below it. + The length of the polygon should be roughly the same as the length of the + green rectangle next to it. If size is significantly different or any of the + polygons is not painted press "Fail" otherwise press "Pass". + Note: one may consider using screen magnifier to compare sizes. + """; + + public void paint(Graphics g) { + Dimension d = getSize(); + Polygon p; + GeneralPath gp; + + g.setColor(Color.white); + g.fillRect(0, 0, d.width, d.height); + + // Reposition for horizontal tests (below) + g.translate(0, 20); + + // Reference shapes + g.setColor(Color.green); + g.fillRect(10, 7, 11, 1); + g.fillRect(10, 17, 11, 2); + g.fillRect(10, 27, 11, 1); + g.fillRect(10, 37, 11, 1); + g.fillRect(10, 47, 11, 2); + g.fillRect(10, 57, 11, 2); + g.fillRect(10, 67, 11, 1); + g.fillRect(10, 77, 11, 2); + g.fillRect(10, 87, 11, 1); + g.fillRect(10, 97, 11, 1); + g.fillRect(10, 107, 11, 1); + g.fillRect(10, 117, 6, 1); g.fillRect(20, 117, 6, 1); + + // Potentially problematic test shapes + g.setColor(Color.blue); + g.drawRect(10, 10, 10, 0); + g.drawRect(10, 20, 10, 1); + g.drawRoundRect(10, 30, 10, 0, 0, 0); + g.drawRoundRect(10, 40, 10, 0, 4, 4); + g.drawRoundRect(10, 50, 10, 1, 0, 0); + g.drawRoundRect(10, 60, 10, 1, 4, 4); + g.drawOval(10, 70, 10, 0); + g.drawOval(10, 80, 10, 1); + p = new Polygon(); + p.addPoint(10, 90); + p.addPoint(20, 90); + g.drawPolyline(p.xpoints, p.ypoints, p.npoints); + p = new Polygon(); + p.addPoint(10, 100); + p.addPoint(20, 100); + g.drawPolygon(p.xpoints, p.ypoints, p.npoints); + ((Graphics2D) g).draw(new Line2D.Double(10, 110, 20, 110)); + gp = new GeneralPath(); + gp.moveTo(10, 120); + gp.lineTo(15, 120); + gp.moveTo(20, 120); + gp.lineTo(25, 120); + ((Graphics2D) g).draw(gp); + + // Polygon limit tests + p = new Polygon(); + trypoly(g, p); + p.addPoint(10, 120); + trypoly(g, p); + + // Reposition for vertical tests (below) + g.translate(20, -20); + + // Reference shapes + g.setColor(Color.green); + g.fillRect(7, 10, 1, 11); + g.fillRect(17, 10, 2, 11); + g.fillRect(27, 10, 1, 11); + g.fillRect(37, 10, 1, 11); + g.fillRect(47, 10, 2, 11); + g.fillRect(57, 10, 2, 11); + g.fillRect(67, 10, 1, 11); + g.fillRect(77, 10, 2, 11); + g.fillRect(87, 10, 1, 11); + g.fillRect(97, 10, 1, 11); + g.fillRect(107, 10, 1, 11); + g.fillRect(117, 10, 1, 6); g.fillRect(117, 20, 1, 6); + + // Potentially problematic test shapes + g.setColor(Color.blue); + g.drawRect(10, 10, 0, 10); + g.drawRect(20, 10, 1, 10); + g.drawRoundRect(30, 10, 0, 10, 0, 0); + g.drawRoundRect(40, 10, 0, 10, 4, 4); + g.drawRoundRect(50, 10, 1, 10, 0, 0); + g.drawRoundRect(60, 10, 1, 10, 4, 4); + g.drawOval(70, 10, 0, 10); + g.drawOval(80, 10, 1, 10); + p = new Polygon(); + p.addPoint(90, 10); + p.addPoint(90, 20); + g.drawPolyline(p.xpoints, p.ypoints, p.npoints); + p = new Polygon(); + p.addPoint(100, 10); + p.addPoint(100, 20); + g.drawPolygon(p.xpoints, p.ypoints, p.npoints); + ((Graphics2D) g).draw(new Line2D.Double(110, 10, 110, 20)); + gp = new GeneralPath(); + gp.moveTo(120, 10); + gp.lineTo(120, 15); + gp.moveTo(120, 20); + gp.lineTo(120, 25); + ((Graphics2D) g).draw(gp); + + // Polygon limit tests + p = new Polygon(); + trypoly(g, p); + p.addPoint(110, 10); + trypoly(g, p); + + // Reposition for oval tests + g.translate(0, 20); + + for (int i = 0, xy = 8; i < 11; i++) { + g.setColor(Color.green); + g.fillRect(xy, 5, i, 1); + g.fillRect(5, xy, 1, i); + g.setColor(Color.blue); + g.fillOval(xy, 8, i, 1); + g.fillOval(8, xy, 1, i); + xy += i + 2; + } + + g.translate(10, 10); + for (int i = 0, xy = 9; i < 6; i++) { + g.setColor(Color.green); + g.fillRect(xy, 5, i, 2); + g.fillRect(5, xy, 2, i); + g.setColor(Color.blue); + g.fillOval(xy, 8, i, 2); + g.fillOval(8, xy, 2, i); + xy += i + 2; + } + } + + public static void trypoly(Graphics g, Polygon p) { + g.drawPolygon(p); + g.drawPolygon(p.xpoints, p.ypoints, p.npoints); + g.drawPolyline(p.xpoints, p.ypoints, p.npoints); + g.fillPolygon(p); + g.fillPolygon(p.xpoints, p.ypoints, p.npoints); + } + + public Dimension getPreferredSize() { + return new Dimension(150, 150); + } + + public static Frame createFrame() { + Frame f = new Frame(); + SmallPrimitives sp = new SmallPrimitives(); + sp.setLocation(0, 0); + f.add(sp); + f.setUndecorated(true); + f.pack(); + return f; + } + + public static void main(String argv[]) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Small Primitives Instructions") + .instructions(INSTRUCTIONS) + .columns(60) + .testUI(SmallPrimitives::createFrame) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/Graphics/TallText.java b/test/jdk/java/awt/Graphics/TallText.java new file mode 100644 index 0000000000000..b21b8a985d04b --- /dev/null +++ b/test/jdk/java/awt/Graphics/TallText.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4844952 + * @summary test large text draws properly to the screen + * @key headful + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual TallText + */ + +import java.awt.Color; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Graphics; +import java.lang.reflect.InvocationTargetException; + +public class TallText extends Frame { + static String INSTRUCTIONS = """ + There should be a window called "Tall Text Test" that contains text "ABCDEFGHIJ". + Test should be properly displayed: no missing letters + and all letters fit within the frame without overlapping. + If all letters are properly displayed press "Pass", otherwise press "Fail". + """; + + public TallText() { + setSize(800, 200); + setTitle("Tall Text Test"); + } + + public void paint(Graphics g) { + Font font = new Font("dialog", Font.PLAIN, 99); + g.setFont(font); + g.setColor(Color.black); + g.drawString("ABCDEFGHIJ", 10, 150); + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Tall Text Instructions") + .instructions(INSTRUCTIONS) + .testUI(TallText::new) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/Graphics/TextAfterXor.java b/test/jdk/java/awt/Graphics/TextAfterXor.java new file mode 100644 index 0000000000000..c9dc56d8b808e --- /dev/null +++ b/test/jdk/java/awt/Graphics/TextAfterXor.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Panel; +import java.awt.RenderingHints; +import java.awt.image.VolatileImage; +import java.lang.reflect.InvocationTargetException; + +/* + * @test + * @bug 4505650 + * @summary Check that you can render solid text after doing XOR mode + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual TextAfterXor + */ + +public class TextAfterXor extends Panel { + public static final int TESTW = 300; + public static final int TESTH = 100; + static String INSTRUCTIONS = """ + In the window called "Text After XOR Test" there should be two + composite components, at the bottom of each component the green text + "Test passes if this is green!" should be visible. + + On the top component this text should be green on all platforms. + On the bottom component it is possible that on non-Windows + platforms text can be of other color or not visible at all. + That does not constitute a problem. + + So if platform is Windows and green text appears twice or on any + other platform green text appears at least once press "Pass", + otherwise press "Fail". + """; + + VolatileImage vimg; + + public void paint(Graphics g) { + render(g); + g.drawString("(Drawing to screen)", 10, 60); + if (vimg == null) { + vimg = createVolatileImage(TESTW, TESTH); + } + do { + vimg.validate(null); + Graphics g2 = vimg.getGraphics(); + render(g2); + String not = vimg.getCapabilities().isAccelerated() ? "" : "not "; + g2.drawString("Image was " + not + "accelerated", 10, 55); + g2.drawString("(only matters on Windows)", 10, 65); + g2.dispose(); + g.drawImage(vimg, 0, TESTH, null); + } while (vimg.contentsLost()); + } + + public void render(Graphics g) { + g.setColor(Color.black); + g.fillRect(0, 0, TESTW, TESTH); + g.setColor(Color.white); + g.fillRect(5, 5, TESTW-10, TESTH-10); + + g.setColor(Color.black); + g.drawString("Test only passes if green string appears", 10, 20); + + g.setColor(Color.white); + g.setXORMode(Color.blue); + g.drawRect(30, 30, 10, 10); + g.setPaintMode(); + g.setColor(Color.green); + + ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + g.drawString("Test passes if this is green!", 10, 80); + + g.setColor(Color.black); + } + + public Dimension getPreferredSize() { + return new Dimension(TESTW, TESTH*2); + } + + public static Frame createFrame() { + Frame f = new Frame("Text After XOR Test"); + f.add(new TextAfterXor()); + f.pack(); + return f; + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Text After XOR Instructions") + .instructions(INSTRUCTIONS) + .testUI(TextAfterXor::createFrame) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/Graphics2D/BasicStrokeValidate.java b/test/jdk/java/awt/Graphics2D/BasicStrokeValidate.java new file mode 100644 index 0000000000000..251f14e5081bd --- /dev/null +++ b/test/jdk/java/awt/Graphics2D/BasicStrokeValidate.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4363534 + * @summary This test verifies that setting a non-thin-line BasicStroke + * on a Graphics2D obtained from a BufferedImage will correctly validate + * the pipelines for the line-widening pipeline even if that is the only + * non-default attribute on the graphics. + * + */ + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; + +public class BasicStrokeValidate { + + public static final int TESTW = 100; + public static final int TESTH = 100; + + public static void main(String[] args) { + BufferedImage bi1 = createImage(false); + BufferedImage bi2 = createImage(true); + compare(bi1, bi2); // images should differ + } + + static BufferedImage createImage(boolean dashed) { + BufferedImage bi = new BufferedImage(TESTW, TESTH, BufferedImage.TYPE_INT_RGB); + Graphics2D g2d = bi.createGraphics(); + g2d.setColor(Color.white); + g2d.fillRect(0, 0, TESTW, TESTH); + g2d.setColor(Color.black); + if (dashed) { + g2d.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_SQUARE, + BasicStroke.JOIN_MITER, 10.0f, + new float[] {2.5f, 3.5f}, + 0.0f)); + } + g2d.drawRect(10, 10, TESTW-20, TESTH-20); + g2d.setStroke(new BasicStroke(10f)); + g2d.drawRect(20, 20, TESTW-40, TESTH-40); + return bi; + } + + static void compare(BufferedImage i1, BufferedImage i2) { + boolean same = true; + int w = i1.getWidth(), h = i1.getHeight(); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + int p1 = i1.getRGB(x, y); + int p2 = i2.getRGB(x, y); + if (p1 != p2) { + same = false; + } + } + if (!same) { + break; + } + } + if (same) { + throw new RuntimeException("No difference"); + } + } +} diff --git a/test/jdk/java/awt/Graphics2D/DrawImageIAETest/DrawImageIAETest.java b/test/jdk/java/awt/Graphics2D/DrawImageIAETest/DrawImageIAETest.java new file mode 100644 index 0000000000000..af7ebae72a6c8 --- /dev/null +++ b/test/jdk/java/awt/Graphics2D/DrawImageIAETest/DrawImageIAETest.java @@ -0,0 +1,169 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4191004 + * @summary Tests that no IllegalArgumentException is thrown when calling + * drawImage with certain conditions + * @key headful + */ + +import java.awt.AlphaComposite; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.MediaTracker; +import java.awt.Toolkit; +import java.awt.geom.GeneralPath; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import javax.swing.JFrame; +import javax.swing.JPanel; + +public class DrawImageIAETest extends Frame { + + static String filename = "/duke.gif"; + private volatile Image dimg; + private volatile BufferedImage bimg; + static volatile DrawImageIAETest app; + static volatile JFrame jframe; + static volatile boolean passed = true; + static volatile Exception exception = null; + static volatile CountDownLatch imageLatch = new CountDownLatch(1); + + DrawImageIAETest(String title) { + super(title); + } + + public static void main(final String[] args) throws Exception { + EventQueue.invokeAndWait(DrawImageIAETest:: createUI); + imageLatch.await(3, TimeUnit.MILLISECONDS); + try { + if (!passed) { + throw new RuntimeException("Test FAILED: exception caught:" + exception); + } + } finally { + if (jframe != null) { + EventQueue.invokeAndWait(jframe::dispose); + } + if (app != null) { + EventQueue.invokeAndWait(app::dispose); + } + } + } + + static void createUI() { + app = new DrawImageIAETest("DrawImageIAETest"); + app.setLayout (new BorderLayout()); + app.setSize(200,200); + app.setLocationRelativeTo(null); + app.setVisible(true); + + String file; + try { + String dir = System.getProperty("test.src", + System.getProperty("user.dir")); + file = dir + filename; + } catch (Exception e) { + file = "." + filename; + } + + Image textureAlphaSource = null; + MediaTracker tracker = new MediaTracker(app); + app.dimg = Toolkit.getDefaultToolkit().getImage(file); + tracker.addImage(app.dimg, 1); + try { + tracker.waitForAll(); + imageLatch.countDown(); + } catch (Exception e) { + System.err.println("Can't load images"); + } + + if (app.dimg == null) { + passed = false; + return; + } + + jframe = new JFrame("Test DrawImageIAETest"); + jframe.setSize(300, 300); + JPanel jpanel; + jframe.getContentPane().add("Center", jpanel = new JPanel() { + public void paint(Graphics _g) { + Graphics2D g = (Graphics2D)_g; + Dimension d = getSize(); + Graphics2D g2 = app.createGraphics2D(d.width, d.height); + app.drawDemo(d.width, d.height, g2); + g2.dispose(); + g.drawImage(app.bimg, 0, 0, app); + } + }); + jpanel.setSize(140, 140); + jframe.setVisible(true); + } + + public void drawDemo(int w, int h, Graphics2D g2) { + GeneralPath p1 = new GeneralPath(); + GeneralPath p2 = new GeneralPath(); + + int dukeX = 73; + int dukeY = 26; + + double x = 118; + double y = 17; + double ew = 50; + double eh = 48; + + p1.append(new Ellipse2D.Double(x, y, ew, eh), false); + p2.append(new Rectangle2D.Double(x+5, y+5, ew-10, eh-10),false); + + g2.setClip(p1); + g2.clip(p2); + try { + g2.drawImage(dimg, dukeX, dukeY, null); + } catch (IllegalArgumentException e) { + passed = false; + exception = e; + } + } + + public Graphics2D createGraphics2D(int w, int h) { + Graphics2D g2 = null; + if (bimg == null || bimg.getWidth() != w || bimg.getHeight() != h) { + bimg = (BufferedImage) createImage(w, h); + } + g2 = bimg.createGraphics(); + g2.setBackground(getBackground()); + g2.clearRect(0, 0, w, h); + g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f)); + return g2; + } +} diff --git a/test/jdk/java/awt/Graphics2D/DrawImageIAETest/duke.gif b/test/jdk/java/awt/Graphics2D/DrawImageIAETest/duke.gif new file mode 100644 index 0000000000000..ed32e0ff79b05 Binary files /dev/null and b/test/jdk/java/awt/Graphics2D/DrawImageIAETest/duke.gif differ diff --git a/test/jdk/java/awt/Graphics2D/ImageRendering/ImageRendering.java b/test/jdk/java/awt/Graphics2D/ImageRendering/ImageRendering.java new file mode 100644 index 0000000000000..209a93e92d50f --- /dev/null +++ b/test/jdk/java/awt/Graphics2D/ImageRendering/ImageRendering.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4203598 + * @summary This test verifies that an image with transparent background can be displayed + * correctly with the red background color given. + * The correct display should be the sleeping Duke on a red background. + * + */ + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.io.File; +import javax.imageio.ImageIO; + +public class ImageRendering { + + public static void main(String[] args) throws Exception { + + String imgName = "snooze.gif"; + File file = new File(System.getProperty("test.src", "."), imgName); + BufferedImage image = ImageIO.read(file); + int w = image.getWidth(); + int h = image.getHeight(); + BufferedImage dest = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); + Graphics2D g2d = dest.createGraphics(); + g2d.drawImage(image, 0, 0, Color.red, null); + int redPixel = Color.red.getRGB(); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + int srcPixel = image.getRGB(x, y); + if ((srcPixel & 0x0ff000000) == 0) { + int destPix = dest.getRGB(x, y); + if (destPix != redPixel) { + throw new RuntimeException("Not red at x=" + x + + " y=" + y + + "pix = " + Integer.toHexString(destPix)); + } + } + } + } + } +} diff --git a/test/jdk/java/awt/Graphics2D/ImageRendering/snooze.gif b/test/jdk/java/awt/Graphics2D/ImageRendering/snooze.gif new file mode 100644 index 0000000000000..e357e316cdbef Binary files /dev/null and b/test/jdk/java/awt/Graphics2D/ImageRendering/snooze.gif differ diff --git a/test/jdk/java/awt/Graphics2D/ScaledThinLineTest.java b/test/jdk/java/awt/Graphics2D/ScaledThinLineTest.java new file mode 100644 index 0000000000000..fd4a5dd5e7ca9 --- /dev/null +++ b/test/jdk/java/awt/Graphics2D/ScaledThinLineTest.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 4210466 4417756 + * @summary thin lines are not draw correctly under large scales + */ +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.image.BufferedImage; +import java.awt.geom.Ellipse2D; + +public class ScaledThinLineTest { + + public static void main(String[] args) { + ScaledThinLineTest c1 = new ScaledThinLineTest(200, 200); + ScaledThinLineTest c2 = new ScaledThinLineTest(1, 10000); + ScaledThinLineTest c3 = new ScaledThinLineTest(10000, 1); + ScaledThinLineTest c4 = new ScaledThinLineTest(0.01, 10000); + ScaledThinLineTest c5 = new ScaledThinLineTest(10000, 0.01); + compare(c1.bi, c2.bi); + compare(c2.bi, c3.bi); + compare(c3.bi, c4.bi); + compare(c4.bi, c5.bi); + } + + private final Shape shape; + private final double scaleX,scaleY; + private BufferedImage bi = null; + + public ScaledThinLineTest(double width, double height) { + shape = new Ellipse2D.Double(0.25*width, 0.25*height, width, height); + scaleX = 200/width; + scaleY = 200/height; + int iw = 300, ih = 300; + bi = new BufferedImage(iw, ih, BufferedImage.TYPE_INT_RGB); + Graphics2D g2 = bi.createGraphics(); + g2.setColor(Color.white); + g2.fillRect(0, 0, iw, ih); + g2.setColor(Color.black); + g2.scale(scaleX,scaleY); + g2.setStroke(new BasicStroke(0)); + g2.draw(shape); + } + + + static void compare(BufferedImage i1, BufferedImage i2) { + int w = i1.getWidth(), h = i1.getHeight(); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + int p1 = i1.getRGB(x, y); + int p2 = i2.getRGB(x, y); + if (p1 != p2) { + System.out.println("images differ at " + x + " " + y); + } + } + } + } +} diff --git a/test/jdk/java/awt/Graphics2D/TextPerf.java b/test/jdk/java/awt/Graphics2D/TextPerf.java new file mode 100644 index 0000000000000..d3e5cc2d4d0dd --- /dev/null +++ b/test/jdk/java/awt/Graphics2D/TextPerf.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4190429 + * @key headful + * @summary In this bug, text drawing performance should be reasonable. + * And should (per string) be consistent with the size of the + * rectangle in which the string is drawn, not the rectangle + * bounding the whole window. + */ + +import java.awt.BorderLayout; +import java.awt.Canvas; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Panel; +import java.awt.RenderingHints; +import java.awt.Toolkit; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class TextPerf extends Canvas { + + static volatile CountDownLatch paintLatch = new CountDownLatch(1); + static volatile long paintTime = 5000; // causes test fail if it is not updated. + static volatile Frame frame; + + public static void main(String[] args) throws Exception { + EventQueue.invokeAndWait(TextPerf::createUI); + paintLatch.await(5, TimeUnit.SECONDS); + if (paintTime > 2000) { + throw new RuntimeException("Paint time is " + paintTime + "ms"); + } + if (frame != null) { + EventQueue.invokeAndWait(frame::dispose); + } + } + + static void createUI() { + frame = new Frame("TextPerf"); + frame.setLayout(new BorderLayout()); + TextPerf tp = new TextPerf(); + frame.add(tp, BorderLayout.CENTER); + frame.pack(); + frame.setVisible(true); + } + + public Dimension getPreferredSize() { + Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); + return new Dimension(d.width - 50, d.height - 50); + } + + static Font[] fonts = { + new Font(Font.SERIF, Font.PLAIN, 10), + new Font(Font.SANS_SERIF, Font.PLAIN, 10), + new Font(Font.MONOSPACED, Font.ITALIC, 10), + new Font(Font.SERIF, Font.PLAIN, 14), + new Font(Font.SERIF, Font.BOLD, 12), + }; + + public void paint(Graphics g1) { + + Graphics2D g = (Graphics2D)g1; + String text = "Hello,_Wgjpqy!"; + Toolkit.getDefaultToolkit().sync(); + long startTime = System.currentTimeMillis(); + FontMetrics[] cachedMetrics = new FontMetrics[fonts.length]; + Dimension size = getSize(); + int prim = 0; + int spaceWidth = 5; + Color cols[] = { Color.red, Color.blue, Color.yellow, + Color.green, Color.pink, Color.orange} ; + + for (int y = 20; y < size.height; y += 20) { + int i = 0; + for (int x = 0; x < size.width; i++) { + Font font = fonts[i % fonts.length]; + FontMetrics metrics = cachedMetrics[i % fonts.length]; + if (metrics == null) { + metrics = g.getFontMetrics(font); + cachedMetrics[i % fonts.length] = metrics; + } + + g.setFont(font); + g.setColor(cols[i % cols.length]); + switch (prim++) { + case 0: g.drawString(text, x, y); + break; + case 1: g.drawBytes(text.getBytes(), 0, text.length(), x, y); + break; + case 2: char[] chs= new char[text.length()]; + text.getChars(0,text.length(), chs, 0); + g.drawChars(chs, 0, text.length(), x, y); + break; + case 3: GlyphVector gv = font.createGlyphVector( + g.getFontRenderContext(), text); + g.drawGlyphVector(gv, (float)x, (float)y); + default: prim = 0; + } + + x += metrics.stringWidth(text) + spaceWidth; + } + } + + // Draw some transformed text to verify correct bounds calculated + AffineTransform at = AffineTransform.getTranslateInstance(50, 50); + at.scale(7.0,7.0); + at.rotate(1.0); + g.transform(at); + g.setColor(Color.black); + Font font = new Font(Font.SERIF, Font.PLAIN, 20); + RenderingHints hints = new RenderingHints(null); + hints.put(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + g.setRenderingHints(hints); + g.setFont(font); + FontMetrics metrics = g.getFontMetrics(font); + g.drawString("Java", 5,5); + + Toolkit.getDefaultToolkit().sync(); + long endTime = System.currentTimeMillis(); + paintTime = endTime - startTime; + String msg = "repainted in " + paintTime + " milliseconds"; + System.out.println(msg); + System.out.flush(); + + paintLatch.countDown(); + } +} diff --git a/test/jdk/java/awt/GraphicsEnvironment/DefaultScreenDeviceTest.java b/test/jdk/java/awt/GraphicsEnvironment/DefaultScreenDeviceTest.java new file mode 100644 index 0000000000000..b59460322daf6 --- /dev/null +++ b/test/jdk/java/awt/GraphicsEnvironment/DefaultScreenDeviceTest.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Color; +import java.awt.Frame; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Label; +import java.awt.Rectangle; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.util.List; + +/* + * @test + * @bug 4473671 + * @summary Test to verify GraphicsEnvironment.getDefaultScreenDevice always + * returning first screen + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DefaultScreenDeviceTest + */ + +public class DefaultScreenDeviceTest { + private static Frame testFrame; + + public static void main(String[] args) throws Exception { + GraphicsEnvironment ge = GraphicsEnvironment. + getLocalGraphicsEnvironment(); + GraphicsDevice[] gds = ge.getScreenDevices(); + if (gds.length < 2) { + System.out.println("Test requires at least 2 displays"); + return; + } + + String INSTRUCTIONS = """ + 1. The test is for systems which allows primary display + selection in multiscreen systems. + Set the system primary screen to be the rightmost + (i.e. the right screen in two screen configuration) + This can be done by going to OS Display Settings + selecting the screen and checking the 'Use this device + as primary monitor' checkbox. + 2. When done, click on 'Frame on Primary Screen' button and + see where the frame will pop up + 3. If Primary Frame pops up on the primary display, + the test passed, otherwise it failed + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .build() + .awaitAndCheck(); + } + + private static List initialize() { + Frame frame = new Frame("Default screen device test"); + GraphicsConfiguration gc = + GraphicsEnvironment.getLocalGraphicsEnvironment(). + getDefaultScreenDevice().getDefaultConfiguration(); + + testFrame = new Frame("Primary screen frame", gc); + frame.setLayout(new BorderLayout()); + frame.setSize(200, 200); + + Button b = new Button("Frame on Primary Screen"); + b.addActionListener(e -> { + if (testFrame != null) { + testFrame.setVisible(false); + testFrame.dispose(); + } + + testFrame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e1) { + testFrame.setVisible(false); + testFrame.dispose(); + } + }); + testFrame.add(new Label("This frame should be on the primary screen")); + testFrame.setBackground(Color.red); + testFrame.pack(); + Rectangle rect = gc.getBounds(); + testFrame.setLocation(rect.x + 100, rect.y + 100); + testFrame.setVisible(true); + }); + frame.add(b); + return List.of(testFrame, frame); + } +} diff --git a/test/jdk/java/awt/GridBagLayout/ComponentShortage.java b/test/jdk/java/awt/GridBagLayout/ComponentShortage.java new file mode 100644 index 0000000000000..dd641bbf066c7 --- /dev/null +++ b/test/jdk/java/awt/GridBagLayout/ComponentShortage.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4238932 + * @summary JTextField in gridBagLayout does not properly set MinimumSize + * @key headful + * @run main ComponentShortage + */ + +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Robot; +import javax.swing.JFrame; +import javax.swing.JTextField; + +public class ComponentShortage { + static final int WIDTH_REDUCTION = 50; + static JFrame frame; + static JTextField jtf; + static volatile Dimension size; + static volatile Dimension fSize; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + try { + EventQueue.invokeAndWait(() -> { + frame = new JFrame(); + frame.setLayout(new GridBagLayout()); + GridBagConstraints gBC = new GridBagConstraints(); + + gBC.gridx = 1; + gBC.gridy = 0; + gBC.gridwidth = 1; + gBC.gridheight = 1; + gBC.weightx = 1.0; + gBC.weighty = 0.0; + gBC.fill = GridBagConstraints.NONE; + gBC.anchor = GridBagConstraints.NORTHWEST; + jtf = new JTextField(16); + frame.add(jtf, gBC); + frame.pack(); + frame.setVisible(true); + }); + robot.waitForIdle(); + robot.delay(1000); + + EventQueue.invokeAndWait(() -> { + size = jtf.getSize(); + }); + System.out.println("TextField size before Frame's width reduction : " + size); + + EventQueue.invokeAndWait(() -> { + frame.setSize(frame.getSize().width - WIDTH_REDUCTION, frame.getSize().height); + }); + frame.repaint(); + + EventQueue.invokeAndWait(() -> { + size = jtf.getSize(); + fSize = frame.getSize(); + }); + System.out.println("TextField size after Frame's width reduction : " + size); + + if (size.width < fSize.width - WIDTH_REDUCTION) { + throw new RuntimeException("Width of JTextField is too small to be visible."); + } + System.out.println("Test passed."); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } +} diff --git a/test/jdk/java/awt/Icon/ChildFrameIconTest.java b/test/jdk/java/awt/Icon/ChildFrameIconTest.java new file mode 100644 index 0000000000000..6d83fa95acba7 --- /dev/null +++ b/test/jdk/java/awt/Icon/ChildFrameIconTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JOptionPane; + +/* + * @test + * @bug 4284610 + * @summary Tests that a child of the non-resizable dialog acquires valid icon. + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ChildFrameIconTest + */ + +public class ChildFrameIconTest { + public static void main(String[] args) throws Exception { + final String INSTRUCTIONS = """ + Press "Show Dialog" button to open a dialog with this message: + Do you see a coffee cup icon in the upper left corner ? + + Look at the icon in the upper left corner of the message dialog. + + Press Pass if you see default coffee cup icon else press Fail."""; + + PassFailJFrame.builder() + .title("ChildFrameIconTest Instruction") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(ChildFrameIconTest::createUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createUI() { + JFrame f = new JFrame("ChildFrameIconTest UI"); + JButton b = new JButton("Show Dialog"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + String msg = "Do you see a coffee cup icon in the upper left corner ?"; + JDialog dlg = new JDialog(f, "Non-resizable JDialog", false); + dlg.setResizable(false); + JOptionPane.showMessageDialog(dlg, msg); + } + }); + f.add(b); + f.setSize(250, 100); + return f; + } +} diff --git a/test/jdk/java/awt/InputMethods/DiacriticsTest/DiacriticsTest.html b/test/jdk/java/awt/InputMethods/DiacriticsTest/DiacriticsTest.html deleted file mode 100644 index 5cf5c91ea71bd..0000000000000 --- a/test/jdk/java/awt/InputMethods/DiacriticsTest/DiacriticsTest.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - DiacriticsTest - - - - -Test run requires the following keyboard layouts to be installed: -Linux OS: English (US, alternative international) -Windows OS: Hungarian -A keyboard layout having compose function or compose-like key. Programmer -Dvorak (http://www.kaufmann.no/roland/dvorak/) is suggested to use. - -To test JDK-8000423 fix (Linux only!): -please switch to US alternative international layout and try to type diacritics -(using the following combinations: `+e; `+u; etc.) - -To test JDK-7197619 fix (Windows only!): -please switch to Hungarian keyboard layout and try to type diacritics -(Ctrl+Alt+2 e; Ctrl+Alt+2 E) - -To test JDK-8139189 fix: -please switch to Programmer Dvorak keyboard layout try to type diacritics -using compose combinations (Compose+z+d, Compose+z+Shift+d). The Compose key -in Programmer Dvorak layout is OEM102 the key which is located between -Left Shift and Z keys on the standard 102-key keyboard. - -If you can do that then the test is passed; otherwise failed. - - diff --git a/test/jdk/java/awt/InputMethods/DiacriticsTest/DiacriticsTest.java b/test/jdk/java/awt/InputMethods/DiacriticsTest/DiacriticsTest.java index 7c610e4791f33..1481f7ec47733 100644 --- a/test/jdk/java/awt/InputMethods/DiacriticsTest/DiacriticsTest.java +++ b/test/jdk/java/awt/InputMethods/DiacriticsTest/DiacriticsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,30 +21,94 @@ * questions. */ +import jdk.test.lib.Platform; +import java.awt.GridLayout; +import java.awt.TextArea; +import java.awt.TextField; +import javax.swing.JPanel; /* - @test - @bug 8000423 7197619 8025649 - @summary Check if diacritical signs could be typed for TextArea and TextField - @run applet/manual=yesno DiacriticsTest.html -*/ + * @test + * @bug 8000423 7197619 8025649 + * @summary Check if diacritical signs could be typed for TextArea and TextField + * @requires (os.family == "windows" | os.family == "linux") + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame jdk.test.lib.Platform + * @run main/manual DiacriticsTest + */ +public class DiacriticsTest { -import java.applet.Applet; -import java.awt.*; -import javax.swing.JPanel; + private static final String INSTRUCTIONS_WIN = """ + Test run requires the following keyboard layouts to be installed: + - Hungarian + - A keyboard layout having compose function or compose-like key. Programmer + Dvorak (http://www.kaufmann.no/roland/dvorak/) is suggested to use. + To the right are a text area and a text field, you should check the behavior + for both of them. -public class DiacriticsTest extends Applet { + To test the JDK-7197619 fix: + Please switch to Hungarian keyboard layout and try to type diacritics + (Ctrl+Alt+2 e; Ctrl+Alt+2 E) - public void init() { - this.setLayout(new BorderLayout()); - } + To test the JDK-8139189 fix: + Please switch to Programmer Dvorak keyboard layout try to type diacritics + using compose combinations (Compose+z+d, Compose+z+Shift+d). + + The Compose key in the Programmer Dvorak layout is OEM102, the key located + between the and Z keys on a standard 102-key keyboard. + If you do not have this key on your keyboard, you can skip this part of the test. + + If you can do that then the test is passed; otherwise failed. + """; + + private static final String INSTRUCTIONS_LIN = """ + Test run requires the following keyboard layouts to be installed: + - English (US, alternative international), aka English (US, alt. intl.) + - A keyboard layout having compose function or compose-like key. Programmer + Dvorak (http://www.kaufmann.no/roland/dvorak/) is suggested to use. + + To the right are a text area and a text field, you should check the behavior + for both of them. - public void start() { + To test the JDK-8000423 fix: + Please switch to US alternative international layout and try to type diacritics + (using the following combinations: `+e; `+u; etc.) - setSize(350, 200); + To test the JDK-8139189 fix: + Please switch to Programmer Dvorak keyboard layout try to type diacritics + using compose combinations (Compose+z+d, Compose+z+Shift+d).. + The Compose key in the Programmer Dvorak layout is OEM102, the key located + between the and Z keys on a standard 102-key keyboard. + + If the above key does not work in the Gnome shell, + it can be overridden in the system preferences: + System > Keyboard > Special character entry > Compose key + and set it to another key(e.g. menu key or scroll lock.) + + If you can do that then the test is passed; otherwise failed. + """; + + public static void main(String[] args) throws Exception { + String instructions = Platform.isWindows() + ? INSTRUCTIONS_WIN + : INSTRUCTIONS_LIN; + + PassFailJFrame + .builder() + .title("DiacriticsTest Instructions") + .instructions(instructions) + .splitUIRight(DiacriticsTest::createPanel) + .testTimeOut(10) + .rows((int) instructions.lines().count() + 2) + .columns(50) + .build() + .awaitAndCheck(); + } + + public static JPanel createPanel() { JPanel panel = new JPanel(); panel.setLayout(new GridLayout(2, 1)); @@ -54,10 +118,6 @@ public void start() { TextField txtField = new TextField(); panel.add(txtField); - add(panel, BorderLayout.CENTER); - - validate(); - setVisible(true); + return panel; } } - diff --git a/test/jdk/java/awt/InputMethods/InputMethodsTest/InputMethodsTest.html b/test/jdk/java/awt/InputMethods/InputMethodsTest/InputMethodsTest.html deleted file mode 100644 index 1d24a3523b50f..0000000000000 --- a/test/jdk/java/awt/InputMethods/InputMethodsTest/InputMethodsTest.html +++ /dev/null @@ -1,40 +0,0 @@ - - - - - InputMethodsTest - - - - -This test is for Linux only. For other platforms please simply push "Pass". - -Test run requires some Japanese input method to be installed. - -To test JDK-7146572 fix please perform the following steps: -1. Switch on input method and type Japanese in the above text fields. -2. Push "Disable Input Methods" button. -3. Try to type Japanese again. If it can be done then the test is failed; otherwise passed. - - diff --git a/test/jdk/java/awt/InputMethods/InputMethodsTest/InputMethodsTest.java b/test/jdk/java/awt/InputMethods/InputMethodsTest/InputMethodsTest.java index a5665c906b9fb..a3037e02406fd 100644 --- a/test/jdk/java/awt/InputMethods/InputMethodsTest/InputMethodsTest.java +++ b/test/jdk/java/awt/InputMethods/InputMethodsTest/InputMethodsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,68 +21,75 @@ * questions. */ +import java.awt.TextArea; +import java.awt.TextField; +import javax.swing.Box; +import javax.swing.JButton; +import javax.swing.JComponent; /* - @test - @bug 7146572 8024122 - @summary Check if 'enableInputMethods' works properly for TextArea and TextField on Linux platform - @author a.stepanov - @run applet/manual=yesno InputMethodsTest.html + * @test + * @bug 7146572 8024122 + * @summary Check if 'enableInputMethods' works properly for TextArea and TextField on Linux platform + * @requires (os.family == "linux") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual InputMethodsTest */ - -import java.applet.Applet; -import java.awt.*; -import javax.swing.*; -import java.awt.event.*; - - -public class InputMethodsTest extends Applet { - - TextArea txtArea = null; - TextField txtField = null; - JButton btnIM = null; - boolean inputMethodsEnabled = true; - - public void init() { - this.setLayout(new BorderLayout()); +public class InputMethodsTest { + + private static final String INSTRUCTIONS = """ + Test run requires some Japanese input method to be installed. + + To test the JDK-7146572 fix, please follow these steps: + 1. Enable the input method. + 2. Type Japanese in the text area and the text field to the right. + 2. Press the "Disable Input Methods" button. + 3. Try typing Japanese again. + 4. If input methods are not disabled, then press fail; + otherwise, press pass. + """; + + static boolean inputMethodsEnabled = true; + + public static void main(String[] args) throws Exception { + PassFailJFrame + .builder() + .title("InputMethodsTest Instructions") + .instructions(INSTRUCTIONS) + .splitUIRight(InputMethodsTest::createPanel) + .testTimeOut(10) + .rows(10) + .columns(40) + .build() + .awaitAndCheck(); } - public void start() { + public static JComponent createPanel() { + Box verticalBox = Box.createVerticalBox(); - setSize(350, 200); + TextArea textArea = new TextArea(); + verticalBox.add(textArea); - JPanel panel = new JPanel(); - panel.setLayout(new GridLayout(2, 1)); + TextField textField = new TextField(); + verticalBox.add(textField); - txtArea = new TextArea(); - panel.add(txtArea); + JButton btnIM = new JButton(); + setBtnText(btnIM); - txtField = new TextField(); - panel.add(txtField); - - add(panel, BorderLayout.CENTER); - - btnIM = new JButton(); - setBtnText(); - - btnIM.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - inputMethodsEnabled = !inputMethodsEnabled; - setBtnText(); - txtArea.enableInputMethods(inputMethodsEnabled); - txtField.enableInputMethods(inputMethodsEnabled); - } + btnIM.addActionListener(e -> { + inputMethodsEnabled = !inputMethodsEnabled; + setBtnText(btnIM); + textArea.enableInputMethods(inputMethodsEnabled); + textField.enableInputMethods(inputMethodsEnabled); }); - add(btnIM, BorderLayout.SOUTH); - - validate(); - setVisible(true); + verticalBox.add(btnIM); + return verticalBox; } - private void setBtnText() { + private static void setBtnText(JButton btnIM) { String s = inputMethodsEnabled ? "Disable" : "Enable"; btnIM.setText(s + " Input Methods"); } diff --git a/test/jdk/java/awt/InputMethods/SpanishDiacriticsTest.java b/test/jdk/java/awt/InputMethods/SpanishDiacriticsTest.java new file mode 100644 index 0000000000000..0468f222774b7 --- /dev/null +++ b/test/jdk/java/awt/InputMethods/SpanishDiacriticsTest.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8169355 + * @summary Check if Spanish diacritical signs could be typed for TextField + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @run main/manual SpanishDiacriticsTest +*/ + + +import java.util.concurrent.locks.LockSupport; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import javax.swing.JFrame; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; +import javax.swing.WindowConstants; + +public class SpanishDiacriticsTest { + + static final String INSTRUCTIONS = """ + This test requires the following keyboard layout to be installed: + Windows OS: Spanish (United States) with 'Latin American' keyboard layout. + If using a US layout, the results should still be as described but + you have not tested the real bug. + + 1. A frame with a text field should be displayed. + 2. Set focus to the text field and switch to Spanish + with 'Latin American' keyboard layout. + 3. Type the following: ' ' o - i.e. single quote two times, then o. + If your keyboard has a US physical layout the [ key can be used + to type the single quote when in 'Latin American' keyboard mode. + 4. Type these characters at a normal speed but do NOT be concerned + that they take several seconds to display. That is an + expected behaviour for this test. + + If the text field displays the same three characters you typed: ''o + (i.e. two single quotes followed by o without an acute) + then press Pass; otherwise press Fail. + """; + + public static void main(String[] args) throws Exception { + + PassFailJFrame.builder() + .title("Spanish Diacritics") + .instructions(INSTRUCTIONS) + .rows(20) + .columns(50) + .testUI(SpanishDiacriticsTest::createTestUI) + .build() + .awaitAndCheck(); + } + + static JFrame createTestUI() { + JFrame frame = new JFrame("Spanish Diacritics Test Frame"); + JTextField textField = new JTextField(20); + textField.addKeyListener(new KeyAdapter() { + @Override + public void keyTyped(KeyEvent e) { + LockSupport.parkNanos(1_000_000_000L); + } + }); + frame.add(textField); + frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + frame.pack(); + return frame; + } +} + diff --git a/test/jdk/java/awt/InputMethods/SpanishDiacriticsTest/SpanishDiacriticsTest.html b/test/jdk/java/awt/InputMethods/SpanishDiacriticsTest/SpanishDiacriticsTest.html deleted file mode 100644 index af6cd0c76102a..0000000000000 --- a/test/jdk/java/awt/InputMethods/SpanishDiacriticsTest/SpanishDiacriticsTest.html +++ /dev/null @@ -1,40 +0,0 @@ - - - - - SpanishDiacriticsTest - - - - -Test run requires the following keyboard layout to be installed: -Windows OS: Spanish (United States) with 'Latin American' keyboard layout - -1. A frame with a text field should be displayed at upper left corner -2. Set focus to the text field and switch to Spanish with 'Latin American' keyboard layout -3. Type the following: ' ' o - i.e. single quote two times (using [ key on US keyboard) then o character. - -If you the text field displays ''o, (i.e. o should be without acute) then the test is passed; otherwise failed. - - diff --git a/test/jdk/java/awt/InputMethods/SpanishDiacriticsTest/SpanishDiacriticsTest.java b/test/jdk/java/awt/InputMethods/SpanishDiacriticsTest/SpanishDiacriticsTest.java deleted file mode 100644 index 78877061e602f..0000000000000 --- a/test/jdk/java/awt/InputMethods/SpanishDiacriticsTest/SpanishDiacriticsTest.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @bug 8169355 - * @summary Check if Spanish diacritical signs could be typed for TextField - * @author Dmitry Markov - * @run applet/manual=yesno SpanishDiacriticsTest.html -*/ - -import javax.swing.*; -import java.applet.Applet; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.util.concurrent.locks.LockSupport; - -public class SpanishDiacriticsTest extends Applet { - @Override - public void init() { - SwingUtilities.invokeLater(() -> { - JFrame frame = new JFrame(); - JTextField textField = new JTextField(20); - textField.addKeyListener(new KeyAdapter() { - @Override - public void keyTyped(KeyEvent e) { - LockSupport.parkNanos(1_000_000_000L); - } - }); - frame.add(textField); - frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); - frame.pack(); - frame.setVisible(true); - }); - } -} - diff --git a/test/jdk/java/awt/Label/ContainerValidateTest.java b/test/jdk/java/awt/Label/ContainerValidateTest.java new file mode 100644 index 0000000000000..e379ebb606b8c --- /dev/null +++ b/test/jdk/java/awt/Label/ContainerValidateTest.java @@ -0,0 +1,169 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Panel; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.TextField; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +/* + * @test + * @key headful + * @bug 4247913 + * @summary Tests that Label repaints after call Container.validate() + * @run main ContainerValidateTest + */ + +public class ContainerValidateTest extends Frame implements MouseListener { + private static Robot robot; + private static Panel currentPanel; + private static Button currentBtn; + private static Panel updatedPanel; + private static Label updatedLabel; + private static TextField updatedTxtField; + private static Button updatedBtn; + + private static volatile Rectangle btnBounds; + + Panel pnl1 = new Panel(); + Panel pnl2 = new Panel(); + Label lbl1 = new Label("Label 1"); + Label lbl2 = new Label("Label 2"); + TextField txt1 = new TextField("field1", 20); + TextField txt2 = new TextField("field2", 20); + Button btn1 = new Button("Swap 1"); + Button btn2 = new Button("Swap 2"); + + public static void main(String[] args) throws Exception { + robot = new Robot(); + + ContainerValidateTest containerValidate = new ContainerValidateTest(); + EventQueue.invokeAndWait(containerValidate::createAndShowUI); + robot.waitForIdle(); + robot.delay(1000); + + containerValidate.testUI(); + } + + private void createAndShowUI() { + this.setTitle("ContainerValidateTest Test"); + pnl1.add(lbl1); + pnl1.add(txt1); + pnl1.add(btn1); + + pnl2.add(lbl2); + pnl2.add(txt2); + pnl2.add(btn2); + + btn1.addMouseListener(this); + btn2.addMouseListener(this); + + this.add(pnl1, BorderLayout.CENTER); + pack(); + setLocationRelativeTo(null); + setVisible(true); + } + + private void testUI() throws Exception { + EventQueue.invokeAndWait(() -> btnBounds + = new Rectangle(btn1.getLocationOnScreen().x, + btn1.getLocationOnScreen().y, + btn1.getWidth(), + btn1.getHeight())); + for (int i= 1; i < 4 ; i++) { + EventQueue.invokeAndWait(() -> { + currentPanel = (Panel) this.getComponent(0); + currentBtn = (Button) currentPanel.getComponent(2); + }); + + robot.mouseMove(btnBounds.x + (int) btnBounds.getWidth() / 2, + btnBounds.y + (int) btnBounds.getHeight() / 2); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + //large delay set for completion of UI validate() + robot.delay(500); + + EventQueue.invokeAndWait(() -> { + updatedPanel = (Panel) this.getComponent(0); + updatedLabel = (Label) updatedPanel.getComponent(0); + updatedTxtField = (TextField) updatedPanel.getComponent(1); + updatedBtn = (Button) updatedPanel.getComponent(2); + }); + testPanelComponents(currentBtn.getLabel()); + } + } + + private void testPanelComponents(String btnLabel) { + if (btnLabel.equals("Swap 1")) { + if (!(updatedLabel.getText().equals(lbl2.getText()) + && updatedTxtField.getText().equals(txt2.getText()) + && updatedBtn.getLabel().equals(btn2.getLabel()))) { + throw new RuntimeException("Test Failed!! Labels not repainted" + + " after Container.validate()"); + } + } else { + if (!(updatedLabel.getText().equals(lbl1.getText()) + && updatedTxtField.getText().equals(txt1.getText()) + && updatedBtn.getLabel().equals(btn1.getLabel()))) { + throw new RuntimeException("Test Failed!! Labels not repainted" + + " after Container.validate()"); + } + } + } + + @Override + public void mousePressed(MouseEvent evt) { + if (evt.getComponent() instanceof Button btn) { + if (btn.equals(btn1)) { + remove(pnl1); + add(pnl2, BorderLayout.CENTER); + } else { + remove(pnl2); + add(pnl1, BorderLayout.CENTER); + } + invalidate(); + validate(); + } + } + + @Override + public void mouseReleased(MouseEvent e) {} + + @Override + public void mouseEntered(MouseEvent e) {} + + @Override + public void mouseExited(MouseEvent e) {} + + @Override + public void mouseClicked(MouseEvent e) {} +} diff --git a/test/jdk/java/awt/LightweightComponent/LWParentMovedTest/LWParentMovedTest.java b/test/jdk/java/awt/LightweightComponent/LWParentMovedTest/LWParentMovedTest.java new file mode 100644 index 0000000000000..d46af3a0d5e51 --- /dev/null +++ b/test/jdk/java/awt/LightweightComponent/LWParentMovedTest/LWParentMovedTest.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4147246 + * @summary Simple check for peer != null in Component.componentMoved + * @key headful + * @run main LWParentMovedTest + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Container; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Graphics; + +public class LWParentMovedTest { + static CMTFrame f; + + // test will throw an exception and fail if lwc is null + public static void main(String[] args) throws Exception { + try { + EventQueue.invokeAndWait(() -> f = new CMTFrame()); + } finally { + if (f != null) { + EventQueue.invokeAndWait(() -> f.dispose()); + } + } + } +} + +class CMTFrame extends Frame { + Container lwc; + Button button; + + public CMTFrame() { + super("Moving LWC Test"); + setLayout(new FlowLayout()); + lwc = new LWSquare(Color.blue, 100, 100); + button = new Button(); + lwc.add(button); + add(lwc); + + setSize(400, 300); + setVisible(true); + + // queue up a bunch of COMPONENT_MOVED events + for (int i = 0; i < 1000; i++) { + lwc.setLocation(i, i); + } + + // remove heavyweight from lightweight container + lwc.remove(button); + } +} + +// +// Lightweight container +// +class LWSquare extends Container { + int width; + int height; + + public LWSquare(Color color, int w, int h) { + setBackground(color); + setLayout(new FlowLayout()); + width = w; + height = h; + setName("LWSquare-" + color.toString()); + } + + public void paint(Graphics g) { + g.setColor(getBackground()); + g.fillRect(0, 0, 1000, 1000); + } +} diff --git a/test/jdk/java/awt/LightweightComponent/LightWeightTabFocus/LightWeightTabFocus.java b/test/jdk/java/awt/LightweightComponent/LightWeightTabFocus/LightWeightTabFocus.java new file mode 100644 index 0000000000000..05889580fd423 --- /dev/null +++ b/test/jdk/java/awt/LightweightComponent/LightWeightTabFocus/LightWeightTabFocus.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4095214 + * @summary Test change of focus on lightweights using the tab key + * @key headful + * @run main LightWeightTabFocus + */ + +import java.awt.Button; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +public class LightWeightTabFocus { + private static Frame f; + private static LightweightButton btn1; + private static Button btn2; + private static Robot robot; + private static volatile Point point; + private static Point loc; + + public static void main(String[] args) throws Exception { + robot = new Robot(); + robot.setAutoDelay(100); + try { + EventQueue.invokeAndWait(() -> createUI()); + robot.delay(1000); + EventQueue.invokeAndWait(() -> { + loc = f.getLocation(); + point = btn2.getLocation(); + }); + robot.mouseMove(loc.x + point.x, loc.y + point.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + // First TAB + robot.keyPress(KeyEvent.VK_TAB); + robot.keyRelease(KeyEvent.VK_TAB); + if (!btn1.hasFocus()) { + new RuntimeException("First tab failed"); + } + // Second TAB + robot.keyPress(KeyEvent.VK_TAB); + robot.keyRelease(KeyEvent.VK_TAB); + if (!btn2.hasFocus()) { + new RuntimeException("Second tab failed"); + } + // First SHIFT+TAB + robot.keyPress(KeyEvent.VK_SHIFT); + robot.keyPress(KeyEvent.VK_TAB); + robot.delay(100); + robot.keyRelease(KeyEvent.VK_TAB); + robot.keyRelease(KeyEvent.VK_SHIFT); + if (!btn1.hasFocus()) { + new RuntimeException("First shift+tab failed"); + } + // Second SHIFT+TAB + robot.keyPress(KeyEvent.VK_SHIFT); + robot.keyPress(KeyEvent.VK_TAB); + robot.delay(100); + robot.keyRelease(KeyEvent.VK_TAB); + robot.keyRelease(KeyEvent.VK_SHIFT); + if (!btn2.hasFocus()) { + new RuntimeException("Second shift+tab failed"); + } + + } catch (Exception e) { + e.printStackTrace(); + } finally { + EventQueue.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + } + } + + private static Frame createUI() { + f = new Frame("TAB Focus Change on LW Test"); + f.setLayout(new FlowLayout()); + btn1 = new LightweightButton(); + f.add(btn1); + btn2 = new Button("Click Me To start"); + f.add(btn2); + f.pack(); + f.setVisible(true); + return f; + } +} + +class LightweightButton extends Component implements FocusListener { + boolean focus; + LightweightButton() { + focus = false; + addFocusListener(this); + } + + public Dimension getPreferredSize() + { + return new Dimension(100, 100); + } + + public void focusGained(FocusEvent e) { + focus = true; + repaint(); + } + + public void focusLost(FocusEvent e) { + focus = false; + repaint(); + } + + public void paint(Graphics g) { + if (focus) { + g.drawString("Has Focus", 10, 20); + } else { + g.drawString("Not Focused", 10, 20); + } + } + + public boolean isFocusable() { + return true; + } +} diff --git a/test/jdk/java/awt/LightweightComponent/LightweightFontTest/LightweightFontTest.java b/test/jdk/java/awt/LightweightComponent/LightweightFontTest/LightweightFontTest.java new file mode 100644 index 0000000000000..4fd90656d6124 --- /dev/null +++ b/test/jdk/java/awt/LightweightComponent/LightweightFontTest/LightweightFontTest.java @@ -0,0 +1,182 @@ +/* + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4077709 4153989 + * @summary Lightweight component font settable test + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual LightweightFontTest + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Frame; +import java.awt.Graphics; + + +public class LightweightFontTest { + static Font desiredFont = null; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + [ There are 7 steps to this test ] + 1. The 2 bordered labels (Emacs vs. vi) should be in a LARGE font + (approximately 1/2 inch tall) + 2. The labels should not overlap. + 3. Each button should be large enough to contain the entire label. + 4. The labels should have red backgrounds + 5. The text in the left label should be blue and the right yellow + 6. Resize the window to make it much smaller and larger + 7. The buttons should never overlap, and they should be large + enough to contain the entire label. + (although the button may disappear if there is not enough + room in the window)" + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(LightweightFontTest::createUI) + .logArea(5) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame f = new Frame("Lightweight Font Test"); + f.setLayout(new FlowLayout()); + + desiredFont = new Font(Font.DIALOG, Font.PLAIN, 36); + Component component; + component = new BorderedLabel("Emacs or vi?"); + component.setFont(desiredFont); + component.setBackground(Color.red); + component.setForeground(Color.blue); + f.add(component); + component = new BorderedLabel("Vi or Emacs???"); + component.setFont(desiredFont); + component.setBackground(Color.red); + component.setForeground(Color.yellow); + f.add(component); + f.pack(); + return f; + } +} + +/** + * Lightweight component + */ +class BorderedLabel extends Component { + boolean superIsButton; + String labelString; + + BorderedLabel(String labelString) { + this.labelString = labelString; + + Component thisComponent = this; + superIsButton = (thisComponent instanceof Button); + if(superIsButton) { + ((Button)thisComponent).setLabel(labelString); + } + } + + public Dimension getMinimumSize() { + Dimension minSize = new Dimension(); + + if (superIsButton) { + minSize = super.getMinimumSize(); + } else { + + Graphics g = getGraphics(); + verifyFont(g); + FontMetrics metrics = g.getFontMetrics(); + + minSize.width = metrics.stringWidth(labelString) + 14; + minSize.height = metrics.getMaxAscent() + metrics.getMaxDescent() + 9; + + g.dispose(); + } + return minSize; + } + + public Dimension getPreferredSize() { + Dimension prefSize = new Dimension(); + if (superIsButton) { + prefSize = super.getPreferredSize(); + } else { + prefSize = getMinimumSize(); + } + return prefSize; + } + + public void paint(Graphics g) { + verifyFont(g); + super.paint(g); + if (superIsButton) { + return; + } + Dimension size = getSize(); + Color oldColor = g.getColor(); + + // draw border + g.setColor(getBackground()); + g.fill3DRect(0, 0, size.width, size.height, false); + g.fill3DRect(3, 3, size.width - 6, size.height - 6, true); + + // draw text + FontMetrics metrics = g.getFontMetrics(); + int centerX = size.width / 2; + int centerY = size.height / 2; + int textX = centerX - (metrics.stringWidth(labelString) / 2); + int textY = centerY + ((metrics.getMaxAscent() + + metrics.getMaxDescent()) / 2); + g.setColor(getForeground()); + g.drawString(labelString, textX, textY); + + g.setColor(oldColor); + } + + /** + * Verifies that the font is correct and prints a warning + * message and/or throws a RuntimeException if it is not. + */ + private void verifyFont(Graphics g) { + Font desiredFont = LightweightFontTest.desiredFont; + Font actualFont = g.getFont(); + if (!actualFont.equals(desiredFont)) { + PassFailJFrame.log("AWT BUG: FONT INFORMATION LOST!"); + PassFailJFrame.log(" Desired font: " + desiredFont); + PassFailJFrame.log(" Actual font: " + actualFont); + PassFailJFrame.forceFail(); + } + } +} diff --git a/test/jdk/java/awt/LightweightComponent/MultipleAddNotifyTest/MultipleAddNotifyTest.java b/test/jdk/java/awt/LightweightComponent/MultipleAddNotifyTest/MultipleAddNotifyTest.java new file mode 100644 index 0000000000000..fbbc2ae61854a --- /dev/null +++ b/test/jdk/java/awt/LightweightComponent/MultipleAddNotifyTest/MultipleAddNotifyTest.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4058400 + * @summary Tests that calling addNotify on a lightweight component more than + * once does not break event dispatching for that component. + * @key headful + * @run main MultipleAddNotifyTest + */ + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Robot; +import java.awt.event.InputEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +public class MultipleAddNotifyTest { + static volatile boolean passFlag; + static volatile int posX; + static volatile int posY; + static Frame f; + static LightComponent l; + + public static void main(String[] args) throws Exception { + Robot r; + try { + r = new Robot(); + r.setAutoWaitForIdle(true); + passFlag = false; + + EventQueue.invokeAndWait(() -> { + f = new Frame("Multiple addNotify Test"); + l = new LightComponent(); + f.setLayout(new FlowLayout()); + l.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + System.out.println("Mouse Clicked"); + passFlag = true; + } + }); + f.add(l); + f.addNotify(); + f.addNotify(); + + if (!l.isVisible()) { + throw new RuntimeException("Test failed. LW Component " + + "not visible."); + } + f.setSize(200, 200); + f.setLocationRelativeTo(null); + f.setVisible(true); + }); + r.waitForIdle(); + r.delay(1000); + + EventQueue.invokeAndWait(() -> { + posX = f.getX() + l.getWidth() + (l.getWidth() / 2); + posY = f.getY() + l.getHeight(); + }); + + r.mouseMove(posX, posY); + r.delay(500); + + r.mousePress(InputEvent.BUTTON1_DOWN_MASK); + r.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + r.delay(500); + + if (!passFlag) { + throw new RuntimeException("Test failed. MouseClicked event " + + "not working properly."); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + } + } +} + +class LightComponent extends Component { + public void paint(Graphics g) { + setSize(100, 100); + Dimension d = getSize(); + g.setColor(Color.red); + g.fillRect(0, 0, d.width, d.height); + } +} diff --git a/test/jdk/java/awt/LightweightComponent/PopupTest/PopupTest.java b/test/jdk/java/awt/LightweightComponent/PopupTest/PopupTest.java new file mode 100644 index 0000000000000..beae4b4d9042e --- /dev/null +++ b/test/jdk/java/awt/LightweightComponent/PopupTest/PopupTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4476083 + * @summary Disabled components do not receive MouseEvent in Popups + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual PopupTest + */ + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Frame; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; + +public class PopupTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + PopupMenus should disappear when a disabled component is + clicked. + + Step 1. Pop down the popup menu by clicking on it. + Step 2. Click on the disabled component to make the menu + disappear. + + If the menu disappears when the disabled component is clicked, + the test passes, otherwise, the test fails. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(PopupTest::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame f = new Frame("Disabled Component in Popup Test"); + f.setLayout(new BorderLayout()); + + JButton b = new JButton("step 1: press me to display menu"); + b.addActionListener(e -> { + JPopupMenu m = new JPopupMenu(); + m.add(new JMenuItem("item 1")); + m.add(new JMenuItem("item 2")); + m.add(new JMenuItem("item 3")); + m.add(new JMenuItem("item 4")); + m.add(new JMenuItem("item 5")); + m.add(new JMenuItem("item 6")); + m.show((Component) e.getSource(), 0, 10); + }); + + JLabel disabled = new JLabel("step 2: click me. the menu should be " + + "dismissed"); + disabled.setEnabled(false); + + JLabel enabled = new JLabel("step 3: there is no step 3"); + + f.add(BorderLayout.NORTH, b); + f.add(BorderLayout.CENTER, disabled); + f.add(BorderLayout.SOUTH, enabled); + f.setSize(300, 200); + return f; + } +} diff --git a/test/jdk/java/awt/List/ActionEventWhenHitEnterTest.java b/test/jdk/java/awt/List/ActionEventWhenHitEnterTest.java new file mode 100644 index 0000000000000..232189ef53bbb --- /dev/null +++ b/test/jdk/java/awt/List/ActionEventWhenHitEnterTest.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4234245 + * @summary the actionEvent is not invoked when hit enter on list. + * @key headful + * @run main ActionEventWhenHitEnterTest + */ + +import java.awt.BorderLayout; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.IllegalComponentStateException; +import java.awt.List; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.KeyEvent; + +public class ActionEventWhenHitEnterTest + implements ActionListener, ItemListener { + + volatile boolean passed1; + volatile boolean passed2; + volatile Point pt; + List list; + Frame frame; + + public static void main(final String[] args) throws Exception { + ActionEventWhenHitEnterTest app = new ActionEventWhenHitEnterTest(); + app.doTest(); + } + + public ActionEventWhenHitEnterTest() { + list = new List(7); + for (int i = 0; i < 10; i++) { + list.add("Item " + i); + } + list.addItemListener(this); + list.addActionListener(this); + } + + public void actionPerformed(ActionEvent ae) { + passed1 = true; + System.out.println("--> Action event invoked: " + ae.getSource()); + } + + public void itemStateChanged(ItemEvent ie) { + passed2 = true; + System.out.println("--> Item state changed:" + ie.getSource()); + } + + public void doTest() throws Exception { + EventQueue.invokeAndWait(() -> { + frame = new Frame("ActionEventWhenHitEnterTest"); + frame.add(list); + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + }); + + try { + Robot robot = new Robot(); + robot.setAutoDelay(100); + robot.waitForIdle(); + robot.delay(1000); + + EventQueue.invokeAndWait(() -> { + pt = list.getLocationOnScreen(); + }); + + if (pt.x != 0 || pt.y != 0) { + robot.mouseMove(pt.x + list.getWidth() / 2, + pt.y + list.getHeight() / 2); + robot.waitForIdle(); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + + robot.keyPress(KeyEvent.VK_ENTER); + robot.keyRelease(KeyEvent.VK_ENTER); + } + + robot.waitForIdle(); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + + if (!passed1 || !passed2) { + throw new RuntimeException("ActionEventWhenHitEnterTest FAILED"); + } + System.out.println("Test PASSED"); + + } + +} diff --git a/test/jdk/java/awt/List/DisabledListIsGreyTest.java b/test/jdk/java/awt/List/DisabledListIsGreyTest.java new file mode 100644 index 0000000000000..ec1a257066606 --- /dev/null +++ b/test/jdk/java/awt/List/DisabledListIsGreyTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2006, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6354810 + * @summary Items in the list are not grayed out when disabled, XToolkit + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DisabledListIsGreyTest +*/ + +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.List; + +public class DisabledListIsGreyTest { + + private static final String INSTRUCTIONS = """ + 1) After the test started you will see two lists. + 2) One of them is enabled, and the second is disabled. + 3) Check that the items of the disabled list are grayed. + 4) If so, the test passed. Otherwise, failed."""; + + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("DisabledListIsGreyTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(DisabledListIsGreyTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + Frame frame = new Frame("DisabledListIsGreyTest Frame"); + frame.setLayout(new FlowLayout()); + + List list1 = new List(3); + List list2 = new List(3); + for (int i = 0; i < 5; i++) { + list1.addItem("Item " + i); + list2.addItem("Item " + i); + } + frame.add(list1); + + list2.setEnabled(false); + frame.add(list2); + frame.pack(); + return frame; + } + +} diff --git a/test/jdk/java/awt/List/HorizScrollWorkTest.java b/test/jdk/java/awt/List/HorizScrollWorkTest.java new file mode 100644 index 0000000000000..6de1de1a4f264 --- /dev/null +++ b/test/jdk/java/awt/List/HorizScrollWorkTest.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2006, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6355467 + * @summary Horizontal scroll bar thumb of a List does not stay at the end + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @requires (os.family == "linux") + * @run main/manual HorizScrollWorkTest +*/ + +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.List; + +public class HorizScrollWorkTest { + + private static final String INSTRUCTIONS = """ + This is a linux only test. + Drag and drop the horizontal scroll bar thumb at the right end. + If the thumb does not stay at the right end, then the test failed. Otherwise passed."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("HorizScrollWorkTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int)INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(HorizScrollWorkTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + Frame frame = new Frame("HorizScrollWorkTest Frame"); + List list = new List(4); + + frame.setLayout (new FlowLayout()); + + list.add("veryyyyyyyyyyyyyyyyyyyyyyyyyy longgggggggggggggggggggggg stringggggggggggggggggggggg"); + + frame.add(list); + frame.pack(); + + return frame; + } +} diff --git a/test/jdk/java/awt/List/HorizScrollbarEraseTest.java b/test/jdk/java/awt/List/HorizScrollbarEraseTest.java new file mode 100644 index 0000000000000..2601a7ed0b2e6 --- /dev/null +++ b/test/jdk/java/awt/List/HorizScrollbarEraseTest.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4895367 + * @summary List scrolling w/ down arrow keys obscures horizontal scrollbar + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @requires (os.family == "linux") + * @run main/manual HorizScrollbarEraseTest + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.List; +import java.awt.Panel; +import java.awt.TextArea; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class HorizScrollbarEraseTest { + + private static final String INSTRUCTIONS = """ + This is a Unix-only test. + Do the four mini-tests below. + If the horizontal scrollbar is ever erased by a rectangle + of the background color, the test FAILS. + If the horizontal scrollbars remain painted, test passes."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("HorizScrollbarEraseTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(HorizScrollbarEraseTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + Frame frame = new Frame("HorizScrollbarEraseTest"); + Panel borderPanel = new Panel(); + borderPanel.setLayout(new BorderLayout()); + Button focusedButton = new Button("Focus starts here"); + borderPanel.add(focusedButton, BorderLayout.NORTH); + + Panel gridPanel = new Panel(); + gridPanel.setLayout(new GridLayout(0, 4)); + borderPanel.add(gridPanel, BorderLayout.CENTER); + + InstructionList il1 = new InstructionList("Tab to Item 2, then \n" + + "press the down" + + "arrow key to scroll down"); + il1.list.select(2); + il1.list.makeVisible(0); + gridPanel.add(il1); + + InstructionList il2 = new InstructionList("Tab to the next List,\n" + + "then press the down\n" + + "arrow key to select\n" + + "the last item."); + il2.list.select(3); + il2.list.makeVisible(0); + gridPanel.add(il2); + + InstructionList il3 = new InstructionList("Click the button to\n" + + "programmatically\n" + + "select item 3 (not showing)"); + Button selectBtn = new Button("Click Me"); + final List selectList = il3.list; + selectBtn.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + selectList.select(3); + } + }); + il3.add(selectBtn, BorderLayout.CENTER); + gridPanel.add(il3); + + InstructionList il4 = new InstructionList("Click the button to\nprogrammatically\ndeselect item 3\n(not showing)"); + Button deselectBtn = new Button("Click Me"); + final List deselectList = il4.list; + deselectBtn.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + deselectList.deselect(3); + } + }); + il4.add(deselectBtn, BorderLayout.CENTER); + il4.list.select(3); + il4.list.makeVisible(0); + gridPanel.add(il4); + + frame.add(borderPanel); + frame.pack(); + return frame; + + } +} + +class InstructionList extends Panel { + TextArea ta; + public List list; + + public InstructionList(String instructions) { + super(); + setLayout(new BorderLayout()); + ta = new TextArea(instructions, 6, 25, TextArea.SCROLLBARS_NONE); + ta.setFocusable(false); + list = new List(); + for (int i = 0; i < 5; i++) { + list.add("Item " + i + ", a long, long, long, long item"); + } + add(ta, BorderLayout.NORTH); + add(list, BorderLayout.SOUTH); + } +} diff --git a/test/jdk/java/awt/List/ItemEventTest/ItemEventTest.java b/test/jdk/java/awt/List/ItemEventTest/ItemEventTest.java index c5d4537990563..ea0f339c92b62 100644 --- a/test/jdk/java/awt/List/ItemEventTest/ItemEventTest.java +++ b/test/jdk/java/awt/List/ItemEventTest/ItemEventTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,8 +27,13 @@ * @bug 8033936 8172510 * @summary Verify that correct ItemEvent is received while selection & * deselection of multi select List items. + * @library /test/lib + * @build jdk.test.lib.Platform + * @run main ItemEventTest */ +// Pass -save to the test to enable screenshots at each select/deselect + import java.awt.AWTException; import java.awt.Event; import java.awt.FlowLayout; @@ -37,26 +42,30 @@ import java.awt.Point; import java.awt.Rectangle; import java.awt.Robot; -import java.awt.event.KeyEvent; import java.awt.event.InputEvent; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; - -public class ItemEventTest extends Frame -{ - List list; - final String expectedSelectionOrder; - StringBuilder actualSelectionOrder; - Robot robot; - - public ItemEventTest() - { - try { - robot = new Robot(); - } catch(AWTException e) { - throw new RuntimeException(e.getMessage()); - } - expectedSelectionOrder = "01230123"; +import java.awt.event.KeyEvent; +import java.awt.image.RenderedImage; +import java.io.File; +import java.io.IOException; + +import javax.imageio.ImageIO; + +import jdk.test.lib.Platform; + +public final class ItemEventTest extends Frame { + private static final String expectedSelectionOrder = "01230123"; + + private static boolean saveScreenshots; + + private final StringBuffer actualSelectionOrder + = new StringBuffer(expectedSelectionOrder.length()); + + private final List list; + private final Robot robot; + + private ItemEventTest() throws AWTException { + robot = new Robot(); + robot.setAutoWaitForIdle(true); list = new List(4, true); list.add("0"); @@ -65,71 +74,76 @@ public ItemEventTest() list.add("3"); add(list); + setSize(400,400); setLayout(new FlowLayout()); pack(); + setLocationRelativeTo(null); setVisible(true); robot.waitForIdle(); } @Override + @SuppressWarnings("deprecation") public boolean handleEvent(Event e) { - if (e.target instanceof List) { - if (e.id == Event.LIST_DESELECT || e.id == Event.LIST_SELECT) { - actualSelectionOrder.append(e.arg); - } + if ((e.target instanceof List) + && (e.id == Event.LIST_DESELECT + || e.id == Event.LIST_SELECT)) { + logEvent("handleEvent: ", e.arg); } return true; } - void testHandleEvent() { + private void logEvent(String method, Object listItem) { + actualSelectionOrder.append(listItem); + System.out.println(method + listItem); + } + + private void testHandleEvent() { // When no ItemListener is added to List, parent's handleEvent is // called with ItemEvent. performTest(); } - void testItemListener() { - list.addItemListener(new ItemListener() { - @Override - public void itemStateChanged(ItemEvent ie) { - actualSelectionOrder.append(ie.getItem()); - } - }); + private void testItemListener() { + list.addItemListener(ie + -> logEvent("testItemListener: ", ie.getItem())); performTest(); } - void performTest() { - actualSelectionOrder = new StringBuilder(); - Point loc = list.getLocationOnScreen(); - Rectangle rect = list.getBounds(); - int dY = rect.height / list.getItemCount(); - loc = new Point(loc.x + 10, loc.y + 5); + private void performTest() { + actualSelectionOrder.setLength(0); + + final Rectangle rect = getListBoundsOnScreen(); + final int dY = rect.height / list.getItemCount(); + final Point loc = new Point(rect.x + rect.width / 2, + rect.y + dY / 2); - String osName = System.getProperty("os.name"); - boolean isMac = osName.contains("Mac") || osName.contains("mac"); - if(isMac) { + if (Platform.isOSX()) { robot.keyPress(KeyEvent.VK_META); - robot.waitForIdle(); } // First loop to select & Second loop to deselect the list items. for (int j = 0; j < 2; ++j) { for (int i = 0; i < list.getItemCount(); ++i) { robot.mouseMove(loc.x, loc.y + i * dY); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); robot.waitForIdle(); - robot.mousePress(InputEvent.BUTTON1_MASK); - robot.waitForIdle(); - robot.mouseRelease(InputEvent.BUTTON1_MASK); - robot.waitForIdle(); + + if (saveScreenshots) { + saveImage(robot.createScreenCapture(rect)); + } } } - if(isMac) { + if (Platform.isOSX()) { robot.keyRelease(KeyEvent.VK_META); } - if (!expectedSelectionOrder.equals(actualSelectionOrder.toString())) { - dispose(); + if (!expectedSelectionOrder.contentEquals(actualSelectionOrder)) { + saveImage(robot.createScreenCapture(rect)); + throw new RuntimeException("ItemEvent for selection & deselection" + " of multi select List's item is not correct" + " Expected : " + expectedSelectionOrder @@ -137,10 +151,32 @@ void performTest() { } } - public static void main(String args[]) { - ItemEventTest test = new ItemEventTest(); - test.testHandleEvent(); - test.testItemListener(); - test.dispose(); + private Rectangle getListBoundsOnScreen() { + return new Rectangle(list.getLocationOnScreen(), + list.getSize()); + } + + private static int imageNo = 0; + + private static void saveImage(RenderedImage image) { + try { + ImageIO.write(image, + "png", + new File(String.format("image-%02d.png", + ++imageNo))); + } catch (IOException ignored) { + } + } + + public static void main(String[] args) throws AWTException { + saveScreenshots = args.length > 0 && "-save".equals(args[0]); + + ItemEventTest test = new ItemEventTest(); + try { + test.testHandleEvent(); + test.testItemListener(); + } finally { + test.dispose(); + } } } diff --git a/test/jdk/java/awt/List/KeyEventsTest/KeyEventsTest.java b/test/jdk/java/awt/List/KeyEventsTest/KeyEventsTest.java index 919ea72a49490..b4c3e5e4f05cf 100644 --- a/test/jdk/java/awt/List/KeyEventsTest/KeyEventsTest.java +++ b/test/jdk/java/awt/List/KeyEventsTest/KeyEventsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,9 +22,8 @@ */ import java.awt.BorderLayout; -import java.awt.EventQueue; -import java.awt.KeyboardFocusManager; import java.awt.Frame; +import java.awt.KeyboardFocusManager; import java.awt.List; import java.awt.Panel; import java.awt.Point; @@ -51,7 +50,7 @@ * @run main KeyEventsTest */ public class KeyEventsTest { - TestState currentState; + private volatile TestState currentState; final Object LOCK = new Object(); final int ACTION_TIMEOUT = 500; @@ -66,16 +65,14 @@ public static void main(final String[] args) throws Exception { r = new Robot(); KeyEventsTest app = new KeyEventsTest(); try { - EventQueue.invokeAndWait(app::initAndShowGui); + app.initAndShowGui(); r.waitForIdle(); r.delay(500); app.doTest(); } finally { - EventQueue.invokeAndWait(() -> { - if (app.keyFrame != null) { - app.keyFrame.dispose(); - } - }); + if (app.keyFrame != null) { + app.keyFrame.dispose(); + } } } @@ -184,52 +181,51 @@ private void test(TestState currentState) throws Exception { throw new RuntimeException("Test failed - list isn't focus owner."); } - EventQueue.invokeAndWait(() -> { - list.deselect(0); - list.deselect(1); - list.deselect(2); - list.deselect(3); - list.deselect(4); - list.deselect(5); - list.deselect(6); - list.deselect(7); - list.deselect(8); - - int selectIndex = 0; - int visibleIndex = 0; - - if (currentState.getScrollMoved()) { - if (currentState.getKeyID() == KeyEvent.VK_PAGE_UP || - currentState.getKeyID() == KeyEvent.VK_HOME) { - selectIndex = 8; - visibleIndex = 8; - } else if (currentState.getKeyID() == KeyEvent.VK_PAGE_DOWN || - currentState.getKeyID() == KeyEvent.VK_END) { + list.deselect(0); + list.deselect(1); + list.deselect(2); + list.deselect(3); + list.deselect(4); + list.deselect(5); + list.deselect(6); + list.deselect(7); + list.deselect(8); + + int selectIndex = 0; + int visibleIndex = 0; + + if (currentState.getScrollMoved()) { + if (currentState.getKeyID() == KeyEvent.VK_PAGE_UP || + currentState.getKeyID() == KeyEvent.VK_HOME) { + selectIndex = 8; + visibleIndex = 8; + } else if (currentState.getKeyID() == KeyEvent.VK_PAGE_DOWN || + currentState.getKeyID() == KeyEvent.VK_END) { + selectIndex = 0; + visibleIndex = 0; + } + } else { + if (currentState.getKeyID() == KeyEvent.VK_PAGE_UP || + currentState.getKeyID() == KeyEvent.VK_HOME) { + if (currentState.getSelectedMoved()) { + selectIndex = 1; + } else { selectIndex = 0; - visibleIndex = 0; } - } else { - if (currentState.getKeyID() == KeyEvent.VK_PAGE_UP || - currentState.getKeyID() == KeyEvent.VK_HOME) { - if (currentState.getSelectedMoved()) { - selectIndex = 1; - } else { - selectIndex = 0; - } - visibleIndex = 0; - } else if (currentState.getKeyID() == KeyEvent.VK_PAGE_DOWN || - currentState.getKeyID() == KeyEvent.VK_END) { - if (currentState.getSelectedMoved()) { - selectIndex = 7; - } else { - selectIndex = 8; - } - visibleIndex = 8; + visibleIndex = 0; + } else if (currentState.getKeyID() == KeyEvent.VK_PAGE_DOWN || + currentState.getKeyID() == KeyEvent.VK_END) { + if (currentState.getSelectedMoved()) { + selectIndex = 7; + } else { + selectIndex = 8; } + visibleIndex = 8; } - list.select(selectIndex); - list.makeVisible(visibleIndex); - }); + } + list.select(selectIndex); + list.makeVisible(visibleIndex); + r.delay(10); r.waitForIdle(); @@ -309,7 +305,7 @@ class TestState { private final boolean scrollMoved; private final int keyID; private final boolean template; - private boolean action; + private volatile boolean action; public TestState(boolean multiple, boolean selectedMoved, boolean scrollMoved, int keyID, boolean template){ this.multiple = multiple; diff --git a/test/jdk/java/awt/List/ListAddPerfTest.java b/test/jdk/java/awt/List/ListAddPerfTest.java new file mode 100644 index 0000000000000..7ff3eaf882c62 --- /dev/null +++ b/test/jdk/java/awt/List/ListAddPerfTest.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4117288 + * @summary JDKversion1.2beta3-J List's add() method is much slower. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ListAddPerfTest + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Frame; +import java.awt.List; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class ListAddPerfTest { + + static Button button; + static List list; + + private static final String INSTRUCTIONS = """ + It is used to check the performance of List add operation. + + Instructions: + Click on the Remove All button. + The list should be cleared. + The button is now named "Add Items". + Click on the "Add Items" button. + 800 items should be added to the list. + Notice not only how fast or slow this is, but also how + 'smooth' it goes as well i.e. without any flashing. + + Press pass if the list performance is acceptable."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("ListAddPerfTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int)INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(ListAddPerfTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + Frame frame = new Frame("ListAddPerfTest"); + frame.setLayout(new BorderLayout()); + + button = new Button("Remove All"); + button.addActionListener((ActionEvent e) -> { + if (list.getItemCount() > 0) { + list.removeAll(); + list.invalidate(); + button.setLabel("Add Items"); + } else { + for (int i = 0; i < 800; i ++) { + list.add("My number is " + i); + } + button.setLabel("Remove All"); + } + }); + + list = new List(15); + for (int i = 0; i < 800; i ++) { + list.add("My number is " + i); + } + + frame.add("North", button); + frame.add("South", list); + + frame.pack(); + return frame; + } +} diff --git a/test/jdk/java/awt/List/ListEnterExitTest.java b/test/jdk/java/awt/List/ListEnterExitTest.java index 998b152a634d3..201a0e43c135d 100644 --- a/test/jdk/java/awt/List/ListEnterExitTest.java +++ b/test/jdk/java/awt/List/ListEnterExitTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,16 +36,24 @@ import java.awt.Point; import java.awt.Robot; -import java.awt.event.InputEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + public class ListEnterExitTest { final List list = new List(); - final MouseEnterExitListener mouseEnterExitListener = new MouseEnterExitListener(); Frame frame; volatile Point p; + private static final int X_OFFSET = 30; + private static final int Y_OFFSET = 40; + private static final int LATCH_TIMEOUT = 3; + + private final CountDownLatch mouseEnterLatch = new CountDownLatch(1); + private final CountDownLatch mouseExitLatch = new CountDownLatch(1); + public static void main(String[] args) throws Exception { ListEnterExitTest test = new ListEnterExitTest(); test.start(); @@ -57,7 +65,11 @@ public void start() throws Exception { frame = new Frame("ListEnterExitTest"); list.add("Item 1"); list.add("Item 2"); - list.addMouseListener(mouseEnterExitListener); + list.add("Item 3"); + list.add("Item 4"); + list.add("Item 5"); + list.add("Item 6"); + list.addMouseListener(new MouseEnterExitListener()); frame.add(list); frame.setLayout(new FlowLayout()); frame.setSize(300, 200); @@ -66,25 +78,28 @@ public void start() throws Exception { }); final Robot robot = new Robot(); - robot.delay(1000); + robot.setAutoDelay(100); robot.waitForIdle(); + robot.delay(1000); EventQueue.invokeAndWait(() -> { p = list.getLocationOnScreen(); }); - robot.mouseMove(p.x + 10, p.y + 10); - robot.delay(100); + robot.mouseMove(p.x + X_OFFSET, p.y + Y_OFFSET); robot.waitForIdle(); - robot.mouseMove(p.x - 10, p.y - 10); - robot.delay(100); + + robot.mouseMove(p.x - X_OFFSET, p.y + Y_OFFSET); robot.waitForIdle(); - robot.mouseMove(p.x + 10, p.y + 10); - robot.mousePress(InputEvent.BUTTON1_MASK); - robot.mouseRelease(InputEvent.BUTTON1_MASK); + robot.mouseMove(p.x + X_OFFSET, p.y + Y_OFFSET); + robot.waitForIdle(); + + if (!mouseEnterLatch.await(LATCH_TIMEOUT, TimeUnit.SECONDS)) { + throw new RuntimeException("Mouse enter event timeout"); + } - synchronized (mouseEnterExitListener) { - mouseEnterExitListener.wait(2000); + if (!mouseExitLatch.await(LATCH_TIMEOUT, TimeUnit.SECONDS)) { + throw new RuntimeException("Mouse exit event timeout"); } } finally { EventQueue.invokeAndWait(() -> { @@ -93,35 +108,19 @@ public void start() throws Exception { } }); } - if (!mouseEnterExitListener.isPassed()) { - throw new RuntimeException("Haven't receive mouse enter/exit events"); - } - } -} - -class MouseEnterExitListener extends MouseAdapter { - - volatile boolean passed_1 = false; - volatile boolean passed_2 = false; - - public void mouseEntered(MouseEvent e) { - passed_1 = true; - } - - public void mouseExited(MouseEvent e) { - passed_2 = true; - } - - public void mousePressed(MouseEvent e) { - synchronized (this) { - System.out.println("mouse pressed"); - this.notifyAll(); + private class MouseEnterExitListener extends MouseAdapter { + @Override + public void mouseEntered(MouseEvent e) { + System.out.println("Mouse Entered Event"); + mouseEnterLatch.countDown(); } - } - public boolean isPassed() { - return passed_1 & passed_2; + @Override + public void mouseExited(MouseEvent e) { + System.out.println("Mouse Exited Event"); + mouseExitLatch.countDown(); + } } } diff --git a/test/jdk/java/awt/List/ListFrameResizeTest.java b/test/jdk/java/awt/List/ListFrameResizeTest.java new file mode 100644 index 0000000000000..4655e817da581 --- /dev/null +++ b/test/jdk/java/awt/List/ListFrameResizeTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4085379 + * @summary List component not properly "resized" with GridBagLayout + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ListFrameResizeTest + */ + +import java.awt.Color; +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.List; + +public class ListFrameResizeTest { + + private static final String INSTRUCTIONS = """ + This test is for windows only. + + 1. A Frame will appear with a List + (the List occupies the whole Frame) + 2. Minimize the Frame, the Frame is now in the Task Bar (ie.,iconified) + 3. Right click (right mouse button) the icon in the task bar + and click on the 'maximize' menuitem to maximize the Frame + 4. If you notice the List has not been resized + (ie.,if it partly occupies the Frame), then press FAIL else press PASS"."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("ListFrameResizeTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(ListFrameResizeTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + wintest client = new wintest("ListFrameResizeTest Frame"); + client.resize(500, 300); + client.setBackground(Color.blue); + return client; + } + +} + +class wintest extends Frame { + private List msg; + + public wintest(String title) { + super(title); + msg = new List(); + for (int i = 0; i < 100; i++) { + msg.add("" + i); + } + + GridBagLayout gridbag = new GridBagLayout(); + GridBagConstraints constraints = new GridBagConstraints(); + + setLayout(gridbag); + + constraints.fill = GridBagConstraints.BOTH; + + constraints.anchor = GridBagConstraints.CENTER; + constraints.insets = new Insets(10, 10, 10, 10); + constraints.ipadx = 0; + constraints.ipady = 0; + constraints.weightx = 1; + constraints.weighty = 1; + constraints.gridx = 0; + constraints.gridy = 0; + constraints.gridwidth = GridBagConstraints.REMAINDER; + constraints.gridheight = GridBagConstraints.REMAINDER; + gridbag.setConstraints(msg, constraints); + add(msg); + } +} diff --git a/test/jdk/java/awt/List/ListScrollbarCursorTest.java b/test/jdk/java/awt/List/ListScrollbarCursorTest.java new file mode 100644 index 0000000000000..fc832029b134a --- /dev/null +++ b/test/jdk/java/awt/List/ListScrollbarCursorTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4290684 + * @summary Tests that cursor on the scrollbar of the list is set to default. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ListScrollbarCursorTest + */ + +import java.awt.Cursor; +import java.awt.Frame; +import java.awt.List; +import java.awt.Panel; + +public class ListScrollbarCursorTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. You see the list in the middle of the panel. + This list has two scrollbars. + 2. The cursor should have a shape of hand over the main area + and a shape of arrow over scrollbars. + 3. Move the mouse cursor to either horizontal or vertical scrollbar. + 4. Press PASS if you see the default arrow cursor else press FAIL. + """; + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(ListScrollbarCursorTest::initialize) + .build() + .awaitAndCheck(); + } + + static Frame initialize() { + Frame frame = new Frame("List Scrollbar Cursor Test"); + Panel panel = new Panel(); + List list = new List(3); + list.add("List item with a very long name" + + "(just to make the horizontal scrollbar visible)"); + list.add("Item 2"); + list.add("Item 3"); + list.setCursor(new Cursor(Cursor.HAND_CURSOR)); + panel.add(list); + frame.add(panel); + frame.setSize(200, 200); + return frame; + } +} diff --git a/test/jdk/java/awt/List/ListScrollbarTest.java b/test/jdk/java/awt/List/ListScrollbarTest.java new file mode 100644 index 0000000000000..b676303b927ab --- /dev/null +++ b/test/jdk/java/awt/List/ListScrollbarTest.java @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4096445 + * @summary Test to verify List Scollbar appears/disappears automatically + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ListScrollbarTest + */ + +import java.awt.Button; +import java.awt.Component; +import java.awt.Event; +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.List; + +public class ListScrollbarTest extends Frame { + static final int ITEMS = 10; + List ltList; + List rtList; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. There are two lists added to the Frame separated by + a column of buttons + 2. Double click on any item(s) on the left list, you would see + a '*' added at the end of the item + 3. Keep double clicking on the same item till the length of the + item exceeds the width of the list + 4. Now, if you don't get the horizontal scrollbar on + the left list click FAIL. + 5. If you get horizontal scrollbar, select the item + (that you double clicked) and press the '>' button + to move the item to the right list. + 6. If horizontal scroll bar appears on the right list + as well as disappears from the left list [only if both + happen] proceed with step 8 else click FAIL + 7. Now move the same item to the left list, by pressing + '<' button + 8. If the horizontal scrollbar appears on the left list + and disappears from the right list[only if both happen] + click PASS else click FAIL. + """; + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(ListScrollbarTest::new) + .build() + .awaitAndCheck(); + } + + public ListScrollbarTest() { + super("List scroll bar test"); + GridBagLayout gbl = new GridBagLayout(); + ltList = new List(ITEMS, true); + rtList = new List(0, true); + setLayout(gbl); + add(ltList, 0, 0, 1, 5, 1.0, 1.0); + add(rtList, 2, 0, 1, 5, 1.0, 1.0); + add(new Button(">"), 1, 0, 1, 1, 0, 1.0); + add(new Button(">>"), 1, 1, 1, 1, 0, 1.0); + add(new Button("<"), 1, 2, 1, 1, 0, 1.0); + add(new Button("<<"), 1, 3, 1, 1, 0, 1.0); + add(new Button("!"), 1, 4, 1, 1, 0, 1.0); + + for (int i = 0; i < ITEMS; i++) { + ltList.addItem("item " + i); + } + setSize(220, 250); + } + + void add(Component comp, int x, int y, int w, int h, double weightx, double weighty) { + GridBagLayout gbl = (GridBagLayout) getLayout(); + GridBagConstraints c = new GridBagConstraints(); + c.fill = GridBagConstraints.BOTH; + c.gridx = x; + c.gridy = y; + c.gridwidth = w; + c.gridheight = h; + c.weightx = weightx; + c.weighty = weighty; + add(comp); + gbl.setConstraints(comp, c); + } + + void reverseSelections(List l) { + for (int i = 0; i < l.countItems(); i++) { + if (l.isSelected(i)) { + l.deselect(i); + } else { + l.select(i); + } + } + } + + void deselectAll(List l) { + for (int i = 0; i < l.countItems(); i++) { + l.deselect(i); + } + } + + void replaceItem(List l, String item) { + for (int i = 0; i < l.countItems(); i++) { + if (l.getItem(i).equals(item)) { + l.replaceItem(item + "*", i); + } + } + } + + void move(List l1, List l2, boolean all) { + + // if all the items are to be moved + if (all) { + for (int i = 0; i < l1.countItems(); i++) { + l2.addItem(l1.getItem(i)); + } + l1.delItems(0, l1.countItems() - 1); + } else { // else move the selected items + String[] items = l1.getSelectedItems(); + int[] itemIndexes = l1.getSelectedIndexes(); + + deselectAll(l2); + for (int i = 0; i < items.length; i++) { + l2.addItem(items[i]); + l2.select(l2.countItems() - 1); + if (i == 0) { + l2.makeVisible(l2.countItems() - 1); + } + } + for (int i = itemIndexes.length - 1; i >= 0; i--) { + l1.delItem(itemIndexes[i]); + } + } + } + + @Override + public boolean action(Event evt, Object arg) { + if (">".equals(arg)) { + move(ltList, rtList, false); + } else if (">>".equals(arg)) { + move(ltList, rtList, true); + } else if ("<".equals(arg)) { + move(rtList, ltList, false); + } else if ("<<".equals(arg)) { + move(rtList, ltList, true); + } else if ("!".equals(arg)) { + if (ltList.getSelectedItems().length > 0) { + reverseSelections(ltList); + } else if (rtList.getSelectedItems().length > 0) { + reverseSelections(rtList); + } + } else if (evt.target == rtList || evt.target == ltList) { + replaceItem((List) evt.target, (String) arg); + } else { + return false; + } + return true; + } + + @Override + public boolean handleEvent(Event evt) { + if (evt.id == Event.LIST_SELECT + || evt.id == Event.LIST_DESELECT) { + if (evt.target == ltList) { + deselectAll(rtList); + } else if (evt.target == rtList) { + deselectAll(ltList); + } + return true; + } + return super.handleEvent(evt); + } +} diff --git a/test/jdk/java/awt/List/MouseDraggedOriginatedByScrollBarTest.java b/test/jdk/java/awt/List/MouseDraggedOriginatedByScrollBarTest.java new file mode 100644 index 0000000000000..6858359d6b40d --- /dev/null +++ b/test/jdk/java/awt/List/MouseDraggedOriginatedByScrollBarTest.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6240151 + * @key headful + * @summary XToolkit: Dragging the List scrollbar initiates DnD + * @requires os.family == "linux" + * @run main MouseDraggedOriginatedByScrollBarTest +*/ + +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.List; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.InputEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; + +public class MouseDraggedOriginatedByScrollBarTest { + private static Frame frame; + private static volatile Point loc; + private static List list; + private static final int XOFFSET = 10; + private static final int YOFFSET = 20; + + public static void main(String[] args) throws Exception { + try { + EventQueue.invokeAndWait(() -> createUI()); + test(); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void createUI() { + frame = new Frame(); + list = new List(4, false); + + list.add("000"); + list.add("111"); + list.add("222"); + list.add("333"); + list.add("444"); + list.add("555"); + list.add("666"); + list.add("777"); + list.add("888"); + list.add("999"); + + frame.add(list); + + list.addMouseMotionListener( + new MouseMotionAdapter(){ + @Override + public void mouseDragged(MouseEvent me){ + System.out.println(me); + throw new RuntimeException("Mouse dragged event detected."); + } + }); + + list.addMouseListener( + new MouseAdapter() { + public void mousePressed(MouseEvent me) { + System.out.println(me); + throw new RuntimeException("Mouse pressed event detected."); + } + + public void mouseReleased(MouseEvent me) { + System.out.println(me); + throw new RuntimeException("Mouse released event detected."); + } + + public void mouseClicked(MouseEvent me){ + System.out.println(me); + throw new RuntimeException("Mouse clicked event detected."); + } + }); + + frame.setLayout(new FlowLayout()); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private static void test() throws Exception { + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(1000); + robot.setAutoWaitForIdle(true); + + EventQueue.invokeAndWait(() -> { + Point p = list.getLocationOnScreen(); + p.translate(list.getWidth() - XOFFSET, YOFFSET); + loc = p; + }); + robot.mouseMove(loc.x, loc.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + for (int i = 0; i < 30; i++) { + robot.mouseMove(loc.x, loc.y + i); + } + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.delay(100); + } +} diff --git a/test/jdk/java/awt/List/MultiSelectionListCrashTest.java b/test/jdk/java/awt/List/MultiSelectionListCrashTest.java new file mode 100644 index 0000000000000..b408a12ebbac7 --- /dev/null +++ b/test/jdk/java/awt/List/MultiSelectionListCrashTest.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4201967 + * @summary tests that a multiselection list doesn't causes crash when FileDialog is invoked + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MultiSelectionListCrashTest + */ + +import java.awt.Button; +import java.awt.FileDialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.List; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class MultiSelectionListCrashTest { + + private static final String INSTRUCTIONS = """ + Press "Invoke dialog" button to invoke a FileDialog. + When it appears close it by pressing cancel button. + If all remaining frames are enabled and + page fault didn't occur the test passed. Otherwise the test failed. + + Try to invoke a FileDialog several times to verify that the bug doesn't exist."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("MultiSelectionListCrashTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(MultiSelectionListCrashTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + + Frame frame = new Frame("MultiSelectionListCrashTest frame"); + Button button = new Button("Invoke dialog"); + button.addActionListener(new FileDialogInvoker(frame)); + List list = new List(4, true); + list.add("Item1"); + list.add("Item2"); + frame.setLayout(new FlowLayout()); + frame.add(button); + frame.add(list); + frame.setSize(200, 200); + return frame; + } +} + +class FileDialogInvoker implements ActionListener { + FileDialog fileDialog; + + public FileDialogInvoker(Frame frame) { + fileDialog = new FileDialog(frame); + } + + public void actionPerformed(ActionEvent e) { + fileDialog.setVisible(true); + } + +} diff --git a/test/jdk/java/awt/List/ScrollbarPositionTest.java b/test/jdk/java/awt/List/ScrollbarPositionTest.java new file mode 100644 index 0000000000000..25167fa56278c --- /dev/null +++ b/test/jdk/java/awt/List/ScrollbarPositionTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4024943 + * @summary Test for position of List scrollbar when it is added + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ScrollbarPositionTest + */ + +import java.awt.Button; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.List; +import java.awt.Panel; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class ScrollbarPositionTest { + static int item = 0; + static List list; + static Button addButton, delButton; + + private static final String INSTRUCTIONS = """ + Click on the "Add List Item" button many times + until the vertical scrollbar appears. + Verify that the displayed vertical scrollbar does not take the space + that was occupied by buttons before the scrollbar is shown."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("ScrollbarPositionTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(ScrollbarPositionTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + Panel pan; + + Frame frame = new Frame("ScrollbarPositionTest Frame"); + frame.setLayout(new GridLayout(1, 2)); + list = new List(); + frame.add(list); + frame.add(pan = new Panel()); + pan.setLayout(new GridLayout(4, 1)); + + MyListener listener = new MyListener(); + addButton = new Button("Add List Item"); + addButton.addActionListener(listener); + pan.add(addButton); + + delButton = new Button("Delete List Item"); + delButton.addActionListener(listener); + pan.add(delButton); + + frame.pack(); + return frame; + } + + static class MyListener implements ActionListener { + public void actionPerformed(ActionEvent evt) { + if (evt.getSource() == addButton) { + String s = "item"; + for (int i = 0; i <= item; i++) { + s = s +" "+Integer.toString(i); + } + item++; + list.addItem(s); + } else if (evt.getSource() == delButton) { + int i; + if ((i = list.countItems()) > 0) { + list.delItem(i - 1); + --item; + } + } + } + } +} diff --git a/test/jdk/java/awt/List/ScrollbarPresenceTest.java b/test/jdk/java/awt/List/ScrollbarPresenceTest.java new file mode 100644 index 0000000000000..d82cbb80060ca --- /dev/null +++ b/test/jdk/java/awt/List/ScrollbarPresenceTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2006, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6336384 + * @summary ScrollBar does not show up correctly + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ScrollbarPresenceTest +*/ + +import java.awt.Font; +import java.awt.Frame; +import java.awt.List; + +public class ScrollbarPresenceTest { + + private static final String INSTRUCTIONS = """ + You will see a list, + If a vertical scrollbar appears on the list and the list is big enough + to show all items then the test failed else the test passed."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("ScrollbarPresenceTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(ScrollbarPresenceTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + Frame frame = new Frame("ScrollbarPresenceTest Frame"); + List list = new List(); + + for (int i = 0; i < 6; i++) { + list.addItem("Row " + i); + } + + list.setFont(new Font("MonoSpaced", Font.PLAIN, 12)); + list.setBounds(30, 30, 128, 104); + frame.add(list); + + frame.pack(); + return frame; + } + +} diff --git a/test/jdk/java/awt/List/SelectedItemVisibilityTest.java b/test/jdk/java/awt/List/SelectedItemVisibilityTest.java new file mode 100644 index 0000000000000..53364e937713c --- /dev/null +++ b/test/jdk/java/awt/List/SelectedItemVisibilityTest.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4676536 + * @summary REGRESSION: makeVisible() method of List Component does not perform + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual SelectedItemVisibilityTest + */ + +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Label; +import java.awt.List; + +public class SelectedItemVisibilityTest { + + static List list1, list2; + static int visibleItem = 4; + static int selectedItems[] = {6, 7, 8}; + static String selectedItemsStr = ""; + + static { + for (int i = 0 ; i < selectedItems.length ; i++) { + selectedItemsStr += ""+selectedItems[i]+" "; + } + } + + private static final String INSTRUCTIONS = + "You should see two lists.\n" + + "\n" + + "list1: \n" + + "\t1. the first visible item should be " + visibleItem + + "\n\t2. the selected item should be " + selectedItems[0] + + "\n" + + "list2:\n" + + "\t1. the first visible item should be " + visibleItem + + "\n\t2. the selected items should be " + selectedItemsStr + + "\n" + + "\nIf it is so the test passed else failed."; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("SelectedItemVisibilityTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(SelectedItemVisibilityTest::createTestUI) + .logArea() + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + + Frame frame = new Frame("SelectedItemVisibilityTest Frame"); + frame.setLayout(new FlowLayout()); + + // list1 + list1 = new List(4); + for (int i = 0; i < 20; i++) { + list1.add(""+i); + } + list1.makeVisible(visibleItem); + list1.select(selectedItems[0]); + frame.add(new Label("list1:")); + frame.add(list1); + + // list2 + list2 = new List(4); + list2.setMultipleMode(true); + for (int i = 0; i < 20; i++) { + list2.add(""+i); + } + list2.makeVisible(visibleItem); + for (int i = 0 ; i < selectedItems.length ; i++) { + list2.select(selectedItems[i]); + } + frame.add(new Label("list2:")); + frame.add(list2); + frame.setSize(200, 200); + + // common output + String s; + int sel[]; + + PassFailJFrame.log("list1: "); + PassFailJFrame.log("\tgetVisibleIndex="+list1.getVisibleIndex()); + sel = list1.getSelectedIndexes(); + s = "\tgetSelectedIndexes="; + for (int i = 0 ; i < sel.length ; i++) { + s += "" + sel[i] + " "; + } + PassFailJFrame.log(s); + + PassFailJFrame.log("list2: "); + PassFailJFrame.log("\tgetVisibleIndex="+list2.getVisibleIndex()); + sel = list2.getSelectedIndexes(); + s = "\tgetSelectedIndexes="; + for (int i = 0 ; i < sel.length ; i++) { + s += "" + sel[i] + " "; + } + PassFailJFrame.log(s); + return frame; + } +} diff --git a/test/jdk/java/awt/List/SetForegroundTest.java b/test/jdk/java/awt/List/SetForegroundTest.java new file mode 100644 index 0000000000000..7b22e5385088b --- /dev/null +++ b/test/jdk/java/awt/List/SetForegroundTest.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6246467 + * @summary Tests that list works correctly if user specified foreground colors on XToolkit/Motif + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual SetForegroundTest + */ + +import java.awt.Button; +import java.awt.Component; +import java.awt.Checkbox; +import java.awt.Choice; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Label; +import java.awt.List; +import java.awt.Panel; +import java.awt.TextArea; +import java.awt.TextField; +import java.awt.ScrollPane; + +public class SetForegroundTest { + + private static final String INSTRUCTIONS = """ + To make sure, that for each component + (Button, Checkbox, Label, List, TextArea, TextField, Choice) + in the frame, + the title exist and the color of the title is red. + If not, the test failed."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("SetForegroundTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(SetForegroundTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + Frame frame = new Frame(); + ScrollPane sp = new ScrollPane() { + public Dimension getPreferredSize() { + return new Dimension(180, 180); + } + }; + Panel p = new Panel(); + Component childs[] = new Component[] {new Button("button"), + new Checkbox("checkbox"), + new Label("label"), + new List(3, false), + new TextArea("text area"), + new TextField("text field"), + new Choice()}; + + p.setLayout (new FlowLayout ()); + + sp.add(p); + + sp.validate(); + + frame.add(sp); + for (int i = 0; i < childs.length; i++){ + childs[i].setForeground(Color.red); + } + + for (int i = 0; i < childs.length; i++) { + p.add(childs[i]); + if (childs[i] instanceof List) { + ((List)childs[i]).add("list1"); + ((List)childs[i]).add("list2"); + } else if (childs[i] instanceof Choice) { + ((Choice)childs[i]).add("choice1"); + ((Choice)childs[i]).add("choice2"); + } + } + frame.pack(); + return frame; + } +} diff --git a/test/jdk/java/awt/Menu/DestroyMenuTest/DestroyMenuTest.java b/test/jdk/java/awt/Menu/DestroyMenuTest/DestroyMenuTest.java new file mode 100644 index 0000000000000..b75ba047dfbe2 --- /dev/null +++ b/test/jdk/java/awt/Menu/DestroyMenuTest/DestroyMenuTest.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4209511 + * @summary Regression test DestroyMenuTest.java Failing + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DestroyMenuTest + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Canvas; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Label; +import java.awt.Panel; +import java.awt.Scrollbar; +import java.awt.TextField; + +public class DestroyMenuTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Create many windows by randomly clicking 'Show Menu Test 1', + 'Show Menu Test 2', 'Show Menu Test 3' buttons. + 2. Ignore the contents of the windows. + Go to the windows created and select menu items inside the menus. + 3. Close the windows by selecting menu item File--> Quit. + 4. Do the above menu item selections as quickly as possible. + If the program crashes when you select File--> Quit, + then the test FAILS. Otherwise the test is PASS. + """; + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(38) + .testUI(DestroyMenuTest::initialize) + .build() + .awaitAndCheck(); + } + + static Frame initialize() { + Frame f = new Frame("Destroy Menu Test"); + Button launchButton = new Button("Show Menu Test 1..."); + Button launchButton2 = new Button("Show Menu Test 2..."); + Button launchButton3 = new Button("Show Menu Test 3..."); + f.setLayout(new FlowLayout()); + f.add(launchButton); + f.add(launchButton2); + f.add(launchButton3); + + launchButton.addActionListener(event -> { + MenuTest frame = new MenuTest("Menu Test 1"); + frame.setBounds(300, 300, 300, 300); + frame.setVisible(true); + }); + + launchButton2.addActionListener(event -> { + MenuTest frame = new MenuTest("Menu Test 2"); + + Button closeButton = new Button("Close"); + + Panel X = new Panel(); + X.setLayout(new BorderLayout()); + + Panel topPanel = new Panel(); + Panel bottomPanel = new Panel(); + + bottomPanel.add(closeButton); + + Scrollbar vScrollbar = new Scrollbar(Scrollbar.VERTICAL); + Scrollbar hScrollbar = new Scrollbar(Scrollbar.HORIZONTAL); + hScrollbar.setValues(hScrollbar.getValue(), 0, 0, 50); + vScrollbar.setValues(vScrollbar.getValue(), 0, 0, 50); + topPanel.setLayout(new BorderLayout()); + topPanel.add(vScrollbar, BorderLayout.EAST); + topPanel.add(hScrollbar, BorderLayout.SOUTH); + + X.add(topPanel, BorderLayout.NORTH); + X.add(bottomPanel, BorderLayout.SOUTH); + frame.add(X, BorderLayout.SOUTH); + frame.setBounds(350, 350, 300, 250); + frame.setVisible(true); + }); + + launchButton3.addActionListener(event -> { + MenuTest frame = new MenuTest("Menu Test 3"); + frame.setBounds(400, 400, 300, 300); + + mySimpleCanvas clock = new mySimpleCanvas(); + frame.add(clock, BorderLayout.CENTER); + + Panel p = new Panel(); + Button closeButton = new Button("Close"); + p.add(closeButton); + + p.add(new Label("Label")); + TextField textField = new TextField(8); + p.add(textField); + f.add(p, BorderLayout.EAST); + + frame.add(p, BorderLayout.SOUTH); + frame.setVisible(true); + }); + f.pack(); + return f; + } + + static class mySimpleCanvas extends Canvas { + @Override + public void paint(Graphics g) { + g.drawOval(0, 0, 100, 100); + g.drawOval(2, 2, 100, 100); + g.drawOval(4, 4, 100, 100); + } + } +} diff --git a/test/jdk/java/awt/Menu/DestroyMenuTest/MenuTest.java b/test/jdk/java/awt/Menu/DestroyMenuTest/MenuTest.java new file mode 100644 index 0000000000000..95569f13fa22e --- /dev/null +++ b/test/jdk/java/awt/Menu/DestroyMenuTest/MenuTest.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Canvas; +import java.awt.CardLayout; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Label; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.Panel; + +public class MenuTest extends Frame { + private MenuItem quitItem; + private final Panel cards; + private final CardLayout layout; + + public MenuTest(String s) { + super(s); + MenuBar mbar = new MenuBar(); + createMenus(mbar); + setMenuBar(mbar); + + cards = new Panel(); + layout = new CardLayout(); + cards.setLayout(layout); + + cards.add(new MyPanelOne("Options"), "Options"); + cards.add(new MyRectCanvas(), "MyRectCanvas"); + cards.add(new MycircleCanvas(), "MyCircleCanvas"); + + add(cards, "Center"); + } + + public void createMenus(MenuBar mbar) { + mbar.add(createFileMenu()); + mbar.add(createEditMenu()); + mbar.add(createOptionMenu1()); + mbar.add(createOptionMenu2()); + mbar.add(createOptionMenu3()); + mbar.add(createOptionMenu4()); + } + + private Menu createFileMenu() { + Menu fileMenu = new Menu("File"); + fileMenu.add(quitItem = new MenuItem("Quit")); + + quitItem.addActionListener(event -> { + MenuItem item = (MenuItem) event.getSource(); + if (item == quitItem) { + dispose(); + } + }); + return fileMenu; + } + + private Menu createEditMenu() { + Menu editMenu = new Menu("Edit"); + + editMenu.add("Cut"); + editMenu.add("Copy"); + editMenu.add("Paste"); + editMenu.addSeparator(); + editMenu.add("Select all"); + editMenu.addSeparator(); + editMenu.add("Find"); + editMenu.add("Find again"); + + return editMenu; + } + + private Menu createOptionMenu1() { + Menu optionMenu1 = new Menu("Option1"); + MenuItem item1, item2, item3; + optionMenu1.add(item1 = new MenuItem("Item1")); + optionMenu1.add(item2 = new MenuItem("Item2")); + optionMenu1.add(item3 = new MenuItem("Item3")); + + item1.addActionListener(event -> { + MenuItem mItem = (MenuItem) event.getSource(); + if (mItem == item1) { + layout.show(cards, "Options"); + } + }); + item2.addActionListener(event -> { + MenuItem mItem = (MenuItem) event.getSource(); + if (mItem == item2) { + layout.show(cards, "MyRectCanvas"); + } + }); + item3.addActionListener(event -> { + MenuItem mItem = (MenuItem) event.getSource(); + if (mItem == item3) { + layout.show(cards, "MyCircleCanvas"); + } + }); + return optionMenu1; + } + + private Menu createOptionMenu2() { + Menu optionMenu2 = new Menu("Option2"); + + optionMenu2.add("Item1"); + optionMenu2.add("Item2"); + + return optionMenu2; + } + + private Menu createOptionMenu3() { + Menu optionMenu3 = new Menu("Option3"); + + optionMenu3.add("Item1"); + optionMenu3.add("Item2"); + optionMenu3.add("Item3"); + optionMenu3.add("Item4"); + + return optionMenu3; + } + + private Menu createOptionMenu4() { + Menu optionMenu4 = new Menu("Option3"); + + optionMenu4.add("Item1"); + optionMenu4.add("Item2"); + optionMenu4.add("Item3"); + + return optionMenu4; + } +} + +class MyRectCanvas extends Canvas { + @Override + public void paint(Graphics g) { + g.drawRect(0, 0, 100, 100); + } +} + +class MyPanelOne extends Panel { + MyPanelOne(String name) { + add(new Label(name + " panel goes here")); + } +} + +class MycircleCanvas extends Canvas { + @Override + public void paint(Graphics g) { + g.drawOval(0, 0, 100, 100); + g.drawOval(2, 2, 100, 100); + g.drawOval(4, 4, 100, 100); + } +} diff --git a/test/jdk/java/awt/Menu/MenuActionEventTest.java b/test/jdk/java/awt/Menu/MenuActionEventTest.java new file mode 100644 index 0000000000000..70fcc81ac864c --- /dev/null +++ b/test/jdk/java/awt/Menu/MenuActionEventTest.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4094620 + * @summary MenuItem.enableEvents does not work + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MenuActionEventTest + */ + +import java.awt.AWTEvent; +import java.awt.BorderLayout; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.event.ActionEvent; + +public class MenuActionEventTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Click on the Menu and then on Menuitem on the frame. + 2. If you find the following message being printed in + the test log area:, + _MenuItem: action event", + click PASS, else click FAIL" + """; + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(MenuActionEventTest::initialize) + .logArea() + .build() + .awaitAndCheck(); + } + + static Frame initialize() { + Frame f = new Frame("Menu Action Event Test"); + f.setLayout(new BorderLayout()); + f.setMenuBar(new MenuBar()); + Menu m = new _Menu("Menu"); + MenuBar mb = f.getMenuBar(); + mb.add(m); + MenuItem mi = new _MenuItem("Menuitem"); + m.add(mi); + f.setBounds(204, 152, 396, 300); + return f; + } + + static class _Menu extends Menu { + public _Menu(String text) { + super(text); + enableEvents(AWTEvent.ACTION_EVENT_MASK); + } + + @Override + protected void processActionEvent(ActionEvent e) { + PassFailJFrame.log("_Menu: action event"); + super.processActionEvent(e); + } + } + + static class _MenuItem extends MenuItem { + public _MenuItem(String text) { + super(text); + enableEvents(AWTEvent.ACTION_EVENT_MASK); + } + + @Override + protected void processActionEvent(ActionEvent e) { + PassFailJFrame.log("_MenuItem: action event"); + super.processActionEvent(e); + } + } + +} diff --git a/test/jdk/java/awt/Menu/MenuAddRemoveCrash.java b/test/jdk/java/awt/Menu/MenuAddRemoveCrash.java new file mode 100644 index 0000000000000..d5c80c27a4f25 --- /dev/null +++ b/test/jdk/java/awt/Menu/MenuAddRemoveCrash.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4410477 + * @summary Tests that menu does not crash during simultaneous drawing + * and removal of items. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MenuAddRemoveCrash + */ + +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; + +public class MenuAddRemoveCrash { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Move and resize the frame. + 2. If the test crashes the test is FAILED. + Otherwise it is PASSED. + """; + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(MenuAddRemoveCrash::initialize) + .build() + .awaitAndCheck(); + } + + public static Frame initialize() { + final TestGui myTestGui = new TestGui(); + Thread test = new Thread() { + public void run() { + while (!Thread.interrupted()) { + myTestGui.changeMenuItems(); + } + } + }; + test.setDaemon(true); + test.start(); + return myTestGui; + } +} + +class TestGui extends Frame { + Menu myMenu1; + Menu myMenu2; + + public TestGui() { + this.setTitle("Try to resize this frame!"); + + this.setSize(300, 300); + this.setVisible(true); + + MenuBar myMenuBar = new MenuBar(); + myMenu1 = new Menu("DemoMenu1"); + myMenu2 = new Menu("DemoMenu2"); + + myMenuBar.add(myMenu1); + myMenuBar.add(myMenu2); + + this.setMenuBar(myMenuBar); + } + + public void changeMenuItems() { + myMenu1.removeAll(); + + for (int i = 0; i < 10; i++) { + MenuItem myMenuItem1 = new MenuItem("DemoMenuItem" + i); + myMenu1.add(myMenuItem1); + } + try { + Thread.sleep(100); + } catch (Exception e) { + throw new RuntimeException("Failed :" + e); + } + } +} diff --git a/test/jdk/java/awt/Menu/MenuVisibilityTest.java b/test/jdk/java/awt/Menu/MenuVisibilityTest.java new file mode 100644 index 0000000000000..cb74cd56e1a1f --- /dev/null +++ b/test/jdk/java/awt/Menu/MenuVisibilityTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 5046491 6423258 + * @summary CheckboxMenuItem: menu text is missing from test frame + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MenuVisibilityTest +*/ + +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; + +public class MenuVisibilityTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Press on a MenuBar with a long name. + 2. Select "First item" in an opened menu. + If you see that "First menu item was pressed" in + the test log area, press PASS + Otherwise press FAIL" + """; + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(MenuVisibilityTest::initialize) + .logArea() + .build() + .awaitAndCheck(); + } + + public static Frame initialize() { + Frame frame = new Frame("Menu visibility test"); + String menuTitle = "I_have_never_seen_so_long_Menu_Title_" + + "!_ehe-eha-ehu-ehi_ugu-gu!!!_;)_BANG_BANG..."; + MenuBar menubar = new MenuBar(); + Menu menu = new Menu(menuTitle); + MenuItem menuItem = new MenuItem("First item"); + menuItem.addActionListener(e -> + PassFailJFrame.log("First menu item was pressed.")); + menu.add(menuItem); + menubar.add(menu); + frame.setMenuBar(menubar); + frame.setSize(100, 200); + return frame; + } +} diff --git a/test/jdk/java/awt/Menu/MenuZOrderTest.java b/test/jdk/java/awt/Menu/MenuZOrderTest.java new file mode 100644 index 0000000000000..fefd3d64c28f9 --- /dev/null +++ b/test/jdk/java/awt/Menu/MenuZOrderTest.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6267182 + * @summary Menu is not visible after showing and disposing a file dialog. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MenuZOrderTest + */ + +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class MenuZOrderTest { + static class Listener implements ActionListener { + @Override + public void actionPerformed(ActionEvent e) { + Frame f = new Frame("Menu Z order test frame"); + f.setBounds(200, 200, 200, 200); + f.setVisible(true); + } + } + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Choose Menu 1 --> Menu Item 1 several times. + 2. If menu window is shown correctly and each click + creates new frame - press PASS. + 3. If menu window is obscured by frame - press FAIL. + """; + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(MenuZOrderTest::initialize) + .build() + .awaitAndCheck(); + } + + static Frame initialize() { + Frame mf = new Frame("Menu Z order test"); + Listener l = new Listener(); + MenuBar mb = new MenuBar(); + Menu m1 = new Menu("Menu 1"); + MenuItem mi1 = new MenuItem("Menu Item 1"); + + mf.setSize(200, 200); + mi1.addActionListener(l); + m1.add(mi1); + mb.add(m1); + mf.setMenuBar(mb); + mf.setVisible(true); + return mf; + } +} diff --git a/test/jdk/java/awt/Menu/OnFlyRepaintMenuTest.java b/test/jdk/java/awt/Menu/OnFlyRepaintMenuTest.java new file mode 100644 index 0000000000000..617d640d90732 --- /dev/null +++ b/test/jdk/java/awt/Menu/OnFlyRepaintMenuTest.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 5024051 + * @summary Tests if menu is repainted in enabling/disabling it and + * changing its label while it is visible, either on MenuBar + * or in other Menu. Menu items are covered as well + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual OnFlyRepaintMenuTest + */ + +import java.awt.Button; +import java.awt.CheckboxMenuItem; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; + +public class OnFlyRepaintMenuTest { + static boolean menuEnabled = true; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Click the button 'Change state' and wait for 5 secs. + 2. If menu is repainted correctly after its setLabel() + and setEnabled() methods called test PASSED, else FAILED. + (During a 5 secs delay you may select the menu to see + the effect for menu items and submenu) + """; + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(OnFlyRepaintMenuTest::initialize) + .build() + .awaitAndCheck(); + } + + static Frame initialize() { + Frame f = new Frame("OnFly Menu Repaint Test"); + + f.setSize(200, 100); + + MenuBar mb = new MenuBar(); + Menu menu = new Menu("Menu"); + MenuItem menuItem = new MenuItem("MenuItem"); + menu.add(menuItem); + Menu submenu = new Menu("SubMenu"); + MenuItem submenuItem = new MenuItem("SubmenuItem"); + submenu.add(submenuItem); + CheckboxMenuItem checkMenuItem = new CheckboxMenuItem("CheckboxmenuItem"); + checkMenuItem.setState(true); + menu.add(checkMenuItem); + menu.add(submenu); + mb.add(menu); + f.setMenuBar(mb); + + Button b = new Button("Change state"); + b.addActionListener(ev -> new Thread(() -> { + try { + Thread.sleep(5000); + } catch (Exception e) { + } + menuEnabled = !menuEnabled; + String label = menuEnabled ? "Enabled" : "Disabled"; + menu.setLabel(label); + menuItem.setLabel(label); + submenu.setLabel(label); + submenuItem.setLabel(label); + checkMenuItem.setLabel(label); + checkMenuItem.setEnabled(menuEnabled); + checkMenuItem.setState(menuEnabled); + submenuItem.setEnabled(menuEnabled); + submenu.setEnabled(menuEnabled); + menuItem.setEnabled(menuEnabled); + menu.setEnabled(menuEnabled); + }).start()); + f.add(b); + return f; + } +} diff --git a/test/jdk/java/awt/Menu/RmInHideTest.java b/test/jdk/java/awt/Menu/RmInHideTest.java new file mode 100644 index 0000000000000..b2d6840064749 --- /dev/null +++ b/test/jdk/java/awt/Menu/RmInHideTest.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4039387 + * @summary Checks that calling Frame.remove() within hide() doesn't + * cause SEGV + * @key headful + * @run main RmInHideTest + */ + +import java.awt.Button; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; + +public class RmInHideTest { + static volatile Point point; + static RmInHideTestFrame frame; + static volatile Dimension dimension; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + try { + EventQueue.invokeAndWait(() -> { + frame = new RmInHideTestFrame(); + frame.setSize(200, 200); + frame.setVisible(true); + }); + robot.waitForIdle(); + robot.delay(1000); + EventQueue.invokeAndWait(() -> { + point = frame.getButtonLocation(); + dimension = frame.getButtonDimension(); + }); + robot.mouseMove(point.x + dimension.width / 2, point.y + dimension.height / 2); + robot.mousePress(MouseEvent.BUTTON2_DOWN_MASK); + robot.mouseRelease(MouseEvent.BUTTON2_DOWN_MASK); + robot.waitForIdle(); + robot.delay(100); + System.out.println("Test pass"); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + static class RmInHideTestFrame extends Frame implements ActionListener { + MenuBar menubar = null; + Button b; + + public RmInHideTestFrame() { + super("RmInHideTest"); + b = new Button("Hide"); + b.setActionCommand("hide"); + b.addActionListener(this); + add("Center", b); + + MenuBar bar = new MenuBar(); + + Menu menu = new Menu("Test1", true); + menu.add(new MenuItem("Test1A")); + menu.add(new MenuItem("Test1B")); + menu.add(new MenuItem("Test1C")); + bar.add(menu); + + menu = new Menu("Test2", true); + menu.add(new MenuItem("Test2A")); + menu.add(new MenuItem("Test2B")); + menu.add(new MenuItem("Test2C")); + bar.add(menu); + setMenuBar(bar); + } + + @Override + public Dimension minimumSize() { + return new Dimension(200, 200); + } + + @Override + public void actionPerformed(ActionEvent e) { + String cmd = e.getActionCommand(); + if (cmd.equals("hide")) { + hide(); + try { + Thread.currentThread().sleep(2000); + } catch (InterruptedException ex) { + // do nothing + } + show(); + } + } + + @Override + public void hide() { + menubar = getMenuBar(); + if (menubar != null) { + remove(menubar); + } + super.hide(); + } + + + @Override + public void show() { + if (menubar != null) { + setMenuBar(menubar); + } + super.show(); + } + + public Point getButtonLocation() { + return b.getLocationOnScreen(); + } + + public Dimension getButtonDimension() { + return b.getSize(); + } + } +} diff --git a/test/jdk/java/awt/Menu/SetShortCutTest.java b/test/jdk/java/awt/Menu/SetShortCutTest.java new file mode 100644 index 0000000000000..b60b83dff9a9f --- /dev/null +++ b/test/jdk/java/awt/Menu/SetShortCutTest.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4203208 + * @summary setShortcut method does not display proper text on Menu component + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual SetShortCutTest + */ + +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.MenuShortcut; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; + +import static java.awt.event.KeyEvent.VK_META; +import static java.awt.event.KeyEvent.VK_SHIFT; + +public class SetShortCutTest { + public static void main(String[] args) throws Exception { + boolean isMac = System.getProperty("os.name").startsWith("Mac"); + String shortcut = "Ctrl+Shift+"; + if (isMac) { + shortcut = KeyEvent.getKeyText(VK_SHIFT) + "+" + KeyEvent.getKeyText(VK_META); + } + + String INSTRUCTIONS = """ + 1. Select menuitem 'Stuff -> Second' once to remove 'File -> First'. + 2. Select menuitem 'Stuff -> Second' again to add 'File -> First'. + 3. If menuitem 'File -> First' reads First """ + shortcut + """ + 'C', press PASS. Otherwise press FAIL. + """; + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(SetShortCutTest::initialize) + .build() + .awaitAndCheck(); + } + + static Frame initialize() { + return new TestMenuShortCut(); + } + + static class TestMenuShortCut extends Frame implements ActionListener { + Menu menu1; + MenuItem item1; + MenuItem item2; + boolean beenHere; + + public TestMenuShortCut() { + setTitle("Set ShortCut test"); + beenHere = false; + MenuBar mTopMenu = buildMenu(); + setSize(300, 300); + this.setMenuBar(mTopMenu); + } + + public MenuBar buildMenu() { + MenuBar bar; + bar = new MenuBar(); + menu1 = new Menu("File"); + item1 = new MenuItem("First"); + menu1.add(item1); + item1.setShortcut(new MenuShortcut(KeyEvent.VK_C, true)); + bar.add(menu1); + + // Stuff menu + item2 = new MenuItem("Second"); + Menu menu2 = new Menu("Stuff"); + menu2.add(item2); + item2.setShortcut(new MenuShortcut(KeyEvent.VK_C, false)); + bar.add(menu2); + + item1.addActionListener(this); + item2.addActionListener(this); + return bar; + } + + @Override + public void actionPerformed(ActionEvent event) { + if (event.getSource() == item1) { + Frame temp = new Frame("Accelerator key is working for 'First'"); + temp.setSize(300, 50); + temp.setVisible(true); + } + + // Click on the "Stuff" menu to remove the "first" menu item + else if (event.getSource() == item2) { + // If the item has not been removed from the menu, + // then remove "First" from the "File" menu + if (beenHere == false) { + item1.removeActionListener(this); + menu1.remove(item1); + beenHere = true; + } else { + item1 = new MenuItem("First"); + menu1.add(item1); + item1.addActionListener(this); + item1.setShortcut(new MenuShortcut(KeyEvent.VK_C, true)); + beenHere = false; + } + } + } + } +} diff --git a/test/jdk/java/awt/MenuBar/CellsResize.java b/test/jdk/java/awt/MenuBar/CellsResize.java new file mode 100644 index 0000000000000..64777095fa8d5 --- /dev/null +++ b/test/jdk/java/awt/MenuBar/CellsResize.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6502052 + * @summary Menu cells must resize if font changes (XToolkit) + * @requires os.family == "linux" + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual CellsResize + */ + +import java.awt.Button; +import java.awt.Font; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuComponent; +import java.awt.MenuItem; +import java.awt.Panel; +import java.awt.PopupMenu; +import java.awt.Toolkit; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +public class CellsResize { + private static Frame frame; + private static MenuBar menuBar; + private static PopupMenu popupMenu; + private static Menu barSubMenu; + private static Menu popupSubMenu; + private static boolean fontMultiplied = false; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Open all nested menus in menu bar. + 2. Click on "popup-menu" button to show popup-menus. + 3. Open all nested menus in popup-menu. + 4. Click on "big-font" button (to make all menus have a + bigger font). + 5. Open all nested menus again (as described in 1, 2, 3). + 6. If all menu items use a bigger font now and their labels fit + into menu-item size, press "pass", otherwise press "fail". + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(CellsResize::createUI) + .logArea(5) + .build() + .awaitAndCheck(); + } + + public static Frame createUI () { + if (!checkToolkit()) { + new RuntimeException("Toolkit check failed."); + } + frame = new Frame("MenuBar Cell Resize Test"); + + popupMenu = new PopupMenu(); + popupMenu.add(createMenu(false)); + + frame.add(popupMenu); + + menuBar = new MenuBar(); + menuBar.add(createMenu(true)); + + frame.setMenuBar(menuBar); + + Button bp = new Button("popup-menu"); + bp.addMouseListener(new MouseAdapter() { + public void mouseReleased(MouseEvent e) { + popupMenu.show(e.getComponent(), e.getX(), e.getY()); + } + }); + + Button bf = new Button("big-font"); + bf.addMouseListener(new MouseAdapter() { + public void mouseReleased(MouseEvent e) { + bigFont(); + } + }); + + Panel panel = new Panel(); + panel.setLayout(new GridLayout(2, 1)); + panel.add(bp); + panel.add(bf); + + frame.add(panel); + frame.setSize(300, 300); + return frame; + } + + static boolean checkToolkit() { + String toolkitName = Toolkit.getDefaultToolkit().getClass().getName(); + return toolkitName.equals("sun.awt.X11.XToolkit"); + } + + static Menu createMenu(boolean bar) { + Menu menu1 = new Menu("Menu-1"); + Menu menu11 = new Menu("Menu-11"); + menu1.add(menu11); + if (bar) { + barSubMenu = menu11; + } else { + popupSubMenu = menu11; + } + menu11.add(new MenuItem("MenuItem")); + return menu1; + } + + static void bigFont() { + if (fontMultiplied) { + return; + } else { + fontMultiplied = true; + } + + multiplyFont(barSubMenu, 7); + multiplyFont(popupSubMenu, 7); + + // NOTE: if previous two are moved below following + // two, they get their font multiplied twice. + + multiplyFont(menuBar, 5); + multiplyFont(popupMenu, 5); + } + + static void multiplyFont(MenuComponent comp, int times) { + Font font = comp.getFont(); + float size = font.getSize() * times; + comp.setFont(font.deriveFont(size)); + } +} diff --git a/test/jdk/java/awt/MenuBar/MenuBarAddRemoveTest/MenuBarAddRemoveTest.java b/test/jdk/java/awt/MenuBar/MenuBarAddRemoveTest/MenuBarAddRemoveTest.java new file mode 100644 index 0000000000000..fdb9f06e98c76 --- /dev/null +++ b/test/jdk/java/awt/MenuBar/MenuBarAddRemoveTest/MenuBarAddRemoveTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4028130 4112308 + * @summary Test for location of Frame/MenuBar when MenuBar is re-added + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MenuBarAddRemoveTest + */ + +import java.awt.Button; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; + +public class MenuBarAddRemoveTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Click the left mouse button on the "Re-Add MenuBar" + button several times. + 3. The Frame/MenuBar may repaint or flash, but the location + of its upper left corner should never change. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(MenuBarAddRemoveTest::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame f = new Frame("Re-Add MenuBar Test Frame"); + Button b = new Button("Re-Add MenuBar"); + b.addActionListener(e -> f.setMenuBar(createMenuBar())); + f.setMenuBar(createMenuBar()); + f.add(b); + f.pack(); + return f; + } + + private static MenuBar createMenuBar() { + MenuBar bar = new MenuBar(); + bar.add(new Menu("foo")); + return bar; + } +} diff --git a/test/jdk/java/awt/MenuBar/MenuBarOnDisabledFrame/MenuBarOnDisabledFrame.java b/test/jdk/java/awt/MenuBar/MenuBarOnDisabledFrame/MenuBarOnDisabledFrame.java new file mode 100644 index 0000000000000..06f5d96c19e32 --- /dev/null +++ b/test/jdk/java/awt/MenuBar/MenuBarOnDisabledFrame/MenuBarOnDisabledFrame.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6185057 + * @summary Disabling a frame does not disable the menus on the frame, on + * solaris/linux + * @requires os.family != "mac" + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MenuBarOnDisabledFrame + */ + +import java.awt.Button; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; + +public class MenuBarOnDisabledFrame { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Check if MenuBar is disabled on 'Disabled frame' + Press pass if menu bar is disabled, fail otherwise + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(MenuBarOnDisabledFrame::createUI) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame f = new Frame("Disabled frame"); + MenuBar mb = new MenuBar(); + Menu m1 = new Menu("Disabled Menu 1"); + Menu m2 = new Menu("Disabled Menu 2"); + MenuItem m11 = new MenuItem("MenuItem 1.1"); + MenuItem m21 = new MenuItem("MenuItem 2.1"); + Button b = new Button("Disabled button"); + + m1.add(m11); + m2.add(m21); + mb.add(m1); + mb.add(m2); + f.setMenuBar(mb); + f.add(b); + f.setEnabled(false); + f.setSize(300, 300); + return f; + } +} diff --git a/test/jdk/java/awt/MenuBar/MenuBarRemoveMenu/MenuBarRemoveMenuTest.java b/test/jdk/java/awt/MenuBar/MenuBarRemoveMenu/MenuBarRemoveMenuTest.java new file mode 100644 index 0000000000000..098065d1361f6 --- /dev/null +++ b/test/jdk/java/awt/MenuBar/MenuBarRemoveMenu/MenuBarRemoveMenuTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4275848 + * @summary Tests that MenuBar is painted correctly after its submenu is removed + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MenuBarRemoveMenuTest + */ + +import java.awt.Button; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class MenuBarRemoveMenuTest implements ActionListener { + private static MenuBar menubar; + private static Button removeButton; + private static Button addButton; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Press "Remove menu" button. If you see that both menus + disappeared, the test failed. Otherwise try to add and remove + menu several times to verify that the test passed. Every time + you press "Remove menu" button only one menu should go away. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(MenuBarRemoveMenuTest::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame frame = new Frame(); + menubar = new MenuBar(); + removeButton = new Button("Remove menu"); + addButton = new Button("Add menu"); + removeButton.addActionListener(new MenuBarRemoveMenuTest()); + addButton.addActionListener(new MenuBarRemoveMenuTest()); + addButton.setEnabled(false); + menubar.add(new Menu("menu")); + menubar.add(new Menu("menu")); + frame.setMenuBar(menubar); + frame.setLayout(new GridLayout(1, 2)); + frame.add(removeButton); + frame.add(addButton); + frame.pack(); + return frame; + } + + public void actionPerformed(ActionEvent e) { + if (e.getSource() == removeButton) { + menubar.remove(0); + removeButton.setEnabled(false); + addButton.setEnabled(true); + } else { + menubar.add(new Menu("menu")); + removeButton.setEnabled(true); + addButton.setEnabled(false); + } + } +} diff --git a/test/jdk/java/awt/MenuBar/MenuBarVisuals/MenuBarVisuals.java b/test/jdk/java/awt/MenuBar/MenuBarVisuals/MenuBarVisuals.java new file mode 100644 index 0000000000000..7663dd0d99be1 --- /dev/null +++ b/test/jdk/java/awt/MenuBar/MenuBarVisuals/MenuBarVisuals.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6180416 + * @summary Tests MenuBar and drop down menu visuals + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MenuBarVisuals + */ + +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.MenuShortcut; +import java.awt.event.KeyEvent; + +public class MenuBarVisuals { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Look at the MenuBar and traverse the menus using mouse and + keyboard. Then check if following is showing correctly: + 1. Mnemonic label Ctrl+A is NOT drawn for Menu 1/Submenu 1.1 + 2. Mnemonic label Ctrl+B is drawn for + Menu 1/Submenu 1.1/Item 1.1.1 + 3. Mnemonic label Ctrl+C is drawn for Menu1/Item 1.2 + Press PASS if Menu is drawing correctly, FAIL otherwise. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(MenuBarVisuals::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame f = new Frame("MenuBar Visuals Test"); + MenuBar mb = new MenuBar(); + Menu menu1 = new Menu("Menu 1"); + Menu submenu11 = new Menu("Submenu 1.1"); + MenuItem item111 = new MenuItem("Item 1.1.1"); + MenuItem item112 = new MenuItem("Item 1.1.2"); + MenuItem item12 = new MenuItem("Item 1.2"); + Menu menu2 = new Menu("Menu 2"); + MenuItem item21 = new MenuItem("Item 2.1"); + MenuItem item22 = new MenuItem("Item 2.2"); + item111.setShortcut(new MenuShortcut(KeyEvent.VK_B, false)); + submenu11.add(item111); + submenu11.add(item112); + submenu11.setShortcut(new MenuShortcut(KeyEvent.VK_A, false)); + menu1.add(submenu11); + item12.setShortcut(new MenuShortcut(KeyEvent.VK_C, false)); + menu1.add(item12); + mb.add(menu1); + menu2.add(item21); + menu2.add(item22); + mb.add(menu2); + f.setMenuBar(mb); + f.setSize(300, 300); + return f; + } +} diff --git a/test/jdk/java/awt/MenuBar/MenuNPE/MenuNPE.java b/test/jdk/java/awt/MenuBar/MenuNPE/MenuNPE.java new file mode 100644 index 0000000000000..a7a3a3480118e --- /dev/null +++ b/test/jdk/java/awt/MenuBar/MenuNPE/MenuNPE.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 5005194 + * @summary Frame.remove(getMenuBar()) throws NPE if the frame doesn't + * have a menu bar + * @key headful + * @run main MenuNPE + */ + +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; + +public class MenuNPE { + private static Frame frame; + public static void main(String[] args) throws Exception { + try { + frame = new Frame("Menu NPE"); + MenuBar menuBar = new MenuBar(); + Menu menu1 = new Menu("Menu 01"); + MenuItem menuLabel = new MenuItem("Item 01"); + menu1.add(menuLabel); + menuBar.add(menu1); + frame.setMenuBar(menuBar); + frame.setSize(200, 200); + frame.setVisible(true); + frame.validate(); + frame.remove(frame.getMenuBar()); + frame.remove(frame.getMenuBar()); + System.out.println("Test passed."); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (frame != null) { + frame.dispose(); + } + } + } +} diff --git a/test/jdk/java/awt/MenuBar/SetHelpMenuTest/SetHelpMenuTest.java b/test/jdk/java/awt/MenuBar/SetHelpMenuTest/SetHelpMenuTest.java new file mode 100644 index 0000000000000..fcfc3e80ed34c --- /dev/null +++ b/test/jdk/java/awt/MenuBar/SetHelpMenuTest/SetHelpMenuTest.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4275843 + * @summary MenuBar doesn't display all of its Menus correctly on Windows + * @requires os.family == "windows" + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual SetHelpMenuTest + */ + +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; + +public class SetHelpMenuTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + An empty frame should be visible. When focused, the MenuBar + should have 5 menus ("one", "two", "three", "Help 2", + "four"). If so, then the test passed. Otherwise, the test + failed. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(SetHelpMenuTest::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame f = new Frame("Help MenuBar Test"); + f.setSize(100, 100); + + MenuBar mb = new MenuBar(); + Menu h1, h2; + + f.setMenuBar(mb); + mb.add(createMenu("one", false)); + mb.add(createMenu("two", false)); + mb.add(createMenu("three", true)); + + mb.add(h1 = createMenu("Help 1", false)); // h1 is HelpMenu + mb.setHelpMenu(h1); + + mb.add(h2 = createMenu("Help 2", false)); // h2 replaced h1 + mb.setHelpMenu(h2); + + mb.add(createMenu("four", false)); + + return f; + } + + private static Menu createMenu(String name, boolean tearOff) { + Menu m = new Menu(name, tearOff); + m.add(new MenuItem(name + " item 1")); + m.add(new MenuItem(name + " item 2")); + m.add(new MenuItem(name + " item 3")); + return m; + } +} diff --git a/test/jdk/java/awt/MenuBar/SetMBarWhenHidden/SetMBarWhenHidden.java b/test/jdk/java/awt/MenuBar/SetMBarWhenHidden/SetMBarWhenHidden.java new file mode 100644 index 0000000000000..67eefe4894248 --- /dev/null +++ b/test/jdk/java/awt/MenuBar/SetMBarWhenHidden/SetMBarWhenHidden.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4105881 + * @summary Sets the menu bar while frame window is hidden, then shows + frame again + * @key headful + * @run main SetMBarWhenHidden + */ + +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.Rectangle; + +// test case for 4105881: FRAME.SETSIZE() DOESN'T WORK FOR SOME SOLARIS WITH +// JDK115+CASES ON +public class SetMBarWhenHidden { + private static Frame f; + private static Rectangle startBounds; + private static Rectangle endBounds; + + public static void main(String[] args) throws Exception { + try { + EventQueue.invokeAndWait(() -> { + f = new Frame("Set MenuBar When Hidden Test"); + Menu file; + Menu edit; + MenuBar menubar = new MenuBar(); + file = new Menu("File"); + menubar.add(file); + edit = new Menu("Edit"); + menubar.add(edit); + edit.setEnabled(false); + f.setMenuBar(menubar); + f.setSize(200, 200); + startBounds = f.getBounds(); + System.out.println("About to call setVisible(false)"); + f.setVisible(false); + System.out.println("About to call setSize(500, 500)"); + f.setSize(500, 500); + // create a new menubar and add + MenuBar menubar1 = new MenuBar(); + menubar1.add(file); + menubar1.add(edit); + System.out.println("About to call setMenuBar"); + f.setMenuBar(menubar1); + System.out.println("About to call setVisible(true)"); + f.setVisible(true); + endBounds = f.getBounds(); + }); + if (startBounds.getHeight() > endBounds.getHeight() && + startBounds.getWidth() > endBounds.getWidth()) { + throw new RuntimeException("Test failed. Frame size didn't " + + "change.\nStart: " + startBounds + "\n" + + "End: " + endBounds); + } else { + System.out.println("Test passed.\nStart: " + startBounds + + "\nEnd: " + endBounds); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + } + } +} diff --git a/test/jdk/java/awt/MenuItem/GiantFontTest.java b/test/jdk/java/awt/MenuItem/GiantFontTest.java new file mode 100644 index 0000000000000..f1e352373bb1e --- /dev/null +++ b/test/jdk/java/awt/MenuItem/GiantFontTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Font; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; + +/* + * @test + * @bug 4700350 + * @requires os.family != "mac" + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Tests menu item font is big + * @run main/manual GiantFontTest + */ + +public class GiantFontTest { + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + A frame with one menu will appear. + On Linux, the menu's (present on menu bar) font should + be quite large (48 point). + If not, test fails. + + On Windows, the menu's (present on menu bar) font + should be normal size. + If the menu text is clipped by the title bar, or is painted over + the title bar or client area, the test fails. + + On both Windows and Linux, the menu items in the popup + menu should be large. + + If so, test passes."""; + + PassFailJFrame.builder() + .title("GiantFontTest") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testUI(GiantFontTest::createAndShowUI) + .build() + .awaitAndCheck(); + } + + private static Frame createAndShowUI() { + Font giantFont = new Font("Dialog", Font.PLAIN, 48); + Frame f = new Frame("GiantFontTest"); + MenuBar mb = new MenuBar(); + Menu m = new Menu("My font is too big!"); + m.setFont(giantFont); + for (int i = 0; i < 5; i++) { + m.add(new MenuItem("Some MenuItems")); + } + mb.add(m); + f.setMenuBar(mb); + f.setSize(450, 400); + return f; + } +} diff --git a/test/jdk/java/awt/MenuItem/LotsOfMenuItemsTest.java b/test/jdk/java/awt/MenuItem/LotsOfMenuItemsTest.java new file mode 100644 index 0000000000000..7a528205d27d8 --- /dev/null +++ b/test/jdk/java/awt/MenuItem/LotsOfMenuItemsTest.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; + +/* + * @test + * @bug 4175790 + * @requires os.family == "windows" + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Win32: Running out of command ids for menu items + * @run main/manual LotsOfMenuItemsTest + */ + +public class LotsOfMenuItemsTest extends ComponentAdapter { + private static final int NUM_WINDOWS = 400; + private static TestFrame firstFrame; + + public static void main(String[] args) throws Exception { + LotsOfMenuItemsTest obj = new LotsOfMenuItemsTest(); + String INSTRUCTIONS = """ + This test creates lots of frames with menu bars. + When it's done you will see two frames. + Try to select menu items from each of them. + + If everything seems to work - test passed. + Click "Pass" button in the test harness window. + + If test crashes on you - test failed."""; + + PassFailJFrame.builder() + .title("LotsOfMenuItemsTest") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testUI(obj::createAndShowUI) + .build() + .awaitAndCheck(); + } + + private Frame createAndShowUI() { + firstFrame = new TestFrame("First frame"); + firstFrame.addComponentListener(this); + return firstFrame; + } + + @Override + public void componentShown(ComponentEvent e) { + final int x = firstFrame.getX(); + final int y = firstFrame.getY() + firstFrame.getHeight() + 8; + TestFrame testFrame; + for (int i = 1; i < NUM_WINDOWS - 1; ++i) { + testFrame = new TestFrame("Running(" + i + ")...", x, y); + testFrame.setVisible(false); + testFrame.dispose(); + } + testFrame = new TestFrame("Last Frame", x, y); + PassFailJFrame.addTestWindow(testFrame); + } + + private static class TestFrame extends Frame { + static int n = 0; + + public TestFrame(String title) { + this(title, 0, 0, false); + } + + public TestFrame(String s, int x, int y) { + this(s, x, y, true); + } + + private TestFrame(String title, int x, int y, boolean visible) { + super(title); + MenuBar mb = new MenuBar(); + for (int i = 0; i < 10; ++i) { + Menu m = new Menu("Menu_" + (i + 1)); + for (int j = 0; j < 20; ++j) { + MenuItem mi = new MenuItem("Menu item " + ++n); + m.add(mi); + } + mb.add(m); + } + setMenuBar(mb); + setLocation(x, y); + setSize(450, 150); + if (visible) { + setVisible(true); + } + } + } +} diff --git a/test/jdk/java/awt/MenuItem/MenuSetFontTest.java b/test/jdk/java/awt/MenuItem/MenuSetFontTest.java new file mode 100644 index 0000000000000..9a4e8f8583905 --- /dev/null +++ b/test/jdk/java/awt/MenuItem/MenuSetFontTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Font; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; + +/* + * @test + * @bug 4066657 8009454 + * @requires os.family != "mac" + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Tests that setting a font on the Menu with MenuItem takes effect. + * @run main/manual MenuSetFontTest + */ + +public class MenuSetFontTest { + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Look at the menu in the upper left corner of the 'SetFont Test' frame. + Click on the "File" menu. You will see "menu item" item. + Press Pass if menu item is displayed using bold and large font, + otherwise press Fail. + If you do not see menu at all, press Fail."""; + + PassFailJFrame.builder() + .title("MenuSetFontTest") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testUI(MenuSetFontTest::createAndShowUI) + .build() + .awaitAndCheck(); + } + + private static Frame createAndShowUI() { + Frame frame = new Frame("SetFont Test"); + MenuBar menuBar = new MenuBar(); + Menu menu = new Menu("File"); + MenuItem item = new MenuItem("menu item"); + menu.add(item); + menuBar.add(menu); + menuBar.setFont(new Font(Font.MONOSPACED, Font.BOLD, 24)); + frame.setMenuBar(menuBar); + frame.setSize(300, 200); + return frame; + } +} diff --git a/test/jdk/java/awt/MenuItem/NullOrEmptyStringLabelTest.java b/test/jdk/java/awt/MenuItem/NullOrEmptyStringLabelTest.java new file mode 100644 index 0000000000000..79d4c02ad246a --- /dev/null +++ b/test/jdk/java/awt/MenuItem/NullOrEmptyStringLabelTest.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 4251036 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary MenuItem setLabel(null/"") behaves differently under Win32 and Solaris + * @run main/manual NullOrEmptyStringLabelTest + */ + +public class NullOrEmptyStringLabelTest { + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + The bug is reproducible under Win32 and Solaris. + Setting 'null' and "" as a label of menu item + should set blank label on all platforms according to the specification. + But under Solaris setting "" as a label of menu item used to + cause some garbage to be set as label. + Under Win32 setting 'null' as a label used to result in + throwing NullPointerException. + + If you see any of these things happen test fails otherwise + it passes."""; + + PassFailJFrame.builder() + .title("NullOrEmptyStringLabelTest") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testUI(NullOrEmptyStringLabelTest::createAndShowUI) + .build() + .awaitAndCheck(); + } + + private static Frame createAndShowUI() { + Frame frame = new Frame("Null Or Empty String Label Test"); + Menu menu = new Menu("Menu"); + MenuItem mi = new MenuItem("Item"); + MenuBar mb = new MenuBar(); + Button button1 = new Button("Set MenuItem label to 'null'"); + Button button2 = new Button("Set MenuItem label to \"\""); + Button button3 = new Button("Set MenuItem label to 'text'"); + button1.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent ev) { + System.out.println("Setting MenuItem label to null"); + mi.setLabel(null); + } + }); + button2.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent ev) { + System.out.println("Setting MenuItem label to \"\""); + mi.setLabel(""); + } + }); + button3.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent ev) { + System.out.println("Setting MenuItem label to 'text'"); + mi.setLabel("text"); + } + }); + menu.add(mi); + mb.add(menu); + frame.add(button1, BorderLayout.NORTH); + frame.add(button2, BorderLayout.CENTER); + frame.add(button3, BorderLayout.SOUTH); + frame.setMenuBar(mb); + frame.setSize(200, 135); + return frame; + } +} diff --git a/test/jdk/java/awt/MenuItem/UnicodeMenuItemTest.java b/test/jdk/java/awt/MenuItem/UnicodeMenuItemTest.java new file mode 100644 index 0000000000000..b73fe954af6db --- /dev/null +++ b/test/jdk/java/awt/MenuItem/UnicodeMenuItemTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; + +/* + * @test + * @bug 4099695 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary menu items with Unicode labels treated as separators + * @run main/manual UnicodeMenuItemTest + */ + +public class UnicodeMenuItemTest { + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Click on the "Menu" on the top-left corner of frame. + + The menu should have four entries: + 1) a row of five unicode characters: \u00c4\u00cb\u00cf\u00d6\u00dc + 2) a menu separator + 3) a unicode character: \u012d + 4) a unicode character: \u022d + + If the menu items look like the list above, the test passes. + It is okay if the unicode characters look like empty boxes + or something - as long as they are not separators. + + If either of the last two menu items show up as separators, + the test FAILS. + + Press 'Pass' if above instructions hold good else press 'Fail'."""; + + PassFailJFrame.builder() + .title("UnicodeMenuItemTest") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testUI(UnicodeMenuItemTest::createAndShowUI) + .build() + .awaitAndCheck(); + } + private static Frame createAndShowUI() { + Frame frame = new Frame("Unicode MenuItem Test"); + MenuBar mb = new MenuBar(); + Menu m = new Menu("Menu"); + + MenuItem mi1 = new MenuItem("\u00c4\u00cb\u00cf\u00d6\u00dc"); + m.add(mi1); + + MenuItem separator = new MenuItem("-"); + m.add(separator); + + MenuItem mi2 = new MenuItem("\u012d"); + m.add(mi2); + + MenuItem mi3 = new MenuItem("\u022d"); + m.add(mi3); + + mb.add(m); + + frame.setMenuBar(mb); + frame.setSize(450, 150); + return frame; + } +} diff --git a/test/jdk/java/awt/Mixing/AWT_Mixing/HierarchyBoundsListenerMixingTest.java b/test/jdk/java/awt/Mixing/AWT_Mixing/HierarchyBoundsListenerMixingTest.java index c11f0476bc74c..3dfdfd8b6b84d 100644 --- a/test/jdk/java/awt/Mixing/AWT_Mixing/HierarchyBoundsListenerMixingTest.java +++ b/test/jdk/java/awt/Mixing/AWT_Mixing/HierarchyBoundsListenerMixingTest.java @@ -51,7 +51,7 @@ /** * AWT Mixing test for HierarchyBoundsListener ancestors. - *

    See CR6768230 for details. + *

    See CR6768230 for details. */ /* * @test diff --git a/test/jdk/java/awt/Mixing/AWT_Mixing/JGlassPaneInternalFrameOverlapping.java b/test/jdk/java/awt/Mixing/AWT_Mixing/JGlassPaneInternalFrameOverlapping.java index 843b4e8832fa6..5331fb11d39e2 100644 --- a/test/jdk/java/awt/Mixing/AWT_Mixing/JGlassPaneInternalFrameOverlapping.java +++ b/test/jdk/java/awt/Mixing/AWT_Mixing/JGlassPaneInternalFrameOverlapping.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,8 +35,8 @@ /** * AWT/Swing overlapping test with JInternalFrame being put in GlassPane. - * See JDK-6637655 and - * JDK-6985776. + * See JDK-6637655 and + * JDK-6985776. *

    See base class for details. */ /* diff --git a/test/jdk/java/awt/Mixing/AWT_Mixing/JGlassPaneMoveOverlapping.java b/test/jdk/java/awt/Mixing/AWT_Mixing/JGlassPaneMoveOverlapping.java index 4c917485e1d3c..5ac719d70a8e4 100644 --- a/test/jdk/java/awt/Mixing/AWT_Mixing/JGlassPaneMoveOverlapping.java +++ b/test/jdk/java/awt/Mixing/AWT_Mixing/JGlassPaneMoveOverlapping.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,8 +36,8 @@ /** * AWT/Swing overlapping test with JInternalFrame being moved in GlassPane. - * See JDK-6637655 and - * JDK-6981919. + * See JDK-6637655 and + * JDK-6981919. *

    See base class for details. */ /* diff --git a/test/jdk/java/awt/Mixing/AWT_Mixing/MixingPanelsResizing.java b/test/jdk/java/awt/Mixing/AWT_Mixing/MixingPanelsResizing.java index 1d827ce569fa1..39a59dc2a5c2a 100644 --- a/test/jdk/java/awt/Mixing/AWT_Mixing/MixingPanelsResizing.java +++ b/test/jdk/java/awt/Mixing/AWT_Mixing/MixingPanelsResizing.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,7 @@ /** * AWT/Swing overlapping test for Panel and JPanel behavior during resizing. - *

    See JDK-6786219 for details + *

    See JDK-6786219 for details */ /* * @test diff --git a/test/jdk/java/awt/Mixing/AWT_Mixing/OpaqueOverlapping.java b/test/jdk/java/awt/Mixing/AWT_Mixing/OpaqueOverlapping.java index 8587454f301a9..4975cc165c294 100644 --- a/test/jdk/java/awt/Mixing/AWT_Mixing/OpaqueOverlapping.java +++ b/test/jdk/java/awt/Mixing/AWT_Mixing/OpaqueOverlapping.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,7 +37,7 @@ /** * AWT/Swing overlapping test for opaque Swing components. *

    This test verify if AWT components are drawn correctly under opaque components. - *

    See JDK-6776743 for details + *

    See JDK-6776743 for details *

    See base class for test info. */ /* diff --git a/test/jdk/java/awt/Mixing/MixingOnDialog.java b/test/jdk/java/awt/Mixing/MixingOnDialog.java index 3e33d055dfb42..e5fdad76d3420 100644 --- a/test/jdk/java/awt/Mixing/MixingOnDialog.java +++ b/test/jdk/java/awt/Mixing/MixingOnDialog.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,70 +27,60 @@ @bug 4811096 @summary Tests whether mixing works on Dialogs @author anthony.petrov@...: area=awt.mixing - @library ../regtesthelpers - @build Util @run main MixingOnDialog */ -/** +/* * MixingOnDialog.java * * summary: Tests whether awt.Button and swing.JButton mix correctly */ -import java.awt.*; -import java.awt.event.*; -import javax.swing.*; -import test.java.awt.regtesthelpers.Util; - - - -public class MixingOnDialog -{ - static volatile boolean heavyClicked = false; +import java.awt.AWTException; +import java.awt.Button; +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.InputEvent; +import java.lang.reflect.InvocationTargetException; +import javax.swing.JButton; +import javax.swing.SwingUtilities; + +public class MixingOnDialog { static volatile boolean lightClicked = false; + static Dialog d; + static volatile Button heavy; - private static void init() - { + private static void init() { // Create components - final Dialog d = new Dialog((Frame)null, "Button-JButton mix test"); - final Button heavy = new Button(" Heavyweight Button "); + d = new Dialog((Frame)null, "Button-JButton mix test"); + heavy = new Button(" Heavyweight Button "); final JButton light = new JButton(" LW Button "); // Actions for the buttons add appropriate number to the test sequence - heavy.addActionListener(new java.awt.event.ActionListener() - { - public void actionPerformed(java.awt.event.ActionEvent e) { - heavyClicked = true; - } - } - ); - - light.addActionListener(new java.awt.event.ActionListener() - { - public void actionPerformed(java.awt.event.ActionEvent e) { - lightClicked = true; - } - } - ); + light.addActionListener(e -> lightClicked = true); // Overlap the buttons - heavy.setBounds(30, 30, 200, 200); - light.setBounds(10, 10, 50, 50); + heavy.setBounds(230, 230, 200, 200); + light.setBounds(210, 210, 50, 50); // Put the components into the frame d.setLayout(null); d.add(light); d.add(heavy); - d.setBounds(50, 50, 400, 400); + d.setBounds(250, 250, 400, 400); d.setVisible(true); + } + public static void main(String[] args) throws InterruptedException, InvocationTargetException, AWTException { + SwingUtilities.invokeAndWait(MixingOnDialog::init); - Robot robot = Util.createRobot(); - robot.setAutoDelay(20); - - Util.waitForIdle(robot); + Robot robot = new Robot(); + robot.setAutoDelay(50); + robot.waitForIdle(); + robot.delay(500); // Move the mouse pointer to the position where both // buttons overlap @@ -98,137 +88,14 @@ public void actionPerformed(java.awt.event.ActionEvent e) { robot.mouseMove(heavyLoc.x + 5, heavyLoc.y + 5); // Now perform the click at this point - robot.mousePress(InputEvent.BUTTON1_MASK); - robot.mouseRelease(InputEvent.BUTTON1_MASK); - Util.waitForIdle(robot); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); - // If the buttons are correctly mixed, the test sequence - // is equal to the check sequence. - if (lightClicked == true) { - MixingOnDialog.pass(); - } else { - MixingOnDialog.fail("The lightweight component left behind the heavyweight one."); - } - }//End init() + SwingUtilities.invokeAndWait(() -> d.dispose()); - - - /***************************************************** - * Standard Test Machinery Section - * DO NOT modify anything in this section -- it's a - * standard chunk of code which has all of the - * synchronisation necessary for the test harness. - * By keeping it the same in all tests, it is easier - * to read and understand someone else's test, as - * well as insuring that all tests behave correctly - * with the test harness. - * There is a section following this for test- - * classes - ******************************************************/ - private static boolean theTestPassed = false; - private static boolean testGeneratedInterrupt = false; - private static String failureMessage = ""; - - private static Thread mainThread = null; - - private static int sleepTime = 300000; - - // Not sure about what happens if multiple of this test are - // instantiated in the same VM. Being static (and using - // static vars), it aint gonna work. Not worrying about - // it for now. - public static void main( String args[] ) throws InterruptedException - { - mainThread = Thread.currentThread(); - try - { - init(); - } - catch( TestPassedException e ) - { - //The test passed, so just return from main and harness will - // interepret this return as a pass - return; + if (!lightClicked) { + throw new RuntimeException("The lightweight component left behind the heavyweight one."); } - //At this point, neither test pass nor test fail has been - // called -- either would have thrown an exception and ended the - // test, so we know we have multiple threads. - - //Test involves other threads, so sleep and wait for them to - // called pass() or fail() - try - { - Thread.sleep( sleepTime ); - //Timed out, so fail the test - throw new RuntimeException( "Timed out after " + sleepTime/1000 + " seconds" ); - } - catch (InterruptedException e) - { - //The test harness may have interrupted the test. If so, rethrow the exception - // so that the harness gets it and deals with it. - if( ! testGeneratedInterrupt ) throw e; - - //reset flag in case hit this code more than once for some reason (just safety) - testGeneratedInterrupt = false; - - if ( theTestPassed == false ) - { - throw new RuntimeException( failureMessage ); - } - } - - }//main - - public static synchronized void setTimeoutTo( int seconds ) - { - sleepTime = seconds * 1000; - } - - public static synchronized void pass() - { - System.out.println( "The test passed." ); - System.out.println( "The test is over, hit Ctl-C to stop Java VM" ); - //first check if this is executing in main thread - if ( mainThread == Thread.currentThread() ) - { - //Still in the main thread, so set the flag just for kicks, - // and throw a test passed exception which will be caught - // and end the test. - theTestPassed = true; - throw new TestPassedException(); - } - theTestPassed = true; - testGeneratedInterrupt = true; - mainThread.interrupt(); - }//pass() - - public static synchronized void fail() - { - //test writer didn't specify why test failed, so give generic - fail( "it just plain failed! :-)" ); } - - public static synchronized void fail( String whyFailed ) - { - System.out.println( "The test failed: " + whyFailed ); - System.out.println( "The test is over, hit Ctl-C to stop Java VM" ); - //check if this called from main thread - if ( mainThread == Thread.currentThread() ) - { - //If main thread, fail now 'cause not sleeping - throw new RuntimeException( whyFailed ); - } - theTestPassed = false; - testGeneratedInterrupt = true; - failureMessage = whyFailed; - mainThread.interrupt(); - }//fail() - -}// class MixingOnDialog - -//This exception is used to exit from any level of call nesting -// when it's determined that the test has passed, and immediately -// end the test. -class TestPassedException extends RuntimeException -{ } diff --git a/test/jdk/java/awt/Modal/AddRemoveTransientForsTest.java b/test/jdk/java/awt/Modal/AddRemoveTransientForsTest.java new file mode 100644 index 0000000000000..65b66e8ce8df5 --- /dev/null +++ b/test/jdk/java/awt/Modal/AddRemoveTransientForsTest.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Dialog; +import java.awt.Frame; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; + +/* + * @test + * @bug 6271779 + * @summary This test shows and hides a modal dialog several times without destroying its + * peer. Without the fix this may lead to application (or even WM) hang. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual AddRemoveTransientForsTest + */ + +public class AddRemoveTransientForsTest { + + private static Dialog d1; + private static Dialog d2; + + public static void main(String[] args) throws Exception { + final String INSTRUCTIONS = """ + When the test starts, a frame is shown with a button 'Show Dialog D1'. + + 1. Press the button 'Show Dialog D1' to show a modal dialog D1 with a button + 'Show dialog D2'. + + 2. Press the button 'Show dialog D2' to show another modal dialog D2 with a button + 'Close'. + + 3. Press the button 'Close' to close dialog D2. + + 4. Repeat steps 2 and 3 several times (at least 3-4 times). + + If the application is not hung, press Pass. + + NOTE: all the modal dialogs must be closed before pressing Pass button."""; + + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(45) + .testUI(AddRemoveTransientForsTest::init) + .build() + .awaitAndCheck(); + } + + public static Frame init() { + Frame f = new Frame("AddRemoveTransientForsTest Frame"); + Button b = new Button("Show dialog D1"); + b.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) + { + d1.setVisible(true); + } + }); + f.add(b); + f.setSize(200, 100); + + WindowListener wl = new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) + { + e.getWindow().dispose(); + } + }; + + d1 = new Dialog(f, "D1", true); + d1.setBounds(200, 200, 200, 100); + d1.addWindowListener(wl); + Button b1 = new Button("Show dialog D2"); + b1.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) + { + d2.setVisible(true); + } + }); + d1.add(b1); + + d2 = new Dialog(d1, "D2", true); + d2.setBounds(300, 300, 200, 100); + Button b2 = new Button("Close"); + b2.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) + { + d2.setVisible(false); + } + }); + d2.add(b2); + + return f; + } +} diff --git a/test/jdk/java/awt/Modal/DialogLosesFocusTest.java b/test/jdk/java/awt/Modal/DialogLosesFocusTest.java new file mode 100644 index 0000000000000..738c2c29ee2b2 --- /dev/null +++ b/test/jdk/java/awt/Modal/DialogLosesFocusTest.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Dialog; +import java.awt.EventQueue; +import java.awt.Frame; + +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; + +/* + * @test + * @bug 6278150 + * @key headful + * @summary Initially modal blocked window causes modal dialog to lose focus + * @run main DialogLosesFocusTest + */ + +public class DialogLosesFocusTest { + private static Frame parent; + private static Dialog dialog; + private static Frame blocked; + private static volatile boolean failed; + + public static void main(String[] args) throws Exception { + try { + createAndShowUI(); + + sleepForMsecs(10000); + + if (failed) { + throw new RuntimeException("Test Failed"); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (parent != null) { + parent.dispose(); + } + if (dialog != null) { + dialog.dispose(); + } + if (blocked != null) { + blocked.dispose(); + } + }); + } + } + + public static void createAndShowUI() throws Exception { + EventQueue.invokeAndWait(() -> { + parent = new Frame("Parent frame"); + parent.setBounds(0, 0, 300, 100); + parent.setVisible(true); + }); + + sleepForMsecs(1000); + + EventQueue.invokeLater(() -> { + dialog = new Dialog(parent, "Modal dialog", Dialog.ModalityType.APPLICATION_MODAL); + dialog.setBounds(100, 120, 300, 100); + dialog.setVisible(true); + }); + + sleepForMsecs(1000); + + EventQueue.invokeAndWait(() -> { + blocked = new Frame("Blocked frame"); + blocked.setBounds(200, 240, 300, 100); + blocked.addWindowListener(new WindowAdapter() { + @Override + public void windowActivated(WindowEvent we) { + if (dialog.isVisible()) { + failed = true; + } + } + }); + blocked.setVisible(true); + }); + } + + private static void sleepForMsecs(int t) { + try { + Thread.sleep(t); + } catch (Exception z) {} + } +} diff --git a/test/jdk/java/awt/Modal/NativeDialogToFrontBackTest.java b/test/jdk/java/awt/Modal/NativeDialogToFrontBackTest.java new file mode 100644 index 0000000000000..6c41ef73b1e7b --- /dev/null +++ b/test/jdk/java/awt/Modal/NativeDialogToFrontBackTest.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Dialog; +import java.awt.FileDialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.util.List; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.print.PageFormat; +import java.awt.print.PrinterJob; + +/* + * @test + * @bug 6393608 + * @summary Tests that toBack/toFront methods works correctly for native dialogs + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual NativeDialogToFrontBackTest + */ + +public class NativeDialogToFrontBackTest { + public static void main(String[] args) throws Exception { + final String INSTRUCTIONS = """ + When the test starts two frames appear: 'Control' and 'Blocked' + 1. Click on the 'Show file dialog' button + 2. Drag the file dialog so it partially overlaps the 'Blocked' frame + 3. 'Blocked' frame must be below the file dialog, if not - press Fail + 3. Click on the 'Blocked to front' button + 4. 'Blocked' frame must still be below the file dialog, if not - press Fail + 5. Close the file dialog + 6. Repeat steps 2 to 4 with print and page dialogs using the corresponding button + 7. If 'Blocked' frame is always below File/Print/Page dialog, press Pass"""; + + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(45) + .testUI(NativeDialogToFrontBackTest::init) + .positionTestUI(WindowLayouts::rightOneColumn) + .build() + .awaitAndCheck(); + } + + public static List init() { + Frame blocked = new Frame("Blocked"); + blocked.setSize(200, 200); + + Frame control = new Frame("Control"); + control.setModalExclusionType(Dialog.ModalExclusionType.APPLICATION_EXCLUDE); + control.setLayout(new FlowLayout()); + + Button showFileDialog = new Button("Show file dialog"); + showFileDialog.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) + { + new FileDialog(control, "File dialog").setVisible(true); + } + }); + control.add(showFileDialog); + + Button showPrintDialog = new Button("Show print dialog"); + showPrintDialog.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) + { + PrinterJob.getPrinterJob().printDialog(); + } + }); + control.add(showPrintDialog); + + Button showPageDialog = new Button("Show page dialog"); + showPageDialog.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) + { + PrinterJob.getPrinterJob().pageDialog(new PageFormat()); + } + }); + control.add(showPageDialog); + + Button blockedToFront = new Button("Blocked to front"); + blockedToFront.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) + { + blocked.toFront(); + } + }); + control.add(blockedToFront); + + control.setSize(200, 200); + return List.of(control, blocked); + } +} diff --git a/test/jdk/java/awt/Mouse/DoubleClickTest.java b/test/jdk/java/awt/Mouse/DoubleClickTest.java new file mode 100644 index 0000000000000..037332a40981e --- /dev/null +++ b/test/jdk/java/awt/Mouse/DoubleClickTest.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Event; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.TextArea; + +/* + * @test + * @bug 4092370 + * @summary Test to verify double click + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DoubleClickTest + */ + +public class DoubleClickTest { + static TextArea ta = new TextArea("", 10, 40); + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Double click on the red area. + 2. Verify that the event reports click_count > 1 on + Double-Click. If click_count shows only 1 for every + Double-Clicks then test FAILS, else test PASS. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .build() + .awaitAndCheck(); + } + + public static Frame initialize() { + Frame frame = new Frame("Double-click Test"); + frame.setLayout(new BorderLayout()); + frame.add("East", new MyPanel(ta)); + frame.add("West", ta); + frame.setSize(200, 200); + return frame; + } +} + +class MyPanel extends Panel { + TextArea ta; + + MyPanel(TextArea ta) { + this.ta = ta; + setBackground(Color.red); + } + + public Dimension getPreferredSize() { + return new Dimension(50, 50); + } + + + public boolean mouseDown(Event event, int x, int y) { + ta.append("event click count= " + event.clickCount + "\n"); + return false; + } +} diff --git a/test/jdk/java/awt/Mouse/GetMousePositionTest/GetMousePositionWithPopup.java b/test/jdk/java/awt/Mouse/GetMousePositionTest/GetMousePositionWithPopup.java index 28f1faa82ee4c..fd779d721f39d 100644 --- a/test/jdk/java/awt/Mouse/GetMousePositionTest/GetMousePositionWithPopup.java +++ b/test/jdk/java/awt/Mouse/GetMousePositionTest/GetMousePositionWithPopup.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,8 +23,10 @@ import test.java.awt.regtesthelpers.Util; -import javax.swing.*; -import java.awt.*; +import javax.swing.SwingUtilities; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Robot; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; @@ -33,11 +35,10 @@ * @key headful * @bug 8012026 8027154 * @summary Component.getMousePosition() does not work in an applet on MacOS - * @author Petr Pchelko * @library ../../regtesthelpers * @build Util * @compile GetMousePositionWithPopup.java - * @run main/othervm GetMousePositionWithPopup + * @run main GetMousePositionWithPopup */ public class GetMousePositionWithPopup { @@ -48,6 +49,7 @@ public class GetMousePositionWithPopup { public static void main(String[] args) throws Exception { try { Robot r = Util.createRobot(); + r.setAutoDelay(100); r.mouseMove(0, 0); Util.waitForIdle(null); @@ -61,7 +63,7 @@ public void run() { Util.waitForIdle(null); r.mouseMove(149, 149); Util.waitForIdle(null); - r.mouseMove(150, 150); + r.mouseMove(170, 170); Util.waitForIdle(null); } finally { @@ -78,20 +80,25 @@ public void run() { private static void constructTestUI() { frame1 = new Frame(); frame1.setBounds(100, 100, 100, 100); + frame1.setVisible(true); frame1.addMouseMotionListener(new MouseMotionAdapter() { - @Override public void mouseMoved(MouseEvent e) { + if (frame2 != null) { + return; + } frame2 = new Frame(); frame2.setBounds(120, 120, 120, 120); - + frame2.setVisible(true); frame2.addMouseMotionListener(new MouseMotionAdapter() { @Override public void mouseMoved(MouseEvent e) { Point positionInFrame2 = frame2.getMousePosition(); - if (positionInFrame2.x != 30 || positionInFrame2.y != 30) { - throw new RuntimeException("Wrong position reported. Should be [30, 30] but was [" + + int deltaX = Math.abs(50 - positionInFrame2.x); + int deltaY = Math.abs(50 - positionInFrame2.y); + if (deltaX > 2 || deltaY > 2) { + throw new RuntimeException("Wrong position reported. Should be [50, 50] but was [" + positionInFrame2.x + ", " + positionInFrame2.y + "]"); } @@ -101,10 +108,7 @@ public void mouseMoved(MouseEvent e) } } }); - - frame2.setVisible(true); } }); - frame1.setVisible(true); } -} \ No newline at end of file +} diff --git a/test/jdk/java/awt/Mouse/MouseClickCount.java b/test/jdk/java/awt/Mouse/MouseClickCount.java new file mode 100644 index 0000000000000..a63d24fcb9ffb --- /dev/null +++ b/test/jdk/java/awt/Mouse/MouseClickCount.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Frame; +import java.awt.TextArea; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +/* + * @test + * @bug 4199397 + * @summary Test to mouse click count + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MouseClickCount + */ + +public class MouseClickCount { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Clicking on Frame panel quickly will produce clickCount larger than 1 + in the TextArea the count is printed for each mouse click + 2. Verify that a left-button click followed by a right button click quickly + will not generate 1, 2, i.e. it's not considered a double clicking. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .build() + .awaitAndCheck(); + } + + private static Frame initialize() { + Frame f = new Frame("Mouse Click Count Test"); + final TextArea ta = new TextArea(); + f.add("South", ta); + f.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent e) { + if (e.getClickCount() == 1) ta.append("\n1"); + else ta.append(", " + e.getClickCount()); + } + }); + f.setSize(300, 500); + return f; + } +} diff --git a/test/jdk/java/awt/Mouse/MouseDragEnterExitTest.java b/test/jdk/java/awt/Mouse/MouseDragEnterExitTest.java new file mode 100644 index 0000000000000..b2d8e446ffc36 --- /dev/null +++ b/test/jdk/java/awt/Mouse/MouseDragEnterExitTest.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.List; + +/* + * @test + * @bug 4141361 + * @summary Test to Ensures that mouse enter / exit is delivered to a new + * frame or component during a drag + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MouseDragEnterExitTest + */ + +public class MouseDragEnterExitTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Click on the blue frame, drag to the white frame, and back + You should get enter/exit messages for the frames when dragging + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(MouseEvents.initialize()) + .logArea(8) + .build() + .awaitAndCheck(); + } +} + +class MouseEvents extends Frame { + static int WITH_WIDGET = 0; + + public MouseEvents(int mode) { + super("Mouse Drag Enter/Exit Test"); + setSize(300, 300); + + addMouseListener(new MouseAdapter() { + @Override + public void mouseEntered(MouseEvent e) { + PassFailJFrame.log("Frame MOUSE_ENTERED" + ": " + " " + + e.getX() + " " + e.getY()); + } + + @Override + public void mouseExited(MouseEvent e) { + PassFailJFrame.log("Frame MOUSE_EXITED" + ": " + " " + + e.getX() + " " + e.getY()); + } + }); + + if (mode == WITH_WIDGET) { + setLayout(new BorderLayout()); + add("Center", new SimplePanel()); + } + } + + public static List initialize() { + MouseEvents m = new MouseEvents(MouseEvents.WITH_WIDGET); + m.setLocation(500, 300); + MouseEvents t = new MouseEvents(MouseEvents.WITH_WIDGET + 1); + t.setLocation(200, 200); + return List.of(m, t); + } +} + +class SimplePanel extends Panel { + public SimplePanel() { + super(); + setName("Test Panel"); + addMouseListener(new MouseAdapter() { + @Override + public void mouseEntered(MouseEvent e) { + PassFailJFrame.log("Panel MOUSE_ENTERED" + ": " + " " + + e.getX() + " " + e.getY()); + } + + @Override + public void mouseExited(MouseEvent e) { + PassFailJFrame.log("Panel MOUSE_EXITED" + ": " + " " + + e.getX() + " " + e.getY()); + } + }); + setSize(100, 100); + setBackground(Color.blue); + } +} + diff --git a/test/jdk/java/awt/Mouse/MouseDragTest.java b/test/jdk/java/awt/Mouse/MouseDragTest.java new file mode 100644 index 0000000000000..cb8be7b0de25b --- /dev/null +++ b/test/jdk/java/awt/Mouse/MouseDragTest.java @@ -0,0 +1,215 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Canvas; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionAdapter; +import java.awt.event.MouseMotionListener; + +/* + * @test + * @bug 4035189 + * @summary Test to verify that Drag events go to wrong component + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MouseDragTest + */ + +class HeavySquare extends Canvas { + private final Color colorNormal; + private boolean gotADragEvent; + + public HeavySquare(Color color) { + colorNormal = color; + setBackground(colorNormal); + new MouseChecker(this); + addMouseMotionListener(new DragAdapter()); + addMouseListener(new PressReleaseAdapter()); + } + + class DragAdapter extends MouseMotionAdapter { + public void mouseDragged(MouseEvent ev) { + if (gotADragEvent) + return; + + Point mousePt = ev.getPoint(); + Dimension csize = getSize(); + boolean inBounds = + (mousePt.x >= 0 && mousePt.x <= csize.width && + mousePt.y >= 0 && mousePt.y <= csize.height); + if (!inBounds) { + setBackground(Color.green); + } + gotADragEvent = true; + } + } + + class PressReleaseAdapter extends MouseAdapter { + public void mousePressed(MouseEvent ev) { + gotADragEvent = false; + } + + public void mouseReleased(MouseEvent ev) { + setBackground(colorNormal); + } + } + + public Dimension preferredSize() { + return new Dimension(50, 50); + } +} + +class MouseFrame extends Frame { + public MouseFrame() { + super("MouseDragTest"); + new MouseChecker(this); + setLayout(new FlowLayout()); + add(new HeavySquare(Color.red)); + add(new HeavySquare(Color.blue)); + setBounds(new Rectangle(20, 20, 400, 300)); + } +} + +public class MouseDragTest { + static Frame TestFrame; + + public MouseDragTest() { + TestFrame = new MouseFrame(); + } + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. A frame with two boxes will appear. Click and drag _very_ quickly + off one of the components. You will know you were quick enough + when the component you dragged off of turns green + 2. Repeat this several times on both boxes, ensuring you get them + to turn green. The components should revert to their original + color when you release the mouse + 3. The test FAILS if the component doesn't revert to original + color, else PASS. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(new MouseFrame()) + .build() + .awaitAndCheck(); + } +} + +class MouseChecker implements MouseListener, MouseMotionListener { + private boolean isPressed = false; + private MouseEvent evPrev = null; + private MouseEvent evPrevPrev = null; + + public MouseChecker(Component comp) { + comp.addMouseListener(this); + comp.addMouseMotionListener(this); + } + + private void recordEv(MouseEvent ev) { + evPrevPrev = evPrev; + evPrev = ev; + } + + private synchronized void failure(String str) { + PassFailJFrame.forceFail("Test Failed : "+str); + } + + public void mouseClicked(MouseEvent ev) { + if (!(evPrev.getID() == MouseEvent.MOUSE_RELEASED && + evPrevPrev.getID() == MouseEvent.MOUSE_PRESSED)) { + failure("Got mouse click without press/release preceding."); + } + recordEv(ev); + } + + public void mousePressed(MouseEvent ev) { + recordEv(ev); + if (isPressed) { + failure("Got two mouse presses without a release."); + } + isPressed = true; + } + + public void mouseReleased(MouseEvent ev) { + recordEv(ev); + if (!isPressed) { + failure("Got mouse release without being pressed."); + } + isPressed = false; + } + + public void mouseEntered(MouseEvent ev) { + recordEv(ev); + Point mousePt = ev.getPoint(); + Component comp = (Component) ev.getSource(); + Dimension size = comp.getSize(); + boolean inBounds = + (mousePt.x >= 0 && mousePt.x <= size.width && + mousePt.y >= 0 && mousePt.y <= size.height); + + if (!inBounds) { + failure("Got mouse entered, but mouse not inside component."); + } + } + + public void mouseExited(MouseEvent ev) { + recordEv(ev); + Point mousePt = ev.getPoint(); + Component comp = (Component) ev.getSource(); + if (comp instanceof Frame) { + return; + } + Dimension size = comp.getSize(); + boolean isOnChild = (comp != comp.getComponentAt(mousePt)); + boolean inBounds = + (mousePt.x >= 0 && mousePt.x <= size.width && + mousePt.y >= 0 && mousePt.y <= size.height); + if (!isOnChild && inBounds) { + failure("Got mouse exit, but mouse still inside component."); + } + } + + public void mouseDragged(MouseEvent ev) { + recordEv(ev); + if (!isPressed) { + failure("Got drag without a press first."); + } + } + + public void mouseMoved(MouseEvent ev) { + recordEv(ev); + } +} diff --git a/test/jdk/java/awt/Mouse/MouseEnterExitTest.java b/test/jdk/java/awt/Mouse/MouseEnterExitTest.java new file mode 100644 index 0000000000000..059ad5c054829 --- /dev/null +++ b/test/jdk/java/awt/Mouse/MouseEnterExitTest.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Robot; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +/* + * @test + * @bug 4050138 + * @key headful + * @summary Test to verify Lightweight components don't get + * enter/exit during drags + * @run main MouseEnterExitTest + */ + +class LWSquare extends Container { + int width; + int height; + + public LWSquare(Color color, int w, int h) { + setBackground(color); + setLayout(new FlowLayout()); + width = w; + height = h; + addMouseListener(new EnterExitAdapter(this)); + setName("LWSquare-" + color.toString()); + } + + public void paint(Graphics g) { + g.setColor(getBackground()); + g.fillRect(0, 0, getSize().width, getSize().height); + super.paint(g); + } + + public Dimension getPreferredSize() { + return new Dimension(width, height); + } + + public Cursor getCursor() { + return new Cursor(Cursor.CROSSHAIR_CURSOR); + } +} + +class MouseFrame extends Frame { + public LWSquare lw; + + public MouseFrame() { + super("MouseEnterExitTest"); + setLayout(new FlowLayout()); + + lw = new LWSquare(Color.red, 75, 75); + add(lw); + setBounds(50, 50, 300, 200); + setVisible(true); + System.out.println(getInsets()); + + addMouseListener(new EnterExitAdapter(this)); + addWindowListener( + new WindowAdapter() { + public void windowClosing(WindowEvent ev) { + dispose(); + } + } + ); + addKeyListener( + new KeyAdapter() { + public void keyPressed(KeyEvent ev) { + MouseEnterExitTest.getFrame().setTitle("MouseEnterExitTest"); + } + } + ); + } +} + + +public class MouseEnterExitTest { + static MouseFrame testFrame; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + + robot.setAutoDelay(100); + try { + EventQueue.invokeAndWait(() -> testFrame = new MouseFrame()); + if (testFrame.lw.getBackground() != Color.red) { + throw new RuntimeException("Initial Background color not matching"); + } + robot.waitForIdle(); + robot.delay(100); + EventQueue.invokeAndWait(() -> robot.mouseMove( + testFrame.getLocationOnScreen().x + testFrame.getSize().width / 2, + testFrame.getLocationOnScreen().y + testFrame.getSize().height / 2)); + robot.waitForIdle(); + robot.delay(100); + + if (testFrame.lw.getBackground() != Color.green) { + throw new RuntimeException("Initial Background color not matching"); + } + EventQueue.invokeAndWait(() -> robot.mouseMove( + testFrame.getLocationOnScreen().x + testFrame.getSize().width * 2, + testFrame.getLocationOnScreen().y + testFrame.getSize().height / 2)); + robot.waitForIdle(); + robot.delay(100); + + if (testFrame.lw.getBackground() != Color.red) { + throw new RuntimeException("Initial Background color not matching"); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (testFrame != null) { + testFrame.dispose(); + } + }); + } + } + + public static Frame getFrame() { + return testFrame; + } +} + +class EnterExitAdapter extends MouseAdapter { + Component compToColor; + Color colorNormal; + + EnterExitAdapter(Component comp) { + compToColor = comp; + colorNormal = comp.getBackground(); + } + + public void mouseEntered(MouseEvent ev) { + compToColor.setBackground(Color.green); + compToColor.repaint(); + } + + public void mouseExited(MouseEvent ev) { + compToColor.setBackground(colorNormal); + compToColor.repaint(); + } +} diff --git a/test/jdk/java/awt/Mouse/MouseEnterExitTest2.java b/test/jdk/java/awt/Mouse/MouseEnterExitTest2.java new file mode 100644 index 0000000000000..e09ac333447f6 --- /dev/null +++ b/test/jdk/java/awt/Mouse/MouseEnterExitTest2.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.GridLayout; +import java.awt.Rectangle; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +/* + * @test + * @bug 4150851 + * @summary Tests enter and exit events when a lightweight component is on a border + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MouseEnterExitTest2 + */ + +public class MouseEnterExitTest2 { + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Verify that white component turns black whenever mouse enters the frame, + except when it enters the red rectangle. + 2. When the mouse enters the red part of the frame the component should stay white. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(EntryExitTest.initialize()) + .build() + .awaitAndCheck(); + } +} + +class EntryExitTest extends Component { + boolean inWin; + + public Dimension getPreferredSize() { + return new Dimension(200, 150); + } + + public void paint(Graphics g) { + Color c1, c2; + String s; + if (inWin) { + c1 = Color.black; + c2 = Color.white; + s = "IN"; + } else { + c2 = Color.black; + c1 = Color.white; + s = "OUT"; + } + g.setColor(c1); + Rectangle r = getBounds(); + g.fillRect(0, 0, r.width, r.height); + g.setColor(c2); + g.drawString(s, r.width / 2, r.height / 2); + } + + public static Frame initialize() { + EntryExitTest test = new EntryExitTest(); + MouseListener frameEnterExitListener = new MouseAdapter() { + public void mouseEntered(MouseEvent e) { + test.inWin = true; + test.repaint(); + } + + public void mouseExited(MouseEvent e) { + test.inWin = false; + test.repaint(); + } + }; + + Frame f = new Frame("Mouse Modifier Test"); + + f.add(test); + Component jc = new Component() { + public Dimension getPreferredSize() { + return new Dimension(100, 50); + } + + public void paint(Graphics g) { + Dimension d = getSize(); + g.setColor(Color.red); + g.fillRect(0, 0, d.width, d.height); + } + }; + final Container cont = new Container() { + public Dimension getPreferredSize() { + return new Dimension(100, 100); + } + }; + cont.setLayout(new GridLayout(2, 1)); + cont.add(jc); + jc.addMouseListener(new MouseAdapter() { + public void mouseEntered(MouseEvent e) { + //System.out.println("Component entered"); + } + public void mouseExited(MouseEvent e) { + //System.out.println("Component exited"); + } + }); + + f.add(cont, BorderLayout.NORTH); + f.addMouseListener(frameEnterExitListener); + f.pack(); + return f; + } +} diff --git a/test/jdk/java/awt/Mouse/MouseEnterExitTest3.java b/test/jdk/java/awt/Mouse/MouseEnterExitTest3.java new file mode 100644 index 0000000000000..d5096d7acf022 --- /dev/null +++ b/test/jdk/java/awt/Mouse/MouseEnterExitTest3.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import javax.swing.JButton; + +/* + * @test + * @bug 4431868 + * @summary Tests that hw container doesn't receive mouse enter/exit events when mouse + * is moved between its lw and hw children + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MouseEnterExitTest3 + */ + +public class MouseEnterExitTest3 { + static final Button button = new Button("Button"); + static final JButton jbutton = new JButton("JButton"); + static final Frame frame = new Frame("Mouse Enter/Exit Test"); + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Move the mouse between Button and JButton + 2. Verify that the frame doesn't receive enter/exit events + (Enter/exit events are dumped to the area below) + 4. If you see enter/exit events dumped the test fails + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .logArea(4) + .build() + .awaitAndCheck(); + } + + final static MouseListener listener = new MouseAdapter() { + public void mouseEntered(MouseEvent e) { + PassFailJFrame.log(e.toString()); + } + + public void mouseExited(MouseEvent e) { + PassFailJFrame.log(e.toString()); + } + }; + + public static Frame initialize() { + frame.setLayout(new GridLayout(2, 1)); + frame.add(button); + frame.add(jbutton); + frame.addMouseListener(listener); + frame.pack(); + return frame; + } +} diff --git a/test/jdk/java/awt/Mouse/MouseEnterExitTest4.java b/test/jdk/java/awt/Mouse/MouseEnterExitTest4.java new file mode 100644 index 0000000000000..2ee3993ae4ede --- /dev/null +++ b/test/jdk/java/awt/Mouse/MouseEnterExitTest4.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Robot; +import java.awt.Window; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +/* + * @test + * @bug 4431868 + * @key headful + * @summary Tests that window totally obscured by its child doesn't receive + * enter/exit events when located over another frame + * @run main MouseEnterExitTest4 + */ + +public class MouseEnterExitTest4 { + static Button button = new Button("Button"); + static Frame frame = new Frame("Mouse Enter/Exit test"); + static Window window = new Window(frame); + static MouseListener listener = new MouseAdapter() { + public void mouseEntered(MouseEvent e) { + throw new RuntimeException("Test failed due to Mouse Enter event"); + } + + public void mouseExited(MouseEvent e) { + throw new RuntimeException("Test failed due to Mouse Exit event"); + } + }; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + + robot.setAutoDelay(100); + try { + EventQueue.invokeAndWait(() -> { + button.setBackground(Color.red); + window.add(button); + frame.setBounds(100, 100, 300, 300); + window.setBounds(200, 200, 100, 100); + window.addMouseListener(listener); + window.setVisible(true); + frame.setVisible(true); + }); + robot.waitForIdle(); + robot.delay(200); + EventQueue.invokeAndWait(() -> robot.mouseMove( + frame.getLocationOnScreen().x + frame.getSize().width / 2, + frame.getLocationOnScreen().y + frame.getSize().height / 2)); + robot.waitForIdle(); + robot.delay(200); + EventQueue.invokeAndWait(() -> robot.mouseMove( + window.getLocationOnScreen().x + window.getSize().width * 2, + window.getLocationOnScreen().y + window.getSize().height / 2)); + robot.waitForIdle(); + robot.delay(500); + System.out.println("Test Passed"); + + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + if (window != null) { + window.dispose(); + } + }); + } + } +} diff --git a/test/jdk/java/awt/Mouse/MousePressedTest.java b/test/jdk/java/awt/Mouse/MousePressedTest.java new file mode 100644 index 0000000000000..721d69bd5ddf0 --- /dev/null +++ b/test/jdk/java/awt/Mouse/MousePressedTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Container; +import java.awt.GridLayout; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JScrollPane; +import javax.swing.JToggleButton; + +/* + * @test + * @bug 4268759 + * @summary Tests whether clicking on the edge of a lightweight button + * causes sticking behavior + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MousePressedTest + */ + +public class MousePressedTest { + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Click and hold on the very bottom border (2-pixel-wide border) of the + JButton. Then drag the mouse straight down out of the JButton and + into the JRadioButton, and release the mouse button + 2. If the component remains highlighted as if the mouse button is still + down, the test fails + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .build() + .awaitAndCheck(); + } + + public static JFrame initialize() { + JFrame f = new JFrame("JButton Test"); + JPanel p = new JPanel(); + p.setLayout(new GridLayout(2, 2)); + JButton b = new JButton("JButton"); + p.add(b); + JCheckBox cb = new JCheckBox("JCheckBox"); + p.add(cb); + JRadioButton rb = new JRadioButton("JRadioButton"); + p.add(rb); + p.add(new JToggleButton("JToggleButton")); + + JScrollPane j = new JScrollPane(p, + JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, + JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); + + Container c = f.getContentPane(); + c.setLayout(new GridLayout(1, 1)); + c.add(j); + f.pack(); + return f; + } +} diff --git a/test/jdk/java/awt/MouseInfo/ContainerResizeMousePositionTest.java b/test/jdk/java/awt/MouseInfo/ContainerResizeMousePositionTest.java new file mode 100644 index 0000000000000..72aa4d93a5c71 --- /dev/null +++ b/test/jdk/java/awt/MouseInfo/ContainerResizeMousePositionTest.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/* + * @test + * @key headful + * @bug 4009555 + * @summary Unit test for a new method in Container class: getMousePosition(boolean) + * while Container resized. + */ + +public class ContainerResizeMousePositionTest { + private static Frame frame; + private static Button button; + private static Robot robot; + private static volatile Point frameLocation; + private static volatile Point newLoc; + private static boolean testSucceeded = false; + + private static final CountDownLatch eventCaught = new CountDownLatch(1); + + public static void main(String[] args) throws Exception { + try { + robot = new Robot(); + EventQueue.invokeAndWait(() -> createAndShowUI()); + robot.waitForIdle(); + robot.delay(1000); + testUI(); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void createAndShowUI() { + frame = new Frame("Testing getMousePosition() after resize"); + button = new Button("Button"); + frame.setLayout(new BorderLayout()); + frame.add(button); + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private static void testUI() throws Exception { + EventQueue.invokeAndWait(() -> { + frameLocation = frame.getLocationOnScreen(); + newLoc = new Point(frame.getWidth() + 10, frame.getHeight() + 10); + }); + + robot.mouseMove(frameLocation.x + newLoc.x, frameLocation.y + newLoc.y); + EventQueue.invokeAndWait(() -> { + button.addComponentListener(new ResizeAdapter()); + frame.setSize(frame.getWidth() * 2, frame.getHeight() * 2); + frame.validate(); + }); + robot.waitForIdle(); + robot.delay(500); + + if (!eventCaught.await(2, TimeUnit.SECONDS)) { + throw new RuntimeException("componentResized Event isn't" + + " received within a timeout"); + } + + if (!testSucceeded) { + throw new RuntimeException("Container.getMousePosition(boolean)" + + " returned incorrect result while Container resized"); + } + } + + static class ResizeAdapter extends ComponentAdapter { + int testStageCounter = 0; + @Override + public void componentResized(ComponentEvent e) { + Point pTrue = frame.getMousePosition(true); + if (frame.getMousePosition(false) == null) { + testStageCounter++; + System.out.println(""" + TEST STAGE 1 PASSED: + Container.getMousePosition(false) + returned NULL over Child Component + during resize. + """); + } + if (pTrue != null) { + testStageCounter++; + System.out.println(""" + TEST STAGE 2 PASSED: + Container.getMousePosition(true) + returned NON-NULL over Child Component + during resize. + """); + } + if (pTrue != null && pTrue.x == newLoc.x && pTrue.y == newLoc.y) { + testStageCounter++; + System.out.println(""" + TEST STAGE 3 PASSED: + Container.getMousePosition(true) + returned correct result over Child Component + during resize. + """); + } + testSucceeded = testStageCounter == 3; + eventCaught.countDown(); + } + } +} diff --git a/test/jdk/java/awt/Multiscreen/DeviceIdentificationTest/DeviceIdentificationTest.java b/test/jdk/java/awt/Multiscreen/DeviceIdentificationTest/DeviceIdentificationTest.java index 425588d669351..684ac9e253860 100644 --- a/test/jdk/java/awt/Multiscreen/DeviceIdentificationTest/DeviceIdentificationTest.java +++ b/test/jdk/java/awt/Multiscreen/DeviceIdentificationTest/DeviceIdentificationTest.java @@ -24,7 +24,7 @@ * @test * @bug 6614214 8198613 * @summary Verifies that we enter the fs mode on the correct screen. - * Here is how to test: start the test on on a multi-screen system. + * Here is how to test: start the test on a multi-screen system. * Verify that the display is correctly tracked by dragging the frame back * and forth between screens. Then verify that the correct device enters * the full-screen mode - when "Enter FS mode" is pressed it should enter on diff --git a/test/jdk/java/awt/Multiscreen/DialogTest.java b/test/jdk/java/awt/Multiscreen/DialogTest.java new file mode 100644 index 0000000000000..fbd72bfd6f0a1 --- /dev/null +++ b/test/jdk/java/awt/Multiscreen/DialogTest.java @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.GridLayout; +import java.awt.Label; +import java.awt.Panel; +import java.awt.Point; +import java.awt.ScrollPane; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.WindowConstants; + +import jtreg.SkippedException; + +/* + * @test + * @bug 4368500 + * @key multimon + * @summary Dialog needs a constructor with GraphicsConfiguration + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame + * @run main/manual DialogTest + */ + +public class DialogTest { + static GraphicsDevice[] gds; + + private static Frame f; + private static Frame dummyFrame = new Frame(); + private static Dialog dummyDialog = new Dialog(dummyFrame); + + public static void main(String[] args) throws Exception { + gds = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices(); + if (gds.length < 2) { + throw new SkippedException("You have only one monitor in your system" + + " - test skipped"); + } + + String INSTRUCTIONS = """ + This test tests the multiscreen functionality of Dialogs and JDialogs. + You should see the message "X screens detected", where X + is the number of screens on your system. If X is incorrect, press Fail. + + In the test window, there are a list of buttons representing each + type of dialog for each screen. + If there aren't buttons for every screen in your system, press Fail. + + Press each button, and the indicated type of dialog should appear + on the indicated screen. + Modal dialogs should not allow to click on the Instructions or + DialogTest windows. + + The buttons turn yellow once they have been pressed, to keep track + of test progress. + + If all Dialogs appear correctly, press Pass. + If Dialogs appear on the wrong screen or don't behave in + proper modality, press Fail."""; + + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(40) + .logArea(5) + .testUI(DialogTest::init) + .build() + .awaitAndCheck(); + } + + public static Frame init() { + PassFailJFrame.log(gds.length + " screens detected."); + f = new Frame("DialogTest UI"); + f.setSize(400, 400); + MyScrollPane sp = new MyScrollPane(); + + Panel p = new Panel(); + p.setLayout(new GridLayout(0, 1)); + + for (int i = 0; i < gds.length; i++) { + Button btn; + + //screen # , modal, frame-owned, swing + btn = new MyButton(new DialogInfo(i, false, false, false)); + p.add(btn); + + btn = new MyButton(new DialogInfo(i, true, false, false)); + p.add(btn); + + btn = new MyButton(new DialogInfo(i, false, true, false)); + p.add(btn); + + btn = new MyButton(new DialogInfo(i, true, true, false)); + p.add(btn); + + btn = new MyButton(new DialogInfo(i, false, false, true)); + p.add(btn); + + btn = new MyButton(new DialogInfo(i, true, false, true)); + p.add(btn); + + btn = new MyButton(new DialogInfo(i, false, true, true)); + p.add(btn); + + btn = new MyButton(new DialogInfo(i, true, true, true)); + p.add(btn); + + } + sp.add(p); + f.add(sp); + return f; + } + + static class MyScrollPane extends ScrollPane { + @Override + public Dimension getPreferredSize() { + return f.getSize(); + } + } + + static class MyButton extends Button { + public MyButton(DialogInfo info) { + setLabel(info.toString()); + addActionListener(new PutupDialog(info)); + } + } + + static class PutupDialog implements ActionListener { + DialogInfo info; + + public PutupDialog(DialogInfo info) { + this.info = info; + } + + @Override + public void actionPerformed(ActionEvent e) { + ((Button) (e.getSource())).setBackground(Color.yellow); + Dialog d = info.createDialog(); + d.show(); + } + } + + static class DialogInfo { + int num; + boolean modal; + boolean frameOwned; + boolean swing; + + public DialogInfo(int num, boolean modal, boolean frameOwned, boolean swing) { + this.num = num; + this.modal = modal; + this.frameOwned = frameOwned; + this.swing = swing; + } + + public Dialog createDialog() { + GraphicsConfiguration gc = gds[num].getDefaultConfiguration(); + + Dialog d; + + if (swing) { + if (frameOwned) { + d = new JDialog(dummyFrame, toString(), modal, gc); + } else { + d = new JDialog(dummyDialog, toString(), modal, gc); + } + + ((JDialog) d).setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE); + if (modal) { + ((JDialog) d).getContentPane().add(new JLabel("Check that I am modal!")); + } + } else { + if (frameOwned) { + d = new Dialog(dummyFrame, toString(), modal, gc); + } else { + d = new Dialog(dummyDialog, toString(), modal, gc); + } + + d.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + e.getComponent().hide(); + } + }); + if (modal) { + d.add(new Label("Check that I am modal!")); + } + } + + d.setLocation(new Point((int) (gc.getBounds().getX() + 20) + , (int) (gc.getBounds().getY() + 20))); + d.setSize(300, 100); + + return d; + } + + public String toString() { + return "Screen " + num + (frameOwned ? " Frame-owned" : " Dialog-owned") + + (modal ? " modal " : " non-modal ") + + (swing ? "JDialog" : "Dialog"); + } + } +} + + diff --git a/test/jdk/java/awt/Multiscreen/FillThisScreen.java b/test/jdk/java/awt/Multiscreen/FillThisScreen.java new file mode 100644 index 0000000000000..542127827ecd8 --- /dev/null +++ b/test/jdk/java/awt/Multiscreen/FillThisScreen.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Frame; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.GridLayout; +import java.awt.Rectangle; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import jtreg.SkippedException; + +/* + * @test + * @bug 4356756 + * @key multimon + * @summary Return all screen devices for physical and virtual display devices + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame + * @run main/manual FillThisScreen + */ + +public class FillThisScreen { + private static Frame f; + private static Button b; + private static Rectangle oldSize; + private static boolean fillmode = true; + static GraphicsDevice[] gs; + + public static void main(String[] args) throws Exception { + gs = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices(); + if (gs.length < 2) { + throw new SkippedException("You have only one monitor in your system" + + " - test skipped"); + } + + String INSTRUCTIONS = """ + This test is for testing the bounds of a multimonitor system. + You will see a Frame with several buttons: one marked 'Fill + This Screen' and an additional button for each display on your system. + + First, drag the Frame onto each display and click the + 'Fill This Screen' button. + + The Frame should resize to take up the entire display area + of the screen it is on, and the button text changes to say, + 'Get Smaller'. + + Click the button again to restore the Frame. + + Next, use the 'Move to screen' buttons to move the Frame to + each display and again click the 'Fill This Screen' button. + + If the number of 'Move to Screen' buttons is not equals to + the number of screens on your system, the test fails. + + If the Frame always correctly resizes to take up ONLY the + entire screen it is on (and not a different screen, or all + screens), the test passes else it fails."""; + + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(FillThisScreen::init) + .build() + .awaitAndCheck(); + } + + public static Frame init() { + Button tempBtn; + + f = new Frame("Drag Me Around"); + f.setLayout(new GridLayout(0, 1)); + + b = new Button("Fill This Screen"); + b.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (fillmode) { + oldSize = f.getBounds(); + Rectangle r = f.getGraphicsConfiguration().getBounds(); + f.setBounds(r); + b.setLabel("Get Smaller"); + } else { + f.setBounds(oldSize); + b.setLabel("Fill This Screen"); + } + fillmode = !fillmode; + } + }); + f.add(b); + + for (int i = 0; i < gs.length; i++) { + tempBtn = new Button("Move to screen:" + i); + tempBtn.addActionListener(new WinMover(i)); + f.add(tempBtn); + } + f.setSize(300, 100); + return f; + } + + private static class WinMover implements ActionListener { + int scrNum; + + public WinMover(int scrNum) { + this.scrNum = scrNum; + } + + public void actionPerformed(ActionEvent e) { + Rectangle newBounds = gs[scrNum].getDefaultConfiguration().getBounds(); + f.setLocation(newBounds.x + newBounds.width / 2, + newBounds.y + newBounds.height / 2); + } + + } +} diff --git a/test/jdk/java/awt/Multiscreen/IMCandidateWindowTest.java b/test/jdk/java/awt/Multiscreen/IMCandidateWindowTest.java new file mode 100644 index 0000000000000..6af3188156db9 --- /dev/null +++ b/test/jdk/java/awt/Multiscreen/IMCandidateWindowTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.FlowLayout; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Rectangle; + +import javax.swing.JFrame; +import javax.swing.JTextField; + +import jtreg.SkippedException; + +/* + * @test + * @bug 4805862 + * @key multimon + * @requires (os.family == "windows") + * @summary Tests IM candidate window is positioned correctly for the + * text components inside a window in multiscreen configurations, if + * this window has negative coordinates + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame + * @run main/manual IMCandidateWindowTest + */ + +public class IMCandidateWindowTest { + static GraphicsConfiguration gc; + + public static void main(String[] args) throws Exception { + GraphicsDevice[] gds = GraphicsEnvironment.getLocalGraphicsEnvironment() + .getScreenDevices(); + if (gds.length < 2) { + throw new SkippedException("You have only one monitor in your system" + + " - test skipped"); + } + + GraphicsDevice gd = null; + + for (int i = 0; i < gds.length; i++) { + gc = gds[i].getDefaultConfiguration(); + if ((gc.getBounds().x < 0) || (gc.getBounds().y < 0)) { + gd = gds[i]; + break; + } + } + + if (gd == null) { + // no screens with negative coords + throw new SkippedException("No screens with negative coords - test skipped"); + } + + String INSTRUCTIONS = """ + This test is for windows + Test requirements: installed support for asian languages + Chinese (PRC) w/ Chinese QuanPing input method. + Multiscreen environment where one of the monitors has negative coords + Go to the text field in the opened Frame. Switch to Chinese language and + start typing "ka". + Note, that IM helper window is appeared. + If this window is appeared near the text field, press PASS button. + If this window is appeared at the edge of the screen or on another + screen, press FAIL button"""; + + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(IMCandidateWindowTest::createUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createUI() { + Rectangle b = gc.getBounds(); + + JFrame f = new JFrame("Frame", gc); + f.setBounds(b.x + b.width / 2 - 150, b.y + b.height / 2 - 100, 300, 200); + f.getContentPane().setLayout(new FlowLayout()); + JTextField tf = new JTextField(10); + f.getContentPane().add(tf); + return f; + } +} diff --git a/test/jdk/java/awt/Paint/ButtonRepaint.java b/test/jdk/java/awt/Paint/ButtonRepaint.java index 77cd1a4649ddb..260e6c1097fd2 100644 --- a/test/jdk/java/awt/Paint/ButtonRepaint.java +++ b/test/jdk/java/awt/Paint/ButtonRepaint.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,28 +21,32 @@ * questions. */ - -import java.awt.*; +import java.awt.Button; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Graphics; /** * @test * @key headful * @bug 7090424 - * @author Sergey Bylokhov */ public final class ButtonRepaint extends Button { public static void main(final String[] args) { for (int i = 0; i < 10; ++i) { - final Frame frame = new Frame(); - frame.setSize(300, 300); - frame.setLocationRelativeTo(null); - ButtonRepaint button = new ButtonRepaint(); - frame.add(button); - frame.setVisible(true); - sleep(); - button.test(); - frame.dispose(); + Frame frame = new Frame(); + try { + frame.setSize(300, 300); + frame.setLocationRelativeTo(null); + ButtonRepaint button = new ButtonRepaint(); + frame.add(button); + frame.setVisible(true); + sleep(); + button.test(); + } finally { + frame.dispose(); + } } } diff --git a/test/jdk/java/awt/Paint/CheckboxRepaint.java b/test/jdk/java/awt/Paint/CheckboxRepaint.java index 409c11f6f4d85..5522497dc2a6e 100644 --- a/test/jdk/java/awt/Paint/CheckboxRepaint.java +++ b/test/jdk/java/awt/Paint/CheckboxRepaint.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,27 +21,32 @@ * questions. */ -import java.awt.*; +import java.awt.Checkbox; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Graphics; /** * @test * @key headful * @bug 7090424 - * @author Sergey Bylokhov */ public final class CheckboxRepaint extends Checkbox { public static void main(final String[] args) { for (int i = 0; i < 10; ++i) { - final Frame frame = new Frame(); - frame.setSize(300, 300); - frame.setLocationRelativeTo(null); - CheckboxRepaint checkbox = new CheckboxRepaint(); - frame.add(checkbox); - frame.setVisible(true); - sleep(); - checkbox.test(); - frame.dispose(); + Frame frame = new Frame(); + try { + frame.setSize(300, 300); + frame.setLocationRelativeTo(null); + CheckboxRepaint checkbox = new CheckboxRepaint(); + frame.add(checkbox); + frame.setVisible(true); + sleep(); + checkbox.test(); + } finally { + frame.dispose(); + } } } diff --git a/test/jdk/java/awt/Paint/LabelRepaint.java b/test/jdk/java/awt/Paint/LabelRepaint.java index 7131f6d4f0f52..56e096a2457a6 100644 --- a/test/jdk/java/awt/Paint/LabelRepaint.java +++ b/test/jdk/java/awt/Paint/LabelRepaint.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,21 +30,23 @@ * @test * @key headful * @bug 7090424 - * @author Sergey Bylokhov */ public final class LabelRepaint extends Label { public static void main(final String[] args) { for (int i = 0; i < 10; ++i) { - final Frame frame = new Frame(); - frame.setSize(300, 300); - frame.setLocationRelativeTo(null); - LabelRepaint label = new LabelRepaint(); - frame.add(label); - frame.setVisible(true); - sleep(); - label.test(); - frame.dispose(); + Frame frame = new Frame(); + try { + frame.setSize(300, 300); + frame.setLocationRelativeTo(null); + LabelRepaint label = new LabelRepaint(); + frame.add(label); + frame.setVisible(true); + sleep(); + label.test(); + } finally { + frame.dispose(); + } } } diff --git a/test/jdk/java/awt/Paint/ListRepaint.java b/test/jdk/java/awt/Paint/ListRepaint.java index 08cf06d63f66a..a2d9c77a95b52 100644 --- a/test/jdk/java/awt/Paint/ListRepaint.java +++ b/test/jdk/java/awt/Paint/ListRepaint.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,26 +30,27 @@ * @test * @key headful * @bug 7090424 - * @author Sergey Bylokhov */ public final class ListRepaint extends List { - static ListRepaint listRepaint; - static Frame frame; - - public static void main(final String[] args) throws Exception { + public static void main(final String[] args) { for (int i = 0; i < 10; ++i) { + Frame frame = new Frame(); try { - EventQueue.invokeLater(ListRepaint::createAndShowGUI); + frame.setSize(300, 300); + frame.setLocationRelativeTo(null); + ListRepaint list = new ListRepaint(); + list.add("1"); + list.add("2"); + list.add("3"); + list.add("4"); + list.select(0); + frame.add(list); + frame.setVisible(true); sleep(); - EventQueue.invokeAndWait(listRepaint::test); + list.test(); } finally { - EventQueue.invokeAndWait(() -> { - if (frame != null) { - frame.dispose(); - frame = null; - } - }); + frame.dispose(); } } } @@ -61,22 +62,6 @@ private static void sleep() { } } - static void createAndShowGUI() { - frame = new Frame(); - frame.setSize(300, 300); - frame.setLocationRelativeTo(null); - - listRepaint = new ListRepaint(); - listRepaint.add("1"); - listRepaint.add("2"); - listRepaint.add("3"); - listRepaint.add("4"); - listRepaint.select(0); - - frame.add(listRepaint); - frame.setVisible(true); - } - @Override public void paint(final Graphics g) { super.paint(g); diff --git a/test/jdk/java/awt/Panel/PanelRepaint/PanelRepaint.java b/test/jdk/java/awt/Panel/PanelRepaint/PanelRepaint.java new file mode 100644 index 0000000000000..da8de3947be9a --- /dev/null +++ b/test/jdk/java/awt/Panel/PanelRepaint/PanelRepaint.java @@ -0,0 +1,455 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4148078 + * @summary Repainting problems in scrolled panel + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual PanelRepaint + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Scrollbar; +import java.awt.TextField; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; + +public class PanelRepaint extends Panel implements FocusListener { + static ScrollPanel sPanel; + static Panel panel; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Using scrollbars or tab keys to scroll the panel and + the panel is messy sometimes, e.g. one row bumps into + another. If all components painted correctly, the test passes. + Otherwise, the test fails. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(PanelRepaint::createUI) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame f = new Frame("Panel Repaint Test"); + f.setLayout(new FlowLayout()); + f.setSize(620, 288); + PanelRepaint pr = new PanelRepaint(); + + panel = new Panel(); + panel.setLayout(null); + panel.setSize(500, 500); + sPanel = new ScrollPanel(panel); + + Button btn = new Button("Open"); + pr.addComp(btn); + btn.setBounds(400, 10, 60, 20); + btn.setActionCommand("OPEN"); + + Button btn1 = new Button("Close"); + pr.addComp(btn1); + btn1.setBounds(400, 50, 60, 20); + btn1.setActionCommand("CLOSE"); + + TextField t1 = new TextField("1"); + pr.addComp(t1); + t1.setBounds(10, 10, 100, 20); + TextField t2 = new TextField("2"); + pr.addComp(t2); + t2.setBounds(10, 50, 100, 20); + TextField t3 = new TextField("3"); + pr.addComp(t3); + t3.setBounds(10, 90, 100, 20); + TextField t4 = new TextField("4"); + pr.addComp(t4); + t4.setBounds(10, 130, 100, 20); + TextField t5 = new TextField("5"); + pr.addComp(t5); + t5.setBounds(10, 170, 100, 20); + TextField t6 = new TextField("6"); + pr.addComp(t6); + t6.setBounds(10, 210, 100, 20); + TextField t7 = new TextField("7"); + pr.addComp(t7); + t7.setBounds(10, 250, 100, 20); + TextField t8 = new TextField("8"); + pr.addComp(t8); + t8.setBounds(10, 290, 100, 20); + TextField t9 = new TextField("9"); + pr.addComp(t9); + t9.setBounds(10, 330, 100, 20); + + TextField t11 = new TextField("1"); + pr.addComp(t11); + t11.setBounds(120, 10, 100, 20); + TextField t12 = new TextField("2"); + pr.addComp(t12); + t12.setBounds(120, 50, 100, 20); + TextField t13 = new TextField("3"); + pr.addComp(t13); + t13.setBounds(120, 90, 100, 20); + TextField t14 = new TextField("4"); + pr.addComp(t14); + t14.setBounds(120, 130, 100, 20); + TextField t15 = new TextField("5"); + pr.addComp(t15); + t15.setBounds(120, 170, 100, 20); + TextField t16 = new TextField("6"); + pr.addComp(t16); + t16.setBounds(120, 210, 100, 20); + TextField t17 = new TextField("7"); + pr.addComp(t17); + t17.setBounds(120, 250, 100, 20); + TextField t18 = new TextField("8"); + pr.addComp(t18); + t18.setBounds(120, 290, 100, 20); + TextField t19 = new TextField("9"); + pr.addComp(t19); + t19.setBounds(120, 330, 100, 20); + + + TextField t21 = new TextField("1"); + pr.addComp(t21); + t21.setBounds(240, 10, 100, 20); + TextField t22 = new TextField("2"); + pr.addComp(t22); + t22.setBounds(240, 50, 100, 20); + TextField t23 = new TextField("3"); + pr.addComp(t23); + t23.setBounds(240, 90, 100, 20); + TextField t24 = new TextField("4"); + pr.addComp(t24); + t24.setBounds(240, 130, 100, 20); + TextField t25 = new TextField("5"); + pr.addComp(t25); + t25.setBounds(240, 170, 100, 20); + TextField t26 = new TextField("6"); + pr.addComp(t26); + t26.setBounds(240, 210, 100, 20); + TextField t27 = new TextField("7"); + pr.addComp(t27); + t27.setBounds(240, 250, 100, 20); + TextField t28 = new TextField("8"); + pr.addComp(t28); + t28.setBounds(240, 290, 100, 20); + TextField t29 = new TextField("9"); + pr.addComp(t29); + t29.setBounds(240, 330, 100, 20); + + pr.add(sPanel); + f.add(pr); + sPanel.setBounds(100, 100, 500, 250); + sPanel.doLayout(); + return f; + } + + public void addComp(Component c) { + panel.add(c); + c.addFocusListener(this); + } + + public void focusGained(FocusEvent e) { + sPanel.showComponent(e.getComponent()); + } + + public void focusLost(FocusEvent e) { + } +} + +class ScrollPanel extends Panel implements AdjustmentListener { + /** + * Constructor + */ + public ScrollPanel(Component c) { + setLayout(null); + setBackground(Color.lightGray); + add(hScroll = new Scrollbar(Scrollbar.HORIZONTAL)); + add(vScroll = new Scrollbar(Scrollbar.VERTICAL)); + add(square = new Panel()); + square.setBackground(Color.lightGray); + add(c); + } + + /** + * Scroll up/down/left/right to show the component specified + * + * @param comp is the component to be shown + */ + public void showComponent(Component comp) { + Component view = getComponent(3); + Rectangle viewRect = view.getBounds(); + Rectangle scrollRect = getBounds(); + Rectangle rect = comp.getBounds(); + while (comp != null) { + Component parent = comp.getParent(); + if (parent == null || parent == view) { + break; + } + Point p = parent.getLocation(); + rect.x += p.x; + rect.y += p.y; + comp = parent; + } + + int i = viewRect.y + rect.y; + int j = (viewRect.y + rect.y + rect.height + ScrollPanel.H_HEIGHT) + - (scrollRect.height); + + if (i < 0) { + vertUpdate(i); + } else if (j > 0) { + vertUpdate(j); + } + + i = viewRect.x + rect.x; + j = (viewRect.x + rect.x + rect.width + (V_WIDTH * 2)) - (scrollRect.width); + + if (i < 0) { + horzUpdate(i); + } else if (j > 0) { + horzUpdate(j); + } + } + + /** + * Returns the panel component of ScrollPanel + * + * @return the panel component of ScrollPanel + */ + public Component getScrolled() { + return getComponent(3); + } + + /** + * updates the scroll panel vertically with value i passed + * + * @param i the value to be updated with + */ + public void vertUpdate(int i) { + update(true, vScroll.getValue() + i); + } + + /** + * updates the scroll panel horizontally with value i passed + * + * @param i the value to be updated with + */ + public void horzUpdate(int i) { + update(false, hScroll.getValue() + i); + } + + /** + * updates the scroll panel vertically if bVert is true else horizontally + * + * @param n is the value + */ + public void update(boolean bVert, int n) { + if (n < 0) n = 0; + if (bVert) { + if (n > max.height) { + n = max.height; + } + if (offset.y != n) { + offset.y = n; + vScroll.setValue(n); + } + } else { + if (n > max.width) { + n = max.width; + } + if (offset.x != n) { + offset.x = n; + hScroll.setValue(n); + } + } + getScrolled().setLocation(-offset.x, -offset.y); + } + + /** + * Implementation of AdjustmentListener + */ + public void adjustmentValueChanged(AdjustmentEvent e) { + boolean bVert = e.getSource() == vScroll; + update(bVert, e.getValue()); + } + + /** + * Reimplementation of Component Methods + */ + public void addNotify() { + super.addNotify(); + vScroll.addAdjustmentListener(this); + hScroll.addAdjustmentListener(this); + } + + public void removeNotify() { + super.removeNotify(); + vScroll.removeAdjustmentListener(this); + hScroll.removeAdjustmentListener(this); + } + + public void setBounds(int x, int y, int w, int h) { + super.setBounds(x, y, w, h); + doLayout(); + } + + public void doLayout() { + Component c = getScrolled(); + Dimension d = c.getSize(); + if (d.width == 0 || d.height == 0) { + d = c.getPreferredSize(); + } + vert = 0; + horz = 0; + Dimension m = getSize(); + if (d.height > m.height || isScroll(true, m.height - horz, 0, d.height)) { + vert = V_WIDTH; + } + if (d.width + vert > m.width || isScroll(false, m.width - vert, 0, d.width)) { + horz = H_HEIGHT; + } + if (d.height + horz > m.height || isScroll(true, m.height - horz, 0, d.height)) { + vert = V_WIDTH; + } + if (d.width + vert > m.width || isScroll(false, m.width - vert, 0, d.width)) { + horz = H_HEIGHT; + } + if (horz != 0) { + if (m.width <= 0) { + m.width = 1; + } + hScroll.setBounds(0, m.height - H_HEIGHT, m.width - vert, H_HEIGHT); + hScroll.setValues(offset.x, m.width - vert, 0, d.width); + int i = d.width / 10; + if (i < 2) { + i = 2; + } + hScroll.setBlockIncrement(i); + i = d.width / 50; + if (i < 1) { + i = 1; + } + hScroll.setUnitIncrement(i); + max.width = d.width; + hScroll.setVisible(true); + } else { + offset.x = 0; + } + if (vert != 0) { + if (m.height <= 0) { + m.height = 1; + } + vScroll.setBounds(m.width - V_WIDTH, 0, V_WIDTH, m.height - horz); + vScroll.setValues(offset.y, m.height - horz, 0, d.height); + int i = d.height / 10; + if (i < 2) i = 2; + vScroll.setBlockIncrement(i); + i = d.height / 50; + if (i < 1) i = 1; + vScroll.setUnitIncrement(i); + max.height = d.height; + vScroll.setVisible(true); + } else { + offset.y = 0; + } + if (horz != 0 && vert != 0) { + square.setBounds(m.width - V_WIDTH, m.height - H_HEIGHT, V_WIDTH, H_HEIGHT); + square.setVisible(true); + } else { + square.setVisible(false); + } + c.setBounds(-offset.x, -offset.y, d.width, d.height); + c.repaint(); + updateScroll(true, offset.y); + updateScroll(false, offset.x); + } + + public Dimension getPreferredSize() { + return getScrolled().getPreferredSize(); + } + + public Dimension getMinimumSize() { + return getScrolled().getMinimumSize(); + } + + boolean isScroll(boolean bVert, int visible, int min, int max) { + int tot = max - min; + int net = tot - visible; + if (net <= 0) { + return false; + } + return true; + } + + void updateScroll(boolean bVert, int n) { + Component c = getScrolled(); + Dimension d = c.getSize(); + Dimension m = getSize(); + m.width -= vert; + m.height -= horz; + if (bVert) { + if (n >= 0 && d.height > m.height) { + if (n + m.height > d.height) + n = d.height - m.height; + } else + n = 0; + update(true, n); + } else { + if (n >= 0 && d.width > m.width) { + if (n + m.width > d.width) + n = d.width - m.width; + } else + n = 0; + update(false, n); + } + } + + static Scrollbar hScroll; + static Scrollbar vScroll; + static int vert = 0; + static int horz = 0; + + static Point offset = new Point(); + static Dimension max = new Dimension(); + // ScrollTimer timer; + static Component square; + final static int V_WIDTH = 17; + final static int H_HEIGHT = 17; +} diff --git a/test/jdk/java/awt/PopupMenu/ActivePopupCrashTest.java b/test/jdk/java/awt/PopupMenu/ActivePopupCrashTest.java new file mode 100644 index 0000000000000..51c12964e6295 --- /dev/null +++ b/test/jdk/java/awt/PopupMenu/ActivePopupCrashTest.java @@ -0,0 +1,220 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.Panel; +import java.awt.Point; +import java.awt.PopupMenu; +import java.awt.Robot; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import java.util.Hashtable; + +/* + * @test + * @bug 4214550 + * @summary Tests that there is no seg fault on repeatedly showing + * PopupMenu by right-clicking Label, Panel or Button + * @key headful + * @run main ActivePopupCrashTest + */ + +public class ActivePopupCrashTest { + private static Frame f; + private static Label l; + private static Button b; + private static Panel p; + + private static volatile Point labelCenter; + private static volatile Point buttonCenter; + private static volatile Point panelCenter; + + public static void main(String[] args) throws Exception { + final int REPEAT_COUNT = 5; + try { + Robot robot = new Robot(); + robot.setAutoDelay(50); + EventQueue.invokeAndWait(ActivePopupCrashTest::createAndShowUI); + robot.delay(1000); + + EventQueue.invokeAndWait(() -> { + labelCenter = getCenterPoint(l); + buttonCenter = getCenterPoint(b); + panelCenter = getCenterPoint(p); + }); + + for (int i = 0; i < REPEAT_COUNT; i++) { + robot.mouseMove(labelCenter.x, labelCenter.y); + robot.mousePress(InputEvent.BUTTON3_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON3_DOWN_MASK); + + robot.mouseMove(buttonCenter.x, buttonCenter.y); + robot.mousePress(InputEvent.BUTTON3_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON3_DOWN_MASK); + + robot.mouseMove(panelCenter.x, panelCenter.y); + robot.mousePress(InputEvent.BUTTON3_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON3_DOWN_MASK); + } + + // To close the popup, otherwise test fails on windows with timeout error + robot.mouseMove(panelCenter.x - 5, panelCenter.y - 5); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + } finally { + EventQueue.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + } + } + + private static Point getCenterPoint(Component component) { + Point p = component.getLocationOnScreen(); + Dimension size = component.getSize(); + return new Point(p.x + size.width / 2, p.y + size.height / 2); + } + + public static void createAndShowUI() { + f = new Frame("ActivePopupCrashTest Test"); + MenuItem item = new MenuItem("file-1"); + item.addActionListener(ActivePopupCrashTest::logActionEvent); + Menu m = new Menu("file"); + m.add(item); + item = new MenuItem("file-2"); + m.add(item); + MenuBar mb = new MenuBar(); + mb.add(m); + + f.setMenuBar(mb); + f.setSize(200, 200); + f.setLayout(new BorderLayout()); + + l = new Label("label"); + addPopup(l, "label"); + f.add(l, BorderLayout.NORTH); + + p = new Panel(); + addPopup(p, "panel"); + f.add(p, BorderLayout.CENTER); + + b = new Button("button"); + addPopup(b, "button"); + f.add(b, BorderLayout.SOUTH); + + f.setSize(400, 300); + f.setLocationRelativeTo(null); + f.setVisible(true); + } + + static void addPopup(Component c, String name) { + PopupMenu pm = new PopupMenu(); + MenuItem mi = new MenuItem(name + "-1"); + mi.addActionListener(ActivePopupCrashTest::logActionEvent); + pm.add(mi); + + mi = new MenuItem(name + "-2"); + pm.add(mi); + + setHash(c, pm); + c.add(pm); + c.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + mouseAction("mouseClicked", e); + } + + @Override + public void mousePressed(MouseEvent e) { + mouseAction("mousePressed", e); + } + + @Override + public void mouseReleased(MouseEvent e) { + mouseAction("mouseReleased", e); + } + }); + } + + static void logActionEvent(ActionEvent e) { + System.out.println("actionPerformed, event=" + e + ", mod=" + getMods(e)); + System.out.println("command=" + e.getActionCommand()); + System.out.println("param=" + e.paramString()); + System.out.println("source=" + e.getSource()); + } + + static String getMods(ActionEvent e) { return getMods(e.getModifiers()); } + + static String getMods(MouseEvent e) { return getMods(e.getModifiers()); } + + static String getMods(int mods) { + String modstr = ""; + if ((mods & ActionEvent.SHIFT_MASK) == ActionEvent.SHIFT_MASK) { + modstr += (" SHIFT"); + } else if ((mods & ActionEvent.ALT_MASK) == ActionEvent.ALT_MASK) { + modstr += (" ALT"); + } else if ((mods & ActionEvent.CTRL_MASK) == ActionEvent.CTRL_MASK) { + modstr += (" CTRL"); + } else if ((mods & ActionEvent.META_MASK) == ActionEvent.META_MASK) { + modstr += (" META"); + } + return modstr; + } + + static void mouseAction(String which, MouseEvent e) { + Component c = e.getComponent(); + System.out.println(which + " e = " + e + " , mods = " + getMods(e) + + " , component = " + c); + if (e.isPopupTrigger()) { + System.out.println("isPopup"); + PopupMenu pm = getHash(c); + pm.show(c, c.getWidth() / 2, c.getHeight() / 2); + } + } + + static Hashtable popupTable = new Hashtable<>(); + + static void setHash(Component c, PopupMenu p) { + popupTable.put(c, p); + } + + static PopupMenu getHash(Component c) { + return popupTable.get(c); + } + +} diff --git a/test/jdk/java/awt/PopupMenu/KeyTraversalCrash.java b/test/jdk/java/awt/PopupMenu/KeyTraversalCrash.java new file mode 100644 index 0000000000000..4d1d4e8f8319b --- /dev/null +++ b/test/jdk/java/awt/PopupMenu/KeyTraversalCrash.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Menu; +import java.awt.MenuItem; +import java.awt.Point; +import java.awt.PopupMenu; +import java.awt.Robot; + +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +/* + * @test + * @bug 5021183 + * @summary Tests Key Traversal doesn't crash PopupMenu + * @key headful + * @run main KeyTraversalCrash + */ + +public class KeyTraversalCrash { + private static Frame f; + private static Label label; + + private static volatile Point loc; + private static volatile Dimension dim; + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + robot.setAutoDelay(100); + EventQueue.invokeAndWait(KeyTraversalCrash::createAndShowUI); + robot.delay(1000); + + EventQueue.invokeAndWait(() -> { + loc = label.getLocationOnScreen(); + dim = label.getSize(); + }); + + robot.mouseMove(loc.x + 20, loc.y + 20); + robot.mousePress(InputEvent.BUTTON3_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON3_DOWN_MASK); + + robot.mouseMove(loc.x + 25, loc.y + 25); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + + robot.keyPress(KeyEvent.VK_LEFT); + robot.keyRelease(KeyEvent.VK_LEFT); + + robot.keyPress(KeyEvent.VK_DOWN); + robot.keyRelease(KeyEvent.VK_DOWN); + + // To close the popup, otherwise test fails on windows with timeout error + robot.mouseMove(loc.x + dim.width - 20, loc.y + dim.height - 20); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + } finally { + EventQueue.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + } + } + + public static void createAndShowUI() { + f = new Frame("KeyTraversalCrash Test"); + final PopupMenu popup = new PopupMenu(); + for (int i = 0; i < 10; i++) { + Menu menu = new Menu("Menu " + i); + for(int j = 0; j < 10; j++) { + MenuItem menuItem = new MenuItem("MenuItem " + j); + menu.add(menuItem); + } + popup.add(menu); + } + label = new Label("Label"); + f.add(label); + f.add(popup); + label.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent me) { + if (me.isPopupTrigger()) { + popup.show(me.getComponent(), me.getX(), me.getY()); + } + } + + @Override + public void mouseReleased(MouseEvent me) { + if (me.isPopupTrigger()) { + popup.show(me.getComponent(), me.getX(), me.getY()); + } + } + }); + f.setSize(200, 200); + f.setLocationRelativeTo(null); + f.setVisible(true); + } +} diff --git a/test/jdk/java/awt/PopupMenu/MultiplePopupMenusTest.java b/test/jdk/java/awt/PopupMenu/MultiplePopupMenusTest.java new file mode 100644 index 0000000000000..f939186ca072e --- /dev/null +++ b/test/jdk/java/awt/PopupMenu/MultiplePopupMenusTest.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.AWTEvent; +import java.awt.Button; +import java.awt.Frame; +import java.awt.MenuItem; +import java.awt.PopupMenu; + +import java.awt.event.MouseEvent; + +/* + * @test + * @bug 4186663 4265525 + * @summary Tests that multiple PopupMenus cannot appear at the same time + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MultiplePopupMenusTest + */ + +public class MultiplePopupMenusTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Click the right mouse button on the button + If multiple popups appear at the same time the + test fails else passes. + """; + + PassFailJFrame.builder() + .title("MultiplePopupMenusTest Instruction") + .instructions(INSTRUCTIONS) + .columns(30) + .testUI(MultiplePopupMenusTest::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame fr = new Frame("MultiplePopupMenusTest Test"); + TestButton button = new TestButton("button"); + fr.add(button); + fr.setSize(200, 200); + return fr; + } + + static class TestButton extends Button { + public TestButton(String title) { + super(title); + enableEvents(AWTEvent.MOUSE_EVENT_MASK); + } + + @Override + public void processMouseEvent(MouseEvent e) { + if (e.isPopupTrigger()) { + for (int i = 0; i < 10; i++) { + PopupMenu pm = new PopupMenu("Popup " + i); + pm.add(new MenuItem("item 1")); + pm.add(new MenuItem("item 2")); + add(pm); + pm.show(this, e.getX() + i * 5, e.getY() + i * 5); + } + } + super.processMouseEvent(e); + } + } +} diff --git a/test/jdk/java/awt/PopupMenu/PeripheryOfScreen.java b/test/jdk/java/awt/PopupMenu/PeripheryOfScreen.java new file mode 100644 index 0000000000000..b169a7d9692a7 --- /dev/null +++ b/test/jdk/java/awt/PopupMenu/PeripheryOfScreen.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Frame; +import java.awt.MenuItem; +import java.awt.PopupMenu; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 6267162 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Popup Menu gets hidden below the screen when opened near the periphery + * of the screen, XToolkit Test if popup menu window is adjusted on screen + * when trying to show outside + * @run main/manual PeripheryOfScreen + */ + +public class PeripheryOfScreen { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Click on the button to show popup menu in the center of + frame. Move frame beyond the edge of screen and click on + button to show the popup menu and see if popup menu is + adjusted to the edge. + + Press Pass if popup menu behaves as per instruction, otherwise + press Fail. + """; + + PassFailJFrame.builder() + .title("PeripheryOfScreen Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(PeripheryOfScreen::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI () { + Frame f = new Frame("PeripheryOfScreen Test frame"); + Button b = new Button("Click to show popup menu"); + PopupMenu pm = new PopupMenu("Test menu"); + MenuItem i = new MenuItem("Click me"); + pm.add(i); + b.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + pm.show(f, 100, 100); + } + }); + f.add(b); + f.add(pm); + f.setSize(300, 200); + f.toFront(); + return f; + } +} diff --git a/test/jdk/java/awt/PopupMenu/PopupHangTest.java b/test/jdk/java/awt/PopupMenu/PopupHangTest.java new file mode 100644 index 0000000000000..bfdf6bf2abe38 --- /dev/null +++ b/test/jdk/java/awt/PopupMenu/PopupHangTest.java @@ -0,0 +1,258 @@ +/* + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* + * @test + * @bug 4145193 + * @summary Mouse event activates multiple pull-down menus when testing Oracle app + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual PopupHangTest +*/ + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Label; +import java.awt.MenuItem; +import java.awt.PopupMenu; +import java.awt.event.FocusEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.event.FocusListener; +import java.awt.event.KeyListener; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; + +public class PopupHangTest { + private static final String INSTRUCTIONS = """ + 2 areas yellow and red should be seen. + + Clicking in these areas should cause a menu to popup. See if you can + get the menu to stay up and grab all input. One way to do this is to + click and hold the mouse to popup the menu, move away/outside of the + menu and release the mouse. At that point, the input is grabbed and + the *only* way out is to hit the escape key. Try this on both areas. + + To make things worse, when the popup menu is up, click repeatedly on + the LightWeight component area. Hit escape. Do you see multiple menus appearing ? + + If you do not see either of the two problems above, the problem is fixed. + Press pass, else press Fail"""; + + static Frame frame; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("PopupHangTest") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(45) + .testUI(PopupHangTest::createUI) + .logArea() + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + TestMenuButton m1; + TestHeavyButton m2; + frame = new Frame(); + + frame.setLayout (new BorderLayout ()); + + m1 = new TestMenuButton("LightWeight component"); + m1.setBackground(Color.yellow); + + m2 = new TestHeavyButton("HeavyWeight component"); + m2.setBackground(Color.red); + + frame.add("North", m1); + frame.add("South", m2); + + m1.requestFocus(); + frame.setSize(200, 110); + return frame; + } + +} + +class TestMenuButton extends Component implements MouseListener, + MouseMotionListener, + KeyListener, + FocusListener { + + PopupMenu popupMenu = null; + String name; + + TestMenuButton(String name) { + PopupMenu menu = popupMenu = new PopupMenu("Popup"); + menu.add(new MenuItem("item 1")); + menu.add(new MenuItem("item 2")); + menu.add(new MenuItem("item 3")); + this.add(menu); + this.name = name; + addMouseListener(this); + addMouseMotionListener(this); + addKeyListener(this); + addFocusListener(this); + } + + void println(String messageIn) { + PassFailJFrame.log(messageIn); + } + + public void mouseClicked(MouseEvent event) { + /* + popupMenu.show(this, event.getX(), event.getY()); + */ + } + public void mousePressed(MouseEvent event) { + println("TestMenuButton.mousePressed() called !!"); + popupMenu.show(this, event.getX(), event.getY()); + } + public void mouseReleased(MouseEvent event) { + println("TestMenuButton.mouseReleased() called !!"); + } + public void mouseEntered(MouseEvent event) { + println("TestMenuButton.mouseEntered() called !!"); + requestFocus(); + } + public void mouseExited(MouseEvent event) { + } + + public void mouseDragged(MouseEvent event) { + println("TestMenuButton.mouseDragged() called !!"); + } + public void mouseMoved(MouseEvent event) { + println("TestMenuButton.mouseMoved() called !!"); + } + + public void keyPressed(KeyEvent event) { + println("TestMenuButton.keyPressed() called !!"); + } + public void keyReleased(KeyEvent event) { + println("TestMenuButton.keyReleased() called !!"); + } + public void keyTyped(KeyEvent event) { + println("TestMenuButton.keyTyped() called !!"); + } + + + public void focusGained(FocusEvent e){ + println("TestMenuButton.focusGained():" + e); + } + public void focusLost(FocusEvent e){ + println("TestMenuButton.focusLost():" + e); + } + + + public void paint(Graphics g) { + Dimension d = getSize(); + + g.setColor(getBackground()); + g.fillRect(0, 0, d.width-1, d.height-1); + + g.setColor(Color.black); + g.drawString(name, 15, 15); + } + + public Dimension getPreferredSize() { + return (new Dimension(200, 50)); + } + +} + +class TestHeavyButton extends Label implements MouseListener, + MouseMotionListener, + KeyListener, + FocusListener { + + PopupMenu popupMenu = null; + String name; + + TestHeavyButton(String name) { + super(name); + PopupMenu menu = popupMenu = new PopupMenu("Popup"); + menu.add(new MenuItem("item 1")); + menu.add(new MenuItem("item 2")); + menu.add(new MenuItem("item 3")); + this.add(menu); + this.name = name; + addMouseListener(this); + addMouseMotionListener(this); + addKeyListener(this); + addFocusListener(this); + } + + void println(String messageIn) { + PassFailJFrame.log(messageIn); + } + + public void mouseClicked(MouseEvent event) { + /* + popupMenu.show(this, event.getX(), event.getY()); + */ + } + public void mousePressed(MouseEvent event) { + println("TestHeavyButton.mousePressed() called !!"); + popupMenu.show(this, event.getX(), event.getY()); + } + public void mouseReleased(MouseEvent event) { + println("TestHeavyButton.mouseReleased() called !!"); + } + public void mouseEntered(MouseEvent event) { + println("TestHeavyButton.mouseEntered() called !!"); + requestFocus(); + } + public void mouseExited(MouseEvent event) { + } + + public void mouseDragged(MouseEvent event) { + println("TestHeavyButton.mouseDragged() called !!"); + } + public void mouseMoved(MouseEvent event) { + println("TestHeavyButton.mouseMoved() called !!"); + } + + public void keyPressed(KeyEvent event) { + println("TestHeavyButton.keyPressed() called !!"); + } + public void keyReleased(KeyEvent event) { + println("TestHeavyButton.keyReleased() called !!"); + } + public void keyTyped(KeyEvent event) { + println("TestHeavyButton.keyTyped() called !!"); + } + + public void focusGained(FocusEvent e){ + println("TestHeavyButton.focusGained():" + e); + } + public void focusLost(FocusEvent e){ + println("TestHeavyButton.focusLost():" + e); + } + +} + diff --git a/test/jdk/java/awt/PopupMenu/PopupLeadingSeparatorTest.java b/test/jdk/java/awt/PopupMenu/PopupLeadingSeparatorTest.java new file mode 100644 index 0000000000000..c9274e9b807f0 --- /dev/null +++ b/test/jdk/java/awt/PopupMenu/PopupLeadingSeparatorTest.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Component; +import java.awt.Frame; +import java.awt.Font; +import java.awt.MenuItem; +import java.awt.PopupMenu; + +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +/* + * @test + * @bug 4169155 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Popup menus get a leading separator on Motif system + * @run main/manual PopupLeadingSeparatorTest + */ + +public class PopupLeadingSeparatorTest { + public static void main(String[] args) throws Exception { + PopupLeadingSeparatorTest obj = new PopupLeadingSeparatorTest(); + String INSTRUCTIONS = """ + Press mouse button on the frame. Popup menu without leading + separator should appear. + If a PopupMenu behaves same, press Pass, else press Fail. + """; + + PassFailJFrame.builder() + .title("PopupLeadingSeparatorTest Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(obj::createUI) + .build() + .awaitAndCheck(); + } + + private Frame createUI() { + Frame f = new Frame("PopupLeadingSeparatorTest Test"); + PopupMenu popupMenu = new PopupMenu("Popup Menu Title"); + popupMenu.add(new MenuItem("Item1")); + PopupMenu cascadeMenu = new PopupMenu("Multifont menu"); + cascadeMenu.add(new MenuItem("Item1")); + MenuItem item2 = new MenuItem("Item2"); + item2.setFont(new Font("Serif", Font.BOLD, 36)); + cascadeMenu.add(item2); + + popupMenu.add(cascadeMenu); + f.add(popupMenu); + f.setSize(300, 150); + f.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent evt) { + popupMenu.show((Component) evt.getSource(), evt.getX(), evt.getY()); + } + }); + return f; + } +} diff --git a/test/jdk/java/awt/PopupMenu/PopupMenuCrash.java b/test/jdk/java/awt/PopupMenu/PopupMenuCrash.java new file mode 100644 index 0000000000000..7ba738b21d276 --- /dev/null +++ b/test/jdk/java/awt/PopupMenu/PopupMenuCrash.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Label; +import java.awt.MenuItem; +import java.awt.PopupMenu; + +import java.awt.event.InputEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +/* + * @test + * @bug 4281273 + * @summary PopupMenu crashed in Java. Simplified testcase. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @requires (os.family == "windows") + * @run main/manual PopupMenuCrash + */ + +public class PopupMenuCrash { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This tests a windows specific problem. + When you see a frame titled "PopupMenuCrash Test", right-click on it + several times for a few seconds. Then wait about 10 seconds before the + PopupMenus start to appear. Then dispose them one by one by clicking on them. + When PopupMenus do not appear anymore, press Pass. + In case of a failure, you'll see a crash. + """; + + PassFailJFrame.builder() + .title("PopupMenuCrash Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(PopupMenuCrash::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + final Frame f = new Frame("PopupMenuCrash Test"); + f.setLayout(new FlowLayout()); + f.add(new Label("Press right mouse button inside this frame.")); + f.add(new Label("A pop-up menu should appear.")); + f.addMouseListener(new MouseAdapter() { + PopupMenu popup; + boolean firstPress = true; + + @Override + public void mousePressed(MouseEvent evt) { + if (firstPress) { + firstPress = false; + try { + Thread.sleep(10000); + } catch (InterruptedException ignored) { + } + } + + if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0) { + popup = new PopupMenu("Popup Menu Title"); + MenuItem mi = new MenuItem("MenuItem"); + popup.add(mi); + f.add(popup); + popup.show(evt.getComponent(), evt.getX(), evt.getY()); + } + } + + @Override + public void mouseReleased(MouseEvent evt) { + if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0) { + if (popup != null) { + f.remove(popup); + popup = null; + } + } + } + }); + + f.setSize(400, 350); + return f; + } +} diff --git a/test/jdk/java/awt/PopupMenu/PopupMenuShowTest.java b/test/jdk/java/awt/PopupMenu/PopupMenuShowTest.java new file mode 100644 index 0000000000000..b36fdc5d19c22 --- /dev/null +++ b/test/jdk/java/awt/PopupMenu/PopupMenuShowTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Label; +import java.awt.MenuItem; +import java.awt.PopupMenu; + +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +/* + * @test + * @bug 4168006 4196790 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Popup menu test fails on x86/Solaris 2.6 combination. + * @run main/manual PopupMenuShowTest + */ + +public class PopupMenuShowTest { + public static void main(String[] args) throws Exception { + PopupMenuShowTest obj = new PopupMenuShowTest(); + String INSTRUCTIONS = """ + Press the right mouse button in the PopupTest window. + If a PopupMenu appears, press Pass, else press Fail. + """; + + PassFailJFrame.builder() + .title("PopupMenuShowTest Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(obj::createUI) + .build() + .awaitAndCheck(); + } + + private Frame createUI() { + Frame f = new Frame("PopupMenuShowTest Test"); + f.setLayout(new FlowLayout()); + f.add(new Label("Press right mouse button inside this frame.")); + f.add(new Label("A pop-up menu should appear.")); + PopupMenu popupMenu = new PopupMenu("Popup Menu Title"); + MenuItem mi = new MenuItem("Menu Item"); + popupMenu.add(mi); + f.add(popupMenu); + f.setSize(400, 350); + f.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + popupMenu.show(e.getComponent(), e.getX(), e.getY()); + } + }); + return f; + } +} diff --git a/test/jdk/java/awt/PopupMenu/PopupMenuVisuals.java b/test/jdk/java/awt/PopupMenu/PopupMenuVisuals.java new file mode 100644 index 0000000000000..3aa437e484540 --- /dev/null +++ b/test/jdk/java/awt/PopupMenu/PopupMenuVisuals.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* + * @test + * @bug 6180413 6184485 6267144 + * @summary test for popup menu visual bugs in XAWT + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual PopupMenuVisuals +*/ + +import java.awt.Button; +import java.awt.CheckboxMenuItem; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuItem; +import java.awt.MenuShortcut; +import java.awt.PopupMenu; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; + +public class PopupMenuVisuals { + private static final String INSTRUCTIONS = """ + This test should show a button 'Popup'. + Click on the button. A popup menu should be shown. + If following conditions are met: + - Menu is disabled + - Menu has caption 'Popup menu' (only applicable for linux) + - Menu items don't show shortcuts + + Click Pass else click Fail."""; + + static PopupMenu pm; + static Frame frame; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("PopupMenu Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(PopupMenuVisuals::createTestUI) + .build() + .awaitAndCheck(); + } + + static class Listener implements ActionListener { + public void actionPerformed(ActionEvent e) { + pm.show(frame, 0, 0); + } + } + + private static Frame createTestUI() { + Button b = new Button("Popup"); + pm = new PopupMenu("Popup menu"); + MenuItem mi1 = new MenuItem("Item 1"); + CheckboxMenuItem mi2 = new CheckboxMenuItem("Item 2"); + CheckboxMenuItem mi3 = new CheckboxMenuItem("Item 3"); + Menu sm = new Menu("Submenu"); + + //Get things going. Request focus, set size, et cetera + frame = new Frame("PopupMenuVisuals"); + frame.setSize (200,200); + frame.validate(); + + frame.add(b); + b.addActionListener(new Listener()); + mi1.setShortcut(new MenuShortcut(KeyEvent.VK_A)); + pm.add(mi1); + pm.add(mi2); + pm.add(mi3); + pm.add(sm); + sm.add(new MenuItem("Item")); + pm.setEnabled(false); + mi3.setState(true); + frame.add(pm); + return frame; + } + +} diff --git a/test/jdk/java/awt/PopupMenu/PopupMenuWithMenuBar.java b/test/jdk/java/awt/PopupMenu/PopupMenuWithMenuBar.java new file mode 100644 index 0000000000000..1a927e29f84bc --- /dev/null +++ b/test/jdk/java/awt/PopupMenu/PopupMenuWithMenuBar.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.PopupMenu; + +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +/* + * @test + * @bug 4038140 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Test for functionality of PopupMenuWithMenuBar + * @run main/manual PopupMenuWithMenuBar + */ + +public class PopupMenuWithMenuBar { + public static void main(String[] args) throws Exception { + PopupMenuWithMenuBar obj = new PopupMenuWithMenuBar(); + String INSTRUCTIONS = """ + There was a bug that prevented the popup menu from appearing properly + (if even at all) for a frame window when there is also a menu bar. + + Right click inside the frame window to display the popup window. If + the popup menu appears normally, then the test is successful and the + bug has been fixed."""; + + PassFailJFrame.builder() + .title("PopupMenuWithMenuBar Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(obj::createUI) + .build() + .awaitAndCheck(); + } + + private Frame createUI() { + Frame f = new Frame("PopupMenuWithMenuBar Test"); + f.setBounds(10, 10, 300, 250); + MenuBar menuBar = new MenuBar(); + Menu fileMenu = createFileMenu(); + menuBar.add(fileMenu); + f.setMenuBar(menuBar); + PopupMenu popupMenu = createPopupMenu(); + f.add(popupMenu); + f.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + popupMenu.show(f, e.getX(), e.getY()); + } + }); + return f; + } + + private Menu createFileMenu() { + String[] menu1Labels = new String[] + {"Save As", "Save As", "Quit"}; + MenuItem menuItem; + Menu returnMenu = new Menu("File"); + for (int menu1Index = 0; menu1Index < menu1Labels.length; menu1Index++) { + menuItem = new MenuItem(menu1Labels[menu1Index]); + returnMenu.add(menuItem); + } + return returnMenu; + } + + private PopupMenu createPopupMenu() { + String[] popupLabels = new String[] + {"Popup 1", "Popup 2", "Quit"}; + MenuItem menuItem; + PopupMenu returnMenu = new PopupMenu("Popups"); + for (int popupIndex = 0; popupIndex < popupLabels.length; popupIndex++) { + menuItem = new MenuItem(popupLabels[popupIndex]); + returnMenu.add(menuItem); + } + return returnMenu; + } +} diff --git a/test/jdk/java/awt/PopupMenu/PopupOnButton.java b/test/jdk/java/awt/PopupMenu/PopupOnButton.java new file mode 100644 index 0000000000000..581714bf76a85 --- /dev/null +++ b/test/jdk/java/awt/PopupMenu/PopupOnButton.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Component; +import java.awt.Frame; +import java.awt.MenuItem; +import java.awt.PopupMenu; + +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +/* + * @test + * @bug 4181790 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Tests a popup menu on a button. + * @run main/manual PopupOnButton + */ + +public class PopupOnButton { + public static void main(String[] args) throws Exception { + PopupOnButton obj = new PopupOnButton(); + String INSTRUCTIONS = """ + Right-click on the button. + Popup Menu should appear and behave fine. + If a PopupMenu appears, press Pass, else press Fail. + """; + + PassFailJFrame.builder() + .title("PopupOnButton Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(obj::createUI) + .build() + .awaitAndCheck(); + } + + private Frame createUI() { + Frame f = new Frame("PopupOnButton Test"); + Button b = new Button("button with popup menu"); + PopupMenu m = new PopupMenu("popup"); + m.add(new MenuItem("item1")); + m.add(new MenuItem("item2")); + m.add(new MenuItem("item3")); + b.add(m); + b.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + if (e.isPopupTrigger()) { + m.show((Component) e.getSource(), e.getX(), e.getY()); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + if (e.isPopupTrigger()) { + m.show((Component) e.getSource(), e.getX(), e.getY()); + } + } + }); + + f.add(b); + f.setSize(200, 150); + return f; + } + } diff --git a/test/jdk/java/awt/PopupMenu/StressTest.java b/test/jdk/java/awt/PopupMenu/StressTest.java new file mode 100644 index 0000000000000..221aa252aa0b7 --- /dev/null +++ b/test/jdk/java/awt/PopupMenu/StressTest.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.MenuItem; +import java.awt.Panel; +import java.awt.Point; +import java.awt.PopupMenu; +import java.awt.Robot; + +import java.awt.event.InputEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +/* + * @test + * @bug 4083400 + * @key headful + * @summary Tests that excessive popping up and down does not crash or + * throw an exception. + * @run main StressTest + */ + +public class StressTest { + private static Frame fr; + private static PopupTestPanel panel; + + private static volatile Point panelCenter; + + public static void main(String[] args) throws Exception { + final int REPEAT_COUNT = 5; + try { + Robot robot = new Robot(); + robot.setAutoDelay(50); + EventQueue.invokeAndWait(StressTest::createAndShowUI); + robot.delay(1000); + + EventQueue.invokeAndWait(() -> { + Point loc = panel.getLocationOnScreen(); + Dimension dim = panel.getSize(); + panelCenter = new Point(loc.x + dim.width / 2, loc.y + dim.height / 2); + }); + + for (int i = 0; i < REPEAT_COUNT; i++) { + robot.mouseMove(panelCenter.x + i * 2, panelCenter.y + i * 2); + + robot.mousePress(InputEvent.BUTTON3_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON3_DOWN_MASK); + + robot.mouseMove(panelCenter.x - i * 2, panelCenter.y - i * 2); + + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (fr != null) { + fr.dispose(); + } + }); + } + } + + public static void createAndShowUI() { + fr = new Frame("PopupMenu Test"); + panel = new PopupTestPanel(); + fr.add(panel); + fr.setSize(300, 200); + fr.setVisible(true); + } + + static class PopupTestPanel extends Panel { + + static class Item extends MenuItem { + public Item(String text) { + super(text); + } + + public boolean isEnabled() { + try { + Thread.sleep(100); + } catch (InterruptedException ignored) { + } + return super.isEnabled(); + } + } + + final PopupMenu popup; + + public PopupTestPanel() { + popup = new PopupMenu(); + popup.add(new Item("Soap")); + popup.add(new Item("Sponge")); + popup.add(new Item("Flannel")); + popup.add(new Item("Mat")); + popup.add(new Item("Towel")); + add(popup); + addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + if (e.isPopupTrigger()) { + showPopup(e); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + if (e.isPopupTrigger()) { + showPopup(e); + } + } + + private void showPopup(MouseEvent e) { + popup.show((Component) e.getSource(), e.getX(), e.getY()); + } + }); + } + } +} diff --git a/test/jdk/java/awt/PrintJob/PrintCompatibilityTest.java b/test/jdk/java/awt/PrintJob/PrintCompatibilityTest.java new file mode 100644 index 0000000000000..a2433f455470e --- /dev/null +++ b/test/jdk/java/awt/PrintJob/PrintCompatibilityTest.java @@ -0,0 +1,446 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Canvas; +import java.awt.Checkbox; +import java.awt.Choice; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.JobAttributes; +import java.awt.Label; +import java.awt.List; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.PageAttributes; +import java.awt.Panel; +import java.awt.PrintJob; +import java.awt.Scrollbar; +import java.awt.ScrollPane; +import java.awt.TextArea; +import java.awt.TextField; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.JobAttributes.DialogType; +import java.awt.PageAttributes.OriginType; + +import java.util.Enumeration; +import java.util.Properties; + +/* + * @test + * @bug 4247583 + * @key printer + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Tests that the old Properties API still works + * @run main/manual PrintCompatibilityTest + */ + +public class PrintCompatibilityTest { + + public static void main(String[] args) throws Exception { + + String INSTRUCTIONS = """ + A frame window will appear. + Choose 'Print to Printer...' from the 'Print' menu. Make sure that you print + to a printer, not a file. Examine the output and verify that the frame and all + the components in it get printed properly. + + Known problems: + * The text in the second row of the menubar is not indented correctly. + + You can also use the 'Print to Screen...' command for a quick manual check that + printing works, but this is only for debugging purposes."""; + + PassFailJFrame.builder() + .title("PrintComponentTest Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(60) + .testTimeOut(10) + .testUI(new MainFrame()) + .logArea(8) + .build() + .awaitAndCheck(); + } +} + +class MainFrame extends Frame { + private LWContainer lwc; + + public MainFrame() { + super("PrintCompatibilityTest"); + + setSize(800, 400); + setLayout(new FlowLayout()); + + // peered components + Button button = new Button("Button"); + button.setFont(new Font("Dialog", Font.PLAIN, 12)); + add(button); + add(new TestCanvas()); + Checkbox cbox = new Checkbox("Checkbox", true); + cbox.setFont(new Font("DialogInput", Font.PLAIN, 12)); + add(cbox); + Choice choice = new Choice(); + choice.add("Choice 1"); + choice.add("Choice Two"); + choice.setFont(new Font("Monospaced", Font.PLAIN, 12)); + add(choice); + Label label = new Label("Label"); + label.setFont(new Font("Serif", Font.PLAIN, 12)); + add(label); + List list = new List(); + list.add("List 1"); + list.add("List Two"); + list.setFont(new Font("SansSerif", Font.PLAIN, 12)); + add(list); + add(new Scrollbar(Scrollbar.VERTICAL) ); + add(new Scrollbar(Scrollbar.HORIZONTAL) ); + ScrollPane scrollpane = new ScrollPane(); + Button spButton = new Button("Button in a scrollpane"); + spButton.setFont(new Font("Monospaced", Font.PLAIN, 12)); + scrollpane.add(spButton); + add(scrollpane); + TextArea textarea = new TextArea("TextArea", 3, 30); + textarea.setFont(new Font("Dialog", Font.ITALIC, 10)); + add(textarea); + TextField textfield = new TextField("TextField"); + textfield.setFont(new Font("DialogInput", Font.ITALIC, 10)); + add(textfield); + + // nested components + Panel panel1 = new Panel(); + panel1.setLayout(new FlowLayout()); + panel1.setBackground(Color.red); + this.add(panel1); + + Button p1Button = new Button("level 2"); + p1Button.setFont(new Font("Monospaced", Font.ITALIC, 10)); + panel1.add(p1Button); + + Panel panel2 = new Panel(); + panel2.setLayout(new FlowLayout()); + panel2.setBackground(Color.green); + panel1.add(panel2); + + Button p2Button = new Button("level 3"); + p2Button.setFont(new Font("Serif", Font.ITALIC, 10)); + panel2.add(p2Button); + + + // lightweight components + LWButton lwbutton = new LWButton("LWbutton"); + lwbutton.setFont(new Font("SansSerif", Font.ITALIC, 10)); + add(lwbutton); + + lwc = new LWContainer("LWContainerLWContainerLWContainerLWContainerLWContainerLWContainerLWContainerLWContainerLWContainerLWContainerLWContainerLWContainerLWContainer"); + lwc.setFont(new Font("Monospaced", Font.ITALIC, 10)); + add(lwc); + Button lwcButton1 = new Button("HW Button 1"); + Button lwcButton2 = new Button("HW Button 2"); + LWButton lwcButton3 = new LWButton("LW Button"); + lwcButton1.setFont(new Font("Dialog", Font.BOLD, 14)); + lwcButton2.setFont(new Font("DialogInput", Font.BOLD, 14)); + lwcButton3.setFont(new Font("Monospaced", Font.BOLD, 14)); + lwc.add(lwcButton1); + lwc.add(lwcButton2); + lwc.add(lwcButton3); + + // overlapping components + add(new ZOrderPanel()); + + /////////////////////// + + Menu menu = new Menu("Print"); + Menu menu2 = new Menu("File"); + Menu menu3 = new Menu("Edit"); + Menu menu4 = new Menu("ReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyLong"); + menu2.setFont(new Font("SansSerif", Font.BOLD, 20)); + menu2.setEnabled(false); + menu3.setFont(new Font("Monospaced", Font.ITALIC, 18)); + menu3.setEnabled(false); + menu4.setEnabled(false); + MenuItem itemPrinter = new MenuItem("Print to Printer..."); + MenuItem itemScreen = new MenuItem("Print to Screen..."); + menu.add(itemPrinter); + menu.add(itemScreen); + MenuBar menuBar = new MenuBar(); + menuBar.add( menu ); + menuBar.add( menu2 ); + menuBar.add( menu3 ); + menuBar.add( menu4 ); + setMenuBar(menuBar); + + itemPrinter.addActionListener( new ActionPrint() ); + itemScreen.addActionListener( new ActionPrintToScreen() ); + setVisible(true); + } + + static void printProps(Properties props) + { + Enumeration propNames = props.propertyNames(); + while (propNames.hasMoreElements()) { + String propName = (String)propNames.nextElement(); + PassFailJFrame.log( propName + " = " + props.getProperty(propName)); + } + } + + class ActionPrint implements ActionListener { + private final int ITERATIONS = 1; + private Properties props = new Properties(); + + public void actionPerformed(ActionEvent ev) { + PassFailJFrame.log("About to show print dialog..."); + printProps(props); + PrintJob pj = getToolkit().getPrintJob( + MainFrame.this, "Print test!", props); + if (pj == null) { + return; + } + Dimension d = pj.getPageDimension(); + PassFailJFrame.log("About to print..."); + PassFailJFrame.log("Dimensions: " + d); + printProps(props); + + // For xor mode set, there is a printing issue with number of copies to be print. + // So, ITERATIONS are changed to 1 from 3. + // So, for now the XOR related code is commented out. + + //boolean xor = false; + + for (int i = 0; i < ITERATIONS; i++) { + Graphics g = pj.getGraphics(); + g.setColor(Color.red); + //if (xor) { + // g.setXORMode(Color.blue); + //} + g.translate(13, 13); + printAll(g); + g.dispose(); + //xor = (xor) ? false : true; + } + + // For xor mode set, LWC components don't get printed. + // So, for now the code is commented out and separate bug + // (JDK-8340495) is filed to handle it. + + // one more page so that we can test printing a lightweight + // at the top of the hierarchy (BugId 4212564) + //Graphics g = pj.getGraphics(); + //g.setColor(Color.red); + //g.translate(13, 13); + //lwc.printAll(g); + //g.dispose(); + // end 4212564 + + pj.end(); + } + } + + class ActionPrintToScreen implements ActionListener { + public void actionPerformed(ActionEvent ev) { + PrintFrame printFrame = new PrintFrame(MainFrame.this); + printFrame.show(); + Graphics g = printFrame.getGraphics(); + g.setColor(Color.red); + printAll(g); + g.dispose(); + } + } + + // Frame window that displays results of printing + // main window to a screen Graphics-- useful for + // quick testing of printing + class PrintFrame extends Frame + { + private Component printComponent; + public PrintFrame( Component c ) + { + super("Print to Screen"); + printComponent = c ; + addWindowListener( new WindowAdapter() { + public void windowClosing(WindowEvent ev) { + setVisible(false); + dispose(); + } + } + ); + setSize(printComponent.getSize()); + setResizable(false); + } + + public void paint( Graphics g ) { + printComponent.printAll(g); + } + } + + class LWButton extends Component { + String label; + int width = 100; + int height = 30; + + public LWButton(String label) { + super(); + this.label = label; + } + + public void paint(Graphics g) { + Dimension d = getSize(); + g.setColor(Color.orange); + g.setFont(getFont()); + g.fillRect(0, 0, d.width, d.height); + g.setColor(Color.black); + int x = 5; + int y = (d.height - 5); + g.drawString(label, x, y); + } + + public Dimension getPreferredSize() + { + return new Dimension(width, height); + } + } + + class LWContainer extends Container { + String label; + int width = 300; + int height = 100; + + public LWContainer(String label) { + super(); + this.label = label; + setLayout(new FlowLayout()); + } + + public void paint(Graphics g) { + super.paint(g); + Dimension d = getSize(); + g.setColor(Color.green); + g.setFont(getFont()); + g.drawLine(0, 0, d.width - 1, 0); + g.drawLine(d.width - 1, 0, d.width - 1, d.height - 1); + g.drawLine(d.width - 1, d.height - 1, 0, d.height - 1); + g.drawLine(0, d.height - 1, 0, 0); + g.setColor(Color.black); + int x = 5; + int y = (d.height - 5); + g.drawString(label, x, y); + } + + public Dimension getPreferredSize() + { + return new Dimension(width, height); + } + } + + class TestCanvas extends Canvas { + int width = 100; + int height = 100; + + public void paint(Graphics g) { + g.setColor(Color.blue); + g.fillRoundRect(10, 10, 50, 50, 15, 30); + g.setColor(Color.red); + g.fillOval(70, 70, 25, 25); + } + public Dimension getPreferredSize() { + return new Dimension(width, height); + } + } + + class ZOrderPanel extends Panel + { + ZOrderPanel() + { + setLayout(null); + + Component first, second, third, fourth; + + setVisible(true); + // add first component + first = makeBox("Second", Color.blue, + new Font("Serif", Font.BOLD, 14), + -1); + // insert on top + second = makeBox("First", Color.yellow, + new Font("SansSerif", Font.BOLD, 14), + 0); + // put at the back + fourth = makeBox("Fourth", Color.red, + new Font("Monospaced", Font.BOLD, 14), + 2); + // insert in last position + third = makeBox("Third", Color.green, + new Font("Dialog", Font.PLAIN, 12), + 3); + // swap third and fourth to correct positions + remove(third); + add(third, 2); + // re-validate so third and fourth peers change position + validate(); + // now make things really interesting with a lightweight + // component at the top of the z-order, that should print + // _below_ the native guys to match the screen... + add(new LWButton("LWButton"), 0); + } + + public Dimension preferredSize() + { + return new Dimension(260, 80); + } + + public void layout() + { + int i, n; + Insets ins = getInsets(); + n = getComponentCount(); + for (i = n-1; i >= 0; i--) { + Component p = getComponent(i); + p.setBounds(ins.left + 40 * i, ins.top + 5 * i, 60, 60); + } + } + + public Component makeBox(String s, Color c, Font f, int index) + { + Label l = new Label(s); + l.setBackground(c); + l.setAlignment(Label.RIGHT); + l.setFont(f); + add(l, index); + validate(); + return l; + } + } +} diff --git a/test/jdk/java/awt/PrintJob/PrintComponentTest.java b/test/jdk/java/awt/PrintJob/PrintComponentTest.java new file mode 100644 index 0000000000000..d568c5a427977 --- /dev/null +++ b/test/jdk/java/awt/PrintJob/PrintComponentTest.java @@ -0,0 +1,486 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Canvas; +import java.awt.Checkbox; +import java.awt.Choice; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.JobAttributes; +import java.awt.Label; +import java.awt.List; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.PageAttributes; +import java.awt.Panel; +import java.awt.PrintJob; +import java.awt.Scrollbar; +import java.awt.ScrollPane; +import java.awt.TextArea; +import java.awt.TextField; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.JobAttributes.DialogType; +import java.awt.PageAttributes.OriginType; + + +/* + * @test + * @bug 4111262 4035285 4038900 4046147 4049680 4084038 4100004 4105875 + * @bug 4117502 4037486 4068433 4128031 4151161 4151707 4155884 4212564 + * @bug 4025626 4029565 4034365 4036068 4040622 4061890 4067405 4086256 + * @bug 4113827 4116722 4121984 4145350 4146510 4172659 4179886 4218471 + * @bug 4219657 4227128 4242308 4245917 4265746 + * @key printer + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Test printing of lightweight (and heavyweight) components + * @run main/manual PrintComponentTest + */ + +public class PrintComponentTest { + public static void main(String[] args) throws Exception { + + String INSTRUCTIONS = """ + A frame window will appear. + Choose 'Print to Printer...' from the 'Print' menu. Examine the output + and verify that the frame and all the components in it get printed properly. + + Print using both 'Portrait' and 'Landscape' orientation. + Verify that the paper dimensions printed to standard error + are exactly inverted. + (That is, if the output for 'Portrait' is + "Dimensions: java.awt.Dimension[width=612,height=792]" then the output + for 'Landscape' should be "Dimensions: java.awt.Dimension[width=792, height=612].) + + Now, attempt to print a second time. When the print dialog box appears, + however, cancel the print request. + Verify that _no_ output is sent to standard error. + + You should attempt to print with both the native and common print dialogs, + as well as with no dialog. + Note that on Linux the native and common print dialogs are identical. + + On Windows, the common print dialog communicates with the printer to + determine supported paper sizes and duplex capability. + Verify that these constraints are properly enforced in the common dialog + for the target printer. + + Known problems: + * The text in the second row of the menubar is not indented + correctly. + + You can also use the 'Print to Screen...' command for a quick manual + check that printing works, but this is only for debugging purposes."""; + + PassFailJFrame.builder() + .title("PrintComponentTest Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(60) + .testTimeOut(10) + .testUI(new MainFrame()) + .logArea(8) + .build() + .awaitAndCheck(); + } +} + +class MainFrame extends Frame { + private LWContainer lwc; + + public MainFrame() { + super("PrintComponentTest"); + + setSize(800, 400); + setLayout(new FlowLayout()); + + // peered components + Button button = new Button("Button"); + button.setFont(new Font("Dialog", Font.PLAIN, 12)); + add(button); + add(new TestCanvas()); + Checkbox cbox = new Checkbox("Checkbox", true); + cbox.setFont(new Font("DialogInput", Font.PLAIN, 12)); + add(cbox); + Choice choice = new Choice(); + choice.add("Choice 1"); + choice.add("Choice Two"); + choice.setFont(new Font("Monospaced", Font.PLAIN, 12)); + add(choice); + Label label = new Label("Label"); + label.setFont(new Font("Serif", Font.PLAIN, 12)); + add(label); + List list = new List(); + list.add("List 1"); + list.add("List Two"); + list.setFont(new Font("SansSerif", Font.PLAIN, 12)); + add(list); + add(new Scrollbar(Scrollbar.VERTICAL) ); + add(new Scrollbar(Scrollbar.HORIZONTAL) ); + ScrollPane scrollpane = new ScrollPane(); + Button spButton = new Button("Button in a scrollpane"); + spButton.setFont(new Font("Monospaced", Font.PLAIN, 12)); + scrollpane.add(spButton); + add(scrollpane); + TextArea textarea = new TextArea("TextArea", 3, 30); + textarea.setFont(new Font("Dialog", Font.ITALIC, 10)); + add(textarea); + TextField textfield = new TextField("TextField"); + textfield.setFont(new Font("DialogInput", Font.ITALIC, 10)); + add(textfield); + + // nested components + Panel panel1 = new Panel(); + panel1.setLayout(new FlowLayout()); + panel1.setBackground(Color.red); + this.add(panel1); + + Button p1Button = new Button("level 2"); + p1Button.setFont(new Font("Monospaced", Font.ITALIC, 10)); + panel1.add(p1Button); + + Panel panel2 = new Panel(); + panel2.setLayout(new FlowLayout()); + panel2.setBackground(Color.green); + panel1.add(panel2); + + Button p2Button = new Button("level 3"); + p2Button.setFont(new Font("Serif", Font.ITALIC, 10)); + panel2.add(p2Button); + + + // lightweight components + LWButton lwbutton = new LWButton("LWbutton"); + lwbutton.setFont(new Font("SansSerif", Font.ITALIC, 10)); + add(lwbutton); + + lwc = new LWContainer("LWContainerLWContainerLWContainerLWContainerLWContainerLWContainerLWContainerLWContainerLWContainerLWContainerLWContainerLWContainerLWContainer"); + lwc.setFont(new Font("Monospaced", Font.ITALIC, 10)); + add(lwc); + Button lwcButton1 = new Button("HW Button 1"); + Button lwcButton2 = new Button("HW Button 2"); + LWButton lwcButton3 = new LWButton("LW Button"); + lwcButton1.setFont(new Font("Dialog", Font.BOLD, 14)); + lwcButton2.setFont(new Font("DialogInput", Font.BOLD, 14)); + lwcButton3.setFont(new Font("Monospaced", Font.BOLD, 14)); + lwc.add(lwcButton1); + lwc.add(lwcButton2); + lwc.add(lwcButton3); + + // overlapping components + add(new ZOrderPanel()); + + /////////////////////// + + Menu menu = new Menu("Print"); + Menu menu2 = new Menu("File"); + Menu menu3 = new Menu("Edit"); + Menu menu4 = new Menu("ReallyReallyReallyReallyReallyReallyReallyReally" + + "ReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReally" + + "ReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyLong"); + menu2.setFont(new Font("SansSerif", Font.BOLD, 20)); + menu2.setEnabled(false); + menu3.setFont(new Font("Monospaced", Font.ITALIC, 18)); + menu3.setEnabled(false); + menu4.setEnabled(false); + MenuItem itemJFC = + new MenuItem("Print to Printer with Cross-Platform Dialog..."); + itemJFC.setActionCommand("common"); + MenuItem itemNative = + new MenuItem("Print to Printer with Native Dialog..."); + itemNative.setActionCommand("native"); + MenuItem itemBackground = + new MenuItem("Print to Printer in Background"); + itemBackground.setActionCommand("none"); + MenuItem itemScreen = new MenuItem("Print to Screen..."); + menu.add(itemJFC); + menu.add(itemNative); + menu.add(itemBackground); + menu.add(itemScreen); + MenuBar menuBar = new MenuBar(); + menuBar.add( menu ); + menuBar.add( menu2 ); + menuBar.add( menu3 ); + menuBar.add( menu4 ); + setMenuBar(menuBar); + + ActionPrint actionPrint = new ActionPrint(); + + itemJFC.addActionListener( actionPrint ); + itemNative.addActionListener( actionPrint ); + itemBackground.addActionListener( actionPrint ); + itemScreen.addActionListener( new ActionPrintToScreen() ); + } + + class ActionPrint implements ActionListener { + private final int ITERATIONS = 1; + private PageAttributes pageAttributes = new PageAttributes(); + private JobAttributes jobAttributes = new JobAttributes(); + + public void actionPerformed(ActionEvent ev) { + DialogType dialog; + if (ev.getActionCommand().equals("common")) { + dialog = DialogType.COMMON; + } else if (ev.getActionCommand().equals("native")) { + dialog = DialogType.NATIVE; + } else { + dialog = DialogType.NONE; + } + jobAttributes.setDialog(dialog); + pageAttributes.setOrigin(OriginType.PRINTABLE); + System.err.println(jobAttributes); + System.err.println(pageAttributes); + + PassFailJFrame.log("About to show print dialog..."); + + PrintJob pj = getToolkit().getPrintJob( + MainFrame.this, "Print test!", jobAttributes, pageAttributes); + if (pj == null) { + return; + } + Dimension d = pj.getPageDimension(); + PassFailJFrame.log("About to print..."); + PassFailJFrame.log("Dimensions: " + d); + System.err.println(jobAttributes); + System.err.println(pageAttributes); + + // For xor mode set, there is a printing issue with number of copies to be print. + // So, ITERATIONS are changed to 1 from 3. + // So, for now the XOR related code is commented out. + + //boolean xor = false; + + for (int i = 0; i < ITERATIONS; i++) { + Graphics g = pj.getGraphics(); + g.setColor(Color.red); + //if (xor) { + // g.setXORMode(Color.blue); + //} + printAll(g); + g.dispose(); + //xor = (xor) ? false : true; + } + + // For xor mode set, LWC components don't get printed. + // So, for now the code is commented out and separate bug + // (JDK-8340495) is filed to handle it. + + // one more page so that we can test printing a lightweight + // at the top of the hierarchy (BugId 4212564) + //Graphics g = pj.getGraphics(); + //g.setColor(Color.red); + //lwc.printAll(g); + //g.dispose(); + // end 4212564 + + pj.end(); + } + } + + class ActionPrintToScreen implements ActionListener { + public void actionPerformed(ActionEvent ev) { + PrintFrame printFrame = new PrintFrame(MainFrame.this); + printFrame.show(); + Graphics g = printFrame.getGraphics(); + g.setColor(Color.red); + printAll(g); + g.dispose(); + } + } + + // Frame window that displays results of printing + // main window to a screen Graphics-- useful for + // quick testing of printing + class PrintFrame extends Frame + { + private Component printComponent; + public PrintFrame( Component c ) + { + super("Print to Screen"); + printComponent = c ; + addWindowListener( new WindowAdapter() { + public void windowClosing(WindowEvent ev) { + setVisible(false); + dispose(); + } + } + ); + setSize(printComponent.getSize()); + setResizable(false); + } + + public void paint( Graphics g ) { + printComponent.printAll(g); + } + } + + class LWButton extends Component { + String label; + int width = 100; + int height = 30; + + public LWButton(String label) { + super(); + this.label = label; + } + + public void paint(Graphics g) { + Dimension d = getSize(); + g.setColor(Color.orange); + g.setFont(getFont()); + g.fillRect(0, 0, d.width, d.height); + g.setColor(Color.black); + int x = 5; + int y = (d.height - 5); + g.drawString(label, x, y); + } + + public Dimension getPreferredSize() + { + return new Dimension(width, height); + } + } + + class LWContainer extends Container { + String label; + int width = 300; + int height = 100; + + public LWContainer(String label) { + super(); + this.label = label; + setLayout(new FlowLayout()); + } + + public void paint(Graphics g) { + super.paint(g); + Dimension d = getSize(); + g.setColor(Color.green); + g.setFont(getFont()); + g.drawLine(0, 0, d.width - 1, 0); + g.drawLine(d.width - 1, 0, d.width - 1, d.height - 1); + g.drawLine(d.width - 1, d.height - 1, 0, d.height - 1); + g.drawLine(0, d.height - 1, 0, 0); + g.setColor(Color.black); + int x = 5; + int y = (d.height - 5); + g.drawString(label, x, y); + } + + public Dimension getPreferredSize() + { + return new Dimension(width, height); + } + } + + class TestCanvas extends Canvas { + int width = 100; + int height = 100; + + public void paint(Graphics g) { + g.setColor(Color.blue); + g.fillRoundRect(10, 10, 50, 50, 15, 30); + g.setColor(Color.red); + g.fillOval(70, 70, 25, 25); + } + public Dimension getPreferredSize() { + return new Dimension(width, height); + } + } + + class ZOrderPanel extends Panel + { + ZOrderPanel() + { + setLayout(null); + + Component first, second, third, fourth; + + setVisible(true); + // add first component + first = makeBox("Second", Color.blue, + new Font("Serif", Font.BOLD, 14), + -1); + // insert on top + second = makeBox("First", Color.yellow, + new Font("SansSerif", Font.BOLD, 14), + 0); + // put at the back + fourth = makeBox("Fourth", Color.red, + new Font("Monospaced", Font.BOLD, 14), + 2); + // insert in last position + third = makeBox("Third", Color.green, + new Font("Dialog", Font.PLAIN, 12), + 3); + // swap third and fourth to correct positions + remove(third); + add(third, 2); + // re-validate so third and fourth peers change position + validate(); + // now make things really interesting with a lightweight + // component at the top of the z-order, that should print + // _below_ the native guys to match the screen... + add(new LWButton("LWButton"), 0); + } + + public Dimension preferredSize() + { + return new Dimension(260, 80); + } + + public void layout() + { + int i, n; + Insets ins = getInsets(); + n = getComponentCount(); + for (i = n-1; i >= 0; i--) { + Component p = getComponent(i); + p.setBounds(ins.left + 40 * i, ins.top + 5 * i, 60, 60); + } + } + + public Component makeBox(String s, Color c, Font f, int index) + { + Label l = new Label(s); + l.setBackground(c); + l.setAlignment(Label.RIGHT); + l.setFont(f); + add(l, index); + validate(); + return l; + } + } +} diff --git a/test/jdk/java/awt/PrintJob/ScaledImagePrintingTest.java b/test/jdk/java/awt/PrintJob/ScaledImagePrintingTest.java new file mode 100644 index 0000000000000..838c9210e308b --- /dev/null +++ b/test/jdk/java/awt/PrintJob/ScaledImagePrintingTest.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.PrintJob; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 4257962 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary tests that scaled images are printed at resolution greater than 72dpi + * @run main/manual ScaledImagePrintingTest + */ + +public class ScaledImagePrintingTest { + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Press 'Print' button from the test UI. + + The test will bring up a print dialog. Select a printer and proceed. + Verify that the output is a series of a horizontal lines in a + rectangular box in the center of the page. + + If output is as mentioned above, press Pass else Fail."""; + + PassFailJFrame.builder() + .title("ScaledImagePrintingTest Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testTimeOut(5) + .testUI(ScaledImagePrintingTest::createUI) + .logArea(8) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame frame = new Frame("ResolutionTest"); + Button b = new Button("Print"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + PrintJob pj = frame.getToolkit().getPrintJob(frame, "ResolutionTest", null); + PassFailJFrame.log("Printing code started."); + if (pj != null) { + Graphics g = pj.getGraphics(); + g.setColor(Color.black); + int w = 200; + int h = 200; + Image image = frame.createImage(w, h); + Graphics imageGraphics = image.getGraphics(); + Dimension d = pj.getPageDimension(); + imageGraphics.setColor(Color.black); + for (int i = 0; i < h; i += 20) { + imageGraphics.drawLine(0, i, w, i); + } + g.translate(d.width / 2, d.height / 2); + g.drawImage(image, -w / 8, -h / 8, w / 4, h / 4, frame); + g.setColor(Color.black); + g.drawRect(-w / 4, -h / 4, w / 2, h / 2); + imageGraphics.dispose(); + g.dispose(); + pj.end(); + } + PassFailJFrame.log("Printing code finished."); + } + }); + frame.add(b); + frame.setSize(50, 50); + return frame; + } +} diff --git a/test/jdk/java/awt/Rectangle/IntersectionTest.java b/test/jdk/java/awt/Rectangle/IntersectionTest.java new file mode 100644 index 0000000000000..41680b2e7b6b1 --- /dev/null +++ b/test/jdk/java/awt/Rectangle/IntersectionTest.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4147957 + * @key headful + * @summary Test to verify setClip with invalid rect changes rect to valid + * @run main IntersectionTest + */ + +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Panel; +import java.awt.Rectangle; +import java.awt.Robot; + +public class IntersectionTest { + public static Frame frame; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + try { + robot.setAutoDelay(100); + EventQueue.invokeAndWait(() -> { + TestFrame panel = new TestFrame(); + frame = new Frame("Rectangle Intersection Test"); + frame.add(panel); + + frame.pack(); + frame.setVisible(true); + }); + robot.waitForIdle(); + robot.delay(200); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } +} + +class TestFrame extends Panel { + @Override + public void paint(Graphics g) { + Rectangle r1 = new Rectangle(0, 0, 100, 100); + Rectangle r2 = new Rectangle(200, 200, 20, 20); + Rectangle r3 = r1.intersection(r2); + System.out.println("intersect:(" + (int) r3.getX() + "," + + (int) r3.getY() + "," + (int) r3.getWidth() + "," + + (int) r3.getHeight() + ")"); + g.setClip(r3); + Rectangle r4 = g.getClipBounds(); + System.out.println("getClipBounds:(" + (int) r4.getX() + "," + + (int) r4.getY() + "," + (int) r4.getWidth() + "," + + (int) r4.getHeight() + ")"); + + if ((r4.getWidth() <= 0) || (r4.getHeight() <= 0)) { + System.out.println("Test Passed"); + } else { + throw new RuntimeException("IntersectionTest failed. " + + "Non-empty clip bounds."); + } + } +} diff --git a/test/jdk/java/awt/Robot/CreateScreenCapture.java b/test/jdk/java/awt/Robot/CreateScreenCapture.java new file mode 100644 index 0000000000000..8060240ba8f1d --- /dev/null +++ b/test/jdk/java/awt/Robot/CreateScreenCapture.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4292503 + * @summary OutOfMemoryError with lots of Robot.createScreenCapture + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @requires (os.family == "linux") + * @run main/manual CreateScreenCapture +*/ + +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.Image; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.TextArea; + +public class CreateScreenCapture { + + static TextArea messageText; + + private static final String INSTRUCTIONS = """ + This test is linux only! + Once you see these instructions, run 'top' program. + Watch for java process. + The memory size used by this process should stop growing after several steps. + Numbers of steps test is performing are displayed in output window. + After 5-7 steps the size taken by the process should become stable. + If this happens, then test passed otherwise test failed. + + Small oscillations of the memory size are, however, acceptable."""; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + PassFailJFrame passFail = new PassFailJFrame(INSTRUCTIONS); + Dialog dialog = new Dialog(new Frame(), "Instructions"); + messageText = new TextArea("", 5, 80, TextArea.SCROLLBARS_BOTH); + dialog.add(messageText); + PassFailJFrame.addTestWindow(dialog); + PassFailJFrame.positionTestWindow(dialog, PassFailJFrame.Position.HORIZONTAL); + dialog.setSize(200, 300); + dialog.setVisible(true); + Rectangle rect = new Rectangle(0, 0, 1000, 1000); + for (int i = 0; i < 100; i++) { + Image image = robot.createScreenCapture(rect); + image.flush(); + image = null; + robot.delay(200); + log("step #" + i); + } + passFail.awaitAndCheck(); + } + + private static void log(String messageIn) { + messageText.append(messageIn + "\n"); + } +} + diff --git a/test/jdk/java/awt/Robot/RobotScrollTest.java b/test/jdk/java/awt/Robot/RobotScrollTest.java new file mode 100644 index 0000000000000..20dc9f2f4ebd9 --- /dev/null +++ b/test/jdk/java/awt/Robot/RobotScrollTest.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4374578 + * @summary Test robot wheel scrolling of Text + * @requires (os.family == "Windows") | (os.family == "linux") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual RobotScrollTest +*/ + +import java.awt.Frame; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.TextArea; + +public class RobotScrollTest { + + static TextArea ta; + static Robot robot; + + private static final String INSTRUCTIONS = """ + 0. DON'T TOUCH ANYTHING! + 1. This test is for Windows and Linux only. + 2. Just sit back, and watch the Robot move the mouse to the TextArea. + 3. Once the pointer is on the text area, the Robot will use the mouse wheel + to scroll the text. + If the text scrolled, press PASS, else, press FAIL."""; + + public static void main(String[] args) throws Exception { + robot = new Robot(); + robot.setAutoDelay(100); + PassFailJFrame passFail = new PassFailJFrame(INSTRUCTIONS); + createTestUI(); + passFail.awaitAndCheck(); + } + + private static void createTestUI() { + Frame f = new Frame("RobotScrollTest"); + ta = new TextArea(); + for (int i = 0; i < 100; i++) { + ta.append(i + "\n"); + } + f.add(ta); + f.setLocation(0, 400); + f.pack(); + PassFailJFrame.addTestWindow(f); + PassFailJFrame.positionTestWindow(f, PassFailJFrame.Position.HORIZONTAL); + f.setVisible(true); + doTest(); + } + + private static void doTest() { + robot.waitForIdle(); + robot.delay(1000); + // get loc of TextArea + Point taAt = ta.getLocationOnScreen(); + // get bounds of button + Rectangle bounds = ta.getBounds(); + + // move mouse to middle of button + robot.mouseMove(taAt.x + bounds.width / 2, + taAt.y + bounds.height / 2); + + // rotate wheel a few times + for (int j = 1; j < 8; j++) { + for (int k = 0; k < 5; k++) { + robot.mouseWheel(j); + } + + for (int k = 0; k < 5; k++) { + robot.mouseWheel(-1 * j); + } + } + } + +} + diff --git a/test/jdk/java/awt/ScrollPane/ScrollPaneAsNeededTest.java b/test/jdk/java/awt/ScrollPane/ScrollPaneAsNeededTest.java new file mode 100644 index 0000000000000..e4be90fae311d --- /dev/null +++ b/test/jdk/java/awt/ScrollPane/ScrollPaneAsNeededTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4152524 + * @summary ScrollPane AS_NEEDED always places scrollbars first time component + * is laid out + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ScrollPaneAsNeededTest + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Frame; +import java.awt.ScrollPane; + +public class ScrollPaneAsNeededTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. You will see a frame titled 'ScrollPane as needed' + of minimum possible size in the middle of the screen. + 2. If for the first resize of frame(using mouse) to + a very big size(may be, to half the area of the screen) + the scrollbars(any - horizontal, vertical or both) + appear, click FAIL else, click PASS. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(ScrollPaneAsNeededTest::initialize) + .build() + .awaitAndCheck(); + } + + static Frame initialize() { + Frame f = new Frame("ScrollPane as needed"); + f.setLayout(new BorderLayout()); + ScrollPane sp = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED); + sp.add(new Button("TEST")); + f.add("Center", sp); + f.setSize(200, 200); + return f; + } +} diff --git a/test/jdk/java/awt/ScrollPane/ScrollPaneComponentTest.java b/test/jdk/java/awt/ScrollPane/ScrollPaneComponentTest.java new file mode 100644 index 0000000000000..b2fc4c77b6321 --- /dev/null +++ b/test/jdk/java/awt/ScrollPane/ScrollPaneComponentTest.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4100671 + * @summary removing and adding back ScrollPane component does not work + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ScrollPaneComponentTest + */ + +import java.awt.Adjustable; +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Panel; +import java.awt.ScrollPane; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class ScrollPaneComponentTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Notice the scrollbars (horizontal and vertical) + in the Frame titled 'ScrollPane Component Test' + 2. Click the button labeled 'Remove and add back + ScrollPane Contents' + 3. If the Scrollbars (horizontal or vertical or both) + disappears in the Frame, then press FAIL, else press PASS. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(ScrollPaneComponentTest::initialize) + .build() + .awaitAndCheck(); + } + + static Frame initialize() { + Frame fr = new Frame("ScrollPane Component Test"); + fr.setLayout(new BorderLayout()); + ScrollTester test = new ScrollTester(); + + fr.add(test); + fr.pack(); + fr.setSize(200, 200); + + Adjustable vadj = test.pane.getVAdjustable(); + Adjustable hadj = test.pane.getHAdjustable(); + vadj.setUnitIncrement(5); + hadj.setUnitIncrement(5); + return fr; + } +} + +class Box extends Component { + public Dimension getPreferredSize() { + System.out.println("asked for size"); + return new Dimension(300, 300); + } + + public void paint(Graphics gr) { + super.paint(gr); + gr.setColor(Color.red); + gr.drawLine(5, 5, 295, 5); + gr.drawLine(295, 5, 295, 295); + gr.drawLine(295, 295, 5, 295); + gr.drawLine(5, 295, 5, 5); + System.out.println("Painted!!"); + } +} + +class ScrollTester extends Panel { + public ScrollPane pane; + private final Box child; + + class Handler implements ActionListener { + public void actionPerformed(ActionEvent e) { + System.out.println("Removing scrollable component"); + pane.remove(child); + System.out.println("Adding back scrollable component"); + pane.add(child); + System.out.println("Done Adding back scrollable component"); + } + } + + public ScrollTester() { + pane = new ScrollPane(); + pane.setSize(200, 200); + child = new Box(); + pane.add(child); + setLayout(new BorderLayout()); + Button changeScrollContents = new Button("Remove and add back ScrollPane Contents"); + changeScrollContents.setBackground(Color.red); + changeScrollContents.addActionListener(new Handler()); + add("North", changeScrollContents); + add("Center", pane); + } +} diff --git a/test/jdk/java/awt/ScrollPane/ScrollPaneEventType.java b/test/jdk/java/awt/ScrollPane/ScrollPaneEventType.java new file mode 100644 index 0000000000000..47b4ae416c128 --- /dev/null +++ b/test/jdk/java/awt/ScrollPane/ScrollPaneEventType.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4075484 + * @summary Tests that events of different types are generated for the + * corresponding scroll actions. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ScrollPaneEventType + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.ScrollPane; +import java.awt.event.AdjustmentListener; + +public class ScrollPaneEventType { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. This test verifies that when user performs some scrolling operation on + ScrollPane the correct AdjustmentEvent is being generated. + 2. To test this, press on: + - scrollbar's arrows and verify that UNIT event is generated, + - scrollbar's grey area(non-thumb) and verify that BLOCK event is + generated, + - drag scrollbar's thumb and verify that TRACK event is generated + If you see correct events for both scroll bars then test is PASSED. + Otherwise it is FAILED. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(ScrollPaneEventType::initialize) + .logArea() + .build() + .awaitAndCheck(); + } + + static Frame initialize() { + Frame frame = new Frame("ScrollPane event type test"); + frame.setLayout(new BorderLayout()); + ScrollPane pane = new ScrollPane(ScrollPane.SCROLLBARS_ALWAYS); + pane.add(new Button("press") { + public Dimension getPreferredSize() { + return new Dimension(1000, 1000); + } + }); + + AdjustmentListener listener = e -> PassFailJFrame.log(e.toString()); + pane.getHAdjustable().addAdjustmentListener(listener); + pane.getVAdjustable().addAdjustmentListener(listener); + frame.add(pane); + frame.setSize(200, 200); + return frame; + } +} diff --git a/test/jdk/java/awt/ScrollPane/ScrollPaneFlicker.java b/test/jdk/java/awt/ScrollPane/ScrollPaneFlicker.java new file mode 100644 index 0000000000000..3594ff50f54c9 --- /dev/null +++ b/test/jdk/java/awt/ScrollPane/ScrollPaneFlicker.java @@ -0,0 +1,215 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4073822 + * @summary ScrollPane repaints entire window when scrolling fast + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ScrollPaneFlicker + */ + +import java.awt.Button; +import java.awt.Canvas; +import java.awt.Checkbox; +import java.awt.Choice; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Label; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.Panel; +import java.awt.Rectangle; +import java.awt.ScrollPane; +import java.awt.Scrollbar; +import java.awt.TextArea; +import java.awt.TextField; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollBar; +import javax.swing.JTextArea; +import javax.swing.JTextField; + +public class ScrollPaneFlicker { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + When scrolling a ScrollPane fast(i.e. holding the down/up arrow + down for a while), the ScrollPane would inexplicably refresh + the entire window. + + 1. Select a type of ScrollPane content from the content menu. + 2. Scroll the content using the up/down/left/right arrows on + the scroll bar. Try scrolling the entire content area using + the scroll arrows-- from top to bottom and left to right. + 3. Verify that the entire pane does not refresh when scrolling + - only the newly exposed areas should be repainting. + 4. Repeat for all content types. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(ScrollPaneFlicker::initialize) + .build() + .awaitAndCheck(); + } + + static Frame initialize() { + return new FlickerFrame(); + } +} + +class FlickerFrame extends Frame { + ScrollPane pane; + + public FlickerFrame() { + super("ScrollPane Flicker Test"); + TextPanel textPanel = new TextPanel(); + GradientPanel gradientPanel = new GradientPanel(); + ComponentPanel componentPanel = new ComponentPanel(); + SwingPanel swingPanel = new SwingPanel(); + MenuBar menubar = new MenuBar(); + Menu testMenu = new Menu("Test Options"); + + pane = new ScrollPane(); + pane.getHAdjustable().setUnitIncrement(8); + pane.getVAdjustable().setUnitIncrement(16); + pane.add(textPanel); + add(pane); + + testMenu.add(makeContentItem("Text Lines", textPanel)); + testMenu.add(makeContentItem("Gradient Fill", gradientPanel)); + testMenu.add(makeContentItem("AWT Components", componentPanel)); + testMenu.add(makeContentItem("Swing Components", swingPanel)); + menubar.add(testMenu); + + setMenuBar(menubar); + setSize(400, 300); + } + + public MenuItem makeContentItem(String title, final Component content) { + MenuItem menuItem = new MenuItem(title); + menuItem.addActionListener( + ev -> { + pane.add(content); + pane.validate(); + } + ); + return menuItem; + } +} + +class GradientPanel extends Canvas { + public void paint(Graphics g) { + // just paint something that'll take a while + int x, y; + int width = getSize().width; + int height = getSize().height; + int step = 8; + + for (x = 0; x < width; x += step) { + for (y = 0; y < height; y += step) { + int red = (255 * y) / height; + int green = (255 * x * y) / (width * height); + int blue = (255 * x) / width; + Rectangle bounds = g.getClipBounds(); + Rectangle fbounds = new Rectangle(x, y, x + step, y + step); + if (bounds.intersects(fbounds)) { + Color color = new Color(red, green, blue); + g.setColor(color); + g.fillRect(x, y, x + step, y + step); + } + } + } + } + + public Dimension getPreferredSize() { + return new Dimension(200, 1000); + } +} + +class TextPanel extends Canvas { + public void paint(Graphics g) { + Font font = new Font("SanSerif", Font.ITALIC, 12); + + g.setFont(font); + // just paint something that'll take a while + int x, y; + int width = getWidth(); + int height = getHeight(); + int step = 16; + + for (x = y = 0; y < height; y += step) { + Rectangle bounds = g.getClipBounds(); + Rectangle tbounds = new Rectangle(x, y - 16, x + width, y); + if (bounds.intersects(tbounds)) { + g.drawString(y + " : The quick brown fox jumps over the lazy dog. " + + "The rain in Spain falls mainly on the plain.", x, y); + } + } + } + + public Dimension getPreferredSize() { + return new Dimension(640, 1000); + } +} + +class ComponentPanel extends Panel { + ComponentPanel() { + add(new Label("Label")); + add(new Button("Button")); + add(new Checkbox("Checkbox")); + Choice c = new Choice(); + c.add("choice"); + java.awt.List l = new java.awt.List(); + l.add("list"); + add(new Scrollbar()); + add(new TextField("TextField")); + add(new TextArea("TextArea")); + add(new Panel()); + add(new Canvas()); + } +} + +class SwingPanel extends JPanel { + SwingPanel() { + add(new JLabel("JLabel")); + add(new JButton("JButton")); + add(new JCheckBox("JCheckBox")); + JComboBox c = new JComboBox(); + JList l = new JList(); + add(new JScrollBar()); + add(new JTextField("This is a JTextField with some text in it to make it longer.")); + add(new JTextArea("This is a JTextArea with some text in it to make it longer.")); + } +} diff --git a/test/jdk/java/awt/ScrollPane/ScrollPanePaint.java b/test/jdk/java/awt/ScrollPane/ScrollPanePaint.java new file mode 100644 index 0000000000000..0d7b7779018e7 --- /dev/null +++ b/test/jdk/java/awt/ScrollPane/ScrollPanePaint.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Licensed Materials - Property of IBM + * + * (C) Copyright IBM Corporation 1998 All Rights Reserved. + * + * US Government Users Restricted Rights - Use, duplication or disclosure + * restricted by GSA ADP Schedule Contract with IBM Corp. + */ + +/* + * @test + * @bug 4160721 + * @summary AWT ScrollPane painting problem + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ScrollPanePaint + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Color; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Panel; +import java.awt.ScrollPane; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.List; + +public class ScrollPanePaint { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Press the button marked "Toggle" a few times. + 2. The contents of the frame should alternate between + a red panel and a scroll pane containing a green panel. + If this does not happen (specifically, if the scroll + pane does not consistently contain a green panel), + then the test has FAILED. + """; + ScrollPaintTest scrollPaintTest = new ScrollPaintTest(); + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(scrollPaintTest::initialize) + .positionTestUI(WindowLayouts::rightOneColumn) + .build() + .awaitAndCheck(); + } + + private static class ScrollPaintTest implements ActionListener { + static Frame f; + static boolean showScroll; + + public List initialize() { + Frame frame = new Frame("Scrollpane paint test"); + frame.setLayout(new BorderLayout()); + f = new Frame("Scrollpane paint test"); + f.setLayout(new GridLayout(0, 1)); + + Button b = new Button("Toggle"); + b.addActionListener(this); + + frame.add(b, BorderLayout.CENTER); + frame.pack(); + + showScroll = false; + actionPerformed(null); + return List.of(frame, f); + } + + public void actionPerformed(ActionEvent e) { + Container c; + if (!showScroll) { + c = (Container) new TestPanel(new Dimension(100, 100)); + c.setBackground(Color.red); + } else { + c = new ScrollPane(ScrollPane.SCROLLBARS_ALWAYS); + Panel p = new TestPanel(new Dimension(20, 20)); + p.setBackground(Color.green); + c.add(p); + } + + f.removeAll(); + f.add("Center", c); + f.pack(); + showScroll = !showScroll; + } + } + + private static class TestPanel extends Panel { + Dimension dim; + + TestPanel(Dimension d) { + dim = d; + } + + public Dimension getMinimumSize() { + return getPreferredSize(); + } + + public Dimension getPreferredSize() { + return dim; + } + } + +} diff --git a/test/jdk/java/awt/ScrollPane/ScrollPaneSize.java b/test/jdk/java/awt/ScrollPane/ScrollPaneSize.java new file mode 100644 index 0000000000000..3130563d4a97c --- /dev/null +++ b/test/jdk/java/awt/ScrollPane/ScrollPaneSize.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4117404 + * @summary Tests that child component is always at least large as scrollpane + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ScrollPaneSize + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Insets; +import java.awt.Panel; +import java.awt.ScrollPane; +import java.util.List; + +public class ScrollPaneSize { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Three frames representing the three different ScrollPane scrollbar + policies will appear. + 2. Verify that when you resize the windows, the child component in the + scrollpane always expands to fill the scrollpane. The scrollpane + background is colored red to show any improper bleed through. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(ScrollPaneSize::initialize) + .positionTestUIRightColumn() + .build() + .awaitAndCheck(); + } + + static List initialize() { + return List.of(new ScrollFrame("SCROLLBARS_AS_NEEDED", ScrollPane.SCROLLBARS_AS_NEEDED), + new ScrollFrame("SCROLLBARS_ALWAYS", ScrollPane.SCROLLBARS_ALWAYS), + new ScrollFrame("SCROLLBARS_NEVER", ScrollPane.SCROLLBARS_NEVER)); + } +} + +class ScrollFrame extends Frame { + ScrollFrame(String title, int policy) { + super(title); + setLayout(new GridLayout(1, 1)); + ScrollPane c = new ScrollPane(policy); + c.setBackground(Color.red); + Panel panel = new TestPanel(); + c.add(panel); + add(c); + pack(); + Dimension size = panel.getPreferredSize(); + Insets insets = getInsets(); + setSize(size.width + 45 + insets.right + insets.left, + size.height + 20 + insets.top + insets.bottom); + } +} + +class TestPanel extends Panel { + TestPanel() { + setLayout(new FlowLayout()); + setBackground(Color.white); + + Button b1, b2, b3; + add(b1 = new Button("Button1")); + add(b2 = new Button("Button2")); + add(b3 = new Button("Button3")); + } +} diff --git a/test/jdk/java/awt/ScrollPane/ScrollPanechildViewportTest.java b/test/jdk/java/awt/ScrollPane/ScrollPanechildViewportTest.java new file mode 100644 index 0000000000000..0dc0772aebafd --- /dev/null +++ b/test/jdk/java/awt/ScrollPane/ScrollPanechildViewportTest.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4094581 + * @summary ScrollPane does not refresh properly when child is just smaller than viewport + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ScrollPanechildViewportTest + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.ScrollPane; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class ScrollPanechildViewportTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Click "Slightly Large" and ensure scrollbars are VISIBLE + 2. Click "Slightly Small" and ensure there are NO scrollbars + 3. Click "Smaller" and ensure there are NO scrollbars + 4. If everything is ok, click PASS, else click FAIL. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(ScrollPanechildViewportTest::initialize) + .build() + .awaitAndCheck(); + } + + static Frame initialize() { + return new Test(); + } +} + +class Test extends Frame implements ActionListener { + Button b1, b2, b3; + MyPanel p; + int state; // 0 = slightly large, 1 = slightly smaller, 2 = smaller + + public Test() { + ScrollPane sp = new ScrollPane(); + p = new MyPanel(); + p.setBackground(Color.yellow); + state = 1; + sp.add(p); + add(sp, "Center"); + + Panel p1 = new Panel(); + b1 = new Button("Slightly Large"); + b1.addActionListener(this); + p1.add(b1); + b2 = new Button("Slightly Small"); + b2.addActionListener(this); + p1.add(b2); + b3 = new Button("Smaller"); + b3.addActionListener(this); + p1.add(b3); + + add(p1, "South"); + + setSize(400, 200); + //added to test to move test frame away from instructions + setLocation(0, 350); + } + + public void actionPerformed(ActionEvent e) { + Object source = e.getSource(); + + // set size to small and re-validate the panel to get correct size of + // scrollpane viewport without scrollbars + + state = 2; + p.invalidate(); + validate(); + + Dimension pd = ((ScrollPane) p.getParent()).getViewportSize(); + + if (source.equals(b1)) { + p.setBackground(Color.green); + state = 0; + } else if (source.equals(b2)) { + p.setBackground(Color.yellow); + state = 1; + } else if (source.equals(b3)) { + p.setBackground(Color.red); + state = 2; + } + + p.invalidate(); + validate(); + System.out.println("Panel Size = " + p.getSize()); + System.out.println("ScrollPane Viewport Size = " + pd); + System.out.println(" "); + } + + class MyPanel extends Panel { + public Dimension getPreferredSize() { + Dimension d = null; + Dimension pd = ((ScrollPane) getParent()).getViewportSize(); + switch (state) { + case 0 -> { + d = new Dimension(pd.width + 2, pd.height + 2); + System.out.println("Preferred size: " + d); + } + case 1 -> { + d = new Dimension(pd.width - 2, pd.height - 2); + System.out.println("Preferred size: " + d); + } + case 2 -> { + d = new Dimension(50, 50); + System.out.println("Preferred size: " + d); + } + } + return d; + } + } +} diff --git a/test/jdk/java/awt/ScrollPane/ScrollPositionTest.java b/test/jdk/java/awt/ScrollPane/ScrollPositionTest.java new file mode 100644 index 0000000000000..9c082a8dfc910 --- /dev/null +++ b/test/jdk/java/awt/ScrollPane/ScrollPositionTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4008152 + * @summary ScrollPane position does not return correct values + * @key headful + * @run main ScrollPositionTest + */ + +import java.awt.Adjustable; +import java.awt.BorderLayout; +import java.awt.Canvas; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Robot; +import java.awt.ScrollPane; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; + +public class ScrollPositionTest { + static Frame frame; + static int i = 0; + static Point p; + static ScrollPane sp; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + try { + EventQueue.invokeAndWait(() -> { + frame = new Frame("Scroll Position Test"); + frame.setLayout(new BorderLayout()); + frame.setSize(200, 200); + sp = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED); + Canvas canvas = new Canvas(); + canvas.setSize(300, 300); + sp.add(canvas); + frame.add("Center", sp); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + }); + robot.waitForIdle(); + robot.delay(1000); + EventQueue.invokeAndWait(() -> { + Adjustable saH = sp.getHAdjustable(); + saH.addAdjustmentListener(new TestAdjustmentListener()); + }); + for (i = 0; i < 1000; i++) { + EventQueue.invokeAndWait(() -> { + p = new Point(i % 100, i % 100); + sp.setScrollPosition(p); + }); + + robot.waitForIdle(); + robot.delay(10); + EventQueue.invokeAndWait(() -> { + if (!sp.getScrollPosition().equals(p)) { + throw new RuntimeException("Test failed. " + i + " : " + + "Expected " + p + ", but Returned: " + sp.getScrollPosition()); + } + }); + } + System.out.println("Test Passed."); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static class TestAdjustmentListener implements AdjustmentListener { + public void adjustmentValueChanged(AdjustmentEvent e) { + System.out.println("AdjEvent caught:" + e); + } + } +} diff --git a/test/jdk/java/awt/ScrollPane/ScrollbarsAsNeededTest.java b/test/jdk/java/awt/ScrollPane/ScrollbarsAsNeededTest.java new file mode 100644 index 0000000000000..c5f48f8000701 --- /dev/null +++ b/test/jdk/java/awt/ScrollPane/ScrollbarsAsNeededTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4094248 + * @summary Test initial appearance of SCROLLBARS_AS_NEEDED policy + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ScrollbarsAsNeededTest + */ + +import java.awt.Color; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.ScrollPane; + +public class ScrollbarsAsNeededTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. A Frame window with a ScrollPane that is + initially created with the SCROLLBARS_AS_NEEDED policy. + 2. If there are no scrollbars around the ScrollPane then + the test PASS. Otherwise the test FAILS. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(ScrollbarsAsNeededTest::initialize) + .build() + .awaitAndCheck(); + } + + static Frame initialize() { + Frame frame = new Frame("Scrollbar as needed test"); + ScrollPane scrollPane = new ScrollPane() { + @Override + public void paint(Graphics g) { + super.paint(g); + g.drawString("ScrollPane", 10, 50); + } + }; + scrollPane.setBackground(Color.WHITE); + frame.setBackground(Color.GRAY); + frame.setSize(200, 200); + frame.setLayout(new FlowLayout()); + frame.add(scrollPane); + return frame; + } +} diff --git a/test/jdk/java/awt/Scrollbar/ListScrollbarTest.java b/test/jdk/java/awt/Scrollbar/ListScrollbarTest.java new file mode 100644 index 0000000000000..fb441fe44ba1c --- /dev/null +++ b/test/jdk/java/awt/Scrollbar/ListScrollbarTest.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4029465 + * @summary Win95 Multiselect List doesn't display scrollbar + * @key headful + * @run main ListScrollbarTest + */ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.List; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; + +public class ListScrollbarTest { + + private static final Color BG_COLOR = Color.RED; + private static Robot robot; + private static Frame frame; + private static List list; + private static int counter = 0; + private static volatile Rectangle listBounds; + + public static void main(String[] args) throws Exception { + try { + EventQueue.invokeAndWait(ListScrollbarTest::createAndShowUI); + test(); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void test() throws Exception { + robot = new Robot(); + robot.waitForIdle(); + robot.delay(500); + + EventQueue.invokeAndWait(() -> { + Point locationOnScreen = list.getLocationOnScreen(); + Dimension size = list.getSize(); + listBounds = new Rectangle(locationOnScreen, size); + }); + + Point point = new Point(listBounds.x + listBounds.width - 5, + listBounds.y + listBounds.height / 2); + + + for (int i = 0; i < 4; i++) { + scrollbarCheck(point, false); + addListItem(); + } + scrollbarCheck(point, true); + } + + public static boolean areColorsSimilar(Color c1, Color c2, int tolerance) { + return Math.abs(c1.getRed() - c2.getRed()) <= tolerance + && Math.abs(c1.getGreen() - c2.getGreen()) <= tolerance + && Math.abs(c1.getBlue() - c2.getBlue()) <= tolerance; + } + + private static void scrollbarCheck(Point point, boolean isScrollbarExpected) { + Color pixelColor = robot.getPixelColor(point.x, point.y); + boolean areColorsSimilar = areColorsSimilar(BG_COLOR, pixelColor, 5); + + if (isScrollbarExpected && areColorsSimilar) { + throw new RuntimeException((""" + Scrollbar is expected, but pixel color \ + is similar to the background color + %s pixel color + %s bg color""") + .formatted(pixelColor, BG_COLOR)); + } + + if (!isScrollbarExpected && !areColorsSimilar) { + throw new RuntimeException((""" + Scrollbar is not expected, but pixel color \ + is not similar to the background color + %s pixel color + %s bg color""") + .formatted(pixelColor, BG_COLOR)); + } + } + + private static void addListItem() throws Exception { + EventQueue.invokeAndWait(() -> { + counter++; + System.out.println("Adding list item " + counter); + list.add("List Item " + counter); + frame.validate(); + }); + robot.waitForIdle(); + robot.delay(150); + } + + private static void createAndShowUI() { + frame = new Frame("ListScrollbarTest"); + list = new List(3, true); + list.setBackground(BG_COLOR); + + // do not draw border around items, it can affect screen capture + list.setFocusable(false); + + frame.add(list); + + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } +} diff --git a/test/jdk/java/awt/Scrollbar/ScrollbarCtrlClickTest.java b/test/jdk/java/awt/Scrollbar/ScrollbarCtrlClickTest.java new file mode 100644 index 0000000000000..3b5a24008fd8c --- /dev/null +++ b/test/jdk/java/awt/Scrollbar/ScrollbarCtrlClickTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4075950 + * @summary Test for functionality of Control Click on Scrollbar + * @key headful + * @run main ScrollbarCtrlClickTest + */ + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.Scrollbar; +import java.awt.TextArea; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class ScrollbarCtrlClickTest { + private static Frame frame; + private static TextArea ta; + private static Scrollbar scrollbar; + private static final CountDownLatch latch = new CountDownLatch(1); + private static volatile Rectangle sbBounds; + + public static void main(String[] args) throws Exception { + try { + EventQueue.invokeAndWait(ScrollbarCtrlClickTest::initAndShowGUI); + test(); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void initAndShowGUI() { + frame = new Frame("ScrollbarDimensionTest"); + ta = new TextArea("", 30, 100); + + + scrollbar = new Scrollbar(Scrollbar.VERTICAL, + 0, 10, 0, 20); + + // Just setting layout so scrollbar thumb will be big enough to use + frame.setLayout(new BorderLayout()); + frame.add("East", scrollbar); + frame.add("West", ta); + + scrollbar.addAdjustmentListener(e -> { + System.out.println(e.paramString()); + ta.append(e.paramString() + "\n"); + latch.countDown(); + }); + + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private static void test() throws Exception { + Robot robot = new Robot(); + robot.waitForIdle(); + robot.setAutoDelay(25); + robot.delay(500); + + EventQueue.invokeAndWait(() -> { + Point locationOnScreen = scrollbar.getLocationOnScreen(); + Dimension size = scrollbar.getSize(); + sbBounds = new Rectangle(locationOnScreen, size); + }); + + robot.mouseMove(sbBounds.x + sbBounds.width / 2, + sbBounds.y + sbBounds.height - 50); + + robot.keyPress(KeyEvent.VK_CONTROL); + robot.mousePress(MouseEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(MouseEvent.BUTTON1_DOWN_MASK); + robot.keyRelease(KeyEvent.VK_CONTROL); + + if (!latch.await(1, TimeUnit.SECONDS)) { + throw new RuntimeException("Timed out waiting for latch"); + } + } +} diff --git a/test/jdk/java/awt/Scrollbar/UnitIncrementTest.java b/test/jdk/java/awt/Scrollbar/UnitIncrementTest.java new file mode 100644 index 0000000000000..03dba0f3a994b --- /dev/null +++ b/test/jdk/java/awt/Scrollbar/UnitIncrementTest.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4169461 + * @summary Test for Motif Scrollbar unit increment + * @key headful + * @run main UnitIncrementTest + */ + +import javax.swing.UIManager; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.Scrollbar; +import java.awt.event.AdjustmentEvent; +import java.awt.event.MouseEvent; +import java.util.ArrayList; + +public class UnitIncrementTest { + private static Frame frame; + private static Scrollbar scrollbar; + private static final java.util.List eventsList = new ArrayList<>(); + private static final int UNIT_INCREMENT_VALUE = 5; + private static final int INCREMENTS_COUNT = 10; + private static volatile Rectangle scrollbarBounds; + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel"); + + try { + EventQueue.invokeAndWait(UnitIncrementTest::createAndShowUI); + test(); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void createAndShowUI() { + frame = new Frame("UnitIncrementTest"); + + scrollbar = new Scrollbar(Scrollbar.HORIZONTAL); + + scrollbar.setUnitIncrement(UNIT_INCREMENT_VALUE); + scrollbar.setBlockIncrement(20); + + scrollbar.addAdjustmentListener(e -> { + eventsList.add(e); + System.out.println(e); + }); + + frame.add(scrollbar); + + frame.setSize(300, 100); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private static void test() throws Exception { + Robot robot = new Robot(); + robot.waitForIdle(); + robot.setAutoDelay(25); + robot.delay(500); + + EventQueue.invokeAndWait(() -> { + Point locationOnScreen = scrollbar.getLocationOnScreen(); + Dimension size = scrollbar.getSize(); + scrollbarBounds = new Rectangle(locationOnScreen, size); + }); + + robot.mouseMove(scrollbarBounds.x + scrollbarBounds.width - 10, + scrollbarBounds.y + scrollbarBounds.height / 2); + + for (int i = 0; i < INCREMENTS_COUNT; i++) { + robot.mousePress(MouseEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(MouseEvent.BUTTON1_DOWN_MASK); + robot.delay(150); + } + + robot.waitForIdle(); + robot.delay(250); + + if (eventsList.size() != INCREMENTS_COUNT) { + throw new RuntimeException("Wrong number of events: " + eventsList.size()); + } + + int oldValue = 0; + for (AdjustmentEvent event : eventsList) { + System.out.println("\nChecking event " + event); + + int diff = event.getValue() - oldValue; + System.out.printf("diff: %d - %d = %d\n", event.getValue(), oldValue, diff); + + if (diff != UNIT_INCREMENT_VALUE) { + throw new RuntimeException("Unexpected adjustment value: %d".formatted(diff)); + } + + oldValue = event.getValue(); + } + } +} diff --git a/test/jdk/java/awt/Selection/TestClipboard.java b/test/jdk/java/awt/Selection/TestClipboard.java new file mode 100644 index 0000000000000..ed65d55489c3c --- /dev/null +++ b/test/jdk/java/awt/Selection/TestClipboard.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import java.io.Serializable; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JOptionPane; + +/* + * @test + * @bug 4139552 + * @summary Checks to see if 'isDataFlavorSupported' throws exception. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual TestClipboard + */ + +public class TestClipboard { + + public static void main(String[] args) throws Exception { + final String INSTRUCTIONS = """ + This test has two steps: + + 1. you need to place some text onto the system clipboard, + for example, + on Windows, you could highlight some text in notepad, and do a Ctrl-C + or select menu Edit->Copy; + + on Linux or Mac, you can do the same with any Terminal or Console or + Text application. + + 2. After you copy to system clipboard, press "Click Me" button. + + Test will fail if any exception is thrown. + + Press Pass if you see "Test Passed" in log area."""; + + PassFailJFrame.builder() + .title("TestClipboard Instruction") + .instructions(INSTRUCTIONS) + .columns(45) + .testUI(TestClipboard::createUI) + .logArea(4) + .build() + .awaitAndCheck(); + } + + private static JFrame createUI() { + JFrame f = new JFrame("ChildFrameIconTest UI"); + JButton b = new JButton("Click Me"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + try { + new MyTest(); + } catch (Exception ex) { + throw new RuntimeException("Exception Thrown : " + ex); + } + } + }); + f.add(b); + f.setSize(200, 100); + return f; + } + + static class MyFlavor extends Object implements Serializable { + // Stub class needed in order to define the data flavor type + } + + static class MyTest { + public MyTest() throws Exception { + // Create an arbitrary dataflavor + DataFlavor myFlavor = new DataFlavor(MyFlavor.class, "TestClipboard"); + // Get the system clipboard + Clipboard theClipboard = + Toolkit.getDefaultToolkit().getSystemClipboard(); + // Get the current contents of the clipboard + Transferable theTransfer = theClipboard.getContents(this); + + // See if the flavor is supported. This may result in a null + // pointer exception. + theTransfer.isDataFlavorSupported(myFlavor); + PassFailJFrame.log("Test Passed"); + } + } +} diff --git a/test/jdk/java/awt/TextArea/TextAreaCursorTest/HoveringAndDraggingTest.html b/test/jdk/java/awt/TextArea/TextAreaCursorTest/HoveringAndDraggingTest.html deleted file mode 100644 index 9fc7f55ceba29..0000000000000 --- a/test/jdk/java/awt/TextArea/TextAreaCursorTest/HoveringAndDraggingTest.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - HoveringAndDraggingTest - - - -

    HoveringAndDraggingTest
    Bug ID: 6497109

    - -

    See the dialog box (usually in upper left corner) for instructions

    - - - - diff --git a/test/jdk/java/awt/TextArea/TextAreaCursorTest/HoveringAndDraggingTest.java b/test/jdk/java/awt/TextArea/TextAreaCursorTest/HoveringAndDraggingTest.java index 753fc4909674a..646bc6aa6b976 100644 --- a/test/jdk/java/awt/TextArea/TextAreaCursorTest/HoveringAndDraggingTest.java +++ b/test/jdk/java/awt/TextArea/TextAreaCursorTest/HoveringAndDraggingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,71 +21,122 @@ * questions. */ -/* - test - @bug 6497109 - @summary Mouse cursor icons for TextArea should be correct in case of hovering or dragging mouse over different subcomponents. - @author Konstantin Voloshin: area=awt.TextArea - @run applet/manual=yesno HoveringAndDraggingTest.html -*/ - -/** - * HoveringAndDraggingTest.java - * - * summary: Mouse cursor icons for TextArea should be correct in case - * of hovering or dragging mouse over different subcomponents. - */ - +import java.awt.Button; +import java.awt.Dimension; +import java.awt.EventQueue; import java.awt.Frame; -import java.awt.Panel; +import java.awt.GridBagLayout; import java.awt.GridLayout; +import java.awt.Panel; import java.awt.TextArea; -import java.awt.Dialog; +import java.util.concurrent.CountDownLatch; -public class HoveringAndDraggingTest extends java.applet.Applet { - public void start() { - String[] instructions = new String[] { - "1. Notice components in test window: main-panel, box-for-text," - +" 2 scroll-sliders, and 4 scroll-buttons.", - "2. Hover mouse over box-for-text." - +" Make sure, that mouse cursor is TextCursor (a.k.a. \"beam\").", - "3. Hover mouse over each of components (see item 1), except for box-for-text." - +" Make sure, that cursor is DefaultCursor (arrow).", - "4. Drag mouse (using any mouse button) from box-for-text to every" - +" component in item 1, and also outside application window." - +" Make sure, that cursor remains TextCursor while mouse button is pressed.", - "5. Repeat item 4 for each other component in item 1, except for box-for-text," - +" _but_ now make sure that cursor is DefaultCursor.", - "6. If cursor behaves as described in items 2-3-4-5, then test passed; otherwise it failed." - }; - Sysout.createDialogWithInstructions( instructions ); +/* + * @test + * @bug 6497109 + * @summary Mouse cursor icons for TextArea should be correct in case of + * hovering or dragging mouse over different subcomponents. + * @run main/manual HoveringAndDraggingTest + */ + +public class HoveringAndDraggingTest { + static Frame frame; + static Frame instructionsFrame; + static CountDownLatch countDownLatch; + public static CountDownLatch createCountDownLatch() { + return new CountDownLatch(1); + } + public static void main(String[] args) throws Exception { + countDownLatch = createCountDownLatch(); + EventQueue.invokeAndWait(() -> { + initialize(); + showInstructionFrame(); + }); + countDownLatch.await(); + System.out.println("Test Pass"); + } + + public static void initialize() { Panel panel = new Panel(); - panel.setLayout( new GridLayout(3,3) ); + panel.setLayout(new GridLayout(3, 3)); - for( int y=0; y<3; ++y ) { - for( int x=0; x<3; ++x ) { - if( x==1 && y==1 ) { - panel.add( new TextArea( bigString() ) ); + for (int y = 0; y < 3; ++y) { + for (int x = 0; x < 3; ++x) { + if (x == 1 && y == 1) { + panel.add(new TextArea(bigString())); } else { - panel.add( new Panel() ); + panel.add(new Panel()); } } } - Frame frame = new Frame( "TextArea cursor icon test" ); - frame.setSize( 300, 300 ); - frame.add( panel ); - frame.setVisible( true ); + frame = new Frame("TextArea cursor icon test"); + frame.setSize(300, 300); + frame.setLocation(450, 400); + frame.add(panel); + frame.setVisible(true); + } + + static void showInstructionFrame() { + String INSTRUCTIONS = """ + 1. Notice components in test window: main-panel,box-for-text, + 2 scroll-sliders, and 4 scroll-buttons. + 2. Hover mouse over box-for-text. + Make sure, that mouse cursor is TextCursor(a.k.a. \"beam\"). + 3. Hover mouse over each of components (see item 1), + except for box-for-text. + Make sure, that cursor is DefaultCursor (arrow). + 4. Drag mouse (using any mouse button) from box-for-text to every" + component in item 1, and also outside application window." + Make sure, that cursor remains TextCursor + while mouse button is pressed. + 5. Repeat item 4 for each other component in item 1, + except for box-for-text + _but_ now make sure that cursor is DefaultCursor. + 6. If cursor behaves as described in items 2-3-4-5, + then test is PASS otherwise it FAILED. + """; + TextArea textArea = new TextArea(INSTRUCTIONS, 16, 65, TextArea.SCROLLBARS_NONE); + Button passBtn = new Button("PASS"); + Button failBtn = new Button("FAIL"); + Panel btnPanel = new Panel(new GridBagLayout()); + Panel panel = new Panel(new GridBagLayout()); + instructionsFrame = new Frame("Test Instructions"); + passBtn.setMaximumSize(new Dimension(100, 30)); + failBtn.setMaximumSize(new Dimension(100, 30)); + btnPanel.add(passBtn); + btnPanel.add(failBtn); + passBtn.addActionListener(e -> disposeFrames()); + failBtn.addActionListener(e -> { + disposeFrames(); + throw new RuntimeException("Test Failed"); + }); + panel.add(textArea); + panel.add(btnPanel); + instructionsFrame.add(panel); + instructionsFrame.pack(); + instructionsFrame.setLocation(300, 100); + instructionsFrame.setVisible(true); + } + + static void disposeFrames() { + countDownLatch.countDown(); + if (frame != null) { + frame.dispose(); + } + if (instructionsFrame != null) { + instructionsFrame.dispose(); + } } static String bigString() { String s = ""; - for( int lines=0; ; ++lines ) { - for( int symbols=0; symbols<100; ++symbols ) { + for (int lines = 0; ; ++lines) { + for (int symbols = 0; symbols < 100; ++symbols) { s += "0"; } - if( lines<50 ) { + if (lines < 50) { s += "\n"; } else { break; @@ -94,141 +145,3 @@ static String bigString() { return s; } } - - -/**************************************************** - Standard Test Machinery - DO NOT modify anything below -- it's a standard - chunk of code whose purpose is to make user - interaction uniform, and thereby make it simpler - to read and understand someone else's test. - ****************************************************/ - -/** - This is part of the standard test machinery. - It creates a dialog (with the instructions), and is the interface - for sending text messages to the user. - To print the instructions, send an array of strings to Sysout.createDialog - WithInstructions method. Put one line of instructions per array entry. - To display a message for the tester to see, simply call Sysout.println - with the string to be displayed. - This mimics System.out.println but works within the test harness as well - as standalone. - */ - -class Sysout -{ - private static TestDialog dialog; - - public static void createDialogWithInstructions( String[] instructions ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - dialog.printInstructions( instructions ); - dialog.setVisible(true); - println( "Any messages for the tester will display here." ); - } - - public static void createDialog( ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - String[] defInstr = { "Instructions will appear here. ", "" } ; - dialog.printInstructions( defInstr ); - dialog.setVisible(true); - println( "Any messages for the tester will display here." ); - } - - - public static void printInstructions( String[] instructions ) - { - dialog.printInstructions( instructions ); - } - - - public static void println( String messageIn ) - { - dialog.displayMessage( messageIn ); - } - -}// Sysout class - -/** - This is part of the standard test machinery. It provides a place for the - test instructions to be displayed, and a place for interactive messages - to the user to be displayed. - To have the test instructions displayed, see Sysout. - To have a message to the user be displayed, see Sysout. - Do not call anything in this dialog directly. - */ -class TestDialog extends Dialog -{ - - TextArea instructionsText; - TextArea messageText; - int maxStringLength = 80; - - //DO NOT call this directly, go through Sysout - public TestDialog( Frame frame, String name ) - { - super( frame, name ); - int scrollBoth = TextArea.SCROLLBARS_BOTH; - instructionsText = new TextArea( "", 15, maxStringLength, scrollBoth ); - add( "North", instructionsText ); - - messageText = new TextArea( "", 5, maxStringLength, scrollBoth ); - add("Center", messageText); - - pack(); - - setVisible(true); - }// TestDialog() - - //DO NOT call this directly, go through Sysout - public void printInstructions( String[] instructions ) - { - //Clear out any current instructions - instructionsText.setText( "" ); - - //Go down array of instruction strings - - String printStr, remainingStr; - for( int i=0; i < instructions.length; i++ ) - { - //chop up each into pieces maxSringLength long - remainingStr = instructions[ i ]; - while( remainingStr.length() > 0 ) - { - //if longer than max then chop off first max chars to print - if( remainingStr.length() >= maxStringLength ) - { - //Try to chop on a word boundary - int posOfSpace = remainingStr. - lastIndexOf( ' ', maxStringLength - 1 ); - - if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1; - - printStr = remainingStr.substring( 0, posOfSpace + 1 ); - remainingStr = remainingStr.substring( posOfSpace + 1 ); - } - //else just print - else - { - printStr = remainingStr; - remainingStr = ""; - } - - instructionsText.append( printStr + "\n" ); - - }// while - - }// for - - }//printInstructions() - - //DO NOT call this directly, go through Sysout - public void displayMessage( String messageIn ) - { - messageText.append( messageIn + "\n" ); - System.out.println(messageIn); - } - -}// TestDialog class diff --git a/test/jdk/java/awt/TextComponent/AltPlusNumberKeyCombinationsTest.java b/test/jdk/java/awt/TextComponent/AltPlusNumberKeyCombinationsTest.java new file mode 100644 index 0000000000000..27599b0a91c9c --- /dev/null +++ b/test/jdk/java/awt/TextComponent/AltPlusNumberKeyCombinationsTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4737679 4623376 4501485 4740906 4708221 + * @requires (os.family == "windows") + * @summary Alt+Left/right/up/down generate characters in JTextArea + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual AltPlusNumberKeyCombinationsTest + */ + +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.TextArea; +import java.awt.TextField; + +public class AltPlusNumberKeyCombinationsTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + [WINDOWS PLATFORM ONLY] + Please do the following steps for both TextField and TextArea: + 1. Hold down ALT and press a NON-NUMPAD right arrow, then release + ALT key. If any symbol is typed the test failed. + 2. Hold down ALT and press one after another the following + NUMPAD keys: 0, 1, 2, 8. Release ALT key. If the Euro symbol + is not typed the test failed + 3. Hold down ALT and press one after another the following + NUMPAD keys: 0, 2, 2, 7. Release ALT key. If nothing or + the blank symbol is typed the test failed + If all the steps are done successfully the test PASSed, + else test FAILS. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(initialize()) + .build() + .awaitAndCheck(); + } + + public static Frame initialize() { + Frame f = new Frame("key combination test"); + f.setLayout(new FlowLayout()); + TextField tf = new TextField("TextField"); + f.add(tf); + TextArea ta = new TextArea("TextArea"); + f.add(ta); + f.setSize(200, 200); + return f; + } +} diff --git a/test/jdk/java/awt/TextComponent/BackgroundTest.java b/test/jdk/java/awt/TextComponent/BackgroundTest.java new file mode 100644 index 0000000000000..543f2cfd89311 --- /dev/null +++ b/test/jdk/java/awt/TextComponent/BackgroundTest.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4258667 4405602 + * @summary Make sure TextComponents are grayed out when non-editable + * if the background color has not been set by client code. + * Make sure TextComponents are not grayed out when non-editable + * if the background color has been set by client code. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual BackgroundTest + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.TextArea; +import java.awt.TextField; + +public class BackgroundTest { + private static final String enableString = "EnableText"; + private static final String disableString = "DisableText"; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. When the frame appears, it should have a blue background. + 2. The first TextField and TextArea will be the default color. + The second TextField and TextArea will be green. + 3. Press the "DisableText" button. + The first TextField and TextArea should change colors to the + default disabled color. On Windows, this is usually gray. + On linux and macos it will match the environment settings. + If the TextField or the TextArea do not change colors as described, + the test FAILS. + 4. The second TextField and TextArea should still be green. + If either of them are not green, the test FAILS. + Press the "EnableText" button (same button as before). + The first TextField and TextArea should return to their + original colors as described in the first paragraph. If they + do not, the test FAILS. + 5. The second TextField and TextArea should still be green. + If either of them are not green, the test FAILS. + Otherwise, the test PASSES. + """; + + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(BackgroundTest::initialize) + .build() + .awaitAndCheck(); + } + + public static Frame initialize() { + Frame frame = new Frame("Background Test"); + frame.setLayout(new FlowLayout()); + TextField tf = new TextField(30); + TextArea ta = new TextArea(4, 30); + TextField setTf = new TextField(30); + TextArea setTa = new TextArea(4, 30); + Button enableButton = new Button(disableString); + + enableButton.setBackground(Color.red); + frame.setSize(500, 250); + + frame.setBackground(Color.blue); + + tf.setText("Background not set - should be default"); + tf.setEditable(true); + frame.add(tf); + ta.setText("Background not set - should be default"); + ta.setEditable(true); + frame.add(ta); + + setTf.setText("Background is set - should be Green"); + setTf.setBackground(Color.green); + setTf.setEditable(true); + frame.add(setTf); + setTa.setText("Background is set - should be Green"); + setTa.setBackground(Color.green); + setTa.setEditable(true); + frame.add(setTa); + + enableButton.addActionListener(e -> { + boolean currentlyEditable = tf.isEditable(); + + if (currentlyEditable) { + tf.setEditable(false); + ta.setEditable(false); + setTf.setEditable(false); + setTa.setEditable(false); + enableButton.setLabel(enableString); + } else { + tf.setEditable(true); + ta.setEditable(true); + setTf.setEditable(true); + setTa.setEditable(true); + enableButton.setLabel(disableString); + } + }); + frame.add(enableButton); + return frame; + } +} \ No newline at end of file diff --git a/test/jdk/java/awt/TextComponent/CorrectTextComponentSelectionTest.java b/test/jdk/java/awt/TextComponent/CorrectTextComponentSelectionTest.java new file mode 100644 index 0000000000000..9d3e6d8563733 --- /dev/null +++ b/test/jdk/java/awt/TextComponent/CorrectTextComponentSelectionTest.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 5100806 + * @summary TextArea.select(0,0) does not de-select the selected text properly + * @key headful + * @run main CorrectTextComponentSelectionTest + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Robot; +import java.awt.TextArea; +import java.awt.TextComponent; +import java.awt.TextField; +import java.lang.reflect.InvocationTargetException; + +public class CorrectTextComponentSelectionTest { + static TextField tf = new TextField("TextField"); + static TextArea ta = new TextArea("TextArea"); + static Robot r; + static Frame frame; + static volatile Color color_center; + static volatile Point loc; + + public static void main(String[] args) throws Exception { + try { + r = new Robot(); + EventQueue.invokeAndWait(() -> { + initialize(); + }); + r.waitForIdle(); + r.delay(1000); + + test(tf); + test(ta); + System.out.println("Test Passed!"); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + public static void initialize() { + frame = new Frame("TextComponent Selection Test"); + frame.setLayout(new BorderLayout()); + + // We should place to the text components the long strings in order to + // cover the components by the selection completely + String sf = ""; + for (int i = 0; i < 50; i++) { + sf = sf + " "; + } + tf.setText(sf); + // We check the color of the text component in order to find out the + // bug reproducible situation + tf.setForeground(Color.WHITE); + tf.setBackground(Color.WHITE); + + String sa = ""; + for (int i = 0; i < 50; i++) { + for (int j = 0; j < 50; j++) { + sa = sa + " "; + } + sa = sa + "\n"; + } + ta.setText(sa); + ta.setForeground(Color.WHITE); + ta.setBackground(Color.WHITE); + + frame.add(tf, "North"); + frame.add(ta, "South"); + frame.setSize(200, 200); + frame.setVisible(true); + } + + private static void test(TextComponent tc) throws Exception { + if (tc instanceof TextField) { + System.out.println("TextField testing ..."); + } else if (tc instanceof TextArea) { + System.out.println("TextArea testing ..."); + } + + r.waitForIdle(); + r.delay(100); + EventQueue.invokeAndWait(() -> { + tc.requestFocus(); + tc.selectAll(); + tc.select(0, 0); + }); + + r.waitForIdle(); + r.delay(100); + EventQueue.invokeAndWait(() -> { + loc = tc.getLocationOnScreen(); + }); + r.waitForIdle(); + r.delay(100); + + EventQueue.invokeAndWait(() -> { + color_center = r.getPixelColor(loc.x + tc.getWidth() / 2, loc.y + tc.getHeight() / 2); + }); + + System.out.println("Color of the text component (CENTER) =" + color_center); + System.out.println("White color=" + Color.WHITE); + + if (color_center.getRGB() != Color.WHITE.getRGB()) { + throw new RuntimeException("Test Failed"); + } + } +} diff --git a/test/jdk/java/awt/TextComponent/DisableTest.java b/test/jdk/java/awt/TextComponent/DisableTest.java new file mode 100644 index 0000000000000..820b9c8bd3034 --- /dev/null +++ b/test/jdk/java/awt/TextComponent/DisableTest.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 5042122 + * @summary Verifies the TextComponent is grayed out when disabled + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DisableTest + */ + +import javax.swing.BoxLayout; +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Component; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.TextArea; +import java.awt.TextField; +import java.awt.event.ActionListener; +import java.util.Vector; +import java.util.Iterator; + +public class DisableTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Click "Enable" and "Disable" buttons and verify the text + components are disabled and enabled correctly. + 2. Verify that the disabled text components are grayed + out and are uneditable. + 3. Click PASS or FAIL accordingly. + """; + + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(DisableTest::initialize) + .build() + .awaitAndCheck(); + } + + public static Frame initialize() { + Frame frame = new Frame("TextComponent Disabled test"); + frame.setLayout(new BorderLayout()); + frame.setSize(200, 200); + final Vector comps = new Vector(); + comps.add(new TextField("TextField")); + TextArea ta = new TextArea("TextArea", 2, 100, TextArea.SCROLLBARS_NONE); + comps.add(ta); + Panel pc = new Panel(); + pc.setLayout(new BoxLayout(pc, BoxLayout.Y_AXIS)); + Iterator iter = comps.iterator(); + while (iter.hasNext()) { + Component c = (Component) iter.next(); + c.setEnabled(false); + pc.add(c); + } + frame.add(pc, BorderLayout.CENTER); + Panel p = new Panel(); + final Button be = new Button("Enable"); + final Button bd = new Button("Disable"); + p.add(be); + p.add(bd); + ActionListener al = ev -> { + boolean enable = (ev.getSource() == be); + Iterator iterator = comps.iterator(); + while (iterator.hasNext()) { + Component c = (Component) iterator.next(); + c.setEnabled(enable); + } + }; + be.addActionListener(al); + bd.addActionListener(al); + frame.add(p, BorderLayout.SOUTH); + return frame; + } +} diff --git a/test/jdk/java/awt/TextComponent/ModifiersTest.java b/test/jdk/java/awt/TextComponent/ModifiersTest.java new file mode 100644 index 0000000000000..e9e76a9b694b3 --- /dev/null +++ b/test/jdk/java/awt/TextComponent/ModifiersTest.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4035364 + * @summary Checks that Caps Lock key works + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ModifiersTest + */ + +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Label; +import java.awt.TextArea; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; + +public class ModifiersTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Type some text in the TextArea in upper and lowercase, + using the Caps Lock ON/OFF. + 2. If Caps Lock toggles correctly and you are able to type in + both cases, the test PASS. Else Test FAILS. + """; + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(ModifiersTest::initialize) + .build() + .awaitAndCheck(); + } + + public static Frame initialize() { + Frame frame = new Frame("Modifiers Test"); + frame.setLayout(new GridLayout(1, 1)); + frame.addKeyListener(new KeyChecker()); + frame.setLayout(new GridLayout(2, 1)); + Label label = new Label("See if you can type in upper and lowercase using Caps Lock:"); + frame.add(label); + TextArea ta = new TextArea(); + frame.add(ta); + ta.addKeyListener(new KeyChecker()); + ta.requestFocus(); + frame.setSize(400, 300); + return frame; + } +} + +// a KeyListener for debugging purposes +class KeyChecker extends KeyAdapter { + public void keyPressed(KeyEvent ev) { + System.out.println(ev); + } + + public void keyReleased(KeyEvent ev) { + System.out.println(ev); + } + + public void keyTyped(KeyEvent ev) { + System.out.println(ev); + } +} diff --git a/test/jdk/java/awt/TextComponent/SelectionAndCaretColor.java b/test/jdk/java/awt/TextComponent/SelectionAndCaretColor.java new file mode 100644 index 0000000000000..60b932e4be8df --- /dev/null +++ b/test/jdk/java/awt/TextComponent/SelectionAndCaretColor.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6287895 + * @requires (os.family == "linux") + * @summary Test cursor and selected text incorrectly colored in TextField + * @key headful + * @run main SelectionAndCaretColor + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.TextArea; +import java.awt.TextComponent; +import java.awt.TextField; +import java.awt.image.BufferedImage; + +public class SelectionAndCaretColor { + static TextField tf = new TextField(20); + static TextArea ta = new TextArea("", 1, 20, TextArea.SCROLLBARS_NONE); + static Robot r; + static Frame frame; + static volatile int flips; + + public static void main(String[] args) throws Exception { + try { + frame = new Frame("Selection and Caret color test"); + r = new Robot(); + + EventQueue.invokeAndWait(() -> { + frame.setLayout(new BorderLayout()); + tf.setFont(new Font("Monospaced", Font.PLAIN, 15)); + ta.setFont(new Font("Monospaced", Font.PLAIN, 15)); + + frame.add(tf, BorderLayout.NORTH); + frame.add(ta, BorderLayout.SOUTH); + frame.setSize(200, 200); + frame.setVisible(true); + }); + r.waitForIdle(); + r.delay(1000); + test(tf); + test(ta); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static int countFlips(TextComponent tc) { + int y = tc.getLocationOnScreen().y + tc.getHeight() / 2; + int x1 = tc.getLocationOnScreen().x + 5; + int x2 = tc.getLocationOnScreen().x + tc.getWidth() - 5; + + int[] fb = {tc.getBackground().getRGB(), tc.getForeground().getRGB()}; + int i = 0; + int flips = 0; + + BufferedImage img = r.createScreenCapture(new Rectangle(x1, y, x2 - x1, 1)); + for (int x = 0; x < x2 - x1; x++) { + int c = img.getRGB(x, 0); + if (c == fb[i]) { + ; + } else if (c == fb[1 - i]) { + flips++; + i = 1 - i; + } else { + throw new RuntimeException("Invalid color detected: " + + Integer.toString(c, 16) + " instead of " + + Integer.toString(fb[i], 16)); + } + } + return flips; + } + + private static void test(TextComponent tc) throws Exception { + if (tc instanceof TextField) { + System.out.println("TextField testing ..."); + } else if (tc instanceof TextArea) { + System.out.println("TextArea testing ..."); + } + + // now passing along the component's vertical center, + // skipping 5px from both sides, + // we should see bg - textcolor - bg - selcolor - + // seltextcolor - selcolor - bg + // that is bg-fg-bg-fg-bg-fg-bg, 6 flips + + EventQueue.invokeAndWait(() -> { + tc.setForeground(Color.green); + tc.setBackground(Color.magenta); + + tc.setText(" I I "); + tc.select(5, 10); + tc.requestFocus(); + }); + r.waitForIdle(); + r.delay(200); + EventQueue.invokeAndWait(() -> { + flips = countFlips(tc); + }); + if (flips != 6) { + throw new RuntimeException("Invalid number of flips: " + + flips + " instead of 6"); + } + EventQueue.invokeAndWait(() -> { + // same for caret: spaces in the tc, caret in the middle + // bg-fg-bg - 2 flips + + tc.select(0, 0); + tc.setText(" "); + tc.setCaretPosition(5); + }); + r.waitForIdle(); + r.delay(200); + + for (int i = 0; i < 10; i++) { + EventQueue.invokeAndWait(() -> { + flips = countFlips(tc); + }); + + if (flips == 2) { + break; + } + if (flips == 0) { + continue; + } + throw new RuntimeException("Invalid number of flips: " + + flips + " instead of 2"); + } + } +} diff --git a/test/jdk/java/awt/TextComponent/SelectionTest.java b/test/jdk/java/awt/TextComponent/SelectionTest.java new file mode 100644 index 0000000000000..d29818d762176 --- /dev/null +++ b/test/jdk/java/awt/TextComponent/SelectionTest.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4056231 + * @summary Checks that TextComponents don't grab the global CDE selection + * upon construction if their own selection is null. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual SelectionTest + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Frame; +import java.awt.TextArea; +import java.awt.TextField; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class SelectionTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. "Select some text in another window, then click the button.", + 2. "If the text in the other window is de-selected, the test FAILS.", + "If the text remains selected, the test PASSES." + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(initialize()) + .build() + .awaitAndCheck(); + } + + public static Frame initialize() { + Frame frame = new Frame("Selection Test"); + frame.setLayout(new BorderLayout()); + Button b = new Button("Select some text in another window, then" + + " click me"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + frame.add(new TextField("text field test")); + frame.add(new TextArea("text area test")); + } + }); + frame.add(b); + frame.setSize(400, 400); + return frame; + } +} diff --git a/test/jdk/java/awt/TextComponent/TextFieldMargin.java b/test/jdk/java/awt/TextComponent/TextFieldMargin.java new file mode 100644 index 0000000000000..6baf144254c1f --- /dev/null +++ b/test/jdk/java/awt/TextComponent/TextFieldMargin.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4129511 + * @summary Tests that TextField margins are not exceedingly wide + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual TextFieldMargin + */ + +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Label; +import java.awt.TextArea; +import java.awt.TextField; + +public class TextFieldMargin { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Examine the TextField, Label, and TextArea to see + that the text is vertically aligned along the left + 2. If all are aligned along the left, then test PASS, + else test FAILS. + """; + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(TextFieldMargin::initialize) + .build() + .awaitAndCheck(); + } + + public static Frame initialize() { + Frame frame = new Frame("Frame with a text field & a label"); + frame.setLayout(new GridLayout(5, 1)); + TextField text_field = new TextField("Left Textfield"); + frame.add(text_field); + Label label = new Label("Left Label"); + frame.add(label); + TextArea text_area = new TextArea("Left Textfield"); + frame.add(text_area); + frame.setBounds(50, 50, 300, 300); + return frame; + } +} diff --git a/test/jdk/java/awt/TextField/GetTextTest/GetTextTest.java b/test/jdk/java/awt/TextField/GetTextTest/GetTextTest.java new file mode 100644 index 0000000000000..ab5aebc11d47a --- /dev/null +++ b/test/jdk/java/awt/TextField/GetTextTest/GetTextTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4100188 + * @key headful + * @summary Make sure that TextFields contain all of, + * and exactly, the text that was entered into them. + * @run main GetTextTest + */ + +import java.awt.AWTException; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Point; +import java.awt.Robot; +import java.awt.TextField; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.lang.reflect.InvocationTargetException; + +public class GetTextTest extends Frame implements ActionListener { + private final String s = "test string"; + private volatile String ac; + private TextField t; + private Point location; + private Dimension size; + + public void setupGUI() { + setLayout(new FlowLayout(FlowLayout.LEFT)); + + t = new TextField(s, 32); + add(new Label("Hit after text")); + add(t); + t.addActionListener(this); + setLocationRelativeTo(null); + pack(); + setVisible(true); + } + + public void actionPerformed(ActionEvent evt) { + ac = evt.getActionCommand(); + } + + public void performTest() throws AWTException, InterruptedException, + InvocationTargetException { + EventQueue.invokeAndWait(() -> { + location = t.getLocationOnScreen(); + size = t.getSize(); + }); + Robot robot = new Robot(); + robot.setAutoDelay(50); + robot.delay(1000); + robot.waitForIdle(); + robot.mouseMove(location.x + size.width - 3, location.y + (size.height / 2)); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.delay(1000); + robot.waitForIdle(); + robot.keyPress(KeyEvent.VK_ENTER); + robot.keyRelease(KeyEvent.VK_ENTER); + robot.delay(1000); + if (!s.equals(ac)) { + throw new RuntimeException("Action command should be the same as text field content"); + } + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, AWTException { + GetTextTest test = new GetTextTest(); + EventQueue.invokeAndWait(test::setupGUI); + try { + test.performTest(); + } finally { + EventQueue.invokeAndWait(test::dispose); + } + } +} diff --git a/test/jdk/java/awt/TextField/SetEchoCharTest.java b/test/jdk/java/awt/TextField/SetEchoCharTest.java new file mode 100644 index 0000000000000..d5cb71975e6a7 --- /dev/null +++ b/test/jdk/java/awt/TextField/SetEchoCharTest.java @@ -0,0 +1,178 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Point; +import java.awt.Robot; +import java.awt.TextField; +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.StringSelection; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import jdk.test.lib.Platform; + +/* + * @test + * @bug 4124697 + * @key headful + * @summary Make sure that after setting and then changing the echo + * character again, the TextField continues to function as expected. + * @library /test/lib + * @build jdk.test.lib.Platform + * @run main SetEchoCharTest + */ + +public class SetEchoCharTest { + private static Frame frame; + private static Robot robot; + private static TextField tfPassword; + private static Button btn1; + private static Button btn2; + private static volatile Point btn1Loc; + private static volatile Point btn2Loc; + + private static final String CHANGE = "Change echo char"; + private static final String PRINT = "Print text"; + private static final String INITIAL_TEXT = "DefaultPwd"; + private static final String CHANGED_TEXT = "NewPwd"; + private static final char NEW_ECHO_CHAR = '*'; + + public static void main(String[] args) throws Exception { + try { + robot = new Robot(); + robot.setAutoWaitForIdle(true); + robot.setAutoDelay(50); + + EventQueue.invokeAndWait(() -> createAndShowUI()); + robot.waitForIdle(); + robot.delay(1000); + + testEchoChar(); + robot.waitForIdle(); + robot.delay(200); + + testNewEchoChar(); + robot.waitForIdle(); + robot.delay(200); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void createAndShowUI() { + frame = new Frame("SetEchoCharTest"); + frame.setLayout(new FlowLayout()); + + Label label = new Label("Pwd:"); + tfPassword = new TextField(INITIAL_TEXT, 10); + tfPassword.setEchoChar('X'); + tfPassword.addActionListener((ActionListener) e -> { + if (e.getActionCommand().equals(CHANGED_TEXT)) { + //check the 2nd condition only if ActionEvent + //is triggered by changed text + if (!(tfPassword.getText().equals(CHANGED_TEXT) + && tfPassword.getEchoChar() == NEW_ECHO_CHAR)) { + throw new RuntimeException("Test Failed!!! TextField not working" + + " as expected after echo char change"); + } + } + }); + frame.add(label); + frame.add(tfPassword); + + btn1 = new Button(PRINT); + btn1.addActionListener(new BtnActionListener()); + frame.add(btn1); + + btn2 = new Button(CHANGE); + btn2.addActionListener(new BtnActionListener()); + frame.add(btn2); + frame.setSize(200,200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private static void testEchoChar() throws Exception { + EventQueue.invokeAndWait(() -> { + btn1Loc = btn1.getLocationOnScreen(); + btn2Loc = btn2.getLocationOnScreen(); + }); + + robot.mouseMove(btn1Loc.x + btn1.getWidth() / 2, + btn1Loc.y + btn1.getHeight() / 2); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.delay(1000); + + robot.mouseMove(btn2Loc.x + btn2.getWidth() / 2, + btn2Loc.y + btn2.getHeight() / 2); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.delay(1000); + } + + private static void testNewEchoChar() { + StringSelection stringSelection = new StringSelection(CHANGED_TEXT); + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + clipboard.setContents(stringSelection, stringSelection); + + int ctrlKey = Platform.isOSX() ? KeyEvent.VK_META : KeyEvent.VK_CONTROL; + robot.keyPress(ctrlKey); + robot.keyPress(KeyEvent.VK_V); + robot.keyRelease(KeyEvent.VK_V); + robot.keyRelease(ctrlKey); + + robot.keyPress(KeyEvent.VK_ENTER); + robot.keyRelease(KeyEvent.VK_ENTER); + } + + private static class BtnActionListener implements ActionListener { + public void actionPerformed(ActionEvent evt) { + String ac = evt.getActionCommand(); + if (CHANGE.equals(ac)) { + tfPassword.setText(""); + tfPassword.setEchoChar(NEW_ECHO_CHAR); + tfPassword.requestFocus(); + } + if (PRINT.equals(ac)) { + if (!tfPassword.getText().equals(INITIAL_TEXT)) { + throw new RuntimeException("Test Failed!!!" + + " Initial text not as expected"); + } + } + } + } +} + diff --git a/test/jdk/java/awt/TextField/SetEchoCharTest3/SetEchoCharTest3.java b/test/jdk/java/awt/TextField/SetEchoCharTest3/SetEchoCharTest3.java new file mode 100644 index 0000000000000..10779365defc4 --- /dev/null +++ b/test/jdk/java/awt/TextField/SetEchoCharTest3/SetEchoCharTest3.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4222122 + * @summary TextField.setEchoCharacter() seems to be broken + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual SetEchoCharTest3 + */ + +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Label; +import java.awt.TextField; +import java.lang.reflect.InvocationTargetException; + +public class SetEchoCharTest3 extends Frame { + static String INSTRUCTIONS = """ + Type in the text field and "*" characters should echo. + If only one "*" echoes and then the system beeps after + the second character is typed, then press Fail, otherwise press Pass. + """; + public SetEchoCharTest3() { + setLayout(new FlowLayout()); + add(new Label("Enter text:")); + TextField tf = new TextField(15); + tf.setEchoChar('*'); + add(tf); + pack(); + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Set Echo Char Test 3") + .testUI(SetEchoCharTest3::new) + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 1) + .columns(40) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/TextField/SetEchoCharTest4/SetEchoCharTest4.java b/test/jdk/java/awt/TextField/SetEchoCharTest4/SetEchoCharTest4.java index c1cc07afac0ad..e96485dcb542a 100644 --- a/test/jdk/java/awt/TextField/SetEchoCharTest4/SetEchoCharTest4.java +++ b/test/jdk/java/awt/TextField/SetEchoCharTest4/SetEchoCharTest4.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,6 @@ import java.awt.TextField; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.lang.reflect.InvocationTargetException; /* * @test @@ -54,16 +53,17 @@ public class SetEchoCharTest4 extends Frame implements ActionListener { Make sure the actual text matches what you typed in for each field. Press Pass if everything's ok, otherwise Fail - """; + """; public SetEchoCharTest4() { + super("SetEchoCharTest4"); setLayout(new FlowLayout()); tf1.setEchoChar('*'); tf2.setEchoChar('$'); tf3.setEchoChar('#'); addStuff(); b.addActionListener(this); - setSize (200,200); + setSize (300, 150); } private void addStuff() { @@ -78,7 +78,6 @@ public void actionPerformed(ActionEvent ae) { PassFailJFrame.log("TextField2 = " + tf2.getText()); PassFailJFrame.log("TextField3 = " + tf3.getText()); PassFailJFrame.log("--------------"); - setVisible(false); remove(tf1); remove(tf2); remove(tf3); @@ -86,16 +85,14 @@ public void actionPerformed(ActionEvent ae) { addStuff(); } - public static void main(String[] args) throws InterruptedException, - InvocationTargetException { + public static void main(String[] args) throws Exception { PassFailJFrame.builder() - .title("Set Echo Character Test") - .testUI(SetEchoCharTest4::new) - .instructions(INSTRUCTIONS) - .rows((int) INSTRUCTIONS.lines().count() + 1) - .columns(40) - .logArea() - .build() - .awaitAndCheck(); + .title("Set Echo Character Test") + .testUI(SetEchoCharTest4::new) + .instructions(INSTRUCTIONS) + .columns(40) + .logArea() + .build() + .awaitAndCheck(); } } diff --git a/test/jdk/java/awt/TextField/SetEchoCharWordOpsTest.java b/test/jdk/java/awt/TextField/SetEchoCharWordOpsTest.java new file mode 100644 index 0000000000000..9116a4dff3bd7 --- /dev/null +++ b/test/jdk/java/awt/TextField/SetEchoCharWordOpsTest.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.FlowLayout; +import java.awt.Label; +import java.awt.TextField; +import javax.swing.JPanel; + +import jdk.test.lib.Platform; + +/* + * @test + * @bug 6191897 8354646 + * @summary Verifies that ctrl+left/right does not move word-by-word in a TextField + * with echo character set + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame jdk.test.lib.Platform + * @run main/manual SetEchoCharWordOpsTest + */ + +public class SetEchoCharWordOpsTest { + + public static void main(String[] args) throws Exception { + String selectAllKey; + String moveKeys; + String selectKeys; + + if (Platform.isOSX()) { + selectAllKey = "Cmd + A"; + moveKeys = "Alt + Right/Left"; + selectKeys = "Shift + Alt + Right/Left"; + } else { + selectAllKey = "Ctrl + A"; + moveKeys = "Ctrl + Right/Left"; + selectKeys = "Shift + Ctrl + Right/Left"; + } + + String instructions = + "The password field (in the bottom panel) in this test contains" + + " a few words (3 words).\n" + + "Move the focus to the text field and press " + selectAllKey + ".\n" + + "Try moving the caret word-by-word with " + moveKeys + " or" + + " extending selection with " + selectKeys + "." + + " You should NOT be able to do that.\n\n" + + "If you are able to move the caret word-by-word press FAIL," + + " else press PASS."; + + PassFailJFrame.builder() + .title("SetEchoCharClipboard Instructions") + .instructions(instructions) + .rows((int) instructions.lines().count() + 3) + .columns(45) + .splitUIBottom(SetEchoCharWordOpsTest::createAndShowUI) + .build() + .awaitAndCheck(); + } + + + private static JPanel createAndShowUI() { + JPanel jPanel = new JPanel(); + TextField tf = new TextField("one two three", 15); + Label tfLabel = new Label("Password Field:"); + + jPanel.setLayout(new FlowLayout()); + tf.setEchoChar('*'); + jPanel.add(tfLabel); + jPanel.add(tf); + return jPanel; + } +} diff --git a/test/jdk/java/awt/Toolkit/DesktopProperties/DesktopPropertyTest.java b/test/jdk/java/awt/Toolkit/DesktopProperties/DesktopPropertyTest.java new file mode 100644 index 0000000000000..c2e3f5652f028 --- /dev/null +++ b/test/jdk/java/awt/Toolkit/DesktopProperties/DesktopPropertyTest.java @@ -0,0 +1,272 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4287882 + * @summary Tests internal use Windows properties + * @requires os.family == "windows" + * @key headful + * @run main DesktopPropertyTest + */ + +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableModel; +import java.awt.Robot; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.Arrays; +import java.util.Vector; + +/* + * This is a test of new Windows-specific desktop + * properties added in Kestrel. + * + * The new properties are meant for the use of the + * Windows PLAF only and are not public at this time. + */ +public class DesktopPropertyTest { + private static JFrame frame; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + try { + SwingUtilities.invokeAndWait(DesktopPropertyTest::runTest); + robot.waitForIdle(); + robot.delay(1000); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + public static void runTest() { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception e) { + throw new RuntimeException(e); + } + frame = new DesktopPropertyFrame(); + frame.setVisible(true); + } + + static class DesktopPropertyFrame extends JFrame { + JTable table; + + DesktopPropertyFrame() { + super("Toolkit.getDesktopProperty API Test"); + setBackground(Color.white); + add(new JScrollPane(createTable())); + setLocationRelativeTo(null); + setSize(500, 400); + } + + public JTable createTable() { + TableModel dataModel = new AbstractTableModel() { + final PropertyVector pv = new PropertyVector(); + + public int getColumnCount() { + return 3; + } + + public int getRowCount() { + return pv.size(); + } + + public String getColumnName(int column) { + String[] colnames = {"Property", "Type", "Value"}; + return colnames[column]; + } + + public Object getValueAt(int row, int col) { + Object[] prow = pv.get(row); + return prow[col]; + } + }; + + table = new JTable(dataModel); + table.setDefaultRenderer(Object.class, new DesktopPropertyRenderer()); + table.addMouseListener(new ClickListener()); + return table; + } + + class ClickListener extends MouseAdapter { + ClickListener() { + } + + public void mouseClicked(MouseEvent e) { + for (int row = 0; row <= table.getModel().getRowCount(); row++) { + Rectangle r = table.getCellRect(row, 2, false); + if (r.contains(e.getX(), e.getY())) { + Object value = table.getModel().getValueAt(row, 2); + if (value instanceof Runnable) { + ((Runnable) value).run(); + } + } + } + } + } + + class PropertyVector { + private static final int NAME = 0; + private static final int TYPE = 1; + private static final int VALUE = 2; + + private final Vector vector = new Vector<>(); + + PropertyVector() { + Object[] props = (Object[]) getToolkit() + .getDesktopProperty("win.propNames"); + if (props == null) { + throw new RuntimeException( + "'win.propNames' property not available. " + + "This test is valid only on Windows."); + } + for (Object prop : props) { + String propertyName = prop.toString(); + vector.addElement(createEntry(propertyName)); + } + } + + Object[] createEntry(String name) { + Object[] row = new Object[3]; + Object value = getToolkit().getDesktopProperty(name); + row[NAME] = name; + row[TYPE] = value.getClass().getName(); + row[VALUE] = value; + + System.out.println(Arrays.toString(row)); + // update this vector when property changes + getToolkit().addPropertyChangeListener(name, new DesktopPropertyChangeListener(row)); + return row; + } + + Object[] get(int row) { + return (Object[]) vector.elementAt(row); + } + + int size() { + return vector.size(); + } + + static class DesktopPropertyChangeListener implements PropertyChangeListener { + Object[] row; + + DesktopPropertyChangeListener(Object[] row) { + this.row = row; + } + + public void propertyChange(PropertyChangeEvent evt) { + this.row[VALUE] = evt.getNewValue(); + } + } + } + + static class DesktopPropertyRenderer implements TableCellRenderer { + ValueProp vprop = new ValueProp(); + FontProp fprop = new FontProp(); + ColorProp cprop = new ColorProp(); + RunnableProp rprop = new RunnableProp(); + RenderingHintsProp rhprop = new RenderingHintsProp(); + + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, boolean hasFocus, + int row, int column) { + + ValueProp propComponent; + if (value instanceof Boolean + || value instanceof Integer + || value instanceof String) { + propComponent = vprop; + } else if (value instanceof Font) { + propComponent = fprop; + } else if (value instanceof Color) { + propComponent = cprop; + } else if (value instanceof Runnable) { + propComponent = rprop; + } else if (value instanceof RenderingHints) { + propComponent = rhprop; + } else { + throw new RuntimeException("ASSERT unexpected value %s / %s\n" + .formatted(value != null ? value.getClass() : "", value)); + } + + propComponent.setValue(value); + + return propComponent; + } + } + + static class ValueProp extends JLabel { + public void setValue(Object value) { + setText(value.toString()); + } + } + + static class FontProp extends ValueProp { + public void setValue(Object value) { + Font font = (Font) value; + String style; + if (font.getStyle() == Font.BOLD) { + style = "Bold"; + } else if (font.getStyle() > Font.BOLD) { + style = "BoldItalic"; + } else { + style = "Plain"; + } + setText(font.getName() + ", " + style + ", " + font.getSize()); + setFont(font); + } + } + + static class ColorProp extends ValueProp { + public void setValue(Object value) { + Color color = (Color) value; + setText("%d, %d, %d" + .formatted(color.getRed(), color.getGreen(), color.getBlue())); + setBackground(color); + setOpaque(true); + } + } + + static class RunnableProp extends ValueProp {} + static class RenderingHintsProp extends ValueProp {} + } +} \ No newline at end of file diff --git a/test/jdk/java/awt/Toolkit/Headless/HeadlessToolkit.java b/test/jdk/java/awt/Toolkit/Headless/HeadlessToolkit.java index 0076bdb2067d1..ebf4c598a15c0 100644 --- a/test/jdk/java/awt/Toolkit/Headless/HeadlessToolkit.java +++ b/test/jdk/java/awt/Toolkit/Headless/HeadlessToolkit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,8 +21,20 @@ * questions. */ -import javax.imageio.ImageIO; -import java.awt.*; +import java.awt.AWTEvent; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsEnvironment; +import java.awt.HeadlessException; +import java.awt.Image; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.event.AWTEventListener; import java.awt.event.KeyEvent; @@ -35,16 +47,16 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.net.URL; import java.util.Map; +import javax.imageio.ImageIO; + /* * @test * @summary Check that Toolkit methods do not throw unexpected exceptions * in headless mode * @run main/othervm -Djava.awt.headless=true HeadlessToolkit */ - public class HeadlessToolkit { class awtEventListener implements AWTEventListener { @@ -275,13 +287,12 @@ void doTest() throws IOException { im = tk.createImage(image.getAbsolutePath()); im.flush(); - } - - im = tk.getImage(new URL("http://openjdk.java.net/images/openjdk.png")); - im.flush(); + im = tk.getImage(image.toURI().toURL()); + im.flush(); - im = tk.createImage(new URL("http://openjdk.java.net/images/openjdk.png")); - im.flush(); + im = tk.createImage(image.toURI().toURL()); + im.flush(); + } MemoryImageSource mis; int pixels[] = new int[50 * 50]; diff --git a/test/jdk/java/awt/Toolkit/Headless/WrappedToolkitTest/TestWrapped.java b/test/jdk/java/awt/Toolkit/Headless/WrappedToolkitTest/TestWrapped.java index e2ded58e7f298..4b4041dbcfdfd 100644 --- a/test/jdk/java/awt/Toolkit/Headless/WrappedToolkitTest/TestWrapped.java +++ b/test/jdk/java/awt/Toolkit/Headless/WrappedToolkitTest/TestWrapped.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,55 +22,52 @@ */ /* - * test + * @test * @bug 6282388 - * @summary Tests that AWT use correct toolkit to be wrapped into HeadlessToolkit - * @author artem.ananiev@sun.com: area=awt.headless - * @run shell WrappedToolkitTest.sh + * @summary Tests that AWT uses correct toolkit wrapped into HeadlessToolkit + * @modules java.desktop/sun.awt:open + * @library /test/lib + * @run main/othervm -Djava.awt.headless=true TestWrapped */ -import java.awt.*; +import java.awt.Toolkit; +import java.lang.Class; +import java.lang.reflect.Field; -import java.lang.reflect.*; +import jdk.test.lib.Platform; -import sun.awt.*; +public final class TestWrapped { -public class TestWrapped -{ - public static void main(String[] args) - { - try - { - if (args.length != 1) { - System.err.println("No correct toolkit class name is specified, test is not run"); - System.exit(0); + private static final String HEADLESS_TOOLKIT = "sun.awt.HeadlessToolkit"; + private static final String MACOSX_TOOLKIT = "sun.lwawt.macosx.LWCToolkit"; + private static final String UNIX_TOOLKIT = "sun.awt.X11.XToolkit"; + private static final String WINDOWS_TOOLKIT = "sun.awt.windows.WToolkit"; + + public static void main(String[] args) throws Exception { + String expectedToolkitClassName; + if (Platform.isWindows()) { + expectedToolkitClassName = WINDOWS_TOOLKIT; + } else if (Platform.isOSX()) { + expectedToolkitClassName = MACOSX_TOOLKIT; + } else { + expectedToolkitClassName = UNIX_TOOLKIT; } - String correctToolkitClassName = args[0]; Toolkit tk = Toolkit.getDefaultToolkit(); - Class tkClass = tk.getClass(); - if (!tkClass.getName().equals("sun.awt.HeadlessToolkit")) - { - System.err.println(tkClass.getName()); - System.err.println("Error: default toolkit is not an instance of HeadlessToolkit"); - System.exit(-1); + Class tkClass = tk.getClass(); + if (!tkClass.getName().equals(HEADLESS_TOOLKIT)) { + System.err.println("Expected: " + HEADLESS_TOOLKIT); + System.err.println("Actual: " + tkClass.getName()); + throw new RuntimeException("Wrong default toolkit"); } Field f = tkClass.getDeclaredField("tk"); f.setAccessible(true); - Class wrappedClass = f.get(tk).getClass(); - if (!wrappedClass.getName().equals(correctToolkitClassName)) { - System.err.println(wrappedClass.getName()); - System.err.println("Error: wrapped toolkit is not an instance of " + correctToolkitClassName); - System.exit(-1); - } + Class wrappedClass = f.get(tk).getClass(); + if (!wrappedClass.getName().equals(expectedToolkitClassName)) { + System.err.println("Expected: " + expectedToolkitClassName); + System.err.println("Actual: " + wrappedClass.getName()); + throw new RuntimeException("Wrong wrapped toolkit"); } - catch (Exception z) - { - z.printStackTrace(System.err); - System.exit(-1); - } - - System.exit(0); } } diff --git a/test/jdk/java/awt/Toolkit/Headless/WrappedToolkitTest/WrappedToolkitTest.sh b/test/jdk/java/awt/Toolkit/Headless/WrappedToolkitTest/WrappedToolkitTest.sh deleted file mode 100644 index ac0cf58942c77..0000000000000 --- a/test/jdk/java/awt/Toolkit/Headless/WrappedToolkitTest/WrappedToolkitTest.sh +++ /dev/null @@ -1,221 +0,0 @@ -#!/bin/ksh -p - -# -# Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -# -# @test -# @bug 6282388 8030640 -# @summary Tests that AWT use correct toolkit to be wrapped into HeadlessToolkit -# @author artem.ananiev@sun.com: area=awt.headless -# compile TestWrapped.java -# @run shell WrappedToolkitTest.sh - -# Beginning of subroutines: -status=1 - -#Call this from anywhere to fail the test with an error message -# usage: fail "reason why the test failed" -fail() - { echo "The test failed :-(" - echo "$*" 1>&2 - echo "exit status was $status" - exit $status - } #end of fail() - -#Call this from anywhere to pass the test with a message -# usage: pass "reason why the test passed if applicable" -pass() - { echo "The test passed!!!" - echo "$*" 1>&2 - exit 0 - } #end of pass() - -# end of subroutines - - -# The beginning of the script proper - -# Checking for proper OS -OS=`uname -s` -case "$OS" in - AIX | CYGWIN* | Darwin | Linux ) - FILESEP="/" - ;; - - Windows* ) - FILESEP="\\" - ;; - - # catch all other OSs - * ) - echo "Unrecognized system! $OS" - fail "Unrecognized system! $OS" - ;; -esac - -# check that some executable or other file you need is available, abort if not -# note that the name of the executable is in the fail string as well. -# this is how to check for presence of the compiler, etc. -#RESOURCE=`whence SomeProgramOrFileNeeded` -#if [ "${RESOURCE}" = "" ] ; -# then fail "Need SomeProgramOrFileNeeded to perform the test" ; -#fi - -# Want this test to run standalone as well as in the harness, so do the -# following to copy the test's directory into the harness's scratch directory -# and set all appropriate variables: - -if [ -z "${TESTJAVA}" ] ; then - # TESTJAVA is not set, so the test is running stand-alone. - # TESTJAVA holds the path to the root directory of the build of the JDK - # to be tested. That is, any java files run explicitly in this shell - # should use TESTJAVA in the path to the java interpreter. - # So, we'll set this to the JDK spec'd on the command line. If none - # is given on the command line, tell the user that and use a cheesy - # default. - # THIS IS THE JDK BEING TESTED. - if [ -n "$1" ] ; - then TESTJAVA=$1 - else fail "no JDK specified on command line!" - fi - TESTSRC=. - TESTCLASSES=. - STANDALONE=1; -fi -echo "JDK under test is: $TESTJAVA" - -#if in test harness, then copy the entire directory that the test is in over -# to the scratch directory. This catches any support files needed by the test. -if [ -z "${STANDALONE}" ] ; - then cp ${TESTSRC}/* . -fi -case "$OS" in - Windows* | CYGWIN* ) - ${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} \ - --add-exports java.desktop/sun.awt=ALL-UNNAMED \ - --add-exports java.desktop/sun.awt.windows=ALL-UNNAMED ${CP} \ - *.java - status=$? - if [ ! $status -eq "0" ]; then - fail "Compilation failed"; - fi - ;; - - AIX | Linux ) - ${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} \ - --add-exports java.desktop/sun.awt=ALL-UNNAMED \ - --add-exports java.desktop/sun.awt.X11=ALL-UNNAMED ${CP} \ - *.java - status=$? - if [ ! $status -eq "0" ]; then - fail "Compilation failed"; - fi - ;; - - Darwin) - ${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} \ - --add-exports java.desktop/sun.awt=ALL-UNNAMED \ - --add-exports java.desktop/sun.lwawt.macosx=ALL-UNNAMED ${CP} \ - *.java - status=$? - if [ ! $status -eq "0" ]; then - fail "Compilation failed"; - fi - ;; - -esac - -#Just before executing anything, make sure it has executable permission! -chmod 777 ./* - -############### YOUR TEST CODE HERE!!!!!!! ############# - -case "$OS" in - Windows* | CYGWIN* ) - ${TESTJAVA}/bin/java ${TESTVMOPTS} -Djava.awt.headless=true \ - --add-opens java.desktop/sun.awt=ALL-UNNAMED \ - --add-opens java.desktop/sun.awt.windows=ALL-UNNAMED ${CP} \ - TestWrapped sun.awt.windows.WToolkit - status=$? - if [ ! $status -eq "0" ]; then - fail "Test FAILED: toolkit wrapped into HeadlessToolkit is not an instance of sun.awt.windows.WToolkit"; - fi - ${TESTJAVA}/bin/java ${TESTVMOPTS} -Djava.awt.headless=true \ - --add-opens java.desktop/sun.awt=ALL-UNNAMED \ - --add-opens java.desktop/sun.awt.windows=ALL-UNNAMED ${CP} \ - -Dawt.toolkit=sun.awt.windows.WToolkit \ - TestWrapped sun.awt.windows.WToolkit - status=$? - if [ ! $status -eq "0" ]; then - fail "Test FAILED: toolkit wrapped into HeadlessToolkit is not an instance of sun.awt.windows.WToolkit"; - fi - ;; - - AIX | Linux ) - ${TESTJAVA}/bin/java ${TESTVMOPTS} -Djava.awt.headless=true \ - --add-opens java.desktop/sun.awt=ALL-UNNAMED \ - --add-opens java.desktop/sun.awt.X11=ALL-UNNAMED ${CP} \ - -Dawt.toolkit=sun.awt.X11.XToolkit \ - TestWrapped sun.awt.X11.XToolkit - status=$? - if [ ! $status -eq "0" ]; then - fail "Test FAILED: toolkit wrapped into HeadlessToolkit is not an instance of sun.awt.xawt.XToolkit"; - fi - AWT_TOOLKIT=XToolkit ${TESTJAVA}/bin/java ${TESTVMOPTS} \ - --add-opens java.desktop/sun.awt=ALL-UNNAMED \ - --add-opens java.desktop/sun.awt.X11=ALL-UNNAMED ${CP} \ - -Djava.awt.headless=true \ - TestWrapped sun.awt.X11.XToolkit - status=$? - if [ ! $status -eq "0" ]; then - fail "Test FAILED: toolkit wrapped into HeadlessToolkit is not an instance of sun.awt.xawt.XToolkit"; - fi - ;; - - Darwin) - ${TESTJAVA}/bin/java ${TESTVMOPTS} -Djava.awt.headless=true \ - --add-opens java.desktop/sun.awt=ALL-UNNAMED \ - --add-opens java.desktop/sun.lwawt.macosx=ALL-UNNAMED ${CP} \ - TestWrapped sun.lwawt.macosx.LWCToolkit - status=$? - if [ ! $status -eq "0" ]; then - fail "Test FAILED: toolkit wrapped into HeadlessToolkit is not an instance of sun.lwawt.macosx.LWCToolkit"; - fi - ${TESTJAVA}/bin/java ${TESTVMOPTS} -Djava.awt.headless=true \ - --add-opens java.desktop/sun.awt=ALL-UNNAMED \ - --add-opens java.desktop/sun.lwawt.macosx=ALL-UNNAMED ${CP} \ - -Dawt.toolkit=sun.lwawt.macosx.LWCToolkit \ - TestWrapped sun.lwawt.macosx.LWCToolkit - status=$? - if [ ! $status -eq "0" ]; then - fail "Test FAILED: toolkit wrapped into HeadlessToolkit is not an instance of sun.lwawt.macosx.LWCToolkit"; - fi - ;; - -esac - -pass "All the tests are PASSED"; - -#For additional examples of how to write platform independent KSH scripts, -# see the jtreg file itself. It is a KSH script for both Solaris and Win32 diff --git a/test/jdk/java/awt/Toolkit/TimeUnsignedConversionTest.java b/test/jdk/java/awt/Toolkit/TimeUnsignedConversionTest.java new file mode 100644 index 0000000000000..64d05ef24989b --- /dev/null +++ b/test/jdk/java/awt/Toolkit/TimeUnsignedConversionTest.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 5097241 + * @summary Tests the problem of time type conversion on XToolkit. The conversion should be unsigned. + * @requires os.family == "linux" + * @key headful + * @library /java/awt/regtesthelpers /test/lib + * @build Util jtreg.SkippedException + * @run main/othervm -Dsun.awt.disableGtkFileDialogs=true TimeUnsignedConversionTest + */ + +import java.awt.Button; +import java.awt.EventQueue; +import java.awt.FileDialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Robot; +import java.awt.Toolkit; +import java.awt.event.KeyEvent; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import jtreg.SkippedException; +import test.java.awt.regtesthelpers.Util; + +public class TimeUnsignedConversionTest { + static Robot robot; + static Frame frame; + static volatile Button button; + static volatile FileDialog dialog; + static volatile boolean dialogShown = false; + + static final CountDownLatch passedLatch = new CountDownLatch(1); + + public static void main(String[] args) throws Exception { + if (!Toolkit.getDefaultToolkit().getClass().getName().equals("sun.awt.X11.XToolkit")) { + throw new SkippedException("XAWT test only! Skipped."); + } + + try { + EventQueue.invokeAndWait(TimeUnsignedConversionTest::createAndShowGUI); + test(); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void createAndShowGUI() { + frame = new Frame("TimeUnsignedConversionTest frame"); + button = new Button("Show Dialog"); + dialog = new FileDialog(frame, "TimeUnsignedConversionTest Dialog", FileDialog.LOAD); + + Toolkit.getDefaultToolkit().addAWTEventListener(e -> { + System.out.println(e); + if (dialogShown && ((KeyEvent)e).getKeyCode() == KeyEvent.VK_K) { + passedLatch.countDown(); + } + }, KeyEvent.KEY_EVENT_MASK); + + frame.setLayout(new FlowLayout()); + frame.add(button); + + button.addActionListener(ae -> { + if (ae.getActionCommand().equals("Show Dialog")) { + dialog.setSize(200, 200); + dialog.setLocationRelativeTo(frame); + dialog.setVisible(true); + } + }); + + frame.setSize(100, 100); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private static void test() throws Exception { + robot = new Robot(); + robot.waitForIdle(); + + Util.waitTillShown(button); + + robot.waitForIdle(); + robot.keyPress(KeyEvent.VK_SPACE); + robot.delay(50); + robot.keyRelease(KeyEvent.VK_SPACE); + + Util.waitTillShown(dialog); + dialogShown = true; + + robot.waitForIdle(); + robot.keyPress(KeyEvent.VK_K); + robot.delay(50); + robot.keyRelease(KeyEvent.VK_K); + + if (!passedLatch.await(2, TimeUnit.SECONDS)) { + throw new RuntimeException("Test failed!"); + } + + System.out.println("Test passed."); + } +} diff --git a/test/jdk/java/awt/TrayIcon/ClearPrevImageTest.java b/test/jdk/java/awt/TrayIcon/ClearPrevImageTest.java new file mode 100644 index 0000000000000..e97f645bec594 --- /dev/null +++ b/test/jdk/java/awt/TrayIcon/ClearPrevImageTest.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.AWTException; +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.RenderingHints; +import java.awt.SystemTray; +import java.awt.TrayIcon; +import java.awt.image.BufferedImage; + +import jtreg.SkippedException; + +/* + * @test + * @bug 6267936 + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame jtreg.SkippedException + * @summary Tests that the previous image in TrayIcon is cleared + * when a new image is set + * @run main/manual ClearPrevImageTest + */ + +public class ClearPrevImageTest { + private static SystemTray tray; + private static TrayIcon icon; + private static final String INSTRUCTIONS = """ + This test checks that the previous image in TrayIcon is cleared + when a new image is set. + + When the test starts, a RED square TrayIcon is added + to the SystemTray (also, called Taskbar Status Area in MS Windows, + Notification Area in, GNOME and System Tray in KDE). + + You should see it change into YELLOW after 5 seconds. + If you still see RED TrayIcon after 5 seconds, + press FAIL, otherwise press PASS + """; + + + public static void main(String[] args) throws Exception { + if (!SystemTray.isSupported()) { + throw new SkippedException("Test not applicable as" + + " System Tray not supported"); + } + + PassFailJFrame passFailJFrame + = PassFailJFrame.builder() + .title("TrayIcon Change Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .build(); + + EventQueue.invokeAndWait(ClearPrevImageTest::createAndShowTrayIcon); + try { + changeTrayIcon(); + passFailJFrame.awaitAndCheck(); + } catch (Exception e) { + throw new RuntimeException("Test failed: ", e); + } finally { + EventQueue.invokeAndWait(() -> { + if (tray != null) { + tray.remove(icon); + } + }); + } + } + + private static void createAndShowTrayIcon() { + Image img1 = createIcon(Color.RED); + tray = SystemTray.getSystemTray(); + icon = new TrayIcon(img1); + icon.setImageAutoSize(true); + + try { + tray.add(icon); + } catch (AWTException e) { + throw new RuntimeException("Error while adding" + + " icon to system tray", e); + } + } + + private static void changeTrayIcon() { + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + Image img2 = createIcon(Color.YELLOW); + icon.setImage(img2); + } + + private static Image createIcon(Color color) { + BufferedImage image = new BufferedImage(16, 16, + BufferedImage.TYPE_INT_ARGB); + Graphics2D g = image.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + g.setColor(color); + g.fillRect(0, 0, 16, 16); + g.dispose(); + return image; + } +} diff --git a/test/jdk/java/awt/TrayIcon/DblClickActionEventTest/DblClickActionEventTest.html b/test/jdk/java/awt/TrayIcon/DblClickActionEventTest/DblClickActionEventTest.html deleted file mode 100644 index bb92e6990fe44..0000000000000 --- a/test/jdk/java/awt/TrayIcon/DblClickActionEventTest/DblClickActionEventTest.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - DblClickActionEventTest - - - -

    DblClickActionEventTest
    Bug ID: 6284070

    - -

    See the dialog box (usually in upper left corner) for instructions

    - - - - diff --git a/test/jdk/java/awt/TrayIcon/DblClickActionEventTest/DblClickActionEventTest.java b/test/jdk/java/awt/TrayIcon/DblClickActionEventTest/DblClickActionEventTest.java deleted file mode 100644 index e3c4bc916098f..0000000000000 --- a/test/jdk/java/awt/TrayIcon/DblClickActionEventTest/DblClickActionEventTest.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - test - @bug 6284070 - @summary Tests that ActionEvent is generated when a tray icon is double-clicked - @library ../../regtesthelpers - @build Sysout - @author artem.ananiev: area=awt.tray - @run applet/manual=yesno DblClickActionEventTest.html -*/ - -import java.applet.*; - -import java.awt.*; -import java.awt.event.*; -import java.awt.image.*; - -import jdk.test.lib.Platform; -import test.java.awt.regtesthelpers.Sysout; - -public class DblClickActionEventTest extends Applet { - boolean traySupported; - - public void init() { - this.setLayout(new BorderLayout()); - - String[] instructions; - traySupported = SystemTray.isSupported(); - if (traySupported) { - String clickInstruction; - if (Platform.isOSX()) { - clickInstruction = "right"; - } else { - clickInstruction = "left"; - } - instructions = new String[]{ - "When the test starts an icon is added to the SystemTray area.", - " Double-click on it with a " + clickInstruction + " button and make sure that", - " ACTION_PERFORMED event is sent to Java (all the clicks and", - " action events are shown below these instructions).", - "Then, if your system allows the tray icon to get focus (for", - " example, windows 2000 or windows XP), double-click on the", - " icon with SPACE button and single-click with RETURN button.", - " Both of them must also trigger ACTION_PERFORMED event.", - "If you see ACTION_PERFORMED events after each of your actions", - " (either mouse clicks or key presses), press PASS, else FAIL" - }; - } else { - instructions = new String[]{ - "The test cannot be run because SystemTray is not supported.", - "Simply press PASS button." - }; - } - Sysout.createDialogWithInstructions(instructions); - } - - public void start() { - setSize(200, 200); - setVisible(true); - validate(); - - if (!traySupported) { - return; - } - - BufferedImage img = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB); - Graphics g = img.createGraphics(); - g.setColor(Color.WHITE); - g.fillRect(0, 0, 32, 32); - g.setColor(Color.RED); - g.fillRect(6, 6, 20, 20); - g.dispose(); - - SystemTray tray = SystemTray.getSystemTray(); - TrayIcon icon = new TrayIcon(img); - icon.setImageAutoSize(true); - icon.addActionListener(ev -> Sysout.println(ev.toString())); - icon.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent ev) { - Sysout.println(ev.toString()); - } - } - ); - - try { - tray.add(icon); - } catch (AWTException e) { - Sysout.println(e.toString()); - Sysout.println("!!! The test coudn't be performed !!!"); - } - } -} - diff --git a/test/jdk/java/awt/TrayIcon/FocusLostAfterTrayTest.java b/test/jdk/java/awt/TrayIcon/FocusLostAfterTrayTest.java new file mode 100644 index 0000000000000..bd831ce94e4c0 --- /dev/null +++ b/test/jdk/java/awt/TrayIcon/FocusLostAfterTrayTest.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.AWTException; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.SystemTray; +import java.awt.TextArea; +import java.awt.TrayIcon; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.image.BufferedImage; + +import jtreg.SkippedException; + +/* + * @test + * @bug 6269309 + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame jtreg.SkippedException + * @summary Tests that focus is transferred properly back + * to toplevel after clicking on a tray icon. + * @run main/manual FocusLostAfterTrayTest + */ + +public class FocusLostAfterTrayTest { + private static SystemTray tray; + private static TrayIcon icon; + + private static final String INSTRUCTIONS = """ + This test checks that focus is transferred properly back + to top-level after clicking on a tray icon. + + When the test starts, a Frame with a text area & a RED tray icon + are shown. If you don't see a tray icon please make sure that + the tray area (also called Taskbar Status Area on MS Windows + Notification Area on Gnome or System Tray on KDE) is visible. + + Click with a mouse inside a text area and make sure that it has + received input focus. Then click on the tray icon and then back + on the text area and verify that it has input focus again. Repeat + the last step several times. Ensure that the text area always + has the input focus and there are no "FOCUS LOST" event + for text area in the log section. + + If above is true, Press PASS, else FAIL. + """; + + public static void main(String[] args) throws Exception { + if (!SystemTray.isSupported()) { + throw new SkippedException("Test not applicable as" + + " System Tray not supported"); + } + + try { + PassFailJFrame.builder() + .title("FocusLostAfterTrayTest Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(FocusLostAfterTrayTest::createAndShowTrayIcon) + .logArea() + .build() + .awaitAndCheck(); + } finally { + EventQueue.invokeAndWait(() -> { + if (tray != null) { + tray.remove(icon); + } + }); + } + } + + private static Frame createAndShowTrayIcon() { + Frame frame = new Frame("FocusLostAfterTrayTest"); + frame.setBounds(100, 300, 200, 200); + frame.setLayout(new BorderLayout()); + TextArea ta = new TextArea(); + ta.addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + PassFailJFrame.log("FOCUS GAINED: " + + e.getComponent().getClass().toString()); + } + @Override + public void focusLost(FocusEvent e) { + PassFailJFrame.log("FOCUS LOST !! " + + e.getComponent().getClass().toString()); + } + }); + frame.add(ta); + + BufferedImage image = new BufferedImage(16, 16, + BufferedImage.TYPE_INT_ARGB); + Graphics2D g = image.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + g.setColor(Color.RED); + g.fillRect(0, 0, 16, 16); + g.dispose(); + tray = SystemTray.getSystemTray(); + icon = new TrayIcon(image); + icon.setImageAutoSize(true); + + try { + tray.add(icon); + } catch (AWTException e) { + throw new RuntimeException("Error while adding" + + " icon to system tray", e); + } + return frame; + } +} diff --git a/test/jdk/java/awt/TrayIcon/MouseMoveTest.java b/test/jdk/java/awt/TrayIcon/MouseMoveTest.java new file mode 100644 index 0000000000000..51d80ff127b1b --- /dev/null +++ b/test/jdk/java/awt/TrayIcon/MouseMoveTest.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.AWTException; +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.Graphics; +import java.awt.SystemTray; +import java.awt.TrayIcon; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; +import java.awt.image.BufferedImage; + +import jtreg.SkippedException; + +/* + * @test + * @bug 6267980 + * @summary PIT:Spurious MouseMoved events are triggered by Tray Icon + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame jtreg.SkippedException + * @run main/manual MouseMoveTest + */ + +public class MouseMoveTest { + private static SystemTray tray; + private static TrayIcon icon; + private static final String INSTRUCTIONS = """ + 1) You will see a tray icon (white square) in notification area, + 2) Move mouse pointer to the icon and leave it somewhere inside the icon, + 3) Verify that MOUSE_MOVE events are NOT triggered after you have STOPPED + moving mouse. + 4) If events are still triggered Press FAIL else PASS. + """; + + public static void main(String[] args) throws Exception { + if (!SystemTray.isSupported()) { + throw new SkippedException("Test not applicable as" + + " System Tray not supported"); + } + + PassFailJFrame passFailJFrame + = PassFailJFrame.builder() + .title("TrayIcon Change Test Instructions") + .instructions(INSTRUCTIONS) + .columns(45) + .logArea() + .build(); + + try { + EventQueue.invokeAndWait(MouseMoveTest::createAndShowTrayIcon); + passFailJFrame.awaitAndCheck(); + } finally { + EventQueue.invokeAndWait(() -> { + if (tray != null) { + tray.remove(icon); + } + }); + } + } + + private static void createAndShowTrayIcon() { + BufferedImage img = new BufferedImage(32, 32, + BufferedImage.TYPE_INT_ARGB); + Graphics g = img.createGraphics(); + g.setColor(Color.WHITE); + g.fillRect(0, 0, 32, 32); + g.dispose(); + + tray = SystemTray.getSystemTray(); + icon = new TrayIcon(img); + icon.setImageAutoSize(true); + + icon.addMouseMotionListener(new MouseMotionAdapter() { + public void mouseMoved(MouseEvent me){ + PassFailJFrame.log(me.toString()); + } + }); + + try { + tray.add(icon); + } catch (AWTException e) { + throw new RuntimeException("Error while adding" + + " icon to system tray", e); + } + } +} diff --git a/test/jdk/java/awt/TrayIcon/TrayIconKeySelectTest.java b/test/jdk/java/awt/TrayIcon/TrayIconKeySelectTest.java new file mode 100644 index 0000000000000..5e41f54638236 --- /dev/null +++ b/test/jdk/java/awt/TrayIcon/TrayIconKeySelectTest.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.AWTException; +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.Graphics; +import java.awt.SystemTray; +import java.awt.TrayIcon; +import java.awt.image.BufferedImage; + +import jtreg.SkippedException; + +/* + * @test + * @bug 6267943 + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame jtreg.SkippedException + * @summary Tests the possibility of selecting a tray icon with the keyboard. + * @run main/manual TrayIconKeySelectTest + */ + +public class TrayIconKeySelectTest { + private static SystemTray tray; + private static TrayIcon icon; + private static final String INSTRUCTIONS = """ + Tests that TrayIcon is selectable with the keyboard + When the test is started you will see three-color icon + in the system tray. + + 1. Bring the focus to the icon with TAB. Press ENTER key. + - One or more ActionEvent should be generated + (see the output area of the test) + + 2. Bring the focus again to the icon. Press SPACE key twice. + - One or more ActionEvent should be generated. + + 3. Bring the focus again to the icon. Click on the icon with + the LEFT mouse button twice. + - One or more ActionEvent should be generated. + + 4. Again bring the focus to the icon. Click on the icon with + the LEFT mouse button just once. + - NO ActionEvent should be generated. + + 5. Repeat the 4th step with other mouse buttons. + + If all the above are true press PASS, else FAIL + """; + + public static void main(String[] args) throws Exception { + if (!SystemTray.isSupported()) { + throw new SkippedException("Test not applicable as" + + " System Tray not supported"); + } + PassFailJFrame passFailJFrame; + try { + passFailJFrame + = PassFailJFrame.builder() + .title("TrayIconKeySelectTest Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .logArea() + .build(); + + EventQueue.invokeAndWait(TrayIconKeySelectTest::createAndShowTrayIcon); + passFailJFrame.awaitAndCheck(); + } finally { + EventQueue.invokeAndWait(() -> { + if (tray != null) { + tray.remove(icon); + } + }); + } + } + + private static void createAndShowTrayIcon() { + BufferedImage im = new BufferedImage(16, 16, + BufferedImage.TYPE_INT_ARGB); + Graphics gr = im.createGraphics(); + gr.setColor(Color.white); + gr.fillRect(0, 0, 16, 5); + gr.setColor(Color.blue); + gr.fillRect(0, 5, 16, 10); + gr.setColor(Color.red); + gr.fillRect(0, 10, 16, 16); + gr.dispose(); + + tray = SystemTray.getSystemTray(); + icon = new TrayIcon(im); + icon.setImageAutoSize(true); + icon.addActionListener(e -> PassFailJFrame.log(e.toString())); + + try { + tray.add(icon); + } catch (AWTException e) { + throw new RuntimeException("Error while adding" + + " icon to system tray", e); + } + } +} diff --git a/test/jdk/java/awt/TrayIcon/TrayIconTest.java b/test/jdk/java/awt/TrayIcon/TrayIconTest.java new file mode 100644 index 0000000000000..c78f6c134fed3 --- /dev/null +++ b/test/jdk/java/awt/TrayIcon/TrayIconTest.java @@ -0,0 +1,613 @@ +/* + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.AWTException; +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Checkbox; +import java.awt.CheckboxGroup; +import java.awt.Choice; +import java.awt.Color; +import java.awt.Container; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Graphics2D; +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.Insets; +import java.awt.Label; +import java.awt.MenuItem; +import java.awt.Panel; +import java.awt.PopupMenu; +import java.awt.RenderingHints; +import java.awt.SystemTray; +import java.awt.TextField; +import java.awt.Toolkit; +import java.awt.TrayIcon; +import java.awt.TrayIcon.MessageType; +import java.awt.event.ActionEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.image.BufferedImage; +import java.beans.PropertyChangeEvent; +import java.util.HashMap; +import java.util.Map; +import jtreg.SkippedException; + +/* + * @test + * @key headful + * @bug 4310333 + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame jtreg.SkippedException + * @summary A unit test for TrayIcon RFE + * @run main/manual TrayIconTest + */ + +public class TrayIconTest { + private static SystemTray tray; + static Frame frame = new Frame("TrayIcon Test"); + private static final String INSTRUCTIONS = """ + The test frame contains CheckboxGroup of tray icons. + A selected checkbox represents the TrayIcon (or null + TrayIcon) whose functionality is currently tested. + + If you are under Linux make sure your Application Panel has + System Tray (on Gnome it is called Notification Area). + + Perform all the cases (1-7) documented below. + + CASE 1: Testing ADD/REMOVE/PropertyChange functionality. + -------------------------------------------------------- + 1. Select null TrayIcon and pressAdd button: + - NullPointerException should be thrown. + 2. Select some of the valid TrayIcons and press Add button: + - The selected TrayIcon should appear in the SystemTray. + - PropertyChangeEvent should be fired (the property is + an array of TrayIcons added to the system tray). + 3. Press Add button again: + - IllegalArgumentException should be thrown. + - No PropertyChangeEvent should be fired. + 4. Press Remove button: + - The TrayIcon should disappear from the SystemTray. + - PropertyChangeEvent should be fired. + 5. Press Remove button again: + - It should have no effect. + - No PropertyChangeEvent should be fired. + 6. Add all the valid TrayIcons (by selecting everyone and pressing Add + button): + - All the TrayIcons should appear in the SystemTray. + - PropertyChangeEvent should be fired on each adding. + 7. Remove all the TrayIcons (again by selecting everyone and pressing + Remove): + - All the TrayIcons should disappear from the SystemTray. + - PropertyChangeEvent should be fired on each removing. + 8. Not for Windows! Remove the system tray (Notification Area) from + the desktop. Try to add some valid TrayIcon: + - AWTException should be thrown. + - No PropertyChangeEvent should be fired. + 9. Not for Windows! Add the system tray back to the desktop. Add all the + valid TrayIcons: + - All the TrayIcons should appear in the system tray. + - PropertyChangeEvent should be fired on each adding. + 11. Not for Windows! Remove the system tray from the desktop: + - All the TrayIcons should disappear. + - PropertyChangeEvent should be fired for each TrayIcon + removal. + - PropertyChangeEvent should be fired for SystemTray removal. + 12. Add the system tray and go to the next step. + - All the TrayIcons should appear again. + - PropertyChangeEvent should be fired for SystemTray addition. + - PropertyChangeEvent shouldn't be fired for TrayIcon removal. + + CASE 2: Testing RESIZE functionality. + ------------------------------------- + 1. Select some of the TrayIcons and add it. Then press resize button: + - The TrayIcon selected should be resized to fit the area it occupies. + 2. Press resize button again: + - The TrayIcon should be resized to the original size. + 3. Repeat the 1-2 steps for other TrayIcons: + - The TrayIcons should be resized appropriately. + + CASE 3: Testing EVENTS functionality + --------------------------------- + 1. Select some of the TrayIcons and add it. Select MouseEvent from the + group of checkboxes at the top-right of the test frame. + Click on the TrayIcon in the SystemTray: + - MOUSE_PRESSED MOUSE_RELEASED and MOUSE_CLICKED events should be + generated. + 2. Press mouse inside the TrayIcon dragging mouse and releasing it. + - Make sure that MOUSE_CLICKED event is not triggered. + 3. Click on the TrayIcon with different modification keys: + - there should be appropriate modifiers in the events. + 4. Keep clicking on the TrayIcon: + - there should be correct absolute coordinates in the events. + 5. Only for Windows! Focus the system tray using keyboard: + - press WIN key once to bring up the start menu then press ESC once to + close the menu the focus should be on the start button + - press TAB key for several times until you focus on the system + tray then use ARROW keys to move to the TrayIcon + - press ENTER or SPACE should trigger ACTION_PERFORMED message + make sure that mouse events are not triggered. + 6. Select MouseMotionEvent checkbox. Move mouse over the TrayIcon: + - MOUSE_MOVED event should be generated. It should contain + correct coordinates. + 7. Deselect both the checkboxes and then select AWTEventListener. + Click on the TrayIcon and then move mouse over it: + - Appropriate mouse events should be generated (catched by the + AWTEventListener). + 8. Deselect all the checkboxes and go to the following step. + + CASE 4: Testing DISPLAY MESSAGE functionality. + ---------------------------------------------- + 1. Select some of the TrayIcons and add it. Then press Display message + button: + - A balloon message should appear near the TrayIcon. + 2. After the message is displayed wait for some period: + - The message window should be closed automatically. + 3. Display the message again. Close it by pressing X in its top-right + corner: + - The message window should be closed immediately. + 4. Display the message again. Click inside it: + - The message should be closed an ACTION_PERFORMED event should be + generated with correct information and an Ok dialog should appear. + Close the dialog. + 5. Select a message type from the Type choice and display the message + again: + - It should contain an icon appropriate to the message type selected + or no icon if NONE is selected. + 6. Change the content of the Message and Caption text fields and + display the message: + - The message content should be changed in the accordance with the text + typed. + 7. Not for Windows! Type some too long or too short text for the Caption + and Message: + - The message should process the text correctly. The long text should + be cut. + 8. Not for Windows! Type null in the Message text field and display + the message: + - The message body should contain no text. + 9. Type null in the Caption text field and display the message: + - The message caption should contain no text. + 10. Type null in the both Message and Caption fields and display + the message: + - NullPointerException should be generated and no message should be + displayed. + 11. Try to hide the taskbar. Click Display message for several times. + Then restore the taskbar. Click on the TrayIcon: + - No message should appear. + Try to display the message once more: + - It should appear appropriately. + 12. Try to display the message for other TrayIcons: + - The messages should be displayed appropriately. + + CASE 5: Testing POPUP MENU functionality. + ----------------------------------------- + 1. Add some TrayIcon to the system tray. Press Set button in the + Popup menu test area. Trigger the popup menu for the TrayIcon with + the mouse: + - A popup menu should appear. Make sure it behaves properly. + - Make sure the 'duke.gif' image is animated while the popup menu is shown. + 2. Press Remove button for the popup menu and try to trigger it again: + - No popup menu should appear. + 3. Perform 1-2 steps for other TrayIcons: + - Make sure the popup menu behaves properly. + 4. Add more than one TrayIcons to the system tray. Press Set button in + the PopupMenu test area for some of the TrayIcon added. Trigger + the popup menu for this TrayIcon: + - A popup menu should appear properly. + 5. Try to set the popup menu to the same TrayIcon again: + - It should have no effect + 6. Try to set the popup menu for other TrayIcons you've added to the system + tray: + - for each one IllegalArgumentException should be thrown. + + CASE 6: Testing TOOLTIP functionality. + -------------------------------------- + 1. Type something in the Tooltip text field and press Set button. + Then move mouse cursor over the TrayIcon and wait for a second: + - A tooltip should appear containing the text typed. + 2. Show a tooltip again and keep your mouse over the TrayIcon for some period: + - The tooltip should disappear automatically. + 3. Show a tooltip again and leave the TrayIcon: + - The tooltip should disappear immediately. + 4. Type null in the Tooltip field and press set then move your + mouse to the SystemTray: + - The tooltip shouldn't appear. + 5. Type something too long in the Tooltip field and show the tooltip: + - The tooltip text should be cut. + + CASE 7: Testing ACTION functionality. + ------------------------------------- + 1. Add some TrayIcon to the system tray. Double click it with the left mouse + button: + - An ACTION_PERFORMED event should be generated. + 2. Double click the TrayIcon with the left mouse button several times: + - Several ACTION_PERFORMED events should be generated + - Make sure that the time-stamp of each event ('when' field) is increased. + + If all the above cases work as expected Press PASS else FAIL. + """; + + public static void main(String[] args) throws Exception { + if (!SystemTray.isSupported()) { + throw new SkippedException("Test not applicable as" + + " System Tray not supported"); + } + try { + PassFailJFrame.builder() + .title("TrayIconTest Instructions") + .instructions(INSTRUCTIONS) + .columns(50) + .rows(40) + .testUI(TrayIconTest::createAndShowUI) + .logArea(10) + .build() + .awaitAndCheck(); + + } finally { + EventQueue.invokeAndWait(() -> { + if (tray != null) { + //Remove any remaining tray icons before ending the test. + TrayIcon[] icons = tray.getTrayIcons(); + for (TrayIcon icon : icons) { + tray.remove(icon); + } + } + }); + } + } + + private static Frame createAndShowUI() { + final TrayIconControl ctrl = new TrayIconControl(); + frame.setLayout(new BorderLayout()); + frame.add(ctrl.cont, BorderLayout.CENTER); + frame.setBackground(Color.LIGHT_GRAY); + + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + ctrl.dispose(); + } + }); + + frame.pack(); + return frame; + } + + private static class TrayIconControl { + final String RED_ICON = "RED ICON"; + final String BLUE_ICON = "BLUE ICON"; + final String GREEN_ICON = "GREEN ICON"; + + CheckboxGroup cbg = new CheckboxGroup(); + Button addButton = new PackedButton(" Add "); + Button remButton = new PackedButton("Remove"); + Button resizeButton = new PackedButton("Resize"); + Button balloonButton = new PackedButton("Display message"); + Choice balloonChoice = new Choice(); + String[] balloonTypes = new String[] { "ERROR", "WARNING", "INFO", "NONE" }; + + TextField balloonText = new TextField( + "A TrayIcon can generate various MouseEvents and" + + " supports adding corresponding listeners to receive" + + " notification of these events. TrayIcon processes" + + " some of the events by itself. For example," + + " by default, when the right-mouse click", 70); + TextField balloonCaption = new TextField("TrayIcon", 70); + + MessageType[] typeArr = new MessageType[] { MessageType.ERROR, MessageType.WARNING, + MessageType.INFO, MessageType.NONE }; + Checkbox mouseListenerCbox = new Checkbox("MouseEvent"); + Checkbox motionListenerCbox = new Checkbox(" MouseMotionEvent"); + Checkbox awtListenerCbox = new Checkbox(" AWTEventListener"); + TextField tipText = new TextField("TrayIcon", 50); + Button tipButton = new PackedButton("Set"); + Button setPopupButton = new PackedButton("Set"); + Button remPopupButton = new PackedButton("Remove"); + + PopupMenu popupMenu = new PopupMenu(); + + Map resToObjMap = new HashMap<>(); + + Container cont = new Container(); + + TrayIconControl() { + Toolkit.getDefaultToolkit().addAWTEventListener(e -> { + if (e.getSource() instanceof TrayIcon && awtListenerCbox.getState()) { + PassFailJFrame.log(e.toString()); + } + }, MouseEvent.MOUSE_EVENT_MASK | MouseEvent.MOUSE_MOTION_EVENT_MASK | + ActionEvent.ACTION_EVENT_MASK); + + cont.setLayout(new GridLayout(4, 1)); + + Container raw1 = new Container(); + raw1.setLayout(new GridLayout(1, 4)); + cont.add(raw1); + + InsetsPanel cbgPanel = new InsetsPanel(); + cbgPanel.setLayout(new GridLayout(4, 1)); + Checkbox nullCbox = new Checkbox("null", cbg, true); + Checkbox redCbox = new Checkbox(RED_ICON, cbg, false); + Checkbox blueCbox = new Checkbox(BLUE_ICON, cbg, false); + Checkbox greenCbox = new Checkbox(GREEN_ICON, cbg, false); + cbgPanel.add(nullCbox); + cbgPanel.add(redCbox); + cbgPanel.add(blueCbox); + cbgPanel.add(greenCbox); + cbgPanel.addTo(raw1); + + InsetsPanel addremPanel = new InsetsPanel(); + addremPanel.setLayout(new BorderLayout()); + addremPanel.add(addButton.getParent(), BorderLayout.NORTH); + addremPanel.add(remButton.getParent(), BorderLayout.SOUTH); + addremPanel.addTo(raw1); + + InsetsPanel resizePanel = new InsetsPanel(); + resizePanel.add(resizeButton); + resizePanel.addTo(raw1); + + InsetsPanel lstPanel = new InsetsPanel(); + lstPanel.setLayout(new GridLayout(3, 1)); + lstPanel.add(mouseListenerCbox); + lstPanel.add(motionListenerCbox); + lstPanel.add(awtListenerCbox); + lstPanel.addTo(raw1); + + Container raw2 = new Container(); + raw2.setLayout(new BorderLayout()); + cont.add(raw2); + + InsetsPanel balloonPanel = new InsetsPanel(); + balloonPanel.setLayout(new BorderLayout()); + balloonPanel.add(balloonButton.getParent(), BorderLayout.NORTH); + Container bc = new Container(); + bc.setLayout(new FlowLayout()); + bc.add(new Label(" Type:")); + bc.add(balloonChoice); + balloonPanel.add(bc, BorderLayout.SOUTH); + balloonPanel.addTo(raw2, BorderLayout.WEST); + + InsetsPanel blnTextPanel = new InsetsPanel(); + blnTextPanel.setLayout(new GridLayout(2, 2)); + Container c1 = new Panel(); + c1.setLayout(new FlowLayout()); + blnTextPanel.add(c1); + c1.add(new Label("Message:")); + c1.add(balloonText); + + Container c2 = new Panel(); + c2.setLayout(new FlowLayout()); + blnTextPanel.add(c2); + c2.add(new Label("Caption:")); + c2.add(balloonCaption); + blnTextPanel.addTo(raw2, BorderLayout.CENTER); + + + Container raw3 = new Container(); + raw3.setLayout(new BorderLayout()); + cont.add(raw3); + + InsetsPanel popupPanel = new InsetsPanel(); + popupPanel.setLayout(new FlowLayout()); + popupPanel.add(new Label("Popup menu:")); + popupPanel.add(setPopupButton); + popupPanel.add(remPopupButton); + popupPanel.addTo(raw3); + + + Container raw4 = new Container(); + raw4.setLayout(new BorderLayout()); + cont.add(raw4); + + InsetsPanel tipPanel = new InsetsPanel(); + tipPanel.setLayout(new FlowLayout()); + tipPanel.add(new Label("Tooltip:")); + tipPanel.add(tipText); + tipPanel.add(tipButton); + tipPanel.addTo(raw4); + + addButton.addActionListener(e -> { + try { + tray.add(getCurIcon()); + } catch (NullPointerException npe) { + if (npe.getMessage() == null) { + PassFailJFrame.log("Probably wrong path to the images."); + throw npe; // if wrong images path was set + } + PassFailJFrame.log(npe.toString()); + } catch (IllegalArgumentException iae) { + PassFailJFrame.log(iae.toString()); + } catch (AWTException ise) { + PassFailJFrame.log(ise.toString()); + } + }); + remButton.addActionListener(e -> tray.remove(getCurIcon())); + + resizeButton.addActionListener( + e -> getCurIcon().setImageAutoSize(!getCurIcon().isImageAutoSize())); + + balloonButton.addActionListener(e -> { + String text = null, caption = null; + if (balloonText.getText().compareToIgnoreCase("null") != 0) { + text = balloonText.getText(); + } + if (balloonCaption.getText().compareToIgnoreCase("null") != 0) { + caption = balloonCaption.getText(); + } + try { + getCurIcon().displayMessage(caption, text, typeArr[balloonChoice.getSelectedIndex()]); + } catch (NullPointerException npe) { + PassFailJFrame.log(npe.toString()); + } + }); + + tipButton.addActionListener(e -> { + String tip = null; + if (tipText.getText().compareToIgnoreCase("null") != 0) { + tip = tipText.getText(); + } + getCurIcon().setToolTip(tip); + }); + + setPopupButton.addActionListener(e -> { + try { + getCurIcon().setPopupMenu(popupMenu); + } catch (IllegalArgumentException iae) { + PassFailJFrame.log(iae.toString()); + } + }); + + remPopupButton.addActionListener(e -> getCurIcon().setPopupMenu(null)); + for (String s: balloonTypes) { + balloonChoice.add(s); + } + + init(); + } + + void init() { + tray = SystemTray.getSystemTray(); + tray.addPropertyChangeListener("trayIcons", + e -> printPropertyChangeEvent(e)); + + tray.addPropertyChangeListener("systemTray", + e -> printPropertyChangeEvent(e)); + + configureTrayIcon(RED_ICON); + configureTrayIcon(BLUE_ICON); + configureTrayIcon(GREEN_ICON); + + for (String s: balloonTypes) { + popupMenu.add(new MenuItem(s)); + } + } + + void printPropertyChangeEvent(PropertyChangeEvent e) { + String name = e.getPropertyName(); + Object oldValue = e.getOldValue(); + Object newValue = e.getNewValue(); + + PassFailJFrame.log("PropertyChangeEvent[name=" + name + + ",oldValue=" + oldValue + ",newValue=" + newValue + "]"); + } + + void configureTrayIcon(String icon) { + Color color = Color.WHITE; + switch (icon) { + case "RED ICON" -> color = Color.RED; + case "BLUE ICON" -> color = Color.BLUE; + case "GREEN ICON" -> color = Color.GREEN; + } + Image image = createIcon(color); + TrayIcon trayIcon = new TrayIcon(image); + + trayIcon.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent e) { + if (mouseListenerCbox.getState()) + PassFailJFrame.log(e.toString()); + } + public void mouseReleased(MouseEvent e) { + if (mouseListenerCbox.getState()) + PassFailJFrame.log(e.toString()); + } + public void mouseClicked(MouseEvent e) { + if (mouseListenerCbox.getState()) + PassFailJFrame.log(e.toString()); + } + }); + trayIcon.addMouseMotionListener(new MouseMotionAdapter() { + public void mouseMoved(MouseEvent e) { + if (motionListenerCbox.getState()) + PassFailJFrame.log(e.toString()); + } + }); + trayIcon.addActionListener(e -> PassFailJFrame.log(e.toString())); + + resToObjMap.remove(icon); + resToObjMap.put(icon, trayIcon); + } + + String getCurImgName() { + return cbg.getSelectedCheckbox().getLabel(); + } + + TrayIcon getCurIcon() { + return resToObjMap.get(getCurImgName()); + } + + public void dispose() { + tray.remove(getCurIcon()); + } + + private static Image createIcon(Color color) { + BufferedImage image = new BufferedImage(16, 16, + BufferedImage.TYPE_INT_ARGB); + Graphics2D g = image.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + g.setColor(color); + g.fillRect(0, 0, 16, 16); + g.dispose(); + return image; + } + + } + + private static class InsetsPanel extends Panel { + Container parent = new Container() { + public Insets getInsets() { + return new Insets(2, 2, 2, 2); + } + }; + + InsetsPanel() { + parent.setLayout(new BorderLayout()); + setBackground(new Color(240, 240, 240)); + } + + void addTo(Container c) { + parent.add(this); + c.add(parent); + } + + void addTo(Container c, String pos) { + parent.add(this); + c.add(parent, pos); + } + } + + private static class PackedButton extends Button { + Container parent = new Container(); + PackedButton(String l) { + super(l); + parent.setLayout(new FlowLayout()); + parent.add(this); + } + } +} + diff --git a/test/jdk/java/awt/Window/BadConfigure/BadConfigure.java b/test/jdk/java/awt/Window/BadConfigure/BadConfigure.java new file mode 100644 index 0000000000000..a25ab9b91b9a5 --- /dev/null +++ b/test/jdk/java/awt/Window/BadConfigure/BadConfigure.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6261336 + * @summary Tests that Choice inside ScrollPane opens at the right location + * after resize + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual BadConfigure +*/ + +import java.awt.BorderLayout; +import java.awt.Choice; +import java.awt.Frame; + +public class BadConfigure +{ + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Please resize the BadConfigure window using the left border. + Now click on choice. Its popup will be opened. + Please verify that the popup is opened right under the choice. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(initialize()) + .build() + .awaitAndCheck(); + } + + private static Frame initialize() { + Frame f = new Frame("BadConfigure"); + f.setLayout(new BorderLayout()); + Choice ch = new Choice(); + f.add(ch, BorderLayout.NORTH); + ch.add("One"); + ch.add("One"); + ch.add("One"); + ch.add("One"); + ch.add("One"); + ch.add("One"); + f.setSize(200, 200); + f.validate(); + return f; + } +} diff --git a/test/jdk/java/awt/Window/InvalidFocusLostEventTest/InvalidFocusLostEventTest.java b/test/jdk/java/awt/Window/InvalidFocusLostEventTest/InvalidFocusLostEventTest.java new file mode 100644 index 0000000000000..569d59f146d38 --- /dev/null +++ b/test/jdk/java/awt/Window/InvalidFocusLostEventTest/InvalidFocusLostEventTest.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4397883 + * @summary Tests that non-focusable Window doesn't grab focus + * @key headful + * @run main InvalidFocusLostEventTest + */ + +import java.awt.Button; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.KeyboardFocusManager; +import java.awt.Point; +import java.awt.Robot; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; + +public class InvalidFocusLostEventTest implements ActionListener { + private static Frame f; + private static Button b; + private static KeyboardFocusManager fm; + private static volatile Point bp; + private static volatile int width, height; + private static Robot robot; + + public static void main(String[] args) throws Exception { + try { + InvalidFocusLostEventTest test = new InvalidFocusLostEventTest(); + EventQueue.invokeAndWait(() -> test.createUI()); + runTest(); + // we should check focus after all events are processed, + // since focus transfers are asynchronous + robot.waitForIdle(); + if (fm.getFocusOwner() != b) { + throw new RuntimeException("Failed: focus was lost"); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + } + } + + private void createUI() { + f = new Frame("InvalidFocusLostEventTest"); + b = new Button("Press me"); + fm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); + b.addActionListener(this); + f.add(b); + f.pack(); + f.setLocationRelativeTo(null); + f.setVisible(true); + } + + private static void runTest() throws Exception { + robot = new Robot(); + robot.setAutoDelay(100); + robot.setAutoWaitForIdle(true); + EventQueue.invokeAndWait(() -> { + bp = b.getLocationOnScreen(); + width = b.getWidth(); + height = b.getHeight(); + }); + robot.mouseMove(bp.x + width / 2, bp.y + height / 2 ); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + } + + public void actionPerformed(ActionEvent ev) { + // pop up a non-focusable window + Window win = new Window(f); + win.setFocusableWindowState(false); + } +} diff --git a/test/jdk/java/awt/Window/LocationByPlatform/TestLocationByPlatform.java b/test/jdk/java/awt/Window/LocationByPlatform/TestLocationByPlatform.java new file mode 100644 index 0000000000000..33b9d048ef2a9 --- /dev/null +++ b/test/jdk/java/awt/Window/LocationByPlatform/TestLocationByPlatform.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6318630 + * @summary Test that location by platform works + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual TestLocationByPlatform + */ + +import java.awt.BorderLayout; +import java.awt.Canvas; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Graphics; + +public class TestLocationByPlatform { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + You should see two frames. One has locationByPlatform set, it + should be displayed somewhere on the screen most probably without + intersecting other Frames or stacked over normal frame with some + offset. Another has its location explicitly set to (0, 450). + Please verify that the frames are located correctly on the screen. + + Also verify that the picture inside of frames looks the same + and consists of red descending triangle occupying exactly the bottom + half of the frame. Make sure that there is a blue rectangle exactly + surrounding the client area of frame with no pixels between it and + the frame's decorations. Press Pass if this all is true, + otherwise press Fail. + """; + + PassFailJFrame passFailJFrame = PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows(13) + .columns(40) + .build(); + EventQueue.invokeAndWait(TestLocationByPlatform::createUI); + passFailJFrame.awaitAndCheck(); + } + private static void createUI() { + Frame frame = new Frame("Normal"); + frame.setLocation(0, 450); + Canvas c = new MyCanvas(); + frame.add(c, BorderLayout.CENTER); + frame.pack(); + PassFailJFrame.addTestWindow(frame); + frame.setVisible(true); + + frame = new Frame("Location by platform"); + frame.setLocationByPlatform(true); + c = new MyCanvas(); + frame.add(c, BorderLayout.CENTER); + frame.pack(); + PassFailJFrame.addTestWindow(frame); + frame.setVisible(true); + } + + static class MyCanvas extends Canvas { + @Override + public Dimension getPreferredSize() { + return new Dimension(400, 400); + } + + @Override + public void paint(Graphics g) { + g.setColor(Color.red); + for (int i = 399; i >= 0; i--) { + g.drawLine(400 - i - 1, 400 - i - 1, + 400 - i - 1, 399); + } + g.setColor(Color.blue); + g.drawLine(0, 0, 399, 0); + g.drawLine(0, 0, 0, 399); + g.drawLine(0, 399, 399, 399); + g.drawLine(399, 0, 399, 399); + } + } +} diff --git a/test/jdk/java/awt/Window/LocationByPlatformWithControls/TestLocationByPlatformWithControls.java b/test/jdk/java/awt/Window/LocationByPlatformWithControls/TestLocationByPlatformWithControls.java new file mode 100644 index 0000000000000..bfdba97e4eb99 --- /dev/null +++ b/test/jdk/java/awt/Window/LocationByPlatformWithControls/TestLocationByPlatformWithControls.java @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4102292 + * @summary Tests that location by platform works with other APIs + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual TestLocationByPlatformWithControls + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Checkbox; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Panel; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.util.Vector; + +public class TestLocationByPlatformWithControls extends Frame + implements ActionListener, ItemListener { + Panel northP; + Panel centerP; + Checkbox undecoratedCB; + Checkbox defaultLocationCB; + Checkbox visibleCB; + Checkbox iconifiedCB; + Checkbox maximizedCB; + Button createB; + Button packB; + Button moveB; + Button resizeB; + Button reshapeB; + Button disposeB; + Vector frames; + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This test is to check that LocationByPlatform works with other + controls API. + 1) Create New Frame by clicking on "Create" Button in + "TestLocationByPlatformWithControls" window. + 2) Initially this Frame will not be visible, Click on checkbox + "LocationByPlatform" to set default platform location for the frame + and then click on checkbox "Visible" to see that Frame is displayed + at default offsets. + 3) Now you can play with different controls like Iconified, + Maximized, Pack, Move, Resize and Reshape to verify that these + controls work properly with the Frame. + 4) At the end dispose the Frame by clicking on "Dispose" button. + 5) Also we can do verify this for Undecorated Frame but for that we + need to follow same steps but in step 2 before we click on checkbox + "Visible", select "Undecorated" checkbox along with + "LocationByPlatform". + 6) If everything works properly test is passed, otherwise failed. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(TestLocationByPlatformWithControls::new) + .logArea(4) + .build() + .awaitAndCheck(); + } + + public TestLocationByPlatformWithControls() { + northP = new Panel(); + centerP = new Panel(); + + undecoratedCB = new Checkbox("Undecorated"); + defaultLocationCB = new Checkbox("LocationByPlatform"); + visibleCB = new Checkbox("Visible"); + iconifiedCB = new Checkbox("Iconified"); + maximizedCB = new Checkbox("Maximized"); + + createB = new Button("Create"); + packB = new Button("Pack"); + moveB = new Button("Move"); + resizeB = new Button("Resize"); + reshapeB = new Button("Reshape"); + disposeB = new Button("Dispose"); + + frames = new Vector(10); + this.setTitle("TestLocationByPlatformWithControls"); + this.setLayout(new BorderLayout()); + this.add(northP, BorderLayout.NORTH); + + northP.add(new Label("New Frame")); + + createB.addActionListener(this); + northP.add(createB); + + centerP.setEnabled(false); + this.add(centerP, BorderLayout.CENTER); + + centerP.add(new Label("Last Frame")); + + centerP.add(defaultLocationCB); + defaultLocationCB.addItemListener(this); + + centerP.add(undecoratedCB); + undecoratedCB.addItemListener(this); + + centerP.add(iconifiedCB); + iconifiedCB.addItemListener(this); + + centerP.add(maximizedCB); + maximizedCB.addItemListener(this); + + centerP.add(visibleCB); + visibleCB.addItemListener(this); + + packB.addActionListener(this); + centerP.add(packB); + + moveB.addActionListener(this); + centerP.add(moveB); + + resizeB.addActionListener(this); + centerP.add(resizeB); + + reshapeB.addActionListener(this); + centerP.add(reshapeB); + + disposeB.addActionListener(this); + centerP.add(disposeB); + this.pack(); + } + + public void actionPerformed(ActionEvent e) { + if (e.getSource() == createB) { + Frame frame = new Frame(); + frame.setSize(200, 200); + frames.add(frame); + updateControls(frame); + Panel panel = new Panel(); + frame.add(panel); + panel.add(new Button ("Test Button")); + panel.add(new Button ("Test Button 1")); + panel.add(new Button ("Test Button 2")); + panel.add(new Button ("Test Button 3")); + centerP.setEnabled(true); + return; + } + + if (frames.isEmpty()) { + return; + } + + Frame last = (Frame)frames.lastElement(); + + if (e.getSource() == packB) { + last.pack(); + } else + if (e.getSource() == moveB) { + int x = (int)(Math.random() * 200); + int y = (int)(Math.random() * 200); + last.setLocation(x, y); + } else + if (e.getSource() == resizeB) { + int w = (int)(Math.random() * 200); + int h = (int)(Math.random() * 200); + last.setSize(w, h); + } else + if (e.getSource() == reshapeB) { + int x = (int)(Math.random() * 200); + int y = (int)(Math.random() * 200); + int w = (int)(Math.random() * 200); + int h = (int)(Math.random() * 200); + last.setBounds(x, y, w, h); + } else + if (e.getSource() == disposeB) { + last.dispose(); + frames.remove(frames.size() - 1); + if (frames.isEmpty()) { + updateControls(null); + centerP.setEnabled(false); + return; + } + last = (Frame)frames.lastElement(); + } + updateControls(last); + } + + public void updateControls(Frame f) { + undecoratedCB.setState(f != null ? + f.isUndecorated() : false); + defaultLocationCB.setState(f != null ? + f.isLocationByPlatform() : false); + visibleCB.setState(f != null ? + f.isVisible() : false); + iconifiedCB.setState(f != null ? + (f.getExtendedState() & Frame.ICONIFIED) != 0 : false); + maximizedCB.setState(f != null ? + (f.getExtendedState() & Frame.MAXIMIZED_BOTH) != 0 : false); + } + + public void itemStateChanged(ItemEvent e) { + Frame last = (Frame)frames.lastElement(); + try { + boolean state = e.getStateChange() == ItemEvent.SELECTED; + if (e.getSource() == visibleCB) { + last.setVisible(state); + } else + if (e.getSource() == defaultLocationCB) { + last.setLocationByPlatform(state); + } else + if (e.getSource() == undecoratedCB) { + last.setUndecorated(state); + } else + if (e.getSource() == iconifiedCB) { + if (state) { + last.setExtendedState(last.getExtendedState() | + Frame.ICONIFIED); + } else { + last.setExtendedState(last.getExtendedState() & + ~Frame.ICONIFIED); + } + } else + if (e.getSource() == maximizedCB) { + if (state) { + last.setExtendedState(last.getExtendedState() | + Frame.MAXIMIZED_BOTH); + } else { + last.setExtendedState(last.getExtendedState() & + ~Frame.MAXIMIZED_BOTH); + } + } + } catch (Throwable ex) { + PassFailJFrame.log(ex.getMessage()); + } finally { + updateControls(last); + } + } + + @Override + public void dispose() { + while (!frames.isEmpty()) { + Frame last = (Frame)frames.lastElement(); + last.dispose(); + frames.remove(frames.size() - 1); + } + } +} diff --git a/test/jdk/java/awt/Window/NoResizeEvent/NoResizeEvent.java b/test/jdk/java/awt/Window/NoResizeEvent/NoResizeEvent.java new file mode 100644 index 0000000000000..50b264acde970 --- /dev/null +++ b/test/jdk/java/awt/Window/NoResizeEvent/NoResizeEvent.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4942457 + * @key headful + * @summary Verifies that filtering of resize events on native level works. + * I.E.after Frame is shown no additional resize events are generated. + * @library /java/awt/patchlib ../../regtesthelpers + * @build java.desktop/java.awt.Helper + * @build Util + * @run main NoResizeEvent + */ + +import test.java.awt.regtesthelpers.Util; + +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; + +public class NoResizeEvent { + //Mutter can send window insets too late, causing additional resize events. + private static final boolean IS_MUTTER = Util.getWMID() == Util.MUTTER_WM; + private static final int RESIZE_COUNT_LIMIT = IS_MUTTER ? 5 : 3; + private static Frame frame; + static int resize_count = 0; + + public static void main(String[] args) throws Exception { + try { + EventQueue.invokeAndWait(() -> createUI()); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + if (resize_count > RESIZE_COUNT_LIMIT) { + throw new RuntimeException("Resize event arrived: " + + resize_count + " times."); + } + } + } + + private static void createUI() { + frame = new Frame("NoResizeEvent"); + frame.addComponentListener(new ComponentAdapter() { + public void componentResized(ComponentEvent e) { + System.out.println(e); + resize_count++; + } + }); + frame.setVisible(true); + + try { + Thread.sleep(3000); + } catch (InterruptedException ie) { + } + System.out.println("Resize count: " + resize_count); + } +} diff --git a/test/hotspot/jtreg/vmTestbase/vm/share/process/CmdExecutor.java b/test/jdk/java/awt/Window/OwnedWindowShowTest/OwnedWindowShowTest.java similarity index 53% rename from test/hotspot/jtreg/vmTestbase/vm/share/process/CmdExecutor.java rename to test/jdk/java/awt/Window/OwnedWindowShowTest/OwnedWindowShowTest.java index bea1bd8282bbb..c8b5ad4a619d0 100644 --- a/test/hotspot/jtreg/vmTestbase/vm/share/process/CmdExecutor.java +++ b/test/jdk/java/awt/Window/OwnedWindowShowTest/OwnedWindowShowTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -20,44 +20,35 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package vm.share.process; -import java.io.IOException; -import java.util.Collection; - -public class CmdExecutor extends ProcessExecutor { - private final StringBuilder cmd = new StringBuilder(); - @Override - public void clearArgs() { - cmd.setLength(0); - } +/* + * @test + * @bug 4177156 + * @key headful + * @summary Tests that multiple level of window ownership doesn't cause + * NullPointerException when showing a Window + * @run main OwnedWindowShowTest + */ - @Override - public void addArg(String arg) { - cmd.append(" " + arg); - } +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Window; - @Override - public void addArgs(String[] args) { - for (String arg : args) { - addArg(arg); - } +public class OwnedWindowShowTest { + public static void main(String[] args) throws Exception { + EventQueue.invokeAndWait(OwnedWindowShowTest::runTest); } - @Override - public void addArgs(Collection args) { - for (String arg : args) { - addArg(arg); + static void runTest() { + Frame parent = new Frame("OwnedWindowShowTest"); + try { + Window owner = new Window(parent); + Window window = new Window(owner); + // Showing a window with multiple levels of ownership + // should not throw NullPointerException + window.setVisible(true); + } finally { + parent.dispose(); } } - - @Override - protected Process createProcess() throws IOException { - return Runtime.getRuntime().exec(cmd.toString()); - } - - @Override - public String toString() { - return cmd.toString(); - } } diff --git a/test/jdk/java/awt/Window/ProxyCrash/PopupProxyCrash.java b/test/jdk/java/awt/Window/ProxyCrash/PopupProxyCrash.java new file mode 100644 index 0000000000000..b17b934a70280 --- /dev/null +++ b/test/jdk/java/awt/Window/ProxyCrash/PopupProxyCrash.java @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4381561 + * @key headful + * @summary Tests that when we show the popup window AWT doesn't crash due to + * the problems with focus proxy window code + * @run main PopupProxyCrash + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Color; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; + +import javax.swing.Box; +import javax.swing.JComboBox; +import javax.swing.JTextField; +import javax.swing.plaf.basic.BasicComboBoxUI; +import javax.swing.plaf.basic.BasicComboPopup; +import javax.swing.plaf.basic.ComboPopup; + +public class PopupProxyCrash implements ActionListener { + private static JTextField jtf; + private static Button tf; + private static Panel panel; + private static Font[] fonts; + private static Robot robot; + + private static JComboBox cb; + + private static MyComboBoxUI comboBoxUI; + private static Frame frame; + private static int TEST_COUNT = 10; + public static void main(String[] args) throws Exception { + try { + robot = new Robot(); + robot.setAutoDelay(100); + EventQueue.invokeAndWait(() -> createUI()); + robot.waitForIdle(); + runTest(); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void createUI() { + frame = new Frame("PopupProxyCrash"); + Font dialog = new Font("Dialog", Font.PLAIN, 12); + Font serif = new Font("Serif", Font.PLAIN, 12); + Font monospaced = new Font("Monospaced", Font.PLAIN, 12); + + fonts = new Font[] { dialog, serif, monospaced }; + + cb = new JComboBox(fonts); + + cb.setLightWeightPopupEnabled(false); + comboBoxUI = new MyComboBoxUI(); + cb.setUI(comboBoxUI); + jtf = new JTextField("JTextField"); + jtf.setFont(fonts[1]); + tf = new Button("TextField"); + tf.setFont(fonts[1]); + cb.addActionListener(new PopupProxyCrash()); + + panel = new Panel() { + public Dimension getPreferredSize() { + return new Dimension(100, 20); + } + public void paint(Graphics g) { + System.out.println("Painting with font " + getFont()); + g.setColor(Color.white); + g.fillRect(0, 0, getWidth(), getHeight()); + g.setColor(Color.black); + g.setFont(getFont()); + g.drawString("LightWeight", 10, 10); + } + }; + panel.setFont(fonts[1]); + + Container parent = Box.createVerticalBox(); + parent.add(jtf); + parent.add(tf); + parent.add(panel); + parent.add(cb); + + frame.add(parent, BorderLayout.CENTER); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private static Point getComboBoxLocation() throws Exception { + final Point[] result = new Point[1]; + + EventQueue.invokeAndWait(() -> { + Point point = cb.getLocationOnScreen(); + Dimension size = cb.getSize(); + + point.x += size.width / 2; + point.y += size.height / 2; + result[0] = point; + }); + return result[0]; + } + + private static Point getItemPointToClick(final int item) throws Exception { + final Point[] result = new Point[1]; + + EventQueue.invokeAndWait(() -> { + BasicComboPopup popup = (BasicComboPopup)comboBoxUI.getComboPopup(); + Point point = popup.getLocationOnScreen(); + Dimension size = popup.getSize(); + + int step = size.height / fonts.length; + point.x += size.width / 2; + point.y += step / 2 + step * item; + result[0] = point; + }); + return result[0]; + } + + static void runTest() throws Exception { + for (int i = 0; i < TEST_COUNT; i++) { + Point point = getComboBoxLocation(); + robot.mouseMove(point.x, point.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(500); + + point = getItemPointToClick(i % fonts.length); + robot.mouseMove(point.x, point.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(500); + } + } + public void actionPerformed(ActionEvent ae) { + System.out.println("Font selected"); + Font font = fonts[((JComboBox)ae.getSource()).getSelectedIndex()]; + + tf.setFont(font); + jtf.setFont(font); + panel.setFont(font); + panel.repaint(); + } + + private static class MyComboBoxUI extends BasicComboBoxUI { + public ComboPopup getComboPopup() { + return popup; + } + } +} diff --git a/test/jdk/java/awt/Window/ResizeTest/ResizeTest.java b/test/jdk/java/awt/Window/ResizeTest/ResizeTest.java new file mode 100644 index 0000000000000..a9191f8bd0536 --- /dev/null +++ b/test/jdk/java/awt/Window/ResizeTest/ResizeTest.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4225955 + * @summary Tests that focus lost is delivered to a lightweight component + * in a disposed window + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ResizeTest + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Dialog; +import java.awt.Frame; + +public class ResizeTest +{ + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1) Push button A to create modal dialog 2. + 2) Resize dialog 2, then click button B to hide it. + 3) Push button A again. Dialog B should be packed to its original + size. + 4) Push button B again to hide, and A to reshow. + Dialog B should still be the same size, then test is passed, + otherwise failed. + 5) Push button B to hide the modal dialog and then select pass/fail. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(ResizeTest::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame f = new Frame("1"); + Dialog d = new Dialog(f, "2", true); + d.setLocationRelativeTo(null); + Button b2 = new Button("B"); + b2.addActionListener(e -> d.setVisible(false)); + d.setLayout(new BorderLayout()); + d.add(b2, BorderLayout.CENTER); + + Button b = new Button("A"); + f.add(b, BorderLayout.CENTER); + b.addActionListener(e -> { + d.pack(); + d.setVisible(true); + }); + f.pack(); + return f; + } +} diff --git a/test/jdk/java/awt/Window/ShowWindowTest/ShowWindowTest.java b/test/jdk/java/awt/Window/ShowWindowTest/ShowWindowTest.java new file mode 100644 index 0000000000000..4857929c94ebc --- /dev/null +++ b/test/jdk/java/awt/Window/ShowWindowTest/ShowWindowTest.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4084997 + * @summary See if Window can be created without its size explicitly set + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ShowWindowTest + */ + +import java.awt.Button; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class ShowWindowTest implements ActionListener +{ + private static Window window; + private static Button showButton; + private static Button hideButton; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. You should see a Frame with a "Show" and a "Hide" button in it. + 2. Click on the "Show" button. A window with a "Hello World" Label + should appear + 3. If the window does not appear, the test failed, otherwise passed. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(ShowWindowTest::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame frame = new Frame("ShowWindowTest"); + frame.setLayout(new FlowLayout()); + frame.setSize(100,100); + frame.add(showButton = new Button("Show")); + frame.add(hideButton = new Button("Hide")); + + ActionListener handler = new ShowWindowTest(); + showButton.addActionListener(handler); + hideButton.addActionListener(handler); + + window = new Window(frame); + window.add("Center", new Label("Hello World")); + window.setLocationRelativeTo(null); + return frame; + } + + public void actionPerformed(ActionEvent e) { + if (e.getSource() == showButton) { + window.pack(); + window.setVisible(true); + } else if (e.getSource() == hideButton) + window.setVisible(false); + } +} diff --git a/test/jdk/java/awt/Window/WindowToFrontTest/WindowToFrontTest.java b/test/jdk/java/awt/Window/WindowToFrontTest/WindowToFrontTest.java new file mode 100644 index 0000000000000..e6bbadcb546db --- /dev/null +++ b/test/jdk/java/awt/Window/WindowToFrontTest/WindowToFrontTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4488209 + * @summary JFrame toFront causes the entire frame to be repainted, causes UI + * to flash + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual WindowToFrontTest + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class WindowToFrontTest implements ActionListener { + static Frame frame; + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1) Click the "toFront" button, this causes the + "WindowToFrontTest" frame to move front and gets repainted + completely. + 2) Move "WindowToFrontTest" window and continue to click on "toFront + multiple times. If the "WindowToFrontTest" Frame content is not + drawn properly and continues to blink, test is failed + otherwise passed. + """; + + PassFailJFrame passFailJFrame = PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows(13) + .columns(40) + .build(); + EventQueue.invokeAndWait(() -> createUI()); + passFailJFrame.awaitAndCheck(); + } + + private static void createUI() { + frame = new Frame("WindowToFrontTest"); + frame.setLayout(new BorderLayout()); + frame.setSize(512, 512); + PassFailJFrame.addTestWindow(frame); + frame.setVisible(true); + + Frame buttonFrame = new Frame("Test Button"); + Button push = new Button("toFront"); + push.addActionListener(new WindowToFrontTest()); + buttonFrame.add(push); + buttonFrame.pack(); + PassFailJFrame.addTestWindow(buttonFrame); + buttonFrame.setVisible(true); + } + + public void actionPerformed(ActionEvent e) { + frame.toFront(); + } +} diff --git a/test/jdk/java/awt/Window/bug4189244.java b/test/jdk/java/awt/Window/bug4189244.java new file mode 100644 index 0000000000000..df37d2fce0e2b --- /dev/null +++ b/test/jdk/java/awt/Window/bug4189244.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4189244 + * @summary Swing Popup menu is not being refreshed (cleared) under a Dialog + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @requires (os.family == "windows") + * @run main/manual bug4189244 +*/ + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; + +public class bug4189244 { + + private static final String INSTRUCTIONS = """ + This is Windows only test! + + Click right button on frame to show popup menu. + (menu should be placed inside frame otherwise bug is not reproducible) + click on any menu item (dialog will be shown). + close dialog. + if you see part of popupmenu, under dialog, before it is closed, + then test failed, else passed."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug4189244 Instructions") + .instructions(INSTRUCTIONS) + .rows((int)INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(bug4189244::createTestUI) + .build() + .awaitAndCheck(); + } + + + private static JFrame createTestUI() { + RefreshBug panel = new RefreshBug(); + JFrame frame = new JFrame("Popup refresh bug"); + + frame.add(panel, BorderLayout.CENTER); + panel.init(); + frame.setSize(400, 400); + return frame; + } +} + +class RefreshBug extends JPanel implements ActionListener { + JPopupMenu _jPopupMenu = new JPopupMenu(); + + public void init() { + JMenuItem menuItem; + JButton jb = new JButton("Bring the popup here and select an item"); + + this.add(jb, BorderLayout.CENTER); + + for(int i = 1; i < 10; i++) { + menuItem = new JMenuItem("Item " + i); + menuItem.addActionListener(this); + _jPopupMenu.add(menuItem); + } + + MouseListener ml = new MouseAdapter() { + public void mouseReleased(MouseEvent e) { + if (e.isPopupTrigger()) { + _jPopupMenu.show(e.getComponent(), + e.getX(), e.getY()); + } + } + }; + this.addMouseListener(ml); + + jb.addMouseListener(ml); + + } + + // An action is requested by the user + public void actionPerformed(java.awt.event.ActionEvent e) { + JOptionPane.showMessageDialog(this, + "Check if there is some popup left under me\n"+ + "if not, retry and let the popup appear where i am", + "WARNING", + JOptionPane.WARNING_MESSAGE); + + } +} diff --git a/test/jdk/java/awt/color/ICC_Profile/SerializedFormSize.java b/test/jdk/java/awt/color/ICC_Profile/SerializedFormSize.java new file mode 100644 index 0000000000000..bb8d7a0ab88e5 --- /dev/null +++ b/test/jdk/java/awt/color/ICC_Profile/SerializedFormSize.java @@ -0,0 +1,72 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.color.ColorSpace; +import java.awt.color.ICC_Profile; +import java.io.ByteArrayOutputStream; +import java.io.ObjectOutputStream; + +/** + * @test + * @bug 8369032 + * @summary Checks the size of the serialized ICC_Profile for standard and + * non-standard profiles. + */ +public final class SerializedFormSize { + + private static final ICC_Profile[] PROFILES = { + ICC_Profile.getInstance(ColorSpace.CS_sRGB), + ICC_Profile.getInstance(ColorSpace.CS_LINEAR_RGB), + ICC_Profile.getInstance(ColorSpace.CS_CIEXYZ), + ICC_Profile.getInstance(ColorSpace.CS_PYCC), + ICC_Profile.getInstance(ColorSpace.CS_GRAY) + }; + + public static void main(String[] args) throws Exception { + for (ICC_Profile profile : PROFILES) { + byte[] data = profile.getData(); + int dataSize = data.length; + int min = 3; // At least version, name and data fields + int max = 200; // Small enough to confirm no data saved + + // Standard profile: should serialize to a small size, no data + test(profile, min, max); + // Non-standard profile: includes full data, but only once + test(ICC_Profile.getInstance(data), dataSize, dataSize + max); + } + } + + private static void test(ICC_Profile p, int min, int max) throws Exception { + try (var bos = new ByteArrayOutputStream(); + var oos = new ObjectOutputStream(bos)) + { + oos.writeObject(p); + int size = bos.size(); + if (size < min || size > max) { + System.err.println("Expected: >= " + min + " and <= " + max); + System.err.println("Actual: " + size); + throw new RuntimeException("Wrong size"); + } + } + } +} diff --git a/test/jdk/java/awt/color/ICC_Profile/ValidateICCHeaderData/ValidateICCHeaderData.java b/test/jdk/java/awt/color/ICC_Profile/ValidateICCHeaderData/ValidateICCHeaderData.java new file mode 100644 index 0000000000000..28831a422b0e3 --- /dev/null +++ b/test/jdk/java/awt/color/ICC_Profile/ValidateICCHeaderData/ValidateICCHeaderData.java @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8337703 + * @summary To verify if ICC_Profile's setData() and getInstance() methods + * validate header data and throw IAE for invalid values. + * @run main ValidateICCHeaderData + */ + +import java.awt.color.ColorSpace; +import java.awt.color.ICC_Profile; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.math.BigInteger; +import java.nio.ByteBuffer; + +public class ValidateICCHeaderData { + private static ICC_Profile profile; + + private static final boolean DEBUG = false; + private static final int VALID_HEADER_SIZE = 128; + private static final int HEADER_TAG = ICC_Profile.icSigHead; + private static final int PROFILE_CLASS_START_INDEX = ICC_Profile.icHdrDeviceClass; + private static final int COLOR_SPACE_START_INDEX = ICC_Profile.icHdrColorSpace; + private static final int RENDER_INTENT_START_INDEX = ICC_Profile.icHdrRenderingIntent; + private static final int PCS_START_INDEX = ICC_Profile.icHdrPcs; + + private static final int[] VALID_PROFILE_CLASS = new int[] { + ICC_Profile.icSigInputClass, ICC_Profile.icSigDisplayClass, + ICC_Profile.icSigOutputClass, ICC_Profile.icSigLinkClass, + ICC_Profile.icSigAbstractClass, ICC_Profile.icSigColorSpaceClass, + ICC_Profile.icSigNamedColorClass + }; + + private static final int[] VALID_COLOR_SPACE = new int[] { + ICC_Profile.icSigXYZData, ICC_Profile.icSigLabData, + ICC_Profile.icSigLuvData, ICC_Profile.icSigYCbCrData, + ICC_Profile.icSigYxyData, ICC_Profile.icSigRgbData, + ICC_Profile.icSigGrayData, ICC_Profile.icSigHsvData, + ICC_Profile.icSigHlsData, ICC_Profile.icSigCmykData, + ICC_Profile.icSigSpace2CLR, ICC_Profile.icSigSpace3CLR, + ICC_Profile.icSigSpace4CLR, ICC_Profile.icSigSpace5CLR, + ICC_Profile.icSigSpace6CLR, ICC_Profile.icSigSpace7CLR, + ICC_Profile.icSigSpace8CLR, ICC_Profile.icSigSpace9CLR, + ICC_Profile.icSigSpaceACLR, ICC_Profile.icSigSpaceBCLR, + ICC_Profile.icSigSpaceCCLR, ICC_Profile.icSigSpaceDCLR, + ICC_Profile.icSigSpaceECLR, ICC_Profile.icSigSpaceFCLR, + ICC_Profile.icSigCmyData + }; + + private static final int[] VALID_RENDER_INTENT = new int[] { + ICC_Profile.icPerceptual, ICC_Profile.icMediaRelativeColorimetric, + ICC_Profile.icSaturation, ICC_Profile.icAbsoluteColorimetric + }; + + private static void createCopyOfBuiltInProfile() { + ICC_Profile builtInProfile = ICC_Profile.getInstance(ColorSpace.CS_sRGB); + //copy of SRGB BuiltIn Profile that can be modified + //using ICC_Profile.setData() + profile = ICC_Profile.getInstance(builtInProfile.getData()); + } + + public static void main(String[] args) throws Exception { + createCopyOfBuiltInProfile(); + + System.out.println("CASE 1: Testing VALID Profile Classes ..."); + testValidHeaderData(VALID_PROFILE_CLASS, PROFILE_CLASS_START_INDEX, 4); + System.out.println("CASE 1: Passed \n"); + + // PCS field validation for Profile class != DEVICE_LINK + System.out.println("CASE 2: Testing VALID PCS Type" + + " for Profile class != DEVICE_LINK ..."); + testValidHeaderData(new int[] {ICC_Profile.icSigXYZData, ICC_Profile.icSigLabData}, + PCS_START_INDEX, 4); + System.out.println("CASE 2: Passed \n"); + + System.out.println("CASE 3: Testing INVALID PCS Type" + + " for Profile class != DEVICE_LINK ..."); + testInvalidHeaderData(ICC_Profile.icSigCmykData, PCS_START_INDEX, 4); + System.out.println("CASE 3: Passed \n"); + + System.out.println("CASE 4: Testing DEVICE LINK PROFILE CLASS ..."); + testValidHeaderData(new int[] {ICC_Profile.icSigLinkClass}, + PROFILE_CLASS_START_INDEX, 4); + //to check if instantiating BufferedImage with + //ICC_Profile device class = CLASS_DEVICELINK does not throw IAE. + BufferedImage img = new BufferedImage(100, 100, + BufferedImage.TYPE_3BYTE_BGR); + System.out.println("CASE 4: Passed \n"); + + // PCS field validation for Profile class == DEVICE_LINK + System.out.println("CASE 5: Testing VALID PCS Type" + + " for Profile class == DEVICE_LINK ..."); + testValidHeaderData(VALID_COLOR_SPACE, PCS_START_INDEX, 4); + System.out.println("CASE 5: Passed \n"); + + System.out.println("CASE 6: Testing INVALID PCS Type" + + " for Profile class == DEVICE_LINK ..."); + //original icSigLabData = 0x4C616220 + int invalidSigLabData = 0x4C616221; + testInvalidHeaderData(invalidSigLabData, PCS_START_INDEX, 4); + System.out.println("CASE 6: Passed \n"); + + System.out.println("CASE 7: Testing VALID Color Spaces ..."); + testValidHeaderData(VALID_COLOR_SPACE, COLOR_SPACE_START_INDEX, 4); + System.out.println("CASE 7: Passed \n"); + + System.out.println("CASE 8: Testing VALID Rendering Intent ..."); + testValidHeaderData(VALID_RENDER_INTENT, RENDER_INTENT_START_INDEX, 4); + System.out.println("CASE 8: Passed \n"); + + System.out.println("CASE 9: Testing INVALID Profile Class ..."); + //original icSigInputClass = 0x73636E72 + int invalidSigInputClass = 0x73636E70; + testInvalidHeaderData(invalidSigInputClass, PROFILE_CLASS_START_INDEX, 4); + System.out.println("CASE 9: Passed \n"); + + System.out.println("CASE 10: Testing INVALID Color Space ..."); + //original icSigXYZData = 0x58595A20 + int invalidSigXYZData = 0x58595A21; + testInvalidHeaderData(invalidSigXYZData, COLOR_SPACE_START_INDEX, 4); + System.out.println("CASE 10: Passed \n"); + + System.out.println("CASE 11: Testing INVALID Rendering Intent ..."); + //valid rendering intent values are 0-3 + int invalidRenderIntent = 5; + testInvalidHeaderData(invalidRenderIntent, RENDER_INTENT_START_INDEX, 4); + System.out.println("CASE 11: Passed \n"); + + System.out.println("CASE 12: Testing INVALID Header Size ..."); + testInvalidHeaderSize(); + System.out.println("CASE 12: Passed \n"); + + System.out.println("CASE 13: Testing ICC_Profile.getInstance(..)" + + " with VALID profile data ..."); + testProfileCreation(true); + System.out.println("CASE 13: Passed \n"); + + System.out.println("CASE 14: Testing ICC_Profile.getInstance(..)" + + " with INVALID profile data ..."); + testProfileCreation(false); + System.out.println("CASE 14: Passed \n"); + + System.out.println("CASE 15: Testing Deserialization of ICC_Profile ..."); + testDeserialization(); + System.out.println("CASE 15: Passed \n"); + + System.out.println("Successfully completed testing all 15 cases. Test Passed !!"); + } + + private static void testValidHeaderData(int[] validData, int startIndex, + int fieldLength) { + for (int value : validData) { + setTag(value, startIndex, fieldLength); + } + } + + private static void testInvalidHeaderData(int invalidData, int startIndex, + int fieldLength) { + try { + setTag(invalidData, startIndex, fieldLength); + throw new RuntimeException("Test Failed ! Expected IAE NOT thrown"); + } catch (IllegalArgumentException iae) { + System.out.println("Expected IAE thrown: " + iae.getMessage()); + } + } + + private static void setTag(int value, int startIndex, int fieldLength) { + byte[] byteArray; + if (startIndex == RENDER_INTENT_START_INDEX) { + byteArray = ByteBuffer.allocate(4).putInt(value).array(); + } else { + BigInteger big = BigInteger.valueOf(value); + byteArray = (big.toByteArray()); + } + + if (DEBUG) { + System.out.print("Byte Array : "); + for (int i = 0; i < byteArray.length; i++) { + System.out.print(byteArray[i] + " "); + } + System.out.println("\n"); + } + + byte[] iccProfileHeaderData = profile.getData(HEADER_TAG); + System.arraycopy(byteArray, 0, iccProfileHeaderData, startIndex, fieldLength); + profile.setData(HEADER_TAG, iccProfileHeaderData); + } + + private static void testProfileCreation(boolean validCase) { + ICC_Profile builtInProfile = ICC_Profile.getInstance(ColorSpace.CS_GRAY); + byte[] profileData = builtInProfile.getData(); + + int validDeviceClass = ICC_Profile.icSigInputClass; + BigInteger big = BigInteger.valueOf(validDeviceClass); + //valid case set device class to 0x73636E72 (icSigInputClass) + //invalid case set device class to 0x00000000 + byte[] field = validCase ? big.toByteArray() + : ByteBuffer.allocate(4).putInt(0).array(); + System.arraycopy(field, 0, profileData, PROFILE_CLASS_START_INDEX, 4); + + try { + ICC_Profile.getInstance(profileData); + if (!validCase) { + throw new RuntimeException("Test Failed ! Expected IAE NOT thrown"); + } + } catch (IllegalArgumentException iae) { + if (!validCase) { + System.out.println("Expected IAE thrown: " + iae.getMessage()); + } else { + throw new RuntimeException("Unexpected IAE thrown"); + } + } + } + + private static void testInvalidHeaderSize() { + byte[] iccProfileHeaderData = profile.getData(HEADER_TAG); + byte[] invalidHeaderSize = new byte[VALID_HEADER_SIZE - 1]; + System.arraycopy(iccProfileHeaderData, 0, + invalidHeaderSize, 0, invalidHeaderSize.length); + try { + profile.setData(HEADER_TAG, invalidHeaderSize); + throw new RuntimeException("Test Failed ! Expected IAE NOT thrown"); + } catch (IllegalArgumentException iae) { + System.out.println("Expected IAE thrown: " + iae.getMessage()); + } + } + + private static void testDeserialization() throws IOException { + //invalidSRGB.icc is serialized on older version of JDK + //Upon deserialization, the invalid profile is expected to throw IAE + try { + ICC_Profile.getInstance("./invalidSRGB.icc"); + throw new RuntimeException("Test Failed ! Expected IAE NOT thrown"); + } catch (IllegalArgumentException iae) { + System.out.println("Expected IAE thrown: " + iae.getMessage()); + } + } +} diff --git a/test/jdk/java/awt/color/ICC_Profile/ValidateICCHeaderData/invalidSRGB.icc b/test/jdk/java/awt/color/ICC_Profile/ValidateICCHeaderData/invalidSRGB.icc new file mode 100644 index 0000000000000..1520dac1f1a0c Binary files /dev/null and b/test/jdk/java/awt/color/ICC_Profile/ValidateICCHeaderData/invalidSRGB.icc differ diff --git a/test/jdk/java/awt/color/NonICCFilterTest.java b/test/jdk/java/awt/color/NonICCFilterTest.java new file mode 100644 index 0000000000000..bddd89ed6b355 --- /dev/null +++ b/test/jdk/java/awt/color/NonICCFilterTest.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.GradientPaint; +import java.awt.Graphics2D; +import java.awt.Transparency; +import java.awt.color.ColorSpace; +import java.awt.image.BufferedImage; +import java.awt.image.ColorConvertOp; +import java.awt.image.ComponentColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.WritableRaster; + +/* + * @test + * @bug 8316497 + * @summary Verifies Color filter on non-ICC profile + */ +public final class NonICCFilterTest { + private static final int WIDTH = 100; + private static final int HEIGHT = 100; + + private enum ColorSpaceSelector { + GRAY, + RGB, + PYCC, + WRAPPED_GRAY, + WRAPPED_RGB, + WRAPPED_PYCC + } + + private static final class TestColorSpace extends ColorSpace { + + private final ColorSpace cs; + + TestColorSpace(ColorSpace cs) { + super(cs.getType(), cs.getNumComponents()); + this.cs = cs; + } + + @Override + public float[] toRGB(float[] colorvalue) { + return cs.toRGB(colorvalue); + } + + @Override + public float[] fromRGB(float[] rgbvalue) { + return cs.fromRGB(rgbvalue); + } + + @Override + public float[] toCIEXYZ(float[] colorvalue) { + return cs.toCIEXYZ(colorvalue); + } + + @Override + public float[] fromCIEXYZ(float[] xyzvalue) { + return cs.fromCIEXYZ(xyzvalue); + } + } + + private static BufferedImage createTestImage(final ColorSpace cs) { + ComponentColorModel cm = new ComponentColorModel(cs, false, false, + Transparency.OPAQUE, DataBuffer.TYPE_BYTE); + WritableRaster raster = cm.createCompatibleWritableRaster(WIDTH, HEIGHT); + BufferedImage img = new BufferedImage(cm, raster, false, null); + + Graphics2D g = img.createGraphics(); + GradientPaint gp = new GradientPaint(0, 0, Color.GREEN, + raster.getWidth(), raster.getHeight(), Color.BLUE); + g.setPaint(gp); + g.fillRect(0, 0, raster.getWidth(), raster.getHeight()); + g.dispose(); + + return img; + } + + private static ColorSpace createCS(ColorSpaceSelector selector) { + return switch (selector) { + case GRAY -> ColorSpace.getInstance(ColorSpace.CS_GRAY); + case WRAPPED_GRAY -> new TestColorSpace(ColorSpace.getInstance(ColorSpace.CS_GRAY)); + + case RGB -> ColorSpace.getInstance(ColorSpace.CS_sRGB); + case WRAPPED_RGB -> new TestColorSpace(ColorSpace.getInstance(ColorSpace.CS_sRGB)); + + case PYCC -> ColorSpace.getInstance(ColorSpace.CS_PYCC); + case WRAPPED_PYCC -> new TestColorSpace(ColorSpace.getInstance(ColorSpace.CS_PYCC)); + }; + } + + private static boolean areImagesEqual(BufferedImage destTest, BufferedImage destGold) { + for (int x = 0; x < destTest.getWidth(); x++) { + for (int y = 0; y < destTest.getHeight(); y++) { + int rgb1 = destTest.getRGB(x, y); + int rgb2 = destGold.getRGB(x, y); + if (rgb1 != rgb2) { + System.err.println("x = " + x + ", y = " + y); + System.err.println("rgb1 = " + Integer.toHexString(rgb1)); + System.err.println("rgb2 = " + Integer.toHexString(rgb2)); + return false; + } + } + } + return true; + } + + public static void main(String[] args) { + BufferedImage srcTest = createTestImage(createCS(ColorSpaceSelector.WRAPPED_GRAY)); + BufferedImage destTest = createTestImage(createCS(ColorSpaceSelector.WRAPPED_RGB)); + + BufferedImage srcGold = createTestImage(createCS(ColorSpaceSelector.GRAY)); + BufferedImage destGold = createTestImage(createCS(ColorSpaceSelector.RGB)); + + ColorConvertOp gold = new ColorConvertOp(createCS(ColorSpaceSelector.PYCC), null); + gold.filter(srcTest, destTest); + gold.filter(srcGold, destGold); + + if (!areImagesEqual(destTest, destGold)) { + throw new RuntimeException("ICC test failed"); + } + + ColorConvertOp test = new ColorConvertOp(createCS(ColorSpaceSelector.WRAPPED_PYCC), null); + test.filter(srcTest, destTest); + test.filter(srcGold, destGold); + + if (!areImagesEqual(destTest, destGold)) { + throw new RuntimeException("Wrapper test failed"); + } + } +} diff --git a/test/jdk/java/awt/color/XAWTDifference/XAWTColors.jpg b/test/jdk/java/awt/color/XAWTDifference/XAWTColors.jpg new file mode 100644 index 0000000000000..f53b30407cc28 Binary files /dev/null and b/test/jdk/java/awt/color/XAWTDifference/XAWTColors.jpg differ diff --git a/test/jdk/java/awt/color/XAWTDifference/XAWTDifference.java b/test/jdk/java/awt/color/XAWTDifference/XAWTDifference.java new file mode 100644 index 0000000000000..ef0f5aacf4479 --- /dev/null +++ b/test/jdk/java/awt/color/XAWTDifference/XAWTDifference.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Canvas; +import java.awt.Checkbox; +import java.awt.Choice; +import java.awt.Component; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.Label; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.Panel; +import java.awt.PopupMenu; +import java.awt.ScrollPane; +import java.awt.Scrollbar; +import java.awt.TextArea; +import java.awt.TextField; +import java.awt.Window; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.ImageIcon; +import javax.swing.JLabel; + +/* + * @test + * @bug 5092883 6513478 7154025 + * @requires (os.family == "linux") + * @summary REGRESSION: SystemColor class gives back wrong values under Linux + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual XAWTDifference + */ + +public class XAWTDifference { + + private static final String INSTRUCTIONS = """ + You would see a frame with title "XAWTDifference Test Frame". + + Test Frame (1) + + a) It has three columns in it. The 1st one with ordinary components. + The 2nd one with disabled components. + The 3rd one with uneditable components (only text components + are there). Verify that the difference between different states + is visible. + + Standard Frame (2) + + b) You would also see a frame named StandardFrame (2) + with a lot of components in it. Actually this is just a jpg-image + in a frame. Verify that every component in the frame (1) looks + similar to the same component in (2). + + They might differ in colors and be darker or brighter but + the whole picture should be the same. + + c) Also check the color of the MenuBar Items in the MenuBar and + the PopupMenu assigned to TextArea. + As you can't compare the colors of menu items with the picture + so just look if the are adequate enough. + """; + private static final int HGAP = 20; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(XAWTDifference::createAndShowUI) + .positionTestUI(XAWTDifference::positionMultiTestUI) + .build() + .awaitAndCheck(); + } + + private static Panel addComponentsIntoPanel(boolean enabled, boolean editable) { + TextField tf = new TextField("TextField"); + TextArea ta = new TextArea("TextArea", 10, 10); + + Choice levelChooser = new Choice(); + levelChooser.add("Item #1"); + levelChooser.add("Item #2"); + + Button b = new Button("BUTTON"); + Label label = new Label("LABEL"); + java.awt.List list = new java.awt.List(4, false); + list.add("one"); + list.add("two"); + list.add("three"); + + Checkbox chb = new Checkbox(); + Scrollbar sb = new Scrollbar(Scrollbar.HORIZONTAL); + ScrollPane sp = new ScrollPane(ScrollPane.SCROLLBARS_ALWAYS); + sp.add(new TextArea("this is a textarea in scrollpane")); + sp.setSize(200, 200); + Canvas canvas = new Canvas(); + canvas.setSize(100, 100); + + //add popup menu to Button + final PopupMenu pm = new PopupMenu(); + MenuItem i1 = new MenuItem("Item1"); + MenuItem i2 = new MenuItem("Item2"); + MenuItem i3 = new MenuItem("Item3"); + i3.setEnabled(false); + pm.add(i1); + pm.add(i2); + pm.add(i3); + canvas.add(pm); + + ta.add(pm); + ta.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent me) { + if (me.isPopupTrigger()) { + pm.show(me.getComponent(), me.getX(), me.getY()); + } + } + }); + + ArrayList componentList = new ArrayList<>(); + + componentList.add(tf); + componentList.add(ta); + if (editable){ + componentList.add(levelChooser); + componentList.add(b); + componentList.add(label); + componentList.add(list); + componentList.add(chb); + componentList.add(sb); + componentList.add(sp); + componentList.add(canvas); + } else { + tf.setEditable(false); + ta.setEditable(false); + } + + Panel panel = new Panel(); + panel.setLayout(new GridLayout(0, 1)); + for (Component c : componentList) { + if (!enabled) { + c.setEnabled(false); + } + panel.add(c); + } + return panel; + } + + private static List createAndShowUI() { + Frame testFrame = new Frame("XAWTDifference Test Frame"); + StandardFrame standardFrame = new StandardFrame("StandardFrame"); + standardFrame.pack(); + + testFrame.setLayout(new GridLayout(1, 3)); + testFrame.add(addComponentsIntoPanel(true, true)); + testFrame.add(addComponentsIntoPanel(false, true)); + testFrame.add(addComponentsIntoPanel(true, false)); + + MenuItem mi1 = new MenuItem("Item1"); + MenuItem mi2 = new MenuItem("Item2"); + MenuItem mi3 = new MenuItem("Disabled Item3"); + mi3.setEnabled(false); + + MenuBar mb = new MenuBar(); + Menu enabledMenu = new Menu("Enabled Menu"); + Menu disabledMenu = new Menu("Disabled Menu"); + disabledMenu.setEnabled(false); + mb.add(enabledMenu); + mb.add(disabledMenu); + enabledMenu.add(mi1); + enabledMenu.add(mi2); + enabledMenu.add(mi3); + + testFrame.setMenuBar(mb); + testFrame.setSize(standardFrame.getWidth(), standardFrame.getHeight()); + return List.of(testFrame, standardFrame); + } + + private static void positionMultiTestUI(List windows, + PassFailJFrame.InstructionUI instructionUI) { + int x = instructionUI.getLocation().x + instructionUI.getSize().width + HGAP; + for (Window w : windows) { + w.setLocation(x, instructionUI.getLocation().y); + x += w.getWidth() + HGAP; + } + } + + private static class StandardFrame extends Frame { + public StandardFrame(String name) { + super(name); + String testPath = System.getProperty("test.src", "."); + Panel panel = new Panel(); + panel.add(new JLabel(new ImageIcon(testPath + File.separator + "XAWTColors.jpg"))); + add(panel); + } + } +} diff --git a/test/jdk/java/awt/datatransfer/ClipboardPerformanceTest.java b/test/jdk/java/awt/datatransfer/ClipboardPerformanceTest.java new file mode 100644 index 0000000000000..3bad044b9498f --- /dev/null +++ b/test/jdk/java/awt/datatransfer/ClipboardPerformanceTest.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4463560 + * @requires (os.family == "windows") + * @summary Tests that datatransfer doesn't take too much time to complete + * @key headful + * @library /test/lib + * @run main/timeout=300 ClipboardPerformanceTest + */ + +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class ClipboardPerformanceTest { + public static final int CODE_FAILURE = 1; + public static final int CODE_OTHER_FAILURE = 2; + static String eoln; + static char[] text; + public static final int ARRAY_SIZE = 100000; + public static final int RATIO_THRESHOLD = 10; + + public static void main(String[] args) throws Exception { + if (args.length == 0) { + ClipboardPerformanceTest clipboardPerformanceTest = new ClipboardPerformanceTest(); + clipboardPerformanceTest.initialize(); + return; + } + + long before, after, oldTime, newTime; + float ratio; + + try { + Transferable t = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null); + before = System.currentTimeMillis(); + String ss = (String) t.getTransferData(new DataFlavor("text/plain; class=java.lang.String")); + after = System.currentTimeMillis(); + + System.err.println("Size: " + ss.length()); + newTime = after - before; + System.err.println("Time consumed: " + newTime); + + initArray(); + + StringBuffer buf = new StringBuffer(new String(text)); + int eoln_len = eoln.length(); + before = System.currentTimeMillis(); + + for (int i = 0; i + eoln_len <= buf.length(); i++) { + if (eoln.equals(buf.substring(i, i + eoln_len))) { + buf.replace(i, i + eoln_len, "\n"); + } + } + + after = System.currentTimeMillis(); + oldTime = after - before; + System.err.println("Old algorithm: " + oldTime); + ratio = oldTime / newTime; + System.err.println("Ratio: " + ratio); + + if (ratio < RATIO_THRESHOLD) { + System.out.println("Time ratio failure!!"); + System.exit(CODE_FAILURE); + } + } catch (Throwable e) { + e.printStackTrace(); + System.exit(CODE_OTHER_FAILURE); + } + System.out.println("Test Pass!"); + } + + public static void initArray() { + text = new char[ARRAY_SIZE + 2]; + + for (int i = 0; i < ARRAY_SIZE; i += 3) { + text[i] = '\r'; + text[i + 1] = '\n'; + text[i + 2] = 'a'; + } + eoln = "\r\n"; + } + + public void initialize() throws Exception { + initArray(); + Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard(); + cb.setContents(new StringSelection(new String(text)), null); + + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + ClipboardPerformanceTest.class.getName(), + "child" + ); + + Process process = ProcessTools.startProcess("Child", pb); + OutputAnalyzer outputAnalyzer = new OutputAnalyzer(process); + + if (!process.waitFor(15, TimeUnit.SECONDS)) { + process.destroyForcibly(); + throw new TimeoutException("Timed out waiting for Child"); + } + + outputAnalyzer.shouldHaveExitValue(0); + } +} diff --git a/test/jdk/java/awt/datatransfer/HTMLDataFlavors/ManualHTMLDataFlavorTest.html b/test/jdk/java/awt/datatransfer/HTMLDataFlavors/ManualHTMLDataFlavorTest.html deleted file mode 100644 index 0a444d5b8ea45..0000000000000 --- a/test/jdk/java/awt/datatransfer/HTMLDataFlavors/ManualHTMLDataFlavorTest.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - -ManualHTMLDataFlavorTest - - - -

    ManualHTMLDataFlavorTest
    Bug ID: 7075105

    - -

    See the dialog box (usually in upper left corner) for instructions

    - - - - diff --git a/test/jdk/java/awt/datatransfer/HTMLDataFlavors/ManualHTMLDataFlavorTest.java b/test/jdk/java/awt/datatransfer/HTMLDataFlavors/ManualHTMLDataFlavorTest.java index 015d46f03b244..4e875461ba274 100644 --- a/test/jdk/java/awt/datatransfer/HTMLDataFlavors/ManualHTMLDataFlavorTest.java +++ b/test/jdk/java/awt/datatransfer/HTMLDataFlavors/ManualHTMLDataFlavorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,24 +22,32 @@ */ /* - test + @test @bug 7075105 @summary WIN: Provide a way to format HTML on drop - @author Denis Fokin: area=datatransfer - @run applet/manual=yesno ManualHTMLDataFlavorTest + @library /java/awt/regtesthelpers + @run main/manual ManualHTMLDataFlavorTest */ -import java.applet.Applet; -import java.awt.*; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Panel; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; -import java.awt.dnd.*; +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.IOException; -public class ManualHTMLDataFlavorTest extends Applet { +public class ManualHTMLDataFlavorTest { - class DropPane extends Panel implements DropTargetListener { + static class DropPane extends Panel implements DropTargetListener { DropPane() { requestFocus(); @@ -49,7 +57,7 @@ class DropPane extends Panel implements DropTargetListener { @Override public Dimension getPreferredSize() { - return new Dimension(200,200); + return new Dimension(400, 400); } @Override @@ -73,15 +81,15 @@ public void dragExit(DropTargetEvent dte) {} @Override public void drop(DropTargetDropEvent dtde) { if (!dtde.isDataFlavorSupported(DataFlavor.allHtmlFlavor)) { - Sysout.println("DataFlavor.allHtmlFlavor is not present in the system clipboard"); + ManualHTMLDataFlavorTest.log("DataFlavor.allHtmlFlavor is not present in the system clipboard"); dtde.rejectDrop(); return; } else if (!dtde.isDataFlavorSupported(DataFlavor.fragmentHtmlFlavor)) { - Sysout.println("DataFlavor.fragmentHtmlFlavor is not present in the system clipboard"); + ManualHTMLDataFlavorTest.log("DataFlavor.fragmentHtmlFlavor is not present in the system clipboard"); dtde.rejectDrop(); return; } else if (!dtde.isDataFlavorSupported(DataFlavor.selectionHtmlFlavor)) { - Sysout.println("DataFlavor.selectionHtmlFlavor is not present in the system clipboard"); + ManualHTMLDataFlavorTest.log("DataFlavor.selectionHtmlFlavor is not present in the system clipboard"); dtde.rejectDrop(); return; } @@ -90,12 +98,13 @@ public void drop(DropTargetDropEvent dtde) { Transferable t = dtde.getTransferable(); try { - Sysout.println("ALL:"); - Sysout.println(t.getTransferData(DataFlavor.allHtmlFlavor).toString()); - Sysout.println("FRAGMENT:"); - Sysout.println(t.getTransferData(DataFlavor.fragmentHtmlFlavor).toString()); - Sysout.println("SELECTION:"); - Sysout.println(t.getTransferData(DataFlavor.selectionHtmlFlavor).toString()); + ManualHTMLDataFlavorTest.log("ALL:"); + ManualHTMLDataFlavorTest.log(t.getTransferData(DataFlavor.allHtmlFlavor).toString()); + t.getTransferData(DataFlavor.allHtmlFlavor).toString(); + ManualHTMLDataFlavorTest.log("FRAGMENT:"); + ManualHTMLDataFlavorTest.log(t.getTransferData(DataFlavor.fragmentHtmlFlavor).toString()); + ManualHTMLDataFlavorTest.log("SELECTION:"); + ManualHTMLDataFlavorTest.log(t.getTransferData(DataFlavor.selectionHtmlFlavor).toString()); } catch (UnsupportedFlavorException | IOException e) { e.printStackTrace(); } @@ -103,189 +112,49 @@ public void drop(DropTargetDropEvent dtde) { } } - public void init() { - - String[] instructions = - { - "1) The test contains a drop-aware panel with a red background", - "2) Open some page in a browser, select some text", - " Drag and drop it on the red panel", - " IMPORTANT NOTE: the page should be stored locally.", - " otherwise for instance iexplore can prohibit drag and drop from", - " the browser to other applications because of", - " the protected mode restrictions.", - " On Mac OS X do NOT use Safari, it does not provide the needed DataFlavor", - "3) Check the data in the output area of this dialog", - "5) The output should not contain information that any of", - " flavors is not present in the system clipboard", - "6) The output should contain data in three different formats", - " provided by the system clipboard", - " - Data after the \"ALL:\" marker should include the data", - " from the the \"SELECTION:\" marker", - " - Data after the \"FRAGMENT\" marker should include the data", - " from the \"SELECTION:\" marker and may be some closing", - " tags could be added to the mark-up", - " - Data after the \"SELECTION:\" marker should correspond", - " to the data selected in the browser", - "7) If the above requirements are met, the test is passed" - }; - - add(new DropPane()); - Sysout.createDialogWithInstructions( instructions ); - - new ManualHTMLDataFlavorTest(); + static final String INSTRUCTIONS = """ + 1) The test contains a drop-aware panel with a red background. + 2) Open some page in a browser, select some text. + Drag and drop it on the red panel. + IMPORTANT NOTE: the page should be stored locally. + Otherwise for instance Internet Explorer may prohibit drag and drop from + the browser to other applications because of protected mode restrictions. + On MacOS do NOT use Safari, it does not provide the needed DataFlavor. + 3) Check the data in the output area of this window. + 5) The output should not contain information that any of + flavors is not present in the system clipboard. + 6) The output should contain data in three different formats + provided by the system clipboard. + - Data after the "ALL:" marker should include the data + from the "SELECTION:" marker". + - Data after the "FRAGMENT" marker should include the data + from the "SELECTION:" marker and may be some closing + tags could be added to the mark-up. + - Data after the "SELECTION:" marker should correspond + to the data selected in the browser. + 7) If the above requirements are met, the test is passed. + """; + + static Frame createDropWindow() { + Frame frame = new Frame("Manual HTML DataFlavor Test"); + frame.add(new DropPane()); + frame.setAlwaysOnTop(true); + frame.pack(); + return frame; } - public void start () - { - setSize (200,200); - setVisible(true); - validate(); - - }// start() - + static void log(String msg) { + PassFailJFrame.log(msg); + } + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .rows(25) + .columns(50) + .testUI(ManualHTMLDataFlavorTest::createDropWindow) + .logArea() + .build() + .awaitAndCheck(); + } } - - -/* Place other classes related to the test after this line */ - - - - - -/**************************************************** - Standard Test Machinery - DO NOT modify anything below -- it's a standard - chunk of code whose purpose is to make user - interaction uniform, and thereby make it simpler - to read and understand someone else's test. - ****************************************************/ - -/** - This is part of the standard test machinery. - It creates a dialog (with the instructions), and is the interface - for sending text messages to the user. - To print the instructions, send an array of strings to Sysout.createDialog - WithInstructions method. Put one line of instructions per array entry. - To display a message for the tester to see, simply call Sysout.println - with the string to be displayed. - This mimics System.out.println but works within the test harness as well - as standalone. - */ - -class Sysout -{ - private static TestDialog dialog; - - public static void createDialogWithInstructions( String[] instructions ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - dialog.printInstructions( instructions ); - dialog.setVisible(true); - println( "Any messages for the tester will display here." ); - } - - public static void createDialog( ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - String[] defInstr = { "Instructions will appear here. ", "" } ; - dialog.printInstructions( defInstr ); - dialog.setVisible(true); - println( "Any messages for the tester will display here." ); - } - - - public static void printInstructions( String[] instructions ) - { - dialog.printInstructions( instructions ); - } - - - public static void println( String messageIn ) - { - dialog.displayMessage( messageIn ); - } - -}// Sysout class - -/** - This is part of the standard test machinery. It provides a place for the - test instructions to be displayed, and a place for interactive messages - to the user to be displayed. - To have the test instructions displayed, see Sysout. - To have a message to the user be displayed, see Sysout. - Do not call anything in this dialog directly. - */ -class TestDialog extends Dialog -{ - - TextArea instructionsText; - TextArea messageText; - int maxStringLength = 80; - - //DO NOT call this directly, go through Sysout - public TestDialog( Frame frame, String name ) - { - super( frame, name ); - int scrollBoth = TextArea.SCROLLBARS_BOTH; - instructionsText = new TextArea( "", 15, maxStringLength, scrollBoth ); - add( "North", instructionsText ); - - messageText = new TextArea( "", 5, maxStringLength, scrollBoth ); - add("Center", messageText); - - pack(); - - setVisible(true); - }// TestDialog() - - //DO NOT call this directly, go through Sysout - public void printInstructions( String[] instructions ) - { - //Clear out any current instructions - instructionsText.setText( "" ); - - //Go down array of instruction strings - - String printStr, remainingStr; - for( int i=0; i < instructions.length; i++ ) - { - //chop up each into pieces maxSringLength long - remainingStr = instructions[ i ]; - while( remainingStr.length() > 0 ) - { - //if longer than max then chop off first max chars to print - if( remainingStr.length() >= maxStringLength ) - { - //Try to chop on a word boundary - int posOfSpace = remainingStr. - lastIndexOf( ' ', maxStringLength - 1 ); - - if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1; - - printStr = remainingStr.substring( 0, posOfSpace + 1 ); - remainingStr = remainingStr.substring( posOfSpace + 1 ); - } - //else just print - else - { - printStr = remainingStr; - remainingStr = ""; - } - - instructionsText.append( printStr + "\n" ); - - }// while - - }// for - - }//printInstructions() - - //DO NOT call this directly, go through Sysout - public void displayMessage( String messageIn ) - { - messageText.append( messageIn + "\n" ); - System.out.println(messageIn); - } - -}// TestDialog class diff --git a/test/jdk/java/awt/datatransfer/HTMLTransferConsoleOutputTest.java b/test/jdk/java/awt/datatransfer/HTMLTransferConsoleOutputTest.java new file mode 100644 index 0000000000000..6a18a11270fcb --- /dev/null +++ b/test/jdk/java/awt/datatransfer/HTMLTransferConsoleOutputTest.java @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4638351 + * @summary tests that HTML transfer doesn't cause console output + * @key headful + * @library /test/lib + * @run main HTMLTransferConsoleOutputTest + */ + +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.ClipboardOwner; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class HTMLTransferConsoleOutputTest implements ClipboardOwner { + static final Clipboard clipboard = + Toolkit.getDefaultToolkit().getSystemClipboard(); + static final DataFlavor dataFlavor = + new DataFlavor("text/html; class=java.lang.String", null); + static final String magic = "TESTMAGICSTRING"; + static final Transferable transferable = new Transferable() { + final DataFlavor[] flavors = new DataFlavor[]{dataFlavor}; + final String data = "" + magic + ""; + + public DataFlavor[] getTransferDataFlavors() { + return flavors; + } + + public boolean isDataFlavorSupported(DataFlavor df) { + return dataFlavor.equals(df); + } + + public Object getTransferData(DataFlavor df) + throws UnsupportedFlavorException { + if (!isDataFlavorSupported(df)) { + throw new UnsupportedFlavorException(df); + } + return data; + } + }; + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + public static final int CLIPBOARD_DELAY = 1000; + + public static void main(String[] args) throws Exception { + if (args.length == 0) { + HTMLTransferConsoleOutputTest htmlTransferConsoleOutputTest = new HTMLTransferConsoleOutputTest(); + htmlTransferConsoleOutputTest.initialize(); + return; + } + final ClipboardOwner clipboardOwner = new ClipboardOwner() { + public void lostOwnership(Clipboard clip, + Transferable contents) { + System.exit(0); + } + }; + clipboard.setContents(transferable, clipboardOwner); + final Object o = new Object(); + synchronized (o) { + try { + o.wait(); + } catch (InterruptedException ie) { + ie.printStackTrace(); + } + } + System.out.println("Test Pass!"); + } + + public void initialize() throws Exception { + clipboard.setContents(transferable, this); + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + HTMLTransferConsoleOutputTest.class.getName(), + "child" + ); + + Process process = ProcessTools.startProcess("Child", pb); + OutputAnalyzer outputAnalyzer = new OutputAnalyzer(process); + + if (!process.waitFor(15, TimeUnit.SECONDS)) { + process.destroyForcibly(); + throw new TimeoutException("Timed out waiting for Child"); + } + + byte[] bytes = baos.toByteArray(); + String string = null; + try { + string = new String(bytes, "ASCII"); + } catch (UnsupportedEncodingException uee) { + uee.printStackTrace(); + } + if (string.lastIndexOf(magic) != -1) { + throw new RuntimeException("Test failed. Output contains:" + + string); + } + + outputAnalyzer.shouldHaveExitValue(0); + } + + + static class ForkOutputStream extends OutputStream { + final OutputStream outputStream1; + final OutputStream outputStream2; + + public ForkOutputStream(OutputStream os1, OutputStream os2) { + outputStream1 = os1; + outputStream2 = os2; + } + + public void write(int b) throws IOException { + outputStream1.write(b); + outputStream2.write(b); + } + + public void flush() throws IOException { + outputStream1.flush(); + outputStream2.flush(); + } + + public void close() throws IOException { + outputStream1.close(); + outputStream2.close(); + } + } + + public void lostOwnership(Clipboard clip, Transferable contents) { + final Runnable r = () -> { + try { + Thread.sleep(CLIPBOARD_DELAY); + } catch (InterruptedException e) { + e.printStackTrace(); + } + final PrintStream oldOut = System.out; + final PrintStream newOut = + new PrintStream(new ForkOutputStream(oldOut, baos)); + Transferable t = clipboard.getContents(null); + try { + System.setOut(newOut); + t.getTransferData(dataFlavor); + System.setOut(oldOut); + } catch (IOException | UnsupportedFlavorException ioe) { + ioe.printStackTrace(); + } + clipboard.setContents(transferable, null); + }; + final Thread t = new Thread(r); + t.start(); + } +} diff --git a/test/jdk/java/awt/datatransfer/ImageTransferCrashTest.java b/test/jdk/java/awt/datatransfer/ImageTransferCrashTest.java new file mode 100644 index 0000000000000..76e484b3ce2b5 --- /dev/null +++ b/test/jdk/java/awt/datatransfer/ImageTransferCrashTest.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4513976 + * @summary tests that inter-JVM image transfer doesn't cause crash + * @key headful + * @library /test/lib + * @run main ImageTransferCrashTest + */ + +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.ClipboardOwner; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.image.BufferedImage; +import java.awt.image.WritableRaster; +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class ImageTransferCrashTest implements ClipboardOwner { + static final Clipboard clipboard = + Toolkit.getDefaultToolkit().getSystemClipboard(); + final Transferable textTransferable = new StringSelection("TEXT"); + public static final int CLIPBOARD_DELAY = 10; + + public static void main(String[] args) throws Exception { + if (args.length == 0) { + ImageTransferCrashTest imageTransferCrashTest = new ImageTransferCrashTest(); + imageTransferCrashTest.initialize(); + return; + } + final ClipboardOwner clipboardOwner = (clip, contents) -> System.exit(0); + final int width = 100; + final int height = 100; + final BufferedImage bufferedImage = + new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + final WritableRaster writableRaster = + bufferedImage.getWritableTile(0, 0); + final int[] color = new int[]{0x80, 0x80, 0x80}; + for (int i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { + writableRaster.setPixel(i, j, color); + } + } + bufferedImage.releaseWritableTile(0, 0); + + final Transferable imageTransferable = new Transferable() { + final DataFlavor[] flavors = new DataFlavor[]{ + DataFlavor.imageFlavor}; + + public DataFlavor[] getTransferDataFlavors() { + return flavors; + } + + public boolean isDataFlavorSupported(DataFlavor df) { + return DataFlavor.imageFlavor.equals(df); + } + + public Object getTransferData(DataFlavor df) + throws UnsupportedFlavorException { + if (!isDataFlavorSupported(df)) { + throw new UnsupportedFlavorException(df); + } + return bufferedImage; + } + }; + clipboard.setContents(imageTransferable, clipboardOwner); + final Object o = new Object(); + synchronized (o) { + try { + o.wait(); + } catch (InterruptedException ie) { + ie.printStackTrace(); + } + } + System.out.println("Test Pass!"); + } + + public void initialize() throws Exception { + clipboard.setContents(textTransferable, this); + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + ImageTransferCrashTest.class.getName(), + "child" + ); + + Process process = ProcessTools.startProcess("Child", pb); + OutputAnalyzer outputAnalyzer = new OutputAnalyzer(process); + + if (!process.waitFor(15, TimeUnit.SECONDS)) { + process.destroyForcibly(); + throw new TimeoutException("Timed out waiting for Child"); + } + + outputAnalyzer.shouldHaveExitValue(0); + } + + public void lostOwnership(Clipboard clip, Transferable contents) { + final Runnable r = () -> { + while (true) { + try { + Thread.sleep(CLIPBOARD_DELAY); + Transferable t = clipboard.getContents(null); + t.getTransferData(DataFlavor.imageFlavor); + } catch (IllegalStateException e) { + e.printStackTrace(); + System.err.println("clipboard is not prepared yet"); + continue; + } catch (Exception e) { + e.printStackTrace(); + } + break; + } + clipboard.setContents(textTransferable, null); + }; + final Thread t = new Thread(r); + t.start(); + } +} diff --git a/test/jdk/java/awt/dnd/CustomDragCursorTest.java b/test/jdk/java/awt/dnd/CustomDragCursorTest.java new file mode 100644 index 0000000000000..9e5e006b31c81 --- /dev/null +++ b/test/jdk/java/awt/dnd/CustomDragCursorTest.java @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Robot; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetContext; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.dnd.DropTargetListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/* + * @test + * @bug 4451328 + * @summary tests that a custom drag cursor is not changed + to the default drag cursor + * @key headful + * @run main CustomDragCursorTest + */ + +public class CustomDragCursorTest { + private static Frame frame; + private static final DragSourcePanel dragSourcePanel = new DragSourcePanel(); + private static final DropTargetPanel dropTargetPanel = new DropTargetPanel(); + + private static volatile Point srcPoint; + private static volatile Point dstPoint; + private static volatile boolean passed = true; + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + EventQueue.invokeAndWait(CustomDragCursorTest::createAndShowUI); + robot.waitForIdle(); + robot.delay(1000); + + EventQueue.invokeAndWait(() -> { + Point p = dragSourcePanel.getLocationOnScreen(); + Dimension d = dragSourcePanel.getSize(); + p.translate(d.width / 2, d.height / 2); + srcPoint = p; + + p = dropTargetPanel.getLocationOnScreen(); + d = dropTargetPanel.getSize(); + p.translate(d.width / 2, d.height / 2); + dstPoint = p; + }); + + robot.mouseMove(srcPoint.x, srcPoint.y); + robot.keyPress(KeyEvent.VK_CONTROL); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + for (; !srcPoint.equals(dstPoint); + srcPoint.translate(sign(dstPoint.x - srcPoint.x), + sign(dstPoint.y - srcPoint.y))) { + robot.mouseMove(srcPoint.x, srcPoint.y); + robot.delay(10); + } + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.keyRelease(KeyEvent.VK_CONTROL); + robot.waitForIdle(); + robot.delay(1000); + + if (!passed) { + throw new RuntimeException("Custom drag cursor changed to default."); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void createAndShowUI() { + frame = new Frame("CustomDragCursorTest"); + frame.setLayout(new GridLayout(2, 1)); + frame.add(dragSourcePanel); + frame.add(dropTargetPanel); + frame.setLocationRelativeTo(null); + frame.setSize(300, 400); + frame.setVisible(true); + } + + public static void failed() { + passed = false; + } + + private static int sign(int n) { + return Integer.compare(n, 0); + } + + private static class DragSourceButton extends Button implements Serializable, + Transferable, + DragGestureListener, + DragSourceListener { + private final DataFlavor dataflavor = + new DataFlavor(Button.class, "DragSourceButton"); + private final Cursor dragCursor = new Cursor(Cursor.HAND_CURSOR); + + public DragSourceButton() { + this("DragSourceButton"); + } + + public DragSourceButton(String str) { + super(str); + + DragSource ds = DragSource.getDefaultDragSource(); + ds.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY, + this); + } + + public void dragGestureRecognized(DragGestureEvent dge) { + dge.startDrag(dragCursor, this, this); + } + + public void dragEnter(DragSourceDragEvent dsde) { + if (!dragCursor.equals(dsde.getDragSourceContext().getCursor())) { + CustomDragCursorTest.failed(); + } + } + + public void dragExit(DragSourceEvent dse) { + if (!dragCursor.equals(dse.getDragSourceContext().getCursor())) { + CustomDragCursorTest.failed(); + } + } + + public void dragOver(DragSourceDragEvent dsde) { + if (!dragCursor.equals(dsde.getDragSourceContext().getCursor())) { + CustomDragCursorTest.failed(); + } + } + + public void dragDropEnd(DragSourceDropEvent dsde) { + if (!dragCursor.equals(dsde.getDragSourceContext().getCursor())) { + CustomDragCursorTest.failed(); + } + } + + public void dropActionChanged(DragSourceDragEvent dsde) { + if (!dragCursor.equals(dsde.getDragSourceContext().getCursor())) { + CustomDragCursorTest.failed(); + } + } + + public Object getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException, IOException { + + if (!isDataFlavorSupported(flavor)) { + throw new UnsupportedFlavorException(flavor); + } + + Object retObj; + + ByteArrayOutputStream baoStream = new ByteArrayOutputStream(); + ObjectOutputStream ooStream = new ObjectOutputStream(baoStream); + ooStream.writeObject(this); + + ByteArrayInputStream baiStream = new ByteArrayInputStream(baoStream.toByteArray()); + ObjectInputStream ois = new ObjectInputStream(baiStream); + try { + retObj = ois.readObject(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + throw new RuntimeException(e.toString()); + } + + return retObj; + } + + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[] { dataflavor }; + } + + public boolean isDataFlavorSupported(DataFlavor dflavor) { + return dataflavor.equals(dflavor); + } + } + + private static class DragSourcePanel extends Panel { + + final Dimension preferredDimension = new Dimension(200, 100); + + public DragSourcePanel() { + setLayout(new GridLayout(1, 1)); + add(new DragSourceButton()); + } + + public Dimension getPreferredSize() { + return preferredDimension; + } + } + + private static class DropTargetPanel extends Panel implements DropTargetListener { + + final Dimension preferredDimension = new Dimension(200, 100); + + public DropTargetPanel() { + setDropTarget(new DropTarget(this, this)); + } + + public Dimension getPreferredSize() { + return preferredDimension; + } + + public void dragEnter(DropTargetDragEvent dtde) {} + + public void dragExit(DropTargetEvent dte) {} + + public void dragOver(DropTargetDragEvent dtde) {} + + public void dropActionChanged(DropTargetDragEvent dtde) {} + + public void drop(DropTargetDropEvent dtde) { + DropTargetContext dtc = dtde.getDropTargetContext(); + + if ((dtde.getSourceActions() & DnDConstants.ACTION_COPY) != 0) { + dtde.acceptDrop(DnDConstants.ACTION_COPY); + } else { + dtde.rejectDrop(); + } + + DataFlavor[] dfs = dtde.getCurrentDataFlavors(); + Component comp = null; + + if(dfs != null && dfs.length >= 1) { + Transferable transfer = dtde.getTransferable(); + + try { + comp = (Component)transfer.getTransferData(dfs[0]); + } catch (Throwable e) { + e.printStackTrace(); + dtc.dropComplete(false); + } + } + dtc.dropComplete(true); + add(comp); + } + } +} diff --git a/test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDAcceptanceTest.java b/test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDAcceptanceTest.java new file mode 100644 index 0000000000000..700187f9dce49 --- /dev/null +++ b/test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDAcceptanceTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Frame; +import java.awt.Panel; + +/* + * @test + * @bug 4166541 4225247 4297663 + * @summary Tests Basic DnD functionality + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DnDAcceptanceTest + */ + +public class DnDAcceptanceTest { + private static final String INSTRUCTIONS = """ + When test runs a Frame which contains a yellow button labeled + "Drag ME!" and a RED Panel will appear. + + Click on the button and drag to the red panel. + When the mouse enters the red panel + during the drag the panel should turn yellow. + + Release the mouse button, panel should turn red again and + a yellow button labeled Drag ME! should appear inside the panel. + You should be able to repeat this operation multiple times. + + If above is true press PASS, else press FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(38) + .testUI(DnDAcceptanceTest::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame frame = new Frame("DnDAcceptanceTest"); + Panel mainPanel; + Component dragSource, dropTarget; + + frame.setSize(400, 400); + frame.setLayout(new BorderLayout()); + + mainPanel = new Panel(); + mainPanel.setLayout(new BorderLayout()); + + mainPanel.setBackground(Color.BLACK); + + dropTarget = new DnDTarget(Color.RED, Color.YELLOW); + dragSource = new DnDSource("Drag ME!"); + + mainPanel.add(dragSource, "North"); + mainPanel.add(dropTarget, "Center"); + frame.add(mainPanel, BorderLayout.CENTER); + frame.setAlwaysOnTop(true); + return frame; + } +} diff --git a/test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDSource.java b/test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDSource.java new file mode 100644 index 0000000000000..a35ccd9ee12cb --- /dev/null +++ b/test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDSource.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Container; +import java.awt.Toolkit; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.MouseDragGestureRecognizer; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.InvalidDnDOperationException; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +class DnDSource extends Button implements Transferable, + DragGestureListener, + DragSourceListener { + private DataFlavor df; + private transient int dropAction; + + DnDSource(String label) { + super(label); + Toolkit.getDefaultToolkit().createDragGestureRecognizer(MouseDragGestureRecognizer.class, + DragSource.getDefaultDragSource(), + this, DnDConstants.ACTION_COPY, this); + setBackground(Color.yellow); + setForeground(Color.blue); + df = new DataFlavor(DnDSource.class, "DnDSource"); + } + + public void dragGestureRecognized(DragGestureEvent dge) { + System.err.println("starting Drag"); + try { + dge.startDrag(null, this, this); + } catch (InvalidDnDOperationException e) { + e.printStackTrace(); + } + } + + public void dragEnter(DragSourceDragEvent dsde) { + System.err.println("[Source] dragEnter"); + dsde.getDragSourceContext().setCursor(DragSource.DefaultCopyDrop); + } + + public void dragOver(DragSourceDragEvent dsde) { + System.err.println("[Source] dragOver"); + dropAction = dsde.getDropAction(); + System.out.println("dropAction = " + dropAction); + } + + public void dragGestureChanged(DragSourceDragEvent dsde) { + System.err.println("[Source] dragGestureChanged"); + dropAction = dsde.getDropAction(); + System.out.println("dropAction = " + dropAction); + } + + public void dragExit(DragSourceEvent dsde) { + System.err.println("[Source] dragExit"); + dsde.getDragSourceContext().setCursor(null); + } + + public void dragDropEnd(DragSourceDropEvent dsde) { + System.err.println("[Source] dragDropEnd"); + } + + public void dropActionChanged(DragSourceDragEvent dsde) { + System.err.println("[Source] dropActionChanged"); + dropAction = dsde.getDropAction(); + System.out.println("dropAction = " + dropAction); + } + + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[] {df}; + } + + public boolean isDataFlavorSupported(DataFlavor sdf) { + return df.equals(sdf); + } + + public Object getTransferData(DataFlavor tdf) throws UnsupportedFlavorException, IOException { + + Object copy = null; + + if (!df.equals(tdf)) { + throw new UnsupportedFlavorException(tdf); + } + Container parent = getParent(); + switch (dropAction) { + case DnDConstants.ACTION_COPY: + try { + copy = this.clone(); + } catch (CloneNotSupportedException e) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + + oos.writeObject(this); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream ois = new ObjectInputStream(bais); + try { + copy = ois.readObject(); + } catch (ClassNotFoundException cnfe) { + // do nothing + } + } + + parent.add(this); + return copy; + + case DnDConstants.ACTION_MOVE: + synchronized(this) { + if (parent != null) parent.remove(this); + } + return this; + + case DnDConstants.ACTION_LINK: + return this; + + default: + //throw new IOException("bad operation"); + return this; // workaround for: 4135456 getDropAction() always return 0 + } + } +} diff --git a/test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDTarget.java b/test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDTarget.java new file mode 100644 index 0000000000000..d147741f0d225 --- /dev/null +++ b/test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDTarget.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Panel; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetContext; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.dnd.DropTargetListener; +import java.io.IOException; + +class DnDTarget extends Panel implements DropTargetListener { + Color bgColor; + Color htColor; + + DnDTarget(Color bgColor, Color htColor) { + super(); + this.bgColor = bgColor; + this.htColor = htColor; + setBackground(bgColor); + setDropTarget(new DropTarget(this, this)); + } + + public void dragEnter(DropTargetDragEvent e) { + System.err.println("[Target] dragEnter"); + e.acceptDrag(DnDConstants.ACTION_COPY); + setBackground(htColor); + repaint(); + } + + public void dragOver(DropTargetDragEvent e) { + System.err.println("[Target] dragOver"); + e.acceptDrag(DnDConstants.ACTION_COPY); + } + + public void dragExit(DropTargetEvent e) { + System.err.println("[Target] dragExit"); + setBackground(bgColor); + repaint(); + } + + public void drop(DropTargetDropEvent dtde) { + System.err.println("[Target] drop"); + DropTargetContext dtc = dtde.getDropTargetContext(); + + if ((dtde.getSourceActions() & DnDConstants.ACTION_COPY) != 0) { + dtde.acceptDrop(DnDConstants.ACTION_COPY); + } else { + dtde.rejectDrop(); + return; + } + + DataFlavor[] dfs = dtde.getCurrentDataFlavors(); + if (dfs != null && dfs.length >= 1) { + Transferable transfer = dtde.getTransferable(); + Object obj; + try { + obj = transfer.getTransferData(dfs[0]); + } catch (IOException | UnsupportedFlavorException ex) { + System.err.println(ex.getMessage()); + dtc.dropComplete(false); + return; + } + + if (obj != null) { + Button button; + try { + button = (Button) obj; + } catch (Exception e) { + System.err.println(e.getMessage()); + dtc.dropComplete(false); + return; + } + add(button); + repaint(); + } + } + setBackground(bgColor); + invalidate(); + validate(); + repaint(); + dtc.dropComplete(true); + } + + public void dropActionChanged(DropTargetDragEvent e) { + System.err.println("[Target] dropActionChanged"); + } +} diff --git a/test/jdk/java/awt/dnd/DnDClipboardDeadlockTest.java b/test/jdk/java/awt/dnd/DnDClipboardDeadlockTest.java new file mode 100644 index 0000000000000..ab2bff8ecc5a5 --- /dev/null +++ b/test/jdk/java/awt/dnd/DnDClipboardDeadlockTest.java @@ -0,0 +1,441 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4388802 + * @summary tests that clipboard operations during drag-and-drop don't deadlock + * @key headful + * @run main DnDClipboardDeadlockTest + */ + +import java.awt.AWTEvent; +import java.awt.AWTException; +import java.awt.Button; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.GridLayout; +import java.awt.List; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Robot; +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetContext; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.dnd.DropTargetListener; +import java.awt.event.AWTEventListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; + +public class DnDClipboardDeadlockTest { + + public static final int CODE_NOT_RETURNED = -1; + public static final int CODE_OK = 0; + public static final int CODE_FAILURE = 1; + + private int returnCode = CODE_NOT_RETURNED; + + final Frame frame = new Frame(); + Robot robot = null; + Panel panel = null; + + public static void main(String[] args) throws Exception { + DnDClipboardDeadlockTest test = new DnDClipboardDeadlockTest(); + if (args.length == 4) { + test.run(args); + } else { + test.start(); + } + } + + public void run(String[] args) throws InterruptedException, AWTException { + try { + if (args.length != 4) { + throw new RuntimeException("Incorrect command line arguments."); + } + + int x = Integer.parseInt(args[0]); + int y = Integer.parseInt(args[1]); + int w = Integer.parseInt(args[2]); + int h = Integer.parseInt(args[3]); + + Transferable t = new StringSelection("TEXT"); + panel = new DragSourcePanel(t); + + frame.setTitle("DragSource frame"); + frame.setLocation(300, 200); + frame.add(panel); + frame.pack(); + frame.setVisible(true); + + Util.waitForInit(); + + Point sourcePoint = panel.getLocationOnScreen(); + Dimension d = panel.getSize(); + sourcePoint.translate(d.width / 2, d.height / 2); + + Point targetPoint = new Point(x + w / 2, y + h / 2); + + robot = new Robot(); + + if (!Util.pointInComponent(robot, sourcePoint, panel)) { + throw new RuntimeException("WARNING: Cannot locate source panel"); + } + + robot.mouseMove(sourcePoint.x, sourcePoint.y); + robot.keyPress(KeyEvent.VK_CONTROL); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + for (; !sourcePoint.equals(targetPoint); + sourcePoint.translate(sign(targetPoint.x - sourcePoint.x), + sign(targetPoint.y - sourcePoint.y))) { + robot.mouseMove(sourcePoint.x, sourcePoint.y); + Thread.sleep(10); + } + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.keyRelease(KeyEvent.VK_CONTROL); + if (frame != null) { + frame.dispose(); + } + + } catch (Throwable e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } // run() + + public static int sign(int n) { + return n < 0 ? -1 : n == 0 ? 0 : 1; + } + + public void start() { + panel = new DropTargetPanel(); + + frame.setTitle("DropTarget frame"); + frame.setLocation(10, 200); + frame.add(panel); + + frame.pack(); + frame.setVisible(true); + + try { + Util.waitForInit(); + + Point p = panel.getLocationOnScreen(); + Dimension d = panel.getSize(); + + try { + Robot robot = new Robot(); + Point center = new Point(p); + center.translate(d.width / 2, d.height / 2); + if (!Util.pointInComponent(robot, center, panel)) { + System.err.println("WARNING: Cannot locate target panel"); + return; + } + } catch (AWTException awte) { + awte.printStackTrace(); + return; + } + + String javaPath = System.getProperty("java.home", ""); + String command = javaPath + File.separator + "bin" + + File.separator + "java -cp " + + System.getProperty("java.class.path", ".") + + " DnDClipboardDeadlockTest " + + p.x + " " + p.y + " " + d.width + " " + d.height; + + Process process = Runtime.getRuntime().exec(command); + returnCode = process.waitFor(); + + InputStream errorStream = process.getErrorStream(); + int count = errorStream.available(); + if (count > 0) { + byte[] b = new byte[count]; + errorStream.read(b); + System.err.println("========= Child VM System.err ========"); + System.err.print(new String(b)); + System.err.println("======================================"); + } + + } catch (Throwable e) { + e.printStackTrace(); + } + switch (returnCode) { + case CODE_NOT_RETURNED: + System.err.println("Child VM: failed to start"); + break; + case CODE_OK: + System.err.println("Child VM: normal termination"); + break; + case CODE_FAILURE: + System.err.println("Child VM: abnormal termination"); + break; + } + if (returnCode != CODE_OK) { + throw new RuntimeException("The test failed."); + } + if (frame != null) { + frame.dispose(); + } + } // start() +} // class DnDClipboardDeadlockTest + +class Util implements AWTEventListener { + private static final Toolkit tk = Toolkit.getDefaultToolkit(); + private static final Object SYNC_LOCK = new Object(); + private Component clickedComponent = null; + private static final int PAINT_TIMEOUT = 10000; + private static final int MOUSE_RELEASE_TIMEOUT = 10000; + private static final Util util = new Util(); + + static { + tk.addAWTEventListener(util, 0xFFFFFFFF); + } + + private void reset() { + clickedComponent = null; + } + + public void eventDispatched(AWTEvent e) { + if (e.getID() == MouseEvent.MOUSE_RELEASED) { + clickedComponent = (Component) e.getSource(); + synchronized (SYNC_LOCK) { + SYNC_LOCK.notifyAll(); + } + } + } + + public static boolean pointInComponent(Robot robot, Point p, Component comp) + throws InterruptedException { + return util.isPointInComponent(robot, p, comp); + } + + private boolean isPointInComponent(Robot robot, Point p, Component comp) + throws InterruptedException { + tk.sync(); + robot.waitForIdle(); + reset(); + robot.mouseMove(p.x, p.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + synchronized (SYNC_LOCK) { + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + SYNC_LOCK.wait(MOUSE_RELEASE_TIMEOUT); + } + + Component c = clickedComponent; + + while (c != null && c != comp) { + c = c.getParent(); + } + + return c == comp; + } + + public static void waitForInit() throws InterruptedException { + final Frame f = new Frame() { + public void paint(Graphics g) { + dispose(); + synchronized (SYNC_LOCK) { + SYNC_LOCK.notifyAll(); + } + } + }; + f.setBounds(600, 400, 200, 200); + synchronized (SYNC_LOCK) { + f.setVisible(true); + SYNC_LOCK.wait(PAINT_TIMEOUT); + } + tk.sync(); + } +} + +class DragSourceButton extends Button implements Serializable, + DragGestureListener, + DragSourceListener { + static final Clipboard systemClipboard = + Toolkit.getDefaultToolkit().getSystemClipboard(); + final Transferable transferable; + + public DragSourceButton(Transferable t) { + super("DragSourceButton"); + + this.transferable = t; + DragSource ds = DragSource.getDefaultDragSource(); + ds.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY, + this); + } + + public void dragGestureRecognized(DragGestureEvent dge) { + dge.startDrag(null, transferable, this); + } + + public void dragEnter(DragSourceDragEvent dsde) { + } + + public void dragExit(DragSourceEvent dse) { + } + + public void dragOver(DragSourceDragEvent dsde) { + try { + Transferable t = systemClipboard.getContents(null); + if (t.isDataFlavorSupported(DataFlavor.stringFlavor)) { + String str = (String) t.getTransferData(DataFlavor.stringFlavor); + } + systemClipboard.setContents(new StringSelection("SOURCE"), null); + } catch (IOException ioe) { + ioe.printStackTrace(); + if (!ioe.getMessage().equals("Owner failed to convert data")) { + throw new RuntimeException("Owner failed to convert data"); + } + } catch (IllegalStateException e) { + // IllegalStateExceptions do not indicate a bug in this case. + // They result from concurrent modification of system clipboard + // contents by the parent and child processes. + // These exceptions are numerous, so we avoid dumping their + // backtraces to prevent blocking child process io, which + // causes test failure on timeout. + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void dragDropEnd(DragSourceDropEvent dsde) { + System.exit(DnDClipboardDeadlockTest.CODE_OK); + } + + public void dropActionChanged(DragSourceDragEvent dsde) { + } +} + +class DragSourcePanel extends Panel { + + final Dimension preferredDimension = new Dimension(200, 200); + + public DragSourcePanel(Transferable t) { + setLayout(new GridLayout(1, 1)); + add(new DragSourceButton(t)); + } + + public Dimension getPreferredSize() { + return preferredDimension; + } +} + +class DropTargetPanel extends Panel implements DropTargetListener { + + static final Clipboard systemClipboard = + Toolkit.getDefaultToolkit().getSystemClipboard(); + final Dimension preferredDimension = new Dimension(200, 200); + + public DropTargetPanel() { + setBackground(Color.green); + setDropTarget(new DropTarget(this, this)); + setLayout(new GridLayout(1, 1)); + } + + public Dimension getPreferredSize() { + return preferredDimension; + } + + public void dragEnter(DropTargetDragEvent dtde) { + dtde.acceptDrag(DnDConstants.ACTION_COPY); + } + + public void dragExit(DropTargetEvent dte) { + } + + public void dragOver(DropTargetDragEvent dtde) { + dtde.acceptDrag(DnDConstants.ACTION_COPY); + try { + Transferable t = systemClipboard.getContents(null); + if (t.isDataFlavorSupported(DataFlavor.stringFlavor)) { + String str = (String) t.getTransferData(DataFlavor.stringFlavor); + } + systemClipboard.setContents(new StringSelection("TARGET"), null); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void drop(DropTargetDropEvent dtde) { + DropTargetContext dtc = dtde.getDropTargetContext(); + + if ((dtde.getSourceActions() & DnDConstants.ACTION_COPY) != 0) { + dtde.acceptDrop(DnDConstants.ACTION_COPY); + } else { + dtde.rejectDrop(); + return; + } + + removeAll(); + final List list = new List(); + add(list); + + Transferable t = dtde.getTransferable(); + DataFlavor[] dfs = t.getTransferDataFlavors(); + + for (int i = 0; i < dfs.length; i++) { + + DataFlavor flavor = dfs[i]; + String str = null; + + if (DataFlavor.stringFlavor.equals(flavor)) { + try { + str = (String) t.getTransferData(flavor); + } catch (Exception e) { + e.printStackTrace(); + } + } + + list.add(str + ":" + flavor.getMimeType()); + } + + dtc.dropComplete(true); + validate(); + } + + public void dropActionChanged(DropTargetDragEvent dtde) { + } +} diff --git a/test/jdk/java/awt/dnd/DnDCursorCrashTest.java b/test/jdk/java/awt/dnd/DnDCursorCrashTest.java new file mode 100644 index 0000000000000..7b1f4483ef991 --- /dev/null +++ b/test/jdk/java/awt/dnd/DnDCursorCrashTest.java @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4343300 + * @summary tests that drag attempt doesn't cause crash when + * custom cursor is used + * @key headful + * @run main DnDCursorCrashTest + */ + +import java.awt.Button; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Panel; +import java.awt.Robot; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetContext; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.dnd.DropTargetListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +public class DnDCursorCrashTest { + static final Frame frame = new Frame(); + static final DragSourcePanel dragSourcePanel = new DragSourcePanel(); + static final DropTargetPanel dropTargetPanel = new DropTargetPanel(); + + public static void main(String[] args) throws Exception { + try { + EventQueue.invokeAndWait(() -> { + frame.setTitle("DnD Cursor Test Frame"); + frame.setLocation(200, 200); + frame.setLayout(new GridLayout(2, 1)); + frame.add(dragSourcePanel); + frame.add(dropTargetPanel); + frame.pack(); + frame.setVisible(true); + }); + + Robot robot = new Robot(); + robot.delay(1000); + robot.mouseMove(250, 250); + robot.keyPress(KeyEvent.VK_CONTROL); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + for (int y = 250; y < 350; y += 5) { + robot.mouseMove(250, y); + robot.delay(100); + } + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.keyRelease(KeyEvent.VK_CONTROL); + } finally { + if (frame != null) { + EventQueue.invokeAndWait(() -> frame.dispose()); + } + } + } +} + +class DragSourceButton extends Button implements Serializable, + Transferable, + DragGestureListener, + DragSourceListener { + private final DataFlavor dataflavor = + new DataFlavor(Button.class, "DragSourceButton"); + + public DragSourceButton() { + this("DragSourceButton"); + } + + public DragSourceButton(String str) { + super(str); + + DragSource ds = DragSource.getDefaultDragSource(); + ds.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY, + this); + } + + public void dragGestureRecognized(DragGestureEvent dge) { + dge.startDrag(new Cursor(Cursor.HAND_CURSOR), this, this); + } + + public void dragEnter(DragSourceDragEvent dsde) {} + + public void dragExit(DragSourceEvent dse) {} + + public void dragOver(DragSourceDragEvent dsde) {} + + public void dragDropEnd(DragSourceDropEvent dsde) {} + + public void dropActionChanged(DragSourceDragEvent dsde) {} + + public Object getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException, IOException { + + if (!isDataFlavorSupported(flavor)) { + throw new UnsupportedFlavorException(flavor); + } + + Object retObj; + + ByteArrayOutputStream baoStream = new ByteArrayOutputStream(); + ObjectOutputStream ooStream = new ObjectOutputStream(baoStream); + ooStream.writeObject(this); + + ByteArrayInputStream baiStream = new ByteArrayInputStream(baoStream.toByteArray()); + ObjectInputStream ois = new ObjectInputStream(baiStream); + try { + retObj = ois.readObject(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + throw new RuntimeException(e.toString()); + } + + return retObj; + } + + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[] { dataflavor }; + } + + public boolean isDataFlavorSupported(DataFlavor dflavor) { + return dataflavor.equals(dflavor); + } +} + +class DragSourcePanel extends Panel { + + final Dimension preferredDimension = new Dimension(200, 100); + + public DragSourcePanel() { + setLayout(new GridLayout(1, 1)); + add(new DragSourceButton()); + } + + public Dimension getPreferredSize() { + return preferredDimension; + } +} + +class DropTargetPanel extends Panel implements DropTargetListener { + + final Dimension preferredDimension = new Dimension(200, 100); + + public DropTargetPanel() { + setDropTarget(new DropTarget(this, this)); + } + + public Dimension getPreferredSize() { + return preferredDimension; + } + + public void dragEnter(DropTargetDragEvent dtde) {} + + public void dragExit(DropTargetEvent dte) {} + + public void dragOver(DropTargetDragEvent dtde) {} + + public void dropActionChanged(DropTargetDragEvent dtde) {} + + public void drop(DropTargetDropEvent dtde) { + DropTargetContext dtc = dtde.getDropTargetContext(); + + if ((dtde.getSourceActions() & DnDConstants.ACTION_COPY) != 0) { + dtde.acceptDrop(DnDConstants.ACTION_COPY); + } else { + dtde.rejectDrop(); + } + + DataFlavor[] dfs = dtde.getCurrentDataFlavors(); + Component comp = null; + + if (dfs != null && dfs.length >= 1) { + Transferable transfer = dtde.getTransferable(); + + try { + comp = (Component)transfer.getTransferData(dfs[0]); + } catch (Throwable e) { + e.printStackTrace(); + dtc.dropComplete(false); + } + } + dtc.dropComplete(true); + + add(comp); + } +} diff --git a/test/jdk/java/awt/dnd/DnDFileGroupDescriptor/DnDFileGroupDescriptor.html b/test/jdk/java/awt/dnd/DnDFileGroupDescriptor/DnDFileGroupDescriptor.html deleted file mode 100644 index 72a65bbe39fd8..0000000000000 --- a/test/jdk/java/awt/dnd/DnDFileGroupDescriptor/DnDFileGroupDescriptor.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - DnDToWordpadTest - - - -

    DnDFileGroupDescriptor
    Bug ID: 6242241

    - -

    See the dialog box (usually in upper left corner) for instructions

    - - - - diff --git a/test/jdk/java/awt/dnd/DnDFileGroupDescriptor/DnDFileGroupDescriptor.java b/test/jdk/java/awt/dnd/DnDFileGroupDescriptor/DnDFileGroupDescriptor.java index 06d4dc43b5587..51129f2640829 100644 --- a/test/jdk/java/awt/dnd/DnDFileGroupDescriptor/DnDFileGroupDescriptor.java +++ b/test/jdk/java/awt/dnd/DnDFileGroupDescriptor/DnDFileGroupDescriptor.java @@ -1,188 +1,80 @@ - /* - * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - /* - test - @bug 6242241 - @summary Tests basic DnD functionality in an applet - @requires (os.family == "windows") - @author Your Name: Alexey Utkin area=dnd - @run applet/manual=yesno DnDFileGroupDescriptor.html -*/ - -import java.applet.Applet; -import java.awt.*; - -public class DnDFileGroupDescriptor extends Applet { - public void init() { - setLayout(new BorderLayout()); + * Copyright (c) 2009, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Frame; +import java.awt.Panel; - String[] instructions = { - "The applet window contains a red field.", - "1. Start MS Outlook program. Find and open ", - " the mail form with attachments.", - "2. Select attachments from the mail and drag into a red field of applet.", - " When the mouse enters the field during the drag, the application ", - " should change the cursor form to OLE-copy and field color to yellow.", - "3. Release the mouse button (drop attachments) over the field.", - "", - "File paths in temporary folder should appear.", - "", - "You should be able to repeat this operation multiple times.", - "Please, select \"Pass\" just in case of success or \"Fail\" for another." - }; - Sysout.createDialogWithInstructions( instructions ); +/* + * @test + * @bug 6242241 + * @summary Tests TransferFlavor that supports DnD of MS Outlook attachments. + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DnDFileGroupDescriptor + */ + +public class DnDFileGroupDescriptor { + private static final String INSTRUCTIONS = """ + When the test starts, a RED panel appears. + 1. Start MS Outlook program. Find and open the mail form with attachments. + + 2. Select attachments from the mail and drag into a red field of applet. + When the mouse enters the field during the process of drag, the application + should change the cursor form to OLE-copy and field color to yellow. + + 3. Release the mouse button (drop attachments) over the field. + File paths in temporary folder should appear. + You should be able to repeat this operation multiple times. + + If the above is the case then press PASS, else FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testUI(DnDFileGroupDescriptor::createUI) + .build() + .awaitAndCheck(); } - public void start() { - Panel mainPanel; - Component dropTarget; - - mainPanel = new Panel(); + private static Frame createUI() { + Frame frame = new Frame("Test MS Outlook Mail Attachments DnD"); + Panel mainPanel = new Panel(); mainPanel.setLayout(new BorderLayout()); - mainPanel.setBackground(Color.blue); - dropTarget = new DnDTarget(Color.red, Color.yellow); - + Component dropTarget = new DnDTarget(Color.RED, Color.YELLOW); mainPanel.add(dropTarget, "Center"); - add(mainPanel); - setSize(200,200); + frame.add(mainPanel); + frame.setSize(400, 200); + frame.setAlwaysOnTop(true); + return frame; } } - -/**************************************************** - Standard Test Machinery - DO NOT modify anything below -- it's a standard - chunk of code whose purpose is to make user - interaction uniform, and thereby make it simpler - to read and understand someone else's test. - ****************************************************/ - -class Sysout - { - private static TestDialog dialog; - - public static void createDialogWithInstructions( String[] instructions ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - dialog.printInstructions( instructions ); - dialog.show(); - println( "Any messages for the tester will display here." ); - } - - public static void createDialog( ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - String[] defInstr = { "Instructions will appear here. ", "" } ; - dialog.printInstructions( defInstr ); - dialog.show(); - println( "Any messages for the tester will display here." ); - } - - - public static void printInstructions( String[] instructions ) - { - dialog.printInstructions( instructions ); - } - - - public static void println( String messageIn ) - { - dialog.displayMessage( messageIn ); - } - - }// Sysout class - -class TestDialog extends Dialog - { - - TextArea instructionsText; - TextArea messageText; - int maxStringLength = 80; - - //DO NOT call this directly, go through Sysout - public TestDialog( Frame frame, String name ) - { - super( frame, name ); - int scrollBoth = TextArea.SCROLLBARS_BOTH; - instructionsText = new TextArea( "", 15, maxStringLength, scrollBoth ); - add( "North", instructionsText ); - - messageText = new TextArea( "", 5, maxStringLength, scrollBoth ); - add("South", messageText); - - pack(); - - show(); - }// TestDialog() - - //DO NOT call this directly, go through Sysout - public void printInstructions( String[] instructions ) - { - //Clear out any current instructions - instructionsText.setText( "" ); - - //Go down array of instruction strings - - String printStr, remainingStr; - for( int i=0; i < instructions.length; i++ ) - { - //chop up each into pieces maxSringLength long - remainingStr = instructions[ i ]; - while( remainingStr.length() > 0 ) - { - //if longer than max then chop off first max chars to print - if( remainingStr.length() >= maxStringLength ) - { - //Try to chop on a word boundary - int posOfSpace = remainingStr. - lastIndexOf( ' ', maxStringLength - 1 ); - - if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1; - - printStr = remainingStr.substring( 0, posOfSpace + 1 ); - remainingStr = remainingStr.substring( posOfSpace + 1 ); - } - //else just print - else - { - printStr = remainingStr; - remainingStr = ""; - } - - instructionsText.append( printStr + "\n" ); - - }// while - - }// for - - }//printInstructions() - - //DO NOT call this directly, go through Sysout - public void displayMessage( String messageIn ) - { - messageText.append( messageIn + "\n" ); - } - - }// TestDialog class diff --git a/test/jdk/java/awt/dnd/DnDHTMLToOutlookTest/DnDHTMLToOutlookTest.java b/test/jdk/java/awt/dnd/DnDHTMLToOutlookTest/DnDHTMLToOutlookTest.java new file mode 100644 index 0000000000000..8a39ce537056e --- /dev/null +++ b/test/jdk/java/awt/dnd/DnDHTMLToOutlookTest/DnDHTMLToOutlookTest.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Frame; +import java.awt.Panel; + + +/* + * @test + * @bug 6392086 + * @summary Tests dnd to another screen + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DnDHTMLToOutlookTest + */ + +public class DnDHTMLToOutlookTest { + + private static final String INSTRUCTIONS = """ + The window contains a yellow button. Click on the button + to copy HTML from DnDSource.html file into the clipboard or drag + HTML context. Paste into or drop over the HTML capable editor in + external application such as Outlook, Word. + + When the mouse enters the editor, cursor should change to indicate + that copy operation is about to happen and then release the mouse + button. HTML text without tags should appear inside the document. + + You should be able to repeat this operation multiple times. + If the above is true Press PASS else FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(DnDHTMLToOutlookTest::createAndShowUI) + .build() + .awaitAndCheck(); + } + + private static Frame createAndShowUI() { + Frame frame = new Frame("DnDHTMLToOutlookTest"); + Panel mainPanel; + Component dragSource; + + mainPanel = new Panel(); + mainPanel.setLayout(new BorderLayout()); + + mainPanel.setBackground(Color.YELLOW); + dragSource = new DnDSource("Drag ME (HTML)!"); + + mainPanel.add(dragSource, BorderLayout.CENTER); + frame.add(mainPanel); + frame.setSize(200, 200); + return frame; + } +} diff --git a/test/jdk/javax/swing/JSlider/6742358/bug6742358.html b/test/jdk/java/awt/dnd/DnDHTMLToOutlookTest/DnDSource.html similarity index 80% rename from test/jdk/javax/swing/JSlider/6742358/bug6742358.html rename to test/jdk/java/awt/dnd/DnDHTMLToOutlookTest/DnDSource.html index 0407b0d3ee26f..0f1b9751decdb 100644 --- a/test/jdk/javax/swing/JSlider/6742358/bug6742358.html +++ b/test/jdk/java/awt/dnd/DnDHTMLToOutlookTest/DnDSource.html @@ -1,5 +1,5 @@ - - - -Check that all sliders look good. - - +

    DnDHTMLToOutlookTest
    HTML Drag & Paste problem

    +

    if you see the bold header above without HTML tags and without StartHTML as the first word, press PASS

    diff --git a/test/jdk/java/awt/dnd/DnDHTMLToOutlookTest/DnDSource.java b/test/jdk/java/awt/dnd/DnDHTMLToOutlookTest/DnDSource.java new file mode 100644 index 0000000000000..58f17e9415c50 --- /dev/null +++ b/test/jdk/java/awt/dnd/DnDHTMLToOutlookTest/DnDSource.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Toolkit; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.InvalidDnDOperationException; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; + +class DnDSource extends Button implements Transferable, + DragGestureListener, + DragSourceListener { + private DataFlavor m_df; + private transient int m_dropAction; + private ByteArrayInputStream m_data = null; + + DnDSource(String label) { + super(label); + setBackground(Color.yellow); + setForeground(Color.blue); + setSize(200, 120); + + try { + m_df = new DataFlavor("text/html; Class=" + InputStream.class.getName() + "; charset=UTF-8"); + } catch (Exception e) { + e.printStackTrace(); + } + + DragSource dragSource = new DragSource(); + dragSource.createDefaultDragGestureRecognizer( + this, + DnDConstants.ACTION_COPY_OR_MOVE, + this + ); + dragSource.addDragSourceListener(this); + + String dir = System.getProperty("test.src", "."); + + try { + m_data = new ByteArrayInputStream(Files.readAllBytes( + Paths.get(dir, "DnDSource.html"))); + m_data.mark(m_data.available()); + addActionListener( + new ActionListener(){ + public void actionPerformed(ActionEvent ae){ + Toolkit.getDefaultToolkit().getSystemClipboard() + .setContents( DnDSource.this, null); + } + } + ); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void dragGestureRecognized(DragGestureEvent dge) { + System.err.println("starting Drag"); + try { + dge.startDrag(null, this, this); + } catch (InvalidDnDOperationException e) { + e.printStackTrace(); + } + } + + public void dragEnter(DragSourceDragEvent dsde) { + System.err.println("[Source] dragEnter"); + } + + public void dragOver(DragSourceDragEvent dsde) { + System.err.println("[Source] dragOver"); + m_dropAction = dsde.getDropAction(); + System.out.println("m_dropAction = " + m_dropAction); + } + + public void dragExit(DragSourceEvent dsde) { + System.err.println("[Source] dragExit"); + } + + public void dragDropEnd(DragSourceDropEvent dsde) { + System.err.println("[Source] dragDropEnd"); + } + + public void dropActionChanged(DragSourceDragEvent dsde) { + System.err.println("[Source] dropActionChanged"); + m_dropAction = dsde.getDropAction(); + System.out.println("m_dropAction = " + m_dropAction); + } + + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[] {m_df}; + } + + public boolean isDataFlavorSupported(DataFlavor sdf) { + System.err.println("[Source] isDataFlavorSupported" + m_df.equals(sdf)); + return m_df.equals(sdf); + } + + public Object getTransferData(DataFlavor tdf) throws UnsupportedFlavorException { + if (!m_df.equals(tdf)) { + throw new UnsupportedFlavorException(tdf); + } + System.err.println("[Source] Ok"); + m_data.reset(); + return m_data; + } +} diff --git a/test/jdk/java/awt/dnd/DnDRemoveFocusOwnerCrashTest.java b/test/jdk/java/awt/dnd/DnDRemoveFocusOwnerCrashTest.java new file mode 100644 index 0000000000000..1fc7c2e82b89c --- /dev/null +++ b/test/jdk/java/awt/dnd/DnDRemoveFocusOwnerCrashTest.java @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4357905 + * @summary Tests that removal of the focus owner component during + * drop processing doesn't cause crash + * @key headful + * @run main DnDRemoveFocusOwnerCrashTest + */ + +import java.awt.Button; +import java.awt.Component; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Robot; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetContext; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.dnd.DropTargetListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.io.Serializable; + +public class DnDRemoveFocusOwnerCrashTest { + public static final int FRAME_ACTIVATION_TIMEOUT = 1000; + public static Frame frame; + public static Robot robot; + public static DragSourceButton dragSourceButton; + static volatile Point p; + + public static void main(String[] args) throws Exception { + try { + robot = new Robot(); + robot.setAutoWaitForIdle(true); + robot.delay(FRAME_ACTIVATION_TIMEOUT); + EventQueue.invokeAndWait(() -> { + frame = new Frame(); + dragSourceButton = new DragSourceButton(); + DropTargetPanel dropTargetPanel = + new DropTargetPanel(dragSourceButton); + frame.add(new Button("Test")); + frame.setTitle("Remove Focus Owner Test Frame"); + frame.setLocation(200, 200); + frame.add(dropTargetPanel); + frame.pack(); + frame.setVisible(true); + }); + + robot.waitForIdle(); + robot.delay(FRAME_ACTIVATION_TIMEOUT); + + EventQueue.invokeAndWait(() -> { + p = dragSourceButton.getLocationOnScreen(); + p.translate(10, 10); + }); + + robot.delay(FRAME_ACTIVATION_TIMEOUT); + robot.mouseMove(p.x, p.y); + robot.delay(FRAME_ACTIVATION_TIMEOUT); + robot.keyPress(KeyEvent.VK_CONTROL); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + for (int dy = 0; dy < 50; dy++) { + robot.mouseMove(p.x, p.y + dy); + robot.delay(10); + } + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.keyRelease(KeyEvent.VK_CONTROL); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + static class DragSourceButton extends Button implements Serializable, + Transferable, + DragGestureListener, + DragSourceListener { + + private static DataFlavor dataflavor; + + static { + try { + dataflavor = + new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType); + dataflavor.setHumanPresentableName("Local Object Flavor"); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + throw new ExceptionInInitializerError(); + } + } + + public DragSourceButton() { + this("DragSourceButton"); + } + + public DragSourceButton(String str) { + super(str); + + DragSource ds = DragSource.getDefaultDragSource(); + ds.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY, + this); + } + + public void dragGestureRecognized(DragGestureEvent dge) { + dge.startDrag(null, this, this); + } + + public void dragEnter(DragSourceDragEvent dsde) { + } + + public void dragExit(DragSourceEvent dse) { + } + + public void dragOver(DragSourceDragEvent dsde) { + } + + public void dragDropEnd(DragSourceDropEvent dsde) { + } + + public void dropActionChanged(DragSourceDragEvent dsde) { + } + + public Object getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException { + + if (!isDataFlavorSupported(flavor)) { + throw new UnsupportedFlavorException(flavor); + } + + return this; + } + + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[]{dataflavor}; + } + + public boolean isDataFlavorSupported(DataFlavor dflavor) { + return dataflavor.equals(dflavor); + } + } + + static class DropTargetPanel extends Panel implements DropTargetListener { + + public DropTargetPanel(DragSourceButton button) { + setLayout(new FlowLayout(FlowLayout.CENTER, 50, 50)); + add(button); + setDropTarget(new DropTarget(this, this)); + } + + public void dragEnter(DropTargetDragEvent dtde) { + } + + public void dragExit(DropTargetEvent dte) { + } + + public void dragOver(DropTargetDragEvent dtde) { + } + + public void dropActionChanged(DropTargetDragEvent dtde) { + } + + public void drop(DropTargetDropEvent dtde) { + removeAll(); + + DropTargetContext dtc = dtde.getDropTargetContext(); + + if ((dtde.getSourceActions() & DnDConstants.ACTION_COPY) != 0) { + dtde.acceptDrop(DnDConstants.ACTION_COPY); + } else { + dtde.rejectDrop(); + } + + DataFlavor[] dfs = dtde.getCurrentDataFlavors(); + Component comp = null; + + if (dfs != null && dfs.length >= 1) { + Transferable transfer = dtde.getTransferable(); + + try { + comp = (Component) transfer.getTransferData(dfs[0]); + } catch (Throwable e) { + e.printStackTrace(); + dtc.dropComplete(false); + } + } + dtc.dropComplete(true); + + add(comp); + validate(); + } + } +} diff --git a/test/jdk/java/awt/dnd/DnDToWordpadTest.java b/test/jdk/java/awt/dnd/DnDToWordpadTest.java new file mode 100644 index 0000000000000..181a6ee873a85 --- /dev/null +++ b/test/jdk/java/awt/dnd/DnDToWordpadTest.java @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6362095 + * @summary Tests basic DnD functionality to a wordpad + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DnDToWordpadTest + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Component; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Panel; +import java.awt.Toolkit; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.InvalidDnDOperationException; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; + +import javax.imageio.ImageIO; + +import static java.awt.image.BufferedImage.TYPE_INT_ARGB; + +public class DnDToWordpadTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + The test window contains a yellow button. Click on the button + to copy image into the clipboard or drag the image. + Paste or drop the image over Wordpad (when the mouse + enters the Wordpad during the drag, the application + should change the cursor to indicate that a copy operation is + about to happen; release the mouse button). + An image of a red rectangle should appear inside the document. + You should be able to repeat this operation multiple times. + Please, click "Pass" if above conditions are true, + otherwise click "Fail". + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(DnDToWordpadTest::createUI) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame f = new Frame("DnD To WordPad Test"); + Panel mainPanel; + Component dragSource; + + mainPanel = new Panel(); + mainPanel.setLayout(null); + + mainPanel.setBackground(Color.black); + try { + dragSource = new DnDSource("Drag ME!"); + mainPanel.add(dragSource); + f.add(mainPanel); + } catch (IOException e) { + e.printStackTrace(); + } + + f.setSize(200, 200); + return f; + } +} + +class DnDSource extends Button implements Transferable, + DragGestureListener, + DragSourceListener { + private DataFlavor m_df; + private transient int m_dropAction; + private Image m_img; + + DnDSource(String label) throws IOException { + super(label); + + setBackground(Color.yellow); + setForeground(Color.blue); + setSize(200, 120); + + m_df = DataFlavor.imageFlavor; + + DragSource dragSource = new DragSource(); + dragSource.createDefaultDragGestureRecognizer( + this, + DnDConstants.ACTION_COPY_OR_MOVE, + this + ); + dragSource.addDragSourceListener(this); + + // Create test gif image to drag + Path p = Path.of(System.getProperty("test.classes", ".")); + BufferedImage bImg = new BufferedImage(79, 109, TYPE_INT_ARGB); + Graphics2D cg = bImg.createGraphics(); + cg.setColor(Color.RED); + cg.fillRect(0, 0, 79, 109); + ImageIO.write(bImg, "png", new File(p + java.io.File.separator + + "DnDSource_Red.gif")); + + m_img = Toolkit.getDefaultToolkit() + .getImage(System.getProperty("test.classes", ".") + + java.io.File.separator + "DnDSource_Red.gif"); + + addActionListener( + ae -> Toolkit.getDefaultToolkit().getSystemClipboard().setContents( + (Transferable) DnDSource.this, + null + ) + ); + } + + public void paint(Graphics g) { + g.drawImage(m_img, 10, 10, null); + } + + /** + * a Drag gesture has been recognized + */ + + public void dragGestureRecognized(DragGestureEvent dge) { + System.err.println("starting Drag"); + try { + dge.startDrag(null, this, this); + } catch (InvalidDnDOperationException e) { + e.printStackTrace(); + } + } + + /** + * as the hotspot enters a platform dependent drop site + */ + + public void dragEnter(DragSourceDragEvent dsde) { + System.err.println("[Source] dragEnter"); + } + + /** + * as the hotspot moves over a platform dependent drop site + */ + + public void dragOver(DragSourceDragEvent dsde) { + System.err.println("[Source] dragOver"); + m_dropAction = dsde.getDropAction(); + System.out.println("m_dropAction = " + m_dropAction); + } + + /** + * as the operation changes + */ + + public void dragGestureChanged(DragSourceDragEvent dsde) { + System.err.println("[Source] dragGestureChanged"); + m_dropAction = dsde.getDropAction(); + System.out.println("m_dropAction = " + m_dropAction); + } + + /** + * as the hotspot exits a platform dependent drop site + */ + + public void dragExit(DragSourceEvent dsde) { + System.err.println("[Source] dragExit"); + } + + /** + * as the operation completes + */ + + public void dragDropEnd(DragSourceDropEvent dsde) { + System.err.println("[Source] dragDropEnd"); + } + + public void dropActionChanged(DragSourceDragEvent dsde) { + System.err.println("[Source] dropActionChanged"); + m_dropAction = dsde.getDropAction(); + System.out.println("m_dropAction = " + m_dropAction); + } + + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[]{m_df}; + } + + public boolean isDataFlavorSupported(DataFlavor sdf) { + System.err.println("[Source] isDataFlavorSupported" + m_df.equals(sdf)); + return m_df.equals(sdf); + } + + public Object getTransferData(DataFlavor tdf) throws UnsupportedFlavorException { + if (!m_df.equals(tdf)) { + throw new UnsupportedFlavorException(tdf); + } + System.err.println("[Source] Ok"); + return m_img; + } +} diff --git a/test/jdk/java/awt/dnd/DragExitBeforeDropTest.java b/test/jdk/java/awt/dnd/DragExitBeforeDropTest.java new file mode 100644 index 0000000000000..2ef778a18ba46 --- /dev/null +++ b/test/jdk/java/awt/dnd/DragExitBeforeDropTest.java @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Robot; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetContext; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.dnd.DropTargetListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/* + * @test + * @bug 4395290 + * @key headful + * @summary tests that dragExit() is not called before drop() + */ + +public class DragExitBeforeDropTest { + private static Frame frame; + private static final DragSourceButton dragSourceButton = new DragSourceButton(); + private static final DropTargetPanel dropTargetPanel = new DropTargetPanel(); + private static volatile Point srcPoint; + private static volatile Point dstPoint; + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + EventQueue.invokeAndWait(DragExitBeforeDropTest::createAndShowUI); + robot.waitForIdle(); + robot.delay(1000); + + EventQueue.invokeAndWait(() -> { + Point p = dragSourceButton.getLocationOnScreen(); + Dimension d = dragSourceButton.getSize(); + p.translate(d.width / 2, d.height / 2); + srcPoint = p; + + p = dropTargetPanel.getLocationOnScreen(); + d = dropTargetPanel.getSize(); + p.translate(d.width / 2, d.height / 2); + dstPoint = p; + }); + + robot.mouseMove(srcPoint.x, srcPoint.y); + robot.keyPress(KeyEvent.VK_CONTROL); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + for (; !srcPoint.equals(dstPoint); + srcPoint.translate(sign(dstPoint.x - srcPoint.x), + sign(dstPoint.y - srcPoint.y))) { + robot.mouseMove(srcPoint.x, srcPoint.y); + robot.delay(10); + } + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.keyRelease(KeyEvent.VK_CONTROL); + robot.waitForIdle(); + robot.delay(1000); + + if (!dropTargetPanel.getStatus()) { + throw new RuntimeException("The test failed: dragExit()" + + " is called before drop()"); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void createAndShowUI() { + frame = new Frame("DragExitBeforeDropTest"); + frame.setLayout(new GridLayout(2, 1)); + frame.add(dragSourceButton); + frame.add(dropTargetPanel); + frame.setLocationRelativeTo(null); + frame.setSize(300, 400); + frame.setVisible(true); + } + + public static int sign(int n) { + return Integer.compare(n, 0); + } + + private static class DragSourceButton extends Button implements Serializable, + Transferable, + DragGestureListener, + DragSourceListener { + private final DataFlavor dataflavor = + new DataFlavor(Button.class, "DragSourceButton"); + + public DragSourceButton() { + this("DragSourceButton"); + } + + public DragSourceButton(String str) { + super(str); + + DragSource ds = DragSource.getDefaultDragSource(); + ds.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY, + this); + } + + public void dragGestureRecognized(DragGestureEvent dge) { + dge.startDrag(null, this, this); + } + + public void dragEnter(DragSourceDragEvent dsde) {} + + public void dragExit(DragSourceEvent dse) {} + + public void dragOver(DragSourceDragEvent dsde) {} + + public void dragDropEnd(DragSourceDropEvent dsde) {} + + public void dropActionChanged(DragSourceDragEvent dsde) {} + + public Object getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException, IOException { + + if (!isDataFlavorSupported(flavor)) { + throw new UnsupportedFlavorException(flavor); + } + + Object retObj; + + ByteArrayOutputStream baoStream = new ByteArrayOutputStream(); + ObjectOutputStream ooStream = new ObjectOutputStream(baoStream); + ooStream.writeObject(this); + + ByteArrayInputStream baiStream = + new ByteArrayInputStream(baoStream.toByteArray()); + ObjectInputStream ois = new ObjectInputStream(baiStream); + try { + retObj = ois.readObject(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + throw new RuntimeException(e.toString()); + } + + return retObj; + } + + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[] { dataflavor }; + } + + public boolean isDataFlavorSupported(DataFlavor dflavor) { + return dataflavor.equals(dflavor); + } + } + + private static class DropTargetPanel extends Panel implements DropTargetListener { + + final Dimension preferredDimension = new Dimension(200, 100); + volatile boolean testPassed = true; + + public DropTargetPanel() { + setDropTarget(new DropTarget(this, this)); + } + + public boolean getStatus() { + return testPassed; + } + + public Dimension getPreferredSize() { + return preferredDimension; + } + + public void dragEnter(DropTargetDragEvent dtde) {} + + public void dragExit(DropTargetEvent dte) { + testPassed = false; + } + + public void dragOver(DropTargetDragEvent dtde) {} + + public void dropActionChanged(DropTargetDragEvent dtde) {} + + public void drop(DropTargetDropEvent dtde) { + DropTargetContext dtc = dtde.getDropTargetContext(); + + if ((dtde.getSourceActions() & DnDConstants.ACTION_COPY) != 0) { + dtde.acceptDrop(DnDConstants.ACTION_COPY); + } else { + dtde.rejectDrop(); + } + + DataFlavor[] dfs = dtde.getCurrentDataFlavors(); + Component comp = null; + + if(dfs != null && dfs.length >= 1) { + Transferable transfer = dtde.getTransferable(); + + try { + comp = (Component)transfer.getTransferData(dfs[0]); + } catch (Throwable e) { + e.printStackTrace(); + dtc.dropComplete(false); + } + } + dtc.dropComplete(true); + add(comp); + } + } +} + + + diff --git a/test/jdk/java/awt/dnd/DragSourceMotionListenerTest.java b/test/jdk/java/awt/dnd/DragSourceMotionListenerTest.java new file mode 100644 index 0000000000000..25bf7ef03fd1e --- /dev/null +++ b/test/jdk/java/awt/dnd/DragSourceMotionListenerTest.java @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.AWTEvent; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Robot; +import java.awt.Toolkit; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceAdapter; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetAdapter; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetListener; +import java.awt.event.AWTEventListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/* + * @test + * @key headful + * @bug 4422345 + * @summary tests that DragSourceMotionListeners work correctly and + DragSourceEvents position is correct + */ + +public class DragSourceMotionListenerTest implements AWTEventListener { + static class TestPanel extends Panel { + final Dimension preferredDimension = new Dimension(200, 200); + public Dimension getPreferredSize() { + return preferredDimension; + } + } + + private static Frame frame; + private static final Panel source = new TestPanel(); + private static final Panel target = new TestPanel(); + private static final DragSource ds = DragSource.getDefaultDragSource(); + private static volatile CountDownLatch mouseReleaseEvent; + + static volatile boolean passedTest1 = false; + static volatile boolean passedTest2 = false; + + private static final Point testPoint1 = new Point(); + private static final Point testPoint2 = new Point(); + private static volatile Point srcPoint; + private static volatile Point dstOutsidePoint; + private static volatile Point dstInsidePoint; + + private static final Transferable t = new StringSelection("TEXT"); + private static final DragGestureListener gestureListener = e -> e.startDrag(null, t); + + private static final DragSourceAdapter sourceAdapter = new DragSourceAdapter() { + public void dragMouseMoved(DragSourceDragEvent dsde) { + if (Math.abs(dsde.getX() - testPoint1.getX()) < 5) { + passedTest1 = true; + } + } + + public void dragDropEnd(DragSourceDropEvent dsde) { + if (Math.abs(dsde.getX() - testPoint2.getX()) < 5) { + passedTest2 = true; + } + } + }; + + private static final DropTargetListener targetAdapter = new DropTargetAdapter() { + public void drop(DropTargetDropEvent e) { + e.acceptDrop(DnDConstants.ACTION_COPY); + try { + final Transferable t = e.getTransferable(); + final String str = + (String) t.getTransferData(DataFlavor.stringFlavor); + e.dropComplete(true); + } catch (Exception ex) { + ex.printStackTrace(); + e.dropComplete(false); + } + } + }; + + private static final DropTarget dropTarget = new DropTarget(target, targetAdapter); + Component clickedComponent = null; + + private void createAndShowUI() { + frame = new Frame("DragSourceMotionListenerTest"); + ds.addDragSourceListener(sourceAdapter); + ds.addDragSourceMotionListener(sourceAdapter); + ds.createDefaultDragGestureRecognizer(source, DnDConstants.ACTION_COPY, gestureListener); + target.setDropTarget(dropTarget); + + frame.setLayout(new GridLayout(1, 2)); + + frame.add(source); + frame.add(target); + + Toolkit.getDefaultToolkit() + .addAWTEventListener(this, AWTEvent.MOUSE_EVENT_MASK); + frame.pack(); + frame.setVisible(true); + } + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + robot.setAutoDelay(10); + + DragSourceMotionListenerTest dsmObj = new DragSourceMotionListenerTest(); + EventQueue.invokeAndWait(dsmObj::createAndShowUI); + robot.waitForIdle(); + robot.delay(1000); + + EventQueue.invokeAndWait(() -> { + srcPoint = getPoint(source, 1); + + dstOutsidePoint = getPoint(frame, 3); + testPoint1.setLocation(dstOutsidePoint); + + dstInsidePoint = getPoint(target, 1); + testPoint2.setLocation(dstInsidePoint); + }); + robot.waitForIdle(); + + if (!dsmObj.pointInComponent(robot, srcPoint, source)) { + throw new RuntimeException("WARNING: Couldn't locate source panel."); + } + + if (!dsmObj.pointInComponent(robot, dstInsidePoint, target)) { + throw new RuntimeException("WARNING: Couldn't locate target panel."); + } + + robot.mouseMove(srcPoint.x, srcPoint.y); + robot.keyPress(KeyEvent.VK_CONTROL); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + for (; !srcPoint.equals(dstOutsidePoint); + srcPoint.translate(sign(dstOutsidePoint.x - srcPoint.x), + sign(dstOutsidePoint.y - srcPoint.y))) { + robot.mouseMove(srcPoint.x, srcPoint.y); + } + + for (int i = 0; i < 10; i++) { + robot.mouseMove(srcPoint.x, srcPoint.y++); + } + + for (;!srcPoint.equals(dstInsidePoint); + srcPoint.translate(sign(dstInsidePoint.x - srcPoint.x), + sign(dstInsidePoint.y - srcPoint.y))) { + robot.mouseMove(srcPoint.x, srcPoint.y); + } + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.keyRelease(KeyEvent.VK_CONTROL); + robot.waitForIdle(); + robot.delay(1000); + + if (!passedTest1) { + throw new RuntimeException("Failed first test."); + } + + if (!passedTest2) { + throw new RuntimeException("Failed second test."); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static Point getPoint(Container container, int multiple) { + Point p = container.getLocationOnScreen(); + Dimension d = container.getSize(); + p.translate(multiple * d.width / 2, d.height / 2); + return p; + } + + public static int sign(int n) { + return Integer.compare(n, 0); + } + + public void eventDispatched(AWTEvent e) { + if (e.getID() == MouseEvent.MOUSE_RELEASED) { + clickedComponent = (Component)e.getSource(); + mouseReleaseEvent.countDown(); + } + } + + boolean pointInComponent(Robot robot, Point p, Component comp) throws Exception { + robot.waitForIdle(); + clickedComponent = null; + mouseReleaseEvent = new CountDownLatch(1); + robot.mouseMove(p.x, p.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + if (!mouseReleaseEvent.await(2, TimeUnit.SECONDS)) { + throw new RuntimeException("Mouse Release Event not received"); + } + + Component c = clickedComponent; + while (c != null && c != comp) { + c = c.getParent(); + } + return c == comp; + } +} diff --git a/test/jdk/java/awt/dnd/DragThresholdTest.java b/test/jdk/java/awt/dnd/DragThresholdTest.java new file mode 100644 index 0000000000000..bcb3bbf91c2ce --- /dev/null +++ b/test/jdk/java/awt/dnd/DragThresholdTest.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Robot; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; + +/* + @test + @key headful + @bug 4415175 + @summary tests DragSource.getDragThreshold() and + that the AWT default drag gesture recognizers + honor the drag gesture motion threshold +*/ + +public class DragThresholdTest { + private static Frame frame; + private static Panel panel; + private static MouseEvent lastMouseEvent; + private static volatile boolean failed; + private static volatile Point startPoint; + private static volatile Point endPoint; + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + + EventQueue.invokeAndWait(DragThresholdTest::createAndShowDnD); + robot.waitForIdle(); + robot.delay(1000); + + EventQueue.invokeAndWait(() -> { + Point p = panel.getLocationOnScreen(); + p.translate(50, 50); + startPoint = p; + endPoint = new Point(p.x + 2 * DragSource.getDragThreshold(), + p.y + 2 * DragSource.getDragThreshold()); + }); + + robot.mouseMove(startPoint.x, startPoint.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + for (Point p = new Point(startPoint); !p.equals(endPoint); + p.translate(sign(endPoint.x - p.x), + sign(endPoint.y - p.y))) { + robot.mouseMove(p.x, p.y); + robot.delay(100); + } + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(200); + + if (failed) { + throw new RuntimeException("drag gesture recognized too early"); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void createAndShowDnD() { + frame = new Frame("DragThresholdTest"); + panel = new Panel(); + // Mouse motion listener mml is added to the panel first. + // We rely on it that this listener will be called first. + panel.addMouseMotionListener(new MouseMotionAdapter() { + public void mouseDragged(MouseEvent evt) { + lastMouseEvent = evt; + System.out.println(evt); + } + }); + frame.add(panel); + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + + DragGestureListener dgl = dge -> { + Point dragOrigin = dge.getDragOrigin(); + int diffx = Math.abs(dragOrigin.x - lastMouseEvent.getX()); + int diffy = Math.abs(dragOrigin.y - lastMouseEvent.getY()); + System.out.println("dragGestureRecognized(): " + + " diffx=" + diffx + " diffy=" + diffy + + " DragSource.getDragThreshold()=" + + DragSource.getDragThreshold()); + if (diffx <= DragSource.getDragThreshold() && + diffy <= DragSource.getDragThreshold()) { + failed = true; + System.out.println("drag gesture recognized too early!"); + } + }; + + // Default drag gesture recognizer is a mouse motion listener. + // It is added to the panel second. + new DragSource().createDefaultDragGestureRecognizer( + panel, + DnDConstants.ACTION_COPY_OR_MOVE, dgl); + frame.setVisible(true); + } + + private static int sign(int n) { + return Integer.compare(n, 0); + } +} diff --git a/test/jdk/java/awt/dnd/DragToAnotherScreenTest.java b/test/jdk/java/awt/dnd/DragToAnotherScreenTest.java new file mode 100644 index 0000000000000..89f18061845fe --- /dev/null +++ b/test/jdk/java/awt/dnd/DragToAnotherScreenTest.java @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Frame; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Label; +import java.awt.Toolkit; +import java.awt.Window; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetAdapter; +import java.awt.dnd.DropTargetDropEvent; +import java.util.List; + +import javax.swing.JOptionPane; + +/* + * @test + * @bug 6179157 + * @key multimon + * @summary Tests dnd to another screen + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DragToAnotherScreenTest + */ + +public class DragToAnotherScreenTest { + private static Label label0; + private static Label label1; + private static final int HGAP = 20; + + private static final String INSTRUCTIONS = """ + The following test is applicable for Single as well + as Multi-monitor screens. + + It is a semi-automated test, the test will prompt + the user whether the drag and drop action was successful or not + and automatically PASS/FAIL the test. + + If on multi-monitor screens then please position + the drag and drop windows on different screens. + + If you can not move the mouse from the frame "Drag Source" + to the frame "Drop Target" press PASS, + else proceed to the next step. + + Drag the label "Drag me" and drop it on the + label "Drop on me". + + If you can not drag to the second label (for example + if you can not drag across screens) press FAIL. + + After the drag and drop action, the test displays + Success/Failure msg in JOptionPane. + Click on OK button and the test is configured to + automatically PASS/FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(DragToAnotherScreenTest::createAndShowUI) + .positionTestUI(DragToAnotherScreenTest::positionMultiTestUI) + .logArea(10) + .build() + .awaitAndCheck(); + } + + private static List createAndShowUI() { + PassFailJFrame.log("----- System Configuration ----"); + PassFailJFrame.log("Toolkit:" + Toolkit.getDefaultToolkit() + .getClass() + .getName()); + + GraphicsDevice[] gd = GraphicsEnvironment.getLocalGraphicsEnvironment() + .getScreenDevices(); + if (gd.length == 1) { + PassFailJFrame.log("Single Monitor"); + } else { + PassFailJFrame.log("Multi-Monitor"); + } + PassFailJFrame.log("--------------"); + PassFailJFrame.log("Test logs:\n"); + Frame frame0 = new Frame("Drag Source", gd[0].getDefaultConfiguration()); + frame0.setSize(300, 300); + label0 = new Label("Drag me"); + frame0.add(label0); + + Frame frame1 = new Frame("Drop Target", gd[(gd.length > 1 ? 1 : 0)].getDefaultConfiguration()); + frame1.setSize(300, 300); + label1 = new Label("Drop on me"); + frame1.add(label1); + + DragGestureListener dragGestureListener = dge -> dge.startDrag(null, new StringSelection(label0.getText()), null); + new DragSource().createDefaultDragGestureRecognizer(label0, + DnDConstants.ACTION_COPY, dragGestureListener); + + DropTargetAdapter dropTargetAdapter = new DropTargetAdapter() { + public void drop(DropTargetDropEvent dtde) { + Transferable t = dtde.getTransferable(); + if (t.isDataFlavorSupported(DataFlavor.stringFlavor)) { + dtde.acceptDrop(DnDConstants.ACTION_COPY); + try { + String str = (String) t.getTransferData(DataFlavor.stringFlavor); + label1.setText(str); + JOptionPane.showMessageDialog(frame0, + "getTransferData was successful", + "Test Passed", JOptionPane.PLAIN_MESSAGE); + } catch (Exception e) { + dtde.dropComplete(false); + e.printStackTrace(); + PassFailJFrame.log("getTransferData() Failed"); + JOptionPane.showMessageDialog(frame0, + "getTransferData() Failed", + "Test Failed", JOptionPane.ERROR_MESSAGE); + PassFailJFrame.forceFail("getTransferData() Failed"); + } + dtde.dropComplete(true); + } else { + dtde.rejectDrop(); + PassFailJFrame.log("stringFlavor is not supported by Transferable"); + JOptionPane.showMessageDialog(frame0, + "stringFlavor is not supported by Transferable", + "Test Failed", JOptionPane.ERROR_MESSAGE); + PassFailJFrame.forceFail("stringFlavor is not supported by Transferable"); + } + } + }; + new DropTarget(label1, dropTargetAdapter); + return List.of(frame0, frame1); + } + + private static void positionMultiTestUI(List windows, + PassFailJFrame.InstructionUI instructionUI) { + int x = instructionUI.getLocation().x + instructionUI.getSize().width + HGAP; + for (Window w : windows) { + w.setLocation(x, instructionUI.getLocation().y); + x += w.getWidth() + HGAP; + } + } +} diff --git a/test/jdk/java/awt/dnd/ImageDecoratedDnD/ImageDecoratedDnD.html b/test/jdk/java/awt/dnd/ImageDecoratedDnD/ImageDecoratedDnD.html deleted file mode 100644 index e261df0d6cab5..0000000000000 --- a/test/jdk/java/awt/dnd/ImageDecoratedDnD/ImageDecoratedDnD.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - ImageDecoratedDnD - - - -

    ImageDecoratedDnD
    Bug ID: 4874070

    - -

    See the dialog box (usually in upper left corner) for instructions

    - - - - diff --git a/test/jdk/java/awt/dnd/ImageDecoratedDnD/ImageDecoratedDnD.java b/test/jdk/java/awt/dnd/ImageDecoratedDnD/ImageDecoratedDnD.java index c44b5417b97c3..60fd8809c3db3 100644 --- a/test/jdk/java/awt/dnd/ImageDecoratedDnD/ImageDecoratedDnD.java +++ b/test/jdk/java/awt/dnd/ImageDecoratedDnD/ImageDecoratedDnD.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,177 +21,87 @@ * questions. */ -/* - test %W% %E% - @bug 4874070 - @summary Tests basic DnD functionality - @author Your Name: Alexey Utkin area=dnd - @run applet/manual=yesno ImageDecoratedDnD.html -*/ - -import java.applet.Applet; -import java.awt.*; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Frame; +import java.awt.Panel; import java.awt.dnd.DragSource; +/* + * @test + * @bug 4874070 + * @summary Tests CTRL + DnD functionality + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ImageDecoratedDnD + */ +public class ImageDecoratedDnD { + private static final String INSTRUCTIONS = """ + When test runs a Frame which contains a yellow button labeled + "Drag ME!" and a RED Panel will appear. + + 1. Click on the button and drag it to the red panel while holding + the "CTRL" key on the keyboard. + + 2. When the mouse enters the red panel during the drag, the panel + should turn yellow. + + On systems that supports pictured drag, the image under the + drag-cursor should appear. + "Image under drag-cursor" is a translucent blue rectangle + red + circle and includes an anchor that is shifted from top-left + corner of the picture to inside the picture to 10pt + in both dimensions. + + On Windows, the image under the cursor would be visible ONLY over + drop targets with activated extended D'n'D support. + It means the image may not be displayed when dragging over some + windows, this is not an error. + The image should be displayed when dragging over the red/yellow panel. + + 3. Release the mouse button. + + The panel should turn red again and a yellow button labeled + "Drag ME!" should appear inside the panel. You should be able + to repeat this operation multiple times. + + If above is true press PASS, else press FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(38) + .testUI(ImageDecoratedDnD::createUI) + .build() + .awaitAndCheck(); + } -public class ImageDecoratedDnD extends Applet { - //Declare things used in the test, like buttons and labels here - - public void init() { - //Create instructions for the user here, as well as set up - // the environment -- set the layout manager, add buttons, - // etc. - this.setLayout(new BorderLayout()); - - String[] instructions = - { - "A Frame, which contains a yellow button labeled \"Drag ME!\" and ", - "a red panel, will appear below. ", - "1. Click on the button and drag to the red panel. ", - "2. When the mouse enters the red panel during the drag, the panel ", - "should turn yellow. On the systems that supports pictured drag, ", - "the image under the drag-cursor should appear (ancor is shifted ", - "from top-left corner of the picture inside the picture to 10pt in both dimensions ). ", - "In WIN32 systems the image under cursor would be visible ONLY over ", - "the drop targets with activated extended OLE D\'n\'D support (that are ", - "the desktop and IE ).", - "3. Release the mouse button.", - "The panel should turn red again and a yellow button labeled ", - "\"Drag ME!\" should appear inside the panel. You should be able ", - "to repeat this operation multiple times." - }; - Sysout.createDialogWithInstructions(instructions); - - }//End init() - - public void start() { - Frame f = new Frame("Use keyboard for DnD change"); + public static Frame createUI() { + Frame frame = new Frame("Ctrl + Drag - Image DnD test"); Panel mainPanel; Component dragSource, dropTarget; - f.setBounds(0, 400, 200, 200); - f.setLayout(new BorderLayout()); + frame.setSize(400, 400); + frame.setLayout(new BorderLayout()); mainPanel = new Panel(); mainPanel.setLayout(new BorderLayout()); - mainPanel.setBackground(Color.blue); + mainPanel.setBackground(Color.BLUE); - dropTarget = new DnDTarget(Color.red, Color.yellow); - dragSource = new DnDSource("Drag ME! (" + (DragSource.isDragImageSupported()?"with ":"without") + " image)" ); + dropTarget = new DnDTarget(Color.RED, Color.YELLOW); + dragSource = new DnDSource("Drag ME! (" + + (DragSource.isDragImageSupported() ? "with " : "without") + " image)"); mainPanel.add(dragSource, "North"); mainPanel.add(dropTarget, "Center"); - f.add(mainPanel, BorderLayout.CENTER); - - f.setVisible(true); - }// start() -}// class DnDAcceptanceTest - - -/** - * ************************************************* - * Standard Test Machinery - * DO NOT modify anything below -- it's a standard - * chunk of code whose purpose is to make user - * interaction uniform, and thereby make it simpler - * to read and understand someone else's test. - * ************************************************** - */ -class Sysout { - private static TestDialog dialog; - - public static void createDialogWithInstructions(String[] instructions) { - dialog = new TestDialog(new Frame(), "Instructions"); - dialog.printInstructions(instructions); - dialog.show(); - println("Any messages for the tester will display here."); - } - - public static void createDialog() { - dialog = new TestDialog(new Frame(), "Instructions"); - String[] defInstr = {"Instructions will appear here. ", ""}; - dialog.printInstructions(defInstr); - dialog.show(); - println("Any messages for the tester will display here."); + frame.add(mainPanel, BorderLayout.CENTER); + frame.setAlwaysOnTop(true); + return frame; } - - - public static void printInstructions(String[] instructions) { - dialog.printInstructions(instructions); - } - - - public static void println(String messageIn) { - dialog.displayMessage(messageIn); - } - -}// Sysout class - - -class TestDialog extends Dialog { - - TextArea instructionsText; - TextArea messageText; - int maxStringLength = 80; - - //DO NOT call this directly, go through Sysout - public TestDialog(Frame frame, String name) { - super(frame, name); - int scrollBoth = TextArea.SCROLLBARS_BOTH; - instructionsText = new TextArea("", 15, maxStringLength, scrollBoth); - add("North", instructionsText); - - messageText = new TextArea("", 5, maxStringLength, scrollBoth); - add("South", messageText); - - pack(); - - show(); - }// TestDialog() - - //DO NOT call this directly, go through Sysout - public void printInstructions(String[] instructions) { - //Clear out any current instructions - instructionsText.setText(""); - - //Go down array of instruction strings - - String printStr, remainingStr; - for (int i = 0; i < instructions.length; i++) { - //chop up each into pieces maxSringLength long - remainingStr = instructions[i]; - while (remainingStr.length() > 0) { - //if longer than max then chop off first max chars to print - if (remainingStr.length() >= maxStringLength) { - //Try to chop on a word boundary - int posOfSpace = remainingStr. - lastIndexOf(' ', maxStringLength - 1); - - if (posOfSpace <= 0) posOfSpace = maxStringLength - 1; - - printStr = remainingStr.substring(0, posOfSpace + 1); - remainingStr = remainingStr.substring(posOfSpace + 1); - } - //else just print - else { - printStr = remainingStr; - remainingStr = ""; - } - - instructionsText.append(printStr + "\n"); - - }// while - - }// for - - }//printInstructions() - - //DO NOT call this directly, go through Sysout - public void displayMessage(String messageIn) { - messageText.append(messageIn + "\n"); - } - -}// TestDialog class - +} diff --git a/test/jdk/java/awt/dnd/NonAsciiFilenames.java b/test/jdk/java/awt/dnd/NonAsciiFilenames.java new file mode 100644 index 0000000000000..b508350c05ef3 --- /dev/null +++ b/test/jdk/java/awt/dnd/NonAsciiFilenames.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4187490 + * @summary Verify that Non-ASCII file names can be dragged and dropped + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual NonAsciiFilenames + */ + +import java.awt.Color; +import java.awt.datatransfer.DataFlavor; +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.AbstractList; + +import javax.swing.JFrame; +import javax.swing.JLabel; + +public class NonAsciiFilenames { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This test must be run on an OS which does not use ISO 8859-1 + as its default encoding. + + Open a native file browsing application, such as Windows + Explorer. Try to find a file whose name uses non-ISO 8859-1 + characters. Create a file and name it such that it contains + non-ISO 8859-1 characters (For ex. é, à, ö, €, ¥). Drag + the file from the native application and drop it on the test + Frame. If the file name appears normally, then the test passes. + If boxes or question marks appear for characters, or if you see + the word "Error", then the test fails. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(NonAsciiFilenames::createUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createUI() { + JFrame frame = new JFrame(); + frame.setTitle("DropLabel test"); + frame.getContentPane().add(new DropLabel("Drop here")); + frame.setSize(300, 100); + return frame; + } +} + +class DropLabel extends JLabel implements DropTargetListener { + public DropLabel(String s) { + setText(s); + new DropTarget(this, DnDConstants.ACTION_COPY, this, true); + showDrop(false); + } + + private void showDrop(boolean b) { + setForeground(b ? Color.white : Color.black); + } + + /** + * Configure to desired flavor of dropped data. + */ + private DataFlavor getDesiredFlavor() { + return DataFlavor.javaFileListFlavor; + } + + /** + * Check to make sure that the contains the expected object types. + */ + private void checkDroppedData(Object data) { + System.out.println("Got data: " + data.getClass().getName()); + if (data instanceof AbstractList) { + AbstractList files = (AbstractList) data; + if (((File) files.get(0)).isFile()) + setText(((File) files.get(0)).toString()); + else + setText("Error: not valid file: " + + ((File) files.get(0)).toString()); + } else { + System.out.println("Error: wrong type of data dropped"); + } + } + + private boolean isDragOk(DropTargetDragEvent e) { + boolean canDrop = false; + try { + canDrop = e.isDataFlavorSupported(getDesiredFlavor()); + } catch (Exception ex) { + } + + if (canDrop) + e.acceptDrag(DnDConstants.ACTION_COPY); + else + e.rejectDrag(); + showDrop(canDrop); + return canDrop; + } + + public void dragEnter(DropTargetDragEvent e) { + isDragOk(e); + } + + + public void dragOver(DropTargetDragEvent e) { + isDragOk(e); + } + + public void dropActionChanged(DropTargetDragEvent e) { + isDragOk(e); + } + + public void dragExit(DropTargetEvent e) { + showDrop(false); + } + + public void drop(DropTargetDropEvent e) { + try { + e.acceptDrop(DnDConstants.ACTION_COPY); + checkDroppedData(e.getTransferable(). + getTransferData(getDesiredFlavor())); + } catch (Exception err) { + } + e.dropComplete(true); + showDrop(false); + } +} diff --git a/test/jdk/java/awt/dnd/RejectDragDropActionTest.java b/test/jdk/java/awt/dnd/RejectDragDropActionTest.java index f8d53ac45bfa6..9ff3c00ead006 100644 --- a/test/jdk/java/awt/dnd/RejectDragDropActionTest.java +++ b/test/jdk/java/awt/dnd/RejectDragDropActionTest.java @@ -27,10 +27,9 @@ @summary tests that DropTargetDragEvent.getDropAction() returns correct value after DropTargetDragEvent.rejectDrag() @key headful - @run main RejectDragDropActionTest + @run main/timeout=300 RejectDragDropActionTest */ -import java.awt.AWTException; import java.awt.EventQueue; import java.awt.Frame; import java.awt.Point; @@ -46,14 +45,13 @@ import java.awt.dnd.DropTargetDropEvent; import java.awt.dnd.DropTargetListener; import java.awt.event.InputEvent; -import java.lang.reflect.InvocationTargetException; public class RejectDragDropActionTest { private static volatile boolean incorrectActionDetected = false; - private static final int FRAME_ACTIVATION_TIMEOUT = 3000; + private static final int DELAY_TIME = 500; private static Frame frame; private static DragSource ds; @@ -74,21 +72,23 @@ public void drop(DropTargetDropEvent dtde) { }; private final DropTarget dt = new DropTarget(frame, dtl); - public static void main(String[] args) throws InterruptedException, - InvocationTargetException, AWTException { + public static void main(String[] args) throws Exception { EventQueue.invokeAndWait(() -> { frame = new Frame("RejectDragDropActionTest"); ds = DragSource.getDefaultDragSource(); dgl = dge -> dge.startDrag(null, new StringSelection("OOKK")); - dgr = ds.createDefaultDragGestureRecognizer(frame, DnDConstants.ACTION_COPY, dgl); - frame.setBounds(100, 100, 200, 200); + dgr = ds.createDefaultDragGestureRecognizer(frame, + DnDConstants.ACTION_COPY, dgl); + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); frame.setVisible(true); }); try { Robot robot = new Robot(); + robot.setAutoWaitForIdle(true); robot.waitForIdle(); - robot.delay(FRAME_ACTIVATION_TIMEOUT); + robot.delay(DELAY_TIME); Point startPoint = frame.getLocationOnScreen(); Point endPoint = new Point(startPoint); @@ -97,11 +97,11 @@ public static void main(String[] args) throws InterruptedException, robot.mouseMove(startPoint.x, startPoint.y); robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); - for (Point p = new Point(startPoint); !p.equals(endPoint); + for (Point p = new Point(startPoint); + !p.equals(endPoint) && !incorrectActionDetected; p.translate(sign(endPoint.x - p.x), - sign(endPoint.y - p.y))) { + sign(endPoint.y - p.y))) { robot.mouseMove(p.x, p.y); - robot.delay(50); } robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); diff --git a/test/jdk/java/awt/dnd/RejectDragTest.java b/test/jdk/java/awt/dnd/RejectDragTest.java new file mode 100644 index 0000000000000..c65612436bc38 --- /dev/null +++ b/test/jdk/java/awt/dnd/RejectDragTest.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Robot; +import java.awt.datatransfer.StringSelection; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceAdapter; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetAdapter; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.event.InputEvent; + +/* + * @test + * @key headful + * @bug 4407521 + * @summary Tests that DragSourceListener.dragEnter() and + DragSourceListener.dragOver() are not called after + drag rejecting, but DragSourceListener.dragExit() is. + */ + +public class RejectDragTest { + private static Frame frame; + private static Robot robot; + private static volatile boolean dragEnterCalled; + private static volatile boolean dragOverCalled; + private static volatile boolean dragExitCalledAtFirst; + private static volatile Point startPoint; + private static volatile Point endPoint; + + public static void main(String[] args) throws Exception { + try { + robot = new Robot(); + + EventQueue.invokeAndWait(RejectDragTest::createAndShowUI); + robot.waitForIdle(); + robot.delay(1000); + + EventQueue.invokeAndWait(RejectDragTest::addDnDListeners); + robot.waitForIdle(); + + testDnD(); + robot.waitForIdle(); + robot.delay(200); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void addDnDListeners() { + final DragSourceListener dragSourceListener = new DragSourceAdapter() { + private boolean first = true; + + public void dragEnter(DragSourceDragEvent dsde) { + first = false; + dragEnterCalled = true; + } + + public void dragExit(DragSourceEvent dse) { + if (first) { + dragExitCalledAtFirst = true; + first = false; + } + } + + public void dragDropEnd(DragSourceDropEvent dsde) { + first = false; + } + + public void dragOver(DragSourceDragEvent dsde) { + first = false; + dragOverCalled = true; + } + + public void dropActionChanged(DragSourceDragEvent dsde) { + first = false; + } + }; + + DragGestureListener dragGestureListener = + dge -> dge.startDrag(null, new StringSelection("OKAY"), + dragSourceListener); + new DragSource().createDefaultDragGestureRecognizer(frame, + DnDConstants.ACTION_COPY, + dragGestureListener); + + DropTargetAdapter dropTargetListener = new DropTargetAdapter() { + public void dragEnter(DropTargetDragEvent dtde) { + dtde.rejectDrag(); + } + + public void drop(DropTargetDropEvent dtde) { + dtde.rejectDrop(); + } + }; + + new DropTarget(frame, dropTargetListener); + } + + private static void createAndShowUI() { + frame = new Frame("RejectDragTest"); + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private static void testDnD() throws Exception { + EventQueue.invokeAndWait(() -> { + Point start = frame.getLocationOnScreen(); + start.translate(50, 50); + startPoint = start; + + Point end = new Point(start); + end.translate(150, 150); + endPoint = end; + }); + + robot.mouseMove(startPoint.x, startPoint.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + for (Point p = new Point(startPoint); !p.equals(endPoint); + p.translate(sign(endPoint.x - p.x), + sign(endPoint.y - p.y))) { + robot.mouseMove(p.x, p.y); + robot.delay(30); + } + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + + if (dragEnterCalled || dragOverCalled) { + throw new RuntimeException("Test failed: " + + (dragEnterCalled ? "DragSourceListener.dragEnter() was called; " : "") + + (dragOverCalled ? "DragSourceListener.dragOver() was called; " : "") + + (!dragExitCalledAtFirst ? "DragSourceListener.dragExit() was not " + + "called immediately after rejectDrag() " : "")); + } + } + + public static int sign(int n) { + return Integer.compare(n, 0); + } +} diff --git a/test/jdk/java/awt/dnd/WinMoveFileToShellTest.java b/test/jdk/java/awt/dnd/WinMoveFileToShellTest.java new file mode 100644 index 0000000000000..5ae7dd3890d71 --- /dev/null +++ b/test/jdk/java/awt/dnd/WinMoveFileToShellTest.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Frame; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceAdapter; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceListener; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/* + * @test + * @bug 4414739 + * @requires (os.family == "windows") + * @summary verifies that getDropSuccess() returns correct value for moving + a file from a Java drag source to the Windows shell + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual WinMoveFileToShellTest + */ + +public class WinMoveFileToShellTest { + private static final String INSTRUCTIONS = """ + Drag from the frame titled "Drag Frame" and drop on to Windows Desktop. + After Drag and Drop, check for "Drop Success" status in the log area. + If "Drop Success" is true press PASS else FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(WinMoveFileToShellTest::createAndShowUI) + .logArea(5) + .build() + .awaitAndCheck(); + } + + private static Frame createAndShowUI() { + Frame frame = new Frame("Drag Frame"); + final DragSourceListener dsl = new DragSourceAdapter() { + public void dragDropEnd(DragSourceDropEvent e) { + PassFailJFrame.log("Drop Success: " + e.getDropSuccess()); + } + }; + + DragGestureListener dgl = dge -> { + File file = new File(System.getProperty("test.classes", ".") + + File.separator + "move.me"); + try { + file.createNewFile(); + } catch (IOException exc) { + exc.printStackTrace(); + } + ArrayList list = new ArrayList<>(); + list.add(file); + dge.startDrag(null, new FileListSelection(list), dsl); + }; + + new DragSource().createDefaultDragGestureRecognizer(frame, + DnDConstants.ACTION_MOVE, dgl); + frame.setSize(200, 100); + return frame; + } + + private static class FileListSelection implements Transferable { + private static final int FL = 0; + + private static final DataFlavor[] flavors = + new DataFlavor[] { DataFlavor.javaFileListFlavor }; + + + private List data; + + public FileListSelection(List data) { + this.data = data; + } + + public DataFlavor[] getTransferDataFlavors() { + return flavors.clone(); + } + + public boolean isDataFlavorSupported(DataFlavor flavor) { + for (DataFlavor dataFlavor : flavors) { + if (flavor.equals(dataFlavor)) { + return true; + } + } + return false; + } + + public Object getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException, IOException + { + if (flavor.equals(flavors[FL])) { + return data; + } else { + throw new UnsupportedFlavorException(flavor); + } + } + } +} diff --git a/test/jdk/java/awt/event/ClickEventsTest.java b/test/jdk/java/awt/event/ClickEventsTest.java new file mode 100644 index 0000000000000..936100ac950f6 --- /dev/null +++ b/test/jdk/java/awt/event/ClickEventsTest.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + @test + @bug 4087762 + @summary Sometimes click events are missing when you click the color components alternately. + @key headful + @library /test/jdk/java/awt/regtesthelpers + @build Util + @run main ClickEventsTest +*/ + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Robot; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import test.java.awt.regtesthelpers.Util; + +public class ClickEventsTest { + static Frame frame; + static ColorComponent redComponent; + static ColorComponent blueComponent; + static Robot robot; + + public static void main(String[] args) throws Exception { + try { + EventQueue.invokeAndWait(ClickEventsTest::createAndShowGUI); + test(); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void test() throws Exception { + robot = new Robot(); + robot.waitForIdle(); + robot.delay(500); + + for (int i = 0; i < 10; i++) { + redComponent.clickAndCheck(); + blueComponent.clickAndCheck(); + } + } + + private static void createAndShowGUI() { + frame = new Frame("ClickEventsTest"); + redComponent = new ColorComponent(Color.RED); + blueComponent = new ColorComponent(Color.BLUE); + + frame.add("North", redComponent); + frame.add("South", blueComponent); + + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + static class ColorComponent extends Component { + public Color myColor; + + private final CyclicBarrier barrier = new CyclicBarrier(2); + + private final MouseAdapter mouseAdapter = new MouseAdapter() { + public void mouseClicked(MouseEvent event) { + System.out.println(myColor + " area clicked"); + try { + barrier.await(1, TimeUnit.SECONDS); + } catch (InterruptedException | BrokenBarrierException | TimeoutException e) { + throw new RuntimeException(e); + } + } + }; + + public ColorComponent(Color c) { + myColor = c; + addMouseListener(mouseAdapter); + } + + public Dimension getPreferredSize() { + return new Dimension(200, 100); + } + + public void paint(Graphics g) { + g.setColor(myColor); + g.fillRect(0, 0, 200, 100); + } + + public void clickAndCheck() throws InterruptedException, BrokenBarrierException { + barrier.reset(); + Util.clickOnComp(this, robot); + try { + barrier.await(1, TimeUnit.SECONDS); + } catch (TimeoutException e) { + throw new RuntimeException(myColor + " was not clicked"); + } + } + } +} diff --git a/test/jdk/java/awt/event/InputEvent/InputEventTimeTest.java b/test/jdk/java/awt/event/InputEvent/InputEventTimeTest.java new file mode 100644 index 0000000000000..38339f8e87703 --- /dev/null +++ b/test/jdk/java/awt/event/InputEvent/InputEventTimeTest.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4176525 + * @summary InputEvent.getWhen() returns the wrong event time. + * @key headful + * @run main InputEventTimeTest + */ + +import java.awt.AWTEvent; +import java.awt.AWTException; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.lang.reflect.InvocationTargetException; +import java.util.Date; + +public class InputEventTimeTest extends Frame { + public void initUI() { + setTitle("Input Event Time Test"); + enableEvents(AWTEvent.MOUSE_EVENT_MASK); + enableEvents(AWTEvent.KEY_EVENT_MASK); + setSize(200, 200); + setLocationRelativeTo(null); + setVisible(true); + } + + public void center(Point point) { + Point loc = getLocationOnScreen(); + Dimension size = getSize(); + point.setLocation(loc.x + (size.width / 2), loc.y + (size.height / 2)); + } + + public void processEvent(AWTEvent e) { + long currentTime; + long eventTime; + long difference; + + if (!(e instanceof InputEvent)) { + return; + } + + currentTime = (new Date()).getTime(); + eventTime = ((InputEvent) e).getWhen(); + difference = currentTime - eventTime; + + if ((difference > 5000) || (difference < -5000)) { + throw new RuntimeException("The difference between current time" + + " and event creation time is " + difference + "ms"); + } + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, AWTException { + InputEventTimeTest test = new InputEventTimeTest(); + try { + EventQueue.invokeAndWait(test::initUI); + Robot robot = new Robot(); + robot.setAutoDelay(50); + robot.waitForIdle(); + robot.delay(1000); + Point center = new Point(); + EventQueue.invokeAndWait(() -> test.center(center)); + robot.mouseMove(center.x, center.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.mousePress(InputEvent.BUTTON2_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON2_DOWN_MASK); + robot.waitForIdle(); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + for (int i = 0; i < 6; i++) { + robot.keyPress(KeyEvent.VK_A + i); + robot.keyRelease(KeyEvent.VK_A + i); + robot.waitForIdle(); + } + for (int i = 0; i < 150; i += 5) { + robot.mouseMove(center.x - i, center.y - i); + } + for (int i = 150; i > 0; i -= 5) { + robot.mouseMove(center.x - i, center.y - i); + } + } finally { + EventQueue.invokeAndWait(test::dispose); + } + } +} diff --git a/test/jdk/java/awt/event/KeyEvent/AltGrTest.java b/test/jdk/java/awt/event/KeyEvent/AltGrTest.java new file mode 100644 index 0000000000000..9f771ea7e1960 --- /dev/null +++ b/test/jdk/java/awt/event/KeyEvent/AltGrTest.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4122687 4209844 + * @summary Characters typed with AltGr have Alt bit set on + * KEY_TYPED events + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual AltGrTest + */ + +import java.awt.BorderLayout; +import java.awt.Frame; +import java.awt.TextField; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.lang.reflect.InvocationTargetException; + +public class AltGrTest extends Frame implements KeyListener { + static String INSTRUCTIONS = """ + Switch to German (Germany) keyboard layout and type + few characters using key. + Note: on windows keyboards without an AltGr key, + you should use Ctrl-Alt to synthesize AltGr. + For example, on German keyboards, `@' is AltGr-Q + `{' is AltGr-7 and '[' is AltGr-8 + If you see the corresponding symbols appear in the text field + and there are no entries in log area starting with word "FAIL:" + press "Pass", otherwise press "Fail". + """; + + public AltGrTest() { + setLayout(new BorderLayout()); + TextField entry = new TextField(); + entry.addKeyListener(this); + add(entry, BorderLayout.CENTER); + pack(); + } + + public void keyTyped(KeyEvent e) { + PassFailJFrame.log("----"); + PassFailJFrame.log("Got " + e); + + if (e.isControlDown() || e.isAltDown()) { + PassFailJFrame.log("FAIL: character typed has following modifiers bits set:"); + PassFailJFrame.log((e.isControlDown() ? " Control" : "") + + (e.isAltDown() ? " Alt" : "")); + } + + if (!(e.isAltGraphDown())) { + PassFailJFrame.log("FAIL: AltGraph modifier is missing"); + } + } + + public void keyPressed(KeyEvent ignore) {} + public void keyReleased(KeyEvent ignore) {} + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .logArea(10) + .testUI(AltGrTest::new) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/event/KeyEvent/CRTest.java b/test/jdk/java/awt/event/KeyEvent/CRTest.java new file mode 100644 index 0000000000000..0cc17fd568eb3 --- /dev/null +++ b/test/jdk/java/awt/event/KeyEvent/CRTest.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4257434 + * @summary Ensures that the right results are produced by the + * carriage return keys. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual CRTest + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Frame; +import java.awt.TextField; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.lang.reflect.InvocationTargetException; +import java.util.concurrent.atomic.AtomicBoolean; + +public class CRTest extends Frame implements KeyListener, ActionListener { + StringBuilder error = new StringBuilder(); + AtomicBoolean actionCompleted = new AtomicBoolean(false); + static String INSTRUCTIONS = """ + This test requires keyboard with the numeric keypad (numpad). + If your keyboard does not have numpad press "Pass" to skip testing. + Click on the text field in window named "Check KeyChar values". + Press Enter on keypad. Then press Return key on a standard keyboard. + Then click on "Done" button. Test will pass or fail automatically. + """; + + public CRTest() { + super("Check KeyChar values"); + setLayout(new BorderLayout()); + TextField tf = new TextField(30); + + tf.addKeyListener(this); + tf.addActionListener(this); + + add(tf, BorderLayout.CENTER); + + Button done = new Button("Done"); + done.addActionListener((event) -> { + checkAndComplete(); + }); + add(done, BorderLayout.SOUTH); + pack(); + } + + public void checkAndComplete() { + if (!actionCompleted.get()) { + error.append("\nNo action received!"); + } + + if (!error.isEmpty()) { + PassFailJFrame.forceFail(error.toString()); + } else { + PassFailJFrame.forcePass(); + } + } + + public void keyPressed(KeyEvent evt) { + if ((evt.getKeyChar() != '\n') || (evt.getKeyCode() != KeyEvent.VK_ENTER)) { + error.append("\nKeyPressed: Unexpected code " + evt.getKeyCode()); + } else { + PassFailJFrame.log("KeyPressed Test PASSED"); + } + } + + public void keyTyped(KeyEvent evt) { + if ((evt.getKeyChar() != '\n') || (evt.getKeyCode() != KeyEvent.VK_UNDEFINED)) { + error.append("\nKeyTyped: Unexpected code " + evt.getKeyCode()); + } else { + PassFailJFrame.log("KeyTyped Test PASSED"); + } + } + + public void keyReleased(KeyEvent evt) { + if ((evt.getKeyChar() != '\n') || (evt.getKeyCode() != KeyEvent.VK_ENTER)) { + error.append("\nKeyReleased: Unexpected code " + evt.getKeyCode()); + } else { + PassFailJFrame.log("KeyReleased Test PASSED"); + } + } + + public void actionPerformed(ActionEvent evt) { + PassFailJFrame.log("ActionPerformed Test PASSED"); + actionCompleted.set(true); + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .logArea(10) + .testUI(CRTest::new) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/event/KeyEvent/CharUndefinedTest.java b/test/jdk/java/awt/event/KeyEvent/CharUndefinedTest.java new file mode 100644 index 0000000000000..bc58bdab14a0e --- /dev/null +++ b/test/jdk/java/awt/event/KeyEvent/CharUndefinedTest.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4115484 4164672 4167893 + * @summary Ensures that KeyEvent has right keyChar for modifier and action keys. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual CharUndefinedTest + */ + +import java.awt.BorderLayout; +import java.awt.Frame; +import java.awt.TextField; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.lang.reflect.InvocationTargetException; + +public class CharUndefinedTest extends Frame implements KeyListener { + + static String INSTRUCTIONS = """ + Click on the text field inside the window named "Check KeyChar values". + Of any of the keys mentioned in this list that exist on your keyboard + press each of the listed keys once and also press them in two-key combinations such as + Control-Shift or Alt-Control. + The list of keys is: "Control, Shift, Meta, Alt, Command, Option". + After that press all function keys from F1 to F12 once, + Insert, Home, End, PageUp, PageDown and four arrow keys. + Check the log area below. If there are no messages starting with word "ERROR" + press "Pass" otherwise press "Fail". + """; + + public CharUndefinedTest() { + super("Check KeyChar values"); + setLayout(new BorderLayout()); + TextField tf = new TextField(30); + tf.addKeyListener(this); + add(tf, BorderLayout.CENTER); + pack(); + tf.requestFocus(); + } + + public void keyPressed(KeyEvent e) { + if (e.getKeyChar() != KeyEvent.CHAR_UNDEFINED) { + PassFailJFrame.log("ERROR: KeyPressed: keyChar = " + e.getKeyChar() + + " keyCode = " + e.getKeyCode() + " " + e.getKeyText(e.getKeyCode())); + } + } + + public void keyTyped(KeyEvent e) { + if (e.getKeyChar() != KeyEvent.CHAR_UNDEFINED) { + PassFailJFrame.log("ERROR: KeyTyped: keyChar = " + e.getKeyChar() + + " keyCode = " + e.getKeyCode() + " " + e.getKeyText(e.getKeyCode())); + } + } + + public void keyReleased(KeyEvent e) { + if (e.getKeyChar() != KeyEvent.CHAR_UNDEFINED) { + PassFailJFrame.log("ERROR: KeyReleased: keyChar = " + e.getKeyChar() + + " keyCode = " + e.getKeyCode() + " " + e.getKeyText(e.getKeyCode())); + } + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(45) + .logArea(10) + .testUI(CharUndefinedTest::new) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/event/KeyEvent/ExtendedKeysTest.java b/test/jdk/java/awt/event/KeyEvent/ExtendedKeysTest.java new file mode 100644 index 0000000000000..1423b2a007e9f --- /dev/null +++ b/test/jdk/java/awt/event/KeyEvent/ExtendedKeysTest.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4218892 4191924 4199284 + * @summary Unable to enter some chars via european keyboard layout(s) + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ExtendedKeysTest + */ + +import java.awt.BorderLayout; +import java.awt.Frame; +import java.awt.TextArea; +import java.lang.reflect.InvocationTargetException; + +public class ExtendedKeysTest extends Frame { + static String INSTRUCTIONS = """ + This test requires Swiss German input. If the Swiss German input + can not be installed or configured press "Pass" to skip testing. + Click on the text area inside the window named "Check input". + Switch to Swiss German input and press key with "\\" on it + (usually this key is above or to the left of the main "Enter" key). + If you see a dollar sign press "Pass". + If you see any other character or question mark press "Fail". + """; + + public ExtendedKeysTest() { + super("Check input"); + setLayout(new BorderLayout()); + add(new TextArea(20, 20), "Center"); + pack(); + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(45) + .logArea(10) + .testUI(ExtendedKeysTest::new) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/event/KeyEvent/FrenchKeyboard.java b/test/jdk/java/awt/event/KeyEvent/FrenchKeyboard.java new file mode 100644 index 0000000000000..c4765c9e17ed3 --- /dev/null +++ b/test/jdk/java/awt/event/KeyEvent/FrenchKeyboard.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4308606 + * @summary Tests whether the keys on the numeric keyboard work + * correctly under French input locale. + * @key i18n + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FrenchKeyboard + */ + +import java.awt.BorderLayout; +import java.awt.Frame; +import java.awt.TextField; +import java.lang.reflect.InvocationTargetException; + +public class FrenchKeyboard extends Frame { + static String INSTRUCTIONS = """ + This test is intended for computers with French input method. If French + input method can not be enabled or your keyboard does not have a numeric + keypad press "Pass" to skip the test. + Make sure that French input method is active and the NumLock is on. + Click on the text field in the window called "Check your keys" + and type once of each of the following keys on the numeric keypad: + /*-+1234567890 + If all the expected characters are displayed exactly once press "Pass". + If any characters do not display or display multiple times press "Fail". + """; + + public FrenchKeyboard() { + super("Check your keys"); + setLayout(new BorderLayout()); + TextField tf = new TextField(30); + add(tf, BorderLayout.CENTER); + pack(); + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("FrenchKeyboard Instructions") + .instructions(INSTRUCTIONS) + .testUI(FrenchKeyboard::new) + .build() + .awaitAndCheck(); + } +} + diff --git a/test/jdk/java/awt/event/KeyEvent/HomeEndKeyTest.java b/test/jdk/java/awt/event/KeyEvent/HomeEndKeyTest.java new file mode 100644 index 0000000000000..2c0155a0b60a8 --- /dev/null +++ b/test/jdk/java/awt/event/KeyEvent/HomeEndKeyTest.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * bug 4268912 4115514 + * summary Ensures that KeyEvent has right results for the following + * non-numpad keys: Home/Eng/PageUp/PageDn + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual HomeEndKeyTest + */ + +import java.awt.BorderLayout; +import java.awt.Frame; +import java.awt.TextField; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.lang.reflect.InvocationTargetException; + + +public class HomeEndKeyTest extends Frame implements KeyListener { + static String INSTRUCTIONS = """ + Before starting this test make sure that system shortcuts and + keybindings for the keys in the list below are disabled. + For example pressing "Print Screen" key should not launch + screen capturing software. + Click on the text field in the window named "Check keyCode values" + and one by one start typing the keys in the list below. + (If you do not have some of the keys on your keyboard skip it + and move to the next line). + After clicking each key look at the log area - the printed name + and key code should correspond to the ones for the key you typed. + Note that on some systems the graphical symbol for the key + can be printed instead of the symbolic name. + If you do not encounter unexpected key codes for the keys you typed, + press "Pass". Otherwise press "Fail". + + Key Keycode + ------------------------- + PrintScreen 154 + ScrollLock 145 + Pause 19 + + Insert 155 + Del 127 + Home 36 + End 35 + PageUp 33 + PageDown 34 + + Left Arrow 37 + Up Arrow 38 + Right Arrow 39 + Down Arrow 40 + """; + + public HomeEndKeyTest() { + super("Check KeyCode values"); + setLayout(new BorderLayout()); + TextField tf = new TextField(30); + tf.addKeyListener(this); + add(tf, BorderLayout.CENTER); + pack(); + } + + public void keyPressed(KeyEvent evt) { + printKey(evt); + } + + public void keyTyped(KeyEvent ignore) { + } + + public void keyReleased(KeyEvent evt) { + printKey(evt); + } + + protected void printKey(KeyEvent evt) { + String str; + switch (evt.getID()) { + case KeyEvent.KEY_PRESSED: + str = "KEY_PRESSED"; + break; + case KeyEvent.KEY_RELEASED: + str = "KEY_RELEASED"; + break; + default: + str = "unknown type"; + } + + str = str + ":name=" + KeyEvent.getKeyText(evt.getKeyCode()) + + " keyCode=" + evt.getKeyCode(); + PassFailJFrame.log(str); + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("HomeEndKeyTest Instructions") + .instructions(INSTRUCTIONS) + .logArea(20) + .testUI(HomeEndKeyTest::new) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/event/KeyEvent/KeyCharTest/KeyCharTest.java b/test/jdk/java/awt/event/KeyEvent/KeyCharTest/KeyCharTest.java index 83b6e7735ea14..819c041308cfe 100644 --- a/test/jdk/java/awt/event/KeyEvent/KeyCharTest/KeyCharTest.java +++ b/test/jdk/java/awt/event/KeyEvent/KeyCharTest/KeyCharTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* @test - @bug 5013984 + @bug 5013984 8360647 @summary Tests KEY_PRESSED has the same KeyChar as KEY_RELEASED @key headful @run main KeyCharTest @@ -37,7 +37,7 @@ import java.util.HashMap; public class KeyCharTest extends Frame implements KeyListener { - HashMap transMap = new HashMap(); + HashMap transMap = new HashMap<>(); public void keyTyped(KeyEvent e){ } @@ -47,22 +47,35 @@ public void keyPressed(KeyEvent e){ } public void keyReleased(KeyEvent e){ - Object value = transMap.get(e.getKeyCode()); - if (value != null && e.getKeyChar() != ((Character)value).charValue()) { + Character value = transMap.get(e.getKeyCode()); + if (value != null && e.getKeyChar() != value) { throw new RuntimeException("Wrong KeyChar on KEY_RELEASED "+ KeyEvent.getKeyText(e.getKeyCode())); } } - public void start () { + private void testKeyRange(Robot robot, int start, int end) { + System.out.printf("\nTesting range on %d to %d\n", start, end); + for(int vkey = start; vkey <= end; vkey++) { + try { + robot.keyPress(vkey); + robot.keyRelease(vkey); + System.out.println(KeyEvent.getKeyText(vkey) + " " + vkey); + } catch (RuntimeException ignored) {} + } + robot.delay(100); + } + + public void start() throws Exception { + Robot robot = new Robot(); addKeyListener(this); setLocationRelativeTo(null); setSize(200, 200); setVisible(true); requestFocus(); + boolean wasNumlockPressed = false; try { - Robot robot = new Robot(); robot.setAutoDelay(10); robot.setAutoWaitForIdle(true); robot.delay(100); @@ -72,22 +85,25 @@ public void start () { robot.mousePress(MouseEvent.BUTTON1_DOWN_MASK); robot.mouseRelease(MouseEvent.BUTTON1_DOWN_MASK); - for(int vkey = 0x20; vkey < 0x7F; vkey++) { - try { - robot.keyPress(vkey); - robot.keyRelease(vkey); - System.out.println(KeyEvent.getKeyText(vkey) + " " + vkey); - } catch (RuntimeException e) { - } - } - robot.delay(100); + testKeyRange(robot, 0x20, 0x7E); + + // Try again with a different numpad state. + robot.keyPress(KeyEvent.VK_NUM_LOCK); + robot.keyRelease(KeyEvent.VK_NUM_LOCK); + wasNumlockPressed = true; + + testKeyRange(robot, KeyEvent.VK_NUMPAD0, KeyEvent.VK_DIVIDE); } catch(Exception e){ - e.printStackTrace(); throw new RuntimeException("Exception while performing Robot actions."); + } finally { + if (wasNumlockPressed) { + robot.keyPress(KeyEvent.VK_NUM_LOCK); + robot.keyRelease(KeyEvent.VK_NUM_LOCK); + } } } - public static void main(String[] args) { + public static void main(String[] args) throws Exception { KeyCharTest test = new KeyCharTest(); try { test.start(); diff --git a/test/jdk/java/awt/event/KeyEvent/KeyDownCaptureTest.java b/test/jdk/java/awt/event/KeyEvent/KeyDownCaptureTest.java new file mode 100644 index 0000000000000..bf2ab7ae95839 --- /dev/null +++ b/test/jdk/java/awt/event/KeyEvent/KeyDownCaptureTest.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4093998 + * @summary keyDown not called on subclasses of Component + * @key headful + * @run main KeyDownCaptureTest + */ + +import java.awt.AWTException; +import java.awt.BorderLayout; +import java.awt.Canvas; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.lang.reflect.InvocationTargetException; +import java.util.concurrent.atomic.AtomicBoolean; + +public class KeyDownCaptureTest extends Frame implements KeyListener { + static AtomicBoolean passed = new AtomicBoolean(false); + + public KeyDownCaptureTest() { + super("Key Down Capture Test"); + } + + public void initUI() { + setLayout (new BorderLayout()); + setSize(200, 200); + setLocationRelativeTo(null); + Canvas canvas = new Canvas(); + canvas.setBackground(Color.RED); + canvas.addKeyListener(this); + add(canvas, BorderLayout.CENTER); + setVisible(true); + } + + public void middle(Point p) { + Point loc = getLocationOnScreen(); + Dimension size = getSize(); + p.setLocation(loc.x + (size.width / 2), loc.y + (size.height / 2)); + } + + @Override + public void keyTyped(KeyEvent ignore) {} + + @Override + public void keyPressed(KeyEvent e) { + passed.set(true); + } + + @Override + public void keyReleased(KeyEvent ignore) {} + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, AWTException { + KeyDownCaptureTest test = new KeyDownCaptureTest(); + try { + EventQueue.invokeAndWait((test::initUI)); + Robot robot = new Robot(); + robot.setAutoDelay(50); + robot.delay(500); + robot.waitForIdle(); + Point target = new Point(); + EventQueue.invokeAndWait(() -> { + test.middle(target); + }); + robot.mouseMove(target.x, target.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.keyPress(KeyEvent.VK_SPACE); + robot.keyRelease(KeyEvent.VK_SPACE); + robot.delay(100); + robot.waitForIdle(); + if (!passed.get()) { + throw new RuntimeException("KeyPressed has not arrived to canvas"); + } + } finally { + if (test != null) { + EventQueue.invokeAndWait(test::dispose); + } + } + } +} diff --git a/test/jdk/java/awt/event/KeyEvent/KeyEventToLightweight.java b/test/jdk/java/awt/event/KeyEvent/KeyEventToLightweight.java new file mode 100644 index 0000000000000..de832bf2e6d4d --- /dev/null +++ b/test/jdk/java/awt/event/KeyEvent/KeyEventToLightweight.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4397557 + * @summary Check that focused lightweight component gets key events + * even if mouse is outside of it or on top of heavyweight component + * @key headful + * @run main KeyEventToLightweight + */ + +import java.awt.AWTException; +import java.awt.Button; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.InputEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.lang.reflect.InvocationTargetException; +import java.util.concurrent.atomic.AtomicBoolean; +import javax.swing.JButton; + +public class KeyEventToLightweight extends Frame { + JButton lwbutton = new JButton("Select Me"); + Button hwbutton = new Button("Heavyweight"); + + AtomicBoolean aTyped = new AtomicBoolean(false); + AtomicBoolean bTyped = new AtomicBoolean(false); + AtomicBoolean cTyped = new AtomicBoolean(false); + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, AWTException { + KeyEventToLightweight test = new KeyEventToLightweight(); + try { + EventQueue.invokeAndWait(test::initUI); + test.performTest(); + } finally { + EventQueue.invokeAndWait(test::dispose); + } + } + + public void initUI() { + this.setLayout(new FlowLayout()); + add(lwbutton); + add(hwbutton); + setSize(200, 200); + setLocationRelativeTo(null); + lwbutton.addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_A) { + aTyped.set(true); + } else if (e.getKeyCode() == KeyEvent.VK_B) { + bTyped.set(true); + } else if (e.getKeyCode() == KeyEvent.VK_C) { + cTyped.set(true); + } + } + }); + setVisible(true); + } + + public void middleOf(Component c, Point p) { + Point loc = c.getLocationOnScreen(); + Dimension size = c.getSize(); + p.setLocation(loc.x + (size.width / 2), loc.y + (size.height / 2)); + } + + public void performTest() throws AWTException, InterruptedException, + InvocationTargetException { + Robot robot = new Robot(); + robot.setAutoDelay(50); + robot.delay(500); + robot.waitForIdle(); + Point target = new Point(); + EventQueue.invokeAndWait(() -> middleOf(lwbutton, target)); + robot.mouseMove(target.x, target.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(500); + robot.keyPress(KeyEvent.VK_A); + robot.keyRelease(KeyEvent.VK_A); + robot.waitForIdle(); + robot.mouseMove(target.x - 200, target.y); + robot.keyPress(KeyEvent.VK_B); + robot.keyRelease(KeyEvent.VK_B); + robot.waitForIdle(); + robot.delay(500); + EventQueue.invokeAndWait(() -> middleOf(hwbutton, target)); + robot.mouseMove(target.x, target.y); + robot.keyPress(KeyEvent.VK_C); + robot.keyRelease(KeyEvent.VK_C); + if (!aTyped.get() || !bTyped.get() || !cTyped.get()) { + throw new RuntimeException("Key event was not delivered, case 1: " + + aTyped.get() + ", case 2: " + bTyped.get() + ", case 3: " + + cTyped.get()); + } + } +} diff --git a/test/jdk/java/awt/event/KeyEvent/KeyModifiers.java b/test/jdk/java/awt/event/KeyEvent/KeyModifiers.java new file mode 100644 index 0000000000000..13cc35581dbe5 --- /dev/null +++ b/test/jdk/java/awt/event/KeyEvent/KeyModifiers.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4193779 4174399 + * @summary Ensures that KeyEvents have the right modifiers set + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame jdk.test.lib.Platform + * @run main/manual KeyModifiers + */ + +import java.awt.BorderLayout; +import java.awt.Frame; +import java.awt.TextField; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.lang.reflect.InvocationTargetException; + +import jdk.test.lib.Platform; + + +public class KeyModifiers extends Frame implements KeyListener { + public KeyModifiers() { + super("Check KeyChar values"); + setLayout(new BorderLayout()); + TextField tf = new TextField(30); + tf.addKeyListener(this); + add(tf, BorderLayout.CENTER); + pack(); + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + + String keys; + if (Platform.isWindows()) { + keys = "\"Shift-n\", \"Alt-n\"\n"; + } else if (Platform.isOSX()) { + keys = "\"Shift-n\", \"Alt-n\", \"Command-n\"\n"; + } else { + keys = "\"Shift-n\", \"Alt-n\", \"Meta-n\"\n"; + } + + String INSTRUCTIONS1 = """ + Click on the text field in the window named "Check KeyChar values" + and type the following key combinations: + """; + String INSTRUCTIONS2 = """ + After each combination check that the KeyPressed and KeyTyped modifiers + are correctly displayed. If modifiers are correct press "Pass", + otherwise press "Fail". + """; + PassFailJFrame.builder() + .title("KeyModifiers Test Instructions") + .instructions(INSTRUCTIONS1 + keys + INSTRUCTIONS2) + .columns(45) + .logArea(10) + .testUI(KeyModifiers::new) + .build() + .awaitAndCheck(); + } + + public void keyPressed(KeyEvent evt) { + int kc = evt.getKeyCode(); + + if (kc == KeyEvent.VK_CONTROL) { + return; + } + + if ((kc == KeyEvent.VK_SHIFT) || (kc == KeyEvent.VK_META) || + (kc == KeyEvent.VK_ALT) || (kc == KeyEvent.VK_ALT_GRAPH)) { + PassFailJFrame.log("Key pressed= " + KeyEvent.getKeyText(kc) + + " modifiers = " + InputEvent.getModifiersExText(evt.getModifiersEx())); + } else { + PassFailJFrame.log("Key pressed = " + evt.getKeyChar() + + " modifiers = " + InputEvent.getModifiersExText(evt.getModifiersEx())); + } + } + + public void keyTyped(KeyEvent evt) { + int kc = evt.getKeyCode(); + + if (kc == KeyEvent.VK_CONTROL) { + return; + } + + if ((kc == KeyEvent.VK_SHIFT) || (kc == KeyEvent.VK_META) || + (kc == KeyEvent.VK_ALT) || (kc == KeyEvent.VK_ALT_GRAPH)) { + PassFailJFrame.log("Key typed = " + KeyEvent.getKeyText(kc) + + " modifiers = " + InputEvent.getModifiersExText(evt.getModifiersEx())); + } else { + PassFailJFrame.log("Key typed = " + evt.getKeyChar() + + " modifiers = " + InputEvent.getModifiersExText(evt.getModifiersEx())); + } + } + + public void keyReleased(KeyEvent evt) { + int kc = evt.getKeyCode(); + + if (kc == KeyEvent.VK_CONTROL) + return; + + if ((kc == KeyEvent.VK_SHIFT) || (kc == KeyEvent.VK_META) || + (kc == KeyEvent.VK_ALT) || (kc == KeyEvent.VK_ALT_GRAPH)) { + PassFailJFrame.log("Key = released " + KeyEvent.getKeyText(kc) + + " modifiers = " + InputEvent.getModifiersExText(evt.getModifiersEx())); + } else { + PassFailJFrame.log("Key released = " + evt.getKeyChar() + + " modifiers = " + InputEvent.getModifiersExText(evt.getModifiersEx())); + } + } +} diff --git a/test/jdk/java/awt/event/KeyEvent/KeyPressedModifiers.java b/test/jdk/java/awt/event/KeyEvent/KeyPressedModifiers.java new file mode 100644 index 0000000000000..08958bdbac21e --- /dev/null +++ b/test/jdk/java/awt/event/KeyEvent/KeyPressedModifiers.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4174399 + * @summary Check that modifier values are set on a KeyPressed event + * when a modifier key is pressed. + * @key headful + * @run main KeyPressedModifiers + */ + +import java.awt.AWTException; +import java.awt.BorderLayout; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Robot; +import java.awt.TextField; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.lang.reflect.InvocationTargetException; +import java.util.concurrent.atomic.AtomicBoolean; + +public class KeyPressedModifiers extends Frame implements KeyListener { + static AtomicBoolean shiftDown = new AtomicBoolean(false); + static AtomicBoolean controlDown = new AtomicBoolean(false); + static AtomicBoolean altDown = new AtomicBoolean(false); + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, AWTException { + KeyPressedModifiers test = new KeyPressedModifiers(); + try { + EventQueue.invokeAndWait(test::initUI); + Robot robot = new Robot(); + robot.setAutoDelay(100); + robot.delay(500); + robot.waitForIdle(); + robot.keyPress(KeyEvent.VK_SHIFT); + robot.keyRelease(KeyEvent.VK_SHIFT); + robot.keyPress(KeyEvent.VK_CONTROL); + robot.keyRelease(KeyEvent.VK_CONTROL); + robot.keyPress(KeyEvent.VK_ALT); + robot.keyRelease(KeyEvent.VK_ALT); + robot.delay(500); + robot.waitForIdle(); + if (!shiftDown.get() || !controlDown.get() || !altDown.get()) { + String error = "Following key modifiers were not registered:" + + (shiftDown.get() ? "" : " SHIFT") + + (controlDown.get() ? "" : " CONTROL") + + (altDown.get() ? "" : " ALT"); + throw new RuntimeException(error); + } + } finally { + EventQueue.invokeAndWait(test::dispose); + } + } + + public void initUI() { + setLayout(new BorderLayout()); + TextField tf = new TextField(30); + tf.addKeyListener(this); + add(tf, BorderLayout.CENTER); + setSize(350, 100); + setVisible(true); + tf.requestFocus(); + } + + public void keyTyped(KeyEvent ignore) { + } + + public void keyReleased(KeyEvent ignore) { + } + + public void keyPressed(KeyEvent e) { + System.out.println(e); + switch (e.getKeyCode()) { + case KeyEvent.VK_SHIFT: + shiftDown.set(e.isShiftDown()); + break; + case KeyEvent.VK_CONTROL: + controlDown.set(e.isControlDown()); + break; + case KeyEvent.VK_ALT: + altDown.set(e.isAltDown()); + break; + } + } +} diff --git a/test/jdk/java/awt/event/KeyEvent/KeyTest.java b/test/jdk/java/awt/event/KeyEvent/KeyTest.java new file mode 100644 index 0000000000000..d11798b4473af --- /dev/null +++ b/test/jdk/java/awt/event/KeyEvent/KeyTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4151419 4090870 4169733 + * @summary Ensures that KeyEvent has right results for the following + * keys -=\[];,./ + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual KeyTest + */ + +import java.awt.BorderLayout; +import java.awt.Frame; +import java.awt.TextField; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.lang.reflect.InvocationTargetException; + +public class KeyTest extends Frame implements KeyListener { + + static String INSTRUCTIONS = """ + Click on the text field in window named "Check KeyChar values" + Type the following keys/characters in the TextField: + - = \\ [ ] ; , . / + Verify that the keyChar and keyCode is correct for each key pressed. + Remember that the keyCode for the KEY_TYPED event should be zero. + Also verify that the character you typed appears in the TextField. + + Key Name keyChar Keycode + ------------------------------------- + - Minus - 45 45 + = Equals = 61 61 + \\ Slash \\ 92 92 + [ Left Brace [ 91 91 + ] Right Brace ] 93 93 + ; Semicolon ; 59 59 + , Comma , 44 44 + . Period . 46 46 + / Front Slash / 47 47 + """; + public KeyTest() { + super("Check KeyChar values"); + setLayout(new BorderLayout()); + TextField tf = new TextField(30); + tf.addKeyListener(this); + add(tf, BorderLayout.CENTER); + pack(); + + } + + public void keyPressed(KeyEvent evt) { + printKey(evt); + } + + public void keyTyped(KeyEvent evt) { + printKey(evt); + } + + public void keyReleased(KeyEvent evt) { + printKey(evt); + } + + protected void printKey(KeyEvent evt) { + if (evt.isActionKey()) { + PassFailJFrame.log("params= " + evt.paramString() + " KeyChar: " + + (int) evt.getKeyChar() + " Action Key"); + } else { + PassFailJFrame.log("params= " + evt.paramString() + " KeyChar: " + + (int) evt.getKeyChar()); + } + } + + public static void main(String[] args) throws InterruptedException, InvocationTargetException { + PassFailJFrame.builder() + .title("KeyTest Instructions") + .instructions(INSTRUCTIONS) + .logArea(20) + .testUI(KeyTest::new) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/event/KeyEvent/NumpadTest.java b/test/jdk/java/awt/event/KeyEvent/NumpadTest.java new file mode 100644 index 0000000000000..00c98ad4150ed --- /dev/null +++ b/test/jdk/java/awt/event/KeyEvent/NumpadTest.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4083691 + * @summary Ensures that KeyEvent has right results for the following + * keys \*-+ + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual NumpadTest + */ + +import java.awt.BorderLayout; +import java.awt.Frame; +import java.awt.TextField; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.lang.reflect.InvocationTargetException; + + +public class NumpadTest extends Frame implements KeyListener { + static String INSTRUCTIONS = """ + This test requires a keyboard with a numeric keypad (numpad). + If your keyboard does not have a numpad press "Pass" to skip testing. + Make sure NumLock is on. + Click on the text field in the window named "Check KeyChar values". + Then, type the following keys/characters in the TextField. + using the numpad keys: + /*-+ + + Verify that the keyChar and keyCode is correct for each key pressed. + Remember that the keyCode for the KEY_TYPED event should be zero. + Also verify that the character you typed appears in the TextField. + + Key Name keyChar Keycode + ------------------------------------- + / Divide / 47 111 + * Multiply * 42 106 + - Subtract - 45 109 + + Add + 43 107 + + Now repeat with the NumLock off. + + If all keycodes are valid and expected characters appear + in the text field press "Pass". Otherwise press "Fail". + """; + + public NumpadTest() { + super("Check KeyChar values"); + setLayout(new BorderLayout()); + TextField tf = new TextField(30); + tf.addKeyListener(this); + add(tf, BorderLayout.CENTER); + pack(); + } + public void keyPressed(KeyEvent evt) { + printKey(evt); + } + + public void keyTyped(KeyEvent evt) { + printKey(evt); + } + + public void keyReleased(KeyEvent evt) { + printKey(evt); + } + + protected void printKey(KeyEvent evt) { + switch (evt.getID()) { + case KeyEvent.KEY_TYPED: + break; + case KeyEvent.KEY_PRESSED: + break; + case KeyEvent.KEY_RELEASED: + break; + default: + return; + } + + if (evt.isActionKey()) { + PassFailJFrame.log("params= " + evt.paramString() + " KeyChar: " + + (int) evt.getKeyChar() + " Action Key"); + } else { + PassFailJFrame.log("params= " + evt.paramString() + " KeyChar: " + + (int) evt.getKeyChar()); + } + } + + public static void main(String[] args) throws InterruptedException, InvocationTargetException { + PassFailJFrame.builder() + .title("NumpadTest Instructions") + .instructions(INSTRUCTIONS) + .logArea(20) + .testUI(NumpadTest::new) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/event/KeyEvent/NumpadTest2.java b/test/jdk/java/awt/event/KeyEvent/NumpadTest2.java new file mode 100644 index 0000000000000..1ee4735431896 --- /dev/null +++ b/test/jdk/java/awt/event/KeyEvent/NumpadTest2.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4279566 + * @summary Tests that numpad keys produce the correct key codes and + * key chars when both the NumLock and CapsLock are on. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual NumpadTest2 +*/ + +import java.awt.BorderLayout; +import java.awt.Frame; +import java.awt.TextField; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.lang.reflect.InvocationTargetException; + +public class NumpadTest2 extends Frame implements KeyListener { + static String INSTRUCTIONS = """ + Make sure that the NumLock and CapsLock are both ON. + Click on the text field inside the window named "Check KeyChar values" + Then, type the NumPad 7 key (not the regular 7 key). + Verify that the keyChar and keyCode is correct for each key pressed. + Remember that the keyCode for the KEY_TYPED event should be zero. + If 7 appears in the text field and the key code printed is correct + press "Pass", otherwise press "Fail". + + Key Name keyChar Keycode + ------------------------------------------------- + Numpad-7 Numpad-7 55 103 + """; + + public NumpadTest2() { + super("Check KeyChar values"); + setLayout(new BorderLayout()); + TextField tf = new TextField(30); + tf.addKeyListener(this); + add(tf, BorderLayout.CENTER); + pack(); + } + + public void keyPressed(KeyEvent evt) { + printKey(evt); + } + + public void keyTyped(KeyEvent evt) { + printKey(evt); + } + + public void keyReleased(KeyEvent evt) { + printKey(evt); + } + + protected void printKey(KeyEvent evt) { + switch (evt.getID()) { + case KeyEvent.KEY_TYPED: + break; + case KeyEvent.KEY_PRESSED: + break; + case KeyEvent.KEY_RELEASED: + break; + default: + System.out.println("Other Event "); + return; + } + + if (evt.isActionKey()) { + PassFailJFrame.log("params= " + evt.paramString() + " KeyChar: " + + (int) evt.getKeyChar() + " Action Key"); + } else { + PassFailJFrame.log("params= " + evt.paramString() + " KeyChar: " + + (int) evt.getKeyChar()); + } + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .logArea(10) + .testUI(NumpadTest2::new) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/event/KeyEvent/TestDoubleKeyEvent.java b/test/jdk/java/awt/event/KeyEvent/TestDoubleKeyEvent.java new file mode 100644 index 0000000000000..24e0bd553c893 --- /dev/null +++ b/test/jdk/java/awt/event/KeyEvent/TestDoubleKeyEvent.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4495473 + * @summary Tests that when you press key on canvas-type heavyweight only one key event arrives + * @key headful + * @run main TestDoubleKeyEvent + */ + +import java.awt.AWTException; +import java.awt.BorderLayout; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Robot; +import java.awt.event.KeyEvent; +import java.lang.reflect.InvocationTargetException; +import javax.swing.JFrame; +import javax.swing.JWindow; +import javax.swing.JTextField; + +public class TestDoubleKeyEvent extends JFrame { + JWindow w; + JTextField tf; + + public void initUI() { + setLayout(new BorderLayout()); + setTitle("Double Key Event Test"); + setLocationRelativeTo(null); + setVisible(true); + w = new JWindow(this); + w.setLayout(new FlowLayout()); + tf = new JTextField(20); + w.add(tf); + w.pack(); + w.setLocationRelativeTo(null); + w.setVisible(true); + tf.requestFocus(); + } + + public void testAndClean() { + String str = tf.getText(); + w.dispose(); + dispose(); + if (str.length() != str.chars().distinct().count()) { + throw new RuntimeException("Duplicate characters found!"); + } + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, AWTException { + TestDoubleKeyEvent test = new TestDoubleKeyEvent(); + EventQueue.invokeAndWait(test::initUI); + Robot robot = new Robot(); + robot.setAutoDelay(50); + robot.waitForIdle(); + robot.delay(1000); + for (int i = 0; i < 15; i++) { + robot.keyPress(KeyEvent.VK_A + i); + robot.keyRelease(KeyEvent.VK_A + i); + robot.waitForIdle(); + } + robot.delay(1000); + EventQueue.invokeAndWait(test::testAndClean); + } +} diff --git a/test/jdk/java/awt/event/MouseEvent/AWTPanelSmoothWheel.java b/test/jdk/java/awt/event/MouseEvent/AWTPanelSmoothWheel.java new file mode 100644 index 0000000000000..32d728874591a --- /dev/null +++ b/test/jdk/java/awt/event/MouseEvent/AWTPanelSmoothWheel.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2009, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.event.MouseWheelEvent; +import javax.swing.JOptionPane; + +/* + * @test + * @bug 6730447 + * @summary To verify the support for high resolution mouse wheel. + * AWT panel needs to support high-res mouse wheel rotation. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual AWTPanelSmoothWheel + */ + +public class AWTPanelSmoothWheel { + private static int wheelEventCount = 0; + private static int hiResWheelCount = 0; + private static final String WARNING_MSG = "WARNING !!!" + + " You might NOT be using a hi-res mouse."; + private static final String INSTRUCTIONS = """ + + + This test is relevant on platforms with high-resolution mouse wheel + or a trackpad can be used too. + Please press PASS if this is not the case.

    + + Place the mouse cursor above the green panel and rotate the mouse wheel, + the test will print mouse wheel event messages in the format + [Event#, WheelRotation, PreciseWheelRotation] into the logging + panel below the instruction window.

    + + A hi-res mouse/trackpad is one which produces MouseWheelEvents having: +
     Math.abs(preciseWheelRotation) < 1. 

    + + Check if the test works OK when the mouse-wheel/trackpad is scrolled + very slowly.

    + This is a semi-automated test, if you are using a hi-res mouse/trackpad + and it satisfies the hi-res MouseWheelEvents as described below, + the test should automatically pass.

    + + When preciseWheelRotation adds up, wheelRotation becomes non-zero + (can be negative when mouse wheel is scrolled down).
    + You should see many events where the absolute value of + preciseWheelRotation < 1 & wheelRotation = 0 followed by + an event where wheelRotation != 0 in the logs.

    + +
    + NOTE: +
      +
    • If you don't see events with preciseWheelRotation < 1, + then the mouse doesn't support high-resolution scrolling.
    • +
    • A warning is shown if you are not using a hi-res mouse.
    • +
    • MouseWheelEvent logs are displayed in the log area + for user reference.
    • +
    • When mouse is scrolled up, preciseWheelRotation & wheelRotation + are positive and they are negative when scrolled down.
    • +
    + + + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows(30) + .columns(54) + .testTimeOut(10) + .logArea(10) + .testUI(AWTPanelSmoothWheel::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame frame = new Frame("Test Wheel Rotation"); + Panel panel = new Panel(); + panel.setBackground(Color.GREEN); + panel.addMouseWheelListener(e -> { + if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) { + PassFailJFrame.log("WheelEvent#" + (++wheelEventCount) + + " --- Wheel Rotation: " + e.getWheelRotation() + + " --- Precise Wheel Rotation: " + + String.format("%.2f", e.getPreciseWheelRotation())); + if (Math.abs(e.getPreciseWheelRotation()) < 1) { + hiResWheelCount++; + } + if (wheelEventCount >= 5 && hiResWheelCount == 0) { + PassFailJFrame.log(WARNING_MSG); + JOptionPane.showMessageDialog(frame, WARNING_MSG, + "Warning", JOptionPane.WARNING_MESSAGE); + } + if (e.getWheelRotation() != 0 && hiResWheelCount > 0) { + PassFailJFrame.log("The test passes: hiResWheelCount = " + + hiResWheelCount); + PassFailJFrame.forcePass(); + } + } + }); + frame.setSize(400, 200); + frame.setLayout(new BorderLayout()); + frame.add(panel, BorderLayout.CENTER); + return frame; + } +} diff --git a/test/jdk/java/awt/event/MouseEvent/AWTPanelSmoothWheel/AWTPanelSmoothWheel.html b/test/jdk/java/awt/event/MouseEvent/AWTPanelSmoothWheel/AWTPanelSmoothWheel.html deleted file mode 100644 index e75f1f0aed03b..0000000000000 --- a/test/jdk/java/awt/event/MouseEvent/AWTPanelSmoothWheel/AWTPanelSmoothWheel.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - AWTPanelSmoothWheel - - - -

    AWTPanelSmoothWheel
    Bug ID: 6730447

    - -

    See the dialog box (usually in upper left corner) for instructions

    - - - - diff --git a/test/jdk/java/awt/event/MouseEvent/AWTPanelSmoothWheel/AWTPanelSmoothWheel.java b/test/jdk/java/awt/event/MouseEvent/AWTPanelSmoothWheel/AWTPanelSmoothWheel.java deleted file mode 100644 index 69b94d4879200..0000000000000 --- a/test/jdk/java/awt/event/MouseEvent/AWTPanelSmoothWheel/AWTPanelSmoothWheel.java +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - test - @bug 6730447 - @summary Support for high resolution mouse wheel is still incomplete. AWT panel needs to be supported - @author dmitry.cherepanov@...: area=awt.mouse - @run applet/manual=yesno AWTPanelSmoothWheel.html -*/ - -/** - * AWTPanelSmoothWheel.java - * - * summary: - */ - -import java.applet.Applet; -import java.awt.*; -import java.awt.event.*; - -//Manual tests should run as applet tests if possible because they -// get their environments cleaned up, including AWT threads, any -// test created threads, and any system resources used by the test -// such as file descriptors. (This is normally not a problem as -// main tests usually run in a separate VM, however on some platforms -// such as the Mac, separate VMs are not possible and non-applet -// tests will cause problems). Also, you don't have to worry about -// synchronisation stuff in Applet tests the way you do in main -// tests... - - -public class AWTPanelSmoothWheel extends Applet -{ - //Declare things used in the test, like buttons and labels here - - public void init() - { - //Create instructions for the user here, as well as set up - // the environment -- set the layout manager, add buttons, - // etc. - this.setLayout (new BorderLayout ()); - - String[] instructions = - { - " the test is relevant for windows platforms and ", - " mouses with high-resolution wheel, please just press pass if it's not the case ", - " place the mouse cursor above the green panel and rotate the mouse wheel " , - " the test will print all mouse wheel messages into the logging panel, ", - " please make sure that some of the messages have non-zero 'wheelRotation' value ", - " in this case the test passes, otherwise it fails, ", - " please make sure the test works OK if the mouse wheel is rotated very slow " - }; - Sysout.createDialogWithInstructions( instructions ); - - }//End init() - - public void start () - { - Panel panel = new Panel(); - panel.setBackground(Color.green); - panel.addMouseWheelListener(new MouseWheelListener() { - public void mouseWheelMoved(MouseWheelEvent e) { - Sysout.println(e.toString()); - } - }); - - //Get things going. Request focus, set size, et cetera - setSize (200,200); - setLayout(new BorderLayout()); - add(panel, BorderLayout.CENTER); - setVisible(true); - validate(); - - //What would normally go into main() will probably go here. - //Use System.out.println for diagnostic messages that you want - // to read after the test is done. - //Use Sysout.println for messages you want the tester to read. - - }// start() - - //The rest of this class is the actions which perform the test... - - //Use Sysout.println to communicate with the user NOT System.out!! - //Sysout.println ("Something Happened!"); - -}// class ManualYesNoTest - -/* Place other classes related to the test after this line */ - - - - - -/**************************************************** - Standard Test Machinery - DO NOT modify anything below -- it's a standard - chunk of code whose purpose is to make user - interaction uniform, and thereby make it simpler - to read and understand someone else's test. - ****************************************************/ - -/** - This is part of the standard test machinery. - It creates a dialog (with the instructions), and is the interface - for sending text messages to the user. - To print the instructions, send an array of strings to Sysout.createDialog - WithInstructions method. Put one line of instructions per array entry. - To display a message for the tester to see, simply call Sysout.println - with the string to be displayed. - This mimics System.out.println but works within the test harness as well - as standalone. - */ - -class Sysout -{ - private static TestDialog dialog; - - public static void createDialogWithInstructions( String[] instructions ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - dialog.printInstructions( instructions ); - dialog.setVisible(true); - println( "Any messages for the tester will display here." ); - } - - public static void createDialog( ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - String[] defInstr = { "Instructions will appear here. ", "" } ; - dialog.printInstructions( defInstr ); - dialog.setVisible(true); - println( "Any messages for the tester will display here." ); - } - - - public static void printInstructions( String[] instructions ) - { - dialog.printInstructions( instructions ); - } - - - public static void println( String messageIn ) - { - dialog.displayMessage( messageIn ); - } - -}// Sysout class - -/** - This is part of the standard test machinery. It provides a place for the - test instructions to be displayed, and a place for interactive messages - to the user to be displayed. - To have the test instructions displayed, see Sysout. - To have a message to the user be displayed, see Sysout. - Do not call anything in this dialog directly. - */ -class TestDialog extends Dialog -{ - - TextArea instructionsText; - TextArea messageText; - int maxStringLength = 80; - - //DO NOT call this directly, go through Sysout - public TestDialog( Frame frame, String name ) - { - super( frame, name ); - int scrollBoth = TextArea.SCROLLBARS_BOTH; - instructionsText = new TextArea( "", 15, maxStringLength, scrollBoth ); - add( "North", instructionsText ); - - messageText = new TextArea( "", 5, maxStringLength, scrollBoth ); - add("Center", messageText); - - pack(); - - setVisible(true); - }// TestDialog() - - //DO NOT call this directly, go through Sysout - public void printInstructions( String[] instructions ) - { - //Clear out any current instructions - instructionsText.setText( "" ); - - //Go down array of instruction strings - - String printStr, remainingStr; - for( int i=0; i < instructions.length; i++ ) - { - //chop up each into pieces maxSringLength long - remainingStr = instructions[ i ]; - while( remainingStr.length() > 0 ) - { - //if longer than max then chop off first max chars to print - if( remainingStr.length() >= maxStringLength ) - { - //Try to chop on a word boundary - int posOfSpace = remainingStr. - lastIndexOf( ' ', maxStringLength - 1 ); - - if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1; - - printStr = remainingStr.substring( 0, posOfSpace + 1 ); - remainingStr = remainingStr.substring( posOfSpace + 1 ); - } - //else just print - else - { - printStr = remainingStr; - remainingStr = ""; - } - - instructionsText.append( printStr + "\n" ); - - }// while - - }// for - - }//printInstructions() - - //DO NOT call this directly, go through Sysout - public void displayMessage( String messageIn ) - { - messageText.append( messageIn + "\n" ); - System.out.println(messageIn); - } - -}// TestDialog class - diff --git a/test/jdk/java/awt/event/MouseEvent/DragMouseEventTest.java b/test/jdk/java/awt/event/MouseEvent/DragMouseEventTest.java new file mode 100644 index 0000000000000..3f8d10651ff19 --- /dev/null +++ b/test/jdk/java/awt/event/MouseEvent/DragMouseEventTest.java @@ -0,0 +1,438 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4327618 4327623 4327639 4327654 4327664 4327666 4327676 4327679 4507822 + * @summary Tests that MouseDragged and MouseReleased are triggered by Button, + * Checkbox, Choice, Label, List, Scrollbar, TextArea, TextField + * for Left, Middle and Right mouse buttons + * @key headful + * @library /lib/client /java/awt/regtesthelpers + * @build ExtendedRobot Util + * @run main/othervm -Dsun.java2d.uiScale=1 DragMouseEventTest +*/ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Checkbox; +import java.awt.Choice; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Label; +import java.awt.List; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Scrollbar; +import java.awt.TextArea; +import java.awt.TextField; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.util.Arrays; + +import test.java.awt.regtesthelpers.Util; + +public class DragMouseEventTest { + private static ExtendedRobot robot; + private static DragMouseEventFrame dmef; + private static final int DELAY = 200; + + public static void main(String[] args) throws Exception { + try { + EventQueue.invokeAndWait(DragMouseEventTest::createAndShowGUI); + test(); + } finally { + EventQueue.invokeAndWait(() -> { + if (dmef != null) { + dmef.dispose(); + } + }); + } + } + + private static void createAndShowGUI() { + dmef = new DragMouseEventFrame(); + dmef.setVisible(true); + } + + private static void test() throws Exception { + robot = new ExtendedRobot(); + robot.waitForIdle(); + robot.delay(500); + + testComponent(dmef.scrollbar); + testComponent(dmef.choice); + testComponent(dmef.textarea); + testComponent(dmef.textfield); + testComponent(dmef.checkbox); + testComponent(dmef.label); + testComponent(dmef.list); + testComponent(dmef.button); + } + + private static void testComponent(Component component) throws Exception { + Rectangle componentBounds = Util.invokeOnEDT(() -> { + Point locationOnScreen = component.getLocationOnScreen(); + Dimension size = component.getSize(); + return new Rectangle(locationOnScreen, size); + }); + + Rectangle frameBounds = Util.invokeOnEDT(() -> dmef.getBounds()); + + Point start = new Point(componentBounds.x + 10, componentBounds.y + 10); + + Adapter adapter = getAdapterFromComponent(component); + + testItemStateChanged(component, adapter, start, componentBounds); + testActionListener(component, adapter, start); + + Point mid = getEndPoint(start, frameBounds, 3); + Point end = getEndPoint(start, frameBounds, 15); + + testButtonDrag(component, adapter, MouseEvent.BUTTON1_DOWN_MASK, start, mid, end); + testButtonDrag(component, adapter, MouseEvent.BUTTON2_DOWN_MASK, start, mid, end); + testButtonDrag(component, adapter, MouseEvent.BUTTON3_DOWN_MASK, start, mid, end); + } + + private static Adapter getAdapterFromComponent(Component component) { + return (Adapter) Arrays + .stream(component.getMouseListeners()) + .filter((m) -> m instanceof Adapter) + .findFirst() + .orElseThrow(); + } + + private static void testItemStateChanged(Component component, + Adapter adapter, + Point start, + Rectangle componentBounds) { + if (!(component instanceof Choice + || component instanceof Checkbox + || component instanceof List)) { + return; + } + + System.out.println("\ntestItemStateChanged " + component); + + adapter.reset(); + robot.mouseMove(start.x, start.y); + robot.waitForIdle(); + robot.click(); + + if (component instanceof Choice) { + robot.mouseMove(start.x, componentBounds.y + componentBounds.height + 25); + robot.waitForIdle(); + robot.click(); + } + + robot.waitForIdle(); + + if (!adapter.itemStateChangedReceived) { + throw new RuntimeException("itemStateChanged was not received for " + component); + } + } + + private static void testActionListener(Component component, + Adapter adapter, + Point start) { + if (!(component instanceof Button || component instanceof List)) { + // skip for not applicable components + return; + } + + System.out.println("\ntestActionListener " + component); + adapter.reset(); + + robot.mouseMove(start.x, start.y); + robot.waitForIdle(); + + if (component instanceof List) { + robot.mousePress(MouseEvent.BUTTON1_DOWN_MASK); + robot.delay(25); + robot.mouseRelease(MouseEvent.BUTTON1_DOWN_MASK); + robot.delay(25); + robot.mousePress(MouseEvent.BUTTON1_DOWN_MASK); + robot.delay(25); + robot.mouseRelease(MouseEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(DELAY); + } else { + robot.click(); + } + + robot.waitForIdle(); + robot.delay(DELAY); + + if (!adapter.actionPerformedReceived) { + throw new RuntimeException("actionPerformed was not received for " + component); + } + } + + private static String getButtonName(int button) { + return switch (button) { + case MouseEvent.BUTTON1_DOWN_MASK -> "BUTTON1"; + case MouseEvent.BUTTON2_DOWN_MASK -> "BUTTON2"; + case MouseEvent.BUTTON3_DOWN_MASK -> "BUTTON3"; + default -> throw new IllegalStateException("Unexpected value: " + button); + }; + } + + private static void testButtonDrag(Component component, + Adapter adapter, + int button, + Point start, Point mid, Point end) { + String buttonName = getButtonName(button); + System.out.printf("\n> testButtonDrag: %s on %s\n", + buttonName, component); + + robot.mouseMove(start.x, start.y); + robot.waitForIdle(); + + robot.mousePress(button); + robot.waitForIdle(); + + System.out.printf("> gliding from (%d,%d) to (%d,%d)\n", + start.x, start.y, mid.x, mid.y); + robot.glide(start, mid); + robot.waitForIdle(); + robot.delay(DELAY); + + + // Catch events only after we leave the frame boundaries + adapter.reset(); + + System.out.printf("> gliding after crossing the border (%d,%d) to (%d,%d)\n", + mid.x, mid.y, end.x, end.y); + robot.glide(mid, end); + + robot.mouseRelease(button); + robot.waitForIdle(); + robot.delay(DELAY); + System.out.printf("> %s released\n", buttonName); + + boolean mouseDraggedReceived = adapter.mouseDraggedReceived; + boolean mouseReleasedReceived = adapter.mouseReleasedReceived; + + if (component instanceof Choice) { + // Close the popup if it is still open + robot.keyPress(KeyEvent.VK_ESCAPE); + robot.delay(25); + robot.keyRelease(KeyEvent.VK_ESCAPE); + robot.waitForIdle(); + robot.delay(DELAY); + } + + + if (!mouseDraggedReceived || !mouseReleasedReceived) { + throw new RuntimeException(("%d: Mouse drag or release was not received\n" + + "mouseDraggedReceived %b mouseReleasedReceived %b") + .formatted(button, mouseDraggedReceived, mouseReleasedReceived)); + } + } + + /* + * returns the closest border point with a specified offset + */ + private static Point getEndPoint(Point start, Rectangle bounds, int offset) { + int left = bounds.x; + int right = bounds.x + bounds.width; + int top = bounds.y; + int bottom = bounds.y + bounds.height; + + int distanceLeft = start.x - left; + int distanceRight = right - start.x; + int distanceTop = start.y - top; + int distanceBottom = bottom - start.y; + + int minDistance = Math.min( + Math.min(distanceLeft, distanceRight), + Math.min(distanceTop, distanceBottom) + ); + + if (minDistance == distanceLeft) { + return new Point(left - offset, start.y); + } else if (minDistance == distanceRight) { + return new Point(right + offset, start.y); + } else if (minDistance == distanceTop) { + return new Point(start.x, top - offset); + } else { + return new Point(start.x, bottom + offset); + } + } + + private static class DragMouseEventFrame extends Frame { + TextArea textarea = new TextArea("TextArea", 20, 30); + Label label = new Label("Label"); + Panel panel = new Panel(); + List list = new List(); + Choice choice = new Choice(); + Button button = new Button("Button"); + TextField textfield = new TextField("TextField"); + Checkbox checkbox = new Checkbox("CheckBox"); + Scrollbar scrollbar = new Scrollbar(); + Panel centerPanel = new Panel(); + + public DragMouseEventFrame() { + setTitle("DragMouseEventTest"); + + add(centerPanel, BorderLayout.CENTER); + centerPanel.setLayout(new FlowLayout()); + + add(panel, BorderLayout.NORTH); + panel.setLayout(new FlowLayout()); + + choice.add("choice item 1"); + choice.add("choice item 2"); + choice.add("choice item 3"); + panel.add(choice); + + Adapter adapter = new Adapter(); + choice.addMouseMotionListener(adapter); + choice.addMouseListener(adapter); + choice.addItemListener(adapter); + + adapter = new Adapter(); + panel.add(label); + label.addMouseMotionListener(adapter); + label.addMouseListener(adapter); + + adapter = new Adapter(); + panel.add(button); + button.addMouseMotionListener(adapter); + button.addMouseListener(adapter); + button.addActionListener(adapter); + + adapter = new Adapter(); + panel.add(checkbox); + checkbox.addMouseMotionListener(adapter); + checkbox.addMouseListener(adapter); + checkbox.addItemListener(adapter); + + adapter = new Adapter(); + panel.add(textfield, BorderLayout.EAST); + textfield.addMouseMotionListener(adapter); + textfield.addMouseListener(adapter); + textfield.addActionListener(adapter); + + adapter = new Adapter(); + add(textarea, BorderLayout.EAST); + textarea.addMouseMotionListener(adapter); + textarea.addMouseListener(adapter); + + adapter = new Adapter(); + list.add("list item 1"); + list.add("list item 2"); + add(list, BorderLayout.SOUTH); + list.addMouseMotionListener(adapter); + list.addMouseListener(adapter); + list.addActionListener(adapter); + list.addItemListener(adapter); + + adapter = new Adapter(); + add(scrollbar, BorderLayout.WEST); + scrollbar.addMouseMotionListener(adapter); + scrollbar.addMouseListener(adapter); + + setSize(500, 400); + setLocationRelativeTo(null); + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + DragMouseEventFrame.this.dispose(); + } + }); + } + } + + private static class Adapter extends MouseAdapter + implements ActionListener, ItemListener { + + public volatile boolean mouseDraggedReceived = false; + public volatile boolean mouseReleasedReceived = false; + public volatile boolean itemStateChangedReceived = false; + public volatile boolean actionPerformedReceived = false; + + + public void mouseDragged(MouseEvent me) { + mouseDraggedReceived = true; + System.out.println(me.paramString()); + } + + private void consumeIfNeeded(MouseEvent me) { + Component c = me.getComponent(); + // do not show popup menu for the following components, + // as it may interfere with the testing. + if (c instanceof TextArea + || c instanceof TextField + || c instanceof Scrollbar) { + if (me.isPopupTrigger()) { + System.out.println("CONSUMED: " + me); + me.consume(); + } + } + } + + public void mouseReleased(MouseEvent me) { + consumeIfNeeded(me); + mouseReleasedReceived = true; + System.out.println(me.paramString()); + } + + public void mousePressed(MouseEvent me) { + consumeIfNeeded(me); + System.out.println(me.paramString()); + } + + public void mouseClicked(MouseEvent me) { + System.out.println(me.paramString()); + } + + public void actionPerformed(ActionEvent e) { + actionPerformedReceived = true; + System.out.println(e.paramString()); + } + + public void itemStateChanged(ItemEvent e) { + itemStateChangedReceived = true; + System.out.println(e.paramString()); + } + + public void reset() { + mouseDraggedReceived = false; + mouseReleasedReceived = false; + itemStateChangedReceived = false; + actionPerformedReceived = false; + } + } +} diff --git a/test/jdk/java/awt/event/MouseEvent/DragToLightweightTest.java b/test/jdk/java/awt/event/MouseEvent/DragToLightweightTest.java new file mode 100644 index 0000000000000..7549c42beebd3 --- /dev/null +++ b/test/jdk/java/awt/event/MouseEvent/DragToLightweightTest.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4417964 + * @summary tests that drag events continue to arrive to heavyweight + * when the mouse is moved to lightweight while dragging. + * @key headful + * @library /lib/client /java/awt/regtesthelpers + * @build ExtendedRobot Util + * @run main DragToLightweightTest +*/ + +import java.awt.Color; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.HeadlessException; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import test.java.awt.regtesthelpers.Util; + +public class DragToLightweightTest { + + private static final CountDownLatch latch = new CountDownLatch(1); + private static volatile MouseTest mouseTest; + + public static void main(String[] args) throws Exception { + + EventQueue.invokeAndWait(() -> mouseTest = new MouseTest()); + + try { + test(); + } finally { + EventQueue.invokeAndWait(() -> { + if (mouseTest != null) { + mouseTest.dispose(); + } + }); + } + } + + private static void test() throws Exception { + ExtendedRobot robot = new ExtendedRobot(); + robot.waitForIdle(); + robot.delay(500); + + Rectangle componentBounds = mouseTest.getLightweightComponentBounds(); + + robot.dragAndDrop( + componentBounds.x + componentBounds.width / 2, componentBounds.y + componentBounds.height + 30, + componentBounds.x + componentBounds.width / 2, componentBounds.y + 2 * componentBounds.height / 3 + ); + + if (!latch.await(5, TimeUnit.SECONDS)) { + throw new RuntimeException("The test failed: no mouse release event received"); + } + + System.out.println("Mouse release event received, the test PASSED"); + } + + private static class MouseTest extends Frame { + + final Foo foo; + + public MouseTest() throws HeadlessException { + super("DragToLightweightTest"); + + setLayout(new FlowLayout()); + + addMouseListener(new MouseAdapter() { + @Override + public void mouseReleased(MouseEvent e) { + System.out.println("mouseReleased"); + latch.countDown(); + } + }); + + // Create a Component that will be a child of the Frame and add + // a MouseListener to it. + foo = new Foo(); + foo.setBackground(Color.red); + + System.out.println(foo.getPreferredSize()); + foo.setPreferredSize(new Dimension(350, 200)); + System.out.println(foo.getPreferredSize()); + + foo.addMouseListener(new DummyAdapter()); + + add(foo); + + setSize(400, 400); + setLocationRelativeTo(null); + setVisible(true); + } + + public Rectangle getLightweightComponentBounds() throws Exception { + return Util.invokeOnEDT(() -> { + Point locationOnScreen = foo.getLocationOnScreen(); + Dimension size = foo.getSize(); + return new Rectangle(locationOnScreen.x, locationOnScreen.y, size.width, size.height); + }); + } + + private static class Foo extends Container { + public void paint(Graphics g) { + g.setColor(getBackground()); + g.fillRect(0, 0, getWidth(), getHeight()); + g.setColor(Color.white); + g.drawString(getBounds().toString(), 5, 20); + super.paint(g); + } + } + + private static class DummyAdapter extends MouseAdapter {} + } +} diff --git a/test/jdk/java/awt/event/MouseEvent/MouseEnterTest.java b/test/jdk/java/awt/event/MouseEvent/MouseEnterTest.java new file mode 100644 index 0000000000000..c5203acfdd813 --- /dev/null +++ b/test/jdk/java/awt/event/MouseEvent/MouseEnterTest.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4095172 + * @summary Test for no proper mouse coordinates on MOUSE_ENTER/MOUSE_EXIT events for Win boxes. + * @key headful + * @library /lib/client /java/awt/regtesthelpers + * @build ExtendedRobot Util + * @run main MouseEnterTest + */ + +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; + +import test.java.awt.regtesthelpers.Util; + +public class MouseEnterTest { + private static Frame frame; + private static final TestMouseAdapter mouseAdapter = new TestMouseAdapter(); + + public static void main(String[] args) throws Exception { + EventQueue.invokeAndWait(MouseEnterTest::initAndShowGUI); + try { + test(); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void initAndShowGUI() { + frame = new Frame("MouseEnterTest"); + frame.setLayout(null); + frame.setSize(300, 200); + frame.setLocationRelativeTo(null); + frame.addMouseListener(mouseAdapter); + frame.setVisible(true); + } + + private static void test() throws Exception { + ExtendedRobot robot = new ExtendedRobot(); + robot.waitForIdle(); + robot.delay(500); + + Rectangle bounds = Util.invokeOnEDT(frame::getBounds); + + java.util.List points = getBorderGlidePoints(bounds); + for (int i = 0; i < points.size(); i += 2) { + Point p1 = points.get(i); + Point p2 = points.get(i + 1); + + System.out.println("\n------------------\n"); + + System.out.printf("%s > %s > %s\n", p1, p2, p1); + robot.glide(p1, p2); + robot.waitForIdle(); + robot.glide(p2, p1); + robot.waitForIdle(); + robot.delay(200); + mouseAdapter.testEvents(); + + System.out.println("\n------------------\n"); + + System.out.printf("%s > %s > %s\n", p2, p1, p2); + robot.glide(p2, p1); + robot.waitForIdle(); + robot.glide(p1, p2); + robot.waitForIdle(); + robot.delay(200); + mouseAdapter.testEvents(); + } + } + + private static java.util.List getBorderGlidePoints(Rectangle bounds) { + java.util.List list = new ArrayList<>(); + + int d = 10; + + // left + list.add(new Point(bounds.x - d, bounds.y + bounds.height / 2)); + list.add(new Point(bounds.x + d, bounds.y + bounds.height / 2)); + + // right + list.add(new Point(bounds.x + bounds.width - d, bounds.y + bounds.height / 2)); + list.add(new Point(bounds.x + bounds.width + d, bounds.y + bounds.height / 2)); + + // top + list.add(new Point(bounds.x + bounds.width / 2, bounds.y - d)); + list.add(new Point(bounds.x + bounds.width / 2, bounds.y + d)); + + // bottom + list.add(new Point(bounds.x + bounds.width / 2, bounds.y + bounds.height - d)); + list.add(new Point(bounds.x + bounds.width / 2, bounds.y + bounds.height + d)); + + return list; + } + + private static final class TestMouseAdapter extends MouseAdapter { + private static final int THRESHOLD = 5; + private volatile MouseEvent lastEnteredEvent = null; + private volatile MouseEvent lastExitedEvent = null; + + @Override + public void mouseEntered(MouseEvent e) { + System.out.println("MouseEntered " + e); + lastEnteredEvent = e; + } + + @Override + public void mouseExited(MouseEvent e) { + System.out.println("MouseExited " + e); + lastExitedEvent = e; + } + + public void testEvents() { + if (lastEnteredEvent == null || lastExitedEvent == null) { + throw new RuntimeException("Missing lastEnteredEvent or lastExitedEvent"); + } + + System.out.println("\nTesting:"); + System.out.println(lastEnteredEvent); + System.out.println(lastExitedEvent); + System.out.println(); + + int diffX = Math.abs(lastEnteredEvent.getX() - lastExitedEvent.getX()); + int diffScreenX = Math.abs(lastEnteredEvent.getY() - lastExitedEvent.getY()); + int diffY = Math.abs(lastEnteredEvent.getXOnScreen() - lastExitedEvent.getXOnScreen()); + int diffScreenY = Math.abs(lastEnteredEvent.getYOnScreen() - lastExitedEvent.getYOnScreen()); + + System.out.printf("THRESHOLD %d, diffX %d diffScreenX %d " + + "diffY %d diffScreenY %d\n", + THRESHOLD, + diffX, diffScreenX, + diffY, diffScreenY + ); + + if (diffX > THRESHOLD + || diffScreenX > THRESHOLD + || diffY > THRESHOLD + || diffScreenY > THRESHOLD) { + throw new RuntimeException("Mouse enter vs exit event is too different"); + } + } + } +} diff --git a/test/jdk/java/awt/event/MouseEvent/MouseEventsDuringDrag.java b/test/jdk/java/awt/event/MouseEvent/MouseEventsDuringDrag.java new file mode 100644 index 0000000000000..4a4c9d36af169 --- /dev/null +++ b/test/jdk/java/awt/event/MouseEvent/MouseEventsDuringDrag.java @@ -0,0 +1,307 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4017222 + * @summary Checks whether mouse events are reported correctly during drag. + * @author Stuart Lawrence, Brent Christian: area=event + * @key headful + * @library /lib/client /java/awt/regtesthelpers + * @build ExtendedRobot Util + * @run main MouseEventsDuringDrag + */ + + +/* + * MouseEventsDuringDrag.java + * + * summary: + * On Solaris drag enter/exit events are only reported for the + * component where drag started, they're not reported on other + * components. On Win32 enter/exit events are reported correctly. + */ + +import test.java.awt.regtesthelpers.Util; + +import java.awt.Canvas; +import java.awt.Choice; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Label; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.InputEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +public class MouseEventsDuringDrag { + + private static ExtendedRobot robot; + private static Frame frame; + + private static final MouseHandler mouseHandler = new MouseHandler(); + + static Label lab; + static Canvas c1, c2; + static Choice choice; + + + public static void main(String[] args) throws Exception { + try { + EventQueue.invokeAndWait(MouseEventsDuringDrag::createAndShowGUI); + test(); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void test() throws Exception { + robot = new ExtendedRobot(); + robot.waitForIdle(); + robot.delay(500); + + // Part 1: Press and hold down the mouse button inside the red box. + // Drag the mouse over to the blue box (whilst still holding down + // the mouse button). + // Whilst dragging the mouse from the red box, enter and exit + // events should be reported for the blue box. + testcase(c2, "c1 to c2"); + + // Part 2: Again, press and hold down the mouse button inside the red box. + // This time drag the mouse over to the Choice menu. + // Enter and exit events should be reported for the Choice menu. + testcase(choice, "c1 to choice"); + } + + private static void testcase(Component moveTo, String message) throws Exception { + System.out.println("\ntestcase: " + message); + Rectangle c1bounds = getBounds(c1); + Rectangle moveToBound = getBounds(moveTo); + + Point startDragLocation = + new Point(c1bounds.x + c1bounds.width - 10, + c1bounds.y + c1bounds.height / 2); + + Point endDragLocation = + new Point(moveToBound.x + 10, moveToBound.y + moveToBound.height / 2); + + robot.mouseMove(startDragLocation); + robot.waitForIdle(); + robot.delay(200); + mouseHandler.reset(); + + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + + robot.glide(startDragLocation, endDragLocation); + robot.waitForIdle(); + + robot.glide(endDragLocation.x, endDragLocation.y, endDragLocation.x - 20, endDragLocation.y); + + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + + robot.waitForIdle(); + robot.delay(200); + + List actual = mouseHandler.getRecordedEvents(); + + List expected = List.of( + new EventRecord(MouseEvent.MOUSE_PRESSED, c1), + new EventRecord(MouseEvent.MOUSE_EXITED, c1), + new EventRecord(MouseEvent.MOUSE_ENTERED, moveTo), + new EventRecord(MouseEvent.MOUSE_EXITED, moveTo), + new EventRecord(MouseEvent.MOUSE_RELEASED, c1) + ); + + System.out.println("Expected:\n" + expected); + System.out.println("Actual:\n" + actual); + if (!actual.equals(expected)) { + throw new RuntimeException("Mismatch between expected and actual events\n%s\n%s" + .formatted(expected, actual)); + } + } + + private static Rectangle getBounds(Component c) throws Exception { + return Util.invokeOnEDT(() -> { + Point locationOnScreen = c.getLocationOnScreen(); + Dimension size = c.getSize(); + return new Rectangle(locationOnScreen.x, locationOnScreen.y, size.width, size.height); + }); + } + + private static void createAndShowGUI() { + frame = new Frame(); + MouseHandler mouseHandler = new MouseHandler(); + frame.setLayout(new GridBagLayout()); + + int canvasSize = 100; + c1 = new Canvas(); + c1.setPreferredSize(new Dimension(canvasSize, canvasSize)); + c1.setBackground(Color.red); + c1.addMouseListener(mouseHandler); + + c2 = new Canvas(); + c2.setPreferredSize(new Dimension(canvasSize, canvasSize)); + c2.setBackground(Color.blue); + c2.addMouseListener(mouseHandler); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.insets = new Insets(5, 5, 5, 5); + + gbc.gridx = 0; + gbc.gridy = 0; + frame.add(c1, gbc); + + gbc.gridx = 2; + frame.add(c2, gbc); + + Panel p1 = new Panel(); + p1.setLayout(new FlowLayout()); + choice = new Choice(); + choice.addItem("Choice"); + choice.addItem("One"); + choice.addItem("Two"); + choice.addMouseListener(mouseHandler); + p1.add(choice); + + gbc.gridx = 1; + gbc.gridy = 1; + frame.add(p1, gbc); + + lab = new Label(); + + gbc.gridx = 0; + gbc.gridy = 2; + gbc.gridwidth = 3; + frame.add(lab, gbc); + + frame.pack(); + frame.setLocationRelativeTo(null); + + frame.setVisible(true); + } + + record EventRecord(int eventId, Component component) { + + @Override + public String toString() { + StringBuilder str = new StringBuilder(80); + switch(eventId) { + case MouseEvent.MOUSE_PRESSED: + str.append("MOUSE_PRESSED"); + break; + case MouseEvent.MOUSE_RELEASED: + str.append("MOUSE_RELEASED"); + break; + case MouseEvent.MOUSE_CLICKED: + str.append("MOUSE_CLICKED"); + break; + case MouseEvent.MOUSE_ENTERED: + str.append("MOUSE_ENTERED"); + break; + case MouseEvent.MOUSE_EXITED: + str.append("MOUSE_EXITED"); + break; + case MouseEvent.MOUSE_MOVED: + str.append("MOUSE_MOVED"); + break; + case MouseEvent.MOUSE_DRAGGED: + str.append("MOUSE_DRAGGED"); + break; + case MouseEvent.MOUSE_WHEEL: + str.append("MOUSE_WHEEL"); + break; + default: + str.append("unknown type"); + } + return str.append(" ").append(component).toString(); + } + } + + static class MouseHandler extends MouseAdapter { + static final List list = new CopyOnWriteArrayList<>(); + + public void mousePressed(MouseEvent e) { + list.add(new EventRecord(e.getID(), e.getComponent())); + if (e.getSource() == c1) { + lab.setText("Mouse pressed in red box"); + } else if (e.getSource() == c2) { + lab.setText("Mouse pressed in blue box"); + } else if (e.getSource() == choice) { + lab.setText("Mouse pressed in choice"); + } + } + + public void mouseReleased(MouseEvent e) { + list.add(new EventRecord(e.getID(), e.getComponent())); + lab.setText("Mouse released"); + } + + public void mouseEntered(MouseEvent e) { + list.add(new EventRecord(e.getID(), e.getComponent())); + if (e.getSource() == c1) { + lab.setText("Mouse entered red box"); + } else if (e.getSource() == c2) { + lab.setText("Mouse entered blue box"); + } else if (e.getSource() == choice) { + lab.setText("Mouse entered choice"); + } + } + + public void mouseExited(MouseEvent e) { + list.add(new EventRecord(e.getID(), e.getComponent())); + if (e.getSource() == c1) { + lab.setText("Mouse exited red box"); + } else if (e.getSource() == c2) { + lab.setText("Mouse exited blue box"); + } else if (e.getSource() == choice) { + lab.setText("Mouse exited choice"); + } + } + + public void reset() { + list.clear(); + } + + public List getRecordedEvents() { + return new ArrayList<>(list); + } + } +} diff --git a/test/jdk/java/awt/event/MouseEvent/MouseModifierTest.java b/test/jdk/java/awt/event/MouseEvent/MouseModifierTest.java new file mode 100644 index 0000000000000..5b8f0f62f7b0a --- /dev/null +++ b/test/jdk/java/awt/event/MouseEvent/MouseModifierTest.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4117523 + * @summary Solaris: MousePressed event has modifier=0 when left button is pressed + * @key headful + * @library /javax/swing/regtesthelpers /test/lib + * @build Util jdk.test.lib.Platform + * @run main MouseModifierTest +*/ + + +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import jdk.test.lib.Platform; + +public class MouseModifierTest { + private static Frame frame; + private static volatile MouseEvent lastMousePressedEvent = null; + + public static void main(String[] args) throws Exception { + try { + EventQueue.invokeAndWait(MouseModifierTest::createAndShowGUI); + test(); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void test() throws Exception { + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(500); + + Point centerPoint = Util.getCenterPoint(frame); + + System.out.println("MOUSE1 press case"); + + robot.mouseMove(centerPoint.x, centerPoint.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.delay(25); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(300); + + if (lastMousePressedEvent == null + || lastMousePressedEvent.getModifiers() != InputEvent.BUTTON1_MASK) { + throw new RuntimeException("Test failed"); + } + + if (Platform.isWindows()) { + System.out.println("Windows: Testing ALT + MOUSE1 press case"); + lastMousePressedEvent = null; + robot.waitForIdle(); + robot.delay(300); + + robot.keyPress(KeyEvent.VK_ALT); + robot.delay(25); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.delay(25); + robot.keyRelease(KeyEvent.VK_ALT); + robot.delay(25); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(300); + + int expectedModifiers = InputEvent.BUTTON1_MASK + | InputEvent.BUTTON2_MASK + | InputEvent.ALT_MASK; + if (lastMousePressedEvent == null + || lastMousePressedEvent.getModifiers() != expectedModifiers) { + throw new RuntimeException("Test failed"); + } + } + } + + private static void createAndShowGUI() { + frame = new Frame("MouseModifierTest"); + frame.setSize(300, 300); + frame.setLocationRelativeTo(null); + frame.addMouseListener(new MouseHandler()); + frame.setVisible(true); + } + + private static class MouseHandler extends MouseAdapter { + public void mouseClicked(MouseEvent e) { + System.out.println("\nmouseClicked:"); + printMouseEventDetail(e); + } + + public void mousePressed(MouseEvent e) { + lastMousePressedEvent = e; + System.out.println("\nmousePressed:"); + printMouseEventDetail(e); + } + + public void mouseReleased(MouseEvent e) { + System.out.println("\nmouseReleased:"); + printMouseEventDetail(e); + } + + public void mouseEntered(MouseEvent e) { + System.out.println("\nmouseEntered:"); + printMouseEventDetail(e); + } + + public void mouseExited(MouseEvent e) { + System.out.println("\nmouseExited:"); + printMouseEventDetail(e); + } + + private void printMouseEventDetail(MouseEvent e) { + System.out.println(e.toString()); + System.out.println("Modifiers: "); + printModifiers(e); + } + + private void printModifiers(MouseEvent e) { + if (e == null) { + return; + } + + int mod = e.getModifiers(); + + if ((mod & InputEvent.ALT_MASK) != 0) { + System.out.println("\tALT_MASK"); + } + if ((mod & InputEvent.BUTTON1_MASK) != 0) { + System.out.println("\tBUTTON1_MASK"); + } + if ((mod & InputEvent.BUTTON2_MASK) != 0) { + System.out.println("\tBUTTON2_MASK"); + } + if ((mod & InputEvent.BUTTON3_MASK) != 0) { + System.out.println("\tBUTTON3_MASK"); + } + if ((mod & InputEvent.CTRL_MASK) != 0) { + System.out.println("\tCTRL_MASK"); + } + if ((mod & InputEvent.META_MASK) != 0) { + System.out.println("\tMETA_MASK"); + } + if ((mod & InputEvent.SHIFT_MASK) != 0) { + System.out.println("\tSHIFT_MASK"); + } + } + } +} diff --git a/test/jdk/java/awt/event/MouseEvent/MouseRButTest.java b/test/jdk/java/awt/event/MouseEvent/MouseRButTest.java new file mode 100644 index 0000000000000..e5cb1195bae84 --- /dev/null +++ b/test/jdk/java/awt/event/MouseEvent/MouseRButTest.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4037521 + * @summary Mouse Right button does not send mouseClick action + * @key headful + * @library /javax/swing/regtesthelpers + * @build Util + * @run main MouseRButTest + */ + +import java.awt.Button; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.InputEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class MouseRButTest { + private static Frame frame; + private static Button button; + private static final CountDownLatch latch = new CountDownLatch(1); + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + try { + EventQueue.invokeAndWait(MouseRButTest::createAndShowGUI); + + robot.waitForIdle(); + robot.delay(500); + + Point point = Util.getCenterPoint(button); + robot.mouseMove(point.x, point.y); + robot.waitForIdle(); + robot.mousePress(InputEvent.BUTTON3_DOWN_MASK); + robot.delay(50); + robot.mouseRelease(InputEvent.BUTTON3_DOWN_MASK); + + if (!latch.await(2, TimeUnit.SECONDS)) { + throw new RuntimeException("mouse click action was not sent"); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void createAndShowGUI() { + button = new Button("Click Me"); + button.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + System.out.println(e); + if (e.getModifiers() == e.BUTTON3_MASK) { + System.out.println("right mouse button clicked"); + latch.countDown(); + } + } + }); + + frame = new Frame(); + frame.setLayout(new FlowLayout()); + frame.add(button); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } +} diff --git a/test/jdk/java/awt/event/MouseEvent/TitleBarGetsMousePressed.java b/test/jdk/java/awt/event/MouseEvent/TitleBarGetsMousePressed.java new file mode 100644 index 0000000000000..35f071a9e6da6 --- /dev/null +++ b/test/jdk/java/awt/event/MouseEvent/TitleBarGetsMousePressed.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Frame; +import java.awt.Window; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +/* + * @test + * @bug 4074498 + * @summary Test: MOUSE_PRESSED events in the title bar of a frame + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual TitleBarGetsMousePressed + */ +public class TitleBarGetsMousePressed { + private static final String INSTRUCTIONS = """ + 1. You will see a Frame window next to this window with instructions + 2. Clicking in the title bar of the Frame and even moving around the Frame + should not generate MOUSE_PRESSED / MOUSE_RELEASED / MOUSE_CLICKED events. + (printed below in the log area). + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("TitleBarGetsMousePressed Instructions") + .instructions(INSTRUCTIONS) + .columns(45) + .testUI(TitleBarGetsMousePressed::createTestUI) + .logArea(5) + .build() + .awaitAndCheck(); + } + + private static Window createTestUI() { + Frame frame = new Frame("TitleBarGetsMousePressed"); + frame.setSize(300, 200); + frame.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent ev) { + PassFailJFrame.log("mouseClicked at x:" + ev.getX() + + " y:" + ev.getY()); + } + + public void mousePressed(MouseEvent ev) { + PassFailJFrame.log("mousePressed at x:" + ev.getX() + + " y:" + ev.getY()); + } + + public void mouseReleased(MouseEvent ev) { + PassFailJFrame.log("mouseReleased at x:" + ev.getX() + + " y:" + ev.getY()); + } + }); + + return frame; + } +} diff --git a/test/jdk/java/awt/event/MouseWheelEvent/HWWheelScroll.java b/test/jdk/java/awt/event/MouseWheelEvent/HWWheelScroll.java new file mode 100644 index 0000000000000..2851ff668fe6e --- /dev/null +++ b/test/jdk/java/awt/event/MouseWheelEvent/HWWheelScroll.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4425654 + * @summary Test wheel scrolling of heavyweight components + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual HWWheelScroll + */ + +import java.awt.Choice; +import java.awt.FileDialog; +import java.awt.Frame; +import java.awt.List; +import java.awt.TextArea; +import java.awt.Window; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; + +public class HWWheelScroll { + public static final int TEXT_TALL = 0; + public static final int TEXT_WIDE = 1; + public static final int TEXT_SMALL = 2; + public static final int TEXT_BIG = 3; + static String INSTRUCTIONS = """ + Test for mouse wheel scrolling of heavyweight components with built-in + scrollbars or similar functionality that is controlled by guestures + such as Apple Magic Mouse or trackpad scrolling guesture. + Several windows containing either a TextArea, List, Choice, or a + FileDialog will appear. For each window, use the mouse wheel to + scroll its content, and then minimize it or move away + and continue with the next window. + Do not close any of the opened windows except the FileDialog. + For the FileDialog, first change to a directory with enough items that a + scrollbar appears. + Some of the other windows don't have enough text to warrant scrollbars, + but should be tested anyway to make sure no crash or hang occurs. + If all scrollbars scroll correctly, press "Pass", otherwise press "Fail". + """; + + public static ArrayList initUI() { + ArrayList retValue = new ArrayList<>(); + retValue.add(makeTextFrame(TextArea.SCROLLBARS_BOTH, TEXT_BIG)); + retValue.add(makeTextFrame(TextArea.SCROLLBARS_BOTH, TEXT_TALL)); + retValue.add(makeTextFrame(TextArea.SCROLLBARS_BOTH, TEXT_SMALL)); + retValue.add(makeTextFrame(TextArea.SCROLLBARS_BOTH, TEXT_WIDE)); + retValue.add(makeTextFrame(TextArea.SCROLLBARS_VERTICAL_ONLY, TEXT_TALL)); + retValue.add(makeTextFrame(TextArea.SCROLLBARS_VERTICAL_ONLY, TEXT_SMALL)); + retValue.add(makeTextFrame(TextArea.SCROLLBARS_HORIZONTAL_ONLY, TEXT_SMALL)); + retValue.add(makeTextFrame(TextArea.SCROLLBARS_HORIZONTAL_ONLY, TEXT_WIDE)); + retValue.add(makeListFrame(TEXT_TALL)); + retValue.add(makeListFrame(TEXT_WIDE)); + retValue.add(makeListFrame(TEXT_SMALL)); + Frame f = new Frame("File Dialog Owner"); + f.setSize(150, 150); + f.setLocationRelativeTo(null); + FileDialog fd = new FileDialog(f, "FileDialog"); + fd.setDirectory("."); + retValue.add(fd); + retValue.add(f); + Frame choiceFrame = new Frame("Choice"); + Choice c = new Choice(); + for (int i = 0; i < 50; i++) { + c.add(i + " choice item"); + } + choiceFrame.add(c); + choiceFrame.setSize(150, 150); + choiceFrame.setLocationRelativeTo(null); + retValue.add(choiceFrame); + return retValue; + } + + public static Frame makeTextFrame(int policy, int textShape) { + Frame f = new Frame("TextArea"); + f.add(makeTextArea(policy, textShape)); + f.setSize(150, 150); + f.setLocationRelativeTo(null); + return f; + } + + public static Frame makeListFrame(int textShape) { + Frame f = new Frame("List"); + f.add(makeList(textShape)); + f.setSize(150, 150); + f.setLocationRelativeTo(null); + return f; + } + + public static TextArea makeTextArea(int policy, int textShape) { + TextArea ta = new TextArea("", 0, 0, policy); + if (textShape == TEXT_TALL) { + for (int i = 0; i < 50 ; i++) { + ta.append(i + "\n"); + } + } else if (textShape == TEXT_WIDE) { + for (int i = 0; i < 2; i++) { + ta.append(i + "very, very, very, very, very, very, very, long line of text number\n"); + } + } else if (textShape == TEXT_SMALL) { + ta.append("text"); + } else if (textShape == TEXT_BIG) { + for (int i = 0; i < 50 ; i++) { + ta.append(i + "very, very, very, very, very, very, very, long line of text number\n"); + } + } + return ta; + } + + public static List makeList(int textShape) { + java.awt.List l = new java.awt.List(); + if (textShape == TEXT_TALL) { + for (int i = 0; i < 50 ; i++) { + l.add(" " + i + " "); + } + } else if (textShape == TEXT_WIDE) { + for (int i = 0; i < 2 ; i++) { + l.add(i + "very, very, very, very, very, very, very, long line of text number"); + } + } else if (textShape == TEXT_SMALL) { + l.add("text"); + } else if (textShape == TEXT_BIG) { + for (int i = 0; i < 50 ; i++) { + l.add(i + "very, very, very, very, very, very, very, long line of text number"); + } + } + return l; + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .logArea(10) + .testUI(HWWheelScroll::initUI) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/event/MouseWheelEvent/WheelEventCoord.java b/test/jdk/java/awt/event/MouseWheelEvent/WheelEventCoord.java new file mode 100644 index 0000000000000..418151d3798f4 --- /dev/null +++ b/test/jdk/java/awt/event/MouseWheelEvent/WheelEventCoord.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4492456 + * @summary MouseWheelEvent coordinates are wrong + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual WheelEventCoord + */ + +import java.awt.Button; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.GridLayout; +import java.lang.reflect.InvocationTargetException; + +public class WheelEventCoord extends Frame { + static String INSTRUCTIONS = """ + This test requires mouse with scrolling wheel or device, + that has capability to simulate scrolling wheel with gestures + such as Apple mouse or a trackpad with gesture control. + If you do not have such device press "Pass". + Move mouse to the top of the button named "Button 1". + While constantly turning mouse wheel up and down slowly move + mouse cursor until it reaches bottom of the button named "Button 3". + While doing so look at the log area. + If despite the wheel direction y coordinate is steadily increases + as you move the mouse down press "Pass". + If y coordinate decreases when cursor is moving down or suddenly jumps + by more than 50 points when crossing to another button press "Fail". + """; + + public WheelEventCoord() { + super("Wheel Event Coordinates"); + setLayout(new GridLayout(3, 1)); + + add(new BigButton("Button 1")); + add(new BigButton("Button 2")); + add(new BigButton("Button 3")); + + addMouseWheelListener(e -> PassFailJFrame.log("Mouse y coordinate = " + e.getY())); + pack(); + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Wheel Event Coordinates Instructions") + .instructions(INSTRUCTIONS) + .logArea(10) + .testUI(WheelEventCoord::new) + .build() + .awaitAndCheck(); + } +} + +class BigButton extends Button { + public BigButton(String label) { + super(label); + } + + public Dimension getPreferredSize() { + return new Dimension(300, 100); + } + + public Dimension getMinimumSize() { + return getPreferredSize(); + } + + public Dimension getMaximumSize() { + return getPreferredSize(); + } +} diff --git a/test/jdk/java/awt/event/MouseWheelEvent/WheelScrollEnabled.java b/test/jdk/java/awt/event/MouseWheelEvent/WheelScrollEnabled.java new file mode 100644 index 0000000000000..cbdd7676e94ad --- /dev/null +++ b/test/jdk/java/awt/event/MouseWheelEvent/WheelScrollEnabled.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4372477 + * @summary Test disabling of wheel scrolling + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual WheelScrollEnabled + */ + +import java.awt.BorderLayout; +import java.awt.Checkbox; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Label; +import java.awt.Panel; +import java.awt.ScrollPane; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.lang.reflect.InvocationTargetException; + +public class WheelScrollEnabled extends Frame { + static String INSTRUCTIONS = """ + This test requires mouse with a scrolling wheel or + device that is able to simulate scrolling using gestures. + If you do not have such device press "Pass" to skip testing. + You should see a ScrollPane with some labels in it and two checkboxes. + For each of the four combinations of the two checkboxes, + move the cursor over the ScrollPane and rotate the mouse wheel. + When (and ONLY when) the 'WheelListener added' checkbox is checked, + scrolling the mouse wheel should produce a text message in the log area. + When (and ONLY when) the 'Wheel scrolling enabled' checkbox is checked, + the ScrollPane should scroll when mouse wheel is scrolled on top of it. + If all four checkbox combinations work properly press "Pass", + otherwise press "Fail". + """; + MouseWheelListener mwl; + Checkbox cb; + Checkbox cb2; + ScrollPane sp; + + public WheelScrollEnabled() { + setLayout(new BorderLayout()); + Panel pnl = new Panel(); + pnl.setLayout(new GridLayout(10, 10)); + for (int i = 0; i < 100; i++) { + pnl.add(new Label("Label " + i)); + } + sp = new ScrollPane(); + sp.add(pnl); + sp.setWheelScrollingEnabled(false); + mwl = new MouseWheelListener() { + int i; + @Override + public void mouseWheelMoved(MouseWheelEvent e) { + PassFailJFrame.log("mouseWheelMoved " + i++); + } + }; + sp.addMouseWheelListener(mwl); + add(sp, BorderLayout.CENTER); + + Panel pl2 = new Panel(); + ItemListener il = new ControlListener(); + + cb = new Checkbox("WheelListener added", true); + cb.addItemListener(il); + pl2.add(cb); + + cb2 = new Checkbox("Wheel scrolling enabled", false); + cb2.addItemListener(il); + pl2.add(cb2); + + add(pl2, BorderLayout.SOUTH); + setSize(400, 200); + } + + class ControlListener implements ItemListener { + public void itemStateChanged(ItemEvent e) { + if (e.getSource() == cb) { + boolean state = cb.getState(); + if (state) { + sp.addMouseWheelListener(mwl); + } + else { + sp.removeMouseWheelListener(mwl); + } + } + if (e.getSource() == cb2) { + sp.setWheelScrollingEnabled(cb2.getState()); + } + } + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Wheel Scroll Enabled Instructions") + .instructions(INSTRUCTIONS) + .logArea(10) + .testUI(WheelScrollEnabled::new) + .build() + .awaitAndCheck(); + } +} + diff --git a/test/jdk/java/awt/event/PaintEvent/RepaintTest.java b/test/jdk/java/awt/event/PaintEvent/RepaintTest.java new file mode 100644 index 0000000000000..caeeaf7d5a8e8 --- /dev/null +++ b/test/jdk/java/awt/event/PaintEvent/RepaintTest.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.FontMetrics; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Panel; +import java.awt.Robot; +import java.util.concurrent.atomic.AtomicInteger; + +/* + * @test + * @bug 4394287 + * @key headful + * @summary Paint pending on heavyweight component move + */ + +public class RepaintTest { + private static Frame frame; + private static Panel panel; + private static volatile IncrementComponent counter; + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + EventQueue.invokeAndWait(RepaintTest::createAndShowUI); + robot.waitForIdle(); + robot.delay(1000); + + EventQueue.invokeAndWait(() -> panel.setLocation(panel.getX() + 10, + panel.getY() + 10)); + robot.waitForIdle(); + robot.delay(500); + + int count = counter.getCount().get(); + + EventQueue.invokeAndWait(panel::repaint); + robot.waitForIdle(); + robot.delay(1000); + + if (counter.getCount().get() == count) { + throw new RuntimeException("Failed"); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void createAndShowUI() { + frame = new MyFrame("Repaint Test"); + frame.setLayout(null); + + counter = new IncrementComponent(); + panel = new Panel(); + panel.add(counter); + frame.add(panel); + panel.setBounds(0, 0, 100, 100); + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private static class MyFrame extends Frame { + + public MyFrame(String title) { + super(title); + } + + public void update(Graphics g) { + System.out.println("UPDATE: " + g.getClipBounds()); + super.update(g); + } + + public void paint(Graphics g) { + System.out.println("PAINT: " + g.getClipBounds()); + super.paint(g); + } + } + + // Subclass of Component, everytime paint is invoked a counter + // is incremented, this counter is displayed in the component. + private static class IncrementComponent extends Component { + private static final AtomicInteger paintCount = new AtomicInteger(0); + + public Dimension getPreferredSize() { + return new Dimension(100, 100); + } + + public AtomicInteger getCount() { + return paintCount; + } + + public void paint(Graphics g) { + g.setColor(Color.red); + g.fillRect(0, 0, getWidth(), getHeight()); + g.setColor(Color.white); + + String string = Integer.toString(paintCount.getAndIncrement()); + FontMetrics metrics = g.getFontMetrics(); + int x = (getWidth() - metrics.stringWidth(string)) / 2; + int y = (getHeight() + metrics.getHeight()) / 2; + g.drawString(string, x, y); + } + } +} diff --git a/test/jdk/java/awt/event/StressTest/LargeAWTEventMulticasterTest.java b/test/jdk/java/awt/event/StressTest/LargeAWTEventMulticasterTest.java new file mode 100644 index 0000000000000..61e7a5892a7f2 --- /dev/null +++ b/test/jdk/java/awt/event/StressTest/LargeAWTEventMulticasterTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; +import java.awt.AWTEventMulticaster; + +/* + * @test + * @bug 8342782 + * @summary Tests large AWTEventMulticasters for StackOverflowErrors + * @run main LargeAWTEventMulticasterTest + */ +public class LargeAWTEventMulticasterTest { + + /** + * This is an empty ActionListener that also has a numeric index. + */ + static class IndexedActionListener implements ActionListener { + private final int index; + + public IndexedActionListener(int index) { + this.index = index; + } + + @Override + public void actionPerformed(ActionEvent e) { + + } + + public int getIndex() { + return index; + } + + @Override + public String toString() { + return Integer.toString(index); + } + } + + public static void main(String[] args) { + int maxA = 0; + try { + for (int a = 1; a < 200_000; a *= 2) { + maxA = a; + testAddingActionListener(a); + } + } finally { + System.out.println("maximum a = " + maxA); + } + } + + private static void testAddingActionListener(int numberOfListeners) { + // step 1: create the large AWTEventMulticaster + ActionListener l = null; + for (int a = 0; a < numberOfListeners; a++) { + l = AWTEventMulticaster.add(l, new IndexedActionListener(a)); + } + + // Prior to 8342782 we could CREATE a large AWTEventMulticaster, but we couldn't + // always interact with it. + + // step 2: dispatch an event + // Here we're making sure we don't get a StackOverflowError when we traverse the tree: + l.actionPerformed(null); + + // step 3: make sure getListeners() returns elements in the correct order + // The resolution for 8342782 introduced a `rebalance` method; we want to + // double-check that the rebalanced tree preserves the appropriate order. + IndexedActionListener[] array = AWTEventMulticaster.getListeners(l, IndexedActionListener.class); + for (int b = 0; b < array.length; b++) { + if (b != array[b].getIndex()) + throw new Error("the listeners are in the wrong order. " + b + " != " + array[b].getIndex()); + } + } +} diff --git a/test/jdk/java/awt/event/WindowActivatedEventTest.java b/test/jdk/java/awt/event/WindowActivatedEventTest.java new file mode 100644 index 0000000000000..5ced411e64aef --- /dev/null +++ b/test/jdk/java/awt/event/WindowActivatedEventTest.java @@ -0,0 +1,169 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4219344 + * @summary tests that WINDOW_ACTIVATED events are generated properly + * @key headful + * @library /test/jdk/java/awt/regtesthelpers + * @build Util + * @run main WindowActivatedEventTest + */ + +import test.java.awt.regtesthelpers.Util; + +import java.awt.Dialog; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Robot; +import java.awt.Window; +import java.awt.event.WindowEvent; +import java.awt.event.WindowAdapter; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicInteger; + +public class WindowActivatedEventTest { + + static Robot robot; + static Frame frame; + static Dialog dialog; + + public static void main(String[] args) throws Exception { + robot = new Robot(); + + try { + EventQueue.invokeAndWait(WindowActivatedEventTest::createAndShowGUI); + robot.waitForIdle(); + robot.delay(500); + + Util.clickOnComp(dialog, robot); + + robot.waitForIdle(); + robot.delay(500); + + for (int i = 0; i < 3 ; i++) { + clickAndCheck(frame); + clickAndCheck(dialog); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + if (dialog != null) { + dialog.dispose(); + } + }); + } + } + + private static void clickAndCheck(Window windowToFocus) + throws InterruptedException, InvocationTargetException { + Window oppositeWindow = (windowToFocus == frame) ? dialog : frame; + + System.out.println("Clicking on " + windowToFocus); + + EventQueue.invokeAndWait(() -> { + if (windowToFocus.isFocused() || !oppositeWindow.isFocused()) { + throw new RuntimeException("%s isFocused %b, %s isFocused %b".formatted( + windowToFocus.getName(), windowToFocus.isFocused(), + oppositeWindow.getName(), oppositeWindow.isFocused() + )); + } + }); + + WindowEventLogger windowLogger = WindowEventLogger.getFromWindow(windowToFocus); + WindowEventLogger oppositeWindowLogger = WindowEventLogger.getFromWindow(oppositeWindow); + + windowLogger.resetCounters(); + oppositeWindowLogger.resetCounters(); + + Util.clickOnComp(windowToFocus, robot); + + robot.delay(500); + + int windowActivatedCount = windowLogger.activatedCount.get(); + int windowDeactivatedCount = windowLogger.deactivatedCount.get(); + int oppositeWindowActivatedCount = oppositeWindowLogger.activatedCount.get(); + int oppositeWindowDeactivatedCount = oppositeWindowLogger.deactivatedCount.get(); + + if (windowActivatedCount != 1 + || windowDeactivatedCount != 0 + || oppositeWindowActivatedCount != 0 + || oppositeWindowDeactivatedCount != 1) { + throw new RuntimeException( + "Invalid activated/deactivated count: %s (%d/%d) / %s (%d/%d)" + .formatted( + windowToFocus.getName(), + windowActivatedCount, + windowDeactivatedCount, + oppositeWindow.getName(), + oppositeWindowActivatedCount, + oppositeWindowDeactivatedCount + )); + } + } + + private static void createAndShowGUI() { + frame = new Frame("frame WindowActivatedEventTest"); + dialog = new Dialog(frame, "dialog WindowActivatedEventTest"); + + frame.addWindowListener(new WindowEventLogger()); + dialog.addWindowListener(new WindowEventLogger()); + + frame.setBounds(400, 0, 200, 200); + frame.setVisible(true); + + dialog.setBounds(400, 200, 200, 200); + dialog.setVisible(true); + } + + private static class WindowEventLogger extends WindowAdapter { + final AtomicInteger activatedCount = new AtomicInteger(0); + final AtomicInteger deactivatedCount = new AtomicInteger(0); + + public void windowActivated(WindowEvent e) { + activatedCount.incrementAndGet(); + System.out.println(e); + } + + public void windowDeactivated(WindowEvent e) { + deactivatedCount.incrementAndGet(); + System.out.println(e); + } + + public void resetCounters() { + activatedCount.set(0); + deactivatedCount.set(0); + } + + public static WindowEventLogger getFromWindow(Window window) { + return (WindowEventLogger) Arrays + .stream(window.getWindowListeners()) + .filter(listener -> listener instanceof WindowEventLogger) + .findFirst().get(); + } + } +} diff --git a/test/jdk/java/awt/font/FontNames/LocaleFamilyNames.java b/test/jdk/java/awt/font/FontNames/LocaleFamilyNames.java index 5356464334e2d..0fc27ea3de03e 100644 --- a/test/jdk/java/awt/font/FontNames/LocaleFamilyNames.java +++ b/test/jdk/java/awt/font/FontNames/LocaleFamilyNames.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,29 +26,32 @@ * @bug 4935798 6521210 6901159 * @summary Tests that all family names that are reported in all locales * correspond to some font returned from getAllFonts(). - * @run main LocaleFamilyNames + * @run main/othervm/timeout=360 LocaleFamilyNames */ import java.awt.*; import java.util.*; public class LocaleFamilyNames { public static void main(String[] args) throws Exception { + System.out.println("Start time: " + java.time.LocalDateTime.now()); GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); - Font[] all_fonts = ge.getAllFonts(); - Locale[] all_locales = Locale.getAvailableLocales(); + System.out.println("Number of fonts: " + all_fonts.length); + System.out.println("Number of locales: " + all_locales.length); + HashSet all_families = new HashSet(); for (int i=0; i map = new HashMap<>(); + map.put(TextAttribute.FONT, font); + layouts[0][0] = new TextLayout(text, map, frc); + AttributedCharacterIterator iter = new AttributedString(text, map).getIterator(); + layouts[0][1] = new LineBreakMeasurer(iter, frc).nextLayout(Float.MAX_VALUE); + + NumericShaper arabic = NumericShaper.getShaper(NumericShaper.ARABIC); + map.put(TextAttribute.NUMERIC_SHAPING, arabic); + layouts[1][0] = new TextLayout(text, map, frc); + iter = new AttributedString(text, map).getIterator(); + layouts[1][1] = new LineBreakMeasurer(iter, frc).nextLayout(Float.MAX_VALUE); + + NumericShaper contextualArabic = NumericShaper.getContextualShaper(NumericShaper.ARABIC, NumericShaper.ARABIC); + map.put(TextAttribute.NUMERIC_SHAPING, contextualArabic); + layouts[2][0] = new TextLayout(text, map, frc); + iter = new AttributedString(text, map).getIterator(); + layouts[2][1] = new LineBreakMeasurer(iter, frc).nextLayout(Float.MAX_VALUE); + + NumericShaper contextualArabicASCII = NumericShaper.getContextualShaper(NumericShaper.ARABIC); + map.put(TextAttribute.NUMERIC_SHAPING, contextualArabicASCII); + layouts[3][0] = new TextLayout(text, map, frc); + iter = new AttributedString(text, map).getIterator(); + layouts[3][1] = new LineBreakMeasurer(iter, frc).nextLayout(Float.MAX_VALUE); + + NumericShaper contextualAll = NumericShaper.getContextualShaper(NumericShaper.ALL_RANGES); + map.put(TextAttribute.NUMERIC_SHAPING, contextualAll); + layouts[4][0] = new TextLayout(text, map, frc); + iter = new AttributedString(text, map).getIterator(); + layouts[4][1] = new LineBreakMeasurer(iter, frc).nextLayout(Float.MAX_VALUE); + + titles = new String[]{ + "plain -- all digits ASCII", + "Arabic -- all digits Arabic", + "contextual Arabic default Arabic -- only leading digits and digits following Arabic text are Arabic", + "contextual Arabic default ASCII -- only digits following Arabic text are Arabic", + "contextual all default ASCII -- leading digits english, others correspond to context" + }; + } + + @Override + public void paint(Graphics g) { + Graphics2D g2d = (Graphics2D) g; + + float x = 5; + float y = 5; + + for (int i = 0; i < layouts.length; ++i) { + y += 18; + g2d.drawString(titles[i], x, y); + y += 4; + + for (int j = 0; j < 2; ++j) { + y += layouts[i][j].getAscent(); + layouts[i][j].draw(g2d, x, y); + y += layouts[i][j].getDescent() + layouts[i][j].getLeading(); + } + } + } +} diff --git a/test/jdk/java/awt/font/TextLayout/MyanmarTextTest.java b/test/jdk/java/awt/font/TextLayout/MyanmarTextTest.java index 32c748ce80f56..20db89ff0c659 100644 --- a/test/jdk/java/awt/font/TextLayout/MyanmarTextTest.java +++ b/test/jdk/java/awt/font/TextLayout/MyanmarTextTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,12 +27,15 @@ * @key headful * @summary Verifies that Myanmar script is rendered correctly: * two characters combined into one glyph + * @library /test/lib + * @build jtreg.SkippedException * @run main MyanmarTextTest */ import java.awt.Font; import java.awt.GraphicsEnvironment; import java.util.Arrays; +import java.util.List; import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.JFrame; @@ -45,12 +48,16 @@ import javax.swing.text.BadLocationException; import javax.swing.text.Position; +import jtreg.SkippedException; + public class MyanmarTextTest { private static final String TEXT = "\u1000\u103C"; - private static final String FONT_WINDOWS = "Myanmar Text"; - private static final String FONT_LINUX = "Padauk"; - private static final String FONT_MACOS = "Myanmar MN"; + private static final List FONT_CANDIDATES = + List.of("Myanmar MN", + "Padauk", + "Myanmar Text", + "Noto Sans Myanmar"); private static final String FONT_NAME = selectFontName(); @@ -61,12 +68,8 @@ public class MyanmarTextTest { public static void main(String[] args) throws Exception { if (FONT_NAME == null) { - System.err.println("Unsupported OS: exiting"); - return; - } - if (!fontExists()) { - System.err.println("Required font is not installed: " + FONT_NAME); - return; + throw new SkippedException("No suitable font found out of the list: " + + String.join(", ", FONT_CANDIDATES)); } try { @@ -130,22 +133,12 @@ private void checkPositions() { } private static String selectFontName() { - String osName = System.getProperty("os.name").toLowerCase(); - if (osName.contains("windows")) { - return FONT_WINDOWS; - } else if (osName.contains("linux")) { - return FONT_LINUX; - } else if (osName.contains("mac")) { - return FONT_MACOS; - } else { - return null; - } + return Arrays.stream(GraphicsEnvironment + .getLocalGraphicsEnvironment() + .getAvailableFontFamilyNames()) + .filter(FONT_CANDIDATES::contains) + .findFirst() + .orElse(null); } - private static boolean fontExists() { - String[] fontFamilyNames = GraphicsEnvironment - .getLocalGraphicsEnvironment() - .getAvailableFontFamilyNames(); - return Arrays.asList(fontFamilyNames).contains(FONT_NAME); - } } diff --git a/test/jdk/java/awt/font/TextLayout/TestGASPHint.java b/test/jdk/java/awt/font/TextLayout/TestGASPHint.java new file mode 100644 index 0000000000000..e7caeb8eabf60 --- /dev/null +++ b/test/jdk/java/awt/font/TextLayout/TestGASPHint.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsEnvironment; +import java.awt.RenderingHints; + +import javax.swing.JPanel; + +/* + * @test + * @bug 6320502 + * @summary Display laid out text which substitutes invisible glyphs correctly. + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame jtreg.SkippedException + * @run main/manual TestGASPHint + */ + +public class TestGASPHint extends JPanel { + private static final String text = "\u0905\u0901\u0917\u094d\u0930\u0947\u091c\u093c\u0940"; + private static final Font font = getPhysicalFontForText(text, Font.PLAIN, 36); + + public static void main(String[] args) throws Exception { + if (font == null) { + throw new jtreg.SkippedException("No Devanagari font found. Test Skipped"); + } + + final String INSTRUCTIONS = """ + A short piece of Devanagari text should appear without any + artifacts. In particular there should be no "empty rectangles" + representing the missing glyph. + + If the above condition is true, press Pass, else Fail."""; + + PassFailJFrame.builder() + .title("TestGASPHint Instruction") + .instructions(INSTRUCTIONS) + .columns(32) + .splitUI(TestGASPHint::new) + .build() + .awaitAndCheck(); + } + + @Override + public Dimension getPreferredSize() { + return new Dimension(200, 200); + } + + @Override + public void paint(Graphics g) { + Graphics2D g2d = (Graphics2D) g; + + g2d.setColor(Color.WHITE); + g2d.fillRect(0, 0, getWidth(), getHeight()); + + g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_GASP); + + g2d.setFont(font); + g2d.setColor(Color.BLACK); + g2d.drawString(text, 10, 50); + } + + /* + * Searches the available system fonts for a font which can display all the + * glyphs in the input text correctly. Returns null, if not found. + */ + private static Font getPhysicalFontForText(String text, int style, int size) { + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + String[] names = ge.getAvailableFontFamilyNames(); + + for (String n : names) { + switch (n.toLowerCase()) { + case "dialog": + case "dialoginput": + case "serif": + case "sansserif": + case "monospaced": + break; + default: + Font f = new Font(n, style, size); + if (f.canDisplayUpTo(text) == -1) { + return f; + } + } + } + return null; + } +} diff --git a/test/jdk/java/awt/font/TextLayout/TestSelection.java b/test/jdk/java/awt/font/TextLayout/TestSelection.java new file mode 100644 index 0000000000000..4a1a74994dfe1 --- /dev/null +++ b/test/jdk/java/awt/font/TextLayout/TestSelection.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.font.FontRenderContext; +import java.awt.font.TextAttribute; +import java.awt.font.TextLayout; +import java.text.AttributedString; + +import javax.swing.JPanel; + +/* + * @test + * @bug 4221422 + * @summary Display several TextLayouts with various selections. + * All the selections should be between non-italic and italic text, + * and the top and bottom of the selection region should be horizontal. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual TestSelection + */ + +public final class TestSelection extends JPanel { + private static final float MARGIN = 20; + + public static void main(String[] args) throws Exception { + final String INSTRUCTIONS = """ + Several TextLayouts are displayed along with selections. + The selection regions should have horizontal top and bottom segments. + + If above condition is true, press Pass else Fail."""; + + PassFailJFrame.builder() + .title("TestSelection Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .splitUI(TestSelection::new) + .build() + .awaitAndCheck(); + } + + @Override + public Dimension getPreferredSize() { + return new Dimension(300, 300); + } + + private float drawSelectionAndLayout(Graphics2D g2d, + TextLayout layout, + float y, + int selStart, + int selLimit) { + Color selectionColor = Color.PINK; + Color textColor = Color.BLACK; + + y += layout.getAscent(); + + g2d.translate(MARGIN, y); + Shape hl = layout.getLogicalHighlightShape(selStart, selLimit); + g2d.setColor(selectionColor); + g2d.fill(hl); + g2d.setColor(textColor); + layout.draw(g2d, 0, 0); + g2d.translate(-MARGIN, -y); + + y += layout.getDescent() + layout.getLeading() + 10; + return y; + } + + @Override + public void paint(Graphics g) { + String text = "Hello world"; + + Graphics2D g2d = (Graphics2D) g; + g2d.setColor(Color.WHITE); + g2d.fillRect(0, 0, getWidth(), getHeight()); + + AttributedString attrStr = new AttributedString(text); + + FontRenderContext frc = g2d.getFontRenderContext(); + + final int midPoint = text.indexOf('w'); + final int selStart = midPoint / 2; + final int selLimit = text.length() - selStart; + final Font italic = new Font(Font.SANS_SERIF, Font.ITALIC, 24); + + float y = MARGIN; + + attrStr.addAttribute(TextAttribute.FONT, italic, 0, midPoint); + TextLayout layout = new TextLayout(attrStr.getIterator(), frc); + + y = drawSelectionAndLayout(g2d, layout, y, selStart - 1, selLimit); + y = drawSelectionAndLayout(g2d, layout, y, selStart, selLimit); + y = drawSelectionAndLayout(g2d, layout, y, selStart + 1, selLimit); + + attrStr = new AttributedString(text); + attrStr.addAttribute(TextAttribute.FONT, + italic, midPoint, text.length()); + layout = new TextLayout(attrStr.getIterator(), frc); + + y = drawSelectionAndLayout(g2d, layout, y, selStart, selLimit); + + attrStr = new AttributedString(text); + attrStr.addAttribute(TextAttribute.FONT, italic, 0, midPoint); + attrStr.addAttribute(TextAttribute.SIZE, 48f, midPoint, text.length()); + layout = new TextLayout(attrStr.getIterator(), frc); + + y = drawSelectionAndLayout(g2d, layout, y, selStart, selLimit); + } +} diff --git a/test/jdk/java/awt/font/TextLayout/TestStrikethrough.java b/test/jdk/java/awt/font/TextLayout/TestStrikethrough.java new file mode 100644 index 0000000000000..eff16d5aa6420 --- /dev/null +++ b/test/jdk/java/awt/font/TextLayout/TestStrikethrough.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.font.FontRenderContext; +import java.awt.font.TextAttribute; +import java.awt.font.TextLayout; +import java.text.AttributedCharacterIterator; +import java.text.AttributedString; + +import javax.swing.JPanel; + +/* + * @test + * @bug 6426360 + * @summary Display a TextLayout with strikethrough at a number of + * different offsets relative to the pixel grid. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual TestStrikethrough + */ + +public class TestStrikethrough extends JPanel { + + public static void main(String[] args) throws Exception { + final String INSTRUCTIONS = """ + Display text with strikethrough at a number of different positions. + + Press Fail if any line is missing a strikethrough else press Pass."""; + + PassFailJFrame.builder() + .title("TestStrikethrough Instruction") + .instructions(INSTRUCTIONS) + .columns(35) + .splitUI(TestStrikethrough::new) + .build() + .awaitAndCheck(); + } + + @Override + public Dimension getPreferredSize() { + return new Dimension(200, 120); + } + + @Override + public void paint(Graphics aContext) { + Graphics2D g2d = (Graphics2D) aContext; + + g2d.setColor(Color.WHITE); + g2d.fillRect(0, 0, getWidth(), getHeight()); + + Font font = new Font(Font.DIALOG, Font.PLAIN, 9); + FontRenderContext frc = g2d.getFontRenderContext(); + String str = "Where is the strikethrough?"; + AttributedString as = new AttributedString(str); + as.addAttribute(TextAttribute.FONT, font); + as.addAttribute(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON); + AttributedCharacterIterator aci = as.getIterator(); + TextLayout tl = new TextLayout(aci, frc); + float delta = (float) (Math.ceil(tl.getAscent() + tl.getDescent() + tl.getLeading()) + .1); + float y = delta - .1f; + g2d.setColor(Color.BLACK); + for (int i = 0; i < 11; ++i) { + tl.draw(g2d, 10f, y); + y += delta; + } + } +} diff --git a/test/jdk/java/awt/geom/Arc2D/Arc2DHitTest.java b/test/jdk/java/awt/geom/Arc2D/Arc2DHitTest.java new file mode 100644 index 0000000000000..de6b39d5c6f7f --- /dev/null +++ b/test/jdk/java/awt/geom/Arc2D/Arc2DHitTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4178123 + * @summary Verifies that the Arc2D.contains(point) methods work correctly. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual Arc2DHitTest + */ + +import java.awt.Color; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Panel; +import java.awt.Point; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.geom.Arc2D; + +public class Arc2DHitTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This test displays an arc figure and lets the user click on it. + The arc will initially be drawn in red and only when the user clicks + within the arc in the window it will be redrawn into green otherwise + it should stay red. + + For convenience, the point being tested is drawn in black. Note + that rounding in the arc rendering routines may cause points near + the boundary of the arc to render incorrectly. Allow for a pixel + or two of leeway near the boundary. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(initialize()) + .build() + .awaitAndCheck(); + } + private static Frame initialize() { + Frame f = new Frame("Arc2DHitTest"); + ArcHitPanel panel = new ArcHitPanel(); + f.add(panel); + f.setSize(300, 250); + return f; + } +} + +class ArcHitPanel extends Panel { + private Arc2D arc; + private Point hit; + public ArcHitPanel() { + arc = new Arc2D.Float(10, 10, 100, 100, 0, 120, Arc2D.PIE); + this.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + hit = e.getPoint(); + repaint(); + } + }); + } + + @Override + public void paint(Graphics g) { + Graphics2D g2 = (Graphics2D) g; + g2.setColor(Color.white); + g2.fill(g2.getClipBounds()); + g2.setColor((hit != null && arc.contains(hit)) + ? Color.green : Color.red); + g2.fill(arc); + if (hit != null) { + g2.setColor(Color.black); + g2.fillRect(hit.x, hit.y, 1, 1); + } + } +} diff --git a/test/jdk/java/awt/geom/Arc2D/BoundsBug.java b/test/jdk/java/awt/geom/Arc2D/BoundsBug.java new file mode 100644 index 0000000000000..a17f45f9dad37 --- /dev/null +++ b/test/jdk/java/awt/geom/Arc2D/BoundsBug.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4197746 + * @summary Verifies that the getBounds2D method of Arc2D returns the + * correct result. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual BoundsBug + */ + +import java.awt.Color; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Panel; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.geom.Arc2D; +import java.awt.geom.Ellipse2D; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +public class BoundsBug { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This test displays three figures and draws the outline of their + bounding boxes. The bounding boxes should correctly encompass + the 3 figures. + + This test also paints two highlight rectangles at the ends of the + angular extents of the arc. The two highlights should correctly + appear at the outer circumference of the arc where the radii lines + from its center intersect that circumference. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(initialize()) + .build() + .awaitAndCheck(); + } + private static Frame initialize() { + Frame f = new Frame("BoundsBug"); + ArcPanel panel = new ArcPanel(); + f.add(panel); + f.setSize(300, 250); + return f; + } +} + +class ArcPanel extends Panel { + protected void drawPoint(Graphics2D g2, Point2D p) { + g2.setColor(Color.green); + g2.fill(new Rectangle2D.Double(p.getX() - 5, p.getY() - 5, 10, 10)); + } + + protected void drawShapeAndBounds(Graphics2D g2, Shape s) { + g2.setColor(Color.orange); + g2.fill(s); + g2.setColor(Color.black); + g2.draw(s); + + Rectangle2D r = s.getBounds2D(); + g2.setColor(Color.gray); + g2.draw(r); + } + + @Override + public void paint(Graphics g) { + Graphics2D g2 = (Graphics2D)g; + g2.setColor(Color.white); + g2.fill(g.getClipBounds()); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + + // Create some interesting shapes. + Ellipse2D ellipse = new Ellipse2D.Float(20, 40, 60, 80); + Arc2D arc = new Arc2D.Float(60, 40, 100, 120, + -30, -40, Arc2D.PIE); + GeneralPath path = new GeneralPath(GeneralPath.WIND_EVEN_ODD); + path.moveTo(0, 0); + path.lineTo(75, -25); + path.lineTo(25, 75); + path.lineTo(0, 25); + path.lineTo(100, 50); + path.lineTo(50, 0); + path.lineTo(25, 50); + path.closePath(); + // Now draw them and their bounds rectangles. + drawShapeAndBounds(g2, ellipse); + drawShapeAndBounds(g2, arc); + drawPoint(g2, arc.getStartPoint()); + drawPoint(g2, arc.getEndPoint()); + g2.translate(180, 65); + drawShapeAndBounds(g2, path); + } +} diff --git a/test/jdk/java/awt/geom/Area/Translate.java b/test/jdk/java/awt/geom/Area/Translate.java new file mode 100644 index 0000000000000..7519f1753bcf3 --- /dev/null +++ b/test/jdk/java/awt/geom/Area/Translate.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4183373 + * @summary Verifies that the translated Area objects display correctly + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual Translate + */ + +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Color; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Panel; +import java.awt.Rectangle; +import java.awt.geom.AffineTransform; +import java.awt.geom.Area; +import java.awt.geom.Rectangle2D; + +public class Translate { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This test displays two sets of rectangular figures. The two sets + should be displayed one on top of the other and should be lined + up vertically with each other. If the two sets of figures are + not directly above and below each other then the test fails + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(initialize()) + .build() + .awaitAndCheck(); + } + private static Frame initialize() { + Frame f = new Frame("Translate"); + TranslatePanel panel = new TranslatePanel(); + f.add(panel); + f.setSize(300, 250); + return f; + } +} + +class TranslatePanel extends Panel { + private static Image bufferedImage; + private static Area a1, a2, a3; + + public TranslatePanel() { + a1 = new Area(new Rectangle2D.Double(20.0, 20.0, 60.0, 60.0)); + + a2 = new Area((Area) a1.clone()); + a2.subtract(new Area(new Rectangle2D.Double(30.0, 30.0, 40.0, 40.0))); + + a3 = new Area((Area) a2.clone()); + a3.add(new Area(new Rectangle2D.Double(40.0, 40.0, 20.0, 20.0))); + + AffineTransform at2 = new AffineTransform(); + at2.translate(100.0, 0.0); + a2.transform(at2); + + AffineTransform at3 = new AffineTransform(); + at3.translate(200.0, 0.0); + a3.transform(at3); + } + private void paintRects(Graphics2D g2) { + Rectangle clip = g2.getClipBounds(); + g2.setColor(Color.white); + g2.fillRect(clip.x, clip.y, clip.width, clip.height); + g2.setPaint(Color.red); + g2.fill(a1); + g2.setPaint(Color.yellow); + g2.fill(a2); + g2.setPaint(Color.blue); + g2.fill(a3); + } + + @Override + public void paint(Graphics g) { + if (bufferedImage == null) { + bufferedImage = createImage(300, 100); + Graphics big = bufferedImage.getGraphics(); + // Notice that if you remove the translate() call, it works fine. + big.translate(-1, -1); + big.setClip(1, 1, 300, 100); + paintRects((Graphics2D)big); + big.translate(1, 1); + } + paintRects((Graphics2D)g); + g.drawImage(bufferedImage, 1, 100, this); + g.setColor(Color.black); + g.drawString("From offscreen image (with translate):", 10, 95); + g.drawString(" (should line up with rectangles above)", 10, 110); + } +} diff --git a/test/jdk/java/awt/im/4490692/bug4490692.html b/test/jdk/java/awt/im/4490692/bug4490692.html deleted file mode 100644 index f75d9a8740edc..0000000000000 --- a/test/jdk/java/awt/im/4490692/bug4490692.html +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - Test for KEY_PRESS event for accented characters - - - -

    Test for KEY_PRESS event for accented characters: Bug id 4490692

    - -This test is for unix platforms only. Press OK if you are not -testing on those platforms. - -Before the test, you need to modify the keyboard mapping for X by issueing -the following command: - -xmodmap -e 'keycode 60 = aacute' (this is for Solaris Sparc keyboard) -xmodmap -e 'keycode 23 = aacute' (this is for Linux PC keyboard) - -This command lets you type 'a with acute' character when you press 'Tab' keytop. Please -do not fail to restore the original key mapping by doing the following after the test - -xmodmap -e 'keycode 60 = Tab' (this is for Solaris Sparc keyboard) -xmodmap -e 'keycode 23 = Tab' (this is for Linux PC keyboard) - -Confirm the following two behaviors: - - "KEYPRESS received for aacute" is displayed when you press 'Tab' keytop. - On Solaris Sparc keyboard, The key sequence ("Compose", "a", "'") generates a-acute character in en_US locale - - - - diff --git a/test/jdk/java/awt/im/4490692/bug4490692.java b/test/jdk/java/awt/im/4490692/bug4490692.java deleted file mode 100644 index 7875b745e601e..0000000000000 --- a/test/jdk/java/awt/im/4490692/bug4490692.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/** - * - * @bug 4490692 - * @author Naoto Sato - */ - -import java.awt.*; -import java.awt.event.*; -import javax.swing.*; - -public class bug4490692 extends javax.swing.JApplet { - public void init() { - new TestFrame(); - } -} - -class TestFrame extends JFrame implements KeyListener { - JTextField text; - JLabel label; - - TestFrame () { - text = new JTextField(); - text.addKeyListener(this); - label = new JLabel(" "); - Container c = getContentPane(); - BorderLayout borderLayout1 = new BorderLayout(); - c.setLayout(borderLayout1); - c.add(text, BorderLayout.CENTER); - c.add(label, BorderLayout.SOUTH); - setSize(300, 200); - setVisible(true); - } - - public void keyPressed(KeyEvent e) { - if (e.getKeyChar() == 0x00e1) { - label.setText("KEYPRESS received for aacute"); - } else { - label.setText(" "); - } - } - - public void keyTyped(KeyEvent e) { - } - - public void keyReleased(KeyEvent e) { - } -} diff --git a/test/jdk/java/awt/im/8132503/bug8132503.html b/test/jdk/java/awt/im/8132503/bug8132503.html deleted file mode 100644 index ca8c8626461be..0000000000000 --- a/test/jdk/java/awt/im/8132503/bug8132503.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - -Verify that Chinese full stop symbol can be entered in JTextArea with Pinyin input method (IM). - -This test is for OS X only. For other platforms please simply press "Pass". - -1. Go to "System Preferences -> Keyboard -> Input Sources" and add "Pinyin – Traditional" or "Pinyin – Simplified" IM from Chinese language group. -2. Set current IM to "Pinyin". -3. Set focus to the text area of the test and press "dot" character on the keyboard. -4. Set current IM to the IM used before "Pinyin" was set. -5. If "。" character is displayed in the text area, press "Pass", if "." character is displayed, press "Fail". - - - - diff --git a/test/jdk/java/awt/im/8148984/bug8148984.html b/test/jdk/java/awt/im/8148984/bug8148984.html deleted file mode 100644 index c6eb7c2d266b3..0000000000000 --- a/test/jdk/java/awt/im/8148984/bug8148984.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - -Verify that Chinese comma can be entered in JTextField with Pinyin input method (IM). - -This test is for OS X only. For other platforms please simply press "Pass". - -1. Go to "System Preferences -> Keyboard -> Input Sources" and add "Pinyin – Traditional" or "Pinyin – Simplified" IM from Chinese language group. -2. Set current IM to "Pinyin". -3. Set focus to the text field of the test and press "comma" character on the keyboard. -4. Set current IM to the IM used before "Pinyin" was set. -5. If "," character is displayed in the text area, press "Pass", if "," character is displayed, press "Fail". - - - - diff --git a/test/jdk/java/awt/im/8154816/bug8154816.html b/test/jdk/java/awt/im/8154816/bug8154816.html deleted file mode 100644 index 1fdb5fd10aeff..0000000000000 --- a/test/jdk/java/awt/im/8154816/bug8154816.html +++ /dev/null @@ -1,39 +0,0 @@ - - - - -Verify that Caps Lock key works properly with Pinyin input method, (i.e. if -Caps Lock is pressed, input should be swithced to lowercase latin letters). - -This test is for OS X only. For other platforms please simply press "Pass". - -1. Go to "System Preferences -> Keyboard -> Input Sources" and add "Pinyin – Traditional" or "Pinyin – Simplified" IM from Chinese language group. -2. Set current IM to "Pinyin". -3. Set focus to the text field of the test and press Caps Lock key on the keyboard. -4. Press "a" character on the keyboard -5. If "a" character is displayed in the text field, press "Pass", if "A" character is displayed, press "Fail". - - - - diff --git a/test/jdk/java/awt/im/PinyinIMCapsTest.java b/test/jdk/java/awt/im/PinyinIMCapsTest.java new file mode 100644 index 0000000000000..4dca27d99225c --- /dev/null +++ b/test/jdk/java/awt/im/PinyinIMCapsTest.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import jtreg.SkippedException; +import sun.awt.OSInfo; + +/* + * @test + * @bug 8154816 + * @summary Caps Lock doesn't work as expected when using Pinyin Simplified input method + * @requires (os.family == "mac") + * @modules java.desktop/sun.awt + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame jtreg.SkippedException Util + * @run main/manual PinyinIMCapsTest + */ + +public class PinyinIMCapsTest { + private static final String INSTRUCTIONS = """ + The test verifies if the Caps Lock key works properly with Pinyin + input method, (i.e. if Caps Lock is pressed, input should be + switched to lowercase latin letters). + + Test settings: + Go to "System Preferences -> Keyboard -> Input Sources" and + add "Pinyin – Traditional" or "Pinyin – Simplified" IM from Chinese language group. + Set current IM to "Pinyin". + + 1. Set focus to the text field shown below and press Caps Lock key on the keyboard. + 2. Press "a" character on the keyboard + 3. If "a" character is displayed in the text field, press "Pass", + if "A" character is displayed, press "Fail". + """; + + public static void main(String[] args) throws Exception { + if (OSInfo.getOSType() != OSInfo.OSType.MACOSX) { + throw new SkippedException("This test is for MacOS only"); + } + PassFailJFrame.builder() + .title("Test Pinyin Input Method") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(45) + .splitUIBottom(PinyinIMCapsTest::createUI) + .testTimeOut(10) + .build() + .awaitAndCheck(); + } + + private static JComponent createUI() { + JPanel panel = new JPanel(); + JTextField input = new JTextField(20); + panel.add(new JLabel("Text field:")); + panel.add(input); + return panel; + } +} + diff --git a/test/jdk/java/awt/im/PinyinIMCommaTest.java b/test/jdk/java/awt/im/PinyinIMCommaTest.java new file mode 100644 index 0000000000000..273403e7d689e --- /dev/null +++ b/test/jdk/java/awt/im/PinyinIMCommaTest.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; + +/* + * @test + * @bug 8148984 + * @summary Verify that Chinese comma can be entered in JTextField + * with Pinyin input method (IM) on OS X. + * @requires (os.family == "mac") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual PinyinIMCommaTest + */ + +public class PinyinIMCommaTest { + private static final String INSTRUCTIONS = """ + This test verifies if Chinese comma can be entered in JTextField + with Pinyin input method (IM). + + Test settings: + Go to "System Preferences -> Keyboard -> Input Sources" and + add "Pinyin – Traditional" or "Pinyin – Simplified" IM from Chinese language group. + Set current IM to "Pinyin". + + 1. Set focus to the text field below and press "comma" character + on the keyboard. + 2. Now change the current IM to the IM used before "Pinyin" or to "U.S". + Press comma character again. + 3. You should notice a difference in the comma. Pinyin IM should output + full width comma "\uff0c" and the other IM should output a Latin comma "\u002c". + + If above is true press "PASS", if "\u002c" character is displayed for Pinyin IM, press "FAIL". + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Test Comma using Pinyin Input Method") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(45) + .splitUIBottom(PinyinIMCommaTest::createUI) + .testTimeOut(10) + .build() + .awaitAndCheck(); + } + + private static JComponent createUI() { + JPanel panel = new JPanel(); + panel.add(new JLabel("Text field:")); + panel.add(new JTextField(20)); + return panel; + } +} diff --git a/test/jdk/java/awt/im/PinyinIMFullstopTest.java b/test/jdk/java/awt/im/PinyinIMFullstopTest.java new file mode 100644 index 0000000000000..82d66f950f905 --- /dev/null +++ b/test/jdk/java/awt/im/PinyinIMFullstopTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextArea; + +/* + * @test + * @bug 8132503 + * @summary [macosx] Chinese full stop symbol cannot be entered with Pinyin IM on OS X + * @requires (os.family == "mac") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual PinyinIMFullstopTest + */ + +public class PinyinIMFullstopTest { + private static final String INSTRUCTIONS = """ + This test verifies if Chinese full stop symbol can be entered in JTextArea + with Pinyin input method (IM). + + Test settings: + Go to "System Preferences -> Keyboard -> Input Sources" and + add "Pinyin – Traditional" or "Pinyin – Simplified" IM from Chinese language group. + Set current IM to "Pinyin". + + 1. Set focus to the text area below and press "dot" character + on the keyboard. + 2. Now change the current IM to the IM used before "Pinyin" or to "U.S". + Press dot character again. + 3. You should notice a difference in the dot. Pinyin IM should output + "。" and the other IM should output "." + + If above is true press "PASS", if normal fullstop "." character is displayed + for Pinyin IM, press "FAIL". + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Test Dot using Pinyin Input Method") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(45) + .splitUIBottom(PinyinIMFullstopTest::createUI) + .testTimeOut(10) + .build() + .awaitAndCheck(); + } + + private static JComponent createUI() { + JPanel panel = new JPanel(); + JTextArea textArea = new JTextArea(5, 40); + panel.add(new JLabel("Text Area:")); + panel.add(textArea); + return panel; + } +} diff --git a/test/jdk/java/awt/im/bug4490692.java b/test/jdk/java/awt/im/bug4490692.java new file mode 100644 index 0000000000000..8b095ae3745c8 --- /dev/null +++ b/test/jdk/java/awt/im/bug4490692.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; + +/* + * @test + * @bug 4490692 + * @summary [Linux] Test for KEY_PRESS event for accented characters. + * @requires (os.family == "linux") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4490692 + */ + +public class bug4490692 { + private static final String INSTRUCTIONS = """ + This test is for unix platforms only. + Before the test, you need to modify the keyboard mapping for + Tab by issuing the following command: + + xmodmap -e 'keycode 23 = aacute' (this is for Linux) + xmodmap -e 'keycode 60 = aacute' (this is for Solaris Sparc) + + This command lets you type 'a with acute (à)' character when you press + 'Tab' key in the JTextField provided below the logging area. + After the test, please DO NOT fail to restore the original + key mapping by doing the following. + + xmodmap -e 'keycode 23 = Tab' (this is for Linux) + xmodmap -e 'keycode 60 = Tab' (this is for Solaris Sparc) + + CASE 1: This is a manual check and for SOLARIS SPARC keyboard + only. Check whether the key sequence ("Compose", "a", " ' ") + generates a-acute character in en_US locale. + + CASE 2: This step is automated and applicable for both + keyboards - LINUX & SOLARIS SPARC. + When Tab key is pressed it should generate a-acute (à) + character, this test automatically passes if the correct character + is generated on keypress else fails. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(45) + .testTimeOut(10) + .splitUIBottom(bug4490692::createUI) + .logArea(8) + .build() + .awaitAndCheck(); + } + + private static JComponent createUI() { + JPanel panel = new JPanel(); + JTextField textField = new JTextField("", 20); + panel.add(new JLabel("Text field:")); + + textField.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + PassFailJFrame.log(e.paramString()); + if (e.getKeyCode() == 23 || e.getKeyCode() == 60 + || e.paramString().contains("rawCode=23") + || e.paramString().contains("rawCode=60")) { + if (e.getKeyChar() == 0x00e1) { + PassFailJFrame.forcePass(); + } else { + PassFailJFrame.forceFail("Tab keypress DID NOT" + + " produce the expected accented character - aacute"); + } + } + } + }); + + panel.add(textField); + return panel; + } +} diff --git a/test/jdk/java/awt/image/BufferedImage/GrayAATextTest.java b/test/jdk/java/awt/image/BufferedImage/GrayAATextTest.java new file mode 100644 index 0000000000000..a484dd392eb03 --- /dev/null +++ b/test/jdk/java/awt/image/BufferedImage/GrayAATextTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4309915 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Check that Antialiased text drawn on a BYTE_GRAY image + * resolves the color correctly + * @run main/manual GrayAATextTest + */ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Panel; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; + +public class GrayAATextTest extends Panel { + + public static final int WIDTH = 600; + public static final int HEIGHT = 200; + + private static final String INSTRUCTIONS = """ + All of the strings in a given column should be drawn + in the same color. If the bug is present, then the + Antialiased strings will all be of a fixed color that + is not the same as the other strings in their column."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("GrayAATextTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int)INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(GrayAATextTest::createTestUI) + .build() + .awaitAndCheck(); + } + + public void paint(Graphics g) { + BufferedImage bi = new BufferedImage(WIDTH, HEIGHT, + BufferedImage.TYPE_BYTE_GRAY); + Graphics2D g2d = bi.createGraphics(); + g2d.setFont(new Font("Helvetica", Font.PLAIN, 24)); + g2d.setColor(Color.white); + g2d.fillRect(0, 0, WIDTH / 2, HEIGHT); + drawText(g2d, Color.black, "Black", 25); + drawText(g2d, Color.lightGray, "Light Gray", 175); + g2d.setColor(Color.black); + g2d.fillRect(WIDTH / 2, 0, WIDTH / 2, HEIGHT); + drawText(g2d, Color.white, "White", 325); + drawText(g2d, Color.lightGray, "Light Gray", 475); + g2d.dispose(); + g.drawImage(bi, 0, 0, null); + } + + public void drawText(Graphics2D g2d, Color c, String colorname, int x) { + g2d.setColor(c); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_OFF); + g2d.drawString(colorname, x, 50); + g2d.drawString("Aliased", x, 100); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + g2d.drawString("Antialiased", x, 150); + } + + public Dimension getPreferredSize() { + return new Dimension(WIDTH, HEIGHT); + } + + private static Frame createTestUI() { + Frame f = new Frame("GrayAATextTest Frame"); + f.add(new GrayAATextTest()); + f.setSize(WIDTH, HEIGHT); + return f; + } +} diff --git a/test/jdk/java/awt/image/GrayAlpha.java b/test/jdk/java/awt/image/GrayAlpha.java new file mode 100644 index 0000000000000..cf477c2638503 --- /dev/null +++ b/test/jdk/java/awt/image/GrayAlpha.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4243044 + * @summary This test should show two windows filled with checker + * board pattern. The transparency should runs from left to right from + * total transparent to total opaque. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual GrayAlpha + */ + +import java.util.List; +import java.awt.Frame; +import java.awt.color.ColorSpace; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ComponentColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; +import java.awt.Point; +import java.awt.Panel; +import java.awt.Transparency; +import java.awt.Window; + +public class GrayAlpha extends Panel { + + private static final String INSTRUCTIONS = """ + This test should show two windows filled with checker board + vpattern. The transparency should runs from left to right from + totally transparent to totally opaque. If either the pattern or + the transparency is not shown correctly, click Fail, otherwise + click Pass."""; + + BufferedImage bi; + AffineTransform identityTransform = new AffineTransform(); + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("GrayAlpha Instructions") + .instructions(INSTRUCTIONS) + .rows((int)INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(GrayAlpha::createTestUI) + .build() + .awaitAndCheck(); + } + + + public GrayAlpha(int width, int height, + boolean hasAlpha, boolean isAlphaPremultiplied, + boolean useRGB) { + boolean isAlphaPremuliplied = true; + int bands = useRGB ? 3 : 1; + bands = hasAlpha ? bands + 1 : bands; + + ColorSpace cs = useRGB ? + ColorSpace.getInstance(ColorSpace.CS_sRGB) : + ColorSpace.getInstance(ColorSpace.CS_GRAY); + int transparency = hasAlpha ? + Transparency.TRANSLUCENT : Transparency.OPAQUE; + int[] bits = new int[bands]; + for (int i = 0; i < bands; i++) { + bits[i] = 8; + } + + ColorModel cm = new ComponentColorModel(cs, + bits, + hasAlpha, + isAlphaPremultiplied, + transparency, + DataBuffer.TYPE_BYTE); + WritableRaster wr = + Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, + width, height, bands, + new Point(0, 0)); + + for (int b = 0; b < bands; b++) { + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int s; + + if (b != bands - 1 || !hasAlpha) { + // Gray band(s), fill with a checkerboard pattern + if (((x / 10) % 2) == ((y / 10) % 2)) { + s = 255; + } else { + s = 0; + } + if (isAlphaPremultiplied) { + int alpha = (x*255)/(width - 1); + s = (s*alpha)/255; + } + } else { + // Alpha band, increase opacity left to right + s = (x*255)/(width - 1); + } + + wr.setSample(x, y, b, s); + } + } + } + + this.bi = new BufferedImage(cm, wr, isAlphaPremultiplied, null); + } + + public Dimension getPreferredSize() { + return new Dimension(bi.getWidth(), bi.getHeight()); + } + + public void paint(Graphics g) { + if (bi != null) { + ((Graphics2D)g).drawImage(bi, 0, 0, null); + } + } + + public static Frame makeFrame(String title, + int x, int y, int width, int height, + boolean hasAlpha, + boolean isAlphaPremultiplied, + boolean useRGB) { + Frame f = new Frame(title); + f.add(new GrayAlpha(width, height, + hasAlpha, isAlphaPremultiplied, useRGB)); + f.pack(); + f.setLocation(x, y); + return f; + } + + private static List createTestUI() { + int width = 200; + int height = 200; + + int x = 100; + int y = 100; + + Frame f1 = makeFrame("Gray (non-premultiplied)", + x, y, width, height, + true, false, false); + x += width + 20; + + Frame f2 = makeFrame("Gray (premultiplied)", + x, y, width, height, + true, true, false); + + return List.of(f1, f2); + } +} diff --git a/test/jdk/java/awt/image/ImageOffsetTest.java b/test/jdk/java/awt/image/ImageOffsetTest.java new file mode 100644 index 0000000000000..c1d4c3931de91 --- /dev/null +++ b/test/jdk/java/awt/image/ImageOffsetTest.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4259548 + * @summary tests that MemoryImageSource correctly handles images with offsets + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ImageOffsetTest + */ + +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.Panel; +import java.awt.Toolkit; +import java.awt.image.ColorModel; +import java.awt.image.IndexColorModel; +import java.awt.image.MemoryImageSource; + +public class ImageOffsetTest { + + static int height = 100; + static int width = 100; + static int levels = 3; + static IndexColorModel cm; + static Image image; + static boolean first = true; + + static byte[] db = new byte[height * width * levels] ; + + private static final String INSTRUCTIONS = """ + If on the appeared 'Test frame' all color squares are of one color + test failed, otherwise it's passed."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("ImageOffsetTest") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(ImageOffsetTest::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame frame = new Frame("ImageOffset Frame"); + frame.add(new Panel() { + public void paint(Graphics g) { + for ( int i=0 ; i<3 ; i++ ) { + g.drawImage( + generateBuggyImage(i * width * height), 10 + i * 110, 10, null); + } + } + }); + frame.setSize(400, 200); + frame.setLocation(300, 200); + createColorModel(); + int l = 0; + for (int k = 0; k < levels; k++) { + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + if( k == 0) { + db[l] = (byte)(70 & 0xff) ; + } + if (k == 1) { + db[l] = (byte)(150 & 0xff) ; + } + if (k == 2) { + db[l] = (byte)(230 & 0xff) ; + } + l++ ; + } + } + } + return frame; + } + + private static void createColorModel() { + byte[] red = new byte[256]; + byte[] green = new byte[256]; + byte[] blue = new byte[256]; + + for (int i = 0; i < 256; i++) { + red[i] = (byte)(i & 0xff); + //green[i] = (byte)( i & 0xff ) ; + blue[i] = (byte)( i & 0xff ) ; + //commented out green so I could get purple + } + + cm = new IndexColorModel( 8, 256, red, green, blue ) ; + } + + private static Image generateBuggyImage(int offset) { + // Initialize the database, Three slices, different shades of grey + // Here the image is created using the offset, + return Toolkit.getDefaultToolkit().createImage( + new MemoryImageSource(width, height, (ColorModel)cm, + db, offset, width)); + } +} diff --git a/test/jdk/java/awt/image/TransformImage.java b/test/jdk/java/awt/image/TransformImage.java new file mode 100644 index 0000000000000..30af3d27f55c0 --- /dev/null +++ b/test/jdk/java/awt/image/TransformImage.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4090743 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Make sure that there is no garbage drawn on the rotated image + * @run main/manual TransformImage + */ + +import java.net.URL; +import java.net.MalformedURLException; +import java.awt.Canvas; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.geom.AffineTransform; +import java.awt.Image; +import java.awt.image.ImageObserver; +import java.awt.MediaTracker; +import java.awt.Toolkit; + +public class TransformImage extends Canvas { + static Image image; + + private static final String INSTRUCTIONS = """ + The rotated image should be drawn without garbage."""; + + public static void main(String[] argv) throws Exception { + PassFailJFrame.builder() + .title("TransformImage Instructions") + .instructions(INSTRUCTIONS) + .rows(5) + .columns(35) + .testUI(TransformImage::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + Frame f = new Frame(); + String dir = System.getProperty("test.src"); + String sep = System.getProperty("file.separator"); + if (dir == null) { + dir = "."; + } + image = Toolkit.getDefaultToolkit().getImage(dir+sep+"duke.gif"); + f.add(new TransformImage()); + + f.pack(); + return f; + } + + public Dimension getPreferredSize() { + return new Dimension (256, 256); + } + + public Dimension getMinimumSize() { + return getPreferredSize(); + } + + public void paint(Graphics g) { + int w, h; + java.awt.Graphics2D g2d = (Graphics2D) g; + AffineTransform at = new AffineTransform(); + + MediaTracker mt = new MediaTracker(this); + mt.addImage(image, 0); + try { + mt.waitForAll(); + } catch (InterruptedException e) { + System.err.println("can't track"); + return; + } + w = image.getWidth(this); + h = image.getHeight(this); + g2d.drawImage(image, 0, 0, this); + g2d.drawRect(0, 0, w, h); + + double rad = .5; + at.rotate(-rad); + g2d.setTransform(at); + g2d.drawImage(image, 0, 100, this); + g2d.drawRect(0, 100, w, h); + } +} diff --git a/test/jdk/java/awt/image/XBMDecoder/XBMDecoderTest.java b/test/jdk/java/awt/image/XBMDecoder/XBMDecoderTest.java new file mode 100644 index 0000000000000..19bc6d95c3921 --- /dev/null +++ b/test/jdk/java/awt/image/XBMDecoder/XBMDecoderTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8361748 + * @summary Tests XBM image size limits and if XBMImageDecoder.produceImage() + * throws appropriate error when parsing invalid XBM image data. + * @run main XBMDecoderTest + */ + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.PrintStream; +import javax.swing.ImageIcon; + +public class XBMDecoderTest { + + public static void main(String[] args) throws Exception { + String dir = System.getProperty("test.src"); + PrintStream originalErr = System.err; + boolean validCase; + + File currentDir = new File(dir); + File[] files = currentDir.listFiles((File d, String s) + -> s.endsWith(".xbm")); + + for (File file : files) { + String fileName = file.getName(); + validCase = fileName.startsWith("valid"); + + System.out.println("--- Testing " + fileName + " ---"); + try (FileInputStream fis = new FileInputStream(file); + ByteArrayOutputStream errContent = new ByteArrayOutputStream()) { + System.setErr(new PrintStream(errContent)); + + ImageIcon icon = new ImageIcon(fis.readAllBytes()); + boolean isErrEmpty = errContent.toString().isEmpty(); + if (!isErrEmpty) { + System.out.println("Expected ImageFormatException occurred."); + System.out.print(errContent); + } + + if (validCase && !isErrEmpty) { + throw new RuntimeException("Test failed: Error stream not empty"); + } else if (!validCase && isErrEmpty) { + throw new RuntimeException("Test failed: ImageFormatException" + + " expected but not thrown"); + } + System.out.println("PASSED\n"); + } finally { + System.setErr(originalErr); + } + } + } +} diff --git a/test/jdk/java/awt/image/XBMDecoder/invalid.xbm b/test/jdk/java/awt/image/XBMDecoder/invalid.xbm new file mode 100644 index 0000000000000..8a8cfc2763227 --- /dev/null +++ b/test/jdk/java/awt/image/XBMDecoder/invalid.xbm @@ -0,0 +1,2 @@ +#define k_ht 3 +h` k[] = { 01x0, 42222222222236319330:: diff --git a/test/jdk/java/awt/image/XBMDecoder/invalid_hex.xbm b/test/jdk/java/awt/image/XBMDecoder/invalid_hex.xbm new file mode 100644 index 0000000000000..c6f819582d0e3 --- /dev/null +++ b/test/jdk/java/awt/image/XBMDecoder/invalid_hex.xbm @@ -0,0 +1,3 @@ +#define k_wt 16 +#define k_ht 1 +k[] = { 0x10, 1234567890}; diff --git a/test/jdk/java/awt/image/XBMDecoder/invalid_ht.xbm b/test/jdk/java/awt/image/XBMDecoder/invalid_ht.xbm new file mode 100644 index 0000000000000..5244651a4cb47 --- /dev/null +++ b/test/jdk/java/awt/image/XBMDecoder/invalid_ht.xbm @@ -0,0 +1,3 @@ +#define k_wt 16 +#define k_ht 0 +k[] = { 0x10, 0x12}; diff --git a/test/jdk/java/awt/image/XBMDecoder/valid.xbm b/test/jdk/java/awt/image/XBMDecoder/valid.xbm new file mode 100644 index 0000000000000..23b57b2c81168 --- /dev/null +++ b/test/jdk/java/awt/image/XBMDecoder/valid.xbm @@ -0,0 +1,6 @@ +#define test_width 16 +#define test_height 3 +#define ht_x 1 +#define ht_y 2 +static unsigned char test_bits[] = { +0x13, 0x11, 0x15, 0x00, 0xAB, 0xcd }; diff --git a/test/jdk/java/awt/image/XBMDecoder/valid_hex.xbm b/test/jdk/java/awt/image/XBMDecoder/valid_hex.xbm new file mode 100644 index 0000000000000..e365d80244729 --- /dev/null +++ b/test/jdk/java/awt/image/XBMDecoder/valid_hex.xbm @@ -0,0 +1,4 @@ +#define test_width 16 +#define test_height 2 +static unsigned char test_bits[] = { 0x13, 0x11, + 0xAB, 0xff }; diff --git a/test/jdk/java/awt/image/duke.gif b/test/jdk/java/awt/image/duke.gif new file mode 100644 index 0000000000000..ed32e0ff79b05 Binary files /dev/null and b/test/jdk/java/awt/image/duke.gif differ diff --git a/test/jdk/java/awt/print/Dialog/DialogOrient.java b/test/jdk/java/awt/print/Dialog/DialogOrient.java index 982e51548bf2a..6650ced64f46e 100644 --- a/test/jdk/java/awt/print/Dialog/DialogOrient.java +++ b/test/jdk/java/awt/print/Dialog/DialogOrient.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -70,8 +70,8 @@ private static void init() throws Exception { Sysout.printInstructions( instructions ); PrinterJob job = PrinterJob.getPrinterJob(); - job.setPrintable(new DialogOrient()); PageFormat landscape = job.pageDialog(job.defaultPage()); + job.setPrintable(new DialogOrient(), landscape); if (job.printDialog()) { job.print(); diff --git a/test/jdk/java/awt/print/Dialog/DialogType.java b/test/jdk/java/awt/print/Dialog/DialogType.java index 472b89e44f213..b8801583dd0f3 100644 --- a/test/jdk/java/awt/print/Dialog/DialogType.java +++ b/test/jdk/java/awt/print/Dialog/DialogType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,49 +26,63 @@ * @bug 6568874 * @key printer * @summary Verify the native dialog works with attribute sets. - * @run main/manual=yesno DialogType + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame + * @run main/manual DialogType */ -import java.awt.print.*; -import javax.print.attribute.*; -import javax.print.attribute.standard.*; +import java.awt.print.PrinterJob; + +import javax.print.attribute.Attribute; +import javax.print.attribute.HashPrintRequestAttributeSet; +import javax.print.attribute.PrintRequestAttributeSet; +import javax.print.attribute.standard.DialogTypeSelection; + +import jtreg.SkippedException; public class DialogType { + private static PrinterJob job; + + private static final String INSTRUCTIONS = """ + Two print dialogs are shown in succession. + Click Cancel in the dialogs to close them. + + On macOS & on Windows, the first dialog is a native + dialog provided by the OS, the second dialog is + implemented in Swing, the dialogs differ in appearance. - static String[] instructions = { - "This test assumes and requires that you have a printer installed", - "It verifies that the dialogs behave properly when using new API", - "to optionally select a native dialog where one is present.", - "Two dialogs are shown in succession.", - "The test passes as long as no exceptions are thrown, *AND*", - "if running on Windows only, the first dialog is a native windows", - "control which differs in appearance from the second dialog", - "" - }; + The test passes as long as no exceptions are thrown. + (If there's an exception, the test will fail automatically.) - public static void main(String[] args) { + The test verifies that the dialogs behave properly when using new API + to optionally select a native dialog where one is present. + """; - for (int i=0;i 0) { - System.out.println("pi is greater than 0"); - return Printable.NO_SUCH_PAGE; - }*/ - // Simply draw two rectangles - Graphics2D g2 = (Graphics2D)g; - g2.setColor(Color.black); - g2.translate(pf.getImageableX(), pf.getImageableY()); - g2.drawRect(1,1,200,300); - g2.drawRect(1,1,25,25); - System.out.println("print method called "+pi + " Orientation "+pf.getOrientation()); - return Printable.PAGE_EXISTS; + if (pj.printDialog()) { + try { + pj.print(); + } catch (PrinterException pe) { + pe.printStackTrace(); + } } + passFailJFrame.awaitAndCheck(); + } + + //printable interface + public int print(Graphics g, PageFormat pf, int pi) throws PrinterException { + + // Simply draw two rectangles + Graphics2D g2 = (Graphics2D) g; + g2.setColor(Color.black); + g2.translate(pf.getImageableX(), pf.getImageableY()); + g2.drawRect(1, 1, 200, 300); + g2.drawRect(1, 1, 25, 25); + return Printable.PAGE_EXISTS; + } } class PageableHandler implements Pageable { - PageFormat pf = new PageFormat(); + PageFormat pf = new PageFormat(); - public int getNumberOfPages() { - return PrintDlgPageable.arg; - //return 0; - } + public int getNumberOfPages() { + return PrintDlgPageable.arg; + } - public Printable getPrintable(int pageIndex) { - return new PrintDlgPageable(); - } + public Printable getPrintable(int pageIndex) { + return new PrintDlgPageable(); + } - public PageFormat getPageFormat(int pageIndex) { - System.out.println("getPageFormat called "+pageIndex); - if (pageIndex == 0) { - pf.setOrientation(PageFormat.PORTRAIT); - System.out.println("Orientation returned from Pageable "+findOrientation(pf.getOrientation())); - return pf; - } else { - pf.setOrientation(PageFormat.LANDSCAPE); - System.out.println("Orientation returned from Pageable "+findOrientation(pf.getOrientation())); - return pf; - } + public PageFormat getPageFormat(int pageIndex) { + if (pageIndex == 0) { + pf.setOrientation(PageFormat.PORTRAIT); + return pf; + } else { + pf.setOrientation(PageFormat.LANDSCAPE); + return pf; } + } - public String findOrientation(int orient) { - if (orient == PageFormat.LANDSCAPE) { - return "LANDSCAPE"; - }else if (orient == PageFormat.PORTRAIT) { - return "PORTRAIT"; - } else if (orient == PageFormat.REVERSE_LANDSCAPE) { - return "REVERSE LANDSCAPE"; - } else { - return null; - } - } } diff --git a/test/jdk/java/awt/print/PrinterJob/PageFormatChange.java b/test/jdk/java/awt/print/PrinterJob/PageFormatChange.java index 3cb4254c64fb9..10aaaea89f1ff 100644 --- a/test/jdk/java/awt/print/PrinterJob/PageFormatChange.java +++ b/test/jdk/java/awt/print/PrinterJob/PageFormatChange.java @@ -35,7 +35,7 @@ public class PageFormatChange { static String[] text = { - "This is is a manual test intended to be run on Windows, and you", + "This is a manual test intended to be run on Windows, and you", "must have at least two printers installed, and ideally the second", "printer should support large paper sizes. When the pageDialog appears", "first change printers, then choose a large paper size, then OK", diff --git a/test/jdk/java/awt/print/PrinterJob/PrintNullString.java b/test/jdk/java/awt/print/PrinterJob/PrintNullString.java index 397dd2902d108..2bcee65fe9b97 100644 --- a/test/jdk/java/awt/print/PrinterJob/PrintNullString.java +++ b/test/jdk/java/awt/print/PrinterJob/PrintNullString.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,164 +21,113 @@ * questions. */ -import java.awt.Button; -import java.awt.Dimension; -import java.awt.Frame; import java.awt.Graphics; import java.awt.Graphics2D; -import java.awt.Panel; import java.awt.print.PageFormat; import java.awt.print.Printable; import java.awt.print.PrinterException; import java.awt.print.PrinterJob; +import java.io.File; import java.text.AttributedCharacterIterator; import java.text.AttributedString; -import javax.swing.JOptionPane; +import javax.print.attribute.HashPrintRequestAttributeSet; +import javax.print.attribute.PrintRequestAttributeSet; +import javax.print.attribute.standard.Destination; /* * @test * @bug 4223328 - * @summary Printer graphics must behave the same as screen graphics + * @summary Printer graphics must throw expected exceptions * @key printer - * @library /java/awt/regtesthelpers - * @build PassFailJFrame - * @run main/manual PrintNullString + * @run main PrintNullString */ -public class PrintNullString extends Frame { - private static final String INSTRUCTIONS = - "This test should print a page which contains the same\n" + - "text messages as in the test window on the screen.\n" + - "\n" + - "The messages should contain only 'OK' and 'expected' messages.\n" + - "Press Pass if it's the case; otherwise press Fail.\n" + - "\n" + - "If the page fails to print, but there were no exceptions\n" + - "then the problem is likely elsewhere (i.e. your printer)"; +public class PrintNullString implements Printable { + private final String nullStr = null; + private final String emptyStr = ""; + private final AttributedString emptyAttStr = new AttributedString(emptyStr); + private final AttributedCharacterIterator nullIterator = null; + private final AttributedCharacterIterator emptyIterator = emptyAttStr.getIterator(); public static void main(String[] args) throws Exception { if (PrinterJob.lookupPrintServices().length == 0) { throw new RuntimeException("Printer not configured or available."); } - PassFailJFrame.builder() - .instructions(INSTRUCTIONS) - .testUI(PrintNullString::new) - .rows((int) INSTRUCTIONS.lines().count() + 1) - .columns(45) - .build() - .awaitAndCheck(); + new PrintNullString(); } - public PrintNullString() { - super("PrintNullString"); - - TextCanvas c = new TextCanvas(); - add("Center", c); - - Button b = new Button("Print"); - add("South", b); - b.addActionListener(e -> { - PrinterJob pj = PrinterJob.getPrinterJob(); - if (pj.printDialog()) { - pj.setPrintable(c); - try { - pj.print(); - } catch (PrinterException ex) { - ex.printStackTrace(); - String msg = "PrinterException: " + ex.getMessage(); - JOptionPane.showMessageDialog(b, msg, "Error occurred", - JOptionPane.ERROR_MESSAGE); - PassFailJFrame.forceFail(msg); - } - } - }); - pack(); + public PrintNullString() throws PrinterException { + PrinterJob pj = PrinterJob.getPrinterJob(); + pj.setPrintable(this, new PageFormat()); + PrintRequestAttributeSet pSet = new HashPrintRequestAttributeSet(); + File file = new File("out.prn"); + file.deleteOnExit(); + pSet.add(new Destination(file.toURI())); + pj.print(pSet); } - private static class TextCanvas extends Panel implements Printable { - private final String nullStr = null; - private final String emptyStr = ""; - private final AttributedString emptyAttStr = new AttributedString(emptyStr); - private final AttributedCharacterIterator nullIterator = null; - private final AttributedCharacterIterator emptyIterator = emptyAttStr.getIterator(); - - @Override - public void paint(Graphics g) { - Graphics2D g2d = (Graphics2D) g; - paint(g2d); + @Override + public int print(Graphics g, PageFormat pgFmt, int pgIndex) { + if (pgIndex > 0) { + return NO_SUCH_PAGE; } - @Override - public int print(Graphics g, PageFormat pgFmt, int pgIndex) { - if (pgIndex > 0) { - return NO_SUCH_PAGE; - } + Graphics2D g2d = (Graphics2D) g; + g2d.translate(pgFmt.getImageableX(), pgFmt.getImageableY()); - Graphics2D g2d = (Graphics2D) g; - g2d.translate(pgFmt.getImageableX(), pgFmt.getImageableY()); - paint(g2d); + // API 1: null & empty drawString(String, int, int); + try { + g2d.drawString(nullStr, 20, 40); + throw new RuntimeException("FAILURE: No NPE for null String, int"); + } catch (NullPointerException e) { + g2d.drawString("caught expected NPE for null String, int", 20, 40); + } + + g2d.drawString(emptyStr, 20, 60); + g2d.drawString("OK for empty String, int", 20, 60); + + // API 2: null & empty drawString(String, float, float); + try { + g2d.drawString(nullStr, 20.0f, 80.0f); + throw new RuntimeException("FAILURE: No NPE for null String, float"); + } catch (NullPointerException e) { + g2d.drawString("caught expected NPE for null String, float", 20, 80); + } - return PAGE_EXISTS; + g2d.drawString(emptyStr, 20.0f, 100.0f); + g2d.drawString("OK for empty String, float", 20.0f, 100.f); + + // API 3: null & empty drawString(Iterator, int, int); + try { + g2d.drawString(nullIterator, 20, 120); + throw new RuntimeException("FAILURE: No NPE for null iterator, int"); + } catch (NullPointerException e) { + g2d.drawString("caught expected NPE for null iterator, int", 20, 120); + } + + try { + g2d.drawString(emptyIterator, 20, 140); + throw new RuntimeException("FAILURE: No IAE for empty iterator, int"); + } catch (IllegalArgumentException e) { + g2d.drawString("caught expected IAE for empty iterator, int", 20, 140); } - private void paint(Graphics2D g2d) { - // API 1: null & empty drawString(String, int, int); - try { - g2d.drawString(nullStr, 20, 40); - g2d.drawString("FAILURE: No NPE for null String, int", 20, 40); - } catch (NullPointerException e) { - g2d.drawString("caught expected NPE for null String, int", 20, 40); - } - - g2d.drawString(emptyStr, 20, 60); - g2d.drawString("OK for empty String, int", 20, 60); - - // API 2: null & empty drawString(String, float, float); - try { - g2d.drawString(nullStr, 20.0f, 80.0f); - g2d.drawString("FAILURE: No NPE for null String, float", 20, 80); - } catch (NullPointerException e) { - g2d.drawString("caught expected NPE for null String, float", 20, 80); - } - - g2d.drawString(emptyStr, 20.0f, 100.0f); - g2d.drawString("OK for empty String, float", 20.0f, 100.f); - - // API 3: null & empty drawString(Iterator, int, int); - try { - g2d.drawString(nullIterator, 20, 120); - g2d.drawString("FAILURE: No NPE for null iterator, int", 20, 120); - } catch (NullPointerException e) { - g2d.drawString("caught expected NPE for null iterator, int", 20, 120); - } - - try { - g2d.drawString(emptyIterator, 20, 140); - g2d.drawString("FAILURE: No IAE for empty iterator, int", 20, 140); - } catch (IllegalArgumentException e) { - g2d.drawString("caught expected IAE for empty iterator, int", 20, 140); - } - - // API 4: null & empty drawString(Iterator, float, int); - try { - g2d.drawString(nullIterator, 20.0f, 160.0f); - g2d.drawString("FAILURE: No NPE for null iterator, float", 20, 160); - } catch (NullPointerException e) { - g2d.drawString("caught expected NPE for null iterator, float", 20, 160); - } - - try { - g2d.drawString(emptyIterator, 20.0f, 180.0f); - g2d.drawString("FAILURE: No IAE for empty iterator, float", 20, 180); - } catch (IllegalArgumentException e) { - g2d.drawString("caught expected IAE for empty iterator, float", 20, 180); - } + // API 4: null & empty drawString(Iterator, float, int); + try { + g2d.drawString(nullIterator, 20.0f, 160.0f); + throw new RuntimeException("FAILURE: No NPE for null iterator, float"); + } catch (NullPointerException e) { + g2d.drawString("caught expected NPE for null iterator, float", 20, 160); } - @Override - public Dimension getPreferredSize() { - return new Dimension(450, 250); + try { + g2d.drawString(emptyIterator, 20.0f, 180.0f); + throw new RuntimeException("FAILURE: No IAE for empty iterator, float"); + } catch (IllegalArgumentException e) { + g2d.drawString("caught expected IAE for empty iterator, float", 20, 180); } + + return PAGE_EXISTS; } } diff --git a/test/jdk/java/awt/regtesthelpers/PassFailJFrame.java b/test/jdk/java/awt/regtesthelpers/PassFailJFrame.java index 1426b97e3ca68..49e56493488ac 100644 --- a/test/jdk/java/awt/regtesthelpers/PassFailJFrame.java +++ b/test/jdk/java/awt/regtesthelpers/PassFailJFrame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -72,6 +72,7 @@ import javax.swing.JTextArea; import javax.swing.Timer; import javax.swing.border.Border; +import javax.swing.event.HyperlinkListener; import javax.swing.text.JTextComponent; import javax.swing.text.html.HTMLEditorKit; import javax.swing.text.html.StyleSheet; @@ -98,7 +99,8 @@ * tester. The instructions can be either plain text or HTML. If the * text of the instructions starts with {@code ""}, the * instructions are displayed as HTML, as supported by Swing, which - * provides richer formatting options. + * provides richer formatting options. To handle navigating links in the + * instructions, call {@link Builder#hyperlinkListener} to install a listener. *

    * The instructions are displayed in a text component with word-wrapping * so that there's no horizontal scroll bar. If the text doesn't fit, a @@ -150,6 +152,17 @@ * Before returning from {@code awaitAndCheck}, the framework disposes of * all the windows and frames. * + *

    + * For semi-automatic tests, use {@code forcePass} or + * {@code forceFail} methods to forcibly pass or fail the test + * when it's determined that the required conditions are already met + * or cannot be met correspondingly. + * These methods release {@code awaitAndCheck}, and + * the test will complete successfully or fail. + *

    + * Refer to examples of using these methods in the description of the + * {@link #forcePass() forcePass} and {@link #forceFail() forceFail} methods. + * *

    Sample Manual Test

    * A simple test would look like this: * {@snippet id='sampleManualTestCode' lang='java': @@ -359,7 +372,7 @@ public enum Position {HORIZONTAL, VERTICAL, TOP_LEFT_CORNER} * the default values of {@value #ROWS} and {@value #COLUMNS} * for rows and columns. *

    - * See {@link #PassFailJFrame(String,String,long,int,int,boolean)} for + * See {@link #PassFailJFrame(String,String,long,int,int)} for * more details. * * @param instructions the instructions for the tester @@ -382,7 +395,7 @@ public PassFailJFrame(String instructions) * and the default values of {@value #ROWS} and {@value #COLUMNS} * for rows and columns. *

    - * See {@link #PassFailJFrame(String,String,long,int,int,boolean)} for + * See {@link #PassFailJFrame(String,String,long,int,int)} for * more details. * * @param instructions the instructions for the tester @@ -404,9 +417,8 @@ public PassFailJFrame(String instructions, long testTimeOut) * with the given title, instructions and timeout as well as * the default values of {@value #ROWS} and {@value #COLUMNS} * for rows and columns. - * The screenshot feature is not enabled, if you use this constructor. *

    - * See {@link #PassFailJFrame(String,String,long,int,int,boolean)} for + * See {@link #PassFailJFrame(String,String,long,int,int)} for * more details. * * @param title the title of the instruction frame @@ -424,41 +436,11 @@ public PassFailJFrame(String title, String instructions, this(title, instructions, testTimeOut, ROWS, COLUMNS); } - /** - * Constructs a frame which displays test instructions and - * the Pass / Fail buttons - * with the given title, instructions, timeout, number of rows and columns. - * The screenshot feature is not enabled, if you use this constructor. - *

    - * See {@link #PassFailJFrame(String,String,long,int,int,boolean)} for - * more details. - * - * @param title the title of the instruction frame - * @param instructions the instructions for the tester - * @param testTimeOut the test timeout in minutes - * @param rows the number of rows for the text component - * which displays test instructions - * @param columns the number of columns for the text component - * which displays test instructions - * - * @throws InterruptedException if the current thread is interrupted - * while waiting for EDT to finish creating UI components - * @throws InvocationTargetException if an exception is thrown while - * creating UI components on EDT - */ - public PassFailJFrame(String title, String instructions, - long testTimeOut, - int rows, int columns) - throws InterruptedException, InvocationTargetException { - this(title, instructions, testTimeOut, rows, columns, false); - } - /** * Constructs a frame which displays test instructions and * the Pass / Fail buttons * as well as supporting UI components with the given title, instructions, - * timeout, number of rows and columns, - * and screen capture functionality. + * timeout, number of rows and columns. * All the UI components are created on the EDT, so it is safe to call * the constructor on the main thread. *

    @@ -483,12 +465,6 @@ public PassFailJFrame(String title, String instructions, * the size of a text component which displays the instructions. * The preferred size of the instructions is calculated by * creating {@code new JTextArea(rows, columns)}. - *

    - * If you enable screenshots by setting the {@code screenCapture} - * parameter to {@code true}, a Screenshot button is added. - * Clicking the Screenshot button takes screenshots of - * all the monitors or all the windows registered with - * {@code PassFailJFrame}. * * @param title the title of the instruction frame * @param instructions the instructions for the tester @@ -497,8 +473,6 @@ public PassFailJFrame(String title, String instructions, * which displays test instructions * @param columns the number of columns for the text component * which displays test instructions - * @param screenCapture if set to {@code true}, enables screen capture - * functionality * * @throws InterruptedException if the current thread is interrupted * while waiting for EDT to finish creating UI components @@ -510,13 +484,11 @@ public PassFailJFrame(String title, String instructions, */ public PassFailJFrame(String title, String instructions, long testTimeOut, - int rows, int columns, - boolean screenCapture) + int rows, int columns) throws InterruptedException, InvocationTargetException { invokeOnEDT(() -> createUI(title, instructions, testTimeOut, - rows, columns, - screenCapture)); + rows, columns)); } /** @@ -613,8 +585,7 @@ private static void invokeOnEDTUncheckedException(Runnable doRun) { } private static void createUI(String title, String instructions, - long testTimeOut, int rows, int columns, - boolean enableScreenCapture) { + long testTimeOut, int rows, int columns) { frame = new JFrame(title); frame.setLayout(new BorderLayout()); @@ -623,7 +594,8 @@ private static void createUI(String title, String instructions, frame.add(createInstructionUIPanel(instructions, testTimeOut, rows, columns, - enableScreenCapture, + null, + false, false, 0), BorderLayout.CENTER); frame.pack(); @@ -641,6 +613,7 @@ private static void createUI(Builder builder) { createInstructionUIPanel(builder.instructions, builder.testTimeOut, builder.rows, builder.columns, + builder.hyperlinkListener, builder.screenCapture, builder.addLogArea, builder.logAreaRows); @@ -662,6 +635,7 @@ private static void createUI(Builder builder) { private static JComponent createInstructionUIPanel(String instructions, long testTimeOut, int rows, int columns, + HyperlinkListener hyperlinkListener, boolean enableScreenCapture, boolean addLogArea, int logAreaRows) { @@ -674,7 +648,12 @@ private static JComponent createInstructionUIPanel(String instructions, JTextComponent text = instructions.startsWith("") ? configureHTML(instructions, rows, columns) : configurePlainText(instructions, rows, columns); + if (hyperlinkListener != null && text instanceof JEditorPane ep) { + ep.addHyperlinkListener(hyperlinkListener); + } text.setEditable(false); + text.setBorder(createTextBorder()); + text.setCaretPosition(0); JPanel textPanel = new JPanel(new BorderLayout()); textPanel.setBorder(createEmptyBorder(GAP, 0, GAP, 0)); @@ -729,7 +708,6 @@ private static JTextComponent configurePlainText(String instructions, JTextArea text = new JTextArea(instructions, rows, columns); text.setLineWrap(true); text.setWrapStyleWord(true); - text.setBorder(createTextBorder()); return text; } @@ -743,10 +721,10 @@ private static JTextComponent configureHTML(String instructions, HTMLEditorKit kit = (HTMLEditorKit) text.getEditorKit(); StyleSheet styles = kit.getStyleSheet(); - // Reduce the default margins - styles.addRule("ol, ul { margin-left-ltr: 20; margin-left-rtl: 20 }"); - // Make the size of code blocks the same as other text - styles.addRule("code { font-size: inherit }"); + // Reduce the list default margins + styles.addRule("ol, ul { margin-left-ltr: 30; margin-left-rtl: 30 }"); + // Make the size of code (and other elements) the same as other text + styles.addRule("code, kbd, samp, pre { font-size: inherit; background: #DDD; }"); return text; } @@ -1335,28 +1313,50 @@ private static synchronized void showUI() { /** * Forcibly pass the test. - *

    The sample usage: - *

    
    -     *      PrinterJob pj = PrinterJob.getPrinterJob();
    -     *      if (pj == null || pj.getPrintService() == null) {
    -     *          System.out.println(""Printer not configured or available.");
    -     *          PassFailJFrame.forcePass();
    -     *      }
    -     * 
    + *

    + * Use this method in semi-automatic tests when + * the test determines that all the conditions for passing the test are met. + *

    + * Do not use this method in cases where a resource is unavailable or a + * feature isn't supported, throw {@code jtreg.SkippedException} instead. + * + *

    A sample usage can be found in + * {@code + * SaveFileNameOverrideTest.java} */ public static void forcePass() { latch.countDown(); } /** - * Forcibly fail the test. + * Forcibly fail the test. + *

    + * Use this method in semi-automatic tests when + * it is determined that the conditions for passing the test cannot be met. + *

    + * Do not use this method in cases where a resource is unavailable or a + * feature isn't supported, throw {@code jtreg.SkippedException} instead. + * + *

    A sample usage can be found in + * {@code + * HorizScrollers.java} */ public static void forceFail() { forceFail("forceFail called"); } /** - * Forcibly fail the test and provide a reason. + * Forcibly fail the test and provide a reason. + *

    + * Use this method in semi-automatic tests when + * it is determined that the conditions for passing the test cannot be met. + *

    + * Do not use this method in cases where a resource is unavailable or a + * feature isn't supported, throw {@code jtreg.SkippedException} instead. + * + *

    A sample usage can be found in + * {@code + * SaveFileNameOverrideTest.java} * * @param reason the reason why the test is failed */ @@ -1406,6 +1406,7 @@ public static final class Builder { private int rows; private int columns; private boolean screenCapture; + private HyperlinkListener hyperlinkListener; private boolean addLogArea; private int logAreaRows = 10; @@ -1486,6 +1487,18 @@ public Builder columns(int columns) { return this; } + /** + * Sets a {@link HyperlinkListener} for navigating links inside + * the instructions pane. + * + * @param hyperlinkListener the listener + * @return this builder + */ + public Builder hyperlinkListener(HyperlinkListener hyperlinkListener) { + this.hyperlinkListener = hyperlinkListener; + return this; + } + public Builder screenCapture() { this.screenCapture = true; return this; @@ -1755,7 +1768,7 @@ public Builder testUI(PanelCreator panelCreator) { * * @throws IllegalStateException if a {@code PanelCreator} is * already set - * @throws IllegalArgumentException if {panelCreator} is {@code null} + * @throws IllegalArgumentException if {@code panelCreator} is {@code null} */ public Builder splitUI(PanelCreator panelCreator) { return splitUIRight(panelCreator); @@ -1772,7 +1785,7 @@ public Builder splitUI(PanelCreator panelCreator) { * * @throws IllegalStateException if a {@code PanelCreator} is * already set - * @throws IllegalArgumentException if {panelCreator} is {@code null} + * @throws IllegalArgumentException if {@code panelCreator} is {@code null} */ public Builder splitUIRight(PanelCreator panelCreator) { return splitUI(panelCreator, JSplitPane.HORIZONTAL_SPLIT); @@ -1789,7 +1802,7 @@ public Builder splitUIRight(PanelCreator panelCreator) { * * @throws IllegalStateException if a {@code PanelCreator} is * already set - * @throws IllegalArgumentException if {panelCreator} is {@code null} + * @throws IllegalArgumentException if {@code panelCreator} is {@code null} */ public Builder splitUIBottom(PanelCreator panelCreator) { return splitUI(panelCreator, JSplitPane.VERTICAL_SPLIT); @@ -1805,7 +1818,7 @@ public Builder splitUIBottom(PanelCreator panelCreator) { * * @throws IllegalStateException if a {@code PanelCreator} is * already set - * @throws IllegalArgumentException if {panelCreator} is {@code null} + * @throws IllegalArgumentException if {@code panelCreator} is {@code null} */ private Builder splitUI(PanelCreator panelCreator, int splitUIOrientation) { @@ -1829,8 +1842,20 @@ public Builder position(Position position) { public PassFailJFrame build() throws InterruptedException, InvocationTargetException { - validate(); - return new PassFailJFrame(this); + try { + validate(); + return new PassFailJFrame(this); + } catch (final Throwable t) { + // Dispose of all the windows, including those that may not + // be registered with PassFailJFrame to allow AWT to shut down + try { + invokeOnEDT(() -> Arrays.stream(Window.getWindows()) + .forEach(Window::dispose)); + } catch (Throwable edt) { + t.addSuppressed(edt); + } + throw t; + } } /** diff --git a/test/jdk/java/awt/regtesthelpers/process/ProcessCommunicator.java b/test/jdk/java/awt/regtesthelpers/process/ProcessCommunicator.java index 60a5952b32c03..474a23717ea91 100644 --- a/test/jdk/java/awt/regtesthelpers/process/ProcessCommunicator.java +++ b/test/jdk/java/awt/regtesthelpers/process/ProcessCommunicator.java @@ -76,7 +76,7 @@ public static ProcessResults executeChildProcess(final Class classToExecute, } /** - * Executes child {code Process} + * Executes child {@code Process} * * @param classToExecute class to be executed as a child java process * @param args args to be passed in to the child process diff --git a/test/jdk/java/beans/Introspector/8159696/UnloadClassBeanInfo.java b/test/jdk/java/beans/Introspector/8159696/UnloadClassBeanInfo.java index 8c77b3565a8fc..0d9775d50d675 100644 --- a/test/jdk/java/beans/Introspector/8159696/UnloadClassBeanInfo.java +++ b/test/jdk/java/beans/Introspector/8159696/UnloadClassBeanInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,7 +38,7 @@ * @bug 8159696 * @library /javax/swing/regtesthelpers * @compile ./stub/Stub.java - * @run main/othervm -mx32M UnloadClassBeanInfo + * @run main/othervm -Xmx32M UnloadClassBeanInfo */ public class UnloadClassBeanInfo { diff --git a/test/jdk/java/beans/Introspector/DefaultMethodBeanPropertyTest.java b/test/jdk/java/beans/Introspector/DefaultMethodBeanPropertyTest.java index a1e528880ef7c..2e79faa2d91a2 100644 --- a/test/jdk/java/beans/Introspector/DefaultMethodBeanPropertyTest.java +++ b/test/jdk/java/beans/Introspector/DefaultMethodBeanPropertyTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,20 +23,26 @@ /* * @test - * @bug 8071693 + * @bug 8071693 8347826 * @summary Verify that the Introspector finds default methods inherited * from interfaces */ +import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.Introspector; +import java.beans.MethodDescriptor; import java.beans.PropertyDescriptor; +import java.beans.SimpleBeanInfo; import java.lang.reflect.Method; +import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.NavigableSet; +import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; public class DefaultMethodBeanPropertyTest { @@ -78,11 +84,17 @@ public Float getObj() { } public static void testScenario1() { + verifyMethods(D1.class, + "public static int DefaultMethodBeanPropertyTest$A1.getStaticValue()", + "public default int DefaultMethodBeanPropertyTest$A1.getValue()", + "public java.lang.Integer DefaultMethodBeanPropertyTest$D1.getFoo()", + "public java.lang.Float DefaultMethodBeanPropertyTest$D1.getObj()" + ); verifyProperties(D1.class, - "getClass", // inherited method - "getValue", // inherited default method - "getFoo", // overridden interface method - "getObj" // overridden default method + "public final native java.lang.Class java.lang.Object.getClass()", + "public default int DefaultMethodBeanPropertyTest$A1.getValue()", + "public java.lang.Integer DefaultMethodBeanPropertyTest$D1.getFoo()", + "public java.lang.Float DefaultMethodBeanPropertyTest$D1.getObj()" ); } @@ -108,9 +120,12 @@ public class D2 implements B2, C2 { } public static void testScenario2() { + verifyMethods(D2.class, + "public default java.lang.Object DefaultMethodBeanPropertyTest$A2.getFoo()" + ); verifyProperties(D2.class, - "getClass", - "getFoo" + "public final native java.lang.Class java.lang.Object.getClass()", + "public default java.lang.Object DefaultMethodBeanPropertyTest$A2.getFoo()" ); } @@ -144,60 +159,404 @@ public NavigableSet getFoo() { } public static void testScenario3() { + verifyMethods(D3.class, + "public java.util.NavigableSet DefaultMethodBeanPropertyTest$D3.getFoo()" + ); verifyProperties(D3.class, - "getClass", - "getFoo" + "public final native java.lang.Class java.lang.Object.getClass()", + "public java.util.NavigableSet DefaultMethodBeanPropertyTest$D3.getFoo()" ); } -// Helper methods +////////////////////////////////////// +// // +// SCENARIO 4 // +// // +////////////////////////////////////// + + public interface A4 { + default Object getDefault0() { + return null; + } + default Object getDefault1() { + return null; + } + default Object getDefault2() { + return null; + } + default Object getDefault3() { + return null; + } + Object getNonDefault(); + } + + public class B4 implements A4 { + @Override + public Object getDefault1() { + return new B4(); + } + @Override + public String getDefault2() { + return null; + } + @Override + public Float getDefault3() { + return null; + } + public Long getNonDefault() { + return null; + } + } + + public static void testScenario4() { + verifyMethods(B4.class, + "public default java.lang.Object DefaultMethodBeanPropertyTest$A4.getDefault0()", + "public java.lang.Object DefaultMethodBeanPropertyTest$B4.getDefault1()", + "public java.lang.String DefaultMethodBeanPropertyTest$B4.getDefault2()", + "public java.lang.Float DefaultMethodBeanPropertyTest$B4.getDefault3()", + "public java.lang.Long DefaultMethodBeanPropertyTest$B4.getNonDefault()" + ); + verifyProperties(B4.class, + "public final native java.lang.Class java.lang.Object.getClass()", + "public default java.lang.Object DefaultMethodBeanPropertyTest$A4.getDefault0()", + "public java.lang.Object DefaultMethodBeanPropertyTest$B4.getDefault1()", + "public java.lang.String DefaultMethodBeanPropertyTest$B4.getDefault2()", + "public java.lang.Float DefaultMethodBeanPropertyTest$B4.getDefault3()", + "public java.lang.Long DefaultMethodBeanPropertyTest$B4.getNonDefault()" + ); + } + +////////////////////////////////////// +// // +// SCENARIO 5 // +// // +////////////////////////////////////// + + public interface A5 { + public default void setParentFoo(Integer num) { + } + public default void setFoo(String num) { + } + public static int getStaticValue() { + return 0; + } + private int getPrivateValue() { + return 0; + } + } + + public class B5 implements A5 { + public void setFoo(Number num) { + } + public void setLocalFoo(Long num) { + } + public static int getStaticValue() { + return 0; + } + } + + public static void testScenario5() { + verifyMethods(B5.class, + "public static int DefaultMethodBeanPropertyTest$B5.getStaticValue()", + "public default void DefaultMethodBeanPropertyTest$A5.setFoo(java.lang.String)", + "public default void DefaultMethodBeanPropertyTest$A5.setParentFoo(java.lang.Integer)", + "public void DefaultMethodBeanPropertyTest$B5.setFoo(java.lang.Number)", + "public void DefaultMethodBeanPropertyTest$B5.setLocalFoo(java.lang.Long)" + ); + verifyProperties(B5.class, + "public final native java.lang.Class java.lang.Object.getClass()", + "public default void DefaultMethodBeanPropertyTest$A5.setParentFoo(java.lang.Integer)", + "public void DefaultMethodBeanPropertyTest$B5.setFoo(java.lang.Number)", + "public void DefaultMethodBeanPropertyTest$B5.setLocalFoo(java.lang.Long)" + ); + } + +////////////////////////////////////// +// // +// SCENARIO 6 // +// // +////////////////////////////////////// + + public class A6 { + public void setParentFoo(Integer num) { + } + public void setFoo(Integer num) { + } + public static int getStaticValue() { + return 0; + } + private int getPrivateValue() { + return 0; + } + } + + public class B6 extends A6 { + public void setFoo(String num) { + } + public void setLocalFoo(Long num) { + } + public static int getStaticValue() { + return 0; + } + } + + public static void testScenario6() { + verifyMethods(B6.class, + "public static int DefaultMethodBeanPropertyTest$B6.getStaticValue()", + "public void DefaultMethodBeanPropertyTest$A6.setFoo(java.lang.Integer)", + "public void DefaultMethodBeanPropertyTest$A6.setParentFoo(java.lang.Integer)", + "public void DefaultMethodBeanPropertyTest$B6.setFoo(java.lang.String)", + "public void DefaultMethodBeanPropertyTest$B6.setLocalFoo(java.lang.Long)" + ); + verifyProperties(B6.class, + "public final native java.lang.Class java.lang.Object.getClass()", + "public void DefaultMethodBeanPropertyTest$A6.setParentFoo(java.lang.Integer)", + "public void DefaultMethodBeanPropertyTest$B6.setFoo(java.lang.String)", + "public void DefaultMethodBeanPropertyTest$B6.setLocalFoo(java.lang.Long)" + ); + } + +////////////////////////////////////// +// // +// SCENARIO 7 // +// // +////////////////////////////////////// + + interface A7 { + T getValue(); + } + + interface B7 { + Runnable getValue(); + } + + interface AB7 extends B7, A7 { + Runnable getValue(); + } + + abstract class D7 implements AB7 { + public void setValue(Runnable value) { + } + } + + public static void testScenario7() { + verifyMethods(D7.class, + "public void DefaultMethodBeanPropertyTest$D7.setValue(java.lang.Runnable)" + ); + verifyProperties(D7.class, + "public final native java.lang.Class java.lang.Object.getClass()", + "public void DefaultMethodBeanPropertyTest$D7.setValue(java.lang.Runnable)" + ); + } + +////////////////////////////////////// +// // +// SCENARIO 8 // +// // +////////////////////////////////////// + + public interface A8 { + public default void setFoo(Float num) { + } + public default void setFoo2(Integer num) { + } + } + public interface B8 extends A8 { + public default void setFoo(Integer num) { + } + public default void setFoo2(Float num) { + } + } + + public class C8 implements B8 { + } + + public static void testScenario8() { + verifyMethods(C8.class, + "public default void DefaultMethodBeanPropertyTest$A8.setFoo(java.lang.Float)", + "public default void DefaultMethodBeanPropertyTest$A8.setFoo2(java.lang.Integer)", + "public default void DefaultMethodBeanPropertyTest$B8.setFoo(java.lang.Integer)", + "public default void DefaultMethodBeanPropertyTest$B8.setFoo2(java.lang.Float)" + ); + verifyProperties(C8.class, + "public final native java.lang.Class java.lang.Object.getClass()", + "public default void DefaultMethodBeanPropertyTest$B8.setFoo(java.lang.Integer)", + "public default void DefaultMethodBeanPropertyTest$B8.setFoo2(java.lang.Float)" + ); + } + +////////////////////////////////////// +// // +// SCENARIO 9 // +// // +////////////////////////////////////// + + public class A9 { + public void setFoo(Object value) { + } + public void setFoo(String value) { + } + public void setFoo2(Object value) { + } + public void setFoo2(Integer value) { + } + // For the same setters with inconvertible arg types PropertyInfo behavior is undefined. + // public void setLocalFoo3(Long num) { } + // public void setLocalFoo3(Float num) { } + } + + public static void testScenario9() { + verifyMethods(A9.class, + "public void DefaultMethodBeanPropertyTest$A9.setFoo(java.lang.String)", + "public void DefaultMethodBeanPropertyTest$A9.setFoo(java.lang.Object)", + "public void DefaultMethodBeanPropertyTest$A9.setFoo2(java.lang.Integer)", + "public void DefaultMethodBeanPropertyTest$A9.setFoo2(java.lang.Object)" + ); + verifyProperties(A9.class, + "public final native java.lang.Class java.lang.Object.getClass()", + "public void DefaultMethodBeanPropertyTest$A9.setFoo(java.lang.String)", + "public void DefaultMethodBeanPropertyTest$A9.setFoo2(java.lang.Integer)" + ); + } + +////////////////////////////////////// +// // +// SCENARIO 10 // +// // +////////////////////////////////////// + + public static class A10 { + public Object getProp() { + return null; + } + } + + public static interface B10 { + Object getProp(); + } - public static void verifyProperties(Class type, String... getterNames) { + public static class C10_1 extends A10 implements B10 { + } + + public static class C10_2 extends A10 implements B10 { + } + + public static class A10BeanInfo extends SimpleBeanInfo { + public MethodDescriptor[] getMethodDescriptors() { + try { + Class params[] = {}; + MethodDescriptor md = new MethodDescriptor(A10.class.getDeclaredMethod("getProp", params)); + md.setDisplayName("display name"); + MethodDescriptor res[] = { md }; + return res; + } catch (Exception exception) { + throw new Error("unexpected exception", exception); + } + } + } - // Gather expected properties - final HashSet expected = new HashSet<>(); - for (String methodName : getterNames) { - final String suffix = methodName.substring(3); - final String propName = Introspector.decapitalize(suffix); - final Method getter; + public static class C10_1BeanInfo extends SimpleBeanInfo { + public BeanInfo[] getAdditionalBeanInfo() { try { - getter = type.getMethod(methodName); - } catch (NoSuchMethodException e) { - throw new Error("unexpected error", e); + BeanInfo res[] = { + Introspector.getBeanInfo(A10.class), + Introspector.getBeanInfo(B10.class) + }; + return res; + } catch (IntrospectionException exception) { + throw new Error("unexpected exception", exception); } - final PropertyDescriptor propDesc; + } + } + + public static class C10_2BeanInfo extends SimpleBeanInfo { + public BeanInfo[] getAdditionalBeanInfo() { try { - propDesc = new PropertyDescriptor(propName, getter, null); - } catch (IntrospectionException e) { - throw new Error("unexpected error", e); + BeanInfo res[] = { + Introspector.getBeanInfo(B10.class), + Introspector.getBeanInfo(A10.class) + }; + return res; + } catch (IntrospectionException exception) { + throw new Error("unexpected exception", exception); } - expected.add(propDesc); } + } - // Verify properties can be found directly - expected.stream() - .map(PropertyDescriptor::getName) - .filter(name -> BeanUtils.getPropertyDescriptor(type, name) == null) - .findFirst() - .ifPresent(name -> { - throw new Error("property \"" + name + "\" not found in " + type); - }); + public static void testScenario10() { + { + var md = getMethodDescriptor(C10_1.class, A10.class, "getProp"); + assertEquals("display name", md.getDisplayName(), "getDisplayName()"); + } + { + var md = getMethodDescriptor(C10_2.class, A10.class, "getProp"); + assertEquals("display name", md.getDisplayName(), "getDisplayName()"); + } + } - // Gather actual properties - final Set actual = - Set.of(BeanUtils.getPropertyDescriptors(type)); +// Helper methods - // Verify the two sets are the same + private static void verifyEquality(String title, Set expected, Set actual) { if (!actual.equals(expected)) { - throw new Error("mismatch: " + type - + "\nACTUAL:\n " - + actual.stream() - .map(Object::toString) - .collect(Collectors.joining("\n ")) - + "\nEXPECTED:\n " - + expected.stream() - .map(Object::toString) - .collect(Collectors.joining("\n "))); + throw new Error(title + " mismatch: " + + "\nACTUAL:\n " + + actual.stream() + .map(Object::toString) + .collect(Collectors.joining("\n ")) + + "\nEXPECTED:\n " + + expected.stream() + .map(Object::toString) + .collect(Collectors.joining("\n "))); + } + } + + public static void verifyProperties(Class type, String... methodNames) { + try { + final Set expected = new HashSet<>(Arrays.asList(methodNames)); + final Set actual = Arrays + .stream(Introspector.getBeanInfo(type) + .getPropertyDescriptors()) + .flatMap(pd -> Stream.of(pd.getReadMethod(), pd.getWriteMethod())) + .filter(Objects::nonNull) + .map((Method m) -> m.toString()) + .collect(Collectors.toSet()); + verifyEquality("properties", expected, actual); + } catch (IntrospectionException exception) { + throw new Error("unexpected exception", exception); + } + } + + public static void verifyMethods(Class type, String... methodNames) { + try { + final Set expected = new HashSet<>(Arrays.asList(methodNames)); + final Set actual = Arrays + .stream(Introspector.getBeanInfo(type, Object.class) + .getMethodDescriptors()) + .map(MethodDescriptor::getMethod) + .map(Method::toString) + .collect(Collectors.toSet()); + verifyEquality("methods", expected, actual); + } catch (IntrospectionException exception) { + throw new Error("unexpected exception", exception); + } + } + + private static MethodDescriptor getMethodDescriptor(Class cls, Class stop, String name) { + try { + for (var md : Introspector.getBeanInfo(cls, stop).getMethodDescriptors()) { + if (md.getName().equals(name)) { + return md; + } + } + return null; + } catch (IntrospectionException exception) { + throw new Error("unexpected exception", exception); + } + } + + private static void assertEquals(Object expected, Object actual, String msg) { + if (!expected.equals(actual)) { + throw new Error(msg + ":\nACTUAL: " + actual + "\nEXPECTED: " + expected); } } @@ -207,5 +566,12 @@ public static void main(String[] args) throws Exception { testScenario1(); testScenario2(); testScenario3(); + testScenario4(); + testScenario5(); + testScenario6(); + testScenario7(); + testScenario8(); + testScenario9(); + testScenario10(); } } diff --git a/test/jdk/java/beans/Introspector/Test5102804.java b/test/jdk/java/beans/Introspector/Test5102804.java index d5b38ea10d017..0563b1d306d77 100644 --- a/test/jdk/java/beans/Introspector/Test5102804.java +++ b/test/jdk/java/beans/Introspector/Test5102804.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,8 +25,7 @@ * @test * @bug 5102804 * @summary Tests memory leak - * @author Sergey Malenkov - * @run main/othervm -ms16m -mx16m Test5102804 + * @run main/othervm -Xms16m -Xmx16m Test5102804 */ import java.beans.BeanInfo; diff --git a/test/jdk/java/beans/Introspector/Test8027905.java b/test/jdk/java/beans/Introspector/Test8027905.java index 60318f345553b..8ebf1c59574f9 100644 --- a/test/jdk/java/beans/Introspector/Test8027905.java +++ b/test/jdk/java/beans/Introspector/Test8027905.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,8 +27,7 @@ * @test * @bug 8027905 * @summary Tests that GC does not affect a property type - * @author Sergey Malenkov - * @run main/othervm -mx16m Test8027905 + * @run main/othervm -Xmx16m Test8027905 */ public class Test8027905 { diff --git a/test/jdk/java/beans/XMLEncoder/Test4646747.java b/test/jdk/java/beans/XMLEncoder/Test4646747.java index 61f7c41c88269..467a015322815 100644 --- a/test/jdk/java/beans/XMLEncoder/Test4646747.java +++ b/test/jdk/java/beans/XMLEncoder/Test4646747.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,8 +25,7 @@ * @test * @bug 4646747 * @summary Tests that persistence delegate is correct after memory stress - * @author Mark Davidson - * @run main/othervm -ms16m -mx16m Test4646747 + * @run main/othervm -Xms16m -Xmx16m Test4646747 */ import java.beans.DefaultPersistenceDelegate; diff --git a/test/jdk/java/io/ByteArrayInputStream/ChunkedTransferTo.java b/test/jdk/java/io/ByteArrayInputStream/ChunkedTransferTo.java new file mode 100644 index 0000000000000..fc334dd780461 --- /dev/null +++ b/test/jdk/java/io/ByteArrayInputStream/ChunkedTransferTo.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8316156 + * @summary Ensure ByteArrayInputStream.transferTo does not cause direct memory + * to overflow MaxDirectMemorySize + * @run junit/othervm -XX:MaxDirectMemorySize=5M ChunkedTransferTo + */ + +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.channels.Channels; +import java.nio.channels.FileChannel; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Random; + +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import static java.nio.file.StandardOpenOption.*; + +import org.junit.jupiter.api.Test; + +public class ChunkedTransferTo { + // this value must exceed MaxDirectMemorySize + private static final int SIZE = 10_000_000; + + @Test + public void byteArrayInputStream() throws IOException { + byte[] src = new byte[SIZE]; + Random rnd = new Random(System.nanoTime()); + rnd.nextBytes(src); + try (ByteArrayInputStream bais = new ByteArrayInputStream(src)) { + Path target = Files.createTempFile("SNA", "FU"); + FileChannel fc = FileChannel.open(target, CREATE, WRITE); + bais.transferTo(Channels.newOutputStream(fc)); + byte[] dst = new byte[SIZE + 1]; + try (FileInputStream fis = new FileInputStream(target.toFile())) { + int n = -1; + if ((n = fis.read(dst)) != SIZE) + throw new RuntimeException(n + " != " + SIZE); + } + Files.delete(target); + if (!Arrays.equals(src, 0, SIZE, dst, 0, SIZE)) + throw new RuntimeException("Arrays are not equal"); + } catch (OutOfMemoryError oome) { + throw new RuntimeException(oome); + } + } +} diff --git a/test/jdk/java/io/File/MacPathTest.java b/test/jdk/java/io/File/MacPathTest.java index c5b50f7e2cf35..7082c4c13e970 100644 --- a/test/jdk/java/io/File/MacPathTest.java +++ b/test/jdk/java/io/File/MacPathTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,6 +21,11 @@ * questions. */ +/* + * This test is launched via a ProcessBuilder in the main test MacPath which + * includes a @requires (os.family == "mac") tag so no operating system + * conditional is applied here. + */ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -29,10 +34,6 @@ public class MacPathTest { public static void main(String args[]) throws Throwable { - String osname = System.getProperty("os.name"); - if (!osname.contains("OS X") && !osname.contains("Darwin")) - return; - // English test("TestDir_apple", // test dir "dir_macosx", // dir diff --git a/test/jdk/java/io/File/MaxPath.java b/test/jdk/java/io/File/MaxPath.java index 658d6a7904b41..30951ac0d8515 100644 --- a/test/jdk/java/io/File/MaxPath.java +++ b/test/jdk/java/io/File/MaxPath.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,16 +24,14 @@ /* @test @bug 6481955 @summary Path length less than MAX_PATH (260) works on Windows + @requires (os.family == "windows") */ -import java.io.*; +import java.io.File; +import java.io.IOException; public class MaxPath { public static void main(String[] args) throws Exception { - String osName = System.getProperty("os.name"); - if (!osName.startsWith("Windows")) { - return; - } int MAX_PATH = 260; String dir = new File(".").getAbsolutePath() + "\\"; String padding = "1234567890123456789012345678901234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890"; diff --git a/test/jdk/java/io/File/MaxPathLength.java b/test/jdk/java/io/File/MaxPathLength.java index 0e0b099afd9ed..02b60f124d1d3 100644 --- a/test/jdk/java/io/File/MaxPathLength.java +++ b/test/jdk/java/io/File/MaxPathLength.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,30 +24,31 @@ /* @test @bug 4759207 4403166 4165006 4403166 6182812 6274272 7160013 @summary Test to see if win32 path length can be greater than 260 + @library .. /test/lib */ import java.io.*; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.DirectoryNotEmptyException; +import jdk.test.lib.Platform; public class MaxPathLength { private static String sep = File.separator; private static String pathComponent = sep + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; private static String fileName = - "areallylongfilenamethatsforsur"; - private static boolean isWindows = false; + "areallylongfilenamethatsforsur"; private static final int MAX_LENGTH = 256; + private static final int FILE_EXISTS_SLEEP = 100; + + private static final int MAX_PATH_COMPONENTS_WINDOWS = 20; + private static int counter = 0; public static void main(String[] args) throws Exception { - String osName = System.getProperty("os.name"); - if (osName.startsWith("Windows")) { - isWindows = true; - } for (int i = 4; i < 7; i++) { String name = fileName; @@ -59,14 +60,9 @@ public static void main(String[] args) throws Exception { } // test long paths on windows - // And these long pathes cannot be handled on Solaris and Mac platforms - if (isWindows) { - String name = fileName; - while (name.length() < MAX_LENGTH) { - testLongPath (20, name, false); - testLongPath (20, name, true); - name = getNextName(name); - } + // And these long paths cannot be handled on Linux and Mac platforms + if (Platform.isWindows()) { + testLongPath(); } } @@ -146,7 +142,7 @@ static void testLongPath(int max, String fn, if (flist == null || !fn.equals(flist[0].getName())) throw new RuntimeException ("File.listFiles() failed"); - if (isWindows && + if (Platform.isWindows() && !fu.getCanonicalPath().equals(f.getCanonicalPath())) throw new RuntimeException ("getCanonicalPath() failed"); @@ -162,7 +158,7 @@ static void testLongPath(int max, String fn, String abPath = f.getAbsolutePath(); if (!abPath.startsWith("\\\\") || abPath.length() < 1093) { - throw new RuntimeException ("File.renameTo() failed for lenth=" + throw new RuntimeException ("File.renameTo() failed for length=" + abPath.length()); } } else { @@ -189,7 +185,7 @@ static void testLongPath(int max, String fn, Files.deleteIfExists(p); // Test if the file is really deleted and wait for 1 second at most for (int j = 0; j < 10 && Files.exists(p); j++) { - Thread.sleep(100); + Thread.sleep(FILE_EXISTS_SLEEP); } } catch (DirectoryNotEmptyException ex) { // Give up the clean-up, let jtreg handle it. @@ -199,4 +195,13 @@ static void testLongPath(int max, String fn, } } } -} + + private static void testLongPath () throws Exception { + String name = fileName; + while (name.length() < MAX_LENGTH) { + testLongPath(MAX_PATH_COMPONENTS_WINDOWS, name, false); + testLongPath(MAX_PATH_COMPONENTS_WINDOWS, name, true); + name = getNextName(name); + } + } +} \ No newline at end of file diff --git a/test/jdk/java/io/File/SymLinks.java b/test/jdk/java/io/File/SymLinks.java index 967250c84305f..40205a4155770 100644 --- a/test/jdk/java/io/File/SymLinks.java +++ b/test/jdk/java/io/File/SymLinks.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -281,22 +281,7 @@ static void go() throws IOException { assertTrue(link2dir.isDirectory()); assertTrue(link2link2dir.isDirectory()); - // on Windows we test with the DOS hidden attribute set - if (System.getProperty("os.name").startsWith("Windows")) { - DosFileAttributeView view = Files - .getFileAttributeView(file.toPath(), DosFileAttributeView.class); - view.setHidden(true); - try { - assertTrue(file.isHidden()); - assertTrue(link2file.isHidden()); - assertTrue(link2link2file.isHidden()); - } finally { - view.setHidden(false); - } - assertFalse(file.isHidden()); - assertFalse(link2file.isHidden()); - assertFalse(link2link2file.isHidden()); - } + testDOSHiddenAttributes(); header("length"); @@ -362,6 +347,26 @@ static void go() throws IOException { } } + static void testDOSHiddenAttributes() throws IOException { + // on Windows we test with the DOS hidden attribute set + if (System.getProperty("os.name").startsWith("Windows")) { + header("testDOSHiddenAttributes"); + DosFileAttributeView view = Files + .getFileAttributeView(file.toPath(), DosFileAttributeView.class); + view.setHidden(true); + try { + assertTrue(file.isHidden()); + assertTrue(link2file.isHidden()); + assertTrue(link2link2file.isHidden()); + } finally { + view.setHidden(false); + } + assertFalse(file.isHidden()); + assertFalse(link2file.isHidden()); + assertFalse(link2link2file.isHidden()); + } + } + public static void main(String[] args) throws IOException { if (supportsSymLinks(top)) { try { diff --git a/test/jdk/java/io/File/TimeZoneLastModified.java b/test/jdk/java/io/File/TimeZoneLastModified.java index 11c61a24408b8..bd423de619b89 100644 --- a/test/jdk/java/io/File/TimeZoneLastModified.java +++ b/test/jdk/java/io/File/TimeZoneLastModified.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,15 +23,14 @@ /* * @test - * @bug 6212869 + * @bug 6212869 8347841 * @summary Determine if lastModified() works after TimeZone.setDefault() * @run main/othervm TimeZoneLastModified */ import java.io.File; -import java.util.Date; +import java.time.ZoneId; import java.util.TimeZone; -import java.text.SimpleDateFormat; public class TimeZoneLastModified { // Tue, 04 Jun 2002 13:56:50.002 GMT @@ -40,6 +39,9 @@ public class TimeZoneLastModified { public static void main(String[] args) throws Throwable { int failures = test(null); for (String timeZoneID : TimeZone.getAvailableIDs()) { + if (ZoneId.SHORT_IDS.containsKey(timeZoneID)) { + continue; + } failures += test(timeZoneID); } if (failures != 0) { diff --git a/test/jdk/java/io/File/WinDeviceName.java b/test/jdk/java/io/File/WinDeviceName.java index a0afe2cade964..0c357fa231f2a 100644 --- a/test/jdk/java/io/File/WinDeviceName.java +++ b/test/jdk/java/io/File/WinDeviceName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,6 +24,7 @@ /* @test @bug 6176051 4858457 @summary Check whether reserved names are handled correctly on Windows + @requires (os.family == "windows") */ import java.io.File; @@ -38,10 +39,6 @@ public class WinDeviceName { }; public static void main(String[] args) { String osName = System.getProperty("os.name"); - if (!osName.startsWith("Windows")) { - return; - } - for (int i = 0; i < devnames.length; i++) { String names[] = { devnames[i], devnames[i] + ".TXT", devnames[i].toLowerCase(), diff --git a/test/jdk/java/io/File/WinMaxPath.java b/test/jdk/java/io/File/WinMaxPath.java index 21dba4cc12c17..704b633395ae3 100644 --- a/test/jdk/java/io/File/WinMaxPath.java +++ b/test/jdk/java/io/File/WinMaxPath.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,15 +25,12 @@ @bug 6384833 @summary Check if appropriate exception FileNotFoundException gets thrown when the pathlengh exceeds the limit. + @requires (os.family == "windows") */ import java.io.*; public class WinMaxPath { public static void main(String[] args) throws Exception { - String osName = System.getProperty("os.name"); - if (!osName.startsWith("Windows")) { - return; - } try { char[] as = new char[65000]; java.util.Arrays.fill(as, 'a'); diff --git a/test/jdk/java/io/File/WinSpecialFiles.java b/test/jdk/java/io/File/WinSpecialFiles.java index a5bff3014959b..027aba769d908 100644 --- a/test/jdk/java/io/File/WinSpecialFiles.java +++ b/test/jdk/java/io/File/WinSpecialFiles.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,15 +25,12 @@ @bug 6192331 6348207 8202076 @summary Check if File.exists()/length() works correctly on Windows special files hiberfil.sys and pagefile.sys + @requires (os.family == "windows") */ import java.io.File; public class WinSpecialFiles { public static void main(String[] args) throws Exception { - String osName = System.getProperty("os.name"); - if (!osName.startsWith("Windows")) { - return; - } File root = new File("C:\\"); File[] dir = root.listFiles(); for (int i = 0; i < dir.length; i++) { diff --git a/test/jdk/java/io/File/createTempFile/SpecialTempFile.java b/test/jdk/java/io/File/createTempFile/SpecialTempFile.java index 532d3389da38f..a5d903cb4e2b6 100644 --- a/test/jdk/java/io/File/createTempFile/SpecialTempFile.java +++ b/test/jdk/java/io/File/createTempFile/SpecialTempFile.java @@ -117,10 +117,6 @@ public static void main(String[] args) throws Exception { // Test JDK-8013827 String[] resvPre = { "LPT1.package.zip", "com7.4.package.zip" }; String[] resvSuf = { ".temp", ".temp" }; - boolean exceptionExpected = - !(System.getProperty("os.name").matches("^.*[11|2025]$") || - new OSVersion(10, 0).compareTo(OSVersion.current()) > 0); - test("ReservedName", resvPre, resvSuf, exceptionExpected); System.out.println("OS name: " + System.getProperty("os.name") + "\n" + "OS version: " + OSVersion.current()); diff --git a/test/jdk/java/io/FileDescriptor/Sync.java b/test/jdk/java/io/FileDescriptor/Sync.java new file mode 100644 index 0000000000000..8af840bc6ed57 --- /dev/null +++ b/test/jdk/java/io/FileDescriptor/Sync.java @@ -0,0 +1,90 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8314120 + * @summary Sanity test for FileDescriptor.sync + * @library /test/lib + * @run main Sync + */ + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.SyncFailedException; + +public class Sync { + + static final String TEST_DIR = System.getProperty("test.dir", "."); + static final int TRIES = 1_000; + + public static void testWith(File file) throws Exception { + try (FileOutputStream fos = new FileOutputStream(file)) { + FileDescriptor fd = fos.getFD(); + for (int t = 0; t < TRIES; t++) { + fd.sync(); + } + } catch (SyncFailedException sfe) { + // Can happen on some filesystems, print it in the log + System.out.println("Sync failed (acceptable)"); + sfe.printStackTrace(); + } + } + + public static void main(String[] args) throws Exception { + // Run on platform threads + System.out.println("With platform threads"); + run(); + + System.out.println("Complete"); + } + + private static class AutoDelete implements AutoCloseable { + private final File file; + + public AutoDelete(File file) { + this.file = file; + } + + public File file() { + return file; + } + + @Override + public void close() throws Exception { + file.delete(); + } + } + + public static void run() throws Exception { + try (var w = new AutoDelete(new File(TEST_DIR, "FileDescriptorSync1"))) { + testWith(w.file()); + } + + try (var w = new AutoDelete(File.createTempFile("FileDescriptorSync2", "tmp"))) { + testWith(w.file()); + } + } +} diff --git a/test/jdk/java/io/FileOutputStream/ManyFiles.java b/test/jdk/java/io/FileOutputStream/ManyFiles.java index 5584a8f42ca81..3aad65de4326e 100644 --- a/test/jdk/java/io/FileOutputStream/ManyFiles.java +++ b/test/jdk/java/io/FileOutputStream/ManyFiles.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,15 +37,6 @@ public class ManyFiles { static int NUM_FILES = 2050; public static void main(String args[]) throws Exception { - // Linux does not yet allow opening this many files; Solaris - // 8 requires an explicit allocation of more file descriptors - // to succeed. Since this test is written to check for a - // Windows capability it is much simpler to only run it - // on that platform. - String osName = System.getProperty("os.name"); - if (osName.startsWith("Linux")) - return; - for (int n = 0; n < NUM_FILES; n++) { File f = new File("file" + count++); files.add(f); diff --git a/test/jdk/java/io/FilePermission/MergeName.java b/test/jdk/java/io/FilePermission/MergeName.java index c2eff765f2d1e..715b98512097d 100644 --- a/test/jdk/java/io/FilePermission/MergeName.java +++ b/test/jdk/java/io/FilePermission/MergeName.java @@ -81,7 +81,7 @@ private static void test(String file, String... actions) throws Exception { } content.add("};"); Files.write(Paths.get(file), content); - ProcessTools.executeTestJvm("-Djava.security.manager", + ProcessTools.executeTestJava("-Djava.security.manager", "-Djava.security.policy=" + file, "MergeName", "x", diff --git a/test/jdk/java/io/FilePermission/ReadFileOnPath.java b/test/jdk/java/io/FilePermission/ReadFileOnPath.java index acc66dac6abda..06e2c6c435ae0 100644 --- a/test/jdk/java/io/FilePermission/ReadFileOnPath.java +++ b/test/jdk/java/io/FilePermission/ReadFileOnPath.java @@ -87,7 +87,7 @@ static void test(String... args) throws Exception { cmds.addAll(List.of( "x", "modules/m", "modules/m/base", "modules/m/p/child", "-", "child", "/base", "../base")); - ProcessTools.executeTestJvm(cmds.toArray(new String[cmds.size()])) + ProcessTools.executeTestJava(cmds.toArray(new String[cmds.size()])) .shouldHaveExitValue(0); } } diff --git a/test/jdk/java/io/InputStreamReader/StatefulDecoderNearEOF.java b/test/jdk/java/io/InputStreamReader/StatefulDecoderNearEOF.java new file mode 100644 index 0000000000000..95759df552261 --- /dev/null +++ b/test/jdk/java/io/InputStreamReader/StatefulDecoderNearEOF.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8292043 + * @run testng StatefulDecoderNearEOF + * @summary Check MalformedInputException is thrown with stateful decoders + * with malformed input before EOF + */ + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.UncheckedIOException; +import java.nio.charset.CodingErrorAction; +import java.nio.charset.MalformedInputException; +import java.nio.charset.StandardCharsets; +import java.util.stream.IntStream; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertThrows; + +@Test +public class StatefulDecoderNearEOF { + + @DataProvider + public Object[][] inputs() { + return new Object[][] { + // BOM, followed by High surrogate (in UTF-16LE). + // First read() should throw an exception. + {new byte[] {(byte)0xff, (byte)0xfe, 0, (byte)0xd8}, 0}, + + // BOM, followed by 'A', 'B', 'C', then by High surrogate (in UTF-16LE). + // Fourth read() should throw an exception. + {new byte[] {(byte)0xff, (byte)0xfe, (byte)0x41, 0, (byte)0x42, 0, (byte)0x43, 0, 0, (byte)0xd8}, 3}, + }; + } + + @Test (dataProvider = "inputs") + public void testStatefulDecoderNearEOF(byte[] ba, int numSucessReads) throws IOException { + try (var r = new InputStreamReader( + new ByteArrayInputStream(ba), + StandardCharsets.UTF_16.newDecoder().onMalformedInput(CodingErrorAction.REPORT))) { + // Issue read() as many as numSucessReads which should not fail + IntStream.rangeClosed(1, numSucessReads).forEach(i -> { + try { + assertEquals(r.read(), (int)ba[i * 2]); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + + // Final dangling high surrogate should throw an exception + assertThrows(MalformedInputException.class, () -> r.read()); + } + } +} diff --git a/test/jdk/java/io/ObjectStreamClass/ObjectStreamClassCaching.java b/test/jdk/java/io/ObjectStreamClass/ObjectStreamClassCaching.java index 5dafdc181cd07..a6a28b82a33fd 100644 --- a/test/jdk/java/io/ObjectStreamClass/ObjectStreamClassCaching.java +++ b/test/jdk/java/io/ObjectStreamClass/ObjectStreamClassCaching.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -69,41 +69,13 @@ /* * @test id=Serial * @requires vm.gc.Serial - * @bug 8277072 + * @bug 8277072 8327180 * @library /test/lib/ * @summary ObjectStreamClass caches keep ClassLoaders alive (Serial GC) * @run testng/othervm -Xmx64m -XX:+UseSerialGC ObjectStreamClassCaching */ public class ObjectStreamClassCaching { - /** - * Test methods execute in same VM and are ordered by name. - * We test effectiveness 1st which is sensitive to previous allocations when ZGC is used. - */ - @Test - public void test1CacheEffectiveness() throws Exception { - var list = new ArrayList<>(); - var ref1 = lookupObjectStreamClass(TestClass1.class); - var ref2 = newWeakRef(); - boolean oome = false; - try { - while (!ref2.refersTo(null)) { - list.add(new byte[1024 * 1024 * 1]); // 1 MiB chunks - System.out.println("1MiB allocated..."); - Thread.sleep(5L); - } - } catch (OutOfMemoryError e) { - // release - list = null; - oome = true; - } - assertFalse(oome, "WeakReference was not cleared although memory was pressed hard"); - assertFalse(ref1.refersTo(null), - "Cache lost entry together with WeakReference being cleared although memory was not under pressure"); - System.gc(); - Thread.sleep(100L); - } - @Test public void test2CacheReleaseUnderMemoryPressure() throws Exception { var list = new ArrayList<>(); diff --git a/test/jdk/java/io/OutputStreamWriter/CloseWriterOnFailedFlush.java b/test/jdk/java/io/OutputStreamWriter/CloseWriterOnFailedFlush.java new file mode 100644 index 0000000000000..b87362ca37610 --- /dev/null +++ b/test/jdk/java/io/OutputStreamWriter/CloseWriterOnFailedFlush.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8136895 + * @summary Verify stream closed after write error in StreamEncoder::implClose + */ + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.nio.charset.MalformedInputException; +import java.nio.charset.StandardCharsets; + +public class CloseWriterOnFailedFlush { + private static final String STR_IOE = "Test"; // IOException + private static final String STR_MIE = "\ud83c"; // MalformedInputException + + public static void main(String[] args) throws IOException { + boolean failed = false; + + for (String s : new String[] {STR_IOE, STR_MIE}) { + System.out.println("string: " + s); + ErroringOutputStream stream = new ErroringOutputStream(); + try (Writer writer = new OutputStreamWriter(stream, + StandardCharsets.UTF_8.newEncoder())) { + writer.write(s); + } catch (IOException ex) { + Class exClass = ex.getClass(); + if (s.equals(STR_IOE) && exClass != IOException.class || + s.equals(STR_MIE) && exClass != MalformedInputException.class) + throw ex; + } + + if (stream.isOpen()) { + System.err.println("Stream is STILL open"); + failed = true; + } else { + System.out.println("Stream is closed"); + } + } + + if (failed) + throw new RuntimeException("Test failed"); + } + + private static class ErroringOutputStream extends OutputStream { + private boolean open = true; + + @Override + public void write(int b) throws IOException { + throw new IOException(); + } + + public boolean isOpen() { + return open; + } + + @Override + public void close() throws IOException { + open = false; + System.out.println("Closing"); + } + } +} diff --git a/test/jdk/java/io/pathNames/win32/SJIS.java b/test/jdk/java/io/pathNames/win32/SJIS.java index d1cad9dec9fe4..580f06330a6ed 100644 --- a/test/jdk/java/io/pathNames/win32/SJIS.java +++ b/test/jdk/java/io/pathNames/win32/SJIS.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,9 +26,15 @@ @summary Check that pathnames containing double-byte characters are not corrupted by win32 path processing @author Mark Reinhold + @library /test/lib */ -import java.io.*; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import jtreg.SkippedException; public class SJIS { @@ -47,8 +53,11 @@ public static void main(String[] args) throws Exception { /* This test is only valid on win32 systems that use the SJIS encoding */ if (File.separatorChar != '\\') return; - String enc = System.getProperty("file.encoding"); - if ((enc == null) || !enc.equals("SJIS")) return; + String enc = System.getProperty("native.encoding"); + if ((enc == null) || !enc.equals("MS932")) { + throw new SkippedException( + "native.encoding(%s) is not MS932".formatted(enc)); + } File f = new File("\u30BD"); if (f.exists()) rm(f); diff --git a/test/jdk/java/lang/Class/ProtectionDomainRace.java b/test/jdk/java/lang/Class/ProtectionDomainRace.java new file mode 100644 index 0000000000000..52b45d7ab105f --- /dev/null +++ b/test/jdk/java/lang/Class/ProtectionDomainRace.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8334394 + * @summary ensure there is no race condition in Class::protectionDomain + * @run main/othervm ProtectionDomainRace + */ +import javax.security.auth.Subject; +import java.security.PrivilegedAction; + +/** + * Without the code fix, this test would fail with + * java.lang.AssertionError: sun.security.util.ResourcesMgr (PD) + * at java.base/java.lang.invoke.MethodHandleImpl$BindCaller.checkInjectedInvoker(MethodHandleImpl.java:1209) + * at java.base/java.lang.invoke.MethodHandleImpl$BindCaller.makeInjectedInvoker(MethodHandleImpl.java:1110) + * at java.base/java.lang.invoke.MethodHandleImpl$BindCaller$1.computeValue(MethodHandleImpl.java:1117) + * at java.base/java.lang.invoke.MethodHandleImpl$BindCaller$1.computeValue(MethodHandleImpl.java:1114) + * at java.base/java.lang.ClassValue.getFromHashMap(ClassValue.java:229) + * at java.base/java.lang.ClassValue.getFromBackup(ClassValue.java:211) + * at java.base/java.lang.ClassValue.get(ClassValue.java:117) + * at java.base/java.lang.invoke.MethodHandleImpl$BindCaller.bindCallerWithInjectedInvoker(MethodHandleImpl.java:1089) + * at java.base/java.lang.invoke.MethodHandleImpl$BindCaller.bindCaller(MethodHandleImpl.java:1077) + * at java.base/java.lang.invoke.MethodHandleImpl.bindCaller(MethodHandleImpl.java:1032) + * at java.base/java.lang.invoke.MethodHandles$Lookup.maybeBindCaller(MethodHandles.java:4149) + * at java.base/java.lang.invoke.MethodHandles$Lookup.getDirectMethodCommon(MethodHandles.java:4133) + * at java.base/java.lang.invoke.MethodHandles$Lookup.getDirectMethodNoSecurityManager(MethodHandles.java:4077) + * at java.base/java.lang.invoke.MethodHandles$Lookup.getDirectMethodForConstant(MethodHandles.java:4326) + * at java.base/java.lang.invoke.MethodHandles$Lookup.linkMethodHandleConstant(MethodHandles.java:4274) + * at java.base/java.lang.invoke.MethodHandleNatives.linkMethodHandleConstant(MethodHandleNatives.java:628) + * at java.base/sun.security.util.ResourcesMgr.getBundle(ResourcesMgr.java:54) + * at java.base/sun.security.util.ResourcesMgr.getString(ResourcesMgr.java:40) + * at java.base/javax.security.auth.Subject.doAs(Subject.java:517) + * ... + * as the Class::protectionDomain might assign different objects to the (original) allPermDomain field. + */ +public class ProtectionDomainRace { + private static volatile Throwable failed = null; + public static void main(String[] args) throws Throwable { + PrivilegedAction pa = () -> null; + Thread[] threads = new Thread[100]; + for (int i = 0; i < 100; i++) { + threads[i] = new Thread(() -> { + try { + Subject.doAs(null, pa); + } catch (Throwable t) { + failed = t; + } + }); + threads[i].start(); + } + for (int i = 0; i < 100; i++) { + threads[i].join(); + } + if (failed != null) { + throw failed; + } + } +} diff --git a/test/jdk/java/lang/ClassLoader/securityManager/ClassLoaderTest.java b/test/jdk/java/lang/ClassLoader/securityManager/ClassLoaderTest.java index efb243bde979e..07f641a57d5a5 100644 --- a/test/jdk/java/lang/ClassLoader/securityManager/ClassLoaderTest.java +++ b/test/jdk/java/lang/ClassLoader/securityManager/ClassLoaderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,9 +26,12 @@ * @bug 8168423 * @summary Different types of ClassLoader running with(out) SecurityManager and * (in)valid security policy file. + * @modules java.base/jdk.internal.org.objectweb.asm + * java.base/jdk.internal.org.objectweb.asm.commons + * java.base/jdk.internal.module * @library /test/lib - * @modules java.base/jdk.internal.module * @build jdk.test.lib.util.JarUtils + * jdk.test.lib.util.ModuleInfoWriter * @build TestClassLoader TestClient * @run main ClassLoaderTest -noPolicy * @run main ClassLoaderTest -validPolicy @@ -48,9 +51,9 @@ import java.util.Collections; import java.util.LinkedList; import java.util.List; -import jdk.internal.module.ModuleInfoWriter; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.util.JarUtils; +import jdk.test.lib.util.ModuleInfoWriter; public class ClassLoaderTest { @@ -237,7 +240,7 @@ private void execute(String[] args, String status, String msg) throws Exception if (s.contains(" ")) { throw new RuntimeException("No spaces in args");} return !s.isEmpty(); }).toArray(String[]::new); - String out = ProcessTools.executeTestJvm(safeArgs).getOutput(); + String out = ProcessTools.executeTestJava(safeArgs).getOutput(); // Handle response. if ("PASS".equals(status) && out.contains(msg)) { System.out.println("PASS: Expected Result: " + msg); diff --git a/test/jdk/java/lang/Math/FusedMultiplyAddTests.java b/test/jdk/java/lang/Math/FusedMultiplyAddTests.java index 61a3bfb0de171..014a4dedc0fcc 100644 --- a/test/jdk/java/lang/Math/FusedMultiplyAddTests.java +++ b/test/jdk/java/lang/Math/FusedMultiplyAddTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 4851642 8253409 + * @bug 4851642 8253409 8362207 * @summary Tests for Math.fusedMac and StrictMath.fusedMac. * @build Tests * @build FusedMultiplyAddTests @@ -352,8 +352,9 @@ private static int testSimpleF() { {1.0f+Math.ulp(1.0f), 1.0f+Math.ulp(1.0f), -1.0f-2.0f*Math.ulp(1.0f), Math.ulp(1.0f)*Math.ulp(1.0f)}, - // Double-rounding if done in double precision - {0x1.fffffep23f, 0x1.000004p28f, 0x1.fep5f, 0x1.000002p52f} + // Double-rounding if done in double precision and/or double fma + {0x1.fffffep23f, 0x1.000004p28f, 0x1.fep5f, 0x1.000002p52f}, + {0x1.001p0f, 0x1.001p0f, 0x1p-100f, 0x1.002002p0f}, }; for (float[] testCase: testCases) diff --git a/test/jdk/java/lang/ModuleTests/AnnotationsTest.java b/test/jdk/java/lang/ModuleTests/AnnotationsTest.java index 7bb0d3e60566c..583b55e9bfce8 100644 --- a/test/jdk/java/lang/ModuleTests/AnnotationsTest.java +++ b/test/jdk/java/lang/ModuleTests/AnnotationsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,7 +36,6 @@ import java.util.List; import java.util.Set; -import jdk.internal.module.ModuleInfoWriter; import jdk.internal.org.objectweb.asm.AnnotationVisitor; import jdk.internal.org.objectweb.asm.Attribute; import jdk.internal.org.objectweb.asm.ClassReader; @@ -44,6 +43,7 @@ import jdk.internal.org.objectweb.asm.ClassWriter; import jdk.internal.org.objectweb.asm.Opcodes; import jdk.internal.org.objectweb.asm.commons.ModuleTargetAttribute; +import jdk.test.lib.util.ModuleInfoWriter; import org.testng.annotations.Test; import static org.testng.Assert.*; @@ -53,6 +53,8 @@ * @modules java.base/jdk.internal.org.objectweb.asm * java.base/jdk.internal.org.objectweb.asm.commons * java.base/jdk.internal.module + * @library /test/lib + * @build jdk.test.lib.util.ModuleInfoWriter * @run testng AnnotationsTest * @summary Basic test of annotations on modules */ diff --git a/test/jdk/java/lang/ProcessBuilder/Basic.java b/test/jdk/java/lang/ProcessBuilder/Basic.java index c4b4f9216cadb..c9c7814a1d2ff 100644 --- a/test/jdk/java/lang/ProcessBuilder/Basic.java +++ b/test/jdk/java/lang/ProcessBuilder/Basic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,15 +27,15 @@ * 5026830 5023243 5070673 4052517 4811767 6192449 6397034 6413313 * 6464154 6523983 6206031 4960438 6631352 6631966 6850957 6850958 * 4947220 7018606 7034570 4244896 5049299 8003488 8054494 8058464 - * 8067796 8224905 8263729 8265173 8272600 8231297 8282219 + * 8067796 8224905 8263729 8265173 8272600 8231297 8282219 8368192 * @key intermittent * @summary Basic tests for Process and Environment Variable code * @modules java.base/java.lang:open * @requires !vm.musl * @requires vm.flagless * @library /test/lib - * @run main/othervm/native/timeout=300 -Djava.security.manager=allow Basic - * @run main/othervm/native/timeout=300 -Djava.security.manager=allow -Djdk.lang.Process.launchMechanism=fork Basic + * @run main/othervm/native/timeout=360 -Djava.security.manager=allow Basic + * @run main/othervm/native/timeout=360 -Djava.security.manager=allow -Djdk.lang.Process.launchMechanism=fork Basic * @author Martin Buchholz */ @@ -203,7 +203,7 @@ private static void compareLinesIgnoreCase(String lines1, String lines2) { private static final Runtime runtime = Runtime.getRuntime(); - private static final String[] winEnvCommand = {"cmd.exe", "/c", "set"}; + private static final String[] winEnvCommand = {"cmd.exe", "/d", "/c", "set"}; private static String winEnvFilter(String env) { return env.replaceAll("\r", "") @@ -762,30 +762,29 @@ private static boolean matches(String str, String regex) { return Pattern.compile(regex).matcher(str).find(); } - private static String matchAndExtract(String str, String regex) { - Matcher matcher = Pattern.compile(regex).matcher(str); - if (matcher.find()) { - return matcher.group(); - } else { - return ""; - } + // Return the string with the matching regex removed + private static String matchAndRemove(String str, String regex) { + return Pattern.compile(regex) + .matcher(str) + .replaceAll(""); } /* Only used for Mac OS X -- - * Mac OS X (may) add the variable __CF_USER_TEXT_ENCODING to an empty - * environment. The environment variable JAVA_MAIN_CLASS_ may also - * be set in Mac OS X. - * Remove them both from the list of env variables + * Mac OS X (may) add the variables: __CF_USER_TEXT_ENCODING, JAVA_MAIN_CLASS_, + * and TMPDIR. + * Remove them from the list of env variables */ private static String removeMacExpectedVars(String vars) { // Check for __CF_USER_TEXT_ENCODING - String cleanedVars = vars.replace("__CF_USER_TEXT_ENCODING=" - +cfUserTextEncoding+",",""); + String cleanedVars = matchAndRemove(vars, + "__CF_USER_TEXT_ENCODING=" + cfUserTextEncoding + ","); // Check for JAVA_MAIN_CLASS_ - String javaMainClassStr - = matchAndExtract(cleanedVars, - "JAVA_MAIN_CLASS_\\d+=Basic.JavaChild,"); - return cleanedVars.replace(javaMainClassStr,""); + cleanedVars = matchAndRemove(cleanedVars, + "JAVA_MAIN_CLASS_\\d+=Basic.JavaChild,"); + // Check and remove TMPDIR + cleanedVars = matchAndRemove(cleanedVars, + "TMPDIR=[^,]*,"); + return cleanedVars; } /* Only used for AIX -- @@ -1904,7 +1903,9 @@ public void doIt(Map environ) { // Test Runtime.exec(...envp...) with envstrings without any `=' //---------------------------------------------------------------- try { - String[] cmdp = {"echo"}; + // In Windows CMD (`cmd.exe`), `echo/` outputs a newline (i.e., an empty line). + // Wrapping it with `cmd.exe /c` ensures compatibility in both native Windows and Cygwin environments. + String[] cmdp = Windows.is() ? new String[]{"cmd.exe", "/c", "echo/"} : new String[]{"echo"}; String[] envp = {"Hello", "World"}; // Yuck! Process p = Runtime.getRuntime().exec(cmdp, envp); equal(commandOutput(p), "\n"); diff --git a/test/jdk/java/lang/ProcessBuilder/CloseRace.java b/test/jdk/java/lang/ProcessBuilder/CloseRace.java index 1fa6d8d5e1562..f6391b099ffd9 100644 --- a/test/jdk/java/lang/ProcessBuilder/CloseRace.java +++ b/test/jdk/java/lang/ProcessBuilder/CloseRace.java @@ -23,10 +23,12 @@ /** * @test - * @bug 8024521 + * @bug 8024521 8315721 * @summary Closing ProcessPipeInputStream at the time the process exits is racy * and leads to data corruption. Run this test manually (as * an ordinary java program) with -Xmx8M to repro bug 8024521. + * @comment Don't allow -Xcomp, it disturbs the timing + * @requires (vm.compMode != "Xcomp") * @run main/othervm -Xmx8M -Dtest.duration=2 CloseRace */ diff --git a/test/jdk/java/lang/RuntimeTests/shutdown/ShutdownHooks.java b/test/jdk/java/lang/RuntimeTests/shutdown/ShutdownHooks.java index 6b2dc7b4dbfe7..36d3878fb41d5 100644 --- a/test/jdk/java/lang/RuntimeTests/shutdown/ShutdownHooks.java +++ b/test/jdk/java/lang/RuntimeTests/shutdown/ShutdownHooks.java @@ -61,7 +61,7 @@ public void testShutdownHooks() throws Exception { // Run in a new process in order to evaluate shutdown hook results String[] testCommand = new String[] {"-classpath", TEST_CLASSES, ShutdownHooksProcess.class.getName()}; - ProcessTools.executeTestJvm(testCommand).shouldHaveExitValue(0); + ProcessTools.executeTestJava(testCommand).shouldHaveExitValue(0); String errorMsg = "File exists despite shutdown hook has been run"; assertFalse(Files.exists(TEST_FILE.toPath()), errorMsg); diff --git a/test/jdk/java/lang/System/LoggerFinder/internal/BootstrapLogger/BootstrapLoggerTest.java b/test/jdk/java/lang/System/LoggerFinder/internal/BootstrapLogger/BootstrapLoggerTest.java index 9f17478f35f75..3d69f4199cef3 100644 --- a/test/jdk/java/lang/System/LoggerFinder/internal/BootstrapLogger/BootstrapLoggerTest.java +++ b/test/jdk/java/lang/System/LoggerFinder/internal/BootstrapLogger/BootstrapLoggerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,6 +39,7 @@ import java.security.ProtectionDomain; import java.util.concurrent.atomic.AtomicBoolean; import java.util.List; +import java.util.Locale; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -88,6 +89,8 @@ static enum TestCase { } public static void main(String[] args) throws Exception { + Locale savedLocale = Locale.getDefault(); + Locale.setDefault(Locale.US); if (args == null || args.length == 0) { args = new String[] { TestCase.SECURE_AND_WAIT.name() }; } @@ -373,6 +376,7 @@ public static void main(String[] args) throws Exception { LogStream.err.println("Not checking executor termination for " + test); } } finally { + Locale.setDefault(savedLocale); SimplePolicy.allowAll.set(Boolean.FALSE); } LogStream.err.println(test.name() + ": PASSED"); diff --git a/test/jdk/java/lang/instrument/BootClassPath/BootClassPathTest.sh b/test/jdk/java/lang/instrument/BootClassPath/BootClassPathTest.sh index 9ab260425cf17..e5cca341d64ed 100644 --- a/test/jdk/java/lang/instrument/BootClassPath/BootClassPathTest.sh +++ b/test/jdk/java/lang/instrument/BootClassPath/BootClassPathTest.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2004, 2023, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -74,7 +74,7 @@ case ${OS} in ;; esac -"$JAVA" ${TESTVMOPTS} -classpath "${TESTCLASSES}" Setup "${TESTCLASSES}" Agent "${CYGWIN}" +"$JAVA" ${TESTVMOPTS} ${TESTJAVAOPTS} -classpath "${TESTCLASSES}" Setup "${TESTCLASSES}" Agent "${CYGWIN}" BOOTDIR=`cat ${TESTCLASSES}/boot.dir` @@ -94,13 +94,13 @@ echo "Creating agent jar file..." echo "Running test..." -"${JAVA}" ${TESTVMOPTS} -javaagent:"${TESTCLASSES}"/Agent.jar -classpath "${TESTCLASSES}" DummyMain +"${JAVA}" ${TESTVMOPTS} ${TESTJAVAOPTS} -javaagent:"${TESTCLASSES}"/Agent.jar -classpath "${TESTCLASSES}" DummyMain result=$? echo "Cleanup..." "$JAVAC" ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d "${TESTCLASSES}" \ "${TESTSRC}"/Cleanup.java -"$JAVA" ${TESTVMOPTS} -classpath "${TESTCLASSES}" Cleanup "${BOOTDIR}" +"$JAVA" ${TESTVMOPTS} ${TESTJAVAOPTS} -classpath "${TESTCLASSES}" Cleanup "${BOOTDIR}" exit $result diff --git a/test/jdk/java/lang/instrument/ManifestTest.sh b/test/jdk/java/lang/instrument/ManifestTest.sh index daed5ad3c4c8d..9feb1de53f571 100644 --- a/test/jdk/java/lang/instrument/ManifestTest.sh +++ b/test/jdk/java/lang/instrument/ManifestTest.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -380,7 +380,7 @@ while read token; do echo "===== begin test case: $token =====" make_a_JAR "$token" - "${JAVA}" ${TESTVMOPTS} -javaagent:${AGENT}.jar \ + "${JAVA}" ${TESTVMOPTS} ${TESTJAVAOPTS} -javaagent:${AGENT}.jar \ -classpath "${TESTCLASSES}" ManifestTestApp > output.log 2>&1 result=$? diff --git a/test/jdk/java/lang/instrument/NegativeAgentRunner.java b/test/jdk/java/lang/instrument/NegativeAgentRunner.java index 9689c323114d1..2d585cc6e6a58 100644 --- a/test/jdk/java/lang/instrument/NegativeAgentRunner.java +++ b/test/jdk/java/lang/instrument/NegativeAgentRunner.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,7 +35,7 @@ public static void main(String argv[]) throws Exception { } String agentClassName = argv[0]; String excepClassName = argv[1]; - ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( "-javaagent:" + agentClassName + ".jar", "-Xmx128m", "-XX:-CreateCoredumpOnCrash", agentClassName); OutputAnalyzer output = new OutputAnalyzer(pb.start()); diff --git a/test/jdk/java/lang/instrument/PremainClass/PremainClassTest.java b/test/jdk/java/lang/instrument/PremainClass/PremainClassTest.java index ecd284fe6e0c1..216dbb94d8a63 100644 --- a/test/jdk/java/lang/instrument/PremainClass/PremainClassTest.java +++ b/test/jdk/java/lang/instrument/PremainClass/PremainClassTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,14 +39,10 @@ public class PremainClassTest { // a non ascii character. // Verify that the premain() function is executed correctly. public static void main(String[] a) throws Exception { - String testArgs = String.format( - "-javaagent:%s/Agent.jar -classpath %s DummyMain", - System.getProperty("test.src"), - System.getProperty("test.classes", ".")); + String testArgs = String.format("-javaagent:%s/Agent.jar", + System.getProperty("test.src")); - ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( - Utils.addTestJavaOpts(testArgs.split("\\s+"))); - System.out.println("testjvm.cmd:" + Utils.getCommandLine(pb)); + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(testArgs, "DummyMain"); OutputAnalyzer output = ProcessTools.executeProcess(pb); System.out.println("testjvm.stdout:" + output.getStdout()); diff --git a/test/jdk/java/lang/instrument/RedefineBigClass.sh b/test/jdk/java/lang/instrument/RedefineBigClass.sh index 1c2bfb6b0c1f4..19421301fb0b8 100644 --- a/test/jdk/java/lang/instrument/RedefineBigClass.sh +++ b/test/jdk/java/lang/instrument/RedefineBigClass.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -62,14 +62,14 @@ JAVAC="${COMPILEJAVA}"/bin/javac JAVA="${TESTJAVA}"/bin/java # Does this VM support the 'detail' level of NMT? -"${JAVA}" ${TESTVMOPTS} -XX:NativeMemoryTracking=detail -version +"${JAVA}" ${TESTVMOPTS} ${TESTJAVAOPTS} -XX:NativeMemoryTracking=detail -version if [ "$?" = 0 ]; then NMT=-XX:NativeMemoryTracking=detail else NMT=-XX:NativeMemoryTracking=summary fi -"${JAVA}" ${TESTVMOPTS} \ +"${JAVA}" ${TESTVMOPTS} ${TESTJAVAOPTS} \ -Xlog:redefine+class+load=debug,redefine+class+load+exceptions=info ${NMT} \ -javaagent:RedefineBigClassAgent.jar=BigClass.class \ -classpath "${TESTCLASSES}" RedefineBigClassApp \ diff --git a/test/jdk/java/lang/instrument/RedefineClassWithNativeMethod.sh b/test/jdk/java/lang/instrument/RedefineClassWithNativeMethod.sh index 0a029bd1ebc7c..8c269ab1929b8 100644 --- a/test/jdk/java/lang/instrument/RedefineClassWithNativeMethod.sh +++ b/test/jdk/java/lang/instrument/RedefineClassWithNativeMethod.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -58,7 +58,7 @@ fi JAVAC="${COMPILEJAVA}"/bin/javac JAVA="${TESTJAVA}"/bin/java -"${JAVA}" ${TESTVMOPTS} \ +"${JAVA}" ${TESTVMOPTS} ${TESTJAVAOPTS} \ -javaagent:RedefineClassWithNativeMethodAgent.jar=java/lang/Thread.class \ -classpath "${TESTCLASSES}" RedefineClassWithNativeMethodApp \ > output.log 2>&1 diff --git a/test/jdk/java/lang/instrument/RedefineMethodAddInvoke.sh b/test/jdk/java/lang/instrument/RedefineMethodAddInvoke.sh index b5864fbc26b88..284051d05d8d0 100644 --- a/test/jdk/java/lang/instrument/RedefineMethodAddInvoke.sh +++ b/test/jdk/java/lang/instrument/RedefineMethodAddInvoke.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -70,7 +70,7 @@ cp "${TESTSRC}"/RedefineMethodAddInvokeTarget_2.java \ mv RedefineMethodAddInvokeTarget.java RedefineMethodAddInvokeTarget_2.java mv RedefineMethodAddInvokeTarget.class RedefineMethodAddInvokeTarget_2.class -"${JAVA}" ${TESTVMOPTS} -javaagent:RedefineMethodAddInvokeAgent.jar \ +"${JAVA}" ${TESTVMOPTS} ${TESTJAVAOPTS} -javaagent:RedefineMethodAddInvokeAgent.jar \ -XX:+AllowRedefinitionToAddDeleteMethods \ -classpath "${TESTCLASSES}" RedefineMethodAddInvokeApp > output.log 2>&1 cat output.log diff --git a/test/jdk/java/lang/instrument/RedefineMethodDelInvoke.sh b/test/jdk/java/lang/instrument/RedefineMethodDelInvoke.sh index 9924af3d2a839..0df5a10474115 100644 --- a/test/jdk/java/lang/instrument/RedefineMethodDelInvoke.sh +++ b/test/jdk/java/lang/instrument/RedefineMethodDelInvoke.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -71,7 +71,7 @@ cp "${TESTSRC}"/RedefineMethodDelInvokeTarget_2.java \ mv RedefineMethodDelInvokeTarget.java RedefineMethodDelInvokeTarget_2.java mv RedefineMethodDelInvokeTarget.class RedefineMethodDelInvokeTarget_2.class -"${JAVA}" ${TESTVMOPTS} -javaagent:RedefineMethodDelInvokeAgent.jar \ +"${JAVA}" ${TESTVMOPTS} ${TESTJAVAOPTS} -javaagent:RedefineMethodDelInvokeAgent.jar \ -XX:+AllowRedefinitionToAddDeleteMethods \ -classpath "${TESTCLASSES}" RedefineMethodDelInvokeApp > output.log 2>&1 diff --git a/test/jdk/java/lang/instrument/RedefineMethodInBacktrace.sh b/test/jdk/java/lang/instrument/RedefineMethodInBacktrace.sh index d7cb2aa1c03ca..85d06fd486e66 100644 --- a/test/jdk/java/lang/instrument/RedefineMethodInBacktrace.sh +++ b/test/jdk/java/lang/instrument/RedefineMethodInBacktrace.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -68,7 +68,7 @@ cp "${TESTSRC}"/RedefineMethodInBacktraceTargetB_2.java \ RedefineMethodInBacktraceTargetB.java "${JAVAC}" ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . RedefineMethodInBacktraceTargetB.java -"${JAVA}" ${TESTVMOPTS} -javaagent:RedefineMethodInBacktraceAgent.jar \ +"${JAVA}" ${TESTVMOPTS} ${TESTJAVAOPTS} -javaagent:RedefineMethodInBacktraceAgent.jar \ -XX:+AllowRedefinitionToAddDeleteMethods \ -classpath "${TESTCLASSES}" RedefineMethodInBacktraceApp > output.log 2>&1 RUN_RESULT=$? diff --git a/test/jdk/java/lang/instrument/RedefineMethodWithAnnotations.sh b/test/jdk/java/lang/instrument/RedefineMethodWithAnnotations.sh index ed3ef4ef4122d..beb0486462352 100644 --- a/test/jdk/java/lang/instrument/RedefineMethodWithAnnotations.sh +++ b/test/jdk/java/lang/instrument/RedefineMethodWithAnnotations.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -67,7 +67,7 @@ cp "${TESTSRC}"/RedefineMethodWithAnnotationsAnnotations.java \ RedefineMethodWithAnnotationsTarget.java \ RedefineMethodWithAnnotationsAnnotations.java -"${JAVA}" ${TESTVMOPTS} -javaagent:RedefineMethodWithAnnotationsAgent.jar \ +"${JAVA}" ${TESTVMOPTS} ${TESTJAVAOPTS} -javaagent:RedefineMethodWithAnnotationsAgent.jar \ -XX:+UnlockDiagnosticVMOptions -XX:+StressLdcRewrite -XX:+IgnoreUnrecognizedVMOptions \ -cp "${TESTCLASSES}" RedefineMethodWithAnnotationsApp > output.log 2>&1 cat output.log diff --git a/test/jdk/java/lang/instrument/RedefineSubclassWithTwoInterfaces.sh b/test/jdk/java/lang/instrument/RedefineSubclassWithTwoInterfaces.sh index e583c9df469f8..fea99aecbaa4e 100644 --- a/test/jdk/java/lang/instrument/RedefineSubclassWithTwoInterfaces.sh +++ b/test/jdk/java/lang/instrument/RedefineSubclassWithTwoInterfaces.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -69,7 +69,7 @@ cp "${TESTSRC}"/RedefineSubclassWithTwoInterfacesImpl_1.java \ "${JAVAC}" ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \ -cp "${TESTCLASSES}" -d . \ RedefineSubclassWithTwoInterfacesTarget.java \ - RedefineSubclassWithTwoInterfacesImpl.java + RedefineSubclassWithTwoInterfacesImpl.java status="$?" if [ "$status" != 0 ]; then echo "FAIL: compile of *_1.java files failed." @@ -87,7 +87,7 @@ mv RedefineSubclassWithTwoInterfacesImpl.class \ echo "INFO: launching RedefineSubclassWithTwoInterfacesApp" -"${JAVA}" ${TESTVMOPTS} \ +"${JAVA}" ${TESTVMOPTS} ${TESTJAVAOPTS} \ -Xlog:redefine+class+load=trace,redefine+class+load+exceptions=trace,redefine+class+timer=trace,redefine+class+obsolete=trace,redefine+class+obsolete+metadata=trace,redefine+class+constantpool=trace \ -javaagent:RedefineSubclassWithTwoInterfacesAgent.jar \ -classpath "${TESTCLASSES}" \ diff --git a/test/jdk/java/lang/instrument/RetransformBigClass.sh b/test/jdk/java/lang/instrument/RetransformBigClass.sh index 455f42a3247df..31c92117e9336 100644 --- a/test/jdk/java/lang/instrument/RetransformBigClass.sh +++ b/test/jdk/java/lang/instrument/RetransformBigClass.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -62,14 +62,14 @@ JAVAC="${COMPILEJAVA}"/bin/javac JAVA="${TESTJAVA}"/bin/java # Does this VM support the 'detail' level of NMT? -"${JAVA}" ${TESTVMOPTS} -XX:NativeMemoryTracking=detail -version +"${JAVA}" ${TESTVMOPTS} ${TESTJAVAOPTS} -XX:NativeMemoryTracking=detail -version if [ "$?" = 0 ]; then NMT=-XX:NativeMemoryTracking=detail else NMT=-XX:NativeMemoryTracking=summary fi -"${JAVA}" ${TESTVMOPTS} \ +"${JAVA}" ${TESTVMOPTS} ${TESTJAVAOPTS} \ -Xlog:redefine+class+load=debug,redefine+class+load+exceptions=info ${NMT} \ -javaagent:RetransformBigClassAgent.jar=BigClass.class \ -classpath "${TESTCLASSES}" RetransformBigClassApp \ diff --git a/test/jdk/java/lang/instrument/StressGetObjectSizeTest.sh b/test/jdk/java/lang/instrument/StressGetObjectSizeTest.sh index a2ba29d238b65..3b9364f0cc371 100644 --- a/test/jdk/java/lang/instrument/StressGetObjectSizeTest.sh +++ b/test/jdk/java/lang/instrument/StressGetObjectSizeTest.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -51,7 +51,7 @@ fi JAVA="${TESTJAVA}"/bin/java -"${JAVA}" ${TESTVMOPTS} -javaagent:basicAgent.jar \ +"${JAVA}" ${TESTVMOPTS} ${TESTJAVAOPTS} -javaagent:basicAgent.jar \ -classpath "${TESTCLASSES}" StressGetObjectSizeApp StressGetObjectSizeApp \ > output.log 2>&1 cat output.log diff --git a/test/jdk/java/lang/instrument/appendToClassLoaderSearch/CircularityErrorTest.sh b/test/jdk/java/lang/instrument/appendToClassLoaderSearch/CircularityErrorTest.sh index 0589b43ee070c..0cfe6a641579c 100644 --- a/test/jdk/java/lang/instrument/appendToClassLoaderSearch/CircularityErrorTest.sh +++ b/test/jdk/java/lang/instrument/appendToClassLoaderSearch/CircularityErrorTest.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -77,4 +77,4 @@ $JAR ${TESTTOOLVMOPTS} -cfm "${TESTCLASSES}"/CircularityErrorTest.jar "${MANIFES # Finally we run the test (cd "${TESTCLASSES}"; - $JAVA ${TESTVMOPTS} -javaagent:CircularityErrorTest.jar CircularityErrorTest) + $JAVA ${TESTVMOPTS} ${TESTJAVAOPTS} -javaagent:CircularityErrorTest.jar CircularityErrorTest) diff --git a/test/jdk/java/lang/instrument/appendToClassLoaderSearch/ClassUnloadTest.sh b/test/jdk/java/lang/instrument/appendToClassLoaderSearch/ClassUnloadTest.sh index 71923152da4ac..d6aec21a2293e 100644 --- a/test/jdk/java/lang/instrument/appendToClassLoaderSearch/ClassUnloadTest.sh +++ b/test/jdk/java/lang/instrument/appendToClassLoaderSearch/ClassUnloadTest.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -80,5 +80,5 @@ $JAR ${TESTTOOLVMOPTS} -cfm "${TESTCLASSES}"/ClassUnloadTest.jar "${MANIFEST}" \ # Finally we run the test (cd "${TESTCLASSES}"; \ - $JAVA ${TESTVMOPTS} -Xlog:class+unload \ + $JAVA ${TESTVMOPTS} ${TESTJAVAOPTS} -Xlog:class+unload \ -javaagent:ClassUnloadTest.jar ClassUnloadTest "${OTHERDIR}" Bar.jar) diff --git a/test/jdk/java/lang/instrument/appendToClassLoaderSearch/run_tests.sh b/test/jdk/java/lang/instrument/appendToClassLoaderSearch/run_tests.sh index 51f89f09239a4..eb7abe71edc71 100644 --- a/test/jdk/java/lang/instrument/appendToClassLoaderSearch/run_tests.sh +++ b/test/jdk/java/lang/instrument/appendToClassLoaderSearch/run_tests.sh @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -56,7 +56,7 @@ failures=0 go() { echo '' - sh -xc "$JAVA ${TESTVMOPTS} -javaagent:Agent.jar -classpath SimpleTests.jar $1 $2 $3" 2>&1 + sh -xc "$JAVA ${TESTVMOPTS} ${TESTJAVAOPTS} -javaagent:Agent.jar -classpath SimpleTests.jar $1 $2 $3" 2>&1 if [ $? != 0 ]; then failures=`expr $failures + 1`; fi } @@ -85,11 +85,11 @@ mv Application.class InstrumentedApplication.bytes cp "${TESTSRC}"/Application.java . "${JAVAC}" ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . Application.java -sh -xc "$JAVA ${TESTVMOPTS} -classpath . -javaagent:Agent.jar DynamicTest" 2>&1 +sh -xc "$JAVA ${TESTVMOPTS} ${TESTJAVAOPTS} -classpath . -javaagent:Agent.jar DynamicTest" 2>&1 if [ $? != 0 ]; then failures=`expr $failures + 1`; fi # Repeat test with security manager -sh -xc "$JAVA ${TESTVMOPTS} -classpath . -javaagent:Agent.jar -Djava.security.manager DynamicTest" 2>&1 +sh -xc "$JAVA ${TESTVMOPTS} ${TESTJAVAOPTS} -classpath . -javaagent:Agent.jar -Djava.security.manager DynamicTest" 2>&1 if [ $? != 0 ]; then failures=`expr $failures + 1`; fi # diff --git a/test/jdk/java/lang/instrument/modules/AppendToClassPathModuleTest.java b/test/jdk/java/lang/instrument/modules/AppendToClassPathModuleTest.java index 1b0e1f50dd283..76655758b8b8a 100644 --- a/test/jdk/java/lang/instrument/modules/AppendToClassPathModuleTest.java +++ b/test/jdk/java/lang/instrument/modules/AppendToClassPathModuleTest.java @@ -24,6 +24,7 @@ /** * @test * @bug 8169909 + * @requires vm.flagless * @library src /test/lib * @build test/* * @run shell AppendToClassPathModuleTest.sh diff --git a/test/jdk/java/lang/management/MemoryMXBean/LowMemoryTest2.java b/test/jdk/java/lang/management/MemoryMXBean/LowMemoryTest2.java index 01a8a3b47c444..de1b22c4075a4 100644 --- a/test/jdk/java/lang/management/MemoryMXBean/LowMemoryTest2.java +++ b/test/jdk/java/lang/management/MemoryMXBean/LowMemoryTest2.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,24 @@ * is generated. */ +/* + * @test + * @bug 4982128 + * @summary Test low memory detection of non-heap memory pool + * + * @run main/othervm/timeout=600 -Xnoclassgc -XX:MaxMetaspaceSize=32m + * LowMemoryTest2 + */ + +/* + * @test + * @bug 4982128 + * @summary Test low memory detection of non-heap memory pool + * + * @run main/othervm/timeout=600 -Xnoclassgc -XX:MaxMetaspaceSize=16m + * -XX:CompressedClassSpaceSize=4m LowMemoryTest2 + */ + import java.lang.management.*; import javax.management.*; import javax.management.openmbean.CompositeData; diff --git a/test/jdk/java/lang/management/MemoryMXBean/LowMemoryTest2.sh b/test/jdk/java/lang/management/MemoryMXBean/LowMemoryTest2.sh deleted file mode 100644 index e549d74cef002..0000000000000 --- a/test/jdk/java/lang/management/MemoryMXBean/LowMemoryTest2.sh +++ /dev/null @@ -1,80 +0,0 @@ -# -# Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -# -# @test -# @bug 4982128 -# @summary Test low memory detection of non-heap memory pool -# -# @requires vm.gc=="null" -# -# @run build LowMemoryTest2 MemoryUtil -# @run shell/timeout=600 LowMemoryTest2.sh -# - -if [ ! -z "${TESTJAVA}" ] ; then - JAVA=${TESTJAVA}/bin/java - CLASSPATH=${TESTCLASSES} - export CLASSPATH -else - echo "--Error: TESTJAVA must be defined as the pathname of a jdk to test." - exit 1 -fi - -# Test execution - -failures=0 - -go() { - echo '' - sh -xc "$JAVA $TESTVMOPTS $*" 2>&1 - if [ $? != 0 ]; then failures=`expr $failures + 1`; fi -} - -# Run test with each GC configuration -# -# Notes: To ensure that metaspace fills up we disable class unloading. -# Also we set the max metaspace to 16MB/32MB - otherwise the test takes too -# long to run. The 32MB setting is required for running with CDS archive. - -go -noclassgc -XX:MaxMetaspaceSize=32m -XX:+UseSerialGC LowMemoryTest2 -go -noclassgc -XX:MaxMetaspaceSize=32m -XX:+UseParallelGC LowMemoryTest2 - -# Test class metaspace - might hit MaxMetaspaceSize instead if -# UseCompressedClassPointers is off or if 32 bit. -# -# (note: This is very shaky and that shakiness exposes a problem with MemoryMXBean: -# -# MemoryMXBean defines "used" "committed" and "max" (see java/lang/management/MemoryUsage.java) -# This abstraction misses a definition for "address space exhausted" which with the new Metaspace (jep387) -# can happen before committed/used hits any trigger. We now commit only on demand and therefore class loaders -# can sit atop of uncommitted address space, denying new loaders address space. In the old Metaspace, -# we would have committed the space right away and therefore the MemoryMXBean "committed" trigger -# would have fired. In the new Metaspace, we don't commit, so the MemoryMXBean does not fire. -go -noclassgc -XX:MaxMetaspaceSize=16m -XX:CompressedClassSpaceSize=4m LowMemoryTest2 - -echo '' -if [ $failures -gt 0 ]; - then echo "$failures test(s) failed"; - else echo "All test(s) passed"; fi -exit $failures diff --git a/test/jdk/java/lang/management/MemoryMXBean/MemoryManagement.java b/test/jdk/java/lang/management/MemoryMXBean/MemoryManagement.java index bf457cd477b13..b136b724b7102 100644 --- a/test/jdk/java/lang/management/MemoryMXBean/MemoryManagement.java +++ b/test/jdk/java/lang/management/MemoryMXBean/MemoryManagement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,9 +30,26 @@ * * @author Mandy Chung * + * @requires vm.gc == null | vm.gc == "G1" + * * @modules jdk.management - * @build MemoryManagement MemoryUtil - * @run main/othervm/timeout=600 -Xmn8m -XX:+IgnoreUnrecognizedVMOptions -XX:G1HeapRegionSize=1 -XX:-UseLargePages MemoryManagement + * + * @run main/othervm/timeout=600 -Xmn8m -XX:+IgnoreUnrecognizedVMOptions + * -XX:G1HeapRegionSize=1 -XX:-UseLargePages MemoryManagement + */ + +/* + * @test + * @bug 4530538 6980984 + * @summary Basic unit test of memory management testing: + * 1) setUsageThreshold() and getUsageThreshold() + * 2) test low memory detection on the old generation. + * + * @author Mandy Chung + * + * @modules jdk.management + * + * @run main/timeout=600 MemoryManagement */ import java.lang.management.*; diff --git a/test/jdk/java/lang/management/MemoryMXBean/MemoryManagementParallelGC.sh b/test/jdk/java/lang/management/MemoryMXBean/MemoryManagementParallelGC.sh deleted file mode 100644 index ebe7e52ca3bc2..0000000000000 --- a/test/jdk/java/lang/management/MemoryMXBean/MemoryManagementParallelGC.sh +++ /dev/null @@ -1,54 +0,0 @@ -# -# Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -# -# @test -# @bug 4530538 -# @summary Run MemoryManagement test with parallel GC -# @author Mandy Chung -# -# @requires vm.gc=="Parallel" | vm.gc=="null" -# -# @run build MemoryManagement -# @run shell/timeout=600 MemoryManagementParallelGC.sh -# - -#Set appropriate jdk - -if [ ! -z "${TESTJAVA}" ] ; then - jdk="$TESTJAVA" -else - echo "--Error: TESTJAVA must be defined as the pathname of a jdk to test." - exit 1 -fi - -runOne() -{ - echo "runOne $@" - $TESTJAVA/bin/java ${TESTVMOPTS} -classpath $TESTCLASSES $@ || exit 2 -} - -# Test MemoryManagement with parallel collector -runOne -XX:+UseParallelGC MemoryManagement - -exit 0 diff --git a/test/jdk/java/lang/management/MemoryMXBean/MemoryManagementSerialGC.sh b/test/jdk/java/lang/management/MemoryMXBean/MemoryManagementSerialGC.sh deleted file mode 100644 index f9de8f575bd0e..0000000000000 --- a/test/jdk/java/lang/management/MemoryMXBean/MemoryManagementSerialGC.sh +++ /dev/null @@ -1,54 +0,0 @@ -# -# Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -# -# @test -# @bug 4530538 -# @summary Run MemoryManagement test with serial GC -# @author Mandy Chung -# -# @requires vm.gc=="Serial" | vm.gc=="null" -# -# @run build MemoryManagement -# @run shell/timeout=600 MemoryManagementSerialGC.sh -# - -#Set appropriate jdk - -if [ ! -z "${TESTJAVA}" ] ; then - jdk="$TESTJAVA" -else - echo "--Error: TESTJAVA must be defined as the pathname of a jdk to test." - exit 1 -fi - -runOne() -{ - echo "runOne $@" - $TESTJAVA/bin/java ${TESTVMOPTS} -classpath $TESTCLASSES $@ || exit 2 -} - -# Test MemoryManagement with serial collector -runOne -XX:+UseSerialGC MemoryManagement - -exit 0 diff --git a/test/jdk/java/lang/management/MemoryMXBean/MemoryTest.java b/test/jdk/java/lang/management/MemoryMXBean/MemoryTest.java index a0828dc63bd15..d07f2f93fa0aa 100644 --- a/test/jdk/java/lang/management/MemoryMXBean/MemoryTest.java +++ b/test/jdk/java/lang/management/MemoryMXBean/MemoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/jdk/java/lang/management/MemoryMXBean/MemoryTestAllGC.sh b/test/jdk/java/lang/management/MemoryMXBean/MemoryTestAllGC.sh deleted file mode 100644 index ed330b2840e8e..0000000000000 --- a/test/jdk/java/lang/management/MemoryMXBean/MemoryTestAllGC.sh +++ /dev/null @@ -1,58 +0,0 @@ -# -# Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -# -# @test -# @bug 4530538 -# @summary -# @author Mandy Chung -# -# @requires vm.gc=="Parallel" | vm.gc=="null" -# -# @run compile MemoryTest.java -# @run shell MemoryTestAllGC.sh -# - -#Set appropriate jdk - -if [ ! -z "${TESTJAVA}" ] ; then - jdk="$TESTJAVA" -else - echo "--Error: TESTJAVA must be defined as the pathname of a jdk to test." - exit 1 -fi - -runOne() -{ - echo "runOne $@" - $TESTJAVA/bin/java ${TESTVMOPTS} -classpath $TESTCLASSES $@ || exit 2 -} - -# Test MemoryTest with default collector -runOne MemoryTest 2 3 - -# Test MemoryTest with parallel scavenger collector -runOne -XX:+UseParallelGC MemoryTest 2 3 - - -exit 0 diff --git a/test/jdk/java/lang/management/MemoryMXBean/Pending.java b/test/jdk/java/lang/management/MemoryMXBean/Pending.java index fb7a9e609e6c7..77e8eb9d35639 100644 --- a/test/jdk/java/lang/management/MemoryMXBean/Pending.java +++ b/test/jdk/java/lang/management/MemoryMXBean/Pending.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,6 +35,8 @@ * * @modules java.base/jdk.internal.misc * java.management + * + * @run main Pending */ import java.lang.management.*; diff --git a/test/jdk/java/lang/management/MemoryMXBean/PendingAllGC.sh b/test/jdk/java/lang/management/MemoryMXBean/PendingAllGC.sh deleted file mode 100644 index d1fbf2384cd99..0000000000000 --- a/test/jdk/java/lang/management/MemoryMXBean/PendingAllGC.sh +++ /dev/null @@ -1,57 +0,0 @@ -# -# Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -# -# @test -# @bug 4530538 -# @summary -# @author Mandy Chung -# @requires vm.gc=="null" -# @modules java.base/jdk.internal.misc -# java.management -# @run compile Pending.java -# @run shell PendingAllGC.sh -# - -#Set appropriate jdk - -if [ ! -z "${TESTJAVA}" ] ; then - jdk="$TESTJAVA" -else - echo "--Error: TESTJAVA must be defined as the pathname of a jdk to test." - exit 1 -fi - -runOne() -{ - echo "runOne $@" - $TESTJAVA/bin/java ${TESTVMOPTS} -classpath $TESTCLASSES $@ || exit 2 -} - -# Test Pending with default collector -runOne Pending - -# Test Pending with parallel scavenger collector -runOne -XX:+UseParallelGC Pending - -exit 0 diff --git a/test/jdk/java/lang/management/ThreadMXBean/ThreadCpuTime.java b/test/jdk/java/lang/management/ThreadMXBean/ThreadCpuTime.java index 61fff63936382..f8cab540465a5 100644 --- a/test/jdk/java/lang/management/ThreadMXBean/ThreadCpuTime.java +++ b/test/jdk/java/lang/management/ThreadMXBean/ThreadCpuTime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -177,6 +177,8 @@ private static void waitUntilThreadBlocked() } } } + // Account for threads using CPU for a few millis after their WAITING state is visible: + goSleep(500); } static class MyThread extends Thread { @@ -228,15 +230,6 @@ public void run() { " CurrentThreadCpuTime = " + time1 + " > ThreadCpuTime = " + time2); } -/************* - * FIXME: Seems that on Solaris-sparc, - * It occasionally returns a different current thread user time > thread user time - if (utime1 > utime2) { - throw new RuntimeException("TEST FAILED: " + getName() + - " CurrentThreadUserTime = " + utime1 + - " > ThreadUserTime = " + utime2); - } -*/ } } diff --git a/test/jdk/java/lang/management/ThreadMXBean/ThreadLists.java b/test/jdk/java/lang/management/ThreadMXBean/ThreadLists.java index 433a3f54c0d15..cadfff1097cbc 100644 --- a/test/jdk/java/lang/management/ThreadMXBean/ThreadLists.java +++ b/test/jdk/java/lang/management/ThreadMXBean/ThreadLists.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,9 +23,11 @@ /* * @test - * @bug 5047639 + * @bug 5047639 8132785 * @summary Check that the "java-level" APIs provide a consistent view of * the thread list + * @comment Must run in othervm mode to avoid interference from other tests. + * @run main/othervm ThreadLists */ import java.lang.management.ManagementFactory; import java.lang.management.ThreadMXBean; @@ -50,6 +52,19 @@ public static void main(String args[]) { // get the thread count int activeCount = top.activeCount(); + // Now enumerate to see if we find any extras yet. + // Ensure the array is big enough for a few extras. + Thread[] threads = new Thread[activeCount * 2]; + int newCount = top.enumerate(threads); + if (newCount != activeCount) { + System.out.println("Found different threads after enumeration:"); + } else { + System.out.println("Initial set of enumerated threads:"); + } + for (int i = 0; i < newCount; i++) { + System.out.println(" - Thread: " + threads[i].getName()); + } + Map stackTraces = Thread.getAllStackTraces(); ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); @@ -68,6 +83,11 @@ public static void main(String args[]) { if (activeCount != threadIds.length) failed = true; if (failed) { + System.out.println("Set of stack-traced threads:"); + for (Thread t : stackTraces.keySet()) { + System.out.println(" - Thread: " + + (t != null ? t.getName() : "null!")); + } throw new RuntimeException("inconsistent results"); } } diff --git a/test/jdk/java/lang/module/ClassFileVersionsTest.java b/test/jdk/java/lang/module/ClassFileVersionsTest.java index 9d723662b24ee..eec18fbc2ad92 100644 --- a/test/jdk/java/lang/module/ClassFileVersionsTest.java +++ b/test/jdk/java/lang/module/ClassFileVersionsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,11 @@ /** * @test - * @modules java.base/jdk.internal.module + * @modules java.base/jdk.internal.org.objectweb.asm + * java.base/jdk.internal.org.objectweb.asm.commons + * java.base/jdk.internal.module + * @library /test/lib + * @build jdk.test.lib.util.ModuleInfoWriter * @run testng ClassFileVersionsTest * @summary Test parsing of module-info.class with different class file versions */ @@ -36,7 +40,7 @@ import static java.lang.module.ModuleDescriptor.Requires.Modifier.*; -import jdk.internal.module.ModuleInfoWriter; +import jdk.test.lib.util.ModuleInfoWriter; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; diff --git a/test/jdk/java/lang/module/ConfigurationTest.java b/test/jdk/java/lang/module/ConfigurationTest.java index aa9f47a201911..ec6678842f037 100644 --- a/test/jdk/java/lang/module/ConfigurationTest.java +++ b/test/jdk/java/lang/module/ConfigurationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,10 +23,13 @@ /** * @test - * @library /test/lib * @modules java.base/jdk.internal.access + * java.base/jdk.internal.org.objectweb.asm + * java.base/jdk.internal.org.objectweb.asm.commons * java.base/jdk.internal.module + * @library /test/lib * @build ConfigurationTest + * jdk.test.lib.util.ModuleInfoWriter * jdk.test.lib.util.ModuleUtils * @run testng ConfigurationTest * @summary Basic tests for java.lang.module.Configuration @@ -47,10 +50,10 @@ import java.util.List; import java.util.Set; +import jdk.test.lib.util.ModuleInfoWriter; import jdk.test.lib.util.ModuleUtils; import jdk.internal.access.SharedSecrets; -import jdk.internal.module.ModuleInfoWriter; import jdk.internal.module.ModuleTarget; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; diff --git a/test/jdk/java/lang/module/ModuleDescriptorTest.java b/test/jdk/java/lang/module/ModuleDescriptorTest.java index 93c40ac51b66d..f1775908dcb0c 100644 --- a/test/jdk/java/lang/module/ModuleDescriptorTest.java +++ b/test/jdk/java/lang/module/ModuleDescriptorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,11 @@ /** * @test * @modules java.base/jdk.internal.access + * java.base/jdk.internal.org.objectweb.asm + * java.base/jdk.internal.org.objectweb.asm.commons * java.base/jdk.internal.module + * @library /test/lib + * @build jdk.test.lib.util.ModuleInfoWriter * @run testng ModuleDescriptorTest * @summary Basic test for java.lang.module.ModuleDescriptor and its builder */ @@ -56,7 +60,7 @@ import jdk.internal.access.JavaLangModuleAccess; import jdk.internal.access.SharedSecrets; -import jdk.internal.module.ModuleInfoWriter; +import jdk.test.lib.util.ModuleInfoWriter; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import static org.testng.Assert.*; diff --git a/test/jdk/java/lang/module/ModuleFinderTest.java b/test/jdk/java/lang/module/ModuleFinderTest.java index 79feee5694b7d..5052b53a187c7 100644 --- a/test/jdk/java/lang/module/ModuleFinderTest.java +++ b/test/jdk/java/lang/module/ModuleFinderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,8 +23,11 @@ /** * @test - * @modules java.base/jdk.internal.module - * @build ModuleFinderTest + * @modules java.base/jdk.internal.org.objectweb.asm + * java.base/jdk.internal.org.objectweb.asm.commons + * java.base/jdk.internal.module + * @library /test/lib + * @build ModuleFinderTest jdk.test.lib.util.ModuleInfoWriter * @run testng ModuleFinderTest * @summary Basic tests for java.lang.module.ModuleFinder */ @@ -45,7 +48,7 @@ import java.util.jar.JarOutputStream; import java.util.stream.Collectors; -import jdk.internal.module.ModuleInfoWriter; +import jdk.test.lib.util.ModuleInfoWriter; import org.testng.annotations.Test; import static org.testng.Assert.*; diff --git a/test/jdk/java/lang/module/ModuleNamesTest.java b/test/jdk/java/lang/module/ModuleNamesTest.java index 141f453b4f1a0..3e4e62e2937d6 100644 --- a/test/jdk/java/lang/module/ModuleNamesTest.java +++ b/test/jdk/java/lang/module/ModuleNamesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,11 @@ /** * @test * @modules java.base/jdk.internal.access + * java.base/jdk.internal.org.objectweb.asm + * java.base/jdk.internal.org.objectweb.asm.commons * java.base/jdk.internal.module + * @library /test/lib + * @build jdk.test.lib.util.ModuleInfoWriter * @run testng ModuleNamesTest * @summary Basic test of reading a module-info.class with module names that * are legal in class files but not legal in the Java Language @@ -42,7 +46,8 @@ import java.util.Set; import jdk.internal.access.SharedSecrets; -import jdk.internal.module.ModuleInfoWriter; + +import jdk.test.lib.util.ModuleInfoWriter; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; diff --git a/test/jdk/java/lang/module/MultiReleaseJarTest.java b/test/jdk/java/lang/module/MultiReleaseJarTest.java index 76a9b8f201df9..a0ff20d7d57ed 100644 --- a/test/jdk/java/lang/module/MultiReleaseJarTest.java +++ b/test/jdk/java/lang/module/MultiReleaseJarTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,9 +23,13 @@ /** * @test + * @modules java.base/jdk.internal.org.objectweb.asm + * java.base/jdk.internal.org.objectweb.asm.commons + * java.base/jdk.internal.module * @library /test/lib - * @modules java.base/jdk.internal.module - * @build MultiReleaseJarTest jdk.test.lib.util.JarUtils + * @build MultiReleaseJarTest + * jdk.test.lib.util.JarUtils + * jdk.test.lib.util.ModuleInfoWriter * @run testng MultiReleaseJarTest * @run testng/othervm -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarTest * @summary Basic test of modular JARs as multi-release JARs @@ -54,7 +58,7 @@ import java.util.jar.Attributes; import java.util.jar.Manifest; -import jdk.internal.module.ModuleInfoWriter; +import jdk.test.lib.util.ModuleInfoWriter; import jdk.test.lib.util.JarUtils; import org.testng.annotations.Test; diff --git a/test/jdk/java/lang/ref/SoftReference/Pin.java b/test/jdk/java/lang/ref/SoftReference/Pin.java index 5416b0f575a42..35aa4d9a51271 100644 --- a/test/jdk/java/lang/ref/SoftReference/Pin.java +++ b/test/jdk/java/lang/ref/SoftReference/Pin.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,9 +24,7 @@ /* @test * @bug 4076287 * @summary Invoking get on a SoftReference shouldn't pin the referent - * @run main/othervm -ms16m -mx16m Pin - * @author Peter Jones - * @author Mark Reinhold + * @run main/othervm -Xms16m -Xmx16m Pin */ diff --git a/test/jdk/java/math/BigDecimal/ToPlainStringTests.java b/test/jdk/java/math/BigDecimal/ToPlainStringTests.java index 7c7928ca85caa..0a1f617c0e578 100644 --- a/test/jdk/java/math/BigDecimal/ToPlainStringTests.java +++ b/test/jdk/java/math/BigDecimal/ToPlainStringTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 4984872 + * @bug 4984872 8318915 * @summary Basic tests of toPlainString method * @run main ToPlainStringTests * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+EliminateAutoBox -XX:AutoBoxCacheMax=20000 ToPlainStringTests @@ -67,6 +67,11 @@ public static void main(String argv[]) { {"12345678901234567890", "12345678901234567890"}, {"12345678901234567890e22", "123456789012345678900000000000000000000000"}, {"12345678901234567890e-22", "0.0012345678901234567890"}, + + {"12345e-1", "1234.5"}, + {"12345e-2", "123.45"}, + {"12345e-3", "12.345"}, + {"12345e-4", "1.2345"}, }; int errors = 0; @@ -89,6 +94,38 @@ public static void main(String argv[]) { } } + String[] failingCases = { + "1E-" + (Integer.MAX_VALUE - 0), // MAX_VALUE + 2 chars + "1E-" + (Integer.MAX_VALUE - 1), // MAX_VALUE + 1 chars + + "-1E-" + (Integer.MAX_VALUE - 0), // MAX_VALUE + 3 chars + "-1E-" + (Integer.MAX_VALUE - 1), // MAX_VALUE + 2 chars + "-1E-" + (Integer.MAX_VALUE - 2), // MAX_VALUE + 1 chars + + "123456789E-" + (Integer.MAX_VALUE - 0), // MAX_VALUE + 2 chars + "123456789E-" + (Integer.MAX_VALUE - 1), // MAX_VALUE + 1 chars + + "-123456789E-" + (Integer.MAX_VALUE - 0), // MAX_VALUE + 3 chars + "-123456789E-" + (Integer.MAX_VALUE - 1), // MAX_VALUE + 2 chars + "-123456789E-" + (Integer.MAX_VALUE - 2), // MAX_VALUE + 1 chars + + "1E" + Integer.MAX_VALUE, // MAX_VALUE + 1 chars + "123456789E" + Integer.MAX_VALUE, // MAX_VALUE + 9 chars + + "-1E" + Integer.MAX_VALUE, // MAX_VALUE + 2 chars + "-123456789E" + Integer.MAX_VALUE, // MAX_VALUE + 10 chars + }; + /* We expect pre-emptive OutOfMemoryErrors, nothing else */ + for (String failingCase : failingCases) { + try { + new BigDecimal(failingCase).toPlainString(); + } catch (OutOfMemoryError expected) { + continue; + } catch (Throwable ignored) { + } + ++errors; + } + if(errors > 0) throw new RuntimeException(errors + " errors during run."); } diff --git a/test/jdk/java/math/BigInteger/ByteArrayConstructorTest.java b/test/jdk/java/math/BigInteger/ByteArrayConstructorTest.java new file mode 100644 index 0000000000000..59784ee4c26ef --- /dev/null +++ b/test/jdk/java/math/BigInteger/ByteArrayConstructorTest.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.test.lib.RandomFactory; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.math.Accessor; +import java.math.BigInteger; +import java.util.Random; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @test + * @bug 8319174 + * @summary Exercises minimality of BigInteger.mag field (use -Dseed=X to set PRANDOM seed) + * @library /test/lib + * @build jdk.test.lib.RandomFactory + * @build java.base/java.math.Accessor + * @key randomness + * @run junit/othervm -DmaxDurationMillis=3000 ByteArrayConstructorTest + */ +public class ByteArrayConstructorTest { + + private static final int DEFAULT_MAX_DURATION_MILLIS = 3_000; + + public static final int N = 1_024; + + private static int maxDurationMillis; + private static final Random rnd = RandomFactory.getRandom(); + private volatile boolean stop = false; + + @BeforeAll + static void setMaxDurationMillis() { + maxDurationMillis = Math.max(maxDurationMillis(), 0); + } + + @Test + public void testNonNegative() throws InterruptedException { + byte[] ba = nonNegativeBytes(); + doBigIntegers(ba, ba[0]); // a mask to flip to 0 and back to ba[0] + } + + @Test + public void testNegative() throws InterruptedException { + byte[] ba = negativeBytes(); + doBigIntegers(ba, (byte) ~ba[0]); // a mask to flip to -1 and back to ba[0] + } + + /* + * Starts a thread th that keeps flipping the "sign" byte in the array ba + * from the original value to 0 or -1 and back, depending on ba[0] being + * non-negative or negative, resp. + * (ba is "big endian", the least significant byte is the one with the + * highest index.) + * + * In the meantime, the current thread keeps creating BigInteger instances + * with ba and checks that the internal invariant holds, despite the + * attempts by thread th to racily modify ba. + * It does so at least as indicated by maxDurationMillis. + * + * Finally, this thread requests th to stop and joins with it, either + * because maxDurationMillis has expired, or because of an invalid invariant. + */ + private void doBigIntegers(byte[] ba, byte mask) throws InterruptedException { + Thread th = new Thread(() -> { + while (!stop) { + ba[0] ^= mask; + } + }); + th.start(); + + try { + createBigIntegers(maxDurationMillis, ba); + } finally { + stop = true; + th.join(1_000); + } + } + + private void createBigIntegers(int maxDurationMillis, byte[] ba) { + long start = System.currentTimeMillis(); + while (System.currentTimeMillis() - start < maxDurationMillis) { + BigInteger bi = new BigInteger(ba); + int[] mag = Accessor.mag(bi); + assertTrue(mag.length == 0 || mag[0] != 0, + String.format("inconsistent BigInteger: mag.length=%d, mag[0]=%d", + mag.length, mag[0])); + } + } + + private byte[] nonNegativeBytes() { + byte[] ba = new byte[1 + N]; + byte b0; + while ((b0 = (byte) rnd.nextInt()) < 0); // empty body + rnd.nextBytes(ba); + ba[0] = b0; + /* Except for ba[0], fill most significant half with zeros. */ + for (int i = 1; i <= N / 2; ++i) { + ba[i] = 0; + } + return ba; + } + + private byte[] negativeBytes() { + byte[] ba = new byte[1 + N]; + byte b0; + while ((b0 = (byte) rnd.nextInt()) >= 0); // empty body + rnd.nextBytes(ba); + ba[0] = b0; + /* Except for ba[0], fill most significant half with -1 bytes. */ + for (int i = 1; i <= N / 2; ++i) { + ba[i] = -1; + } + return ba; + } + + private static int maxDurationMillis() { + try { + return Integer.parseInt(System.getProperty("maxDurationMillis", + Integer.toString(DEFAULT_MAX_DURATION_MILLIS))); + } catch (NumberFormatException ignore) { + } + return DEFAULT_MAX_DURATION_MILLIS; + } + +} diff --git a/test/hotspot/jtreg/vmTestbase/vm/share/process/MessageOutput.java b/test/jdk/java/math/BigInteger/java.base/java/math/Accessor.java similarity index 80% rename from test/hotspot/jtreg/vmTestbase/vm/share/process/MessageOutput.java rename to test/jdk/java/math/BigInteger/java.base/java/math/Accessor.java index 3a0b4549c4855..45a0f9449820c 100644 --- a/test/hotspot/jtreg/vmTestbase/vm/share/process/MessageOutput.java +++ b/test/jdk/java/math/BigInteger/java.base/java/math/Accessor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -20,10 +20,13 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package vm.share.process; -public interface MessageOutput { - public void start(); - public void send(String msg); - public void finish(); +package java.math; + +public class Accessor { + + public static int[] mag(BigInteger bi) { + return bi.mag; + } + } diff --git a/test/jdk/java/net/CookieHandler/B6644726.java b/test/jdk/java/net/CookieHandler/B6644726.java index b1caa182d734d..cc31f64c1b0e5 100644 --- a/test/jdk/java/net/CookieHandler/B6644726.java +++ b/test/jdk/java/net/CookieHandler/B6644726.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -46,8 +46,8 @@ private static void testCookieStore() throws Exception { // Let's test the default path lst.add("myCookie1=foo"); // Then some alternate expires format - lst.add("myCookie2=bar; path=/dir; expires=Tue, 19 Aug 2025 16:00:00 GMT"); - lst.add("myCookie3=test; path=/dir; expires=Tue Aug 19 2025 16:00:00 GMT-0100"); + lst.add("myCookie2=bar; path=/dir; expires=Fri, 19 Aug 4242 16:00:00 GMT"); + lst.add("myCookie3=test; path=/dir; expires=Fri Aug 19 4242 16:00:00 GMT-0100"); // Then Netscape draft cookies and domains lst.add("myCookie4=test; domain=.sun.com; path=/dir/foo"); HashMap> map = new HashMap>(); @@ -64,7 +64,8 @@ private static void testCookieStore() throws Exception { List cookies = cs.getCookies(); // There should be 5 cookies if all dates parsed correctly if (cookies.size() != 5) { - fail("Should have 5 cookies. Got only "+ cookies.size() + ", expires probably didn't parse correctly"); + fail("unexpected cookies: " + cookies + ", should have 5 cookies. Got only " + + cookies.size() + ", expires probably didn't parse correctly"); } // Check Path for first Cookie for (HttpCookie c : cookies) { diff --git a/test/jdk/java/net/CookieStoreTest.java b/test/jdk/java/net/CookieStoreTest.java new file mode 100644 index 0000000000000..50a5ab3fac1a5 --- /dev/null +++ b/test/jdk/java/net/CookieStoreTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.net.CookieManager; +import java.net.CookieStore; +import java.net.HttpCookie; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/* + * @test + * @bug 8365086 + * @summary verify that the implementation of java.net.CookieStore works + * as expected + * @run junit CookieStoreTest + */ +class CookieStoreTest { + + // neither the scheme, host nor the port matters in this test + private static final URI COOKIE_TEST_URI = URI.create("https://127.0.0.1:12345"); + + static List cookieStores() { + final List params = new ArrayList<>(); + // empty CookieStore + params.add(Arguments.of(new CookieManager().getCookieStore(), true)); + + final CookieStore cookieStore = new CookieManager().getCookieStore(); + cookieStore.add(COOKIE_TEST_URI, new HttpCookie("foo", "bar")); + // non-empty CookieStore + params.add(Arguments.of(cookieStore, false)); + + return params; + } + + /* + * Verify that the List returned by CookieStore.getURIs() is immutable. + */ + @ParameterizedTest + @MethodSource("cookieStores") + void testImmutableGetURIs(final CookieStore cookieStore, final boolean expectEmpty) { + final List uris = cookieStore.getURIs(); + assertNotNull(uris, "CookieStore.getURIs() returned null"); + assertEquals(expectEmpty, uris.isEmpty(), "CookieStore.getURIs() returned: " + uris); + assertImmutableList(uris, COOKIE_TEST_URI); + } + + /* + * Verify that the List returned by CookieStore.getCookies() is immutable. + */ + @ParameterizedTest + @MethodSource("cookieStores") + void testImmutableGetCookies(final CookieStore cookieStore, final boolean expectEmpty) { + final List cookies = cookieStore.getCookies(); + assertNotNull(cookies, "CookieStore.getCookies() returned null"); + assertEquals(expectEmpty, cookies.isEmpty(), "CookieStore.getCookies() returned: " + cookies); + assertImmutableList(cookies, new HttpCookie("hello", "world")); + } + + /* + * Verify that the List returned by CookieStore.get(URI) is immutable. + */ + @ParameterizedTest + @MethodSource("cookieStores") + void testImmutableGetCookiesForURI(final CookieStore cookieStore, final boolean expectEmpty) { + final List cookies = cookieStore.get(COOKIE_TEST_URI); + assertNotNull(cookies, "CookieStore.get(URI) returned null"); + assertEquals(expectEmpty, cookies.isEmpty(), "CookieStore.get(URI) returned: " + cookies); + assertImmutableList(cookies, new HttpCookie("hello", "world")); + } + + /* + * Verifies that the attempt to modify the contents of the list will fail + * due to the list being immutable. + */ + private static void assertImmutableList(final List list, T elementToAddOrRemove) { + // the list is expected to be immutable, so each of these operations must fail + assertThrows(UnsupportedOperationException.class, () -> list.add(elementToAddOrRemove)); + assertThrows(UnsupportedOperationException.class, () -> list.remove(elementToAddOrRemove)); + assertThrows(UnsupportedOperationException.class, list::clear); + // even try the replace operation when the list isn't empty + if (!list.isEmpty()) { + assertThrows(UnsupportedOperationException.class, () -> list.set(0, elementToAddOrRemove)); + } + } +} diff --git a/test/jdk/java/net/HttpURLConnection/HttpURLConnectionExpect100Test.java b/test/jdk/java/net/HttpURLConnection/HttpURLConnectionExpect100Test.java new file mode 100644 index 0000000000000..2a858d32fc23c --- /dev/null +++ b/test/jdk/java/net/HttpURLConnection/HttpURLConnectionExpect100Test.java @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8314978 + * @summary Multiple server call from connection failing with expect100 in + * getOutputStream + * @library /test/lib + * @run junit/othervm HttpURLConnectionExpect100Test + */ +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URL; +import java.net.HttpURLConnection; + +import jdk.test.lib.net.URIBuilder; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class HttpURLConnectionExpect100Test { + + private HttpServer server; + private int port; + static final String RESPONSE = "This is default response."; + + @BeforeAll + void setup() throws Exception { + server = HttpServer.create(); + port = server.getPort(); + } + + @AfterAll + void teardown() throws Exception { + server.close(); + } + + @Test + public void expect100ContinueHitCountTest() throws Exception { + server.resetHitCount(); + URL url = URIBuilder.newBuilder() + .scheme("http") + .loopback() + .port(port) + .toURL(); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("PUT"); + //send expect continue + conn.setRequestProperty("Expect", "100-continue"); + sendRequest(conn); + getHeaderField(conn); + assertEquals(1, server.getServerHitCount()); + // Server rejects the expect 100-continue request with 417 response + assertEquals(417, conn.getResponseCode()); + } + + @Test + public void defaultRequestHitCountTest() throws Exception { + server.resetHitCount(); + URL url = URIBuilder.newBuilder() + .scheme("http") + .loopback() + .port(port) + .toURL(); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("PUT"); + sendRequest(conn); + getHeaderField(conn); + assertEquals(1, server.getServerHitCount()); + assertEquals(200, conn.getResponseCode()); + try ( InputStream in = conn.getInputStream()) { + byte[] data = in.readAllBytes(); + assertEquals(RESPONSE.length(), data.length); + } + } + + private void sendRequest(final HttpURLConnection conn) throws Exception { + conn.setDoOutput(true); + conn.setFixedLengthStreamingMode(10); + byte[] payload = new byte[10]; + try ( OutputStream os = conn.getOutputStream()) { + os.write(payload); + os.flush(); + } catch (IOException e) { + // intentional, server will reject the expect 100 + } + } + + private void getHeaderField(final HttpURLConnection conn) { + // Call getHeaderFiels in loop, this should not hit server. + for (int i = 0; i < 5; i++) { + System.out.println("Getting: field" + i); + conn.getHeaderField("field" + i); + } + } + + static class HttpServer extends Thread { + + private final ServerSocket ss; + private static HttpServer inst; + private volatile int hitCount; + private volatile boolean isRunning; + private final int port; + + private HttpServer() throws IOException { + InetAddress loopback = InetAddress.getLoopbackAddress(); + ss = new ServerSocket(); + ss.bind(new InetSocketAddress(loopback, 0)); + port = ss.getLocalPort(); + isRunning = true; + } + + static HttpServer create() throws IOException { + if (inst != null) { + return inst; + } else { + inst = new HttpServer(); + inst.setDaemon(true); + inst.start(); + return inst; + } + } + + int getServerHitCount() { + return hitCount; + } + + void resetHitCount() { + hitCount = 0; + } + + int getPort() { + return port; + } + + void close() { + isRunning = false; + if (ss != null && !ss.isClosed()) { + try { + ss.close(); + } catch (IOException ex) { + } + } + } + + @Override + public void run() { + Socket client; + try { + while (isRunning) { + client = ss.accept(); + System.out.println(client.getRemoteSocketAddress().toString()); + hitCount++; + handleConnection(client); + } + } catch (IOException ex) { + // throw exception only if isRunning is true + if (isRunning) { + throw new RuntimeException(ex); + } + } finally { + if (ss != null && !ss.isClosed()) { + try { + ss.close(); + } catch (IOException ex) { + //ignore + } + } + } + } + + private void handleConnection(Socket client) throws IOException { + try ( BufferedReader in = new BufferedReader( + new InputStreamReader(client.getInputStream())); + PrintStream out = new PrintStream(client.getOutputStream())) { + handle_connection(in, out); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } finally { + try { + client.close(); + } catch (IOException e) { + } + } + } + + private void handle_connection(BufferedReader in, PrintStream out) + throws IOException, InterruptedException { + StringBuilder clientRequest = new StringBuilder(); + String line = null; + do { + line = in.readLine(); + clientRequest.append(line); + } while (line != null && line.length() != 0); + if (clientRequest.toString().contains("100-continue")) { + rejectExpect100Continue(out); + } else { + defaultResponse(out); + } + } + + private void rejectExpect100Continue(PrintStream out) { + out.print("HTTP/1.1 417 Expectation Failed\r\n"); + out.print("Server: Test-Server\r\n"); + out.print("Connection: close\r\n"); + out.print("Content-Length: 0\r\n"); + out.print("\r\n"); + out.flush(); + } + + private void defaultResponse(PrintStream out) { + // send the 200 OK + out.print("HTTP/1.1 200 OK\r\n"); + out.print("Server: Test-Server\r\n"); + out.print("Connection: close\r\n"); + out.print("Content-Length: " + RESPONSE.length() + "\r\n\r\n"); + out.print(RESPONSE); + out.flush(); + } + } +} diff --git a/test/jdk/java/net/HttpURLConnection/HttpURLConnectionExpectContinueTest.java b/test/jdk/java/net/HttpURLConnection/HttpURLConnectionExpectContinueTest.java index c9b8bc85b7081..e701e234d83f4 100644 --- a/test/jdk/java/net/HttpURLConnection/HttpURLConnectionExpectContinueTest.java +++ b/test/jdk/java/net/HttpURLConnection/HttpURLConnectionExpectContinueTest.java @@ -429,7 +429,7 @@ private HttpURLConnection createConnection() throws Exception { .port(control.serverSocket.getLocalPort()) .toURL(); - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY); connection.setDoOutput(true); connection.setReadTimeout(5000); connection.setUseCaches(false); diff --git a/test/jdk/java/net/Inet4Address/PingThis.java b/test/jdk/java/net/Inet4Address/PingThis.java index 885b60341f3f1..476ff0ad8a656 100644 --- a/test/jdk/java/net/Inet4Address/PingThis.java +++ b/test/jdk/java/net/Inet4Address/PingThis.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,27 +27,20 @@ /* @test * @bug 7163874 8133015 + * @summary InetAddress.isReachable is returning false for InetAdress 0.0.0.0 and ::0 + * @requires os.family != "windows" * @library /test/lib - * @summary InetAddress.isReachable is returning false - * for InetAdress 0.0.0.0 and ::0 * @run main PingThis * @run main/othervm -Djava.net.preferIPv4Stack=true PingThis */ -import java.net.Inet6Address; import java.net.InetAddress; -import java.net.NetworkInterface; import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; import java.util.List; import jdk.test.lib.net.IPSupport; public class PingThis { public static void main(String args[]) throws Exception { - if (System.getProperty("os.name").startsWith("Windows")) { - return; - } IPSupport.throwSkippedExceptionIfNonOperational(); List addrs = new ArrayList(); diff --git a/test/jdk/java/net/InetAddress/IsReachableViaLoopbackTest.java b/test/jdk/java/net/InetAddress/IsReachableViaLoopbackTest.java index 5364ba7895ec1..2f2ad0ac7ddf4 100644 --- a/test/jdk/java/net/InetAddress/IsReachableViaLoopbackTest.java +++ b/test/jdk/java/net/InetAddress/IsReachableViaLoopbackTest.java @@ -1,19 +1,44 @@ -import java.io.*; -import java.net.*; -import java.util.*; +/* + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.IOException; +import java.net.InetAddress; +import java.net.NetworkInterface; /** * @test * @bug 8135305 * @key intermittent + * @library /test/lib * @summary ensure we can't ping external hosts via loopback if + * @run main IsReachableViaLoopbackTest */ public class IsReachableViaLoopbackTest { public static void main(String[] args) { try { - InetAddress addr = InetAddress.getByName("localhost"); - InetAddress remoteAddr = InetAddress.getByName("bugs.openjdk.java.net"); + InetAddress addr = InetAddress.getLoopbackAddress(); + InetAddress remoteAddr = InetAddress.getByName("23.197.138.208"); // use literal address to avoid DNS checks if (!addr.isReachable(10000)) throw new RuntimeException("Localhost should always be reachable"); NetworkInterface inf = NetworkInterface.getByInetAddress(addr); @@ -31,7 +56,6 @@ public static void main(String[] args) { } else { System.out.println("inf == null"); } - } catch (IOException e) { throw new RuntimeException("Unexpected exception:" + e); } diff --git a/test/jdk/java/net/InetAddress/getOriginalHostName.java b/test/jdk/java/net/InetAddress/getOriginalHostName.java index 9f1e6e965d178..71567a7915d65 100644 --- a/test/jdk/java/net/InetAddress/getOriginalHostName.java +++ b/test/jdk/java/net/InetAddress/getOriginalHostName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,7 +41,9 @@ public class getOriginalHostName { public static void main(String[] args) throws Exception { final String HOST = "dummyserver.java.net"; InetAddress ia = null; - ia = InetAddress.getByName(HOST); + ia = getInetAddress(HOST); + if (ia != null) testInetAddress(ia, HOST); + ia = InetAddress.getByAddress(HOST, new byte[] { 1, 2, 3, 4}); testInetAddress(ia, HOST); ia = InetAddress.getByName("255.255.255.0"); testInetAddress(ia, null); @@ -53,6 +55,14 @@ public static void main(String[] args) throws Exception { testInetAddress(ia, ia.getHostName()); } + private static InetAddress getInetAddress(String host) { + try { + return InetAddress.getByName(host); + } catch (java.net.UnknownHostException uhe) { + System.out.println("Skipping " + host + " due to " + uhe); + return null; + } + } private static void testInetAddress(InetAddress ia, String expected) throws Exception { diff --git a/test/jdk/java/net/MulticastSocket/NoLoopbackPackets.java b/test/jdk/java/net/MulticastSocket/NoLoopbackPackets.java index 3e1bc8b55eace..36b6f12413d5a 100644 --- a/test/jdk/java/net/MulticastSocket/NoLoopbackPackets.java +++ b/test/jdk/java/net/MulticastSocket/NoLoopbackPackets.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,8 +24,9 @@ /* * @test * @bug 4742177 - * @library /test/lib * @summary Re-test IPv6 (and specifically MulticastSocket) with latest Linux & USAGI code + * @requires os.family != "windows" + * @library /test/lib */ import java.util.*; import java.net.*; @@ -33,20 +34,10 @@ import jdk.test.lib.net.IPSupport; public class NoLoopbackPackets { - private static String osname; - - static boolean isWindows() { - if (osname == null) - osname = System.getProperty("os.name"); - return osname.contains("Windows"); - } private static final String MESSAGE = "hello world (" + System.nanoTime() + ")"; + public static void main(String[] args) throws Exception { - if (isWindows()) { - System.out.println("The test only run on non-Windows OS. Bye."); - return; - } MulticastSocket msock = null; List failedGroups = new ArrayList(); diff --git a/test/jdk/java/net/MulticastSocket/PromiscuousIPv6.java b/test/jdk/java/net/MulticastSocket/PromiscuousIPv6.java index 4ac2dd8f16fe0..8fc23ea96aa86 100644 --- a/test/jdk/java/net/MulticastSocket/PromiscuousIPv6.java +++ b/test/jdk/java/net/MulticastSocket/PromiscuousIPv6.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,9 @@ /* * @test * @bug 8215294 - * @requires os.family == "linux" + * @requires os.family == "linux" & !(os.version ~= "3\\.10\\.0.*") + * @comment This test should only be run on Linux. + * The behavior under test is known NOT to work on Linux 3.10.0* kernels. * @library /test/lib * @build jdk.test.lib.NetworkConfiguration * PromiscuousIPv6 @@ -148,18 +150,6 @@ static void test(NetworkInterface nif, InetAddress group1, InetAddress group2) } public static void main(String args[]) throws IOException { - String os = System.getProperty("os.name"); - - if (!os.equals("Linux")) { - throw new SkippedException("This test should be run only on Linux"); - } else { - String osVersion = System.getProperty("os.version"); - String prefix = "3.10.0"; - if (osVersion.startsWith(prefix)) { - throw new SkippedException( - String.format("The behavior under test is known NOT to work on '%s' kernels", prefix)); - } - } NetworkConfiguration.printSystemConfiguration(System.out); List nifs = NetworkConfiguration.probe() diff --git a/test/jdk/java/net/MulticastSocket/SetOutgoingIf.java b/test/jdk/java/net/MulticastSocket/SetOutgoingIf.java index a2852963a5983..92f2fd05cbe28 100644 --- a/test/jdk/java/net/MulticastSocket/SetOutgoingIf.java +++ b/test/jdk/java/net/MulticastSocket/SetOutgoingIf.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,10 +24,11 @@ /* * @test * @bug 4742177 8241786 + * @summary Re-test IPv6 (and specifically MulticastSocket) with latest Linux & USAGI code + * @requires os.family != "windows" * @library /test/lib * @run main/othervm SetOutgoingIf * @run main/othervm -Djdk.net.usePlainDatagramSocketImpl SetOutgoingIf - * @summary Re-test IPv6 (and specifically MulticastSocket) with latest Linux & USAGI code */ import java.io.IOException; import java.net.*; @@ -37,7 +38,7 @@ public class SetOutgoingIf implements AutoCloseable { - private static String osname; + private final MulticastSocket SOCKET; private final int PORT; private final Map sendersMap = new ConcurrentHashMap<>(); @@ -50,12 +51,6 @@ private SetOutgoingIf() { } } - static boolean isWindows() { - if (osname == null) - osname = System.getProperty("os.name"); - return osname.contains("Windows"); - } - static boolean isMacOS() { return System.getProperty("os.name").contains("OS X"); } @@ -83,10 +78,6 @@ public void close() { } public void run() throws Exception { - if (isWindows()) { - System.out.println("The test only run on non-Windows OS. Bye."); - return; - } if (!hasIPv6()) { System.out.println("No IPv6 available. Bye."); diff --git a/test/jdk/java/net/NetworkInterface/IPv4Only.java b/test/jdk/java/net/NetworkInterface/IPv4Only.java index efa59aa76912a..3d12b3282bd2d 100644 --- a/test/jdk/java/net/NetworkInterface/IPv4Only.java +++ b/test/jdk/java/net/NetworkInterface/IPv4Only.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,12 +29,18 @@ */ -import java.net.*; -import java.util.*; + import jdk.test.lib.net.IPSupport; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.Enumeration; + public class IPv4Only { public static void main(String[] args) throws Exception { + IPSupport.printPlatformSupport(System.out); if (IPSupport.hasIPv4()) { System.out.println("Testing IPv4"); Enumeration nifs = NetworkInterface.getNetworkInterfaces(); @@ -43,7 +49,7 @@ public static void main(String[] args) throws Exception { Enumeration addrs = nif.getInetAddresses(); while (addrs.hasMoreElements()) { InetAddress hostAddr = addrs.nextElement(); - if ( hostAddr instanceof Inet6Address ){ + if (hostAddr instanceof Inet6Address){ throw new RuntimeException( "NetworkInterfaceV6List failed - found v6 address " + hostAddr.getHostAddress() ); } } diff --git a/test/jdk/java/net/ServerSocket/AnotherSelectFdsLimit.java b/test/jdk/java/net/ServerSocket/AnotherSelectFdsLimit.java index 391c5fb0094f2..f50a886140a46 100644 --- a/test/jdk/java/net/ServerSocket/AnotherSelectFdsLimit.java +++ b/test/jdk/java/net/ServerSocket/AnotherSelectFdsLimit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,7 @@ * @test * @bug 8035897 * @summary FD_SETSIZE should be set on macosx + * @requires os.family == "mac" * @run main/othervm AnotherSelectFdsLimit 1023 * @run main/othervm AnotherSelectFdsLimit 1024 * @run main/othervm AnotherSelectFdsLimit 1025 @@ -41,10 +42,6 @@ public class AnotherSelectFdsLimit { static final int DEFAULT_FDS_TO_USE = 1600; public static void main(String [] args) throws Exception { - if (!System.getProperty("os.name").contains("OS X")) { - System.out.println("Test only run on MAC. Exiting."); - return; - } int fdsToUse = DEFAULT_FDS_TO_USE; if (args.length == 1) diff --git a/test/jdk/java/net/ServerSocket/SelectFdsLimit.java b/test/jdk/java/net/ServerSocket/SelectFdsLimit.java index 2ecb1a28e1e39..5ee45bc2299f9 100644 --- a/test/jdk/java/net/ServerSocket/SelectFdsLimit.java +++ b/test/jdk/java/net/ServerSocket/SelectFdsLimit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ * @summary The total number of file descriptors is limited to * 1024(FDSET_SIZE) on MacOSX (the size of fd array passed to select() * call in java.net classes is limited to this value). + * @requires os.family == "mac" * @run main/othervm SelectFdsLimit * @author aleksej.efimov@oracle.com */ @@ -35,7 +36,6 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; -import java.io.InputStream; import java.net.ServerSocket; import java.net.SocketTimeoutException; @@ -72,12 +72,6 @@ static void openFiles(int fn, File f) throws FileNotFoundException, IOException public static void main(String [] args) throws IOException, FileNotFoundException { - //The bug 8021820 is a Mac specific and because of that test will pass on all - //other platforms - if (!System.getProperty("os.name").contains("OS X")) { - return; - } - //Create test directory with test files prepareTestEnv(); diff --git a/test/jdk/java/net/Socket/B8312065.java b/test/jdk/java/net/Socket/B8312065.java index 118792eada3e9..69194ddfa0147 100644 --- a/test/jdk/java/net/Socket/B8312065.java +++ b/test/jdk/java/net/Socket/B8312065.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Alibaba Group Holding Limited. All Rights Reserved. + * Copyright (c) 2023, 2025, Alibaba Group Holding Limited. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,8 @@ * @bug 8312065 * @summary Socket.connect does not timeout as expected when profiling (i.e. keep receiving signal) * @requires (os.family != "windows") + * @library /test/lib + * @build jtreg.SkippedException * @compile NativeThread.java * @run main/othervm/native/timeout=120 -Djdk.net.usePlainSocketImpl B8312065 */ @@ -37,6 +39,8 @@ import java.net.SocketTimeoutException; import java.util.concurrent.TimeUnit; +import jtreg.SkippedException; + public class B8312065 { public static void main(String[] args) throws Exception { System.loadLibrary("NativeThread"); @@ -83,6 +87,8 @@ public static void main(String[] args) throws Exception { System.out.println("Test FAILED: duration " + duration + " ms, expected >= " + timeoutMillis + " ms"); System.exit(1); } + } catch (java.net.ConnectException e) { + throw new SkippedException("Network setup issue, skip this test", e); } } } diff --git a/test/jdk/java/net/Socket/UdpSocket.java b/test/jdk/java/net/Socket/UdpSocket.java index a15f9255b450a..5d13c1f916a60 100644 --- a/test/jdk/java/net/Socket/UdpSocket.java +++ b/test/jdk/java/net/Socket/UdpSocket.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -47,6 +47,8 @@ @Test public class UdpSocket { + private static final int MAX_RETRIES = 3; + /** * Test using the Socket API to send/receive datagrams */ @@ -133,16 +135,21 @@ public void testMaxSockets() throws Exception { } - private Socket newUdpSocket() throws IOException { - Socket s = null; - - try { - s = new Socket(InetAddress.getLoopbackAddress(), 8000, false); - } catch (BindException unexpected) { - System.out.println("BindException caught retry Socket creation"); - s = new Socket(InetAddress.getLoopbackAddress(), 8000, false); + private Socket newUdpSocket() throws IOException, InterruptedException { + BindException unexpected = null; + for (int i=0; i < MAX_RETRIES; i++) { + try { + return new Socket(InetAddress.getLoopbackAddress(), 8000, false); + } catch (BindException be) { + unexpected = be; + if (i != MAX_RETRIES - 1) { + System.out.printf("BindException caught: retry Socket creation [%s/%s]%n", + i + 1, MAX_RETRIES); + Thread.sleep(10 + 10 * i); + } + } } - return s; + throw unexpected; } private void closeAll(Deque sockets) throws IOException { diff --git a/test/jdk/java/net/URLConnection/UNCTest.java b/test/jdk/java/net/URLConnection/UNCTest.java index 653b4c96699c3..2fe88dde65b8e 100644 --- a/test/jdk/java/net/URLConnection/UNCTest.java +++ b/test/jdk/java/net/URLConnection/UNCTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,17 +25,46 @@ * @test * @bug 4401485 * @requires (os.family == "windows") + * @modules java.base/sun.net.www.protocol.file + * @library /test/lib * @summary Check that URL.openConnection() doesn't open connection to UNC - * @run main UNCTest file://jdk/LOCAL-JAVA/jdk1.4/win/README.txt */ +import jtreg.SkippedException; +import sun.net.www.protocol.file.FileURLConnection; + +import java.io.File; +import java.net.InetAddress; +import java.net.URI; import java.net.URL; import java.net.URLConnection; public class UNCTest { - public static void main(String args[]) throws Exception { - URL url = new URL( args[0] ); + public static void main(String[] args) throws Exception { + // Get the "computer name" for this host + String hostName = InetAddress.getLocalHost().getHostName(); + + // UNC path which always exists with Administrative Shares enabled + String path = "\\\\" + hostName + "\\C$\\Windows"; + + // Skip test if Administrative Shares is disabled + if (! new File(path).exists()) { + throw new SkippedException("Administrative Shares not enabled"); + } + + // File URL for the UNC path + URL url = new URI("file://" + hostName + "/C$/Windows").toURL(); + + // Should open a FileURLConnection for the UNC path URLConnection conn = url.openConnection(); + + // Sanity check that the connection is a FileURLConnection + if (! (conn instanceof FileURLConnection)) { + throw new Exception("Expected FileURLConnection, instead got " + + conn.getClass().getName()); + } + + // Verify that the connection is not in an already connected state conn.setRequestProperty( "User-Agent", "Java" ); } } diff --git a/test/jdk/java/net/httpclient/AbstractConnectTimeoutHandshake.java b/test/jdk/java/net/httpclient/AbstractConnectTimeoutHandshake.java index b80ec09747a93..3febe7145b42e 100644 --- a/test/jdk/java/net/httpclient/AbstractConnectTimeoutHandshake.java +++ b/test/jdk/java/net/httpclient/AbstractConnectTimeoutHandshake.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,10 +44,11 @@ import java.net.http.HttpRequest.BodyPublishers; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; + +import jdk.test.lib.net.URIBuilder; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.DataProvider; -import static java.lang.String.format; import static java.lang.System.out; import static java.net.http.HttpClient.Builder.NO_PROXY; import static java.net.http.HttpClient.Version.HTTP_1_1; @@ -56,7 +57,7 @@ import static java.util.concurrent.TimeUnit.NANOSECONDS; import static org.testng.Assert.fail; -public abstract class AbstractConnectTimeoutHandshake { +abstract class AbstractConnectTimeoutHandshake { // The number of iterations each testXXXClient performs. static final int TIMES = 2; @@ -197,15 +198,15 @@ static void printResponse(HttpResponse response) { // -- Infrastructure - static String serverAuthority(Server server) { - return InetAddress.getLoopbackAddress().getHostName() + ":" - + server.getPort(); - } - @BeforeTest public void setup() throws Exception { server = new Server(); - httpsURI = URI.create("https://" + serverAuthority(server) + "/foo"); + httpsURI = URIBuilder.newBuilder() + .scheme("https") + .loopback() + .port(server.getPort()) + .path("/foo") + .build(); out.println("HTTPS URI: " + httpsURI); } diff --git a/test/jdk/java/net/httpclient/AbstractThrowingPublishers.java b/test/jdk/java/net/httpclient/AbstractThrowingPublishers.java index f2a47734e40c4..4079bd81e3e19 100644 --- a/test/jdk/java/net/httpclient/AbstractThrowingPublishers.java +++ b/test/jdk/java/net/httpclient/AbstractThrowingPublishers.java @@ -388,6 +388,24 @@ protected void testSanityImpl(String uri, boolean sameClient) String body = response.join().body(); assertEquals(body, Stream.of(BODY.split("\\|")).collect(Collectors.joining())); + if (!sameClient) { + // Wait for the client to be garbage collected. + // we use the ReferenceTracker API rather than HttpClient::close here, + // because these tests inject faults by throwing inside callbacks, which + // is more likely to get HttpClient::close wedged until jtreg times out. + // By using the ReferenceTracker, we will get some diagnosis about what + // is keeping the client alive if it doesn't get GC'ed within the + // expected time frame. + var tracker = TRACKER.getTracker(client); + client = null; + System.gc(); + System.out.println(now() + "waiting for client to shutdown: " + tracker.getName()); + System.err.println(now() + "waiting for client to shutdown: " + tracker.getName()); + var error = TRACKER.check(tracker, 10000); + if (error != null) throw error; + System.out.println(now() + "client shutdown normally: " + tracker.getName()); + System.err.println(now() + "client shutdown normally: " + tracker.getName()); + } } } @@ -470,6 +488,24 @@ private void testThrowing(String uri, boolean sameClient, if (response != null) { finisher.finish(where, response, thrower); } + if (!sameClient) { + // Wait for the client to be garbage collected. + // we use the ReferenceTracker API rather than HttpClient::close here, + // because these tests inject faults by throwing inside callbacks, which + // is more likely to get HttpClient::close wedged until jtreg times out. + // By using the ReferenceTracker, we will get some diagnosis about what + // is keeping the client alive if it doesn't get GC'ed within the + // expected time frame. + var tracker = TRACKER.getTracker(client); + client = null; + System.gc(); + System.out.println(now() + "waiting for client to shutdown: " + tracker.getName()); + System.err.println(now() + "waiting for client to shutdown: " + tracker.getName()); + var error = TRACKER.check(tracker, 10000); + if (error != null) throw error; + System.out.println(now() + "client shutdown normally: " + tracker.getName()); + System.err.println(now() + "client shutdown normally: " + tracker.getName()); + } } } diff --git a/test/jdk/java/net/httpclient/AbstractThrowingPushPromises.java b/test/jdk/java/net/httpclient/AbstractThrowingPushPromises.java index a9d1906430da4..b5230d48d1040 100644 --- a/test/jdk/java/net/httpclient/AbstractThrowingPushPromises.java +++ b/test/jdk/java/net/httpclient/AbstractThrowingPushPromises.java @@ -340,6 +340,24 @@ public void applyPushPromise(HttpRequest initiatingRequest, assertEquals(promisedBody, promised.uri().toASCIIString()); } assertEquals(3, pushPromises.size()); + if (!sameClient) { + // Wait for the client to be garbage collected. + // we use the ReferenceTracker API rather than HttpClient::close here, + // because these tests inject faults by throwing inside callbacks, which + // is more likely to get HttpClient::close wedged until jtreg times out. + // By using the ReferenceTracker, we will get some diagnosis about what + // is keeping the client alive if it doesn't get GC'ed within the + // expected time frame. + var tracker = TRACKER.getTracker(client); + client = null; + System.gc(); + System.out.println(now() + "waiting for client to shutdown: " + tracker.getName()); + System.err.println(now() + "waiting for client to shutdown: " + tracker.getName()); + var error = TRACKER.check(tracker, 10000); + if (error != null) throw error; + System.out.println(now() + "client shutdown normally: " + tracker.getName()); + System.err.println(now() + "client shutdown normally: " + tracker.getName()); + } } } @@ -425,6 +443,24 @@ private void testThrowing(String uri, boolean sameClient, if (response != null) { finisher.finish(where, req.uri(), response, thrower, promiseMap); } + if (!sameClient) { + // Wait for the client to be garbage collected. + // we use the ReferenceTracker API rather than HttpClient::close here, + // because these tests inject faults by throwing inside callbacks, which + // is more likely to get HttpClient::close wedged until jtreg times out. + // By using the ReferenceTracker, we will get some diagnosis about what + // is keeping the client alive if it doesn't get GC'ed within the + // expected time frame. + var tracker = TRACKER.getTracker(client); + client = null; + System.gc(); + System.out.println(now() + "waiting for client to shutdown: " + tracker.getName()); + System.err.println(now() + "waiting for client to shutdown: " + tracker.getName()); + var error = TRACKER.check(tracker, 10000); + if (error != null) throw error; + System.out.println(now() + "client shutdown normally: " + tracker.getName()); + System.err.println(now() + "client shutdown normally: " + tracker.getName()); + } } } diff --git a/test/jdk/java/net/httpclient/AbstractThrowingSubscribers.java b/test/jdk/java/net/httpclient/AbstractThrowingSubscribers.java index 893950608b1e6..dc7ca3fe9d542 100644 --- a/test/jdk/java/net/httpclient/AbstractThrowingSubscribers.java +++ b/test/jdk/java/net/httpclient/AbstractThrowingSubscribers.java @@ -21,9 +21,6 @@ * questions. */ -import com.sun.net.httpserver.HttpServer; -import com.sun.net.httpserver.HttpsConfigurator; -import com.sun.net.httpserver.HttpsServer; import jdk.test.lib.net.SimpleSSLContext; import org.testng.ITestContext; import org.testng.ITestResult; @@ -33,7 +30,6 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeTest; import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import java.io.BufferedReader; @@ -42,11 +38,8 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.io.UncheckedIOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; import java.net.URI; import java.net.http.HttpClient; -import java.net.http.HttpHeaders; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandler; @@ -63,7 +56,7 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Executor; import java.util.concurrent.Executors; -import java.util.concurrent.Flow; +import java.util.concurrent.Flow.Subscription; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; @@ -72,7 +65,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.httpclient.test.lib.common.HttpServerAdapters; -import jdk.httpclient.test.lib.http2.Http2TestServer; import static java.lang.System.out; import static java.lang.String.format; @@ -99,6 +91,7 @@ public abstract class AbstractThrowingSubscribers implements HttpServerAdapters String https2URI_chunk; static final int ITERATION_COUNT = 1; + static final int REPEAT_RESPONSE = 3; // a shared executor helps reduce the amount of threads created by the test static final Executor executor = new TestExecutor(Executors.newCachedThreadPool()); static final ConcurrentMap FAILURES = new ConcurrentHashMap<>(); @@ -313,7 +306,26 @@ protected void testSanityImpl(String uri, boolean sameClient) BodyHandlers.ofString()); HttpResponse response = client.send(req, handler); String body = response.body(); - assertEquals(URI.create(body).getPath(), URI.create(uri2).getPath()); + Stream.of(body.split("\n")).forEach(u -> + assertEquals(URI.create(u).getPath(), URI.create(uri2).getPath())); + if (!sameClient) { + // Wait for the client to be garbage collected. + // we use the ReferenceTracker API rather than HttpClient::close here, + // because these tests inject faults by throwing inside callbacks, which + // is more likely to get HttpClient::close wedged until jtreg times out. + // By using the ReferenceTracker, we will get some diagnosis about what + // is keeping the client alive if it doesn't get GC'ed within the + // expected time frame. + var tracker = TRACKER.getTracker(client); + client = null; + System.gc(); + System.out.println(now() + "waiting for client to shutdown: " + tracker.getName()); + System.err.println(now() + "waiting for client to shutdown: " + tracker.getName()); + var error = TRACKER.check(tracker, 10000); + if (error != null) throw error; + System.out.println(now() + "client shutdown normally: " + tracker.getName()); + System.err.println(now() + "client shutdown normally: " + tracker.getName()); + } } } @@ -425,6 +437,7 @@ private void testThrowing(String uri, boolean sameClient, throws Exception { HttpClient client = null; + var throwing = thrower; for (Where where : EnumSet.complementOf(excludes)) { if (!sameClient || client == null) @@ -433,6 +446,9 @@ private void testThrowing(String uri, boolean sameClient, HttpRequest req = HttpRequest. newBuilder(URI.create(uri2)) .build(); + + thrower = thrower(where, throwing); + BodyHandler handler = new ThrowingBodyHandler(where.select(thrower), handlers.get()); System.out.println("try throwing in " + where); @@ -450,17 +466,40 @@ private void testThrowing(String uri, boolean sameClient, response = client.send(req, handler); } catch (Error | Exception t) { // synchronous send will rethrow exceptions - Throwable throwable = t.getCause(); - assert throwable != null; - - if (thrower.test(throwable)) { - System.out.println(now() + "Got expected exception: " + throwable); - } else throw causeNotFound(where, t); + Throwable throwable = findCause(t, thrower); + if (throwable == null) throw causeNotFound(where, t); + System.out.println(now() + "Got expected exception: " + throwable); } } if (response != null) { finisher.finish(where, response, thrower); } + var tracker = TRACKER.getTracker(client); + if (!sameClient) { + // Wait for the client to be garbage collected. + // we use the ReferenceTracker API rather than HttpClient::close here, + // because these tests inject faults by throwing inside callbacks, which + // is more likely to get HttpClient::close wedged until jtreg times out. + // By using the ReferenceTracker, we will get some diagnosis about what + // is keeping the client alive if it doesn't get GC'ed within the + // expected time frame. + client = null; + System.gc(); + System.out.println(now() + "waiting for client to shutdown: " + tracker.getName()); + System.err.println(now() + "waiting for client to shutdown: " + tracker.getName()); + var error = TRACKER.check(tracker, 10000); + if (error != null) throw error; + System.out.println(now() + "client shutdown normally: " + tracker.getName()); + System.err.println(now() + "client shutdown normally: " + tracker.getName()); + } else { + System.out.println(now() + "waiting for operation to finish: " + tracker.getName()); + System.err.println(now() + "waiting for operation to finish: " + tracker.getName()); + var error = TRACKER.checkFinished(tracker, 10000); + if (error != null) throw error; + System.out.println(now() + "operation finished normally: " + tracker.getName()); + System.err.println(now() + "operation finished normally: " + tracker.getName()); + + } } } @@ -622,9 +661,38 @@ public BodySubscriber apply(HttpResponse.ResponseInfo rinfo) { } } + static final class BodyCFThrower implements Thrower { + final Thrower thrower; + BodyCFThrower(Thrower thrower) { + this.thrower = thrower; + } + @Override + public boolean test(Throwable throwable) { + // In case of BODY_CF we also cancel the stream, + // which can cause "Stream XX cancelled" to be reported + return thrower.test(throwable) || + throwable instanceof IOException io && ( + io.getMessage().matches("Stream [0-9]+ cancelled") || + io.getMessage().equals("subscription cancelled") + ); + } + @Override + public void accept(Where where) { + thrower.accept(where); + } + } + + static Thrower thrower(Where where, Thrower thrower) { + return switch (where) { + case BODY_CF -> new BodyCFThrower(thrower); + default -> thrower; + }; + } + static final class ThrowingBodySubscriber implements BodySubscriber { private final BodySubscriber subscriber; - volatile boolean onSubscribeCalled; + volatile Subscription subscription; + final CompletableFuture subscriptionCF = new CompletableFuture<>(); final Consumer throwing; ThrowingBodySubscriber(Consumer throwing, BodySubscriber subscriber) { this.throwing = throwing; @@ -632,17 +700,22 @@ static final class ThrowingBodySubscriber implements BodySubscriber { } @Override - public void onSubscribe(Flow.Subscription subscription) { + public void onSubscribe(Subscription subscription) { //out.println("onSubscribe "); - onSubscribeCalled = true; + this.subscription = subscription; throwing.accept(Where.ON_SUBSCRIBE); subscriber.onSubscribe(subscription); + subscriptionCF.complete(subscription); + } + + boolean onSubscribeCalled() { + return subscription != null; } @Override public void onNext(List item) { // out.println("onNext " + item); - assertTrue(onSubscribeCalled); + assertTrue(onSubscribeCalled(), "onNext called before onSubscribe"); throwing.accept(Where.ON_NEXT); subscriber.onNext(item); } @@ -650,7 +723,7 @@ public void onNext(List item) { @Override public void onError(Throwable throwable) { //out.println("onError"); - assertTrue(onSubscribeCalled); + assertTrue(onSubscribeCalled(), "onError called before onSubscribe"); throwing.accept(Where.ON_ERROR); subscriber.onError(throwable); } @@ -658,7 +731,7 @@ public void onError(Throwable throwable) { @Override public void onComplete() { //out.println("onComplete"); - assertTrue(onSubscribeCalled, "onComplete called before onSubscribe"); + assertTrue(onSubscribeCalled(), "onComplete called before onSubscribe"); throwing.accept(Where.ON_COMPLETE); subscriber.onComplete(); } @@ -666,10 +739,19 @@ public void onComplete() { @Override public CompletionStage getBody() { throwing.accept(Where.GET_BODY); + boolean shouldCancel = false; try { throwing.accept(Where.BODY_CF); } catch (Throwable t) { + shouldCancel = true; return CompletableFuture.failedFuture(t); + } finally { + // if a BodySubscriber returns a failed future, it + // should take responsibility for cancelling the + // subscription explicitly if needed. + if (shouldCancel) { + subscriptionCF.thenAccept(Subscription::cancel); + } } return subscriber.getBody(); } @@ -726,7 +808,7 @@ public void teardown() throws Exception { sharedClient == null ? null : sharedClient.toString(); sharedClient = null; Thread.sleep(100); - AssertionError fail = TRACKER.check(500); + AssertionError fail = TRACKER.check(5000); try { httpTestServer.stop(); httpsTestServer.stop(); @@ -749,10 +831,13 @@ public void handle(HttpTestExchange t) throws IOException { try (InputStream is = t.getRequestBody()) { is.readAllBytes(); } - byte[] resp = t.getRequestURI().toString().getBytes(StandardCharsets.UTF_8); - t.sendResponseHeaders(200, resp.length); //fixed content length + byte[] resp = (t.getRequestURI() + "\n").getBytes(StandardCharsets.UTF_8); + t.sendResponseHeaders(200, resp.length * 3); //fixed content length try (OutputStream os = t.getResponseBody()) { - os.write(resp); + for (int i=0 ; i < REPEAT_RESPONSE; i++) { + os.write(resp); + os.flush(); + } } } } @@ -761,13 +846,16 @@ static class HTTP_ChunkedHandler implements HttpTestHandler { @Override public void handle(HttpTestExchange t) throws IOException { out.println("HTTP_ChunkedHandler received request to " + t.getRequestURI()); - byte[] resp = t.getRequestURI().toString().getBytes(StandardCharsets.UTF_8); + byte[] resp = (t.getRequestURI() + "\n").getBytes(StandardCharsets.UTF_8); try (InputStream is = t.getRequestBody()) { is.readAllBytes(); } t.sendResponseHeaders(200, -1); // chunked/variable try (OutputStream os = t.getResponseBody()) { - os.write(resp); + for (int i=0 ; i < REPEAT_RESPONSE; i++) { + os.write(resp); + os.flush(); + } } } } diff --git a/test/jdk/java/net/httpclient/AsFileDownloadTest.java b/test/jdk/java/net/httpclient/AsFileDownloadTest.java index 72131bfa03c07..d191c8f4d4ad0 100644 --- a/test/jdk/java/net/httpclient/AsFileDownloadTest.java +++ b/test/jdk/java/net/httpclient/AsFileDownloadTest.java @@ -29,6 +29,8 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.UncheckedIOException; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.URI; @@ -77,7 +79,6 @@ * @run testng/othervm AsFileDownloadTest * @run testng/othervm/java.security.policy=AsFileDownloadTest.policy AsFileDownloadTest */ - public class AsFileDownloadTest { SSLContext sslContext; @@ -89,6 +90,7 @@ public class AsFileDownloadTest { String httpsURI; String http2URI; String https2URI; + final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; Path tempDir; @@ -164,37 +166,49 @@ void test(String uriString, String contentDispositionValue, String expectedFilen { out.printf("test(%s, %s, %s): starting", uriString, contentDispositionValue, expectedFilename); HttpClient client = HttpClient.newBuilder().sslContext(sslContext).build(); + TRACKER.track(client); + ReferenceQueue queue = new ReferenceQueue<>(); + WeakReference ref = new WeakReference<>(client, queue); + try { + URI uri = URI.create(uriString); + HttpRequest request = HttpRequest.newBuilder(uri) + .POST(BodyPublishers.ofString("May the luck of the Irish be with you!")) + .build(); - URI uri = URI.create(uriString); - HttpRequest request = HttpRequest.newBuilder(uri) - .POST(BodyPublishers.ofString("May the luck of the Irish be with you!")) - .build(); - - BodyHandler bh = ofFileDownload(tempDir.resolve(uri.getPath().substring(1)), - CREATE, TRUNCATE_EXISTING, WRITE); - HttpResponse response = client.send(request, bh); - - Path body = response.body(); - out.println("Got response: " + response); - out.println("Got body Path: " + body); - String fileContents = new String(Files.readAllBytes(response.body()), UTF_8); - out.println("Got body: " + fileContents); - - assertEquals(response.statusCode(),200); - assertEquals(body.getFileName().toString(), expectedFilename); - assertTrue(response.headers().firstValue("Content-Disposition").isPresent()); - assertEquals(response.headers().firstValue("Content-Disposition").get(), - contentDispositionValue); - assertEquals(fileContents, "May the luck of the Irish be with you!"); - - if (!body.toAbsolutePath().startsWith(tempDir.toAbsolutePath())) { - System.out.println("Tempdir = " + tempDir.toAbsolutePath()); - System.out.println("body = " + body.toAbsolutePath()); - throw new AssertionError("body in wrong location"); + BodyHandler bh = ofFileDownload(tempDir.resolve(uri.getPath().substring(1)), + CREATE, TRUNCATE_EXISTING, WRITE); + HttpResponse response = client.send(request, bh); + Path body = response.body(); + out.println("Got response: " + response); + out.println("Got body Path: " + body); + String fileContents = new String(Files.readAllBytes(response.body()), UTF_8); + out.println("Got body: " + fileContents); + + assertEquals(response.statusCode(), 200); + assertEquals(body.getFileName().toString(), expectedFilename); + assertTrue(response.headers().firstValue("Content-Disposition").isPresent()); + assertEquals(response.headers().firstValue("Content-Disposition").get(), + contentDispositionValue); + assertEquals(fileContents, "May the luck of the Irish be with you!"); + + if (!body.toAbsolutePath().startsWith(tempDir.toAbsolutePath())) { + System.out.println("Tempdir = " + tempDir.toAbsolutePath()); + System.out.println("body = " + body.toAbsolutePath()); + throw new AssertionError("body in wrong location"); + } + // additional checks unrelated to file download + caseInsensitivityOfHeaders(request.headers()); + caseInsensitivityOfHeaders(response.headers()); + } finally { + client = null; + System.gc(); + while (!ref.refersTo(null)) { + System.gc(); + if (queue.remove(100) == ref) break; + } + AssertionError failed = TRACKER.checkShutdown(1000); + if (failed != null) throw failed; } - // additional checks unrelated to file download - caseInsensitivityOfHeaders(request.headers()); - caseInsensitivityOfHeaders(response.headers()); } // --- Negative @@ -246,18 +260,32 @@ void negativeTest(String uriString, String contentDispositionValue) { out.printf("negativeTest(%s, %s): starting", uriString, contentDispositionValue); HttpClient client = HttpClient.newBuilder().sslContext(sslContext).build(); + TRACKER.track(client); + ReferenceQueue queue = new ReferenceQueue<>(); + WeakReference ref = new WeakReference<>(client, queue); - URI uri = URI.create(uriString); - HttpRequest request = HttpRequest.newBuilder(uri) - .POST(BodyPublishers.ofString("Does not matter")) - .build(); - - BodyHandler bh = ofFileDownload(tempDir, CREATE, TRUNCATE_EXISTING, WRITE); try { - HttpResponse response = client.send(request, bh); - fail("UNEXPECTED response: " + response + ", path:" + response.body()); - } catch (UncheckedIOException | IOException ioe) { - System.out.println("Caught expected: " + ioe); + URI uri = URI.create(uriString); + HttpRequest request = HttpRequest.newBuilder(uri) + .POST(BodyPublishers.ofString("Does not matter")) + .build(); + + BodyHandler bh = ofFileDownload(tempDir, CREATE, TRUNCATE_EXISTING, WRITE); + try { + HttpResponse response = client.send(request, bh); + fail("UNEXPECTED response: " + response + ", path:" + response.body()); + } catch (UncheckedIOException | IOException ioe) { + System.out.println("Caught expected: " + ioe); + } + } finally { + client = null; + System.gc(); + while (!ref.refersTo(null)) { + System.gc(); + if (queue.remove(100) == ref) break; + } + AssertionError failed = TRACKER.checkShutdown(1000); + if (failed != null) throw failed; } } diff --git a/test/jdk/java/net/httpclient/AsyncExecutorShutdown.java b/test/jdk/java/net/httpclient/AsyncExecutorShutdown.java new file mode 100644 index 0000000000000..1163c97fa3faa --- /dev/null +++ b/test/jdk/java/net/httpclient/AsyncExecutorShutdown.java @@ -0,0 +1,417 @@ +/* + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8277969 8299338 + * @summary Test for edge case where the executor is not accepting + * new tasks while the client is still running + * @library /test/lib /test/jdk/java/net/httpclient/lib + * @build jdk.httpclient.test.lib.http2.Http2TestServer jdk.test.lib.net.SimpleSSLContext + * ReferenceTracker + * @run testng/othervm + * -Djdk.internal.httpclient.debug=true + * -Djdk.httpclient.HttpClient.log=trace,headers,requests + * AsyncExecutorShutdown + */ +// -Djdk.internal.httpclient.debug=true + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UncheckedIOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpClient.Redirect; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandlers; +import java.nio.channels.ClosedChannelException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Function; +import jdk.httpclient.test.lib.common.HttpServerAdapters; +import jdk.httpclient.test.lib.http2.Http2TestServer; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLHandshakeException; + +import com.sun.net.httpserver.HttpServer; +import com.sun.net.httpserver.HttpsConfigurator; +import com.sun.net.httpserver.HttpsServer; +import jdk.test.lib.RandomFactory; +import jdk.test.lib.net.SimpleSSLContext; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static java.lang.System.out; +import static java.net.http.HttpClient.Builder.NO_PROXY; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.fail; + +public class AsyncExecutorShutdown implements HttpServerAdapters { + + static { + HttpServerAdapters.enableServerLogging(); + } + static final Random RANDOM = RandomFactory.getRandom(); + + SSLContext sslContext; + HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] + HttpTestServer httpsTestServer; // HTTPS/1.1 + HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + String httpURI; + String httpsURI; + String http2URI; + String https2URI; + + static final String MESSAGE = "AsyncExecutorShutdown message body"; + static final int ITERATIONS = 3; + + @DataProvider(name = "positive") + public Object[][] positive() { + return new Object[][] { + { httpURI, }, + { httpsURI, }, + { http2URI, }, + { https2URI, }, + }; + } + + static final AtomicLong requestCounter = new AtomicLong(); + final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; + + static Throwable getCause(Throwable t) { + while (t instanceof CompletionException || t instanceof ExecutionException) { + t = t.getCause(); + } + return t; + } + + static String readBody(InputStream in) { + try { + return new String(in.readAllBytes(), StandardCharsets.UTF_8); + } catch (IOException io) { + throw new UncheckedIOException(io); + } + } + + static void checkCause(String what, Throwable cause) { + Throwable t = cause; + Throwable accepted = null; + while (t != null) { + out.println(what + ": checking " + t); + if (t instanceof RejectedExecutionException) { + out.println(what + ": Got expected RejectedExecutionException in cause: " + t); + return; + } else if (t instanceof ClosedChannelException) { + out.println(what + ": Accepting ClosedChannelException as a valid cause: " + t); + accepted = t; + } + t = t.getCause(); + } + if (accepted != null) { + out.println(what + ": Didn't find expected RejectedExecutionException, " + + "but accepting " + accepted.getClass().getSimpleName() + + " as a valid cause: " + accepted); + return; + } + throw new AssertionError(what + ": Unexpected exception: " + cause, cause); + } + + @Test(dataProvider = "positive") + void testConcurrent(String uriString) throws Exception { + out.printf("%n---- starting (%s) ----%n", uriString); + ExecutorService executorService = Executors.newCachedThreadPool(); + ExecutorService readerService = Executors.newCachedThreadPool(); + HttpClient client = HttpClient.newBuilder() + .proxy(NO_PROXY) + .followRedirects(Redirect.ALWAYS) + .executor(executorService) + .sslContext(sslContext) + .build(); + TRACKER.track(client); + assert client.executor().isPresent(); + + int step = RANDOM.nextInt(ITERATIONS); + try { + List> bodies = new ArrayList<>(); + for (int i = 0; i < ITERATIONS; i++) { + URI uri = URI.create(uriString + "/concurrent/iteration-" + i); + HttpRequest request = HttpRequest.newBuilder(uri) + .header("X-uuid", "uuid-" + requestCounter.incrementAndGet()) + .build(); + out.printf("Iteration %d request: %s%n", i, request.uri()); + CompletableFuture> responseCF; + CompletableFuture bodyCF; + final int si = i; + try { + responseCF = client.sendAsync(request, BodyHandlers.ofInputStream()) + .thenApply((response) -> { + out.println(si + ": Got response: " + response); + assertEquals(response.statusCode(), 200); + return response; + }); + bodyCF = responseCF.thenApplyAsync(HttpResponse::body, readerService) + .thenApply(AsyncExecutorShutdown::readBody) + .thenApply((s) -> { assertEquals(s, MESSAGE); return s;}); + } catch (RejectedExecutionException x) { + out.println(i + ": Got expected exception: " + x); + continue; + } + long sleep = RANDOM.nextLong(5); + if (sleep > 0) { + out.printf("%d: sleeping %d ms%n", i, sleep); + Thread.sleep(sleep); + } + if (i == step) { + out.printf("%d: shutting down executor now%n", i, sleep); + executorService.shutdownNow(); + } + var cf = bodyCF.exceptionally((t) -> { + Throwable cause = getCause(t); + out.println(si + ": Got expected exception: " + cause); + if (UncheckedIOException.class.isAssignableFrom(cause.getClass())) { + if (cause.getCause() != null) { + out.println(si + ": Got expected exception: " + cause); + cause = cause.getCause(); + } + } + checkCause(String.valueOf(si), cause); + return null; + }); + bodies.add(cf); + } + CompletableFuture.allOf(bodies.toArray(new CompletableFuture[0])).get(); + } finally { + client = null; + executorService.awaitTermination(2000, TimeUnit.MILLISECONDS); + readerService.shutdown(); + readerService.awaitTermination(2000, TimeUnit.MILLISECONDS); + } + } + + @Test(dataProvider = "positive") + void testSequential(String uriString) throws Exception { + out.printf("%n---- starting (%s) ----%n", uriString); + ExecutorService executorService = Executors.newCachedThreadPool(); + ExecutorService readerService = Executors.newCachedThreadPool(); + HttpClient client = HttpClient.newBuilder() + .proxy(NO_PROXY) + .followRedirects(Redirect.ALWAYS) + .executor(executorService) + .sslContext(sslContext) + .build(); + TRACKER.track(client); + assert client.executor().isPresent(); + + int step = RANDOM.nextInt(ITERATIONS); + out.printf("will shutdown executor in step %d%n", step); + try { + for (int i = 0; i < ITERATIONS; i++) { + URI uri = URI.create(uriString + "/sequential/iteration-" + i); + HttpRequest request = HttpRequest.newBuilder(uri) + .header("X-uuid", "uuid-" + requestCounter.incrementAndGet()) + .build(); + out.printf("Iteration %d request: %s%n", i, request.uri()); + final int si = i; + CompletableFuture> responseCF; + CompletableFuture bodyCF; + try { + responseCF = client.sendAsync(request, BodyHandlers.ofInputStream()) + .thenApply((response) -> { + out.println(si + ": Got response: " + response); + assertEquals(response.statusCode(), 200); + return response; + }); + bodyCF = responseCF.thenApplyAsync(HttpResponse::body, readerService) + .thenApply(AsyncExecutorShutdown::readBody) + .thenApply((s) -> {assertEquals(s, MESSAGE); return s;}) + .thenApply((s) -> {out.println(si + ": Got body: " + s); return s;}); + } catch (RejectedExecutionException x) { + out.println(i + ": Got expected exception: " + x); + continue; + } + long sleep = RANDOM.nextLong(5); + if (sleep > 0) { + out.printf("%d: sleeping %d ms%n", i, sleep); + Thread.sleep(sleep); + } + if (i == step) { + out.printf("%d: shutting down executor now%n", i, sleep); + executorService.shutdownNow(); + } + bodyCF.handle((r,t) -> { + if (t != null) { + try { + Throwable cause = getCause(t); + out.println(si + ": Got expected exception: " + cause); + if (UncheckedIOException.class.isAssignableFrom(cause.getClass())) { + if (cause.getCause() != null) { + out.println(si + ": Got expected exception: " + cause); + cause = cause.getCause(); + } + } + checkCause(String.valueOf(si), cause); + } catch (Throwable ase) { + return CompletableFuture.failedFuture(ase); + } + return CompletableFuture.completedFuture(null); + } else { + return CompletableFuture.completedFuture(r); + } + }).thenCompose((c) -> c).get(); + } + } finally { + client = null; + executorService.awaitTermination(2000, TimeUnit.MILLISECONDS); + readerService.shutdown(); + readerService.awaitTermination(2000, TimeUnit.MILLISECONDS); + } + } + + // -- Infrastructure + + @BeforeTest + public void setup() throws Exception { + out.println("\n**** Setup ****\n"); + sslContext = new SimpleSSLContext().get(); + if (sslContext == null) + throw new AssertionError("Unexpected null sslContext"); + + InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); + + httpTestServer = HttpTestServer.of(HttpServer.create(sa, 0)); + httpTestServer.addHandler(new ServerRequestHandler(), "/http1/exec/"); + httpURI = "http://" + httpTestServer.serverAuthority() + "/http1/exec/retry"; + HttpsServer httpsServer = HttpsServer.create(sa, 0); + httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext)); + httpsTestServer = HttpTestServer.of(httpsServer); + httpsTestServer.addHandler(new ServerRequestHandler(),"/https1/exec/"); + httpsURI = "https://" + httpsTestServer.serverAuthority() + "/https1/exec/retry"; + + http2TestServer = HttpTestServer.of(new Http2TestServer("localhost", false, 0)); + http2TestServer.addHandler(new ServerRequestHandler(), "/http2/exec/"); + http2URI = "http://" + http2TestServer.serverAuthority() + "/http2/exec/retry"; + https2TestServer = HttpTestServer.of(new Http2TestServer("localhost", true, sslContext)); + https2TestServer.addHandler(new ServerRequestHandler(), "/https2/exec/"); + https2URI = "https://" + https2TestServer.serverAuthority() + "/https2/exec/retry"; + + httpTestServer.start(); + httpsTestServer.start(); + http2TestServer.start(); + https2TestServer.start(); + } + + @AfterTest + public void teardown() throws Exception { + Thread.sleep(100); + AssertionError fail = TRACKER.checkShutdown(5000); + try { + httpTestServer.stop(); + httpsTestServer.stop(); + http2TestServer.stop(); + https2TestServer.stop(); + } finally { + if (fail != null) throw fail; + } + } + + static class ServerRequestHandler implements HttpTestHandler { + ConcurrentHashMap closedRequests = new ConcurrentHashMap<>(); + + @java.lang.Override + public void handle(HttpTestExchange t) throws IOException { + out.println("ServerRequestHandler for: " + t.getRequestURI()); + + List uuids = t.getRequestHeaders().get("X-uuid"); + if (uuids == null || uuids.size() != 1) { + readAllRequestData(t); + try (OutputStream os = t.getResponseBody()) { + String msg = "Incorrect uuid header values:[" + uuids + "]"; + (new RuntimeException(msg)).printStackTrace(); + t.sendResponseHeaders(500, -1); + os.write(msg.getBytes(UTF_8)); + } + return; + } + + String uuid = uuids.get(0); + // retrying + if (closedRequests.putIfAbsent(uuid, t.getRequestURI().toString()) == null) { + if (t.getExchangeVersion() == HttpClient.Version.HTTP_1_1) { + // Throwing an exception here only causes a retry + // with HTTP_1_1 - where it forces the server to close + // the connection. + // For HTTP/2 then throwing an IOE would cause the server + // to close the stream, and throwing anything else would + // cause it to close the connection, but neither would + // cause the client to retry. + // So we simply do not try to retry with HTTP/2. + out.println("Server will close connection, client will retry: " + + t.getRequestURI().toString()); + throw new IOException("Closing on first request"); + } + } + + // not retrying + readAllRequestData(t); + try (OutputStream os = t.getResponseBody()) { + byte[] bytes = MESSAGE.getBytes(UTF_8); + t.sendResponseHeaders(200, bytes.length); + for (int i=0; i name(r), ITestResult::getThrowable)); + .collect(Collectors.toMap(CancelRequestTest::name, ITestResult::getThrowable)); FAILURES.putAll(failed); try { out.printf("%n%sCreated %d servers and %d clients%n", now(), serverCount.get(), clientCount.get()); if (FAILURES.isEmpty()) return; out.println("Failed tests: "); - FAILURES.entrySet().forEach((e) -> { - out.printf("\t%s: %s%n", e.getKey(), e.getValue()); - e.getValue().printStackTrace(out); + FAILURES.forEach((key, value) -> { + out.printf("\t%s: %s%n", key, value); + value.printStackTrace(out); }); if (tasksFailed) { System.out.println("WARNING: Some tasks failed"); @@ -304,6 +300,7 @@ public void testGetSendAsync(String uri, boolean sameClient, boolean mayInterrup for (int i=0; i< ITERATION_COUNT; i++) { if (!sameClient || client == null) client = newHttpClient(sameClient); + Tracker tracker = TRACKER.getTracker(client); HttpRequest req = HttpRequest.newBuilder(URI.create(uri)) .GET() @@ -323,7 +320,7 @@ public void testGetSendAsync(String uri, boolean sameClient, boolean mayInterrup out.println("cf2 after cancel: " + cf2); try { String body = cf2.get().body(); - assertEquals(body, Stream.of(BODY.split("\\|")).collect(Collectors.joining())); + assertEquals(body, String.join("", BODY.split("\\|"))); throw new AssertionError("Expected CancellationException not received"); } catch (ExecutionException x) { out.println("Got expected exception: " + x); @@ -344,23 +341,31 @@ public void testGetSendAsync(String uri, boolean sameClient, boolean mayInterrup // completed yet - so wait for it here... try { String body = response.get().body(); - assertEquals(body, Stream.of(BODY.split("\\|")).collect(Collectors.joining())); + assertEquals(body, String.join("", BODY.split("\\|"))); if (mayInterruptIfRunning) { // well actually - this could happen... In which case we'll need to // increase the latency in the server handler... throw new AssertionError("Expected Exception not received"); } } catch (ExecutionException x) { - assertEquals(response.isDone(), true); + assertTrue(response.isDone()); Throwable wrapped = x.getCause(); - assertTrue(CancellationException.class.isAssignableFrom(wrapped.getClass())); - Throwable cause = wrapped.getCause(); - out.println("CancellationException cause: " + x); - assertTrue(IOException.class.isAssignableFrom(cause.getClass())); - if (cause instanceof HttpConnectTimeoutException) { + Throwable cause = wrapped; + if (mayInterruptIfRunning) { + assertTrue(CancellationException.class.isAssignableFrom(wrapped.getClass()), + "Unexpected exception: " + wrapped); + cause = wrapped.getCause(); + out.println("CancellationException cause: " + x); + if (cause instanceof HttpConnectTimeoutException) { + cause.printStackTrace(out); + throw new RuntimeException("Unexpected timeout exception", cause); + } + } + if (!IOException.class.isInstance(cause)) { + out.println("Unexpected cause: " + cause.getClass()); cause.printStackTrace(out); - throw new RuntimeException("Unexpected timeout exception", cause); } + assertTrue(IOException.class.isAssignableFrom(cause.getClass())); if (mayInterruptIfRunning) { out.println("Got expected exception: " + wrapped); out.println("\tcause: " + cause); @@ -371,13 +376,22 @@ public void testGetSendAsync(String uri, boolean sameClient, boolean mayInterrup } } - assertEquals(response.isDone(), true); - assertEquals(response.isCancelled(), false); + assertTrue(response.isDone()); + assertFalse(response.isCancelled()); assertEquals(cf1.isCancelled(), hasCancellationException); - assertEquals(cf2.isDone(), true); - assertEquals(cf2.isCancelled(), false); + assertTrue(cf2.isDone()); + assertFalse(cf2.isCancelled()); assertEquals(latch.getCount(), 0); + + var error = TRACKER.check(tracker, 1000, + (t) -> t.getOutstandingOperations() > 0 || t.getOutstandingSubscribers() > 0, + "subscribers for testGetSendAsync(%s)\n\t step [%s]".formatted(req.uri(), i), + false); + Reference.reachabilityFence(client); + if (error != null) throw error; } + assert client != null; + //if (!sameClient) client.close(); } @Test(dataProvider = "asyncurls") @@ -390,10 +404,11 @@ public void testPostSendAsync(String uri, boolean sameClient, boolean mayInterru for (int i=0; i< ITERATION_COUNT; i++) { if (!sameClient || client == null) client = newHttpClient(sameClient); + Tracker tracker = TRACKER.getTracker(client); CompletableFuture> cancelFuture = new CompletableFuture<>(); - Iterable iterable = new Iterable() { + Iterable iterable = new Iterable<>() { @Override public Iterator iterator() { // this is dangerous @@ -428,7 +443,7 @@ public Iterator iterator() { out.println("cf2 after cancel: " + cf2); try { String body = cf2.get().body(); - assertEquals(body, Stream.of(BODY.split("\\|")).collect(Collectors.joining())); + assertEquals(body, String.join("", BODY.split("\\|"))); throw new AssertionError("Expected CancellationException not received"); } catch (ExecutionException x) { out.println("Got expected exception: " + x); @@ -449,14 +464,14 @@ public Iterator iterator() { // completed yet - so wait for it here... try { String body = response.get().body(); - assertEquals(body, Stream.of(BODY.split("\\|")).collect(Collectors.joining())); + assertEquals(body, String.join("", BODY.split("\\|"))); if (mayInterruptIfRunning) { // well actually - this could happen... In which case we'll need to // increase the latency in the server handler... throw new AssertionError("Expected Exception not received"); } } catch (ExecutionException x) { - assertEquals(response.isDone(), true); + assertTrue(response.isDone()); Throwable wrapped = x.getCause(); assertTrue(CancellationException.class.isAssignableFrom(wrapped.getClass())); Throwable cause = wrapped.getCause(); @@ -475,13 +490,22 @@ public Iterator iterator() { } } - assertEquals(response.isDone(), true); - assertEquals(response.isCancelled(), false); + assertTrue(response.isDone()); + assertFalse(response.isCancelled()); assertEquals(cf1.isCancelled(), hasCancellationException); - assertEquals(cf2.isDone(), true); - assertEquals(cf2.isCancelled(), false); + assertTrue(cf2.isDone()); + assertFalse(cf2.isCancelled()); assertEquals(latch.getCount(), 0); + + var error = TRACKER.check(tracker, 1000, + (t) -> t.getOutstandingOperations() > 0 || t.getOutstandingSubscribers() > 0, + "subscribers for testPostSendAsync(%s)\n\t step [%s]".formatted(req.uri(), i), + false); + Reference.reachabilityFence(client); + if (error != null) throw error; } + assert client != null; + //if (!sameClient) client.close(); } @Test(dataProvider = "urls") @@ -493,11 +517,15 @@ public void testPostInterrupt(String uri, boolean sameClient) for (int i=0; i< ITERATION_COUNT; i++) { if (!sameClient || client == null) client = newHttpClient(sameClient); + Tracker tracker = TRACKER.getTracker(client); + Thread main = Thread.currentThread(); CompletableFuture interruptingThread = new CompletableFuture<>(); + var uriStr = uri + "/post/req=" + i; Runnable interrupt = () -> { Thread current = Thread.currentThread(); - out.printf("%s Interrupting main from: %s (%s)", now(), current, uri); + out.printf("%s Interrupting main from: %s (%s)%n", now(), current, uriStr); + err.printf("%s Interrupting main from: %s (%s)%n", now(), current, uriStr); interruptingThread.complete(current); main.interrupt(); }; @@ -508,35 +536,52 @@ public void testPostInterrupt(String uri, boolean sameClient) return List.of(BODY.getBytes(UTF_8)).iterator(); }; - HttpRequest req = HttpRequest.newBuilder(URI.create(uri)) + HttpRequest req = HttpRequest.newBuilder(URI.create(uriStr)) .POST(HttpRequest.BodyPublishers.ofByteArrays(iterable)) .build(); String body = null; Exception failed = null; try { + out.println("Sending: " + uriStr); body = client.send(req, BodyHandlers.ofString()).body(); } catch (Exception x) { failed = x; } - + out.println(uriStr + ": got result or exception"); if (failed instanceof InterruptedException) { - out.println("Got expected exception: " + failed); + out.println(uriStr + ": Got expected exception: " + failed); } else if (failed instanceof IOException) { + out.println(uriStr + ": got IOException: " + failed); // that could be OK if the main thread was interrupted // from the main thread: the interrupt status could have // been caught by writing to the socket from the main // thread. - if (interruptingThread.get() == main) { - out.println("Accepting IOException: " + failed); + if (interruptingThread.isDone() && interruptingThread.get() == main) { + out.println(uriStr + ": Accepting IOException: " + failed); failed.printStackTrace(out); } else { + out.println(uriStr + ": unexpected exception: " + failed); throw failed; } } else if (failed != null) { - assertEquals(body, Stream.of(BODY.split("\\|")).collect(Collectors.joining())); + out.println(uriStr + ": unexpected exception: " + failed); throw failed; + } else { + assert failed == null; + out.println(uriStr + ": got body: " + body); + assertEquals(body, String.join("", BODY.split("\\|"))); } + out.println("next iteration"); + + var error = TRACKER.check(tracker, 2000, + (t) -> t.getOutstandingOperations() > 0 || t.getOutstandingSubscribers() > 0, + "subscribers for testPostInterrupt(%s)\n\t step [%s]".formatted(req.uri(), i), + false); + Reference.reachabilityFence(client); + if (error != null) throw error; } + assert client != null; + //if (!sameClient) client.close(); } diff --git a/test/jdk/java/net/httpclient/CancelStreamedBodyTest.java b/test/jdk/java/net/httpclient/CancelStreamedBodyTest.java new file mode 100644 index 0000000000000..f3603982c4923 --- /dev/null +++ b/test/jdk/java/net/httpclient/CancelStreamedBodyTest.java @@ -0,0 +1,449 @@ +/* + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8294916 8297075 8297149 + * @summary Tests that closing a streaming handler (ofInputStream()/ofLines()) + * without reading all the bytes unregisters the underlying subscriber. + * @library /test/lib /test/jdk/java/net/httpclient/lib + * @build jdk.httpclient.test.lib.common.HttpServerAdapters jdk.test.lib.net.SimpleSSLContext + * ReferenceTracker CancelStreamedBodyTest + * @run testng/othervm -Djdk.internal.httpclient.debug=true + * CancelStreamedBodyTest + */ +import com.sun.net.httpserver.HttpServer; +import com.sun.net.httpserver.HttpsConfigurator; +import com.sun.net.httpserver.HttpsServer; +import jdk.internal.net.http.common.OperationTrackers.Tracker; +import jdk.test.lib.RandomFactory; +import jdk.test.lib.net.SimpleSSLContext; +import org.testng.ITestContext; +import org.testng.ITestResult; +import org.testng.SkipException; +import org.testng.annotations.AfterClass; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.ref.Reference; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpConnectTimeoutException; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandler; +import java.net.http.HttpResponse.BodyHandlers; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import jdk.httpclient.test.lib.common.HttpServerAdapters; +import jdk.httpclient.test.lib.http2.Http2TestServer; + +import static java.lang.System.arraycopy; +import static java.lang.System.out; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +public class CancelStreamedBodyTest implements HttpServerAdapters { + + SSLContext sslContext; + HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] + HttpTestServer httpsTestServer; // HTTPS/1.1 + HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + String httpURI; + String httpsURI; + String http2URI; + String https2URI; + + static final long SERVER_LATENCY = 75; + static final int ITERATION_COUNT = 3; + static final long CLIENT_SHUTDOWN_GRACE_DELAY = 1500; // milliseconds + // a shared executor helps reduce the amount of threads created by the test + static final Executor executor = new TestExecutor(Executors.newCachedThreadPool()); + static final ConcurrentMap FAILURES = new ConcurrentHashMap<>(); + static volatile boolean tasksFailed; + static final AtomicLong serverCount = new AtomicLong(); + static final AtomicLong clientCount = new AtomicLong(); + static final long start = System.nanoTime(); + public static String now() { + long now = System.nanoTime() - start; + long secs = now / 1000_000_000; + long mill = (now % 1000_000_000) / 1000_000; + long nan = now % 1000_000; + return String.format("[%d s, %d ms, %d ns] ", secs, mill, nan); + } + + final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; + private volatile HttpClient sharedClient; + + static class TestExecutor implements Executor { + final AtomicLong tasks = new AtomicLong(); + Executor executor; + TestExecutor(Executor executor) { + this.executor = executor; + } + + @Override + public void execute(Runnable command) { + long id = tasks.incrementAndGet(); + executor.execute(() -> { + try { + command.run(); + } catch (Throwable t) { + tasksFailed = true; + System.out.printf(now() + "Task %s failed: %s%n", id, t); + System.err.printf(now() + "Task %s failed: %s%n", id, t); + FAILURES.putIfAbsent("Task " + id, t); + throw t; + } + }); + } + } + + protected boolean stopAfterFirstFailure() { + return Boolean.getBoolean("jdk.internal.httpclient.debug"); + } + + final AtomicReference skiptests = new AtomicReference<>(); + void checkSkip() { + var skip = skiptests.get(); + if (skip != null) throw skip; + } + static String name(ITestResult result) { + var params = result.getParameters(); + return result.getName() + + (params == null ? "()" : Arrays.toString(result.getParameters())); + } + + @BeforeMethod + void beforeMethod(ITestContext context) { + if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) { + if (skiptests.get() == null) { + SkipException skip = new SkipException("some tests failed"); + skip.setStackTrace(new StackTraceElement[0]); + skiptests.compareAndSet(null, skip); + } + } + } + + @AfterClass + static final void printFailedTests(ITestContext context) { + out.println("\n========================="); + var failed = context.getFailedTests().getAllResults().stream() + .collect(Collectors.toMap(r -> name(r), ITestResult::getThrowable)); + FAILURES.putAll(failed); + try { + out.printf("%n%sCreated %d servers and %d clients%n", + now(), serverCount.get(), clientCount.get()); + if (FAILURES.isEmpty()) return; + out.println("Failed tests: "); + FAILURES.entrySet().forEach((e) -> { + out.printf("\t%s: %s%n", e.getKey(), e.getValue()); + e.getValue().printStackTrace(out); + }); + if (tasksFailed) { + System.out.println("WARNING: Some tasks failed"); + } + } finally { + out.println("\n=========================\n"); + } + } + + private String[] uris() { + return new String[] { + httpURI, + httpsURI, + http2URI, + https2URI, + }; + } + + + @DataProvider(name = "urls") + public Object[][] alltests() { + String[] uris = uris(); + Object[][] result = new Object[uris.length * 2][]; + int i = 0; + for (boolean sameClient : List.of(false, true)) { + for (String uri : uris()) { + String path = sameClient ? "same" : "new"; + result[i++] = new Object[]{uri + path, sameClient}; + } + } + assert i == uris.length * 2; + return result; + } + + private HttpClient makeNewClient() { + clientCount.incrementAndGet(); + var client = HttpClient.newBuilder() + .proxy(HttpClient.Builder.NO_PROXY) + .executor(executor) + .sslContext(sslContext) + .build(); + // It is OK to even track the shared client here: + // the test methods will verify that the client has shut down + // only if it's not the shared client. + // Only the teardown() method verify that the shared client + // has shut down in this test. + return TRACKER.track(client); + } + + HttpClient newHttpClient(boolean share) { + if (!share) return makeNewClient(); + HttpClient shared = sharedClient; + if (shared != null) return shared; + synchronized (this) { + shared = sharedClient; + if (shared == null) { + shared = sharedClient = makeNewClient(); + } + return shared; + } + } + + final static String BODY = "Some string |\n that ?\n can |\n be split ?\n several |\n ways."; + + + @Test(dataProvider = "urls") + public void testAsLines(String uri, boolean sameClient) + throws Exception { + checkSkip(); + HttpClient client = null; + uri = uri + "/testAsLines"; + out.printf("%n%s testAsLines(%s, %b)%n", now(), uri, sameClient); + for (int i=0; i< ITERATION_COUNT; i++) { + if (!sameClient || client == null) + client = newHttpClient(sameClient); + var tracker = TRACKER.getTracker(client); + + HttpRequest req = HttpRequest.newBuilder(URI.create(uri)) + .GET() + .build(); + List lines; + for (int j = 0; j < 2; j++) { + try (Stream body = client.send(req, BodyHandlers.ofLines()).body()) { + lines = body.limit(j).toList(); + assertEquals(lines, BODY.replaceAll("\\||\\?", "") + .lines().limit(j).toList()); + } + // Only check our still alive client for outstanding operations + // and outstanding subscribers here: it should have none. + var error = TRACKER.check(tracker, 500, + (t) -> t.getOutstandingOperations() > 0 || t.getOutstandingSubscribers() > 0, + "subscribers for testAsLines(%s)\n\t step [%s,%s]".formatted(req.uri(), i,j), + false); + Reference.reachabilityFence(client); + if (error != null) throw error; + } + // The shared client is only shut down at the end. + // Skip shutdown check for the shared client. + if (sameClient) continue; + client = null; + System.gc(); + var error = TRACKER.check(tracker, CLIENT_SHUTDOWN_GRACE_DELAY); + if (error != null) throw error; + } + } + + @Test(dataProvider = "urls") + public void testInputStream(String uri, boolean sameClient) + throws Exception { + checkSkip(); + HttpClient client = null; + uri = uri + "/testInputStream"; + out.printf("%n%s testInputStream(%s, %b)%n", now(), uri, sameClient); + for (int i=0; i< ITERATION_COUNT; i++) { + if (!sameClient || client == null) + client = newHttpClient(sameClient); + var tracker = TRACKER.getTracker(client); + + HttpRequest req = HttpRequest.newBuilder(URI.create(uri)) + .GET() + .build(); + int read = -1; + for (int j = 0; j < 2; j++) { + try (InputStream is = client.send(req, BodyHandlers.ofInputStream()).body()) { + for (int k = 0; k < j; k++) { + read = is.read(); + assertEquals(read, BODY.charAt(k)); + } + } + // Only check our still alive client for outstanding operations + // and outstanding subscribers here: it should have none. + var error = TRACKER.check(tracker, 1, + (t) -> t.getOutstandingOperations() > 0 || t.getOutstandingSubscribers() > 0, + "subscribers for testInputStream(%s)\n\t step [%s,%s]".formatted(req.uri(), i,j), + false); + Reference.reachabilityFence(client); + if (error != null) throw error; + } + // The shared client is only shut down at the end. + // Skip shutdown check for the shared client. + if (sameClient) continue; + client = null; + System.gc(); + var error = TRACKER.check(tracker, CLIENT_SHUTDOWN_GRACE_DELAY); + if (error != null) throw error; + } + } + + + + @BeforeTest + public void setup() throws Exception { + sslContext = new SimpleSSLContext().get(); + if (sslContext == null) + throw new AssertionError("Unexpected null sslContext"); + + // HTTP/1.1 + HttpTestHandler h1_chunkHandler = new HTTPSlowHandler(); + InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); + httpTestServer = HttpTestServer.of(HttpServer.create(sa, 0)); + httpTestServer.addHandler(h1_chunkHandler, "/http1/x/"); + httpURI = "http://" + httpTestServer.serverAuthority() + "/http1/x/"; + + HttpsServer httpsServer = HttpsServer.create(sa, 0); + httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext)); + httpsTestServer = HttpTestServer.of(httpsServer); + httpsTestServer.addHandler(h1_chunkHandler, "/https1/x/"); + httpsURI = "https://" + httpsTestServer.serverAuthority() + "/https1/x/"; + + // HTTP/2 + HttpTestHandler h2_chunkedHandler = new HTTPSlowHandler(); + + http2TestServer = HttpTestServer.of(new Http2TestServer("localhost", false, 0)); + http2TestServer.addHandler(h2_chunkedHandler, "/http2/x/"); + http2URI = "http://" + http2TestServer.serverAuthority() + "/http2/x/"; + + https2TestServer = HttpTestServer.of(new Http2TestServer("localhost", true, sslContext)); + https2TestServer.addHandler(h2_chunkedHandler, "/https2/x/"); + https2URI = "https://" + https2TestServer.serverAuthority() + "/https2/x/"; + + serverCount.addAndGet(4); + httpTestServer.start(); + httpsTestServer.start(); + http2TestServer.start(); + https2TestServer.start(); + } + + @AfterTest + public void teardown() throws Exception { + String sharedClientName = + sharedClient == null ? null : sharedClient.toString(); + sharedClient = null; + // check that the shared client (and any other client) have + // properly shut down + System.gc(); + Thread.sleep(100); + AssertionError fail = TRACKER.check(500); + try { + httpTestServer.stop(); + httpsTestServer.stop(); + http2TestServer.stop(); + https2TestServer.stop(); + } finally { + if (fail != null) { + if (sharedClientName != null) { + System.err.println("Shared client name is: " + sharedClientName); + } + throw fail; + } + } + } + + /** + * A handler that slowly sends back a body to give time for the + * the request to get cancelled before the body is fully received. + */ + static class HTTPSlowHandler implements HttpTestHandler { + @Override + public void handle(HttpTestExchange t) throws IOException { + try { + out.println("HTTPSlowHandler received request to " + t.getRequestURI()); + System.err.println("HTTPSlowHandler received request to " + t.getRequestURI()); + + byte[] req; + try (InputStream is = t.getRequestBody()) { + req = is.readAllBytes(); + } + + // we're not expecting a request body. + // if we receive any, pretend we're a teapot. + int status = req.length == 0 ? 200 : 418; + t.sendResponseHeaders(status, -1); // chunked/variable + try (OutputStream os = t.getResponseBody()) { + // lets split the response in several chunks... + String msg = (req != null && req.length != 0) + ? new String(req, UTF_8) + : BODY; + String[] str = msg.split("\\|"); + for (var s : str) { + req = s.getBytes(UTF_8); + os.write(req); + os.flush(); + out.printf("Server wrote %d bytes%n", req.length); + try { + Thread.sleep(SERVER_LATENCY); + } catch (InterruptedException x) { + // OK + } + } + } + } catch (Throwable e) { + out.println("HTTPSlowHandler: unexpected exception: " + e); + e.printStackTrace(); + throw e; + } finally { + out.printf("HTTPSlowHandler reply sent: %s%n", t.getRequestURI()); + System.err.printf("HTTPSlowHandler reply sent: %s%n", t.getRequestURI()); + } + } + } + +} diff --git a/test/jdk/java/net/httpclient/ConnectTimeoutHandshakeAsync.java b/test/jdk/java/net/httpclient/ConnectTimeoutHandshakeAsync.java index a5fa9e75d0952..6cdaf594b1d58 100644 --- a/test/jdk/java/net/httpclient/ConnectTimeoutHandshakeAsync.java +++ b/test/jdk/java/net/httpclient/ConnectTimeoutHandshakeAsync.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,8 @@ * @test * @summary Tests connection timeouts during SSL handshake * @bug 8208391 + * @library /test/lib + * @build AbstractConnectTimeoutHandshake * @run testng/othervm ConnectTimeoutHandshakeAsync */ diff --git a/test/jdk/java/net/httpclient/ConnectTimeoutHandshakeSync.java b/test/jdk/java/net/httpclient/ConnectTimeoutHandshakeSync.java index b6a301dac28e4..17ea3041c5bd1 100644 --- a/test/jdk/java/net/httpclient/ConnectTimeoutHandshakeSync.java +++ b/test/jdk/java/net/httpclient/ConnectTimeoutHandshakeSync.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,8 @@ * @test * @summary Tests connection timeouts during SSL handshake * @bug 8208391 + * @library /test/lib + * @build AbstractConnectTimeoutHandshake * @run testng/othervm ConnectTimeoutHandshakeSync */ diff --git a/test/jdk/java/net/httpclient/ContentLengthHeaderTest.java b/test/jdk/java/net/httpclient/ContentLengthHeaderTest.java new file mode 100644 index 0000000000000..aaff45ecb2bad --- /dev/null +++ b/test/jdk/java/net/httpclient/ContentLengthHeaderTest.java @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Pa6a7709320d28d8e1593b113fdf39ab583fca3687rkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Tests that a Content-length header is not sent for GET requests + * that do not have a body. Also checks that the header is sent when + * a body is present on the GET request. + * @library /test/lib + * @bug 8283544 + * @run testng/othervm ContentLengthHeaderTest + */ + + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ProxySelector; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import jdk.test.lib.net.URIBuilder; + + +import static java.net.http.HttpClient.Version.HTTP_1_1; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.testng.Assert.assertEquals; + + +public class ContentLengthHeaderTest { + + final String NO_BODY_PATH = "/no_body"; + final String BODY_PATH = "/body"; + + static HttpServer testContentLengthServer; + static PrintStream testLog = System.err; + + HttpClient hc; + URI testContentLengthURI; + + @BeforeTest + public void setup() throws IOException, URISyntaxException { + InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); + testContentLengthServer = HttpServer.create(sa, 0); + + // Create handlers for tests that check for the presence of a Content-length header + testContentLengthServer.createContext(NO_BODY_PATH, new NoContentLengthHandler()); + testContentLengthServer.createContext(BODY_PATH, new ContentLengthHandler()); + testContentLengthURI = URIBuilder.newBuilder() + .scheme("http") + .loopback() + .port(testContentLengthServer.getAddress().getPort()) + .build(); + + testContentLengthServer.start(); + testLog.println("Server up at address: " + testContentLengthServer.getAddress()); + testLog.println("Request URI for Client: " + testContentLengthURI); + + hc = HttpClient.newBuilder() + .proxy(HttpClient.Builder.NO_PROXY) + .version(HTTP_1_1) + .build(); + } + + @AfterTest + public void teardown() { + if (testContentLengthServer != null) + testContentLengthServer.stop(0); + } + + @Test + // A GET request with no request body should have no Content-length header + public void getWithNoBody() throws IOException, InterruptedException { + testLog.println("Checking GET with no request body"); + HttpRequest req = HttpRequest.newBuilder() + .version(HTTP_1_1) + .GET() + .uri(URI.create(testContentLengthURI + NO_BODY_PATH)) + .build(); + HttpResponse resp = hc.send(req, HttpResponse.BodyHandlers.ofString(UTF_8)); + assertEquals(resp.statusCode(), 200, resp.body()); + } + + @Test + // A GET request with a request body should have a Content-length header + public void getWithBody() throws IOException, InterruptedException { + testLog.println("Checking GET with request body"); + HttpRequest req = HttpRequest.newBuilder() + .version(HTTP_1_1) + .method("GET", HttpRequest.BodyPublishers.ofString("GET Body")) + .uri(URI.create(testContentLengthURI + BODY_PATH)) + .build(); + HttpResponse resp = hc.send(req, HttpResponse.BodyHandlers.ofString(UTF_8)); + assertEquals(resp.statusCode(), 200, resp.body()); + } + + @Test + // A DELETE request with no request body should have no Content-length header + public void deleteWithNoBody() throws IOException, InterruptedException { + testLog.println("Checking DELETE with no request body"); + HttpRequest req = HttpRequest.newBuilder() + .version(HTTP_1_1) + .DELETE() + .uri(URI.create(testContentLengthURI + NO_BODY_PATH)) + .build(); + HttpResponse resp = hc.send(req, HttpResponse.BodyHandlers.ofString(UTF_8)); + assertEquals(resp.statusCode(), 200, resp.body()); + } + + @Test + // A DELETE request with a request body should have a Content-length header + public void deleteWithBody() throws IOException, InterruptedException { + testLog.println("Checking DELETE with request body"); + HttpRequest req = HttpRequest.newBuilder() + .version(HTTP_1_1) + .method("DELETE", HttpRequest.BodyPublishers.ofString("DELETE Body")) + .uri(URI.create(testContentLengthURI + BODY_PATH)) + .build(); + HttpResponse resp = hc.send(req, HttpResponse.BodyHandlers.ofString(UTF_8)); + assertEquals(resp.statusCode(), 200, resp.body()); + } + + /* + @Test + // A HEAD request with no request body should have no Content-length header + public void headWithNoBody() throws IOException, InterruptedException { + testLog.println("Checking HEAD with no request body"); + HttpRequest req = HttpRequest.newBuilder() + .version(HTTP_1_1) + .HEAD() + .uri(URI.create(testContentLengthURI + NO_BODY_PATH)) + .build(); + HttpResponse resp = hc.send(req, HttpResponse.BodyHandlers.ofString(UTF_8)); + assertEquals(resp.statusCode(), 200, resp.body()); + } + */ + + @Test + // A HEAD request with a request body should have a Content-length header + public void headWithBody() throws IOException, InterruptedException { + testLog.println("Checking HEAD with request body"); + HttpRequest req = HttpRequest.newBuilder() + .version(HTTP_1_1) + .method("HEAD", HttpRequest.BodyPublishers.ofString("HEAD Body")) + .uri(URI.create(testContentLengthURI + BODY_PATH)) + .build(); + // Sending this request invokes sendResponseHeaders which emits a warning about including + // a Content-length header with a HEAD request + HttpResponse resp = hc.send(req, HttpResponse.BodyHandlers.ofString(UTF_8)); + assertEquals(resp.statusCode(), 200, resp.body()); + } + + public static void handleResponse(HttpExchange ex, String body, int rCode) throws IOException { + try (InputStream is = ex.getRequestBody(); + OutputStream os = ex.getResponseBody()) { + is.readAllBytes(); + byte[] bytes = body.getBytes(UTF_8); + ex.sendResponseHeaders(rCode, bytes.length); + os.write(bytes); + } + } + + static class NoContentLengthHandler implements HttpHandler { + + @Override + public void handle(HttpExchange exchange) throws IOException { + testLog.println("NoContentLengthHandler: Received Headers " + exchange.getRequestHeaders().entrySet() + + " from " + exchange.getRequestMethod() + " request."); + String contentLength = exchange.getRequestHeaders().getFirst("Content-length"); + + // Check Content-length header was not set + if (contentLength == null) { + handleResponse(exchange, "Request completed",200); + } else { + String responseBody = exchange.getRequestMethod() + " request contained an unexpected " + + "Content-length header of value: " + exchange.getRequestHeaders().getFirst("Content-length"); + handleResponse(exchange, responseBody, 400); + } + } + } + + static class ContentLengthHandler implements HttpHandler { + + @Override + public void handle(HttpExchange exchange) throws IOException { + testLog.println("ContentLengthHandler: Received Headers " + exchange.getRequestHeaders().entrySet() + + " from " + exchange.getRequestMethod() + " request."); + String contentLength = exchange.getRequestHeaders().getFirst("Content-length"); + + // Check Content-length header was set + if (contentLength != null) { + handleResponse(exchange, "Request completed",200); + } else { + String responseBody = "Expected a Content-length header in " + + exchange.getRequestMethod() + " request but was not present."; + handleResponse(exchange, responseBody, 400); + } + } + } +} diff --git a/test/jdk/java/net/httpclient/DependentPromiseActionsTest.java b/test/jdk/java/net/httpclient/DependentPromiseActionsTest.java index 4e17408ee6f50..32c594808a9c4 100644 --- a/test/jdk/java/net/httpclient/DependentPromiseActionsTest.java +++ b/test/jdk/java/net/httpclient/DependentPromiseActionsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -73,6 +73,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.Flow; import java.util.concurrent.Semaphore; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiPredicate; @@ -112,6 +113,7 @@ public class DependentPromiseActionsTest implements HttpServerAdapters { static volatile boolean tasksFailed; static final AtomicLong serverCount = new AtomicLong(); static final AtomicLong clientCount = new AtomicLong(); + static final AtomicInteger requestCount = new AtomicInteger(); static final long start = System.nanoTime(); public static String now() { long now = System.nanoTime() - start; @@ -244,14 +246,17 @@ HttpClient newHttpClient(boolean share) { } @Test(dataProvider = "noStalls") - public void testNoStalls(String uri, boolean sameClient) + public void testNoStalls(String rootUri, boolean sameClient) throws Exception { + if (!FAILURES.isEmpty()) return; HttpClient client = null; - out.printf("%ntestNoStalls(%s, %b)%n", uri, sameClient); + out.printf("%ntestNoStalls(%s, %b)%n", rootUri, sameClient); for (int i=0; i< ITERATION_COUNT; i++) { if (!sameClient || client == null) client = newHttpClient(sameClient); + String uri = rootUri + "/" + requestCount.incrementAndGet(); + out.printf("\tsending request %s%n", uri); HttpRequest req = HttpRequest.newBuilder(URI.create(uri)) .build(); BodyHandler> handler = @@ -331,6 +336,10 @@ private void testDependent(String name, String uri, boolean sameClient, SubscriberType subscriberType) throws Exception { + if (!FAILURES.isEmpty()) { + out.printf("%s: skipping test - previous failure detected%n", name); + return; + } out.printf("%n%s%s%n", now(), name); try { testDependent(uri, sameClient, handlers, finisher, @@ -341,7 +350,7 @@ private void testDependent(String name, String uri, boolean sameClient, } } - private void testDependent(String uri, boolean sameClient, + private void testDependent(String rootUri, boolean sameClient, Supplier> handlers, Finisher finisher, Extractor extractor, @@ -354,6 +363,8 @@ private void testDependent(String uri, boolean sameClient, if (!sameClient || client == null) client = newHttpClient(sameClient); + String uri = rootUri + "/" + requestCount.incrementAndGet(); + out.printf("\tsending request %s%n", uri); HttpRequest req = HttpRequest. newBuilder(URI.create(uri)) .build(); @@ -363,7 +374,13 @@ private void testDependent(String uri, boolean sameClient, System.out.println("try stalling in " + where); CompletableFuture> responseCF = client.sendAsync(req, handler, promiseHandler); - assert subscriberType == SubscriberType.LAZZY || !responseCF.isDone(); + // The body of the main response can be received before the body + // of the push promise handlers are received. + // The body of the main response doesn't stall, so the cf of + // the main response may be done here even for EAGER subscribers. + // We cannot make any assumption on the state of the main response + // cf here, so the only thing we can do is to call the finisher + // which will wait for them all. finisher.finish(where, responseCF, promiseHandler, extractor); } } diff --git a/test/jdk/java/net/httpclient/DigestEchoClient.java b/test/jdk/java/net/httpclient/DigestEchoClient.java index f7bac2038d1db..54d8aaf460891 100644 --- a/test/jdk/java/net/httpclient/DigestEchoClient.java +++ b/test/jdk/java/net/httpclient/DigestEchoClient.java @@ -23,6 +23,8 @@ import java.io.IOException; import java.io.UncheckedIOException; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; import java.math.BigInteger; import java.net.ProxySelector; import java.net.URI; @@ -32,7 +34,6 @@ import java.net.http.HttpRequest.BodyPublisher; import java.net.http.HttpRequest.BodyPublishers; import java.net.http.HttpResponse; -import java.net.http.HttpResponse.BodyHandler; import java.net.http.HttpResponse.BodyHandlers; import java.nio.charset.StandardCharsets; import java.security.NoSuchAlgorithmException; @@ -55,6 +56,7 @@ import jdk.test.lib.net.SimpleSSLContext; import sun.net.NetProperties; import sun.net.www.HeaderParser; + import static java.lang.System.out; import static java.lang.String.format; @@ -258,8 +260,9 @@ public static void main(String[] args) throws Exception { } try { for (DigestEchoServer.HttpAuthType authType : types) { - // The test server does not support PROXY305 properly - if (authType == DigestEchoServer.HttpAuthType.PROXY305) continue; + // The test server does not support PROXY305 or SERVER307 properly + if (authType == DigestEchoServer.HttpAuthType.PROXY305 || + authType == DigestEchoServer.HttpAuthType.SERVER307) continue; EnumSet basics = EnumSet.of(DigestEchoServer.HttpAuthSchemeType.BASICSERVER, DigestEchoServer.HttpAuthSchemeType.BASIC); @@ -386,6 +389,8 @@ void testBasic(Version clientVersion, Version serverVersion, boolean async, server.getServerAddress(), "/foo/"); HttpClient client = newHttpClient(server); + ReferenceQueue queue = new ReferenceQueue<>(); + WeakReference ref = new WeakReference<>(client, queue); HttpResponse r; CompletableFuture> cf1; String auth = null; @@ -497,6 +502,14 @@ void testBasic(Version clientVersion, Version serverVersion, boolean async, } } } finally { + client = null; + System.gc(); + while (!ref.refersTo(null)) { + System.gc(); + if (queue.remove(100) == ref) break; + } + var error = TRACKER.checkShutdown(500); + if (error != null) throw error; } System.out.println("OK"); } diff --git a/test/jdk/java/net/httpclient/ExecutorShutdown.java b/test/jdk/java/net/httpclient/ExecutorShutdown.java new file mode 100644 index 0000000000000..ff375247d1ad0 --- /dev/null +++ b/test/jdk/java/net/httpclient/ExecutorShutdown.java @@ -0,0 +1,380 @@ +/* + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8277969 + * @summary Test for edge case where the executor is not accepting + * new tasks while the client is still running + * @library /test/lib /test/jdk/java/net/httpclient/lib + * @build jdk.httpclient.test.lib.http2.Http2TestServer jdk.test.lib.net.SimpleSSLContext + * ReferenceTracker + * @run testng/othervm + * -Djdk.internal.httpclient.debug=true + * -Djdk.httpclient.HttpClient.log=trace,headers,requests + * ExecutorShutdown + */ +// -Djdk.internal.httpclient.debug=true + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpClient.Redirect; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandlers; +import java.nio.channels.ClosedChannelException; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import jdk.httpclient.test.lib.common.HttpServerAdapters; +import jdk.httpclient.test.lib.http2.Http2TestServer; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLHandshakeException; + +import com.sun.net.httpserver.HttpServer; +import com.sun.net.httpserver.HttpsConfigurator; +import com.sun.net.httpserver.HttpsServer; +import jdk.test.lib.RandomFactory; +import jdk.test.lib.net.SimpleSSLContext; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static java.lang.System.out; +import static java.net.http.HttpClient.Builder.NO_PROXY; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + +public class ExecutorShutdown implements HttpServerAdapters { + + static { + HttpServerAdapters.enableServerLogging(); + } + static final Random RANDOM = RandomFactory.getRandom(); + + SSLContext sslContext; + HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] + HttpTestServer httpsTestServer; // HTTPS/1.1 + HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + String httpURI; + String httpsURI; + String http2URI; + String https2URI; + + static final String MESSAGE = "ExecutorShutdown message body"; + static final int ITERATIONS = 3; + + @DataProvider(name = "positive") + public Object[][] positive() { + return new Object[][] { + { httpURI, }, + { httpsURI, }, + { http2URI, }, + { https2URI, }, + }; + } + + static final AtomicLong requestCounter = new AtomicLong(); + final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; + + static Throwable getCause(Throwable t) { + while (t instanceof CompletionException || t instanceof ExecutionException) { + t = t.getCause(); + } + return t; + } + + static void checkCause(String what, Throwable cause) { + Throwable t = cause; + Throwable accepted = null; + while (t != null) { + out.println(what + ": checking " + t); + if (t instanceof RejectedExecutionException) { + out.println(what + ": Got expected RejectedExecutionException in cause: " + t); + return; + } else if (t instanceof ClosedChannelException) { + out.println(what + ": Accepting ClosedChannelException as a valid cause: " + t); + accepted = t; + } + t = t.getCause(); + } + if (accepted != null) { + out.println(what + ": Didn't find expected RejectedExecutionException, " + + "but accepting " + accepted.getClass().getSimpleName() + + " as a valid cause: " + accepted); + return; + } + throw new AssertionError(what + ": Unexpected exception: " + cause, cause); + } + + @Test(dataProvider = "positive") + void testConcurrent(String uriString) throws Exception { + out.printf("%n---- starting (%s) ----%n", uriString); + ExecutorService executorService = Executors.newCachedThreadPool(); + HttpClient client = HttpClient.newBuilder() + .proxy(NO_PROXY) + .followRedirects(Redirect.ALWAYS) + .executor(executorService) + .sslContext(sslContext) + .build(); + TRACKER.track(client); + assert client.executor().isPresent(); + + int step = RANDOM.nextInt(ITERATIONS); + try { + List>> responses = new ArrayList<>(); + for (int i = 0; i < ITERATIONS; i++) { + URI uri = URI.create(uriString + "/concurrent/iteration-" + i); + HttpRequest request = HttpRequest.newBuilder(uri) + .header("X-uuid", "uuid-" + requestCounter.incrementAndGet()) + .build(); + out.printf("Iteration %d request: %s%n", i, request.uri()); + CompletableFuture> responseCF; + try { + responseCF = client.sendAsync(request, BodyHandlers.ofString()); + } catch (RejectedExecutionException x) { + out.println(i + ": Got expected exception: " + x); + continue; + } + long sleep = RANDOM.nextLong(5); + if (sleep > 0) { + out.printf("%d: sleeping %d ms%n", i, sleep); + Thread.sleep(sleep); + } + if (i == step) { + out.printf("%d: shutting down executor now%n", i, sleep); + executorService.shutdownNow(); + } + final int si = i; + var cf = responseCF.thenApply((response) -> { + out.println(si + ": Got response: " + response); + out.println(si + ": Got body Path: " + response.body()); + assertEquals(response.statusCode(), 200); + assertEquals(response.body(), MESSAGE); + return response; + }).exceptionally((t) -> { + Throwable cause = getCause(t); + out.println(si + ": Got expected exception: " + cause); + checkCause(String.valueOf(si), cause); + return null; + }); + responses.add(cf); + } + CompletableFuture.allOf(responses.toArray(new CompletableFuture[0])).get(); + } finally { + client = null; + executorService.awaitTermination(2000, TimeUnit.MILLISECONDS); + } + } + + @Test(dataProvider = "positive") + void testSequential(String uriString) throws Exception { + out.printf("%n---- starting (%s) ----%n", uriString); + ExecutorService executorService = Executors.newCachedThreadPool(); + HttpClient client = HttpClient.newBuilder() + .proxy(NO_PROXY) + .followRedirects(Redirect.ALWAYS) + .executor(executorService) + .sslContext(sslContext) + .build(); + TRACKER.track(client); + assert client.executor().isPresent(); + + int step = RANDOM.nextInt(ITERATIONS); + out.printf("will shutdown executor in step %d%n", step); + try { + for (int i = 0; i < ITERATIONS; i++) { + URI uri = URI.create(uriString + "/sequential/iteration-" + i); + HttpRequest request = HttpRequest.newBuilder(uri) + .header("X-uuid", "uuid-" + requestCounter.incrementAndGet()) + .build(); + out.printf("Iteration %d request: %s%n", i, request.uri()); + CompletableFuture> responseCF; + try { + responseCF = client.sendAsync(request, BodyHandlers.ofString()); + } catch (RejectedExecutionException x) { + out.println(i + ": Got expected exception: " + x); + continue; + } + long sleep = RANDOM.nextLong(5); + if (sleep > 0) { + out.printf("%d: sleeping %d ms%n", i, sleep); + Thread.sleep(sleep); + } + if (i == step) { + out.printf("%d: shutting down executor now%n", i, sleep); + executorService.shutdownNow(); + } + final int si = i; + responseCF.thenApply((response) -> { + out.println(si + ": Got response: " + response); + out.println(si + ": Got body Path: " + response.body()); + assertEquals(response.statusCode(), 200); + assertEquals(response.body(), MESSAGE); + return response; + }).handle((r,t) -> { + if (t != null) { + try { + Throwable cause = getCause(t); + out.println(si + ": Got expected exception: " + cause); + checkCause(String.valueOf(si), cause); + } catch (Throwable ase) { + return CompletableFuture.failedFuture(ase); + } + return CompletableFuture.completedFuture(null); + } else { + return CompletableFuture.completedFuture(r); + } + }).thenCompose((c) -> c).get(); + } + } finally { + client = null; + executorService.awaitTermination(2000, TimeUnit.MILLISECONDS); + } + } + + // -- Infrastructure + + @BeforeTest + public void setup() throws Exception { + out.println("\n**** Setup ****\n"); + sslContext = new SimpleSSLContext().get(); + if (sslContext == null) + throw new AssertionError("Unexpected null sslContext"); + + InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); + + httpTestServer = HttpTestServer.of(HttpServer.create(sa, 0)); + httpTestServer.addHandler(new ServerRequestHandler(), "/http1/exec/"); + httpURI = "http://" + httpTestServer.serverAuthority() + "/http1/exec/retry"; + HttpsServer httpsServer = HttpsServer.create(sa, 0); + httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext)); + httpsTestServer = HttpTestServer.of(httpsServer); + httpsTestServer.addHandler(new ServerRequestHandler(),"/https1/exec/"); + httpsURI = "https://" + httpsTestServer.serverAuthority() + "/https1/exec/retry"; + + http2TestServer = HttpTestServer.of(new Http2TestServer("localhost", false, 0)); + http2TestServer.addHandler(new ServerRequestHandler(), "/http2/exec/"); + http2URI = "http://" + http2TestServer.serverAuthority() + "/http2/exec/retry"; + https2TestServer = HttpTestServer.of(new Http2TestServer("localhost", true, sslContext)); + https2TestServer.addHandler(new ServerRequestHandler(), "/https2/exec/"); + https2URI = "https://" + https2TestServer.serverAuthority() + "/https2/exec/retry"; + + httpTestServer.start(); + httpsTestServer.start(); + http2TestServer.start(); + https2TestServer.start(); + } + + @AfterTest + public void teardown() throws Exception { + Thread.sleep(100); + AssertionError fail = TRACKER.check(5000); + try { + httpTestServer.stop(); + httpsTestServer.stop(); + http2TestServer.stop(); + https2TestServer.stop(); + } finally { + if (fail != null) throw fail; + } + } + + static class ServerRequestHandler implements HttpTestHandler { + ConcurrentHashMap closedRequests = new ConcurrentHashMap<>(); + + @java.lang.Override + public void handle(HttpTestExchange t) throws IOException { + out.println("ServerRequestHandler for: " + t.getRequestURI()); + + List uuids = t.getRequestHeaders().get("X-uuid"); + if (uuids == null || uuids.size() != 1) { + readAllRequestData(t); + try (OutputStream os = t.getResponseBody()) { + String msg = "Incorrect uuid header values:[" + uuids + "]"; + (new RuntimeException(msg)).printStackTrace(); + t.sendResponseHeaders(500, -1); + os.write(msg.getBytes(UTF_8)); + } + return; + } + + String uuid = uuids.get(0); + // retrying + if (closedRequests.putIfAbsent(uuid, t.getRequestURI().toString()) == null) { + if (t.getExchangeVersion() == HttpClient.Version.HTTP_1_1) { + // Throwing an exception here only causes a retry + // with HTTP_1_1 - where it forces the server to close + // the connection. + // For HTTP/2 then throwing an IOE would cause the server + // to close the stream, and throwing anything else would + // cause it to close the connection, but neither would + // cause the client to retry. + // So we simply do not try to retry with HTTP/2. + out.println("Server will close connection, client will retry: " + + t.getRequestURI().toString()); + throw new IOException("Closing on first request"); + } + } + + // not retrying + readAllRequestData(t); + try (OutputStream os = t.getResponseBody()) { + byte[] bytes = MESSAGE.getBytes(UTF_8); + t.sendResponseHeaders(200, bytes.length); + for (int i=0; i> cf = client.sendAsync(getRequest, HttpResponse.BodyHandlers.ofString()); + HttpResponse resp = cf.join(); + System.err.println("Response Headers: " + resp.headers()); + System.err.println("Response Status Code: " + resp.statusCode()); + assertEquals(resp.statusCode(), 200); + + System.out.printf("Sending request (%s): %s%n", version, postRequest); + System.err.println("Sending request: " + postRequest); + cf = client.sendAsync(postRequest, HttpResponse.BodyHandlers.ofString()); + resp = cf.join(); + System.err.println("Response Headers: " + resp.headers()); + System.err.println("Response Status Code: " + resp.statusCode()); + assertEquals(resp.statusCode(), 200); + + System.out.printf("Sending request (%s): %s%n", version, hangRequest); + System.err.println("Sending request: " + hangRequest); + cf = client.sendAsync(hangRequest, HttpResponse.BodyHandlers.ofString()); + resp = cf.join(); + System.err.println("Response Headers: " + resp.headers()); + System.err.println("Response Status Code: " + resp.statusCode()); + assertEquals(resp.statusCode(), 417); + } + +} diff --git a/test/jdk/java/net/httpclient/GZIPInputStreamTest.java b/test/jdk/java/net/httpclient/GZIPInputStreamTest.java index e2a382dfc5e29..7db41cde5d638 100644 --- a/test/jdk/java/net/httpclient/GZIPInputStreamTest.java +++ b/test/jdk/java/net/httpclient/GZIPInputStreamTest.java @@ -26,7 +26,7 @@ * @bug 8217264 * @summary Tests that you can map an InputStream to a GZIPInputStream * @library /test/lib /test/jdk/java/net/httpclient/lib - * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.common.HttpServerAdapters + * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.common.HttpServerAdapters ReferenceTracker * @run testng/othervm GZIPInputStreamTest */ diff --git a/test/jdk/java/net/httpclient/HeadTest.java b/test/jdk/java/net/httpclient/HeadTest.java index 81eeb61207354..506b59b6c776c 100644 --- a/test/jdk/java/net/httpclient/HeadTest.java +++ b/test/jdk/java/net/httpclient/HeadTest.java @@ -111,22 +111,21 @@ public class HeadTest implements HttpServerAdapters { @DataProvider(name = "positive") public Object[][] positive() { return new Object[][] { + // HTTP/1.1 { httpURI, "GET", HTTP_NOT_MODIFIED, HTTP_1_1 }, { httpsURI, "GET", HTTP_NOT_MODIFIED, HTTP_1_1 }, - { httpURI, "GET", HTTP_NOT_MODIFIED, HttpClient.Version.HTTP_2 }, - { httpsURI, "GET", HTTP_NOT_MODIFIED, HttpClient.Version.HTTP_2 }, { httpURI, "HEAD", HTTP_OK, HTTP_1_1 }, { httpsURI, "HEAD", HTTP_OK, HTTP_1_1 }, - { httpURI, "HEAD", HTTP_OK, HttpClient.Version.HTTP_2 }, - { httpsURI, "HEAD", HTTP_OK, HttpClient.Version.HTTP_2 }, { httpURI + "transfer/", "GET", HTTP_NOT_MODIFIED, HTTP_1_1 }, { httpsURI + "transfer/", "GET", HTTP_NOT_MODIFIED, HTTP_1_1 }, - { httpURI + "transfer/", "GET", HTTP_NOT_MODIFIED, HttpClient.Version.HTTP_2 }, - { httpsURI + "transfer/", "GET", HTTP_NOT_MODIFIED, HttpClient.Version.HTTP_2 }, { httpURI + "transfer/", "HEAD", HTTP_OK, HTTP_1_1 }, { httpsURI + "transfer/", "HEAD", HTTP_OK, HTTP_1_1 }, - { httpURI + "transfer/", "HEAD", HTTP_OK, HttpClient.Version.HTTP_2 }, - { httpsURI + "transfer/", "HEAD", HTTP_OK, HttpClient.Version.HTTP_2 } + // HTTP/2 + { http2URI, "GET", HTTP_NOT_MODIFIED, HttpClient.Version.HTTP_2 }, + { https2URI, "GET", HTTP_NOT_MODIFIED, HttpClient.Version.HTTP_2 }, + { http2URI, "HEAD", HTTP_OK, HttpClient.Version.HTTP_2 }, + { https2URI, "HEAD", HTTP_OK, HttpClient.Version.HTTP_2 }, + // HTTP2 forbids transfer-encoding }; } @@ -145,11 +144,9 @@ void test(String uriString, String method, HttpRequest.Builder requestBuilder = HttpRequest .newBuilder(uri) + .version(version) .method(method, HttpRequest.BodyPublishers.noBody()); - if (version != null) { - requestBuilder.version(version); - } HttpRequest request = requestBuilder.build(); out.println("Initial request: " + request.uri()); @@ -160,6 +157,7 @@ void test(String uriString, String method, assertEquals(response.statusCode(), expResp); assertEquals(response.body(), ""); assertEquals(response.headers().firstValue("Content-length").get(), CONTENT_LEN); + assertEquals(response.version(), request.version().get()); } // -- Infrastructure @@ -182,7 +180,7 @@ public void setup() throws Exception { http2TestServer = HttpTestServer.create(HTTP_2); http2TestServer.addHandler(new HeadHandler(), "/"); http2URI = "http://" + http2TestServer.serverAuthority() + "/"; - https2TestServer = HttpTestServer.create(HTTP_2, SSLContext.getDefault()); + https2TestServer = HttpTestServer.create(HTTP_2, sslContext); https2TestServer.addHandler(new HeadHandler(), "/"); https2URI = "https://" + https2TestServer.serverAuthority() + "/"; diff --git a/test/jdk/java/net/httpclient/Http1ChunkedTest.java b/test/jdk/java/net/httpclient/Http1ChunkedTest.java index 2df4c9e854dfd..f51ccf9fd3845 100644 --- a/test/jdk/java/net/httpclient/Http1ChunkedTest.java +++ b/test/jdk/java/net/httpclient/Http1ChunkedTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -97,7 +97,7 @@ private static void test(String name, String headers, List body, long de } } if (!received) { - System.out.printf("%s: Unexpected headers received: dropping request.%n"); + System.out.printf("%s: Unexpected headers received: dropping request.%n", name); continue; } OutputStream os = serverConn.getOutputStream(); diff --git a/test/jdk/java/net/httpclient/HttpGetInCancelledFuture.java b/test/jdk/java/net/httpclient/HttpGetInCancelledFuture.java new file mode 100644 index 0000000000000..4b38adebeb111 --- /dev/null +++ b/test/jdk/java/net/httpclient/HttpGetInCancelledFuture.java @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpClient.Version; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; + +import jdk.internal.net.http.common.OperationTrackers.Tracker; +import jdk.test.lib.net.SimpleSSLContext; +import jdk.test.lib.net.URIBuilder; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/* + * @test + * @bug 8316580 + * @library /test/lib + * @run junit/othervm -Djdk.tracePinnedThreads=full + * -DuseReferenceTracker=true + * HttpGetInCancelledFuture + * @summary This test verifies that cancelling a future that + * does an HTTP request using the HttpClient doesn't cause + * HttpClient::close to block forever. + */ +public class HttpGetInCancelledFuture { + + static final boolean useTracker = Boolean.getBoolean("useReferenceTracker"); + + static final class TestException extends RuntimeException { + public TestException(String message, Throwable cause) { + super(message, cause); + } + } + + static ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; + + HttpClient makeClient(URI uri, Version version, Executor executor) { + var builder = HttpClient.newBuilder(); + if (uri.getScheme().equalsIgnoreCase("https")) { + try { + builder.sslContext(new SimpleSSLContext().get()); + } catch (IOException io) { + throw new UncheckedIOException(io); + } + } + return builder.connectTimeout(Duration.ofSeconds(1)) + .executor(executor) + .version(version) + .build(); + } + + record TestCase(String url, int reqCount, Version version) {} + // A server that doesn't accept + static volatile ServerSocket NOT_ACCEPTING; + + static List parameters() { + ServerSocket ss = NOT_ACCEPTING; + if (ss == null) { + synchronized (HttpGetInCancelledFuture.class) { + if ((ss = NOT_ACCEPTING) == null) { + try { + ss = new ServerSocket(); + var loopback = InetAddress.getLoopbackAddress(); + ss.bind(new InetSocketAddress(loopback, 0), 10); + NOT_ACCEPTING = ss; + } catch (IOException io) { + throw new UncheckedIOException(io); + } + } + } + } + URI http = URIBuilder.newBuilder() + .loopback() + .scheme("http") + .port(ss.getLocalPort()) + .path("/not-accepting/") + .buildUnchecked(); + URI https = URIBuilder.newBuilder() + .loopback() + .scheme("https") + .port(ss.getLocalPort()) + .path("/not-accepting/") + .buildUnchecked(); + // use all HTTP versions, without and with TLS + return List.of( + new TestCase(http.toString(), 200, Version.HTTP_2), + new TestCase(http.toString(), 200, Version.HTTP_1_1), + new TestCase(https.toString(), 200, Version.HTTP_2), + new TestCase(https.toString(), 200, Version.HTTP_1_1) + ); + } + + @ParameterizedTest + @MethodSource("parameters") + void runTest(TestCase test) { + System.out.println("Testing with: " + test); + runTest(test.url, test.reqCount, test.version); + } + + static class TestTaskScope implements AutoCloseable { + final ExecutorService pool = new ForkJoinPool(); + final Map, Future> tasks = new ConcurrentHashMap<>(); + final AtomicReference failed = new AtomicReference<>(); + + class Task implements Callable { + final Callable task; + final CompletableFuture cf = new CompletableFuture<>(); + Task(Callable task) { + this.task = task; + } + @Override + public T call() throws Exception { + try { + var res = task.call(); + cf.complete(res); + return res; + } catch (Throwable t) { + cf.completeExceptionally(t); + throw t; + } + } + CompletableFuture cf() { + return cf; + } + } + + + static class ShutdownOnFailure extends TestTaskScope { + public ShutdownOnFailure() {} + + @Override + protected void completed(Task task, T result, Throwable throwable) { + super.completed(task, result, throwable); + if (throwable != null) { + if (failed.get() == null) { + ExecutionException ex = throwable instanceof ExecutionException x + ? x : new ExecutionException(throwable); + failed.compareAndSet(null, ex); + } + tasks.entrySet().forEach(this::cancel); + } + } + + void cancel(Map.Entry, Future> entry) { + entry.getValue().cancel(true); + entry.getKey().cf().cancel(true); + tasks.remove(entry.getKey(), entry.getValue()); + } + + @Override + public CompletableFuture fork(Callable callable) { + var ex = failed.get(); + if (ex == null) { + return super.fork(callable); + } // otherwise do nothing + return CompletableFuture.failedFuture(new RejectedExecutionException()); + } + } + + public CompletableFuture fork(Callable callable) { + var task = new Task<>(callable); + var res = pool.submit(task); + tasks.put(task, res); + task.cf.whenComplete((r,t) -> completed(task, r, t)); + return task.cf; + } + + protected void completed(Task task, T result, Throwable throwable) { + tasks.remove(task); + } + + public void join() throws InterruptedException { + try { + var cfs = tasks.keySet().stream() + .map(Task::cf).toArray(CompletableFuture[]::new); + CompletableFuture.allOf(cfs).get(); + } catch (InterruptedException it) { + throw it; + } catch (ExecutionException ex) { + failed.compareAndSet(null, ex); + } + } + + public void throwIfFailed() throws ExecutionException { + ExecutionException x = failed.get(); + if (x != null) throw x; + } + + public void close() { + // ForkJoinPool does not implement AutoClosable in 17. + //pool.close(); + // Taking the full implementation of close() from 21 + // does not work. Some methods are not public. + pool.shutdownNow(); + try { + pool.awaitTermination(1, TimeUnit.DAYS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + + ExecutorService testExecutor() { + return Executors.newCachedThreadPool(); + } + + void runTest(String url, int reqCount, Version version) { + final var dest = URI.create(url); + ExecutorService executor = null; + try { + executor = testExecutor(); + HttpClient httpClient = makeClient(dest, version, executor); + TRACKER.track(httpClient); + Tracker tracker = TRACKER.getTracker(httpClient); + Throwable failed = null; + try { + try (final var scope = new TestTaskScope.ShutdownOnFailure()) { + launchAndProcessRequests(scope, httpClient, reqCount, dest); + } finally { + System.out.printf("StructuredTaskScope closed: STARTED=%s, SUCCESS=%s, INTERRUPT=%s, FAILED=%s%n", + STARTED.get(), SUCCESS.get(), INTERRUPT.get(), FAILED.get()); + } + System.out.println("ERROR: Expected TestException not thrown"); + throw new AssertionError("Expected TestException not thrown"); + } catch (TestException x) { + System.out.println("Got expected exception: " + x); + } catch (Throwable t) { + System.out.println("ERROR: Unexpected exception: " + t); + failed = t; + throw t; + } finally { + // we can either use the tracker or call HttpClient::close + if (useTracker) { + // using the tracker depends on GC but will give us some diagnostic + // if some operations are not properly cancelled and prevent the client + // from terminating + httpClient = null; + System.gc(); + System.out.println(TRACKER.diagnose(tracker)); + var error = TRACKER.check(tracker, 10000); + if (error != null) { + if (failed != null) error.addSuppressed(failed); + EXCEPTIONS.forEach(x -> { + System.out.println("FAILED: " + x); + }); + EXCEPTIONS.forEach(x -> { + x.printStackTrace(System.out); + }); + throw error; + } + } else { + // if not all operations terminate, close() will block + // forever and the test will fail in jtreg timeout. + // there will be no diagnostic. + //httpClient.close(); + // HttpClient does not implement AutoClosable in 17. + // Omitting this test variant. + } + System.out.println("HttpClient closed"); + } + } finally { + // ExecutorService does not implement AutoClosable in 17. + // Taken from ExecutorService close() implementation in 21. + if (executor != null) { + boolean terminated = executor.isTerminated(); + if (!terminated) { + executor.shutdown(); + boolean interrupted = false; + while (!terminated) { + try { + terminated = executor.awaitTermination(1L, TimeUnit.DAYS); + } catch (InterruptedException e) { + if (!interrupted) { + executor.shutdownNow(); + interrupted = true; + } + } + } + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + System.out.println("ThreadExecutor closed"); + } + // not all tasks may have been started before the scope was cancelled + // due to the first connect/timeout exception, but all tasks that started + // must have either succeeded, be interrupted, or failed + assertTrue(STARTED.get() > 0); + assertEquals(STARTED.get(), SUCCESS.get() + INTERRUPT.get() + FAILED.get()); + if (SUCCESS.get() > 0) { + // we don't expect any server to be listening and responding + System.out.println("WARNING: got some unexpected successful responses from " + + "\"" + NOT_ACCEPTING.getLocalSocketAddress() + "\": " + SUCCESS.get()); + } + } + + private void launchAndProcessRequests( + TestTaskScope.ShutdownOnFailure scope, + HttpClient httpClient, + int reqCount, + URI dest) { + for (int counter = 0; counter < reqCount; counter++) { + scope.fork(() -> + getAndCheck(httpClient, dest) + ); + } + try { + scope.join(); + } catch (InterruptedException e) { + throw new AssertionError("scope.join() was interrupted", e); + } + try { + scope.throwIfFailed(); + } catch (ExecutionException e) { + throw new TestException("something threw an exception in StructuredTaskScope", e); + } + } + + final static AtomicLong ID = new AtomicLong(); + final AtomicLong SUCCESS = new AtomicLong(); + final AtomicLong INTERRUPT = new AtomicLong(); + final AtomicLong FAILED = new AtomicLong(); + final AtomicLong STARTED = new AtomicLong(); + final CopyOnWriteArrayList EXCEPTIONS = new CopyOnWriteArrayList<>(); + private String getAndCheck(HttpClient httpClient, URI url) { + STARTED.incrementAndGet(); + final var response = sendRequest(httpClient, url); + String res = response.body(); + int statusCode = response.statusCode(); + assertEquals(200, statusCode); + return res; + } + + private HttpResponse sendRequest(HttpClient httpClient, URI url) { + var id = ID.incrementAndGet(); + try { + var request = HttpRequest.newBuilder(url).GET().build(); + var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + // System.out.println("Got response for " + id + ": " + response); + SUCCESS.incrementAndGet(); + return response; + } catch (InterruptedException e) { + INTERRUPT.incrementAndGet(); + // System.out.println("Got interrupted for " + id + ": " + e); + throw new RuntimeException(e); + } catch (Exception e) { + FAILED.incrementAndGet(); + EXCEPTIONS.add(e); + //System.out.println("Got exception for " + id + ": " + e); + throw new RuntimeException(e); + } + } + + @AfterAll + static void tearDown() { + try { + System.gc(); + var error = TRACKER.check(5000); + if (error != null) throw error; + } finally { + ServerSocket ss; + synchronized (HttpGetInCancelledFuture.class) { + ss = NOT_ACCEPTING; + NOT_ACCEPTING = null; + } + if (ss != null) { + try { + ss.close(); + } catch (IOException io) { + throw new UncheckedIOException(io); + } + } + } + } +} + diff --git a/test/jdk/java/net/httpclient/HttpsTunnelAuthTest.java b/test/jdk/java/net/httpclient/HttpsTunnelAuthTest.java index f83118adea3f6..07aa861dd2e06 100644 --- a/test/jdk/java/net/httpclient/HttpsTunnelAuthTest.java +++ b/test/jdk/java/net/httpclient/HttpsTunnelAuthTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,12 +37,11 @@ import java.util.stream.Stream; import javax.net.ssl.SSLContext; import jdk.httpclient.test.lib.common.HttpServerAdapters; -import jdk.httpclient.test.lib.http2.Http2TestServer; import jdk.test.lib.net.SimpleSSLContext; import static java.lang.System.out; -/** +/* * @test * @bug 8262027 * @summary Verify that it's possible to handle proxy authentication manually @@ -62,7 +61,7 @@ //-Djdk.internal.httpclient.debug=true -Dtest.debug=true public class HttpsTunnelAuthTest implements HttpServerAdapters, AutoCloseable { - static final String data[] = { + static final String[] data = { "Lorem ipsum", "dolor sit amet", "consectetur adipiscing elit, sed do eiusmod tempor", @@ -108,6 +107,7 @@ public class HttpsTunnelAuthTest implements HttpServerAdapters, AutoCloseable { } void setUp() throws IOException { + HttpServerAdapters.enableServerLogging(); // Creates an HTTP/1.1 Server that will authenticate for // arthur with password dent http1Server = DigestEchoServer.createServer(Version.HTTP_1_1, @@ -143,15 +143,13 @@ void setUp() throws IOException { // Creates a proxy selector that unconditionally select the // above proxy. - var ps = proxySelector = ProxySelector.of(proxy.getProxyAddress()); + proxySelector = ProxySelector.of(proxy.getProxyAddress()); - // Creates a client that uses the above proxy selector - client = newHttpClient(ps); } @Override public void close() throws Exception { - if (proxy != null) close(proxy::stop); + if (proxy != null) close(proxy); if (http1Server != null) close(http1Server::stop); if (https1Server != null) close(https1Server::stop); if (https2Server != null) close(https2Server::stop); @@ -161,7 +159,8 @@ private void close(AutoCloseable closeable) { try { closeable.close(); } catch (Exception x) { - // OK. + // OK to ignore and just log + System.err.println("ignoring failure during close() of " + closeable + " due to: " + x); } } @@ -177,23 +176,28 @@ public static void main(String[] args) throws Exception { try (HttpsTunnelAuthTest test = new HttpsTunnelAuthTest()) { test.setUp(); - // tests proxy and server authentication through: - // - plain proxy connection to plain HTTP/1.1 server, - test.test(Version.HTTP_1_1, "http", "/foo/http1"); + { + HttpClient client = test.newHttpClient(test.proxySelector); + // tests proxy and server authentication through: + // - plain proxy connection to plain HTTP/1.1 server, + test.test(client, Version.HTTP_1_1, "http", "/foo/http1"); + } // can't really test plain proxy connection to plain HTTP/2 server: // this is not supported: we downgrade to HTTP/1.1 in that case // so that is actually somewhat equivalent to the first case: // therefore we will use a new client to force re-authentication // of the proxy connection. - test.client = test.newHttpClient(test.proxySelector); - test.test(Version.HTTP_2, "http", "/foo/http2"); + { + HttpClient client = test.newHttpClient(test.proxySelector); + test.test(client, Version.HTTP_2, "http", "/foo/http2"); - // - proxy tunnel SSL connection to HTTP/1.1 server - test.test(Version.HTTP_1_1, "https", "/foo/https1"); + // - proxy tunnel SSL connection to HTTP/1.1 server + test.test(client, Version.HTTP_1_1, "https", "/foo/https1"); - // - proxy tunnel SSl connection to HTTP/2 server - test.test(Version.HTTP_2, "https", "/foo/https2"); + // - proxy tunnel SSl connection to HTTP/2 server + test.test(client, Version.HTTP_2, "https", "/foo/https2"); + } } } @@ -227,14 +231,14 @@ Version expectedVersion(String scheme, Version version) { return "http".equals(scheme) ? Version.HTTP_1_1 : version; } - public void test(Version version, String scheme, String path) throws Exception { + public void test(HttpClient client, Version version, String scheme, String path) throws Exception { System.out.printf("%nTesting %s, %s, %s%n", version, scheme, path); DigestEchoServer server = server(scheme, version); try { URI uri = jdk.test.lib.net.URIBuilder.newBuilder() .scheme(scheme) - .host("localhost") + .loopback() .port(server.getServerAddress().getPort()) .path(path).build(); diff --git a/test/jdk/java/net/httpclient/ISO_8859_1_Test.java b/test/jdk/java/net/httpclient/ISO_8859_1_Test.java index 4e3f428dbfb3b..e81b013041894 100644 --- a/test/jdk/java/net/httpclient/ISO_8859_1_Test.java +++ b/test/jdk/java/net/httpclient/ISO_8859_1_Test.java @@ -445,7 +445,7 @@ public void teardown() throws Exception { sharedClient == null ? null : sharedClient.toString(); sharedClient = null; Thread.sleep(100); - AssertionError fail = TRACKER.check(500); + AssertionError fail = TRACKER.check(1500); try { http1TestServer.stop(); https1TestServer.stop(); diff --git a/test/jdk/java/net/httpclient/ManyRequests.java b/test/jdk/java/net/httpclient/ManyRequests.java index 36b5c35000cf2..86e17d765af46 100644 --- a/test/jdk/java/net/httpclient/ManyRequests.java +++ b/test/jdk/java/net/httpclient/ManyRequests.java @@ -24,6 +24,7 @@ /* * @test * @bug 8087112 8180044 8256459 + * @key intermittent * @modules java.net.http * java.logging * jdk.httpserver diff --git a/test/jdk/java/net/httpclient/ProxySelectorTest.java b/test/jdk/java/net/httpclient/ProxySelectorTest.java index fd8f85fa6d7d2..a5407801bcdcd 100644 --- a/test/jdk/java/net/httpclient/ProxySelectorTest.java +++ b/test/jdk/java/net/httpclient/ProxySelectorTest.java @@ -376,7 +376,7 @@ public void setup() throws Exception { public void teardown() throws Exception { client = null; Thread.sleep(100); - AssertionError fail = TRACKER.check(500); + AssertionError fail = TRACKER.check(1500); try { proxy.stop(); authproxy.stop(); diff --git a/test/jdk/java/net/httpclient/ProxyServer.java b/test/jdk/java/net/httpclient/ProxyServer.java index e07badd2bb7aa..ddb584840799f 100644 --- a/test/jdk/java/net/httpclient/ProxyServer.java +++ b/test/jdk/java/net/httpclient/ProxyServer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,7 +42,7 @@ * Two threads are created per client connection. So, it's not * intended for large numbers of parallel connections. */ -public class ProxyServer extends Thread implements Closeable { +public final class ProxyServer implements Closeable { // could use the test library here - Platform.isWindows(), // but it would force all tests that use ProxyServer to @@ -99,9 +99,7 @@ public ProxyServer(Integer port, this(port, debug, null); } - public ProxyServer(Integer port, - Boolean debug, - Credentials credentials) + private ProxyServer(Integer port, Boolean debug, Credentials credentials) throws IOException { this.debug = debug; @@ -110,15 +108,11 @@ public ProxyServer(Integer port, listener.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), port)); this.port = ((InetSocketAddress)listener.getLocalAddress()).getPort(); this.credentials = credentials; - setName("ProxyListener"); - setDaemon(true); - connections = new CopyOnWriteArrayList(); - start(); - } - - public ProxyServer(String s) { - credentials = null; connections = new CopyOnWriteArrayList(); + Thread d = new Thread(() -> run()); + d.setName("ProxyListener"); + d.setDaemon(true); + d.start(); } /** @@ -150,7 +144,7 @@ public void close() throws IOException { volatile boolean done; - public void run() { + private void run() { if (System.getSecurityManager() == null) { execute(); } else { @@ -195,6 +189,9 @@ class Connection { volatile InputStream clientIn, serverIn; volatile OutputStream clientOut, serverOut; + boolean proxyInClosed; // only accessed from synchronized block + boolean proxyOutClosed; // only accessed from synchronized block + final static int CR = 13; final static int LF = 10; @@ -594,9 +591,7 @@ synchronized void proxyCommon(boolean log) throws IOException { if (log) System.out.printf("Proxy Forwarding [request body]: total %d%n", body); } - closing = true; - serverSocket.close(); - clientSocket.close(); + closeClientIn(); } catch (IOException e) { if (!closing && debug) { System.out.println("Proxy: " + e); @@ -615,9 +610,7 @@ synchronized void proxyCommon(boolean log) throws IOException { if (log) System.out.printf("Proxy Forwarding [response]: %s%n", new String(bb, 0, n, UTF_8)); if (log) System.out.printf("Proxy Forwarding [response]: total %d%n", resp); } - closing = true; - serverSocket.close(); - clientSocket.close(); + closeClientOut(); } catch (IOException e) { if (!closing && debug) { System.out.println("Proxy: " + e); @@ -641,6 +634,28 @@ void doTunnel(String dest) throws IOException { proxyCommon(false); } + synchronized void closeClientIn() throws IOException { + closing = true; + proxyInClosed = true; + clientSocket.shutdownInput(); + serverSocket.shutdownOutput(); + if (proxyOutClosed) { + serverSocket.close(); + clientSocket.close(); + } + } + + synchronized void closeClientOut() throws IOException { + closing = true; + proxyOutClosed = true; + serverSocket.shutdownInput(); + clientSocket.shutdownOutput(); + if (proxyInClosed) { + serverSocket.close(); + clientSocket.close(); + } + } + @Override public String toString() { return "Proxy connection " + id + ", client sock:" + clientSocket; @@ -651,10 +666,11 @@ public static void main(String[] args) throws Exception { int port = Integer.parseInt(args[0]); boolean debug = args.length > 1 && args[1].equals("-debug"); System.out.println("Debugging : " + debug); - ProxyServer ps = new ProxyServer(port, debug); - System.out.println("Proxy server listening on port " + ps.getPort()); - while (true) { - Thread.sleep(5000); + try (ProxyServer ps = new ProxyServer(port, debug)) { + System.out.println("Proxy server listening on port " + ps.getPort()); + while (true) { + Thread.sleep(5000); + } } } } diff --git a/test/jdk/java/net/httpclient/ReferenceTracker.java b/test/jdk/java/net/httpclient/ReferenceTracker.java index 7e6d88da2b6f9..d7e16d01201e8 100644 --- a/test/jdk/java/net/httpclient/ReferenceTracker.java +++ b/test/jdk/java/net/httpclient/ReferenceTracker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,8 +24,17 @@ import jdk.internal.net.http.common.OperationTrackers; import jdk.internal.net.http.common.OperationTrackers.Tracker; +import java.io.PrintStream; +import java.lang.management.LockInfo; +import java.lang.management.ManagementFactory; +import java.lang.management.MonitorInfo; +import java.lang.management.ThreadInfo; import java.net.http.HttpClient; +import java.time.Duration; +import java.util.Arrays; +import java.util.Objects; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.function.Predicate; import java.util.stream.Collectors; /** @@ -51,8 +60,25 @@ public long getTrackedClientCount() { } public StringBuilder diagnose(StringBuilder warnings) { + return diagnose(warnings, (t) -> t.getOutstandingHttpOperations() > 0); + } + + public StringBuilder diagnose(Tracker tracker) { + return diagnose(tracker, new StringBuilder(), (t) -> t.getOutstandingHttpOperations() > 0); + } + + public StringBuilder diagnose(HttpClient client) { + return diagnose(getTracker(client)); + } + + public StringBuilder diagnose(Tracker tracker, StringBuilder warnings, Predicate hasOutstanding) { + checkOutstandingOperations(warnings, tracker, hasOutstanding); + return warnings; + } + + public StringBuilder diagnose(StringBuilder warnings, Predicate hasOutstanding) { for (Tracker tracker : TRACKERS) { - checkOutstandingOperations(warnings, tracker); + diagnose(tracker, warnings, hasOutstanding); } return warnings; } @@ -61,6 +87,10 @@ public boolean hasOutstandingOperations() { return TRACKERS.stream().anyMatch(t -> t.getOutstandingOperations() > 0); } + public boolean hasOutstandingSubscribers() { + return TRACKERS.stream().anyMatch(t -> t.getOutstandingSubscribers() > 0); + } + public long getOutstandingOperationsCount() { return TRACKERS.stream() .map(Tracker::getOutstandingOperations) @@ -75,22 +105,239 @@ public long getOutstandingClientCount() { .count(); } + public AssertionError check(Tracker tracker, long graceDelayMs) { + Predicate hasOperations = (t) -> t.getOutstandingOperations() > 0; + Predicate hasSubscribers = (t) -> t.getOutstandingSubscribers() > 0; + return check(tracker, graceDelayMs, + hasOperations.or(hasSubscribers) + .or(Tracker::isFacadeReferenced) + .or(Tracker::isSelectorAlive), + "outstanding operations or unreleased resources", true); + } + + public AssertionError checkFinished(Tracker tracker, long graceDelayMs) { + Predicate hasOperations = (t) -> t.getOutstandingOperations() > 0; + Predicate hasSubscribers = (t) -> t.getOutstandingSubscribers() > 0; + return check(tracker, graceDelayMs, + hasOperations.or(hasSubscribers), + "outstanding operations or unreleased resources", false); + } + public AssertionError check(long graceDelayMs) { + Predicate hasOperations = (t) -> t.getOutstandingOperations() > 0; + Predicate hasSubscribers = (t) -> t.getOutstandingSubscribers() > 0; + return check(graceDelayMs, + hasOperations.or(hasSubscribers) + .or(Tracker::isFacadeReferenced) + .or(Tracker::isSelectorAlive), + "outstanding operations or unreleased resources", true); + } + + // This method is copied from ThreadInfo::toString, but removes the + // limit on the stack trace depth (8 frames max) that ThreadInfo::toString + // forcefully implement. We want to print all frames for better diagnosis. + private static String toString(ThreadInfo info) { + StringBuilder sb = new StringBuilder("\"" + info.getThreadName() + "\"" + + (info.isDaemon() ? " daemon" : "") + + " prio=" + info.getPriority() + + " Id=" + info.getThreadId() + " " + + info.getThreadState()); + if (info.getLockName() != null) { + sb.append(" on " + info.getLockName()); + } + if (info.getLockOwnerName() != null) { + sb.append(" owned by \"" + info.getLockOwnerName() + + "\" Id=" + info.getLockOwnerId()); + } + if (info.isSuspended()) { + sb.append(" (suspended)"); + } + if (info.isInNative()) { + sb.append(" (in native)"); + } + sb.append('\n'); + int i = 0; + var stackTrace = info.getStackTrace(); + for (; i < stackTrace.length ; i++) { + StackTraceElement ste = stackTrace[i]; + sb.append("\tat " + ste.toString()); + sb.append('\n'); + if (i == 0 && info.getLockInfo() != null) { + Thread.State ts = info.getThreadState(); + switch (ts) { + case BLOCKED: + sb.append("\t- blocked on " + info.getLockInfo()); + sb.append('\n'); + break; + case WAITING: + sb.append("\t- waiting on " + info.getLockInfo()); + sb.append('\n'); + break; + case TIMED_WAITING: + sb.append("\t- waiting on " + info.getLockInfo()); + sb.append('\n'); + break; + default: + } + } + + for (MonitorInfo mi : info.getLockedMonitors()) { + if (mi.getLockedStackDepth() == i) { + sb.append("\t- locked " + mi); + sb.append('\n'); + } + } + } + if (i < stackTrace.length) { + sb.append("\t..."); + sb.append('\n'); + } + + LockInfo[] locks = info.getLockedSynchronizers(); + if (locks.length > 0) { + sb.append("\n\tNumber of locked synchronizers = " + locks.length); + sb.append('\n'); + for (LockInfo li : locks) { + sb.append("\t- " + li); + sb.append('\n'); + } + } + sb.append('\n'); + return sb.toString(); + } + + private void printThreads(String why, PrintStream out) { + out.println(why); + Arrays.stream(ManagementFactory.getThreadMXBean() + .dumpAllThreads(true, true)) + .map(ReferenceTracker::toString) + .forEach(out::println); + } + + public Tracker getTracker(HttpClient client) { + return OperationTrackers.getTracker(Objects.requireNonNull(client)); + } + + public AssertionError check(Tracker tracker, + long graceDelayMs, + Predicate hasOutstanding, + String description, + boolean printThreads) { AssertionError fail = null; - if (hasOutstandingOperations()) { - try { - Thread.sleep(graceDelayMs); - } catch (InterruptedException x) { - // OK + graceDelayMs = Math.max(graceDelayMs, 100); + long delay = Math.min(graceDelayMs, 10); + var count = delay > 0 ? graceDelayMs / delay : 1; + long waitStart = System.nanoTime(); + long waited = 0; + long toWait = Math.min(graceDelayMs, Math.max(delay, 1)); + int i = 0; + for (i = 0; i < count; i++) { + if (hasOutstanding.test(tracker)) { + System.gc(); + try { + if (i == 0) { + System.out.println("Waiting for HTTP operations to terminate..."); + System.out.println("\tgracedelay: " + graceDelayMs + + " ms, iterations: " + count + ", wait/iteration: " + toWait + "ms"); + } + waited += toWait; + Thread.sleep(toWait); + } catch (InterruptedException x) { + // OK + } + } else { + System.out.println("No outstanding HTTP operations remaining after " + + i + "/" + count + " iterations and " + waited + "/" + graceDelayMs + + " ms, (wait/iteration " + toWait + " ms)"); + break; + } + } + long duration = Duration.ofNanos(System.nanoTime() - waitStart).toMillis(); + if (hasOutstanding.test(tracker)) { + if (i == 0 && waited == 0) { + // we found nothing and didn't wait expecting success, but then found + // something. Respin to make sure we wait. + return check(tracker, graceDelayMs, hasOutstanding, description, printThreads); + } + StringBuilder warnings = diagnose(tracker, new StringBuilder(), hasOutstanding); + if (hasOutstanding.test(tracker)) { + fail = new AssertionError(warnings.toString()); + } + } else { + System.out.println("PASSED: No " + description + " found in " + + tracker.getName() + " in " + duration + " ms"); + } + if (fail != null) { + if (printThreads && tracker.isSelectorAlive()) { + var msg = "Selector manager threads are still alive for " + tracker.getName() + ": "; + printThreads(msg, System.out); + printThreads(msg, System.err); } - StringBuilder warnings = diagnose(new StringBuilder()); + System.out.println("AssertionError: Found some " + description + " in " + + tracker.getName() + " after " + i + " iterations and " + duration + + " ms, waited " + waited + " ms"); + } + return fail; + } + + public AssertionError check(long graceDelayMs, + Predicate hasOutstanding, + String description, + boolean printThreads) { + AssertionError fail = null; + graceDelayMs = Math.max(graceDelayMs, 100); + long waitStart = System.nanoTime(); + long delay = Math.min(graceDelayMs, 10); + long toWait = Math.min(graceDelayMs, Math.max(delay, 1)); + long waited = 0; + var count = delay > 0 ? graceDelayMs / delay : 1; + int i = 0; + for (i = 0; i < count; i++) { + if (TRACKERS.stream().anyMatch(hasOutstanding)) { + System.gc(); + try { + if (i == 0) { + System.out.println("Waiting for HTTP operations to terminate..."); + System.out.println("\tgracedelay: " + graceDelayMs + + " ms, iterations: " + count + ", wait/iteration: " + toWait + "ms"); + } + waited += toWait; + Thread.sleep(toWait); + } catch (InterruptedException x) { + // OK + } + } else { + System.out.println("No outstanding HTTP operations remaining after " + + i + "/" + count + " iterations and " + waited + "/" + graceDelayMs + + " ms, (wait/iteration " + toWait + " ms)"); + break; + } + } + long duration = Duration.ofNanos(System.nanoTime() - waitStart).toMillis(); + if (TRACKERS.stream().anyMatch(hasOutstanding)) { + if (i == 0 && waited == 0) { + // we found nothing and didn't wait expecting success, but then found + // something. Respin to make sure we wait. + return check(graceDelayMs, hasOutstanding, description, printThreads); + } + StringBuilder warnings = diagnose(new StringBuilder(), hasOutstanding); addSummary(warnings); - if (hasOutstandingOperations()) { + if (TRACKERS.stream().anyMatch(hasOutstanding)) { fail = new AssertionError(warnings.toString()); } } else { - System.out.println("PASSED: No outstanding operations found in " - + getTrackedClientCount() + " clients"); + System.out.println("PASSED: No " + description + " found in " + + getTrackedClientCount() + " clients in " + duration + " ms"); + } + if (fail != null) { + Predicate isAlive = Tracker::isSelectorAlive; + if (printThreads && TRACKERS.stream().anyMatch(isAlive)) { + printThreads("Some selector manager threads are still alive: ", System.out); + printThreads("Some selector manager threads are still alive: ", System.err); + } + System.out.println("AssertionError: Found some " + description + " in " + + getTrackedClientCount() + " clients after " + i + " iterations and " + duration + + " ms, waited " + waited + " ms"); } return fail; } @@ -108,23 +355,53 @@ private void addSummary(StringBuilder warning) { .append(" operations still pending out of ") .append(tracked) .append(" tracked clients."); - System.out.println(warning.toString().substring(pos)); - System.err.println(warning.toString().substring(pos)); + System.out.println(warning.substring(pos)); + System.err.println(warning.substring(pos)); } - private static void checkOutstandingOperations(StringBuilder warning, Tracker tracker) { - if (tracker.getOutstandingOperations() > 0) { + private static void checkOutstandingOperations(StringBuilder warning, + Tracker tracker, + Predicate hasOutsanding) { + if (hasOutsanding.test(tracker)) { if (warning.length() > 0) warning.append("\n"); int pos = warning.length(); warning.append("WARNING: tracker for " + tracker.getName() + " has outstanding operations:"); + warning.append("\n\tPending HTTP Requests: " + tracker.getOutstandingHttpRequests()); warning.append("\n\tPending HTTP/1.1 operations: " + tracker.getOutstandingHttpOperations()); warning.append("\n\tPending HTTP/2 streams: " + tracker.getOutstandingHttp2Streams()); warning.append("\n\tPending WebSocket operations: " + tracker.getOutstandingWebSocketOperations()); + warning.append("\n\tPending TCP connections: " + tracker.getOutstandingTcpConnections()); + warning.append("\n\tPending Subscribers: " + tracker.getOutstandingSubscribers()); warning.append("\n\tTotal pending operations: " + tracker.getOutstandingOperations()); warning.append("\n\tFacade referenced: " + tracker.isFacadeReferenced()); - System.out.println(warning.toString().substring(pos)); - System.err.println(warning.toString().substring(pos)); + warning.append("\n\tSelector alive: " + tracker.isSelectorAlive()); + System.out.println(warning.substring(pos)); + System.err.println(warning.substring(pos)); } } + private boolean isSelectorManager(Thread t) { + String name = t.getName(); + if (name == null) return false; + return name.contains("SelectorManager"); + } + + // This is a slightly more permissive check than the default checks, + // it only verifies that all CFs returned by send/sendAsync have been + // completed, and that all opened channels have been closed, and that + // the selector manager thread has exited. + // It doesn't check that all refcounts have reached 0. + // This is typically useful to only check that resources have been released. + public AssertionError checkShutdown(long graceDelayMs) { + Predicate isAlive = Tracker::isSelectorAlive; + Predicate hasPendingRequests = (t) -> t.getOutstandingHttpRequests() > 0; + Predicate hasPendingConnections = (t) -> t.getOutstandingTcpConnections() > 0; + Predicate hasPendingSubscribers = (t) -> t.getOutstandingSubscribers() > 0; + AssertionError failed = check(graceDelayMs, + isAlive.or(hasPendingRequests) + .or(hasPendingConnections) + .or(hasPendingSubscribers), + "outstanding unclosed resources", true); + return failed; + } } diff --git a/test/jdk/java/net/httpclient/ResponseBodyBeforeError.java b/test/jdk/java/net/httpclient/ResponseBodyBeforeError.java index f176531f93e1a..d1cffff72cfd9 100644 --- a/test/jdk/java/net/httpclient/ResponseBodyBeforeError.java +++ b/test/jdk/java/net/httpclient/ResponseBodyBeforeError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,7 @@ * @test * @summary Tests that all response body is delivered to the BodySubscriber * before an abortive error terminates the flow + * @modules java.net.http/jdk.internal.net.http.common * @library /test/lib * @build jdk.test.lib.net.SimpleSSLContext * @run testng/othervm ResponseBodyBeforeError @@ -59,6 +60,8 @@ import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLServerSocketFactory; + +import static java.lang.System.err; import static java.lang.System.out; import static java.net.http.HttpClient.Builder.NO_PROXY; import static java.net.http.HttpResponse.BodyHandlers.ofString; @@ -177,6 +180,7 @@ public Object[][] variants() { } static final int ITERATION_COUNT = 3; + static final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; @Test(dataProvider = "uris") void testSynchronousAllRequestBody(String url, @@ -186,24 +190,34 @@ void testSynchronousAllRequestBody(String url, { out.print("---\n"); HttpClient client = null; - for (int i=0; i< ITERATION_COUNT; i++) { - if (!sameClient || client == null) - client = HttpClient.newBuilder() - .proxy(NO_PROXY) - .sslContext(sslContext) - .build(); - HttpRequest request = HttpRequest.newBuilder(URI.create(url)).build(); - CustomBodySubscriber bs = new CustomBodySubscriber(); - try { - HttpResponse response = client.send(request, r -> bs); - String body = response.body(); - out.println(response + ": " + body); - fail("UNEXPECTED RESPONSE: " + response); - } catch (IOException expected) { - String pm = bs.receivedAsString(); - out.println("partial body received: " + pm); - assertEquals(pm, expectedPatrialBody); + try { + for (int i = 0; i < ITERATION_COUNT; i++) { + if (!sameClient || client == null) { + client = HttpClient.newBuilder() + .proxy(NO_PROXY) + .sslContext(sslContext) + .build(); + TRACKER.track(client); + System.gc(); + } + HttpRequest request = HttpRequest.newBuilder(URI.create(url)).build(); + CustomBodySubscriber bs = new CustomBodySubscriber(); + try { + HttpResponse response = client.send(request, r -> bs); + String body = response.body(); + out.println(response + ": " + body); + fail("UNEXPECTED RESPONSE: " + response); + } catch (IOException expected) { + String pm = bs.receivedAsString(); + out.println("partial body received: " + pm); + assertEquals(pm, expectedPatrialBody); + } } + } finally { + client = null; + System.gc(); + var error = TRACKER.checkShutdown(1000); + if (error != null) throw error; } } @@ -215,28 +229,38 @@ void testAsynchronousAllRequestBody(String url, { out.print("---\n"); HttpClient client = null; - for (int i=0; i< ITERATION_COUNT; i++) { - if (!sameClient || client == null) - client = HttpClient.newBuilder() - .proxy(NO_PROXY) - .sslContext(sslContext) - .build(); - HttpRequest request = HttpRequest.newBuilder(URI.create(url)).build(); - CustomBodySubscriber bs = new CustomBodySubscriber(); - try { - HttpResponse response = client.sendAsync(request, r -> bs).get(); - String body = response.body(); - out.println(response + ": " + body); - fail("UNEXPECTED RESPONSE: " + response); - } catch (ExecutionException ee) { - if (ee.getCause() instanceof IOException) { - String pm = bs.receivedAsString(); - out.println("partial body received: " + pm); - assertEquals(pm, expectedPatrialBody); - } else { - throw ee; + try { + for (int i = 0; i < ITERATION_COUNT; i++) { + if (!sameClient || client == null) { + client = HttpClient.newBuilder() + .proxy(NO_PROXY) + .sslContext(sslContext) + .build(); + System.gc(); + TRACKER.track(client); + } + HttpRequest request = HttpRequest.newBuilder(URI.create(url)).build(); + CustomBodySubscriber bs = new CustomBodySubscriber(); + try { + HttpResponse response = client.sendAsync(request, r -> bs).get(); + String body = response.body(); + out.println(response + ": " + body); + fail("UNEXPECTED RESPONSE: " + response); + } catch (ExecutionException ee) { + if (ee.getCause() instanceof IOException) { + String pm = bs.receivedAsString(); + out.println("partial body received: " + pm); + assertEquals(pm, expectedPatrialBody); + } else { + throw ee; + } } } + } finally { + client = null; + System.gc(); + var error = TRACKER.checkShutdown(1000); + if (error != null) throw error; } } diff --git a/test/jdk/java/net/httpclient/SmallTimeout.java b/test/jdk/java/net/httpclient/SmallTimeout.java index 91ffb3305c662..6515c38647e13 100644 --- a/test/jdk/java/net/httpclient/SmallTimeout.java +++ b/test/jdk/java/net/httpclient/SmallTimeout.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,6 +21,8 @@ * questions. */ +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; @@ -35,6 +37,8 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + import static java.lang.System.out; /** @@ -79,6 +83,7 @@ static HttpResult of(HttpRequest request, Throwable t) { public static void main(String[] args) throws Exception { HttpClient client = HttpClient.newHttpClient(); ReferenceTracker.INSTANCE.track(client); + Reference reference = new WeakReference<>(client); Throwable failed = null; try (ServerSocket ss = new ServerSocket()) { @@ -151,10 +156,12 @@ public static void main(String[] args) throws Exception { .build(); final HttpRequest req = requests[i]; + executor.execute(() -> { Throwable cause = null; try { - HttpResponse r = client.send(req, BodyHandlers.replacing(null)); + HttpClient httpClient = reference.get(); + HttpResponse r = httpClient.send(req, BodyHandlers.replacing(null)); out.println("Unexpected success for r" + n +": " + r); } catch (HttpTimeoutException e) { out.println("Caught expected timeout for r" + n +": " + e); @@ -173,7 +180,31 @@ public static void main(String[] args) throws Exception { checkReturn(requests); - executor.shutdownNow(); + // shuts down the executor and awaits its termination + //executor.close(); + // In 17, ExecutorService does not implement close(). + // I derived this from the implementation of the method + // in 21. + { + boolean terminated = executor.isTerminated(); + if (!terminated) { + executor.shutdown(); + boolean interrupted = false; + while (!terminated) { + try { + terminated = executor.awaitTermination(1L, TimeUnit.DAYS); + } catch (InterruptedException e) { + if (!interrupted) { + executor.shutdownNow(); + interrupted = true; + } + } + } + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } if (error) throw new RuntimeException("Failed. Check output"); @@ -182,8 +213,11 @@ public static void main(String[] args) throws Exception { failed = t; throw t; } finally { + Reference.reachabilityFence(client); + client = null; + System.gc(); try { - Thread.sleep(100); + Thread.sleep(10); } catch (InterruptedException t) { // ignore; } diff --git a/test/jdk/java/net/httpclient/SpecialHeadersTest.java b/test/jdk/java/net/httpclient/SpecialHeadersTest.java index 4f6e5ad2b546d..37cb47a6872fb 100644 --- a/test/jdk/java/net/httpclient/SpecialHeadersTest.java +++ b/test/jdk/java/net/httpclient/SpecialHeadersTest.java @@ -25,7 +25,7 @@ * @test * @summary Verify that some special headers - such as User-Agent * can be specified by the caller. - * @bug 8203771 8218546 + * @bug 8203771 8218546 8297200 * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.common.HttpServerAdapters * jdk.httpclient.test.lib.http2.Http2TestServer @@ -42,6 +42,7 @@ import com.sun.net.httpserver.HttpServer; import com.sun.net.httpserver.HttpsConfigurator; import com.sun.net.httpserver.HttpsServer; +import jdk.internal.net.http.common.OperationTrackers.Tracker; import jdk.test.lib.net.SimpleSSLContext; import org.testng.ITestContext; import org.testng.ITestResult; @@ -300,13 +301,18 @@ static String userAgent() { static final Map> DEFAULTS = Map.of( "USER-AGENT", u -> userAgent(), "HOST", u -> u.getRawAuthority()); + static void throwIfNotNull(Throwable throwable) throws Exception { + if (throwable instanceof Exception ex) throw ex; + if (throwable instanceof Error e) throw e; + } + @Test(dataProvider = "variants") void test(String uriString, String headerNameAndValue, boolean sameClient) throws Exception { - out.println("\n--- Starting "); + out.println("\n--- Starting test " + now()); int index = headerNameAndValue.indexOf(":"); String name = headerNameAndValue.substring(0, index); @@ -318,10 +324,14 @@ void test(String uriString, String value = useDefault ? DEFAULTS.get(key).apply(uri) : v; HttpClient client = null; + Tracker tracker = null; + Throwable thrown = null; for (int i=0; i< ITERATION_COUNT; i++) { try { - if (!sameClient || client == null) + if (!sameClient || client == null) { client = newHttpClient("test", sameClient); + tracker = TRACKER.getTracker(client); + } HttpRequest.Builder requestBuilder = HttpRequest.newBuilder(uri); if (!useDefault) { @@ -361,14 +371,20 @@ void test(String uriString, assertEquals(resp.headers().allValues("X-" + key).size(), 0); } } + } catch (Throwable x) { + thrown = x; } finally { if (!sameClient) { client = null; System.gc(); - var error = TRACKER.check(500); - if (error != null) throw error; + var error = TRACKER.check(tracker, 500); + if (error != null) { + if (thrown != null) error.addSuppressed(thrown); + throw error; + } } } + throwIfNotNull(thrown); } } @@ -378,11 +394,12 @@ void testHomeMadeIllegalHeader(String uriString, boolean sameClient) throws Exception { - out.println("\n--- Starting "); + out.println("\n--- Starting testHomeMadeIllegalHeader " + now()); final URI uri = URI.create(uriString); HttpClient client = newHttpClient("testHomeMadeIllegalHeader", sameClient); - + Tracker tracker = TRACKER.getTracker(client); + Throwable thrown = null; try { // Test a request which contains an illegal header created HttpRequest req = new HttpRequest() { @@ -429,19 +446,29 @@ public HttpHeaders headers() { } catch (IllegalArgumentException ee) { out.println("Got IAE as expected"); } + } catch (Throwable x) { + thrown = x; } finally { if (!sameClient) { client = null; System.gc(); - var error = TRACKER.check(500); - if (error != null) throw error; + var error = TRACKER.check(tracker, 500); + if (error != null) { + if (thrown != null) error.addSuppressed(thrown); + throw error; + } } } + throwIfNotNull(thrown); } + + @Test(dataProvider = "variants") - void testAsync(String uriString, String headerNameAndValue, boolean sameClient) { - out.println("\n--- Starting "); + void testAsync(String uriString, String headerNameAndValue, boolean sameClient) + throws Exception + { + out.println("\n--- Starting testAsync " + now()); int index = headerNameAndValue.indexOf(":"); String name = headerNameAndValue.substring(0, index); String v = headerNameAndValue.substring(index+1).trim(); @@ -452,10 +479,14 @@ void testAsync(String uriString, String headerNameAndValue, boolean sameClient) String value = useDefault ? DEFAULTS.get(key).apply(uri) : v; HttpClient client = null; + Tracker tracker = null; + Throwable thrown = null; for (int i=0; i< ITERATION_COUNT; i++) { try { - if (!sameClient || client == null) + if (!sameClient || client == null) { client = newHttpClient("testAsync", sameClient); + tracker = TRACKER.getTracker(client); + } HttpRequest.Builder requestBuilder = HttpRequest.newBuilder(uri); if (!useDefault) { @@ -498,14 +529,20 @@ void testAsync(String uriString, String headerNameAndValue, boolean sameClient) } }) .join(); + } catch (Throwable x) { + thrown = x; } finally { if (!sameClient) { client = null; System.gc(); - var error = TRACKER.check(500); - if (error != null) throw error; + var error = TRACKER.check(tracker, 500); + if (error != null) { + if (thrown != null) error.addSuppressed(thrown); + throw error; + } } } + throwIfNotNull(thrown); } } @@ -516,6 +553,7 @@ static String serverAuthority(HttpTestServer server) { @BeforeTest public void setup() throws Exception { + out.println("--- Starting setup " + now()); sslContext = new SimpleSSLContext().get(); if (sslContext == null) throw new AssertionError("Unexpected null sslContext"); @@ -545,13 +583,15 @@ public void setup() throws Exception { @AfterTest public void teardown() throws Exception { + out.println("\n--- Teardown " + now()); HttpClient shared = sharedClient; String sharedClientName = shared == null ? null : shared.toString(); if (shared != null) TRACKER.track(shared); shared = sharedClient = null; Thread.sleep(100); - AssertionError fail = TRACKER.check(500); + AssertionError fail = TRACKER.check(2500); + out.println("--- Stopping servers " + now()); try { httpTestServer.stop(); httpsTestServer.stop(); diff --git a/test/jdk/java/net/httpclient/http2/BasicTest.java b/test/jdk/java/net/httpclient/http2/BasicTest.java index 39cad481749d0..9d54c8c39e0ba 100644 --- a/test/jdk/java/net/httpclient/http2/BasicTest.java +++ b/test/jdk/java/net/httpclient/http2/BasicTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,9 +24,14 @@ /* * @test * @bug 8087112 - * @library /test/lib /test/jdk/java/net/httpclient/lib - * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.common.TestUtil - * jdk.httpclient.test.lib.http2.Http2TestServer + * @library /test/jdk/java/net/httpclient/lib + * /test/lib + * @build jdk.httpclient.test.lib.http2.Http2TestServer + * jdk.httpclient.test.lib.http2.Http2TestExchange + * jdk.httpclient.test.lib.http2.Http2EchoHandler + * jdk.test.lib.Asserts + * jdk.test.lib.Utils + * jdk.test.lib.net.SimpleSSLContext * @run testng/othervm -Djdk.httpclient.HttpClient.log=ssl,requests,responses,errors BasicTest */ @@ -44,17 +49,23 @@ import java.util.Collections; import java.util.LinkedList; import java.util.List; -import jdk.httpclient.test.lib.common.TestUtil; import jdk.httpclient.test.lib.http2.Http2TestServer; import jdk.httpclient.test.lib.http2.Http2TestExchange; -import jdk.httpclient.test.lib.http2.Http2Handler; import jdk.httpclient.test.lib.http2.Http2EchoHandler; import jdk.test.lib.net.SimpleSSLContext; import org.testng.annotations.Test; + import static java.net.http.HttpClient.Version.HTTP_2; +import static jdk.test.lib.Asserts.assertFileContentsEqual; +import static jdk.test.lib.Utils.createTempFile; +import static jdk.test.lib.Utils.createTempFileOfSize; @Test public class BasicTest { + + private static final String TEMP_FILE_PREFIX = + HttpClient.class.getPackageName() + '-' + BasicTest.class.getSimpleName() + '-'; + static int httpPort, httpsPort; static Http2TestServer httpServer, httpsServer; static HttpClient client = null; @@ -184,14 +195,6 @@ static void checkStrings(String expected, String found) throws Exception { } } - static Void compareFiles(Path path1, Path path2) { - return TestUtil.compareFiles(path1, path2); - } - - static Path tempFile() { - return TestUtil.tempFile(); - } - static final String SIMPLE_STRING = "Hello world Goodbye world"; static final int LOOPS = 13; @@ -202,7 +205,7 @@ static void streamTest(boolean secure) throws Exception { System.err.printf("streamTest %b to %s\n" , secure, uri); HttpClient client = getClient(); - Path src = TestUtil.getAFile(FILESIZE * 4); + Path src = createTempFileOfSize(TEMP_FILE_PREFIX, null, FILESIZE * 4); HttpRequest req = HttpRequest.newBuilder(uri) .POST(BodyPublishers.ofFile(src)) .build(); @@ -216,7 +219,7 @@ static void streamTest(boolean secure) throws Exception { return resp.body(); }); response.join(); - compareFiles(src, dest); + assertFileContentsEqual(src, dest); System.err.println("streamTest: DONE"); } @@ -269,17 +272,19 @@ static void simpleTest(boolean secure, boolean ping) throws Exception { // Do loops asynchronously CompletableFuture[] responses = new CompletableFuture[LOOPS]; - final Path source = TestUtil.getAFile(FILESIZE); + final Path source = createTempFileOfSize(TEMP_FILE_PREFIX, null, FILESIZE); HttpRequest request = HttpRequest.newBuilder(uri) .POST(BodyPublishers.ofFile(source)) .build(); for (int i = 0; i < LOOPS; i++) { - responses[i] = client.sendAsync(request, BodyHandlers.ofFile(tempFile())) + responses[i] = client.sendAsync(request, BodyHandlers.ofFile(createTempFile(TEMP_FILE_PREFIX, null))) //.thenApply(resp -> compareFiles(resp.body(), source)); .thenApply(resp -> { + Path body = resp.body(); System.out.printf("Resp status %d body size %d\n", - resp.statusCode(), resp.body().toFile().length()); - return compareFiles(resp.body(), source); + resp.statusCode(), body.toFile().length()); + assertFileContentsEqual(body, source); + return null; }); Thread.sleep(100); } diff --git a/test/jdk/java/net/httpclient/http2/ConnectionFlowControlTest.java b/test/jdk/java/net/httpclient/http2/ConnectionFlowControlTest.java new file mode 100644 index 0000000000000..1f18a89784100 --- /dev/null +++ b/test/jdk/java/net/httpclient/http2/ConnectionFlowControlTest.java @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8342075 + * @summary checks connection flow control + * @library /test/lib /test/jdk/java/net/httpclient/lib + * @build jdk.httpclient.test.lib.http2.Http2TestServer jdk.test.lib.net.SimpleSSLContext + * @run testng/othervm -Djdk.internal.httpclient.debug=true + * -Djdk.httpclient.connectionWindowSize=65535 + * -Djdk.httpclient.windowsize=16384 + * ConnectionFlowControlTest + */ + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.ProtocolException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpHeaders; +import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublishers; +import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandler; +import java.net.http.HttpResponse.BodyHandlers; +import java.net.http.HttpResponse.BodySubscriber; +import java.net.http.HttpResponse.ResponseInfo; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map.Entry; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Flow.Subscription; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; + +import jdk.httpclient.test.lib.common.HttpServerAdapters.HttpTestServer; +import jdk.httpclient.test.lib.http2.BodyOutputStream; +import jdk.httpclient.test.lib.http2.Http2Handler; +import jdk.httpclient.test.lib.http2.Http2TestExchange; +import jdk.httpclient.test.lib.http2.Http2TestExchangeImpl; +import jdk.httpclient.test.lib.http2.Http2TestServer; +import jdk.httpclient.test.lib.http2.Http2TestServerConnection; +import jdk.internal.net.http.common.HttpHeadersBuilder; +import jdk.internal.net.http.frame.ContinuationFrame; +import jdk.internal.net.http.frame.HeaderFrame; +import jdk.internal.net.http.frame.HeadersFrame; +import jdk.internal.net.http.frame.Http2Frame; +import jdk.internal.net.http.frame.SettingsFrame; +import jdk.test.lib.Utils; +import jdk.test.lib.net.SimpleSSLContext; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static java.util.List.of; +import static java.util.Map.entry; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotEquals; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +public class ConnectionFlowControlTest { + + SSLContext sslContext; + HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + String http2URI; + String https2URI; + final AtomicInteger reqid = new AtomicInteger(); + + + @DataProvider(name = "variants") + public Object[][] variants() { + return new Object[][] { + { http2URI }, + { https2URI }, + }; + } + + @Test(dataProvider = "variants") + void test(String uri) throws Exception { + System.out.printf("%ntesting %s%n", uri); + ConcurrentHashMap> responseSent = new ConcurrentHashMap<>(); + ConcurrentHashMap> responses = new ConcurrentHashMap<>(); + FCHttp2TestExchange.setResponseSentCB((s) -> responseSent.get(s).complete(s)); + int connectionWindowSize = Math.max(Integer.getInteger( + "jdk.httpclient.connectionWindowSize", 65535), 65535); + int windowSize = Math.max(Integer.getInteger( + "jdk.httpclient.windowsize", 65535), 16384); + int max = connectionWindowSize / windowSize + 2; + System.out.printf("connection window: %s, stream window: %s, will make %s requests%n", + connectionWindowSize, windowSize, max); + + HttpClient client = HttpClient.newBuilder().executor(Executors.newCachedThreadPool()).sslContext(sslContext).build(); + try { + String label = null; + + Throwable t = null; + try { + String[] keys = new String[max]; + for (int i = 0; i < max; i++) { + String query = "reqId=" + reqid.incrementAndGet(); + keys[i] = query; + URI uriWithQuery = URI.create(uri + "?" + query); + CompletableFuture sent = new CompletableFuture<>(); + responseSent.put(query, sent); + HttpRequest request = HttpRequest.newBuilder(uriWithQuery) + .POST(BodyPublishers.ofString("Hello there!")) + .build(); + System.out.println("\nSending request:" + uriWithQuery); + final HttpClient cc = client; + var response = cc.send(request, BodyHandlers.ofInputStream()); + responses.put(query, response); + String ckey = response.headers().firstValue("X-Connection-Key").get(); + if (label == null) label = ckey; + try { + if (i < max - 1) { + // the connection window might be exceeded at i == max - 2, which + // means that the last request could go on a new connection. + assertEquals(ckey, label, "Unexpected key for " + query); + } + } catch (AssertionError ass) { + // since we won't pull all responses, the client + // will not exit unless we ask it to shutdown now. + ExecutorService exec = (ExecutorService)client.executor().get(); + exec.shutdownNow(); + throw ass; + } + } + try { + Thread.sleep(1000); + } catch (InterruptedException ie) { + // ignore + } + CompletableFuture allsent = CompletableFuture.allOf(responseSent.values().stream() + .toArray(CompletableFuture[]::new)); + allsent.get(); + for (int i = 0; i < max; i++) { + try { + String query = keys[i]; + var response = responses.get(keys[i]); + String ckey = response.headers().firstValue("X-Connection-Key").get(); + if (label == null) label = ckey; + if (i < max - 1) { + // the connection window might be exceeded at i == max - 2, which + // means that the last request could go on a new connection. + assertEquals(ckey, label, "Unexpected key for " + query); + } + int wait = uri.startsWith("https://") ? 500 : 250; + try (InputStream is = response.body()) { + Thread.sleep(Utils.adjustTimeout(wait)); + is.readAllBytes(); + } + System.out.printf("%s did not fail: %s%n", query, response.statusCode()); + } catch (AssertionError t1) { + // since we won't pull all responses, the client + // will not exit unless we ask it to shutdown now. + ExecutorService exec = (ExecutorService)client.executor().get(); + exec.shutdownNow(); + throw t1; + } catch (Throwable t0) { + System.out.println("Got EXPECTED: " + t0); + if (t0 instanceof ExecutionException) { + t0 = t0.getCause(); + } + t = t0; + try { + assertDetailMessage(t0, i); + } catch (AssertionError e) { + // since we won't pull all responses, the client + // will not exit unless we ask it to shutdown now. + ExecutorService exec = (ExecutorService)client.executor().get(); + exec.shutdownNow(); + throw e; + } + } + } + } catch (Throwable t0) { + System.out.println("Got EXPECTED: " + t0); + if (t0 instanceof ExecutionException) { + t0 = t0.getCause(); + } + t = t0; + } + if (t == null) { + // we could fail here if we haven't waited long enough + fail("Expected exception, got all responses, should sleep time be raised?"); + } else { + assertDetailMessage(t, max); + } + String query = "reqId=" + reqid.incrementAndGet(); + URI uriWithQuery = URI.create(uri + "?" + query); + CompletableFuture sent = new CompletableFuture<>(); + responseSent.put(query, sent); + HttpRequest request = HttpRequest.newBuilder(uriWithQuery) + .POST(BodyPublishers.ofString("Hello there!")) + .build(); + System.out.println("\nSending last request:" + uriWithQuery); + var response = client.send(request, BodyHandlers.ofString()); + if (label != null) { + String ckey = response.headers().firstValue("X-Connection-Key").get(); + assertNotEquals(ckey, label); + System.out.printf("last request %s sent on different connection as expected:" + + "\n\tlast: %s\n\tprevious: %s%n", query, ckey, label); + } + } finally { + ExecutorService exec = (ExecutorService)client.executor().get(); + exec.shutdownNow(); + } + } + + // Assertions based on implementation specific detail messages. Keep in + // sync with implementation. + static void assertDetailMessage(Throwable throwable, int iterationIndex) { + try { + Throwable cause = throwable; + while (cause != null) { + if (cause instanceof ProtocolException) { + if (cause.getMessage().contains("connection window exceeded")) { + System.out.println("Found expected exception: " + cause); + return; + } + } + cause = cause.getCause(); + } + throw new AssertionError( + "ProtocolException(\"protocol error: connection window exceeded\") not found", + throwable); + } catch (AssertionError e) { + System.out.println("Exception does not match expectation: " + throwable); + throwable.printStackTrace(System.out); + throw e; + } + } + + @BeforeTest + public void setup() throws Exception { + sslContext = new SimpleSSLContext().get(); + if (sslContext == null) + throw new AssertionError("Unexpected null sslContext"); + + var http2TestServer = new Http2TestServer("localhost", false, 0); + http2TestServer.addHandler(new Http2TestHandler(), "/http2/"); + this.http2TestServer = HttpTestServer.of(http2TestServer); + http2URI = "http://" + this.http2TestServer.serverAuthority() + "/http2/x"; + + var https2TestServer = new Http2TestServer("localhost", true, sslContext); + https2TestServer.addHandler(new Http2TestHandler(), "/https2/"); + this.https2TestServer = HttpTestServer.of(https2TestServer); + https2URI = "https://" + this.https2TestServer.serverAuthority() + "/https2/x"; + + // Override the default exchange supplier with a custom one to enable + // particular test scenarios + http2TestServer.setExchangeSupplier(FCHttp2TestExchange::new); + https2TestServer.setExchangeSupplier(FCHttp2TestExchange::new); + + this.http2TestServer.start(); + this.https2TestServer.start(); + } + + @AfterTest + public void teardown() throws Exception { + http2TestServer.stop(); + https2TestServer.stop(); + } + + static class Http2TestHandler implements Http2Handler { + + @Override + public void handle(Http2TestExchange t) throws IOException { + String query = t.getRequestURI().getRawQuery(); + + try (InputStream is = t.getRequestBody(); + OutputStream os = t.getResponseBody()) { + + byte[] bytes = is.readAllBytes(); + System.out.println("Server " + t.getLocalAddress() + " received:\n" + + t.getRequestURI() + ": " + new String(bytes, StandardCharsets.UTF_8)); + t.getResponseHeaders().setHeader("X-Connection-Key", t.getConnectionKey()); + + if (bytes.length == 0) bytes = "no request body!".getBytes(StandardCharsets.UTF_8); + int window = Math.max(16384, Integer.getInteger("jdk.httpclient.windowsize", 2*16*1024)); + final int maxChunkSize; + if (t instanceof FCHttp2TestExchange fct) { + maxChunkSize = Math.min(window, fct.conn.getMaxFrameSize()); + } else { + maxChunkSize = Math.min(window, SettingsFrame.MAX_FRAME_SIZE); + } + byte[] resp = bytes.length < maxChunkSize + ? bytes + : Arrays.copyOfRange(bytes, 0, maxChunkSize); + int max = (window / resp.length); + // send in chunks + t.sendResponseHeaders(200, 0); + int sent = 0; + for (int i=0; i<=max; i++) { + int len = Math.min(resp.length, window - sent); + if (len <= 0) break; + if (os instanceof BodyOutputStream bos) { + try { + // we don't wait for the stream window, but we want + // to wait for the connection window + bos.waitForStreamWindow(len); + } catch (InterruptedException ie) { + // ignore and continue... + } + } + ((BodyOutputStream) os).writeUncontrolled(resp, 0, len); + sent += len; + } + if (sent != window) fail("should have sent %s, sent %s".formatted(window, sent)); + } + if (t instanceof FCHttp2TestExchange fct) { + fct.responseSent(query); + } else { + fail("Exchange is not %s but %s" + .formatted(FCHttp2TestExchange.class.getName(), t.getClass().getName())); + } + } + } + + // A custom Http2TestExchangeImpl that overrides sendResponseHeaders to + // allow headers to be sent with a number of CONTINUATION frames. + static class FCHttp2TestExchange extends Http2TestExchangeImpl { + static volatile Consumer responseSentCB; + static void setResponseSentCB(Consumer responseSentCB) { + FCHttp2TestExchange.responseSentCB = responseSentCB; + } + + final Http2TestServerConnection conn; + FCHttp2TestExchange(int streamid, String method, HttpHeaders reqheaders, + HttpHeadersBuilder rspheadersBuilder, URI uri, InputStream is, + SSLSession sslSession, BodyOutputStream os, + Http2TestServerConnection conn, boolean pushAllowed) { + super(streamid, method, reqheaders, rspheadersBuilder, uri, is, sslSession, os, conn, pushAllowed); + this.conn = conn; + } + public void responseSent(String query) { + System.out.println("Server: response sent for " + query); + responseSentCB.accept(query); + } + + } +} diff --git a/test/jdk/java/net/httpclient/http2/FixedThreadPoolTest.java b/test/jdk/java/net/httpclient/http2/FixedThreadPoolTest.java index cef7bf7df65d4..68cfe826a0ecb 100644 --- a/test/jdk/java/net/httpclient/http2/FixedThreadPoolTest.java +++ b/test/jdk/java/net/httpclient/http2/FixedThreadPoolTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,9 +24,13 @@ /* * @test * @bug 8087112 8177935 - * @library /test/lib /test/jdk/java/net/httpclient/lib - * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.common.TestUtil - * jdk.httpclient.test.lib.http2.Http2TestServer + * @library /test/jdk/java/net/httpclient/lib + * /test/lib + * @build jdk.httpclient.test.lib.http2.Http2TestServer + * jdk.httpclient.test.lib.http2.Http2EchoHandler + * jdk.test.lib.Asserts + * jdk.test.lib.Utils + * jdk.test.lib.net.SimpleSSLContext * @run testng/othervm -Djdk.httpclient.HttpClient.log=ssl,requests,responses,errors FixedThreadPoolTest */ @@ -37,16 +41,23 @@ import javax.net.ssl.*; import java.nio.file.*; import java.util.concurrent.*; -import jdk.httpclient.test.lib.common.TestUtil; import jdk.httpclient.test.lib.http2.Http2TestServer; -import jdk.httpclient.test.lib.http2.Http2TestExchange; import jdk.httpclient.test.lib.http2.Http2EchoHandler; import jdk.test.lib.net.SimpleSSLContext; + import static java.net.http.HttpClient.Version.HTTP_2; +import static jdk.test.lib.Asserts.assertFileContentsEqual; +import static jdk.test.lib.Utils.createTempFile; +import static jdk.test.lib.Utils.createTempFileOfSize; + import org.testng.annotations.Test; @Test public class FixedThreadPoolTest { + + private static final String TEMP_FILE_PREFIX = + HttpClient.class.getPackageName() + '-' + FixedThreadPoolTest.class.getSimpleName() + '-'; + static int httpPort, httpsPort; static Http2TestServer httpServer, httpsServer; static HttpClient client = null; @@ -142,14 +153,6 @@ static void checkStrings(String expected, String found) throws Exception { } } - static Void compareFiles(Path path1, Path path2) { - return TestUtil.compareFiles(path1, path2); - } - - static Path tempFile() { - return TestUtil.tempFile(); - } - static final String SIMPLE_STRING = "Hello world Goodbye world"; static final int LOOPS = 32; @@ -160,7 +163,7 @@ static void streamTest(boolean secure) throws Exception { System.err.printf("streamTest %b to %s\n" , secure, uri); HttpClient client = getClient(); - Path src = TestUtil.getAFile(FILESIZE * 4); + Path src = createTempFileOfSize(TEMP_FILE_PREFIX, null, FILESIZE * 4); HttpRequest req = HttpRequest.newBuilder(uri) .POST(BodyPublishers.ofFile(src)) .build(); @@ -174,7 +177,7 @@ static void streamTest(boolean secure) throws Exception { return resp.body(); }); response.join(); - compareFiles(src, dest); + assertFileContentsEqual(src, dest); System.err.println("DONE"); } @@ -239,17 +242,19 @@ static void simpleTest(boolean secure) throws Exception { // Do loops asynchronously CompletableFuture[] responses = new CompletableFuture[LOOPS]; - final Path source = TestUtil.getAFile(FILESIZE); + final Path source = createTempFileOfSize(TEMP_FILE_PREFIX, null, FILESIZE); HttpRequest request = HttpRequest.newBuilder(uri) .POST(BodyPublishers.ofFile(source)) .build(); for (int i = 0; i < LOOPS; i++) { - responses[i] = client.sendAsync(request, BodyHandlers.ofFile(tempFile())) + responses[i] = client.sendAsync(request, BodyHandlers.ofFile(createTempFile(TEMP_FILE_PREFIX, null))) //.thenApply(resp -> compareFiles(resp.body(), source)); .thenApply(resp -> { + Path body = resp.body(); System.out.printf("Resp status %d body size %d\n", - resp.statusCode(), resp.body().toFile().length()); - return compareFiles(resp.body(), source); + resp.statusCode(), body.toFile().length()); + assertFileContentsEqual(body, source); + return null; }); } CompletableFuture.allOf(responses).join(); diff --git a/test/jdk/java/net/httpclient/http2/H2GoAwayTest.java b/test/jdk/java/net/httpclient/http2/H2GoAwayTest.java new file mode 100644 index 0000000000000..b90d2c46b623c --- /dev/null +++ b/test/jdk/java/net/httpclient/http2/H2GoAwayTest.java @@ -0,0 +1,339 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.IOException; +import java.io.OutputStream; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandlers; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.net.ssl.SSLContext; + +import jdk.httpclient.test.lib.common.HttpServerAdapters.HttpTestExchange; +import jdk.httpclient.test.lib.common.HttpServerAdapters.HttpTestHandler; +import jdk.httpclient.test.lib.common.HttpServerAdapters.HttpTestServer; +import jdk.test.lib.net.SimpleSSLContext; +import jdk.test.lib.net.URIBuilder; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import static java.net.http.HttpClient.Version.HTTP_2; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; + +/* + * @test + * @bug 8335181 + * @summary verify that the HttpClient correctly handles incoming GOAWAY frames and + * retries any unprocessed requests on a new connection + * @library /test/lib /test/jdk/java/net/httpclient/lib + * @build jdk.httpclient.test.lib.common.HttpServerAdapters + * jdk.test.lib.net.SimpleSSLContext + * @run junit/othervm H2GoAwayTest + */ +public class H2GoAwayTest { + private static final String REQ_PATH = "/test"; + private static HttpTestServer server; + private static String REQ_URI_BASE; + private static SSLContext sslCtx; + + @BeforeAll + static void beforeAll() throws Exception { + sslCtx = new SimpleSSLContext().get(); + assertNotNull(sslCtx, "SSLContext couldn't be created"); + server = HttpTestServer.create(HTTP_2, sslCtx); + server.addHandler(new Handler(), REQ_PATH); + server.start(); + System.out.println("Server started at " + server.getAddress()); + REQ_URI_BASE = URIBuilder.newBuilder().scheme("https") + .loopback() + .port(server.getAddress().getPort()) + .path(REQ_PATH) + .build().toString(); + } + + @AfterAll + static void afterAll() { + if (server != null) { + System.out.println("Stopping server at " + server.getAddress()); + server.stop(); + } + } + + /** + * Verifies that when several requests are sent using send() and the server + * connection is configured to send a GOAWAY after processing only a few requests, then + * the remaining requests are retried on a different connection + */ + @Test + public void testSequential() throws Exception { + final LimitedPerConnRequestApprover reqApprover = new LimitedPerConnRequestApprover(); + server.setRequestApprover(reqApprover::allowNewRequest); + final HttpClient client = HttpClient.newBuilder().version(HTTP_2) + .sslContext(sslCtx).build(); + try { + final String[] reqMethods = {"HEAD", "GET", "POST"}; + for (final String reqMethod : reqMethods) { + final int numReqs = LimitedPerConnRequestApprover.MAX_REQS_PER_CONN + 3; + final Set connectionKeys = new LinkedHashSet<>(); + for (int i = 1; i <= numReqs; i++) { + final URI reqURI = new URI(REQ_URI_BASE + "?seq&" + reqMethod + "=" + i); + final HttpRequest req = HttpRequest.newBuilder() + .uri(reqURI) + .method(reqMethod, HttpRequest.BodyPublishers.noBody()) + .build(); + System.out.println("initiating request " + req); + final HttpResponse resp = client.send(req, BodyHandlers.ofString()); + final String respBody = resp.body(); + System.out.println("received response: " + respBody); + assertEquals(200, resp.statusCode(), + "unexpected status code for request " + resp.request()); + // response body is the logical key of the connection on which the + // request was handled + connectionKeys.add(respBody); + } + System.out.println("connections involved in handling the requests: " + + connectionKeys); + // all requests have finished, we now just do a basic check that + // more than one connection was involved in processing these requests + assertEquals(2, connectionKeys.size(), + "unexpected number of connections " + connectionKeys); + } + } finally { + server.setRequestApprover(null); // reset + } + } + + /** + * Verifies that when a server responds with a GOAWAY and then never processes the new retried + * requests on a new connection too, then the application code receives the request failure. + * This tests the send() API of the HttpClient. + */ + @Test + public void testUnprocessedRaisesException() throws Exception { + final HttpClient client = HttpClient.newBuilder().version(HTTP_2) + .sslContext(sslCtx).build(); + { + final Random random = new Random(); + final String[] reqMethods = {"HEAD", "GET", "POST"}; + for (final String reqMethod : reqMethods) { + final int maxAllowedReqs = 2; + final int numReqs = maxAllowedReqs + 3; // 3 more requests than max allowed + // configure the approver + final LimitedRequestApprover reqApprover = new LimitedRequestApprover(maxAllowedReqs); + server.setRequestApprover(reqApprover::allowNewRequest); + try { + int numSuccess = 0; + int numFailed = 0; + for (int i = 1; i <= numReqs; i++) { + final String reqQueryPart = "?sync&" + reqMethod + "=" + i; + final URI reqURI = new URI(REQ_URI_BASE + reqQueryPart); + final HttpRequest req = HttpRequest.newBuilder() + .uri(reqURI) + .method(reqMethod, HttpRequest.BodyPublishers.noBody()) + .build(); + System.out.println("initiating request " + req); + if (i <= maxAllowedReqs) { + // expected to successfully complete + numSuccess++; + final HttpResponse resp = client.send(req, BodyHandlers.ofString()); + final String respBody = resp.body(); + System.out.println("received response: " + respBody); + assertEquals(200, resp.statusCode(), + "unexpected status code for request " + resp.request()); + } else { + // expected to fail as unprocessed + try { + final HttpResponse resp = client.send(req, BodyHandlers.ofString()); + fail("Request was expected to fail as unprocessed," + + " but got response: " + resp.body() + ", status code: " + + resp.statusCode()); + } catch (IOException ioe) { + // verify it failed for the right reason + if (ioe.getMessage() == null + || !ioe.getMessage().contains("request not processed by peer")) { + // propagate the original failure + throw ioe; + } + numFailed++; // failed due to right reason + System.out.println("received expected failure: " + ioe + + ", for request " + reqURI); + } + } + } + // verify the correct number of requests succeeded/failed + assertEquals(maxAllowedReqs, numSuccess, "unexpected number of requests succeeded"); + assertEquals((numReqs - maxAllowedReqs), numFailed, "unexpected number of requests failed"); + } finally { + server.setRequestApprover(null); // reset + } + } + } + } + + /** + * Verifies that when a server responds with a GOAWAY and then never processes the new retried + * requests on a new connection too, then the application code receives the request failure. + * This tests the sendAsync() API of the HttpClient. + */ + @Test + public void testUnprocessedRaisesExceptionAsync() throws Throwable { + final HttpClient client = HttpClient.newBuilder().version(HTTP_2) + .sslContext(sslCtx).build(); + { + final Random random = new Random(); + final String[] reqMethods = {"HEAD", "GET", "POST"}; + for (final String reqMethod : reqMethods) { + final int maxAllowedReqs = 2; + final int numReqs = maxAllowedReqs + 3; // 3 more requests than max allowed + // configure the approver + final LimitedRequestApprover reqApprover = new LimitedRequestApprover(maxAllowedReqs); + server.setRequestApprover(reqApprover::allowNewRequest); + try { + final List>> futures = new ArrayList<>(); + for (int i = 1; i <= numReqs; i++) { + final URI reqURI = new URI(REQ_URI_BASE + "?async&" + reqMethod + "=" + i); + final HttpRequest req = HttpRequest.newBuilder() + .uri(reqURI) + .method(reqMethod, HttpRequest.BodyPublishers.noBody()) + .build(); + System.out.println("initiating request " + req); + final Future> f = client.sendAsync(req, BodyHandlers.ofString()); + futures.add(f); + } + // wait for responses + int numFailed = 0; + int numSuccess = 0; + for (int i = 1; i <= numReqs; i++) { + final String reqQueryPart = "?async&" + reqMethod + "=" + i; + try { + System.out.println("waiting response of request " + + REQ_URI_BASE + reqQueryPart); + final HttpResponse resp = futures.get(i - 1).get(); + numSuccess++; + final String respBody = resp.body(); + System.out.println("request: " + resp.request() + + ", received response: " + respBody); + assertEquals(200, resp.statusCode(), + "unexpected status code for request " + resp.request()); + } catch (ExecutionException ee) { + final Throwable cause = ee.getCause(); + if (!(cause instanceof IOException ioe)) { + throw cause; + } + // verify it failed for the right reason + if (ioe.getMessage() == null + || !ioe.getMessage().contains("request not processed by peer")) { + // propagate the original failure + throw ioe; + } + numFailed++; // failed due to the right reason + System.out.println("received expected failure: " + ioe + + ", for request " + REQ_URI_BASE + reqQueryPart); + } + } + // verify the correct number of requests succeeded/failed + assertEquals(maxAllowedReqs, numSuccess, "unexpected number of requests succeeded"); + assertEquals((numReqs - maxAllowedReqs), numFailed, "unexpected number of requests failed"); + } finally { + server.setRequestApprover(null); // reset + } + } + } + } + + // only allows fixed number of requests, irrespective of which server connection handles + // it. requests that are rejected will either be sent a GOAWAY on the connection + // or a RST_FRAME with a REFUSED_STREAM on the stream + private static final class LimitedRequestApprover { + private final int maxAllowedReqs; + private final AtomicInteger numApproved = new AtomicInteger(); + + private LimitedRequestApprover(final int maxAllowedReqs) { + this.maxAllowedReqs = maxAllowedReqs; + } + + public boolean allowNewRequest(final String serverConnKey) { + final int approved = numApproved.incrementAndGet(); + return approved <= maxAllowedReqs; + } + } + + // allows a certain number of requests per server connection. + // requests that are rejected will either be sent a GOAWAY on the connection + // or a RST_FRAME with a REFUSED_STREAM on the stream + private static final class LimitedPerConnRequestApprover { + private static final int MAX_REQS_PER_CONN = 6; + private final Map numApproved = + new ConcurrentHashMap<>(); + private final Map numDisapproved = + new ConcurrentHashMap<>(); + + public boolean allowNewRequest(final String serverConnKey) { + final AtomicInteger approved = numApproved.computeIfAbsent(serverConnKey, + (k) -> new AtomicInteger()); + int curr = approved.get(); + while (curr < MAX_REQS_PER_CONN) { + if (approved.compareAndSet(curr, curr + 1)) { + return true; // new request allowed + } + curr = approved.get(); + } + final AtomicInteger disapproved = numDisapproved.computeIfAbsent(serverConnKey, + (k) -> new AtomicInteger()); + final int numUnprocessed = disapproved.incrementAndGet(); + System.out.println(approved.get() + " processed, " + + numUnprocessed + " unprocessed requests on connection " + serverConnKey); + return false; + } + } + + private static final class Handler implements HttpTestHandler { + + @Override + public void handle(final HttpTestExchange exchange) throws IOException { + final String connectionKey = exchange.getConnectionKey(); + System.out.println("responding to request: " + exchange.getRequestURI() + + " on connection " + connectionKey); + final byte[] response = connectionKey.getBytes(UTF_8); + exchange.sendResponseHeaders(200, response.length); + try (final OutputStream os = exchange.getResponseBody()) { + os.write(response); + } + } + } +} diff --git a/test/jdk/java/net/httpclient/http2/NoBodyTest.java b/test/jdk/java/net/httpclient/http2/NoBodyTest.java index 2dc7508f5ee70..4dcd99e2c87ac 100644 --- a/test/jdk/java/net/httpclient/http2/NoBodyTest.java +++ b/test/jdk/java/net/httpclient/http2/NoBodyTest.java @@ -26,7 +26,9 @@ * @bug 8087112 * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.http2.Http2TestServer - * @run testng/othervm -Djdk.httpclient.HttpClient.log=ssl,requests,responses,errors NoBodyTest + * @run testng/othervm -Djdk.httpclient.HttpClient.log=ssl,requests,responses,errors + * -Djdk.internal.httpclient.debug=true + * NoBodyTest */ import java.io.IOException; diff --git a/test/jdk/java/net/httpclient/http2/PostPutTest.java b/test/jdk/java/net/httpclient/http2/PostPutTest.java new file mode 100644 index 0000000000000..89b192f6171ad --- /dev/null +++ b/test/jdk/java/net/httpclient/http2/PostPutTest.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8293786 + * @summary Checks to see if the HttpClient can process a request to cancel a transmission from a remote if the server + * does not process any data. The client should read all data from the server and close the connection. + * @library /test/jdk/java/net/httpclient/lib + * @build jdk.httpclient.test.lib.http2.Http2TestServer + * @run testng/othervm/timeout=50 -Djdk.httpclient.HttpClient.log=all + * PostPutTest + */ + +import jdk.httpclient.test.lib.http2.Http2Handler; +import jdk.httpclient.test.lib.http2.Http2TestExchange; +import jdk.httpclient.test.lib.http2.Http2TestServer; + +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.io.PrintStream; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; + +import static java.net.http.HttpClient.Version.HTTP_2; +import static java.net.http.HttpRequest.BodyPublishers.ofByteArray; + +public class PostPutTest { + + Http2TestServer http2TestServer; + URI warmupURI, testHandlerBasicURI, testHandlerCloseBosURI, testHandleNegativeContentLengthURI; + static PrintStream testLog = System.err; + + // As per jdk.internal.net.http.WindowController.DEFAULT_INITIAL_WINDOW_SIZE + final int DEFAULT_INITIAL_WINDOW_SIZE = (64 * 1024) - 1; + // Add on a small amount of arbitrary bytes to see if client hangs when receiving RST_STREAM + byte[] data = new byte[DEFAULT_INITIAL_WINDOW_SIZE + 10]; + + @BeforeTest + public void setup() throws Exception { + http2TestServer = new Http2TestServer(false, 0); + http2TestServer.addHandler(new WarmupHandler(), "/Warmup"); + http2TestServer.addHandler(new TestHandlerBasic(), "/TestHandlerBasic"); + http2TestServer.addHandler(new TestHandlerCloseBos(), "/TestHandlerCloseBos"); + http2TestServer.addHandler(new TestHandleNegativeContentLength(), "/TestHandleNegativeContentLength"); + http2TestServer.start(); + testLog.println("PostPutTest.setup(): Starting server"); + warmupURI = new URI("http://" + http2TestServer.serverAuthority() + "/Warmup"); + testHandlerBasicURI = new URI("http://" + http2TestServer.serverAuthority() + "/TestHandlerBasic"); + testHandlerCloseBosURI = new URI("http://" + http2TestServer.serverAuthority() + "/TestHandlerCloseBos"); + testHandleNegativeContentLengthURI = new URI("http://" + http2TestServer.serverAuthority() + "/TestHandleNegativeContentLength"); + testLog.println("PostPutTest.setup(): warmupURI: " + warmupURI); + testLog.println("PostPutTest.setup(): testHandlerBasicURI: " + testHandlerBasicURI); + testLog.println("PostPutTest.setup(): testHandlerCloseBosURI: " + testHandlerCloseBosURI); + testLog.println("PostPutTest.setup(): testHandleNegativeContentLengthURI: " + testHandleNegativeContentLengthURI); + } + + @AfterTest + public void teardown() { + testLog.println("PostPutTest.teardown(): Stopping server"); + http2TestServer.stop(); + data = null; + } + + @DataProvider(name = "variants") + public Object[][] variants() { + HttpRequest over64kPost, over64kPut, over64kPostCloseBos, over64kPutCloseBos, over64kPostNegativeContentLength, over64kPutNegativeContentLength; + over64kPost = HttpRequest.newBuilder().version(HTTP_2).POST(ofByteArray(data)).uri(testHandlerBasicURI).build(); + over64kPut = HttpRequest.newBuilder().version(HTTP_2).PUT(ofByteArray(data)).uri(testHandlerBasicURI).build(); + + over64kPostCloseBos = HttpRequest.newBuilder().version(HTTP_2).POST(ofByteArray(data)).uri(testHandlerCloseBosURI).build(); + over64kPutCloseBos = HttpRequest.newBuilder().version(HTTP_2).PUT(ofByteArray(data)).uri(testHandlerCloseBosURI).build(); + + over64kPostNegativeContentLength = HttpRequest.newBuilder().version(HTTP_2).POST(ofByteArray(data)).uri(testHandleNegativeContentLengthURI).build(); + over64kPutNegativeContentLength = HttpRequest.newBuilder().version(HTTP_2).PUT(ofByteArray(data)).uri(testHandleNegativeContentLengthURI).build(); + + return new Object[][] { + { over64kPost, "POST data over 64k bytes" }, + { over64kPut, "PUT data over 64k bytes" }, + { over64kPostCloseBos, "POST data over 64k bytes with close bos" }, + { over64kPutCloseBos, "PUT data over 64k bytes with close bos" }, + { over64kPostNegativeContentLength, "POST data over 64k bytes with negative content length" }, + { over64kPutNegativeContentLength, "PUT data over 64k bytes with negative content length" } + }; + } + + public HttpRequest getWarmupReq() { + return HttpRequest.newBuilder() + .GET() + .uri(warmupURI) + .build(); + } + + @Test(dataProvider = "variants") + public void testOver64kPUT(HttpRequest req, String testMessage) { + testLog.println("PostPutTest: Performing test: " + testMessage); + HttpClient hc = HttpClient.newBuilder().version(HTTP_2).build(); + hc.sendAsync(getWarmupReq(), HttpResponse.BodyHandlers.ofString()).join(); + hc.sendAsync(req, HttpResponse.BodyHandlers.ofString()).join(); + /* + If this test fails in timeout, it is likely due to one of two reasons: + - The responseSubscriber is null, so no incoming frames are being processed by the client + (See Stream::schedule) + - The test server is for some reason not sending a RST_STREAM with the NO_ERROR flag set after + sending an empty DATA frame with the END_STREAM flag set. + */ + } + + private static class TestHandlerBasic implements Http2Handler { + + @Override + public void handle(Http2TestExchange exchange) throws IOException { + // The input stream is not read in this bug as this will trigger window updates for the server. This bug + // concerns the case where no updates are sent and the server instead tells the client to abort the transmission. + exchange.sendResponseHeaders(200, 0); + } + } + + private static class TestHandlerCloseBos implements Http2Handler { + + @Override + public void handle(Http2TestExchange exchange) throws IOException { + // This case does actually cause the test to hang due to the body input stream being closed before it can send + // the RST_STREAM frame. + exchange.sendResponseHeaders(200, 0); + exchange.getResponseBody().close(); + } + } + + private static class TestHandleNegativeContentLength implements Http2Handler { + + @Override + public void handle(Http2TestExchange exchange) throws IOException { + exchange.sendResponseHeaders(200, -1); + } + } + + private static class WarmupHandler implements Http2Handler { + + @Override + public void handle(Http2TestExchange exchange) throws IOException { + exchange.sendResponseHeaders(200, 0); + } + } +} \ No newline at end of file diff --git a/test/jdk/java/net/httpclient/http2/ServerPush.java b/test/jdk/java/net/httpclient/http2/ServerPush.java index 60958dce6967b..7f9c82fb28b22 100644 --- a/test/jdk/java/net/httpclient/http2/ServerPush.java +++ b/test/jdk/java/net/httpclient/http2/ServerPush.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,9 +24,11 @@ /* * @test * @bug 8087112 8159814 - * @library /test/lib /test/jdk/java/net/httpclient/lib - * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.http2.Http2TestServer - * jdk.httpclient.test.lib.common.TestUtil jdk.httpclient.test.lib.http2.PushHandler + * @library /test/jdk/java/net/httpclient/lib + * /test/lib + * @build jdk.httpclient.test.lib.http2.Http2TestServer + * jdk.httpclient.test.lib.http2.PushHandler + * jdk.test.lib.Utils * @run testng/othervm * -Djdk.httpclient.HttpClient.log=errors,requests,responses * ServerPush @@ -44,19 +46,21 @@ import java.util.*; import java.util.concurrent.*; import java.util.function.Consumer; -import jdk.httpclient.test.lib.common.TestUtil; import jdk.httpclient.test.lib.http2.Http2TestServer; -import jdk.httpclient.test.lib.http2.Http2TestExchange; -import jdk.httpclient.test.lib.http2.Http2Handler; import jdk.httpclient.test.lib.http2.PushHandler; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; + import static java.nio.charset.StandardCharsets.UTF_8; +import static jdk.test.lib.Utils.createTempFileOfSize; import static org.testng.Assert.*; public class ServerPush { + private static final String TEMP_FILE_PREFIX = + HttpClient.class.getPackageName() + '-' + ServerPush.class.getSimpleName() + '-'; + static final int LOOPS = 13; static final int FILE_SIZE = 512 * 1024 + 343; @@ -67,7 +71,7 @@ public class ServerPush { @BeforeTest public void setup() throws Exception { - tempFile = TestUtil.getAFile(FILE_SIZE); + tempFile = createTempFileOfSize(TEMP_FILE_PREFIX, null, FILE_SIZE); server = new Http2TestServer(false, 0); server.addHandler(new PushHandler(tempFile, LOOPS), "/"); System.out.println("Using temp file:" + tempFile); diff --git a/test/jdk/java/net/httpclient/http2/StreamFlowControlTest.java b/test/jdk/java/net/httpclient/http2/StreamFlowControlTest.java new file mode 100644 index 0000000000000..06a50ed6f722d --- /dev/null +++ b/test/jdk/java/net/httpclient/http2/StreamFlowControlTest.java @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8342075 8343855 + * @library /test/lib /test/jdk/java/net/httpclient/lib + * @build jdk.httpclient.test.lib.http2.Http2TestServer jdk.test.lib.net.SimpleSSLContext + * jdk.httpclient.test.lib.common.TestServerConfigurator + * @run testng/othervm -Djdk.internal.httpclient.debug=true + * -Djdk.httpclient.connectionWindowSize=65535 + * -Djdk.httpclient.windowsize=16384 + * StreamFlowControlTest + */ + +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.net.ProtocolException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpHeaders; +import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublishers; +import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandlers; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; + +import jdk.httpclient.test.lib.common.HttpServerAdapters.HttpHeadOrGetHandler; +import jdk.httpclient.test.lib.common.HttpServerAdapters.HttpTestServer; +import jdk.httpclient.test.lib.http2.BodyOutputStream; +import jdk.httpclient.test.lib.http2.Http2Handler; +import jdk.httpclient.test.lib.http2.Http2TestExchange; +import jdk.httpclient.test.lib.http2.Http2TestExchangeImpl; +import jdk.httpclient.test.lib.http2.Http2TestServer; +import jdk.httpclient.test.lib.http2.Http2TestServerConnection; +import jdk.internal.net.http.common.HttpHeadersBuilder; +import jdk.internal.net.http.frame.SettingsFrame; +import jdk.test.lib.Utils; +import jdk.test.lib.net.SimpleSSLContext; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + +public class StreamFlowControlTest { + + SSLContext sslContext; + HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + String http2URI; + String https2URI; + final AtomicInteger reqid = new AtomicInteger(); + + + @DataProvider(name = "variants") + public Object[][] variants() { + return new Object[][] { + { http2URI, false }, + { https2URI, false }, + { http2URI, true }, + { https2URI, true }, + }; + } + + static void sleep(long wait) throws InterruptedException { + if (wait <= 0) return; + long remaining = Utils.adjustTimeout(wait); + long start = System.nanoTime(); + while (remaining > 0) { + Thread.sleep(remaining); + long end = System.nanoTime(); + remaining = remaining - NANOSECONDS.toMillis(end - start); + } + System.out.printf("Waited %s ms%n", + NANOSECONDS.toMillis(System.nanoTime() - start)); + } + + + @Test(dataProvider = "variants") + void test(String uri, + boolean sameClient) + throws Exception + { + System.out.printf("%ntesting test(%s, %s)%n", uri, sameClient); + ConcurrentHashMap> responseSent = new ConcurrentHashMap<>(); + FCHttp2TestExchange.setResponseSentCB((s) -> responseSent.get(s).complete(s)); + + HttpClient client = null; + try { + int max = sameClient ? 10 : 3; + String label = null; + for (int i = 0; i < max; i++) { + if (!sameClient || client == null) { + client = HttpClient.newBuilder() + .executor(Executors.newCachedThreadPool()) + .sslContext(sslContext).build(); + } + + String query = "reqId=" + reqid.incrementAndGet(); + URI uriWithQuery = URI.create(uri + "?" + query); + CompletableFuture sent = new CompletableFuture<>(); + responseSent.put(query, sent); + HttpRequest request = HttpRequest.newBuilder(uriWithQuery) + .GET() + .build(); + System.out.println("\nSending request:" + uriWithQuery); + final HttpClient cc = client; + try { + HttpResponse response = cc.send(request, BodyHandlers.ofInputStream()); + if (sameClient) { + String key = response.headers().firstValue("X-Connection-Key").get(); + if (label == null) label = key; + assertEquals(key, label, "Unexpected key for " + query); + } + sent.join(); + // we have to pull to get the exception, but slow enough + // so that DataFrames are buffered up to the point that + // the window is exceeded... + long wait = uri.startsWith("https://") ? 800 : 350; + try (InputStream is = response.body()) { + sleep(wait); + is.readAllBytes(); + } + // we could fail here if we haven't waited long enough + fail("Expected exception, got :" + response + ", should sleep time be raised?"); + } catch (IOException ioe) { + System.out.println("Got EXPECTED: " + ioe); + assertDetailMessage(ioe, i); + } finally { + if (!sameClient && client != null) { + ExecutorService exec = (ExecutorService)client.executor().get(); + exec.shutdown(); + client = null; + } + } + } + } finally { + if (sameClient && client != null) { + ExecutorService exec = (ExecutorService)client.executor().get(); + exec.shutdown(); + } + } + + } + + @Test(dataProvider = "variants") + void testAsync(String uri, + boolean sameClient) + { + System.out.printf("%ntesting testAsync(%s, %s)%n", uri, sameClient); + ConcurrentHashMap> responseSent = new ConcurrentHashMap<>(); + FCHttp2TestExchange.setResponseSentCB((s) -> responseSent.get(s).complete(s)); + + HttpClient client = null; + try { + int max = sameClient ? 5 : 3; + String label = null; + for (int i = 0; i < max; i++) { + if (!sameClient || client == null) { + client = HttpClient.newBuilder() + .executor(Executors.newCachedThreadPool()) + .sslContext(sslContext).build(); + } + + String query = "reqId=" + reqid.incrementAndGet(); + URI uriWithQuery = URI.create(uri + "?" + query); + CompletableFuture sent = new CompletableFuture<>(); + responseSent.put(query, sent); + HttpRequest request = HttpRequest.newBuilder(uriWithQuery) + .GET() + .build(); + System.out.println("\nSending request:" + uriWithQuery); + final HttpClient cc = client; + + Throwable t = null; + try { + HttpResponse response = cc.sendAsync(request, BodyHandlers.ofInputStream()).get(); + if (sameClient) { + String key = response.headers().firstValue("X-Connection-Key").get(); + if (label == null) label = key; + assertEquals(key, label, "Unexpected key for " + query); + } + sent.join(); + long wait = uri.startsWith("https://") ? 800 : 350; + try (InputStream is = response.body()) { + sleep(wait); + is.readAllBytes(); + } + // we could fail here if we haven't waited long enough + fail("Expected exception, got :" + response + ", should sleep time be raised?"); + } catch (Throwable t0) { + System.out.println("Got EXPECTED: " + t0); + if (t0 instanceof ExecutionException) { + t0 = t0.getCause(); + } + t = t0; + } finally { + if (!sameClient && client != null) { + ExecutorService exec = (ExecutorService)client.executor().get(); + exec.shutdown(); + client = null; + } + } + assertDetailMessage(t, i); + } + } finally { + if (sameClient && client != null) { + ExecutorService exec = (ExecutorService)client.executor().get(); + exec.shutdown(); + } + } + } + + // Assertions based on implementation specific detail messages. Keep in + // sync with implementation. + static void assertDetailMessage(Throwable throwable, int iterationIndex) { + try { + Throwable cause = throwable; + while (cause != null) { + if (cause instanceof ProtocolException) { + if (cause.getMessage().matches("stream [0-9]+ flow control window exceeded")) { + System.out.println("Found expected exception: " + cause); + return; + } + } + cause = cause.getCause(); + } + throw new AssertionError( + "ProtocolException(\"stream X flow control window exceeded\") not found", + throwable); + } catch (AssertionError e) { + System.out.println("Exception does not match expectation: " + throwable); + throwable.printStackTrace(System.out); + throw e; + } + } + + @BeforeTest + public void setup() throws Exception { + sslContext = new SimpleSSLContext().get(); + if (sslContext == null) + throw new AssertionError("Unexpected null sslContext"); + + var http2TestServer = new Http2TestServer("localhost", false, 0); + http2TestServer.addHandler(new Http2TestHandler(), "/http2/"); + this.http2TestServer = HttpTestServer.of(http2TestServer); + http2URI = "http://" + this.http2TestServer.serverAuthority() + "/http2/x"; + + var https2TestServer = new Http2TestServer("localhost", true, sslContext); + https2TestServer.addHandler(new Http2TestHandler(), "/https2/"); + this.https2TestServer = HttpTestServer.of(https2TestServer); + this.https2TestServer.addHandler(new HttpHeadOrGetHandler(), "/https2/head/"); + https2URI = "https://" + this.https2TestServer.serverAuthority() + "/https2/x"; + String h2Head = "https://" + this.https2TestServer.serverAuthority() + "/https2/head/z"; + + // Override the default exchange supplier with a custom one to enable + // particular test scenarios + http2TestServer.setExchangeSupplier(FCHttp2TestExchange::new); + https2TestServer.setExchangeSupplier(FCHttp2TestExchange::new); + + this.http2TestServer.start(); + this.https2TestServer.start(); + + // warmup to eliminate delay due to SSL class loading and initialization. + HttpClient client = HttpClient.newBuilder().executor(Executors.newCachedThreadPool()).sslContext(sslContext).build(); + try { + var request = HttpRequest.newBuilder(URI.create(h2Head)).method("HEAD", BodyPublishers.noBody()).build(); + var resp = client.send(request, BodyHandlers.discarding()); + assertEquals(resp.statusCode(), 200); + } finally { + ExecutorService exec = (ExecutorService)client.executor().get(); + exec.shutdownNow(); + } + } + + @AfterTest + public void teardown() throws Exception { + http2TestServer.stop(); + https2TestServer.stop(); + } + + static class Http2TestHandler implements Http2Handler { + + @Override + public void handle(Http2TestExchange t) throws IOException { + String query = t.getRequestURI().getRawQuery(); + + try (InputStream is = t.getRequestBody(); + OutputStream os = t.getResponseBody()) { + + byte[] bytes = is.readAllBytes(); + if (bytes.length != 0) { + System.out.println("Server " + t.getLocalAddress() + " received:\n" + + t.getRequestURI() + ": " + new String(bytes, StandardCharsets.UTF_8)); + } else { + System.out.println("No request body for " + t.getRequestMethod()); + } + + t.getResponseHeaders().setHeader("X-Connection-Key", t.getConnectionKey()); + + if (bytes.length == 0) { + bytes = "no request body!" + .repeat(100).getBytes(StandardCharsets.UTF_8); + } + int window = Integer.getInteger("jdk.httpclient.windowsize", 2 * 16 * 1024); + final int maxChunkSize; + if (t instanceof FCHttp2TestExchange fct) { + maxChunkSize = Math.min(window, fct.conn.getMaxFrameSize()); + } else { + maxChunkSize = Math.min(window, SettingsFrame.MAX_FRAME_SIZE); + } + byte[] resp = bytes.length <= maxChunkSize + ? bytes + : Arrays.copyOfRange(bytes, 0, maxChunkSize); + int max = (window / resp.length) + 2; + // send in chunks + t.sendResponseHeaders(200, 0); + for (int i = 0; i <= max; i++) { + if (t instanceof FCHttp2TestExchange fct) { + try { + // we don't wait for the stream window, but we want + // to wait for the connection window + fct.conn.obtainConnectionWindow(resp.length); + } catch (InterruptedException ie) { + var ioe = new InterruptedIOException(ie.toString()); + ioe.initCause(ie); + throw ioe; + } + } + try { + ((BodyOutputStream) os).writeUncontrolled(resp, 0, resp.length); + } catch (IOException x) { + if (t instanceof FCHttp2TestExchange fct) { + fct.conn.updateConnectionWindow(resp.length); + } + throw x; + } + } + } finally { + if (t instanceof FCHttp2TestExchange fct) { + fct.responseSent(query); + } else { + fail("Exchange is not %s but %s" + .formatted(FCHttp2TestExchange.class.getName(), t.getClass().getName())); + } + } + } + } + + // A custom Http2TestExchangeImpl that overrides sendResponseHeaders to + // allow headers to be sent with a number of CONTINUATION frames. + static class FCHttp2TestExchange extends Http2TestExchangeImpl { + static volatile Consumer responseSentCB; + static void setResponseSentCB(Consumer responseSentCB) { + FCHttp2TestExchange.responseSentCB = responseSentCB; + } + + final Http2TestServerConnection conn; + FCHttp2TestExchange(int streamid, String method, HttpHeaders reqheaders, + HttpHeadersBuilder rspheadersBuilder, URI uri, InputStream is, + SSLSession sslSession, BodyOutputStream os, + Http2TestServerConnection conn, boolean pushAllowed) { + super(streamid, method, reqheaders, rspheadersBuilder, uri, is, sslSession, os, conn, pushAllowed); + this.conn = conn; + } + public void responseSent(String query) { + System.out.println("Server: response sent for " + query); + responseSentCB.accept(query); + } + + } +} diff --git a/test/jdk/java/net/httpclient/http2/UserInfoTest.java b/test/jdk/java/net/httpclient/http2/UserInfoTest.java new file mode 100644 index 0000000000000..2d75a55835220 --- /dev/null +++ b/test/jdk/java/net/httpclient/http2/UserInfoTest.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.test.lib.net.SimpleSSLContext; +import jdk.test.lib.net.URIBuilder; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import jdk.httpclient.test.lib.http2.Http2TestServer; +import jdk.httpclient.test.lib.http2.Http2TestExchange; +import jdk.httpclient.test.lib.http2.Http2Handler; + + +import static org.junit.jupiter.api.Assertions.assertEquals; + + +/** + * @test + * @bug 8292876 + * @library /test/lib /test/jdk/java/net/httpclient/lib + * @build jdk.httpclient.test.lib.http2.Http2TestServer jdk.test.lib.net.SimpleSSLContext + * @run junit/othervm UserInfoTest + */ + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class UserInfoTest { + + Http2TestServer server; + int port; + SSLContext sslContext; + + @BeforeAll + void before() throws Exception { + sslContext = new SimpleSSLContext().get(); + server = createServer(sslContext); + port = server.getAddress().getPort(); + server.start(); + } + + @AfterAll + void after() throws Exception { + server.close(); + } + + static class Http2TestHandler implements Http2Handler { + @Override + public void handle(Http2TestExchange e) throws IOException { + String authorityHeader = e.getRequestHeaders().firstValue(":authority").orElse(null); + if (authorityHeader == null || authorityHeader.contains("user@")) { + e.sendResponseHeaders(500, -1); + } else { + e.sendResponseHeaders(200, -1); + } + } + } + + private static Http2TestServer createServer(SSLContext sslContext) throws Exception { + Http2TestServer http2TestServer = new Http2TestServer("localhost", true, sslContext); + Http2TestHandler handler = new Http2TestHandler(); + http2TestServer.addHandler(handler, "/"); + return http2TestServer; + } + + @Test + public void testAuthorityHeader() throws Exception { + HttpClient client = HttpClient + .newBuilder() + .proxy(HttpClient.Builder.NO_PROXY) + .sslContext(sslContext) + .build(); + + URI uri = URIBuilder.newBuilder() + .scheme("https") + .userInfo("user") + .loopback() + .port(port) + .build(); + + HttpRequest request = HttpRequest + .newBuilder(uri) + .GET() + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + assertEquals(200, response.statusCode(), "Test Failed : " + response.uri().getAuthority()); + } +} diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/common/HttpServerAdapters.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/common/HttpServerAdapters.java index 98da7d5533f33..62dd06ade66ca 100644 --- a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/common/HttpServerAdapters.java +++ b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/common/HttpServerAdapters.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -49,6 +49,7 @@ import java.net.InetSocketAddress; import java.net.URI; import java.net.http.HttpHeaders; +import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.List; import java.util.ListIterator; @@ -58,6 +59,7 @@ import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutorService; +import java.util.function.Predicate; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -65,6 +67,9 @@ import javax.net.ssl.SSLContext; +import static java.net.http.HttpClient.Version.HTTP_1_1; +import static java.net.http.HttpClient.Version.HTTP_2; + /** * Defines an adaptation layers so that a test server handlers and filters * can be implemented independently of the underlying server version. @@ -234,12 +239,13 @@ public static abstract class HttpTestExchange implements AutoCloseable { public abstract OutputStream getResponseBody(); public abstract HttpTestRequestHeaders getRequestHeaders(); public abstract HttpTestResponseHeaders getResponseHeaders(); - public abstract void sendResponseHeaders(int code, int contentLength) throws IOException; + public abstract void sendResponseHeaders(int code, long contentLength) throws IOException; public abstract URI getRequestURI(); public abstract String getRequestMethod(); public abstract void close(); public abstract InetSocketAddress getRemoteAddress(); public abstract InetSocketAddress getLocalAddress(); + public abstract String getConnectionKey(); public void serverPush(URI uri, HttpHeaders headers, byte[] body) { ByteArrayInputStream bais = new ByteArrayInputStream(body); serverPush(uri, headers, bais); @@ -254,7 +260,7 @@ public static HttpTestExchange of(HttpExchange exchange) { return new Http1TestExchange(exchange); } public static HttpTestExchange of(Http2TestExchange exchange) { - return new Http2TestExchangeImpl(exchange); + return new H2ExchangeImpl(exchange); } abstract void doFilter(Filter.Chain chain) throws IOException; @@ -266,9 +272,9 @@ private static final class Http1TestExchange extends HttpTestExchange { this.exchange = exch; } @Override - public Version getServerVersion() { return Version.HTTP_1_1; } + public Version getServerVersion() { return HTTP_1_1; } @Override - public Version getExchangeVersion() { return Version.HTTP_1_1; } + public Version getExchangeVersion() { return HTTP_1_1; } @Override public InputStream getRequestBody() { return exchange.getRequestBody(); @@ -286,7 +292,7 @@ public HttpTestResponseHeaders getResponseHeaders() { return HttpTestResponseHeaders.of(exchange.getResponseHeaders()); } @Override - public void sendResponseHeaders(int code, int contentLength) throws IOException { + public void sendResponseHeaders(int code, long contentLength) throws IOException { if (contentLength == 0) contentLength = -1; else if (contentLength < 0) contentLength = 0; exchange.sendResponseHeaders(code, contentLength); @@ -310,21 +316,27 @@ public InetSocketAddress getLocalAddress() { public URI getRequestURI() { return exchange.getRequestURI(); } @Override public String getRequestMethod() { return exchange.getRequestMethod(); } + + @Override + public String getConnectionKey() { + return exchange.getLocalAddress() + "->" + exchange.getRemoteAddress(); + } + @Override public String toString() { return this.getClass().getSimpleName() + ": " + exchange.toString(); } } - private static final class Http2TestExchangeImpl extends HttpTestExchange { + private static final class H2ExchangeImpl extends HttpTestExchange { private final Http2TestExchange exchange; - Http2TestExchangeImpl(Http2TestExchange exch) { + H2ExchangeImpl(Http2TestExchange exch) { this.exchange = exch; } @Override - public Version getServerVersion() { return Version.HTTP_2; } + public Version getServerVersion() { return HTTP_2; } @Override - public Version getExchangeVersion() { return Version.HTTP_2; } + public Version getExchangeVersion() { return HTTP_2; } @Override public InputStream getRequestBody() { return exchange.getRequestBody(); @@ -343,7 +355,7 @@ public HttpTestResponseHeaders getResponseHeaders() { return HttpTestResponseHeaders.of(exchange.getResponseHeaders()); } @Override - public void sendResponseHeaders(int code, int contentLength) throws IOException { + public void sendResponseHeaders(int code, long contentLength) throws IOException { if (contentLength == 0) contentLength = -1; else if (contentLength < 0) contentLength = 0; exchange.sendResponseHeaders(code, contentLength); @@ -371,6 +383,11 @@ public InetSocketAddress getLocalAddress() { return exchange.getLocalAddress(); } + @Override + public String getConnectionKey() { + return exchange.getConnectionKey(); + } + @Override public URI getRequestURI() { return exchange.getRequestURI(); } @Override @@ -408,6 +425,53 @@ private void doHandle(HttpTestExchange t) throws IOException { } } + /** + * An {@link HttpTestHandler} that handles only HEAD and GET + * requests. If another method is used 405 is returned with + * an empty body. + * The response is always returned with fixed length. + */ + public static class HttpHeadOrGetHandler implements HttpTestHandler { + final String responseBody; + public HttpHeadOrGetHandler() { + this("pâté de tête persillé"); + } + public HttpHeadOrGetHandler(String responseBody) { + this.responseBody = Objects.requireNonNull(responseBody); + } + + @Override + public void handle(HttpTestExchange t) throws IOException { + try (var exchg = t) { + exchg.getRequestBody().readAllBytes(); + String method = exchg.getRequestMethod(); + switch (method) { + case "HEAD" -> { + byte[] resp = responseBody.getBytes(StandardCharsets.UTF_8); + if (exchg.getExchangeVersion() != HTTP_1_1) { + // with HTTP/2 or HTTP/3 the server will not send content-length + exchg.getResponseHeaders() + .addHeader("Content-Length", String.valueOf(resp.length)); + } + exchg.sendResponseHeaders(200, resp.length); + exchg.getResponseBody().close(); + } + case "GET" -> { + byte[] resp = responseBody.getBytes(StandardCharsets.UTF_8); + exchg.sendResponseHeaders(200, resp.length); + try (var os = exchg.getResponseBody()) { + os.write(resp); + } + } + default -> { + exchg.sendResponseHeaders(405, 0); + exchg.getResponseBody().close(); + } + } + } + } + } + public static class HttpTestEchoHandler implements HttpTestHandler { @Override @@ -701,7 +765,7 @@ public static abstract class HttpTestContext { /** * A version agnostic adapter class for HTTP Servers. */ - public static abstract class HttpTestServer { + abstract class HttpTestServer implements AutoCloseable { private static final class ServerLogging { private static final Logger logger = Logger.getLogger("com.sun.net.httpserver"); static void enableLogging() { @@ -716,6 +780,12 @@ static void enableLogging() { public abstract HttpTestContext addHandler(HttpTestHandler handler, String root); public abstract InetSocketAddress getAddress(); public abstract Version getVersion(); + public abstract void setRequestApprover(final Predicate approver); + + @Override + public void close() throws Exception { + stop(); + } public String serverAuthority() { InetSocketAddress address = getAddress(); @@ -863,7 +933,12 @@ public InetSocketAddress getAddress() { return new InetSocketAddress(InetAddress.getLoopbackAddress(), impl.getAddress().getPort()); } - public Version getVersion() { return Version.HTTP_1_1; } + public Version getVersion() { return HTTP_1_1; } + + @Override + public void setRequestApprover(final Predicate approver) { + throw new UnsupportedOperationException("not supported"); + } } private static class Http1TestContext extends HttpTestContext { @@ -883,7 +958,7 @@ public void addFilter(HttpTestFilter filter) { public void setAuthenticator(com.sun.net.httpserver.Authenticator authenticator) { context.setAuthenticator(authenticator); } - @Override public Version getVersion() { return Version.HTTP_1_1; } + @Override public Version getVersion() { return HTTP_1_1; } } private static class Http2TestServerImpl extends HttpTestServer { @@ -901,6 +976,13 @@ public void stop() { System.out.println("Http2TestServerImpl: stop"); impl.stop(); } + + @Override + public void close() throws Exception { + System.out.println("Http2TestServerImpl: close"); + impl.close(); + } + @Override public HttpTestContext addHandler(HttpTestHandler handler, String path) { System.out.println("Http2TestServerImpl[" + getAddress() @@ -914,7 +996,12 @@ public InetSocketAddress getAddress() { return new InetSocketAddress(InetAddress.getLoopbackAddress(), impl.getAddress().getPort()); } - public Version getVersion() { return Version.HTTP_2; } + public Version getVersion() { return HTTP_2; } + + @Override + public void setRequestApprover(final Predicate approver) { + this.impl.setRequestApprover(approver); + } } private static class Http2TestContext @@ -947,7 +1034,7 @@ public void setAuthenticator(final Authenticator authenticator) { "only BasicAuthenticator is supported on HTTP/2 context"); } } - @Override public Version getVersion() { return Version.HTTP_2; } + @Override public Version getVersion() { return HTTP_2; } } } diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/common/TestUtil.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/common/TestUtil.java deleted file mode 100644 index 2b2dacff57b29..0000000000000 --- a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/common/TestUtil.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.httpclient.test.lib.common; - -import java.io.*; -import java.nio.file.*; -import java.util.Arrays; - -public class TestUtil { - - static final Path CWD = Paths.get("."); - final static String fileContent = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // repeated - - public static Path getAFile(int size) throws IOException { - Path p = tempFile(); - BufferedWriter writer = Files.newBufferedWriter(p); - int len = fileContent.length(); - int iterations = size / len; - int remainder = size - (iterations * len); - for (int i=0; i q; final int streamid; - boolean closed; - boolean eof; + volatile boolean closed; + volatile boolean eof; final Http2TestServerConnection conn; @SuppressWarnings({"rawtypes","unchecked"}) @@ -100,6 +100,11 @@ private ByteBuffer getBuffer() throws IOException { return buffer; } + + public boolean isEof() { + return eof; + } + @Override public int read(byte[] buf, int offset, int length) throws IOException { if (closed) { @@ -128,9 +133,12 @@ public int read() throws IOException { return one[0] & 0xFF; } + public boolean unconsumed() { + return (!isEof() || q.size() > 0); + } + @Override public void close() { - // TODO reset this stream closed = true; } } diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/BodyOutputStream.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/BodyOutputStream.java index 81b1d97323900..c6eee5afabfdb 100644 --- a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/BodyOutputStream.java +++ b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/BodyOutputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,8 +25,10 @@ import java.io.*; import java.nio.ByteBuffer; +import java.util.Objects; import jdk.internal.net.http.frame.DataFrame; +import jdk.internal.net.http.frame.ResetFrame; /** * OutputStream. Incoming window updates handled by the main connection @@ -39,6 +41,8 @@ public class BodyOutputStream extends OutputStream { final int streamid; int window; volatile boolean closed; + volatile BodyInputStream bis; + volatile int resetErrorCode; boolean goodToGo = false; // not allowed to send until headers sent final Http2TestServerConnection conn; final Queue outputQ; @@ -59,18 +63,28 @@ synchronized void updateWindow(int update) { } void waitForWindow(int demand) throws InterruptedException { - // first wait for the connection window + // first wait for the stream window + waitForStreamWindow(demand); + // now wait for the connection window conn.obtainConnectionWindow(demand); - // now wait for the stream window - synchronized (this) { - while (demand > 0) { - int n = Math.min(demand, window); - demand -= n; - window -= n; - if (demand > 0) { - wait(); + } + + public void waitForStreamWindow(int amount) throws InterruptedException { + int demand = amount; + try { + synchronized (this) { + while (amount > 0) { + int n = Math.min(amount, window); + amount -= n; + window -= n; + if (amount > 0) { + wait(); + } } } + } catch (Throwable t) { + window += (demand - amount); + throw t; } } @@ -80,6 +94,7 @@ public void goodToGo() { @Override public void write(byte[] buf, int offset, int len) throws IOException { + Objects.checkFromIndexSize(offset, len, buf.length); if (closed) { throw new IOException("closed"); } @@ -101,6 +116,34 @@ public void write(byte[] buf, int offset, int len) throws IOException { } } + /** + * This method pushes frames onto the stack without checking + * for flow control, allowing the sender to bypass flow + * control for testing purposes + * @param buf data to send + * @param offset offset at which the data starts + * @param len length of the data to send + * @throws IOException if an I/O error occurs + */ + public void writeUncontrolled(byte[] buf, int offset, int len) + throws IOException { + Objects.checkFromIndexSize(offset, len, buf.length); + if (closed) { + throw new IOException("closed"); + } + + if (!goodToGo) { + throw new IllegalStateException("sendResponseHeaders must be called first"); + } + int max = conn.getMaxFrameSize(); + while (len > 0) { + int n = len > max ? max : len; + send(buf, offset, n, 0); + offset += n; + len -= n; + } + } + private void send(byte[] buf, int offset, int len, int flags) throws IOException { ByteBuffer buffer = ByteBuffer.allocate(len); buffer.put(buf, offset, len); @@ -131,13 +174,25 @@ public void close() { } try { sendEndStream(); + if (bis!= null && bis.unconsumed()) { + // Send a reset if there is still unconsumed data in the input stream + sendReset(EMPTY_BARRAY, 0, 0, ResetFrame.NO_ERROR); + } } catch (IOException ex) { - System.err.println("TestServer: OutputStream.close exception: " + ex); ex.printStackTrace(); } } - protected void sendEndStream() throws IOException { + public void sendEndStream() throws IOException { send(EMPTY_BARRAY, 0, 0, DataFrame.END_STREAM); } + + public void sendReset(byte[] buf, int offset, int len, int flags) throws IOException { + ByteBuffer buffer = ByteBuffer.allocate(len); + buffer.put(buf, offset, len); + buffer.flip(); + assert streamid != 0; + ResetFrame rf = new ResetFrame(streamid, flags); + outputQ.put(rf); + } } diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchange.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchange.java index 1a8b5d92af53d..828c939f53f20 100644 --- a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchange.java +++ b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchange.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -84,4 +84,10 @@ default void sendFrames(List frames) throws IOException { * It may also complete exceptionally */ CompletableFuture sendPing(); + + /** + * {@return the identification of the connection on which this exchange is being + * processed} + */ + String getConnectionKey(); } diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchangeImpl.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchangeImpl.java index 5ac6ab0fd2686..c867b8bf040f5 100644 --- a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchangeImpl.java +++ b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchangeImpl.java @@ -27,6 +27,7 @@ import jdk.internal.net.http.frame.HeaderFrame; import jdk.internal.net.http.frame.HeadersFrame; import jdk.internal.net.http.frame.Http2Frame; +import jdk.internal.net.http.frame.ResetFrame; import javax.net.ssl.SSLSession; import java.io.IOException; @@ -47,7 +48,7 @@ public class Http2TestExchangeImpl implements Http2TestExchange { protected final HttpHeadersBuilder rspheadersBuilder; final URI uri; final String method; - final InputStream is; + protected final InputStream is; protected final BodyOutputStream os; final SSLSession sslSession; protected final int streamid; @@ -139,6 +140,9 @@ public void sendResponseHeaders(int rCode, long responseLength) throws IOExcepti public void sendResponseHeaders(int rCode, long responseLength, BiPredicate insertionPolicy) throws IOException { + // Do not set Content-Length for 100, and do not set END_STREAM + if (rCode == 100) responseLength = 0; + this.responseLength = responseLength; if (responseLength !=0 && rCode != 204 && !isHeadRequest()) { long clen = responseLength > 0 ? responseLength : 0; @@ -156,9 +160,16 @@ public void sendResponseHeaders(int rCode, long responseLength, if (responseLength < 0 || rCode == 204) { response.setFlag(HeadersFrame.END_STREAM); + conn.outputQ.put(response); + // Put a reset frame on the outputQ if there is still unconsumed data in the input stream and output stream + // is going to be marked closed. + if (is instanceof BodyInputStream bis && bis.unconsumed()) { + conn.outputQ.put(new ResetFrame(streamid, ResetFrame.NO_ERROR)); + } os.markClosed(); + } else { + conn.outputQ.put(response); } - conn.outputQ.put(response); os.goodToGo(); System.err.println("Sent response headers " + rCode); } @@ -216,6 +227,11 @@ public void serverPush(URI uri, HttpHeaders headers, InputStream content) { } } + @Override + public String getConnectionKey() { + return conn.connectionKey(); + } + private boolean isHeadRequest() { return HEAD.equalsIgnoreCase(getRequestMethod()); } diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestServer.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestServer.java index fd900f956c8eb..62fb55af79c35 100644 --- a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestServer.java +++ b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestServer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,17 +26,19 @@ import java.io.IOException; import java.net.*; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; +import java.util.function.Predicate; + import javax.net.ServerSocketFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLServerSocket; -import javax.net.ssl.SSLServerSocketFactory; -import javax.net.ssl.SNIServerName; + import jdk.internal.net.http.frame.ErrorFrame; /** @@ -47,35 +49,38 @@ * obtained from the supplied ExecutorService. */ public class Http2TestServer implements AutoCloseable { + static final AtomicLong IDS = new AtomicLong(); + final long id = IDS.incrementAndGet(); final ServerSocket server; final boolean supportsHTTP11; volatile boolean secure; final ExecutorService exec; - volatile boolean stopping = false; + private volatile boolean stopping = false; final Map handlers; final SSLContext sslContext; final String serverName; - final HashMap connections; + final Set connections; final Properties properties; + final String name; + // request approver which takes the server connection key as the input + private volatile Predicate newRequestApprover; - private static ThreadFactory defaultThreadFac = - (Runnable r) -> { + private static ExecutorService createExecutor(String name) { + String threadNamePrefix = "%s-pool".formatted(name); + ThreadFactory threadFactory = (Runnable r) -> { Thread t = new Thread(r); - t.setName("Test-server-pool"); + t.setName(threadNamePrefix); return t; }; - - - private static ExecutorService getDefaultExecutor() { - return Executors.newCachedThreadPool(defaultThreadFac); + return Executors.newCachedThreadPool(threadFactory); } public Http2TestServer(String serverName, boolean secure, int port) throws Exception { - this(serverName, secure, port, getDefaultExecutor(), 50, null, null); + this(serverName, secure, port, null, 50, null, null); } public Http2TestServer(boolean secure, int port) throws Exception { - this(null, secure, port, getDefaultExecutor(), 50, null, null); + this(null, secure, port, null, 50, null, null); } public InetSocketAddress getAddress() { @@ -177,6 +182,7 @@ public Http2TestServer(InetAddress localAddr, boolean supportsHTTP11) throws Exception { + this.name = "TestServer(%d)".formatted(id); this.serverName = serverName; this.supportsHTTP11 = supportsHTTP11; if (secure) { @@ -190,10 +196,10 @@ public Http2TestServer(InetAddress localAddr, server = initPlaintext(port, backlog); } this.secure = secure; - this.exec = exec == null ? getDefaultExecutor() : exec; + this.exec = exec == null ? createExecutor(name) : exec; this.handlers = Collections.synchronizedMap(new HashMap<>()); this.properties = properties == null ? new Properties() : properties; - this.connections = new HashMap<>(); + this.connections = ConcurrentHashMap.newKeySet(); } /** @@ -232,7 +238,7 @@ Http2Handler getHandlerFor(String path) { Http2Handler handler = href.get(); if (handler == null) throw new RuntimeException("No handler found for path " + path); - System.err.println("Using handler for: " + bestMatch.get()); + System.err.println(name + ": Using handler for: " + bestMatch.get()); return handler; } @@ -246,8 +252,8 @@ final ServerSocket initPlaintext(int port, int backlog) throws Exception { public synchronized void stop() { // TODO: clean shutdown GoAway stopping = true; - System.err.printf("Server stopping %d connections\n", connections.size()); - for (Http2TestServerConnection connection : connections.values()) { + System.err.printf("%s: stopping %d connections\n", name, connections.size()); + for (Http2TestServerConnection connection : connections) { connection.close(ErrorFrame.NO_ERROR); } try { @@ -282,13 +288,66 @@ public String serverName() { return serverName; } + public void setRequestApprover(final Predicate approver) { + this.newRequestApprover = approver; + } + + Predicate getRequestApprover() { + return this.newRequestApprover; + } + private synchronized void putConnection(InetSocketAddress addr, Http2TestServerConnection c) { if (!stopping) - connections.put(addr, c); + connections.add(c); } private synchronized void removeConnection(InetSocketAddress addr, Http2TestServerConnection c) { - connections.remove(addr, c); + connections.remove(c); + } + + record AcceptedConnection(Http2TestServer server, + Socket socket) { + void startConnection() { + String name = server.name; + Http2TestServerConnection c = null; + InetSocketAddress addr = null; + try { + addr = (InetSocketAddress) socket.getRemoteSocketAddress(); + System.err.println(name + ": creating connection"); + c = server.createConnection(server, socket, server.exchangeSupplier); + server.putConnection(addr, c); + System.err.println(name + ": starting connection"); + c.run(); + System.err.println(name + ": connection started"); + } catch (Throwable e) { + boolean stopping = server.stopping; + if (!stopping) { + System.err.println(name + ": unexpected exception: " + e); + e.printStackTrace(); + } + // we should not reach here, but if we do + // the connection might not have been closed + // and if so then the client might wait + // forever. + if (c != null) { + server.removeConnection(addr, c); + } + try { + if (c != null) c.close(ErrorFrame.PROTOCOL_ERROR); + } catch (Exception x) { + if (!stopping) + System.err.println(name + ": failed to close connection: " + e); + } finally { + try { + socket.close(); + } catch (IOException x) { + if (!stopping) + System.err.println(name + ": failed to close socket: " + e); + } + } + System.err.println(name + ": failed to start connection: " + e); + } + } } /** @@ -298,38 +357,37 @@ public void start() { exec.submit(() -> { try { while (!stopping) { + System.err.println(name + ": accepting connections"); Socket socket = server.accept(); - Http2TestServerConnection c = null; - InetSocketAddress addr = null; + System.err.println(name + ": connection accepted"); try { - addr = (InetSocketAddress) socket.getRemoteSocketAddress(); - c = createConnection(this, socket, exchangeSupplier); - putConnection(addr, c); - c.run(); + var accepted = new AcceptedConnection(this, socket); + exec.submit(accepted::startConnection); } catch (Throwable e) { + if (!stopping) { + System.err.println(name + ": unexpected exception: " + e); + e.printStackTrace(); + } // we should not reach here, but if we do // the connection might not have been closed // and if so then the client might wait // forever. - if (c != null) { - removeConnection(addr, c); - c.close(ErrorFrame.PROTOCOL_ERROR); - } else { - socket.close(); - } - System.err.println("TestServer: start exception: " + e); + System.err.println(name + ": start exception: " + e); } + System.err.println(name + ": stopping is: " + stopping); } } catch (SecurityException se) { - System.err.println("TestServer: terminating, caught " + se); + System.err.println(name + ": terminating, caught " + se); se.printStackTrace(); stopping = true; try { server.close(); } catch (IOException ioe) { /* ignore */} } catch (Throwable e) { if (!stopping) { - System.err.println("TestServer: terminating, caught " + e); + System.err.println(name + ": terminating, caught " + e); e.printStackTrace(); } + } finally { + System.err.println(name + ": finished"); } }); } @@ -343,6 +401,7 @@ protected Http2TestServerConnection createConnection(Http2TestServer http2TestSe @Override public void close() throws Exception { + System.err.println(name + ": closing"); stop(); } } diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestServerConnection.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestServerConnection.java index 102c7a5672182..059edf0cce77f 100644 --- a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestServerConnection.java +++ b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestServerConnection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -77,15 +77,19 @@ import java.util.Optional; import java.util.Properties; import java.util.Random; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutorService; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; import java.util.function.BiPredicate; import java.util.function.Consumer; +import java.util.function.Predicate; import static java.nio.charset.StandardCharsets.ISO_8859_1; import static java.nio.charset.StandardCharsets.UTF_8; +import static jdk.internal.net.http.frame.ErrorFrame.REFUSED_STREAM; import static jdk.internal.net.http.frame.SettingsFrame.DEFAULT_MAX_FRAME_SIZE; import static jdk.internal.net.http.frame.SettingsFrame.HEADER_TABLE_SIZE; @@ -115,6 +119,10 @@ public class Http2TestServerConnection { volatile boolean stopping; volatile int nextPushStreamId = 2; ConcurrentLinkedQueue pings = new ConcurrentLinkedQueue<>(); + // the max stream id of a processed H2 request. -1 implies none were processed. + private final AtomicInteger maxProcessedRequestStreamId = new AtomicInteger(-1); + // the stream id that was sent in a GOAWAY frame. -1 implies no GOAWAY frame was sent. + private final AtomicInteger goAwayRequestStreamId = new AtomicInteger(-1); final static ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0); final static byte[] EMPTY_BARRAY = new byte[0]; @@ -163,7 +171,7 @@ void fail(Throwable t) { Properties properties) throws IOException { - System.err.println("TestServer: New connection from " + socket); + System.err.println(server.name + ": New connection from " + socket); if (socket instanceof SSLSocket) { SSLSocket sslSocket = (SSLSocket)socket; @@ -210,10 +218,10 @@ private SettingsFrame getServerSettingProperties() { String prop = properties.getProperty(propPrefix + key); if (prop != null) { try { - System.err.println("TestServer: setting " + key + " property to: " + + System.err.println(server.name + ": setting " + key + " property to: " + prop); int num = Integer.parseInt(numS); - System.err.println("TestServer: num = " + num); + System.err.println(server.name + ": num = " + num); s.setParameter(num, Integer.parseInt(prop)); } catch (NumberFormatException e) {/* ignore errors */} } @@ -239,11 +247,29 @@ CompletableFuture sendPing() { return ping.response(); } - void goAway(int error) throws IOException { - int laststream = nextstream >= 3 ? nextstream - 2 : 1; - - GoAwayFrame go = new GoAwayFrame(laststream, error); - outputQ.put(go); + private void sendGoAway(final int error) throws IOException { + int maxProcessedStreamId = maxProcessedRequestStreamId.get(); + if (maxProcessedStreamId == -1) { + maxProcessedStreamId = 0; + } + boolean send = false; + int currentGoAwayReqStrmId = goAwayRequestStreamId.get(); + // update the last processed stream id and send a goaway frame if the new last processed + // stream id is lesser than the last processed stream id sent in + // a previous goaway frame (if any) + while (currentGoAwayReqStrmId == -1 || maxProcessedStreamId < currentGoAwayReqStrmId) { + if (goAwayRequestStreamId.compareAndSet(currentGoAwayReqStrmId, maxProcessedStreamId)) { + send = true; + break; + } + currentGoAwayReqStrmId = goAwayRequestStreamId.get(); + } + if (!send) { + return; + } + final GoAwayFrame frame = new GoAwayFrame(maxProcessedStreamId, error); + outputQ.put(frame); + System.err.println(server.name + ": Sending GOAWAY frame " + frame + " from server connection " + this); } /** @@ -259,7 +285,7 @@ private PingRequest getNextRequest() { */ void handlePing(PingFrame ping) throws IOException { if (ping.streamid() != 0) { - System.err.println("Invalid ping received"); + System.err.println(server.name + ": Invalid ping received"); close(ErrorFrame.PROTOCOL_ERROR); return; } @@ -267,7 +293,7 @@ void handlePing(PingFrame ping) throws IOException { // did we send a Ping? PingRequest request = getNextRequest(); if (request == null) { - System.err.println("Invalid ping ACK received"); + System.err.println(server.name + ": Invalid ping ACK received"); close(ErrorFrame.PROTOCOL_ERROR); return; } else if (!Arrays.equals(request.pingData, ping.getData())) { @@ -297,7 +323,7 @@ private static boolean compareIPAddrs(InetAddress addr1, String host) { private static void handshake(String name, SSLSocket sock) throws IOException { if (name == null) { - sock.getSession(); // awaits handshake completion + sock.startHandshake(); // blocks until handshake done return; } else if (name.equals("localhost")) { name = "localhost"; @@ -319,7 +345,7 @@ public boolean matches (SNIServerName n) { List list = List.of(matcher); params.setSNIMatchers(list); sock.setSSLParameters(params); - sock.getSession(); // blocks until handshake done + sock.startHandshake(); // blocks until handshake done } void closeIncoming() { @@ -330,14 +356,15 @@ void close(int error) { if (stopping) return; stopping = true; - System.err.printf("Server connection to %s stopping. %d streams\n", + System.err.printf(server.name + ": Server connection to %s stopping. %d streams\n", socket.getRemoteSocketAddress().toString(), streams.size()); streams.forEach((i, q) -> { q.orderlyClose(); }); try { - if (error != -1) - goAway(error); + if (error != -1) { + sendGoAway(error); + } outputQ.orderlyClose(); socket.close(); } catch (Exception e) { @@ -349,16 +376,20 @@ private void readPreface() throws IOException { byte[] bytes = new byte[len]; int n = is.readNBytes(bytes, 0, len); if (Arrays.compare(clientPreface, bytes) != 0) { - System.err.printf("Invalid preface: read %d/%d bytes%n", n, len); - throw new IOException("Invalid preface: " + - new String(bytes, 0, len, ISO_8859_1)); + String msg = String.format("Invalid preface: read %s/%s bytes", n, len); + System.err.println(server.name + ": " + msg); + throw new IOException(msg +": \"" + + new String(bytes, 0, n, ISO_8859_1) + .replace("\r", "\\r") + .replace("\n", "\\n") + + "\""); } } Http1InitialRequest doUpgrade(Http1InitialRequest upgrade) throws IOException { String h2c = getHeader(upgrade.headers, "Upgrade"); if (h2c == null || !h2c.equals("h2c")) { - System.err.println("Server:HEADERS: " + upgrade); + System.err.println(server.name + ":HEADERS: " + upgrade); throw new IOException("Bad upgrade 1 " + h2c); } @@ -430,7 +461,7 @@ void run() throws Exception { socket.close(); return; } else { - System.err.println("Server:HEADERS: " + upgrade); + System.err.println(server.name + ":HEADERS: " + upgrade); throw new IOException("Bad upgrade 1 " + h2c); } } @@ -529,7 +560,7 @@ private void handleCommonFrame(Http2Frame f) throws IOException { outputQ.put(frame); return; } else if (f instanceof GoAwayFrame) { - System.err.println("Closing: "+ f.toString()); + System.err.println(server.name + ": Closing connection: "+ f.toString()); close(ErrorFrame.NO_ERROR); } else if (f instanceof PingFrame) { handlePing((PingFrame)f); @@ -619,6 +650,14 @@ void createPrimordialStream(Http1InitialRequest request) throws IOException { path = path + "?" + uri.getRawQuery(); headersBuilder.setHeader(":path", path); + // skip processing the request if configured to do so + final String connKey = connectionKey(); + if (!shouldProcessNewHTTPRequest(connKey)) { + System.err.println(server.name + ": Rejecting primordial stream 1 and sending GOAWAY" + + " on server connection " + connKey + ", for request: " + path); + sendGoAway(ErrorFrame.NO_ERROR); + return; + } Queue q = new Queue(sentinel); byte[] body = getRequestBody(request); addHeaders(getHeaders(request.headers), headersBuilder); @@ -627,11 +666,24 @@ void createPrimordialStream(Http1InitialRequest request) throws IOException { addRequestBodyToQueue(body, q); streams.put(1, q); + maxProcessedRequestStreamId.set(1); exec.submit(() -> { handleRequest(headers, q, 1, true /*complete request has been read*/); }); } + private boolean shouldProcessNewHTTPRequest(final String serverConnKey) { + final Predicate approver = this.server.getRequestApprover(); + if (approver == null) { + return true; // process the request + } + return approver.test(serverConnKey); + } + + final String connectionKey() { + return this.server.getAddress() + "->" + this.socket.getRemoteSocketAddress(); + } + // all other streams created here @SuppressWarnings({"rawtypes","unchecked"}) void createStream(HeaderFrame frame) throws IOException { @@ -639,7 +691,7 @@ void createStream(HeaderFrame frame) throws IOException { frames.add(frame); int streamid = frame.streamid(); if (streamid != nextstream) { - throw new IOException("unexpected stream id"); + throw new IOException("unexpected stream id: " + streamid); } nextstream += 2; @@ -670,12 +722,30 @@ void createStream(HeaderFrame frame) throws IOException { throw new IOException("Unexpected Upgrade in headers:" + headers); } disallowedHeader = headers.firstValue("HTTP2-Settings"); - if (disallowedHeader.isPresent()) + if (disallowedHeader.isPresent()) { throw new IOException("Unexpected HTTP2-Settings in headers:" + headers); + } - + // skip processing the request if the server is configured to do so + final String connKey = connectionKey(); + final String path = headers.firstValue(":path").orElse(""); + if (!shouldProcessNewHTTPRequest(connKey)) { + System.err.println(server.name + ": Rejecting stream " + streamid + + " and sending GOAWAY on server connection " + + connKey + ", for request: " + path); + sendGoAway(ErrorFrame.NO_ERROR); + return; + } Queue q = new Queue(sentinel); streams.put(streamid, q); + // keep track of the largest request id that we have processed + int currentLargest = maxProcessedRequestStreamId.get(); + while (streamid > currentLargest) { + if (maxProcessedRequestStreamId.compareAndSet(currentLargest, streamid)) { + break; + } + currentLargest = maxProcessedRequestStreamId.get(); + } exec.submit(() -> { handleRequest(headers, q, streamid, endStreamReceived); }); @@ -698,17 +768,17 @@ void handleRequest(HttpHeaders headers, //System.out.println("scheme = " + scheme); String authority = headers.firstValue(":authority").orElse(""); //System.out.println("authority = " + authority); - System.err.printf("TestServer: %s %s\n", method, path); + System.err.printf(server.name + ": %s %s\n", method, path); int winsize = clientSettings.getParameter( SettingsFrame.INITIAL_WINDOW_SIZE); //System.err.println ("Stream window size = " + winsize); final InputStream bis; if (endStreamReceived && queue.size() == 0) { - System.err.println("Server: got END_STREAM for stream " + streamid); + System.err.println(server.name + ": got END_STREAM for stream " + streamid); bis = NullInputStream.INSTANCE; } else { - System.err.println("Server: creating input stream for stream " + streamid); + System.err.println(server.name + ": creating input stream for stream " + streamid); bis = new BodyInputStream(queue, streamid, this); } try (bis; @@ -725,13 +795,18 @@ headers, rspheadersBuilder, uri, bis, getSSLSession(), // give to user Http2Handler handler = server.getHandlerFor(uri.getPath()); + + // Need to pass the BodyInputStream reference to the BodyOutputStream, so it can determine if the stream + // must be reset due to the BodyInputStream not being consumed by the handler when invoked. + if (bis instanceof BodyInputStream bodyInputStream) bos.bis = bodyInputStream; + try { handler.handle(exchange); } catch (IOException closed) { if (bos.closed) { Queue q = streams.get(streamid); if (q != null && (q.isClosed() || q.isClosing())) { - System.err.println("TestServer: Stream " + streamid + " closed: " + closed); + System.err.println(server.name + ": Stream " + streamid + " closed: " + closed); return; } } @@ -741,7 +816,7 @@ headers, rspheadersBuilder, uri, bis, getSSLSession(), // everything happens in the exchange from here. Hopefully will // return though. } catch (Throwable e) { - System.err.println("TestServer: handleRequest exception: " + e); + System.err.println(server.name + ": handleRequest exception: " + e); e.printStackTrace(); close(-1); } @@ -773,6 +848,8 @@ void readLoop() { while (!stopping) { Http2Frame frame = readFrameImpl(); if (frame == null) { + System.err.println(server.name + ": EOF reached on connection " + connectionKey() + + ", will no longer accept incoming frames"); closeIncoming(); return; } @@ -792,15 +869,27 @@ void readLoop() { Queue q = streams.get(stream); if (frame.type() == HeadersFrame.TYPE) { if (q != null) { - System.err.println("HEADERS frame for existing stream! Error."); + System.err.println(server.name + ": HEADERS frame for existing stream! Error."); // TODO: close connection continue; } else { + final int streamId = frame.streamid(); + final int finalProcessedStreamId = goAwayRequestStreamId.get(); + // if we already sent a goaway, then don't create new streams with + // higher stream ids. + if (finalProcessedStreamId != -1 && streamId > finalProcessedStreamId) { + System.err.println(server.name + ": " + connectionKey() + + " resetting stream " + streamId + + " as REFUSED_STREAM"); + final ResetFrame rst = new ResetFrame(streamId, REFUSED_STREAM); + outputQ.put(rst); + continue; + } createStream((HeadersFrame) frame); } } else { if (q == null && !pushStreams.contains(stream)) { - System.err.printf("Non Headers frame received with"+ + System.err.printf(server.name + ": Non Headers frame received with"+ " non existing stream (%d) ", frame.streamid()); System.err.println(frame); continue; @@ -830,15 +919,15 @@ void readLoop() { } else if (isClientStreamId(stream) && stream < next) { // We may receive a reset on a client stream that has already // been closed. Just ignore it. - System.err.println("TestServer: received ResetFrame on closed stream: " + stream); + System.err.println(server.name + ": received ResetFrame on closed stream: " + stream); System.err.println(frame); } else if (isServerStreamId(stream) && stream < nextPush) { // We may receive a reset on a push stream that has already // been closed. Just ignore it. - System.err.println("TestServer: received ResetFrame on closed push stream: " + stream); + System.err.println(server.name + ": received ResetFrame on closed push stream: " + stream); System.err.println(frame); } else { - System.err.println("TestServer: Unexpected frame on: " + stream); + System.err.println(server.name + ": Unexpected frame on: " + stream); System.err.println(frame); throw new IOException("Unexpected frame"); } @@ -850,7 +939,7 @@ void readLoop() { } } catch (Throwable e) { if (!stopping) { - System.err.println("Http server reader thread shutdown"); + System.err.println(server.name + ": Http server reader thread shutdown"); e.printStackTrace(); } close(ErrorFrame.PROTOCOL_ERROR); @@ -991,7 +1080,7 @@ void writeLoop() { : new ContinuationFrame(rh.streamid(), flags, list); if (Log.headers()) { // avoid too much chatter: log only if Log.headers() is enabled - System.err.println("TestServer writing " + hf); + System.err.println(server.name + ": writing " + hf); } writeFrame(hf); cont++; @@ -1001,7 +1090,7 @@ void writeLoop() { } else writeFrame(frame); } - System.err.println("TestServer: Connection writer stopping"); + System.err.println(server.name + ": Connection writer stopping " + connectionKey()); } catch (Throwable e) { e.printStackTrace(); /*close(); @@ -1035,7 +1124,7 @@ private void handlePush(OutgoingPushPromise op) throws IOException { SettingsFrame.INITIAL_WINDOW_SIZE), this) { @Override - protected void sendEndStream() throws IOException { + public void sendEndStream() throws IOException { if (properties.getProperty("sendTrailingHeadersAfterPushPromise", "0").equals("1")) { conn.outputQ.put(getTrailingHeadersFrame(promisedStreamid, List.of())); } else { @@ -1053,7 +1142,7 @@ protected void sendEndStream() throws IOException { ii.transferTo(oo); } catch (Throwable ex) { - System.err.printf("TestServer: pushing response error: %s\n", + System.err.printf(server.name + ": pushing response error: %s\n", ex.toString()); } finally { closeIgnore(ii); @@ -1216,7 +1305,7 @@ Http1InitialRequest readHttp1Request() throws IOException { } return new Http1InitialRequest(headers, buf); } catch (IOException e) { - System.err.println("TestServer: headers read: [ " + headers + " ]"); + System.err.println(server.name + ": headers read: [ " + headers + " ]"); throw e; } } @@ -1248,7 +1337,7 @@ void sendHttp1Response(int code, String msg, String... headers) throws IOExcepti } private void unexpectedFrame(Http2Frame frame) { - System.err.println("OOPS. Unexpected"); + System.err.println(server.name + ": OOPS. Unexpected"); assert false; } @@ -1287,19 +1376,45 @@ void registerStreamWindowUpdater(int streamid, Consumer r) { * * @param amount */ - synchronized void obtainConnectionWindow(int amount) throws InterruptedException { - while (amount > 0) { - int n = Math.min(amount, sendWindow); - amount -= n; - sendWindow -= n; - if (amount > 0) - wait(); + public synchronized void obtainConnectionWindow(int amount) throws InterruptedException { + int demand = amount; + try { + int waited = 0; + while (amount > 0) { + int n = Math.min(amount, sendWindow); + amount -= n; + sendWindow -= n; + if (amount > 0) { + // Do not include this print line on a version that does not have + // JDK-8337395 + System.err.printf("%s: blocked waiting for %s connection window, obtained %s%n", + server.name, amount, demand - amount); + waited++; + wait(); + } + } + if (waited > 0) { + // Do not backport this print line on a version that does not have + // JDK-8337395 + System.err.printf("%s: obtained %s connection window, remaining %s%n", + server.name, demand, sendWindow); + } + assert amount == 0; + } catch (Throwable t) { + sendWindow += (demand - amount); + throw t; } } - synchronized void updateConnectionWindow(int amount) { - sendWindow += amount; - notifyAll(); + public void updateConnectionWindow(int amount) { + synchronized (this) { + // Do not backport this print line on a version that does not have + // JDK-8337395 + System.err.printf(server.name + ": update sendWindow (window=%s, amount=%s) is now: %s%n", + sendWindow, amount, sendWindow + amount); + sendWindow += amount; + notifyAll(); + } } // simplified output headers class. really just a type safe container diff --git a/test/jdk/java/net/httpclient/offline/FixedResponseHttpClient.java b/test/jdk/java/net/httpclient/offline/FixedResponseHttpClient.java index 13f7f4f6d8331..8be37044afb9e 100644 --- a/test/jdk/java/net/httpclient/offline/FixedResponseHttpClient.java +++ b/test/jdk/java/net/httpclient/offline/FixedResponseHttpClient.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -189,8 +189,11 @@ public HttpClient.Version version() { if (obp.isPresent()) { ConsumingSubscriber subscriber = new ConsumingSubscriber(); obp.get().subscribe(subscriber); + // wait for our subscriber to be completed and get the + // list of ByteBuffers it received. + var buffers = subscriber.getBuffers().join(); if (responseBodyBytes == ECHO_SENTINAL) { - responseBody = subscriber.buffers; + responseBody = buffers; } } @@ -226,6 +229,13 @@ public HttpClient.Version version() { */ private static class ConsumingSubscriber implements Flow.Subscriber { final List buffers = Collections.synchronizedList(new ArrayList<>()); + // A CompletableFuture that will be completed with a list of ByteBuffers that the + // ConsumingSubscriber has consumed. + final CompletableFuture> consumed = new CompletableFuture<>(); + + public final CompletableFuture> getBuffers() { + return consumed; + } @Override public void onSubscribe(Flow.Subscription subscription) { @@ -236,8 +246,8 @@ public void onSubscribe(Flow.Subscription subscription) { buffers.add(item.duplicate()); } - @Override public void onError(Throwable throwable) { assert false : "Unexpected"; } + @Override public void onError(Throwable throwable) { consumed.completeExceptionally(throwable); } - @Override public void onComplete() { /* do nothing */ } + @Override public void onComplete() { consumed.complete(buffers.stream().toList()); } } } diff --git a/test/jdk/java/net/httpclient/offline/OfflineTesting.java b/test/jdk/java/net/httpclient/offline/OfflineTesting.java index d33f806d31c9e..c747f5b2b88c8 100644 --- a/test/jdk/java/net/httpclient/offline/OfflineTesting.java +++ b/test/jdk/java/net/httpclient/offline/OfflineTesting.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -66,7 +66,7 @@ public void testResponseAsString() { HttpClient client = getClient(); HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create("http://openjdk.java.net/")) + .uri(URI.create("https://openjdk.org/")) .build(); client.sendAsync(request, BodyHandlers.ofString()) @@ -83,7 +83,7 @@ public void testResponseAsByteArray() { HttpClient client = getClient(); HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create("http://openjdk.java.net/")) + .uri(URI.create("https://openjdk.org/")) .build(); client.sendAsync(request, BodyHandlers.ofByteArray()) @@ -115,7 +115,7 @@ public void testFileNotFound() { ""); HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create("http://openjdk.java.net/notFound")) + .uri(URI.create("https://openjdk.org/notFound")) .build(); client.sendAsync(request, BodyHandlers.ofString()) @@ -136,7 +136,7 @@ public void testEcho() { headersOf("Connection", "keep-alive")); HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create("http://openjdk.java.net/echo")) + .uri(URI.create("https://openjdk.org/echo")) .POST(BodyPublishers.ofString("Hello World")) .build(); @@ -156,7 +156,7 @@ public void testEchoBlocking() throws IOException, InterruptedException { headersOf("Connection", "keep-alive")); HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create("http://openjdk.java.net/echo")) + .uri(URI.create("https://openjdk.org/echo")) .POST(BodyPublishers.ofString("Hello chegar!!")) .build(); diff --git a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/ConnectionPoolTest.java b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/ConnectionPoolTest.java index 7d900f30cbf3c..991f30093296b 100644 --- a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/ConnectionPoolTest.java +++ b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/ConnectionPoolTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -47,6 +47,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.concurrent.Flow; +import java.util.concurrent.atomic.AtomicLong; import java.util.stream.IntStream; import java.time.Instant; import java.time.temporal.ChronoUnit; @@ -62,7 +63,6 @@ * connection deadlines and purges the right connections * from the cache. * @bug 8187044 8187111 8221395 - * @author danielfuchs */ public class ConnectionPoolTest { @@ -441,6 +441,8 @@ protected void implConfigureBlocking(boolean block) throws IOException { // Emulates an HttpConnection that has a strong reference to its HttpClient. static class HttpConnectionStub extends HttpConnection { + static final AtomicLong IDS = new AtomicLong(); + public HttpConnectionStub( HttpClient client, InetSocketAddress address, @@ -473,6 +475,12 @@ public HttpConnectionStub( final SocketChannel channel; volatile boolean closed, finished; + // Called from within super constructor + @Override + long newConnectionId(HttpClientImpl client) { + return IDS.incrementAndGet(); + } + // Used for testing closeOrReturnToPool. void finish(boolean finished) { this.finished = finished; } void reopen() { closed = finished = false;} diff --git a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/SSLEchoTubeTest.java b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/SSLEchoTubeTest.java index 82bc48f19cbcd..2884b74eee5d9 100644 --- a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/SSLEchoTubeTest.java +++ b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/SSLEchoTubeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -299,12 +299,12 @@ private void requestMore() { Flow.Subscription s = subscription; if (s == null || cancelled.get()) return; long unfulfilled = queue.size() + --requested; - if (unfulfilled <= maxQueueSize/2) { + if (unfulfilled <= maxQueueSize / 2) { long req = maxQueueSize - unfulfilled; requested += req; s.request(req); System.out.printf("EchoTube request: %s [requested:%s, queue:%s, unfulfilled:%s]%n", - req, requested-req, queue.size(), unfulfilled ); + req, requested - req, queue.size(), unfulfilled); } } diff --git a/test/jdk/java/net/ipv6tests/TcpTest.java b/test/jdk/java/net/ipv6tests/TcpTest.java index 3df9454237674..e00081f920ffd 100644 --- a/test/jdk/java/net/ipv6tests/TcpTest.java +++ b/test/jdk/java/net/ipv6tests/TcpTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,12 +31,16 @@ * @library /test/lib * @build jdk.test.lib.NetworkConfiguration * jdk.test.lib.Platform + * jtreg.SkippedException * @run main TcpTest -d * @run main/othervm -Djdk.net.usePlainSocketImpl TcpTest -d */ import java.net.*; import java.io.*; +import java.util.concurrent.TimeUnit; + +import jtreg.SkippedException; public class TcpTest extends Tests { static ServerSocket server, server1, server2; @@ -62,12 +66,10 @@ public class TcpTest extends Tests { public static void main (String[] args) throws Exception { checkDebug(args); if (ia4addr == null) { - System.out.println ("No IPV4 addresses: exiting test"); - return; + throw new SkippedException("No IPV4 addresses: exiting test"); } if (ia6addr == null) { - System.out.println ("No IPV6 addresses: exiting test"); - return; + throw new SkippedException("No IPV6 addresses: exiting test"); } dprintln ("Local Addresses"); dprintln (ia4addr.toString()); @@ -194,13 +196,14 @@ static void test3 () throws Exception { server = new ServerSocket (0); server.setSoTimeout (5000); int port = server.getLocalPort(); - long t1 = System.currentTimeMillis(); + long t1 = System.nanoTime(); try { server.accept (); throw new RuntimeException ("accept should not have returned"); } catch (SocketTimeoutException e) {} - t1 = System.currentTimeMillis() - t1; - checkTime (t1, 5000); + t1 = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1); + final long expectedTime = TimeUnit.SECONDS.toMillis(5); + checkIfTimeOut(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1), expectedTime); c1 = new Socket (); c1.connect (new InetSocketAddress (ia4addr, port), 1000); diff --git a/test/jdk/java/net/ipv6tests/Tests.java b/test/jdk/java/net/ipv6tests/Tests.java index 7a6917a60382f..21cc83571ae37 100644 --- a/test/jdk/java/net/ipv6tests/Tests.java +++ b/test/jdk/java/net/ipv6tests/Tests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -151,19 +151,14 @@ public static void comparePackets (DatagramPacket p1, DatagramPacket p2) } } - /* check the time got is within 50% of the time expected */ - public static void checkTime (long got, long expected) { - checkTime(got, expected, expected); - } - /* check the time got is between start and end, given 50% tolerance */ - public static void checkTime(long got, long start, long end) { - dprintln("checkTime: got = " + got + " start = " + start + " end = " + end); - long upper = end + (end / 2); - long lower = start - (start / 2); - if (got > upper || got < lower) { - throw new RuntimeException("checkTime failed: got " + got - + ", expected between " + start + " and " + end); + /* check the timeout breached lower bound time rule */ + public static void checkIfTimeOut(long got, long expected) { + dprintln("checkIfTimeOut: got = " + got + " lower bound = " + expected); + + if (got < expected) { + throw new RuntimeException("checkIfTimeOut failed: got " + got + + ", expected at least " + expected ); } } diff --git a/test/jdk/java/net/ipv6tests/UdpTest.java b/test/jdk/java/net/ipv6tests/UdpTest.java index 8986af74ff456..6139db85407dd 100644 --- a/test/jdk/java/net/ipv6tests/UdpTest.java +++ b/test/jdk/java/net/ipv6tests/UdpTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,6 +41,7 @@ import java.net.InetAddress; import java.net.PortUnreachableException; import java.net.SocketTimeoutException; +import java.util.concurrent.TimeUnit; public class UdpTest extends Tests { static DatagramSocket c3, s1, s2, s3; @@ -138,26 +139,27 @@ static void test2 () throws Exception { s1 = new DatagramSocket (); s2 = new DatagramSocket (); s1.setSoTimeout (4000); - long t1 = System.currentTimeMillis(); + long t1 = System.nanoTime(); try { s1.receive (new DatagramPacket (new byte [128], 128)); throw new Exception ("expected receive timeout "); } catch (SocketTimeoutException e) { } - checkTime (System.currentTimeMillis() - t1, 4000); + final long expectedTime = TimeUnit.SECONDS.toMillis(4); + checkIfTimeOut(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1), expectedTime); /* check data can be exchanged now */ simpleDataExchange (s1, ia6addr, s2, ia4addr); /* double check timeout still works */ - t1 = System.currentTimeMillis(); + t1 = System.nanoTime(); try { s1.receive (new DatagramPacket (new byte [128], 128)); throw new Exception ("expected receive timeout "); } catch (SocketTimeoutException e) { } - checkTime (System.currentTimeMillis() - t1, 4000); + checkIfTimeOut(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1), expectedTime); /* check receive works after a delay < timeout */ @@ -174,9 +176,10 @@ public void run () { } catch (Exception e) {} } }); - t1 = System.currentTimeMillis(); + t1 = System.nanoTime(); s1.receive (new DatagramPacket (new byte [128], 128)); - checkTime (System.currentTimeMillis() - t1, 2000, 10000); + final long startTime = TimeUnit.SECONDS.toMillis(2); + checkIfTimeOut(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1), startTime); s1.close (); s2.close (); System.out.println ("Test2: OK"); diff --git a/test/jdk/java/nio/Buffer/Chew.java b/test/jdk/java/nio/Buffer/Chew.java index b1afeabc472ab..30932be526afb 100644 --- a/test/jdk/java/nio/Buffer/Chew.java +++ b/test/jdk/java/nio/Buffer/Chew.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +26,7 @@ * @summary Ensure that direct memory can be unreserved * as the reserving thread sleeps * - * @run main/othervm -mx16M Chew + * @run main/othervm -Xmx16M Chew */ import java.nio.*; diff --git a/test/jdk/java/nio/channels/Channels/CloseWriterOnFailedFlush.java b/test/jdk/java/nio/channels/Channels/CloseWriterOnFailedFlush.java new file mode 100644 index 0000000000000..fc5b173086787 --- /dev/null +++ b/test/jdk/java/nio/channels/Channels/CloseWriterOnFailedFlush.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8136895 + * @summary Verify channel closed after write error in StreamEncoder::implClose + */ + +import java.io.IOException; +import java.io.Writer; +import java.nio.ByteBuffer; +import java.nio.channels.Channels; +import java.nio.channels.WritableByteChannel; +import java.nio.charset.MalformedInputException; +import java.nio.charset.StandardCharsets; + +public class CloseWriterOnFailedFlush { + private static final String STR_IOE = "Test"; // IOException + private static final String STR_MIE = "\ud83c"; // MalformedInputException + + public static void main(String[] args) throws IOException { + boolean failed = false; + + for (String s : new String[] {STR_IOE, STR_MIE}) { + System.out.println("string: " + s); + ErroringByteChannel channel = new ErroringByteChannel(); + try (Writer writer = Channels.newWriter + (channel, StandardCharsets.UTF_8.newEncoder(), -1 )) { + writer.write(s); + } catch (IOException ex) { + Class exClass = ex.getClass(); + if (s.equals(STR_IOE) && exClass != IOException.class || + s.equals(STR_MIE) && exClass != MalformedInputException.class) + throw ex; + } + + if (channel.isOpen()) { + System.err.println("Channel is STILL open"); + failed = true; + } else { + System.out.println("Channel is closed"); + } + } + + if (failed) + throw new RuntimeException("Test failed"); + } + + private static class ErroringByteChannel implements WritableByteChannel { + private boolean open = true; + + @Override + public int write(ByteBuffer src) throws IOException { + throw new IOException(); + } + + @Override + public boolean isOpen() { + return open; + } + + @Override + public void close() { + open = false; + System.out.println("Closing"); + } + } +} diff --git a/test/jdk/java/nio/channels/DatagramChannel/StressNativeSignal.java b/test/jdk/java/nio/channels/DatagramChannel/StressNativeSignal.java index d6d2f083eca0e..cc726d19c0d75 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/StressNativeSignal.java +++ b/test/jdk/java/nio/channels/DatagramChannel/StressNativeSignal.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,42 +36,23 @@ import java.util.concurrent.CountDownLatch; public class StressNativeSignal { - private UDPThread udpThread; - private ServerSocketThread serverSocketThread; + private final UDPThread udpThread; + private final ServerSocketThread serverSocketThread; - StressNativeSignal() { + StressNativeSignal() throws IOException { serverSocketThread = initServerSocketThread(); - if (serverSocketThread != null) { - serverSocketThread.start(); - } + serverSocketThread.start(); udpThread = initUDPThread(); - if (udpThread != null) { - udpThread.start(); - } + udpThread.start(); } - private UDPThread initUDPThread() { - UDPThread aUDPThread = null; - try { - aUDPThread = new UDPThread(); - } catch (Exception z) { - System.err.println("failed to create and start a UDPThread"); - z.printStackTrace(); - } - return aUDPThread; + private UDPThread initUDPThread() throws IOException { + return new UDPThread(); } - private ServerSocketThread initServerSocketThread() { - ServerSocketThread aServerSocketThread = null; - try { - aServerSocketThread = new ServerSocketThread(); - - } catch (Exception z) { - System.err.println("failed to create and start a ServerSocketThread"); - z.printStackTrace(); - } - return aServerSocketThread; + private ServerSocketThread initServerSocketThread() throws IOException { + return new ServerSocketThread(); } public static void main(String[] args) throws Throwable { @@ -80,46 +61,39 @@ public static void main(String[] args) throws Throwable { test.shutdown(); } - public void shutdown() { - if ((udpThread != null) && udpThread.isAlive()) { + public void shutdown() throws InterruptedException, IOException { + if (udpThread != null && udpThread.isAlive()) { udpThread.terminate(); - try { - udpThread.join(); - } catch (Exception z) { - z.printStackTrace(System.err); - } + udpThread.join(); + } else { System.out.println("UDPThread test scenario was not run"); } - if ((serverSocketThread != null) && (serverSocketThread.isAlive())) { + if (serverSocketThread != null && serverSocketThread.isAlive()) { serverSocketThread.terminate(); - try { - serverSocketThread.join(); - } catch (Exception z) { - z.printStackTrace(System.err); - } + serverSocketThread.join(); } else { System.out.println("ServerSocketThread test scenario was not run"); } } - public void waitForTestThreadsToStart() { - if ((udpThread != null) && udpThread.isAlive()) { + public void waitForTestThreadsToStart() throws InterruptedException { + if (udpThread != null && udpThread.isAlive()) { udpThread.waitTestThreadStart(); } - if ((serverSocketThread != null) && (serverSocketThread.isAlive())) { + if (serverSocketThread != null && serverSocketThread.isAlive()) { serverSocketThread.waitTestThreadStart(); } } public class ServerSocketThread extends Thread { private volatile boolean shouldTerminate; - private ServerSocket socket; + private final ServerSocket socket; private final CountDownLatch threadStarted = new CountDownLatch(1); - public ServerSocketThread () throws Exception { - socket = new ServerSocket(1122); + public ServerSocketThread() throws IOException { + socket = new ServerSocket(0); } public void run() { @@ -129,7 +103,7 @@ public void run() { Socket client = socket.accept(); client.close(); throw new RuntimeException("Unexpected return from accept call"); - } catch (Exception z) { + } catch (IOException z) { System.err.println("ServerSocketThread: caught exception " + z.getClass().getName()); if (!shouldTerminate) { z.printStackTrace(System.err); @@ -141,7 +115,7 @@ public void terminate() { shouldTerminate = true; try { socket.close(); - } catch (Exception z) { + } catch (IOException z) { z.printStackTrace(System.err); // ignore } @@ -150,7 +124,7 @@ public void terminate() { public void waitTestThreadStart() { try { threadStarted.await(); - } catch (Exception z) { + } catch (InterruptedException z) { z.printStackTrace(System.err); // ignore } @@ -158,15 +132,14 @@ public void waitTestThreadStart() { } public class UDPThread extends Thread { - private DatagramChannel channel; + private final DatagramChannel channel; private volatile boolean shouldTerminate; private final CountDownLatch threadStarted = new CountDownLatch(1); - public UDPThread () throws Exception { - + public UDPThread() throws IOException { channel = DatagramChannel.open(); channel.setOption(StandardSocketOptions.SO_RCVBUF, 6553600); - channel.bind(new InetSocketAddress(19870)); + channel.bind(new InetSocketAddress(0)); } @Override @@ -191,7 +164,7 @@ public void terminate() { shouldTerminate = true; try { channel.close(); - } catch (Exception z) { + } catch (IOException z) { System.err.println("UDPThread: caught exception " + z.getClass().getName()); z.printStackTrace(System.err); // ignore @@ -201,7 +174,7 @@ public void terminate() { public void waitTestThreadStart() { try { threadStarted.await(); - } catch (Exception z) { + } catch (InterruptedException z) { z.printStackTrace(System.err); // ignore } diff --git a/test/jdk/java/nio/channels/FileChannel/BlockDeviceSize.java b/test/jdk/java/nio/channels/FileChannel/BlockDeviceSize.java index 84e0bf0dd3964..d4ee633c416be 100644 --- a/test/jdk/java/nio/channels/FileChannel/BlockDeviceSize.java +++ b/test/jdk/java/nio/channels/FileChannel/BlockDeviceSize.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,10 @@ * @bug 8054029 * @requires (os.family == "linux") * @summary FileChannel.size() should be equal to RandomAccessFile.size() and > 0 for block devs on Linux + * @comment The test must be launched with sudo or the block devices listed in + * the BLK_FNAMES array must be readable by the user running the test. * @library /test/lib + * @run main/manual BlockDeviceSize */ import java.io.RandomAccessFile; diff --git a/test/jdk/java/nio/channels/FileChannel/directio/DirectIOTest.java b/test/jdk/java/nio/channels/FileChannel/directio/DirectIOTest.java index 34f2bacc7dd86..b9dd309d0f76b 100644 --- a/test/jdk/java/nio/channels/FileChannel/directio/DirectIOTest.java +++ b/test/jdk/java/nio/channels/FileChannel/directio/DirectIOTest.java @@ -27,11 +27,13 @@ * @summary Test for ExtendedOpenOption.DIRECT flag * @requires (os.family == "linux" | os.family == "aix") * @library /test/lib + * @modules java.base/sun.nio.ch:+open java.base/java.io:+open * @build jdk.test.lib.Platform * @run main/native DirectIOTest */ import java.io.*; +import java.lang.reflect.Field; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.channels.*; @@ -47,10 +49,25 @@ public class DirectIOTest { private static final int BASE_SIZE = 4096; + private static final int TRIES = 3; + + public static int getFD(FileChannel channel) throws Exception { + Field fFdFd = channel.getClass().getDeclaredField("fd"); + fFdFd.setAccessible(true); + FileDescriptor fd = (FileDescriptor) fFdFd.get(channel); + + Field fFd = FileDescriptor.class.getDeclaredField("fd"); + fFd.setAccessible(true); + return fFd.getInt(fd); + } + + private static void testWrite(Path p, long blockSize) throws Exception { + try (FileChannel fc = FileChannel.open(p, + StandardOpenOption.READ, + StandardOpenOption.WRITE, + ExtendedOpenOption.DIRECT)) { + int fd = getFD(fc); - private static int testWrite(Path p, long blockSize) throws Exception { - try (FileChannel fc = FileChannel.open(p, StandardOpenOption.WRITE, - ExtendedOpenOption.DIRECT)) { int bs = (int)blockSize; int size = Math.max(BASE_SIZE, bs); int alignment = bs; @@ -60,22 +77,55 @@ private static int testWrite(Path p, long blockSize) throws Exception { for (int j = 0; j < size; j++) { src.put((byte)0); } - src.flip(); - fc.write(src); - return size; + + // If there is AV or other FS tracing software, it may cache the file + // contents on first access, even though we have asked for DIRECT here. + // Do several attempts to make test more resilient. + + for (int t = 0; t < TRIES; t++) { + flushFileCache(size, fd); + src.flip(); + fc.position(0); + fc.write(src); + if (!isFileInCache(size, fd)) { + return; + } + } + + throw new RuntimeException("DirectIO is not working properly with " + + "write. File still exists in cache!"); } } - private static int testRead(Path p, long blockSize) throws Exception { - try (FileChannel fc = FileChannel.open(p, ExtendedOpenOption.DIRECT)) { + private static void testRead(Path p, long blockSize) throws Exception { + try (FileChannel fc = FileChannel.open(p, + StandardOpenOption.READ, + ExtendedOpenOption.DIRECT)) { + int fd = getFD(fc); + int bs = (int)blockSize; int size = Math.max(BASE_SIZE, bs); int alignment = bs; ByteBuffer dest = ByteBuffer.allocateDirect(size + alignment - 1) .alignedSlice(alignment); assert dest.capacity() != 0; - fc.read(dest); - return size; + + // If there is AV or other FS tracing software, it may cache the file + // contents on first access, even though we have asked for DIRECT here. + // Do several attempts to make test more resilient. + + for (int t = 0; t < TRIES; t++) { + flushFileCache(size, fd); + dest.clear(); + fc.position(0); + fc.read(dest); + if (!isFileInCache(size, fd)) { + return; + } + } + + throw new RuntimeException("DirectIO is not working properly with " + + "read. File still exists in cache!"); } } @@ -84,12 +134,8 @@ public static Path createTempFile() throws IOException { Paths.get(System.getProperty("test.dir", ".")), "test", null); } - private static boolean isFileInCache(int size, Path p) { - String path = p.toString(); - return isFileInCache0(size, path); - } - - private static native boolean isFileInCache0(int size, String path); + private static native boolean flushFileCache(int size, int fd); + private static native boolean isFileInCache(int size, int fd); public static void main(String[] args) throws Exception { Path p = createTempFile(); @@ -98,16 +144,8 @@ public static void main(String[] args) throws Exception { System.loadLibrary("DirectIO"); try { - int size = testWrite(p, blockSize); - if (isFileInCache(size, p)) { - throw new RuntimeException("DirectIO is not working properly with " - + "write. File still exists in cache!"); - } - size = testRead(p, blockSize); - if (isFileInCache(size, p)) { - throw new RuntimeException("DirectIO is not working properly with " - + "read. File still exists in cache!"); - } + testWrite(p, blockSize); + testRead(p, blockSize); } finally { Files.delete(p); } diff --git a/test/jdk/java/nio/channels/FileChannel/directio/libDirectIO.c b/test/jdk/java/nio/channels/FileChannel/directio/libDirectIO.c index 4897500bf2d47..5eea51da4c7e3 100644 --- a/test/jdk/java/nio/channels/FileChannel/directio/libDirectIO.c +++ b/test/jdk/java/nio/channels/FileChannel/directio/libDirectIO.c @@ -45,13 +45,27 @@ static void ThrowException(JNIEnv *env, const char *name, const char *msg) { /* * Class: DirectIO - * Method: isFileInCache0 - * Signature: (ILjava/lang/String;)Z + * Method: flushFileCache + * Signature: (II;)V */ -JNIEXPORT jboolean Java_DirectIOTest_isFileInCache0(JNIEnv *env, +JNIEXPORT void Java_DirectIOTest_flushFileCache(JNIEnv *env, jclass cls, jint file_size, - jstring file_path) { + jint fd) { +#ifdef __linux__ + posix_fadvise(fd, 0, file_size, POSIX_FADV_DONTNEED); +#endif +} + +/* + * Class: DirectIO + * Method: isFileInCache + * Signature: (II;)Z + */ +JNIEXPORT jboolean Java_DirectIOTest_isFileInCache(JNIEnv *env, + jclass cls, + jint file_size, + jint fd) { void *f_mmap; #ifdef __linux__ unsigned char *f_seg; @@ -69,17 +83,10 @@ JNIEXPORT jboolean Java_DirectIOTest_isFileInCache0(JNIEnv *env, size_t index = (file_size + page_size - 1) /page_size; jboolean result = JNI_FALSE; - const char* path = (*env)->GetStringUTFChars(env, file_path, JNI_FALSE); - - int fd = open(path, O_RDWR); - - (*env)->ReleaseStringUTFChars(env, file_path, path); - f_mmap = mmap(0, file_size, PROT_NONE, MAP_SHARED, fd, 0); if (f_mmap == MAP_FAILED) { - close(fd); ThrowException(env, "java/io/IOException", - "test of whether file exists in cache failed"); + "test of whether file exists in cache failed: mmap failed"); } f_seg = malloc(index); if (f_seg != NULL) { @@ -95,9 +102,8 @@ JNIEXPORT jboolean Java_DirectIOTest_isFileInCache0(JNIEnv *env, free(f_seg); } else { ThrowException(env, "java/io/IOException", - "test of whether file exists in cache failed"); + "test of whether file exists in cache failed: malloc failed"); } - close(fd); munmap(f_mmap, file_size); return result; } diff --git a/test/jdk/java/nio/channels/SocketChannel/PeerReadsAfterAsyncClose.java b/test/jdk/java/nio/channels/SocketChannel/PeerReadsAfterAsyncClose.java new file mode 100644 index 0000000000000..8bfb71afbd1e9 --- /dev/null +++ b/test/jdk/java/nio/channels/SocketChannel/PeerReadsAfterAsyncClose.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8358764 + * @summary Test closing a socket while a thread is blocked in read. The connection + * should be closed gracefuly so that the peer reads EOF. + * @run junit PeerReadsAfterAsyncClose + */ + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.SocketChannel; +import java.util.Arrays; +import java.util.Objects; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Stream; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.*; + +class PeerReadsAfterAsyncClose { + + static Stream factories() { + return Stream.of(Executors.defaultThreadFactory()); + } + + /** + * Close SocketChannel while a thread is blocked reading from the channel's socket. + */ + @ParameterizedTest + @MethodSource("factories") + void testCloseDuringSocketChannelRead(ThreadFactory factory) throws Exception { + var loopback = InetAddress.getLoopbackAddress(); + try (var listener = new ServerSocket()) { + listener.bind(new InetSocketAddress(loopback, 0)); + + try (SocketChannel sc = SocketChannel.open(listener.getLocalSocketAddress()); + Socket peer = listener.accept()) { + + // start thread to read from channel + var cceThrown = new AtomicBoolean(); + Thread thread = factory.newThread(() -> { + try { + sc.read(ByteBuffer.allocate(1)); + fail(); + } catch (ClosedChannelException e) { + cceThrown.set(true); + } catch (Throwable e) { + e.printStackTrace(); + } + }); + thread.start(); + try { + // close SocketChannel when thread sampled in read() + onReach(thread, "sun.nio.ch.SocketChannelImpl.read", () -> { + try { + sc.close(); + } catch (IOException ignore) { } + }); + + // peer should read EOF + int n = peer.getInputStream().read(); + assertEquals(-1, n); + } finally { + thread.join(); + } + assertEquals(true, cceThrown.get(), "ClosedChannelException not thrown"); + } + } + } + + /** + * Close Socket while a thread is blocked reading from the socket. + */ + @ParameterizedTest + @MethodSource("factories") + void testCloseDuringSocketUntimedRead(ThreadFactory factory) throws Exception { + testCloseDuringSocketRead(factory, 0); + } + + /** + * Close Socket while a thread is blocked reading from the socket with a timeout. + */ + @ParameterizedTest + @MethodSource("factories") + void testCloseDuringSockeTimedRead(ThreadFactory factory) throws Exception { + testCloseDuringSocketRead(factory, 60_000); + } + + private void testCloseDuringSocketRead(ThreadFactory factory, int timeout) throws Exception { + var loopback = InetAddress.getLoopbackAddress(); + try (var listener = new ServerSocket()) { + listener.bind(new InetSocketAddress(loopback, 0)); + + try (Socket s = new Socket(loopback, listener.getLocalPort()); + Socket peer = listener.accept()) { + + // start thread to read from socket + var seThrown = new AtomicBoolean(); + Thread thread = factory.newThread(() -> { + try { + s.setSoTimeout(timeout); + s.getInputStream().read(); + fail(); + } catch (SocketException e) { + seThrown.set(true); + } catch (Throwable e) { + e.printStackTrace(); + } + }); + thread.start(); + try { + // close Socket when thread sampled in implRead + onReach(thread, "sun.nio.ch.NioSocketImpl.implRead", () -> { + try { + s.close(); + } catch (IOException ignore) { } + }); + + // peer should read EOF + int n = peer.getInputStream().read(); + assertEquals(-1, n); + } finally { + thread.join(); + } + assertEquals(true, seThrown.get(), "SocketException not thrown"); + } + } + } + + /** + * Runs the given action when the given target thread is sampled at the given + * location. The location takes the form "{@code c.m}" where + * {@code c} is the fully qualified class name and {@code m} is the method name. + */ + private void onReach(Thread target, String location, Runnable action) { + int index = location.lastIndexOf('.'); + String className = location.substring(0, index); + String methodName = location.substring(index + 1); + Thread tDaemon = new Thread(() -> { + try { + boolean found = false; + while (!found) { + found = contains(target.getStackTrace(), className, methodName); + if (!found) { + Thread.sleep(20); + } + } + action.run(); + } catch (Exception e) { + e.printStackTrace(); + } + }); + tDaemon.setDaemon(true); + tDaemon.start(); + } + + /** + * Returns true if the given stack trace contains an element for the given class + * and method name. + */ + private boolean contains(StackTraceElement[] stack, String className, String methodName) { + return Arrays.stream(stack) + .anyMatch(e -> className.equals(e.getClassName()) + && methodName.equals(e.getMethodName())); + } +} diff --git a/test/jdk/java/nio/charset/Charset/AvailableCharsetNames.java b/test/jdk/java/nio/charset/Charset/AvailableCharsetNames.java index 078700f2faeaa..0f20e95760522 100644 --- a/test/jdk/java/nio/charset/Charset/AvailableCharsetNames.java +++ b/test/jdk/java/nio/charset/Charset/AvailableCharsetNames.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,27 +25,26 @@ * @bug 4422044 * @summary Ensure that keys in available-charset map * are identical to canonical names + * @run junit AvailableCharsetNames */ -import java.io.*; -import java.nio.*; -import java.nio.charset.*; -import java.util.*; +import java.nio.charset.Charset; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; public class AvailableCharsetNames { - public static void main(String[] args) throws Exception { - Iterator charsetIterator = Charset.availableCharsets().keySet().iterator(); - while (charsetIterator.hasNext()) { - String charsetName = (String) charsetIterator.next(); + /** + * Test that the keys in Charset.availableCharsets() + * are equal to the associated Charset.name() value. + */ + @Test + public void canonicalNamesTest() { + for (String charsetName : Charset.availableCharsets().keySet()) { Charset charset = Charset.forName(charsetName); - if (!charset.name().equals(charsetName)) { - throw new Exception("Error: Charset name mismatch - expected " - + charsetName + ", got " + charset.name()); - } + assertEquals(charset.name(), charsetName, "Charset name mismatch"); } - } - } diff --git a/test/jdk/java/nio/charset/Charset/CharsetContainmentTest.java b/test/jdk/java/nio/charset/Charset/CharsetContainmentTest.java index 7e763f7750c25..4bc0538821981 100644 --- a/test/jdk/java/nio/charset/Charset/CharsetContainmentTest.java +++ b/test/jdk/java/nio/charset/Charset/CharsetContainmentTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,93 +25,116 @@ * @bug 4626545 4696726 * @summary Checks the inter containment relationships between NIO charsets * @modules jdk.charsets + * @run junit CharsetContainmentTest */ -import java.nio.charset.*; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; -public class CharsetContainmentTest { - static String[] encodings = - { "US-ASCII", "UTF-16", "UTF-16BE", "UTF-16LE", "UTF-8", - "windows-1252", "ISO-8859-1", "ISO-8859-2", "ISO-8859-3", - "ISO-8859-4", "ISO-8859-5", "ISO-8859-6", "ISO-8859-7", - "ISO-8859-8", "ISO-8859-9", "ISO-8859-13", "ISO-8859-15", "ISO-8859-16", - "ISO-2022-JP", "ISO-2022-KR", +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; - // Temporarily remove ISO-2022-CN-* charsets until full encoder/decoder - // support is added (4673614) - // "x-ISO-2022-CN-CNS", "x-ISO-2022-CN-GB", +import static org.junit.jupiter.api.Assertions.assertTrue; - "x-ISCII91", "GBK", "GB18030", "Big5", - "x-EUC-TW", "GB2312", "EUC-KR", "x-Johab", "Big5-HKSCS", - "x-MS950-HKSCS", "windows-1251", "windows-1253", "windows-1254", - "windows-1255", "windows-1256", "windows-1257", "windows-1258", - "x-mswin-936", "x-windows-949", "x-windows-950", "windows-31j", - "Shift_JIS", "EUC-JP", "KOI8-R", "TIS-620" - }; +public class CharsetContainmentTest { - static String[][] contains = { - { "US-ASCII"}, - encodings, - encodings, - encodings, - encodings, - {"US-ASCII", "windows-1252"}, - {"US-ASCII", "ISO-8859-1"}, - {"US-ASCII", "ISO-8859-2"}, - {"US-ASCII", "ISO-8859-3"}, - {"US-ASCII", "ISO-8859-4"}, - {"US-ASCII", "ISO-8859-5"}, - {"US-ASCII", "ISO-8859-6"}, - {"US-ASCII", "ISO-8859-7"}, - {"US-ASCII", "ISO-8859-8"}, - {"US-ASCII", "ISO-8859-9"}, - {"US-ASCII", "ISO-8859-13"}, - {"US-ASCII", "ISO-8859-15"}, - {"US-ASCII", "ISO-8859-16"}, - {"ISO-2022-JP"}, - {"ISO-2022-KR"}, - // Temporarily remove ISO-2022-CN-* charsets until full encoder/decoder - // support is added (4673614) - //{"x-ISO-2022-CN-CNS"}, - //{"x-ISO-2022-CN-GB"}, - {"US-ASCII", "x-ISCII91"}, - {"US-ASCII", "GBK"}, - encodings, - {"US-ASCII", "Big5"}, - {"US-ASCII", "x-EUC-TW"}, - {"US-ASCII", "GB2312"}, - {"US-ASCII", "EUC-KR"}, - {"US-ASCII", "x-Johab"}, - {"US-ASCII", "Big5-HKSCS", "Big5"}, - {"US-ASCII", "x-MS950-HKSCS", "x-windows-950"}, - {"US-ASCII", "windows-1251"}, - {"US-ASCII", "windows-1253"}, - {"US-ASCII", "windows-1254"}, - {"US-ASCII", "windows-1255"}, - {"US-ASCII", "windows-1256"}, - {"US-ASCII", "windows-1257"}, - {"US-ASCII", "windows-1258"}, - {"US-ASCII", "x-mswin-936"}, - {"US-ASCII", "x-windows-949"}, - {"US-ASCII", "x-windows-950"}, - {"US-ASCII", "windows-31j" }, - {"US-ASCII", "Shift_JIS"}, - {"US-ASCII", "EUC-JP"}, - {"US-ASCII", "KOI8-R"}, - {"US-ASCII", "TIS-620"}}; + /** + * Test that the charsets in 'encodings' contain the charsets + * inside 'contains'. Each value in 'encodings' is mapped to a String + * array in 'contains'. For example, the value, "TIS-620" in 'encodings' + * should contain "US-ASCII", "TIS-620". + */ + @ParameterizedTest + @MethodSource("charsets") + public void interContainmentTest(String containerName, String containedName) { + Charset container = Charset.forName(containerName); + Charset contained = Charset.forName(containedName); + assertTrue(container.contains(contained), + String.format("Charset: %s does not contain: %s", containerName, containedName)); + } + private static Stream charsets() { + String[] encodings = { + "US-ASCII", "UTF-16", "UTF-16BE", "UTF-16LE", "UTF-8", + "windows-1252", "ISO-8859-1", "ISO-8859-2", "ISO-8859-3", + "ISO-8859-4", "ISO-8859-5", "ISO-8859-6", "ISO-8859-7", + "ISO-8859-8", "ISO-8859-9", "ISO-8859-13", "ISO-8859-15", "ISO-8859-16", + "ISO-2022-JP", "ISO-2022-KR", + // Temporarily remove ISO-2022-CN-* charsets until full encoder/decoder + // support is added (4673614) + // "x-ISO-2022-CN-CNS", "x-ISO-2022-CN-GB", + "x-ISCII91", "GBK", "GB18030", "Big5", + "x-EUC-TW", "GB2312", "EUC-KR", "x-Johab", "Big5-HKSCS", + "x-MS950-HKSCS", "windows-1251", "windows-1253", "windows-1254", + "windows-1255", "windows-1256", "windows-1257", "windows-1258", + "x-mswin-936", "x-windows-949", "x-windows-950", "windows-31j", + "Shift_JIS", "EUC-JP", "KOI8-R", "TIS-620" + }; - public static void main(String[] args) throws Exception { + String[][] contains = { + {"US-ASCII"}, + encodings, + encodings, + encodings, + encodings, + {"US-ASCII", "windows-1252"}, + {"US-ASCII", "ISO-8859-1"}, + {"US-ASCII", "ISO-8859-2"}, + {"US-ASCII", "ISO-8859-3"}, + {"US-ASCII", "ISO-8859-4"}, + {"US-ASCII", "ISO-8859-5"}, + {"US-ASCII", "ISO-8859-6"}, + {"US-ASCII", "ISO-8859-7"}, + {"US-ASCII", "ISO-8859-8"}, + {"US-ASCII", "ISO-8859-9"}, + {"US-ASCII", "ISO-8859-13"}, + {"US-ASCII", "ISO-8859-15"}, + {"US-ASCII", "ISO-8859-16"}, + {"ISO-2022-JP"}, + {"ISO-2022-KR"}, + // Temporarily remove ISO-2022-CN-* charsets until full encoder/decoder + // support is added (4673614) + //{"x-ISO-2022-CN-CNS"}, + //{"x-ISO-2022-CN-GB"}, + {"US-ASCII", "x-ISCII91"}, + {"US-ASCII", "GBK"}, + encodings, + {"US-ASCII", "Big5"}, + {"US-ASCII", "x-EUC-TW"}, + {"US-ASCII", "GB2312"}, + {"US-ASCII", "EUC-KR"}, + {"US-ASCII", "x-Johab"}, + {"US-ASCII", "Big5-HKSCS", "Big5"}, + {"US-ASCII", "x-MS950-HKSCS", "x-windows-950"}, + {"US-ASCII", "windows-1251"}, + {"US-ASCII", "windows-1253"}, + {"US-ASCII", "windows-1254"}, + {"US-ASCII", "windows-1255"}, + {"US-ASCII", "windows-1256"}, + {"US-ASCII", "windows-1257"}, + {"US-ASCII", "windows-1258"}, + {"US-ASCII", "x-mswin-936"}, + {"US-ASCII", "x-windows-949"}, + {"US-ASCII", "x-windows-950"}, + {"US-ASCII", "windows-31j"}, + {"US-ASCII", "Shift_JIS"}, + {"US-ASCII", "EUC-JP"}, + {"US-ASCII", "KOI8-R"}, + {"US-ASCII", "TIS-620"}}; + + // Length of encodings and contains should always be equal + if (encodings.length != contains.length) { + throw new RuntimeException("Testing data is not set up correctly"); + } + List charsetList = new ArrayList(); for (int i = 0; i < encodings.length; i++) { - Charset c = Charset.forName(encodings[i]); - for (int j = 0 ; j < contains[i].length; j++) { - if (c.contains(Charset.forName(contains[i][j]))) - continue; - else { - throw new Exception ("Error: charset " + encodings[i] + - "doesn't contain " + contains[i][j]); - } - } + for (int j = 0 ; j < contains[i].length; j++) { + charsetList.add(Arguments.of(encodings[i], contains[i][j])); + } } + return charsetList.stream(); } } diff --git a/test/jdk/java/nio/charset/Charset/Contains.java b/test/jdk/java/nio/charset/Charset/Contains.java index 302809830887a..502c22873c3ad 100644 --- a/test/jdk/java/nio/charset/Charset/Contains.java +++ b/test/jdk/java/nio/charset/Charset/Contains.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,26 +23,139 @@ /* @test * @summary Unit test for charset containment - * @bug 6798572 + * @bug 6798572 8167252 * @modules jdk.charsets + * @run junit Contains */ -import java.nio.charset.*; +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class Contains { - static void ck(Charset cs1, Charset cs2, boolean cont) throws Exception { - if ((cs1.contains(cs2)) != cont) - throw new Exception("Wrong answer: " - + cs1.name() + " contains " + cs2.name()); - System.err.println(cs1.name() - + (cont ? " contains " : " does not contain ") - + cs2.name()); + /** + * Tests the containment of some charsets against themselves. + * This test takes both true and false for 'cont'. + */ + @ParameterizedTest + @MethodSource("charsets") + public void charsetsTest(Charset containerCs, Charset cs, boolean cont){ + shouldContain(containerCs, cs, cont); + } + + /** + * Tests UTF charsets with other charsets. In this case, each UTF charset + * should contain every single charset they are tested against. 'cont' is + * always true. + */ + @ParameterizedTest + @MethodSource("utfCharsets") + public void UTFCharsetsTest(Charset containerCs, Charset cs, boolean cont){ + shouldContain(containerCs, cs, cont); } - public static void main(String[] args) throws Exception { + /** + * Tests the assertion in the contains() method: "Every charset contains itself." + */ + @Test + public void containsSelfTest() { + for (var entry : Charset.availableCharsets().entrySet()) { + Charset charset = entry.getValue(); + boolean contains = charset.contains(charset); + assertTrue(contains, String.format("Charset(%s).contains(Charset(%s)) returns %s", + charset.name(), charset.name(), contains)); + } + } + + /** + * Helper method that checks if a charset should contain another charset. + */ + static void shouldContain(Charset containerCs, Charset cs, boolean cont){ + assertEquals((containerCs.contains(cs)), cont, String.format("%s %s %s", + containerCs.name(), (cont ? " contains " : " does not contain "), cs.name())); + } + private static Stream utfCharsets() { + String[] utfNames = { + "utf-16", + "utf-8", + "utf-16le", + "utf-16be", + "x-utf-16le-bom" + }; + + String[] charsetNames = { + "US-ASCII", + "UTF-8", + "UTF-16", + "UTF-16BE", + "UTF-16LE", + "x-UTF-16LE-BOM", + "GBK", + "GB18030", + "ISO-8859-1", + "ISO-8859-15", + "ISO-8859-2", + "ISO-8859-3", + "ISO-8859-4", + "ISO-8859-5", + "ISO-8859-6", + "ISO-8859-7", + "ISO-8859-8", + "ISO-8859-9", + "ISO-8859-13", + "JIS_X0201", + "x-JIS0208", + "JIS_X0212-1990", + "GB2312", + "EUC-KR", + "x-EUC-TW", + "EUC-JP", + "x-euc-jp-linux", + "KOI8-R", + "TIS-620", + "x-ISCII91", + "windows-1251", + "windows-1252", + "windows-1253", + "windows-1254", + "windows-1255", + "windows-1256", + "windows-1257", + "windows-1258", + "windows-932", + "x-mswin-936", + "x-windows-949", + "x-windows-950", + "windows-31j", + "Big5", + "Big5-HKSCS", + "x-MS950-HKSCS", + "ISO-2022-JP", + "ISO-2022-KR", + "x-ISO-2022-CN-CNS", + "x-ISO-2022-CN-GB", + "Big5-HKSCS", + "x-Johab", + "Shift_JIS" + }; + + // All charsets in utfNames should contain + // all charsets in charsetNames + return Arrays.stream(utfNames).flatMap(cs1 -> Arrays.stream(charsetNames) + .map(cs2 -> Arguments.of(Charset.forName(cs1), Charset.forName(cs2), true))); + } + + private static Stream charsets() { Charset us_ascii = Charset.forName("US-ASCII"); Charset iso_8859_1 = Charset.forName("ISO-8859-1"); Charset iso_8859_15 = Charset.forName("ISO-8859-15"); @@ -50,118 +163,48 @@ public static void main(String[] args) throws Exception { Charset utf_16be = Charset.forName("UTF-16BE"); Charset cp1252 = Charset.forName("CP1252"); - ck(us_ascii, us_ascii, true); - ck(us_ascii, iso_8859_1, false); - ck(us_ascii, iso_8859_15, false); - ck(us_ascii, utf_8, false); - ck(us_ascii, utf_16be, false); - ck(us_ascii, cp1252, false); - - ck(iso_8859_1, us_ascii, true); - ck(iso_8859_1, iso_8859_1, true); - ck(iso_8859_1, iso_8859_15, false); - ck(iso_8859_1, utf_8, false); - ck(iso_8859_1, utf_16be, false); - ck(iso_8859_1, cp1252, false); - - ck(iso_8859_15, us_ascii, true); - ck(iso_8859_15, iso_8859_1, false); - ck(iso_8859_15, iso_8859_15, true); - ck(iso_8859_15, utf_8, false); - ck(iso_8859_15, utf_16be, false); - ck(iso_8859_15, cp1252, false); - - ck(utf_8, us_ascii, true); - ck(utf_8, iso_8859_1, true); - ck(utf_8, iso_8859_15, true); - ck(utf_8, utf_8, true); - ck(utf_8, utf_16be, true); - ck(utf_8, cp1252, true); - - ck(utf_16be, us_ascii, true); - ck(utf_16be, iso_8859_1, true); - ck(utf_16be, iso_8859_15, true); - ck(utf_16be, utf_8, true); - ck(utf_16be, utf_16be, true); - ck(utf_16be, cp1252, true); - - ck(cp1252, us_ascii, true); - ck(cp1252, iso_8859_1, false); - ck(cp1252, iso_8859_15, false); - ck(cp1252, utf_8, false); - ck(cp1252, utf_16be, false); - ck(cp1252, cp1252, true); - - checkUTF(); + return Stream.of( + Arguments.of(us_ascii, us_ascii, true), + Arguments.of(us_ascii, iso_8859_1, false), + Arguments.of(us_ascii, iso_8859_15, false), + Arguments.of(us_ascii, utf_8, false), + Arguments.of(us_ascii, utf_16be, false), + Arguments.of(us_ascii, cp1252, false), + + Arguments.of(iso_8859_1, us_ascii, true), + Arguments.of(iso_8859_1, iso_8859_1, true), + Arguments.of(iso_8859_1, iso_8859_15, false), + Arguments.of(iso_8859_1, utf_8, false), + Arguments.of(iso_8859_1, utf_16be, false), + Arguments.of(iso_8859_1, cp1252, false), + + Arguments.of(iso_8859_15, us_ascii, true), + Arguments.of(iso_8859_15, iso_8859_1, false), + Arguments.of(iso_8859_15, iso_8859_15, true), + Arguments.of(iso_8859_15, utf_8, false), + Arguments.of(iso_8859_15, utf_16be, false), + Arguments.of(iso_8859_15, cp1252, false), + + Arguments.of(utf_8, us_ascii, true), + Arguments.of(utf_8, iso_8859_1, true), + Arguments.of(utf_8, iso_8859_15, true), + Arguments.of(utf_8, utf_8, true), + Arguments.of(utf_8, utf_16be, true), + Arguments.of(utf_8, cp1252, true), + + Arguments.of(utf_16be, us_ascii, true), + Arguments.of(utf_16be, iso_8859_1, true), + Arguments.of(utf_16be, iso_8859_15, true), + Arguments.of(utf_16be, utf_8, true), + Arguments.of(utf_16be, utf_16be, true), + Arguments.of(utf_16be, cp1252, true), + + Arguments.of(cp1252, us_ascii, true), + Arguments.of(cp1252, iso_8859_1, false), + Arguments.of(cp1252, iso_8859_15, false), + Arguments.of(cp1252, utf_8, false), + Arguments.of(cp1252, utf_16be, false), + Arguments.of(cp1252, cp1252, true) + ); } - - static void checkUTF() throws Exception { - for (String utfName : utfNames) - for (String csName : charsetNames) - ck(Charset.forName(utfName), - Charset.forName(csName), - true); - } - - static String[] utfNames = {"utf-16", - "utf-8", - "utf-16le", - "utf-16be", - "x-utf-16le-bom"}; - - static String[] charsetNames = { - "US-ASCII", - "UTF-8", - "UTF-16", - "UTF-16BE", - "UTF-16LE", - "x-UTF-16LE-BOM", - "GBK", - "GB18030", - "ISO-8859-1", - "ISO-8859-15", - "ISO-8859-2", - "ISO-8859-3", - "ISO-8859-4", - "ISO-8859-5", - "ISO-8859-6", - "ISO-8859-7", - "ISO-8859-8", - "ISO-8859-9", - "ISO-8859-13", - "JIS_X0201", - "x-JIS0208", - "JIS_X0212-1990", - "GB2312", - "EUC-KR", - "x-EUC-TW", - "EUC-JP", - "x-euc-jp-linux", - "KOI8-R", - "TIS-620", - "x-ISCII91", - "windows-1251", - "windows-1252", - "windows-1253", - "windows-1254", - "windows-1255", - "windows-1256", - "windows-1257", - "windows-1258", - "windows-932", - "x-mswin-936", - "x-windows-949", - "x-windows-950", - "windows-31j", - "Big5", - "Big5-HKSCS", - "x-MS950-HKSCS", - "ISO-2022-JP", - "ISO-2022-KR", - "x-ISO-2022-CN-CNS", - "x-ISO-2022-CN-GB", - "Big5-HKSCS", - "x-Johab", - "Shift_JIS" - }; } diff --git a/test/jdk/java/nio/charset/Charset/EmptyCharsetName.java b/test/jdk/java/nio/charset/Charset/EmptyCharsetName.java deleted file mode 100644 index 93797622553cb..0000000000000 --- a/test/jdk/java/nio/charset/Charset/EmptyCharsetName.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* @test - * @bug 4786884 - * @summary Ensure that passing the empty string to Charset methods and - * constructors causes an IllegalArgumentException to be thrown - */ - -import java.io.*; -import java.nio.*; -import java.nio.charset.*; - - -public class EmptyCharsetName { - - static abstract class Test { - - public abstract void go() throws Exception; - - Test() throws Exception { - try { - go(); - } catch (Exception x) { - if (x instanceof IllegalCharsetNameException) { - System.err.println("Thrown as expected: " + x); - return; - } - throw new Exception("Incorrect exception: " - + x.getClass().getName(), - x); - } - throw new Exception("No exception thrown"); - } - - } - - public static void main(String[] args) throws Exception { - - new Test() { - public void go() throws Exception { - Charset.forName(""); - }}; - new Test() { - public void go() throws Exception { - Charset.isSupported(""); - }}; - new Test() { - public void go() throws Exception { - new Charset("", new String[] { }) { - public CharsetDecoder newDecoder() { - return null; - } - public CharsetEncoder newEncoder() { - return null; - } - public boolean contains(Charset cs) { - return false; - } - }; - }}; - } - -} diff --git a/test/jdk/java/nio/charset/Charset/EncDec.java b/test/jdk/java/nio/charset/Charset/EncDec.java index b0d2ccd92ec1e..324418ad07403 100644 --- a/test/jdk/java/nio/charset/Charset/EncDec.java +++ b/test/jdk/java/nio/charset/Charset/EncDec.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,23 +23,42 @@ /* @test * @summary Unit test for encode/decode convenience methods + * @run junit EncDec */ +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.stream.Stream; -import java.nio.*; -import java.nio.charset.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; public class EncDec { - public static void main(String[] args) throws Exception { - String s = "Hello, world!"; + /** + * Test that the input String is the same after round tripping + * the Charset.encode() and Charset.decode() methods. + */ + @ParameterizedTest + @MethodSource("stringProvider") + public void roundTripTest(String pre) { ByteBuffer bb = ByteBuffer.allocate(100); - bb.put(Charset.forName("ISO-8859-15").encode(s)).flip(); - String t = Charset.forName("UTF-8").decode(bb).toString(); - System.err.println(t); - if (!t.equals(s)) - throw new Exception("Mismatch: " + s + " != " + t); + Charset preCs = Charset.forName("ISO-8859-15"); + if (!preCs.canEncode()) { + throw new RuntimeException("Error: Trying to test encode and " + + "decode methods on a charset that does not support encoding"); + } + bb.put(preCs.encode(pre)).flip(); + String post = Charset.forName("UTF-8").decode(bb).toString(); + assertEquals(pre, post, "Mismatch after encoding + decoding, :"); } + static Stream stringProvider() { + return Stream.of( + "Hello, world!", + "apple, banana, orange", + "car, truck, horse"); + } } diff --git a/test/jdk/java/nio/charset/Charset/IllegalCharsetName.java b/test/jdk/java/nio/charset/Charset/IllegalCharsetName.java index dd7c68c978225..9f2879a3ff09d 100644 --- a/test/jdk/java/nio/charset/Charset/IllegalCharsetName.java +++ b/test/jdk/java/nio/charset/Charset/IllegalCharsetName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,46 +22,71 @@ */ /* @test - * @bug 6330020 8184665 + * @bug 4786884 6330020 8184665 * @summary Ensure Charset.forName/isSupport throws the correct exception * if the charset names passed in are illegal. + * @run junit IllegalCharsetName */ -import java.nio.charset.*; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.IllegalCharsetNameException; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertThrows; public class IllegalCharsetName { - public static void main(String[] args) throws Exception { - String[] illegalNames = { - ".", - "_", - ":", - "-", - ".name", - "_name", - ":name", - "-name", - "name*name", - "name?name" - }; - for (int i = 0; i < illegalNames.length; i++) { - try { - Charset.forName(illegalNames[i]); - throw new Exception("Charset.forName(): No exception thrown"); - } catch (IllegalCharsetNameException x) { //expected - } - try { - Charset.isSupported(illegalNames[i]); - throw new Exception("Charset.isSupported(): No exception thrown"); - } catch (IllegalCharsetNameException x) { //expected - } - } + // Charset.forName and Charset.isSupported should throw an + // IllegalCharsetNameException when passed an illegal name + @ParameterizedTest + @MethodSource("illegalNames") + public void illegalCharsetsTest(String name) { + assertThrows(IllegalCharsetNameException.class, + () -> Charset.forName(name)); + assertThrows(IllegalCharsetNameException.class, + () -> Charset.isSupported(name)); + } + + // Charset.forName, Charset.isSupported, and the Charset constructor should + // throw an IllegalCharsetNameException when passed an empty name + @Test + public void emptyCharsetsTest() { + assertThrows(IllegalCharsetNameException.class, + () -> Charset.forName("")); + assertThrows(IllegalCharsetNameException.class, + () -> Charset.isSupported("")); + assertThrows(IllegalCharsetNameException.class, + () -> new Charset("", new String[]{}) { + @Override + public boolean contains(Charset cs) { + return false; + } + + @Override + public CharsetDecoder newDecoder() { + return null; + } - // Standard charsets may bypass alias checking during startup, test that - // they're all well-behaved as a sanity test - checkAliases(StandardCharsets.ISO_8859_1); - checkAliases(StandardCharsets.US_ASCII); - checkAliases(StandardCharsets.UTF_8); + @Override + public CharsetEncoder newEncoder() { + return null; + } + }); + } + + // Standard charsets may bypass alias checking during startup, test that + // they're all well-behaved as a sanity test + @Test + public void aliasTest() { + for (Charset cs : Charset.availableCharsets().values()) { + checkAliases(cs); + } } private static void checkAliases(Charset cs) { @@ -70,4 +95,19 @@ private static void checkAliases(Charset cs) { Charset.isSupported(alias); } } + + static Stream illegalNames() { + return Stream.of( + ".", + "_", + ":", + "-", + ".name", + "_name", + ":name", + "-name", + "name*name", + "name?name" + ); + } } diff --git a/test/jdk/java/nio/charset/Charset/NullCharsetName.java b/test/jdk/java/nio/charset/Charset/NullCharsetName.java index 591fbf14cdfe0..a6b3a952215cc 100644 --- a/test/jdk/java/nio/charset/Charset/NullCharsetName.java +++ b/test/jdk/java/nio/charset/Charset/NullCharsetName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2023 Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,29 +24,21 @@ /* @test * @bug 4448594 * @summary Ensure passing null to Charset.forName throws the correct exception + * @run junit NullCharsetName */ -import java.io.*; -import java.nio.*; -import java.nio.charset.*; -import java.util.*; +import java.nio.charset.Charset; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertThrows; public class NullCharsetName { - public static void main(String[] args) throws Exception { - try { - Charset.forName(null); - } catch (Exception x) { - if (x instanceof IllegalArgumentException) { - System.err.println("Thrown as expected: " + x); - return; - } - throw new Exception("Incorrect exception: " - + x.getClass().getName(), - x); - } - throw new Exception("No exception thrown"); + // Charset.forName should throw an exception when passed null + @Test + public void nullCharsetTest() { + assertThrows(IllegalArgumentException.class, + () -> Charset.forName(null)); } - } diff --git a/test/jdk/java/nio/charset/Charset/RegisteredCharsets.java b/test/jdk/java/nio/charset/Charset/RegisteredCharsets.java index 3f61e25737152..31a898580dada 100644 --- a/test/jdk/java/nio/charset/Charset/RegisteredCharsets.java +++ b/test/jdk/java/nio/charset/Charset/RegisteredCharsets.java @@ -26,1283 +26,1321 @@ 6911753 8071447 8186751 8242541 8301119 * @summary Check that registered charsets are actually registered * @modules jdk.charsets - * @run main RegisteredCharsets - * @run main/othervm -Djdk.charset.GB18030=2000 RegisteredCharsets + * @run junit RegisteredCharsets + * @run junit/othervm -Djdk.charset.GB18030=2000 RegisteredCharsets */ -import java.io.*; -import java.nio.*; -import java.nio.charset.*; -import java.util.*; +import java.nio.charset.Charset; +import java.nio.charset.UnsupportedCharsetException; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; public class RegisteredCharsets { - static String [] ianaRegistered = { - "US-ASCII", "UTF8", "Big5", "EUC-JP", - "GBK", "GB18030", "ISO-2022-KR", "ISO-2022-JP", - "GB2312", // IANA preferred name for "EUC-CN" - "ISO-8859-1", "ISO-8859-2", "ISO-8859-3", - "ISO-8859-4", "ISO-8859-5", "ISO-8859-6", - "ISO-8859-7", "ISO-8859-8", "ISO-8859-9", - "ISO-8859-13", "ISO-8859-15", "ISO-8859-16", - "windows-1251", - "windows-1252", "windows-1253", "windows-1254", - "windows-1255", "windows-1256", "windows-31j", - "Shift_JIS", "JIS_X0201", "JIS_X0212-1990", - "TIS-620", "Big5-HKSCS", - "ISO-2022-CN", - "IBM850", - "IBM852", - "IBM855", - "IBM857", - "IBM860", - "IBM861", - "IBM862", - "IBM863", - "IBM864", - "IBM865", - "IBM866", - "IBM868", - "IBM869", - "IBM437", - "IBM775", - "IBM037", - "IBM1026", - "IBM273", - "IBM277", - "IBM278", - "IBM280", - "IBM284", - "IBM285", - "IBM297", - "IBM420", - "IBM424", - "IBM500", - "IBM-Thai", - "IBM870", - "IBM871", - "IBM918", - "IBM1047", - "IBM01140", - "IBM01141", - "IBM01142", - "IBM01143", - "IBM01144", - "IBM01145", - "IBM01146", - "IBM01147", - "IBM01148", - "IBM01149", - "IBM00858" }; - - static String [] ianaUnRegistered = { - "x-EUC-TW", "x-ISCII91", - "x-windows-949", "x-windows-950", - "x-mswin-936", "x-JIS0208", - "x-ISO-8859-11", - "x-windows-874", - "x-PCK", "x-JISAutoDetect", "x-Johab", - "x-MS950-HKSCS", - "x-Big5-Solaris", - "x-ISO-2022-CN-CNS", - "x-ISO-2022-CN-GB", - "x-MacArabic", - "x-MacCentralEurope", - "x-MacCroatian", - "x-MacCyrillic", - "x-MacDingbat", - "x-MacGreek", - "x-MacHebrew", - "x-MacIceland", - "x-MacRoman", - "x-MacRomania", - "x-MacSymbol", - "x-MacThai", - "x-MacTurkish", - "x-MacUkraine", - "x-IBM942", - "x-IBM942C", - "x-IBM943", - "x-IBM943C", - "x-IBM948", - "x-IBM950", - "x-IBM930", - "x-IBM935", - "x-IBM937", - "x-IBM856", - "x-IBM874", - "x-IBM737", - "x-IBM1006", - "x-IBM1046", - "x-IBM1098", - "x-IBM1025", - "x-IBM1112", - "x-IBM1122", - "x-IBM1123", - "x-IBM1124", - "x-IBM1129", - "x-IBM1166", - "x-IBM875", - "x-IBM921", - "x-IBM922", - "x-IBM1097", - "x-IBM949", - "x-IBM949C", - "x-IBM939", - "x-IBM933", - "x-IBM1381", - "x-IBM1383", - "x-IBM970", - "x-IBM964", - "x-IBM33722", - "x-IBM1006", - "x-IBM1046", - "x-IBM1097", - "x-IBM1098", - "x-IBM1112", - "x-IBM1122", - "x-IBM1123", - "x-IBM1124", - "x-IBM33722", - "x-IBM737", - "x-IBM856", - "x-IBM874", - "x-IBM875", - "x-IBM922", - "x-IBM933", - "x-IBM964" }; + /** + * Tests that the aliases of the input String convert + * to the same Charset. This is validated by ensuring the input String + * and Charset.name() values are equal. + */ + @ParameterizedTest + @MethodSource("aliases") + public void testAliases(String canonicalName, String[] aliasNames) { + for (String aliasName : aliasNames) { + Charset cs = Charset.forName(aliasName); + assertEquals(cs.name(), canonicalName); + } + } - static void check(String csn, boolean testRegistered) throws Exception { - if (!Charset.forName(csn).isRegistered() && testRegistered) - throw new Exception("Not registered: " + csn); - else if (Charset.forName(csn).isRegistered() && !testRegistered) - throw new Exception("Registered: " + csn + "should be unregistered"); + /** + * Tests charsets to ensure that they are registered in the + * IANA Charset Registry. + */ + @ParameterizedTest + @MethodSource("ianaRegistered") + public void registeredTest(String cs) throws Exception { + check(cs, true); } - static void aliasCheck(String canonicalName, String[] aliasNames) throws Exception - { - for (int k = 0; k < aliasNames.length; k++ ) { - Charset cs = Charset.forName(aliasNames[k]); - if (!cs.name().equals(canonicalName)) { - throw new Exception("Unexpected Canonical name " + canonicalName); - } - } + /** + * Tests charsets to ensure that they are NOT registered in the + * IANA Charset Registry. + */ + @ParameterizedTest + @MethodSource("ianaUnregistered") + public void unregisteredTest(String cs) throws Exception { + check(cs, false); } - public static void main(String[] args) throws Exception { + /** + * Helper method which checks if a charset is registered and whether + * it should be. + */ + static void check(String csn, boolean testRegistered) throws Exception { + if (!Charset.forName(csn).isRegistered() && testRegistered) { + throw new Exception("Not registered: " + csn); + } + else if (Charset.forName(csn).isRegistered() && !testRegistered) { + throw new Exception("Registered: " + csn + "should be unregistered"); + } + } - for (int i = 0; i < ianaRegistered.length ; i++) - check(ianaRegistered[i], true); + // See https://www.iana.org/assignments/character-sets/character-sets.xhtml + private static Stream ianaRegistered() { + return Stream.of( + "US-ASCII", "UTF8", "Big5", "EUC-JP", + "GBK", "GB18030", "ISO-2022-KR", "ISO-2022-JP", + "GB2312", // IANA preferred name for "EUC-CN" + "ISO-8859-1", "ISO-8859-2", "ISO-8859-3", + "ISO-8859-4", "ISO-8859-5", "ISO-8859-6", + "ISO-8859-7", "ISO-8859-8", "ISO-8859-9", + "ISO-8859-13", "ISO-8859-15", "ISO-8859-16", + "windows-1251", + "windows-1252", "windows-1253", "windows-1254", + "windows-1255", "windows-1256", "windows-31j", + "Shift_JIS", "JIS_X0201", "JIS_X0212-1990", + "TIS-620", "Big5-HKSCS", + "ISO-2022-CN", + "IBM850", + "IBM852", + "IBM855", + "IBM857", + "IBM860", + "IBM861", + "IBM862", + "IBM863", + "IBM864", + "IBM865", + "IBM866", + "IBM868", + "IBM869", + "IBM437", + "IBM775", + "IBM037", + "IBM1026", + "IBM273", + "IBM277", + "IBM278", + "IBM280", + "IBM284", + "IBM285", + "IBM297", + "IBM420", + "IBM424", + "IBM500", + "IBM-Thai", + "IBM870", + "IBM871", + "IBM918", + "IBM1047", + "IBM01140", + "IBM01141", + "IBM01142", + "IBM01143", + "IBM01144", + "IBM01145", + "IBM01146", + "IBM01147", + "IBM01148", + "IBM01149", + "IBM00858" + ); + } - for (int i = 0; i < ianaUnRegistered.length ; i++) - check(ianaUnRegistered[i], false); + private static Stream ianaUnregistered() { + return Stream.of( + "x-EUC-TW", "x-ISCII91", + "x-windows-949", "x-windows-950", + "x-mswin-936", "x-JIS0208", + "x-ISO-8859-11", + "x-windows-874", + "x-PCK", "x-JISAutoDetect", "x-Johab", + "x-MS950-HKSCS", + "x-Big5-Solaris", + "x-ISO-2022-CN-CNS", + "x-ISO-2022-CN-GB", + "x-MacArabic", + "x-MacCentralEurope", + "x-MacCroatian", + "x-MacCyrillic", + "x-MacDingbat", + "x-MacGreek", + "x-MacHebrew", + "x-MacIceland", + "x-MacRoman", + "x-MacRomania", + "x-MacSymbol", + "x-MacThai", + "x-MacTurkish", + "x-MacUkraine", + "x-IBM942", + "x-IBM942C", + "x-IBM943", + "x-IBM943C", + "x-IBM948", + "x-IBM950", + "x-IBM930", + "x-IBM935", + "x-IBM937", + "x-IBM856", + "x-IBM874", + "x-IBM737", + "x-IBM1006", + "x-IBM1046", + "x-IBM1098", + "x-IBM1025", + "x-IBM1112", + "x-IBM1122", + "x-IBM1123", + "x-IBM1124", + "x-IBM1129", + "x-IBM1166", + "x-IBM875", + "x-IBM921", + "x-IBM922", + "x-IBM1097", + "x-IBM949", + "x-IBM949C", + "x-IBM939", + "x-IBM933", + "x-IBM1381", + "x-IBM1383", + "x-IBM970", + "x-IBM964", + "x-IBM33722", + "x-IBM1006", + "x-IBM1046", + "x-IBM1097", + "x-IBM1098", + "x-IBM1112", + "x-IBM1122", + "x-IBM1123", + "x-IBM1124", + "x-IBM33722", + "x-IBM737", + "x-IBM856", + "x-IBM874", + "x-IBM875", + "x-IBM922", + "x-IBM933", + "x-IBM964" + ); + } + private static Stream aliases() { // Check aliases registered with IANA for all NIO supported // Charset implementations. // // The aliases below are in sync with the IANA registered charset // document at: http://www.iana.org/assignments/character-sets // Last updated 7/25/2002 - - aliasCheck("US-ASCII", - new String[] {"ascii","ANSI_X3.4-1968", - "iso-ir-6","ANSI_X3.4-1986", "ISO_646.irv:1991", - "ASCII", "ISO646-US","us","IBM367","cp367", - "csASCII", "default"}); - - aliasCheck("UTF-8", - new String[] { - "UTF8", - "unicode-1-1-utf-8" - }); - - aliasCheck("UTF-16", - new String[] { - "UTF_16", - "utf16" - }); - - aliasCheck("UTF-16BE", - new String[] { - "UTF_16BE", - "ISO-10646-UCS-2", - "X-UTF-16BE", - "UnicodeBigUnmarked" - }); - - aliasCheck("UTF-16LE", - new String[] { - "UTF_16LE", - "X-UTF-16LE", - "UnicodeLittleUnmarked" - }); - - aliasCheck("Big5", - new String[] { - "csBig5" - }); - - aliasCheck("Big5-HKSCS", - new String[] { - "Big5_HKSCS", - "big5hk", - "big5-hkscs", - "big5hkscs" - }); - - aliasCheck("x-MS950-HKSCS", - new String[] { - "MS950_HKSCS" - }); - - aliasCheck("GB18030", - "2000".equals(System.getProperty("jdk.charset.GB18030")) ? - new String[] { - "gb18030-2000" - } : - new String[] { - "gb18030-2022" - }); - - aliasCheck("ISO-2022-KR", new String[] {"csISO2022KR"}); - aliasCheck("ISO-2022-JP", new String[] {"csISO2022JP"}); - aliasCheck("EUC-KR", new String[] { "csEUCKR"}); - aliasCheck("ISO-8859-1", - new String[] { - - // IANA aliases - "iso-ir-100", - "ISO_8859-1", - "latin1", - "l1", - "IBM819", - "cp819", - "csISOLatin1", - - // JDK historical aliases - "819", - "IBM-819", - "ISO8859_1", - "ISO_8859-1:1987", - "ISO_8859_1", - "8859_1", - "ISO8859-1", - - }); - - aliasCheck("ISO-8859-2", - new String[] { - "ISO_8859-2", - "ISO_8859-2:1987", - "iso-ir-101", - "latin2", - "l2", - "8859_2", - "iso_8859-2:1987", - "iso8859-2", - "ibm912", - "ibm-912", - "cp912", - "912", - "csISOLatin2"}); - - aliasCheck("ISO-8859-3", - new String[] {"latin3", - "ISO_8859-3:1988", - "iso-ir-109", - "l3", - "8859_3", - "iso_8859-3:1988", - "iso8859-3", - "ibm913", - "ibm-913", - "cp913", - "913", - "csISOLatin3"}); - - aliasCheck("ISO-8859-4", - new String[] {"csISOLatin4", - "ISO_8859-4:1988", - "iso-ir-110", - "latin4", - "8859_4", - "iso_8859-4:1988", - "iso8859-4", - "ibm914", - "ibm-914", - "cp914", - "914", - "l4"}); - - aliasCheck("ISO-8859-5", - new String[] { - "iso8859_5", // JDK historical - "8859_5", - "iso-ir-144", - "ISO_8859-5", - "ISO_8859-5:1988", - "ISO8859-5", - "cyrillic", - "ibm915", - "ibm-915", - "915", - "cp915", - "csISOLatinCyrillic" - }); - - aliasCheck("ISO-8859-6", - new String[] {"ISO_8859-6:1987", - "iso-ir-127", - "ISO_8859-6", - "ECMA-114", - "ASMO-708", - "arabic", - "8859_6", - "iso_8859-6:1987", - "iso8859-6", - "ibm1089", - "ibm-1089", - "cp1089", - "1089", - "csISOLatinArabic"}); - - aliasCheck("ISO-8859-7", - new String[] {"ISO_8859-7:1987", - "iso-ir-126", - "ISO_8859-7", - "ELOT_928", - "ECMA-118", - "greek", - "greek8", - "8859_7", - "iso_8859-7:1987", - "iso8859-7", - "ibm813", - "ibm-813", - "cp813", - "813", - "csISOLatinGreek"}); - - aliasCheck("ISO-8859-8", - new String[] { - "ISO_8859-8:1988", - "iso-ir-138", - "ISO_8859-8", - "hebrew", - "8859_8", - "iso_8859-8:1988", - "iso8859-8", - "ibm916", - "ibm-916", - "cp916", - "916", - "csISOLatinHebrew"}); - - aliasCheck("ISO-8859-9", - new String[] {"ISO_8859-9:1989", - "iso-ir-148", - "ISO_8859-9", - "latin5", - "l5", - "8859_9", - "iso8859-9", - "ibm920", - "ibm-920", - "cp920", - "920", - "csISOLatin5"}); - - aliasCheck("ISO-8859-13", - new String[] { - "iso8859_13", // JDK historical - "iso_8859-13", - "8859_13", - "ISO8859-13" - }); - - aliasCheck("ISO-8859-15", - new String[] { - // IANA alias - "ISO_8859-15", - "Latin-9", - "csISO885915", - // JDK historical aliases - "8859_15", - "ISO-8859-15", - "ISO_8859-15", - "ISO8859-15", - "ISO8859_15", - "IBM923", - "IBM-923", - "cp923", - "923", - "LATIN0", - "LATIN9", - "L9", - "csISOlatin0", - "csISOlatin9", - "ISO8859_15_FDIS" - }); - - aliasCheck("ISO-8859-16", - new String[] { - "iso-ir-226", - "ISO_8859-16:2001", - "ISO_8859-16", - "ISO8859_16", - "latin10", - "l10", - "csISO885916" - }); - - aliasCheck("JIS_X0212-1990", - new String[] { - "iso-ir-159", - "csISO159JISX02121990"}); - - aliasCheck("JIS_X0201", - new String[]{ - "X0201", - "csHalfWidthKatakana"}); - - aliasCheck("KOI8-R", - new String[] { - "KOI8_R", - "csKOI8R"}); - - aliasCheck("GBK", - new String[] { - "windows-936"}); - - aliasCheck("Shift_JIS", - new String[] { - "MS_Kanji", - "csShiftJIS"}); - - aliasCheck("EUC-JP", - new String[] { - "Extended_UNIX_Code_Packed_Format_for_Japanese", - "csEUCPkdFmtJapanese"}); - - aliasCheck("Big5", new String[] {"csBig5"}); - - aliasCheck("windows-31j", new String[] {"csWindows31J"}); - - aliasCheck("x-iso-8859-11", - new String[] { "iso-8859-11", "iso8859_11" }); - - aliasCheck("windows-1250", - new String[] { - "cp1250", - "cp5346" - }); - - aliasCheck("windows-1251", - new String[] { - "cp1251", - "cp5347", - "ansi-1251" - }); - - aliasCheck("windows-1252", - new String[] { - "cp1252", - "cp5348" - }); - - aliasCheck("windows-1253", - new String[] { - "cp1253", - "cp5349" - }); - - aliasCheck("windows-1254", - new String[] { - "cp1254", - "cp5350" - }); - - aliasCheck("windows-1255", - new String[] { - "cp1255" - }); - - aliasCheck("windows-1256", - new String[] { - "cp1256" - }); - - aliasCheck("windows-1257", - new String[] { - "cp1257", - "cp5353" - }); - - aliasCheck("windows-1258", - new String[] { - "cp1258" - }); - - aliasCheck("x-windows-874", - new String[] { - "ms874", "ms-874", "windows-874" }); - - aliasCheck("GB2312", - new String[] { - "x-EUC-CN", - "gb2312-80", - "gb2312-1980", - "euc-cn", - "euccn" }); - - aliasCheck("x-IBM942" , - new String[] { - "cp942", // JDK historical - "ibm942", - "ibm-942", - "942" - }); - - aliasCheck("x-IBM942C" , - new String[] { - "cp942C", // JDK historical - "ibm942C", - "ibm-942C", - "942C" - } ); - - aliasCheck("x-IBM943" , - new String[] { - "cp943", // JDK historical - "ibm943", - "ibm-943", - "943" - } ); - - aliasCheck("x-IBM943C" , - new String[] { - "cp943c", // JDK historical - "ibm943C", - "ibm-943C", - "943C" - } ); - - aliasCheck("x-IBM948" , - new String[] { - "cp948", // JDK historical - "ibm948", - "ibm-948", - "948" - } ); - - aliasCheck("x-IBM950" , - new String[] { - "cp950", // JDK historical - "ibm950", - "ibm-950", - "950" - } ); - - aliasCheck("x-IBM930" , - new String[] { - "cp930", // JDK historical - "ibm930", - "ibm-930", - "930" - } ); - - aliasCheck("x-IBM935" , - new String[] { - "cp935", // JDK historical - "ibm935", - "ibm-935", - "935" - } ); - - aliasCheck("x-IBM937" , - new String[] { - "cp937", // JDK historical - "ibm937", - "ibm-937", - "937" - } ); - - aliasCheck("IBM850" , - new String[] { - "cp850", // JDK historical - "ibm-850", - "ibm850", - "850", - "cspc850multilingual" - } ); - - aliasCheck("IBM852" , - new String[] { - "cp852", // JDK historical - "ibm852", - "ibm-852", - "852", - "csPCp852" - } ); - - aliasCheck("IBM855" , - new String[] { - "cp855", // JDK historical - "ibm-855", - "ibm855", - "855", - "cspcp855" - } ); - - aliasCheck("x-IBM856" , - new String[] { - "cp856", // JDK historical - "ibm-856", - "ibm856", - "856" - } ); - - aliasCheck("IBM857" , - new String[] { - "cp857", // JDK historical - "ibm857", - "ibm-857", - "857", - "csIBM857" - } ); - - aliasCheck("IBM860" , - new String[] { - "cp860", // JDK historical - "ibm860", - "ibm-860", - "860", - "csIBM860" - } ); - aliasCheck("IBM861" , - new String[] { - "cp861", // JDK historical - "ibm861", - "ibm-861", - "861", - "csIBM861" - } ); - - aliasCheck("IBM862" , - new String[] { - "cp862", // JDK historical - "ibm862", - "ibm-862", - "862", - "csIBM862" - } ); - - aliasCheck("IBM863" , - new String[] { - "cp863", // JDK historical - "ibm863", - "ibm-863", - "863", - "csIBM863" - } ); - - aliasCheck("IBM864" , - new String[] { - "cp864", // JDK historical - "ibm864", - "ibm-864", - "864", - "csIBM864" - } ); - - aliasCheck("IBM865" , - new String[] { - "cp865", // JDK historical - "ibm865", - "ibm-865", - "865", - "csIBM865" - } ); - - aliasCheck("IBM866" , new String[] { - "cp866", // JDK historical - "ibm866", - "ibm-866", - "866", - "csIBM866" - } ); - aliasCheck("IBM868" , - new String[] { - "cp868", // JDK historical - "ibm868", - "ibm-868", - "868", - "cp-ar", - "csIBM868" - } ); - - aliasCheck("IBM869" , - new String[] { - "cp869", // JDK historical - "ibm869", - "ibm-869", - "869", - "cp-gr", - "csIBM869" - } ); - - aliasCheck("IBM437" , - new String[] { - "cp437", // JDK historical - "ibm437", - "ibm-437", - "437", - "cspc8codepage437", - "windows-437" - } ); - - aliasCheck("x-IBM874" , - new String[] { - "cp874", // JDK historical - "ibm874", - "ibm-874", - "874" - } ); - aliasCheck("x-IBM737" , - new String[] { - "cp737", // JDK historical - "ibm737", - "ibm-737", - "737" - } ); - - aliasCheck("IBM775" , - new String[] { - "cp775", // JDK historical - "ibm775", - "ibm-775", - "775" - } ); - - aliasCheck("x-IBM921" , - new String[] { - "cp921", // JDK historical - "ibm921", - "ibm-921", - "921" - } ); - - aliasCheck("x-IBM1006" , - new String[] { - "cp1006", // JDK historical - "ibm1006", - "ibm-1006", - "1006" - } ); - - aliasCheck("x-IBM1046" , - new String[] { - "cp1046", // JDK historical - "ibm1046", - "ibm-1046", - "1046" - } ); - - aliasCheck("IBM1047" , - new String[] { - "cp1047", // JDK historical - "ibm-1047", - "1047" - } ); - - aliasCheck("x-IBM1098" , - new String[] { - "cp1098", // JDK historical - "ibm1098", - "ibm-1098", - "1098", - } ); - - aliasCheck("IBM037" , - new String[] { - "cp037", // JDK historical - "ibm037", - "csIBM037", - "cs-ebcdic-cp-us", - "cs-ebcdic-cp-ca", - "cs-ebcdic-cp-wt", - "cs-ebcdic-cp-nl", - "ibm-037", - "ibm-37", - "cpibm37", - "037" - } ); - - aliasCheck("x-IBM1025" , - new String[] { - "cp1025", // JDK historical - "ibm1025", - "ibm-1025", - "1025" - } ); - - aliasCheck("IBM1026" , - new String[] { - "cp1026", // JDK historical - "ibm1026", - "ibm-1026", - "1026" - } ); - - aliasCheck("x-IBM1112" , - new String[] { - "cp1112", // JDK historical - "ibm1112", - "ibm-1112", - "1112" - } ); - - aliasCheck("x-IBM1122" , - new String[] { - "cp1122", // JDK historical - "ibm1122", - "ibm-1122", - "1122" - } ); - - aliasCheck("x-IBM1123" , - new String[] { - "cp1123", // JDK historical - "ibm1123", - "ibm-1123", - "1123" - } ); - - aliasCheck("x-IBM1124" , - new String[] { - "cp1124", // JDK historical - "ibm1124", - "ibm-1124", - "1124" - } ); - - aliasCheck("x-IBM1129" , - new String[] { - "cp1129", // JDK historical - "ibm1129", - "ibm-1129", - "1129" - } ); - - aliasCheck("x-IBM1166" , - new String[] { - "cp1166", // JDK historical - "ibm1166", - "ibm-1166", - "1166" - } ); - - aliasCheck("IBM273" , - new String[] { - "cp273", // JDK historical - "ibm273", - "ibm-273", - "273" - } ); - - aliasCheck("IBM277" , - new String[] { - "cp277", // JDK historical - "ibm277", - "ibm-277", - "277" - } ); - - aliasCheck("IBM278" , - new String[] { - "cp278", // JDK historical - "ibm278", - "ibm-278", - "278", - "ebcdic-sv", - "ebcdic-cp-se", - "csIBM278" - } ); - - aliasCheck("IBM280" , - new String[] { - "cp280", // JDK historical - "ibm280", - "ibm-280", - "280" - } ); - - aliasCheck("IBM284" , - new String[] { - "cp284", // JDK historical - "ibm284", - "ibm-284", - "284", - "csIBM284", - "cpibm284" - } ); - - aliasCheck("IBM285" , - new String[] { - "cp285", // JDK historical - "ibm285", - "ibm-285", - "285", - "ebcdic-cp-gb", - "ebcdic-gb", - "csIBM285", - "cpibm285" - } ); - - aliasCheck("IBM297" , - new String[] { - "cp297", // JDK historical - "ibm297", - "ibm-297", - "297", - "ebcdic-cp-fr", - "cpibm297", - "csIBM297", - } ); - - aliasCheck("IBM420" , - new String[] { - "cp420", // JDK historical - "ibm420", - "ibm-420", - "ebcdic-cp-ar1", - "420", - "csIBM420" - } ); - - aliasCheck("IBM424" , - new String[] { - "cp424", // JDK historical - "ibm424", - "ibm-424", - "424", - "ebcdic-cp-he", - "csIBM424" - } ); - - aliasCheck("IBM500" , - new String[] { - "cp500", // JDK historical - "ibm500", - "ibm-500", - "500", - "ebcdic-cp-ch", - "ebcdic-cp-bh", - "csIBM500" - } ); - - aliasCheck("IBM-Thai" , - new String[] { - "cp838", // JDK historical - "ibm838", - "ibm-838", - "ibm838", - "838" - } ); - - aliasCheck("IBM870" , - new String[] { - "cp870", // JDK historical - "ibm870", - "ibm-870", - "870", - "ebcdic-cp-roece", - "ebcdic-cp-yu", - "csIBM870" - } ); - - aliasCheck("IBM871" , - new String[] { - "cp871", // JDK historical - "ibm871", - "ibm-871", - "871", - "ebcdic-cp-is", - "csIBM871" - } ); - - aliasCheck("x-IBM875" , - new String[] { - "cp875", // JDK historical - "ibm875", - "ibm-875", - "875" - } ); - - aliasCheck("IBM918" , - new String[] { - "cp918", // JDK historical - "ibm-918", - "918", - "ebcdic-cp-ar2" - } ); - - aliasCheck("x-IBM922" , - new String[] { - "cp922", // JDK historical - "ibm922", - "ibm-922", - "922" - } ); - - aliasCheck("x-IBM1097" , - new String[] { - "cp1097", // JDK historical - "ibm1097", - "ibm-1097", - "1097" - } ); - - aliasCheck("x-IBM949" , - new String[] { - "cp949", // JDK historical - "ibm949", - "ibm-949", - "949" - } ); - - aliasCheck("x-IBM949C" , - new String[] { - "cp949C", // JDK historical - "ibm949C", - "ibm-949C", - "949C" - } ); - - aliasCheck("x-IBM939" , - new String[] { - "cp939", // JDK historical - "ibm939", - "ibm-939", - "939" - } ); - - aliasCheck("x-IBM933" , - new String[] { - "cp933", // JDK historical - "ibm933", - "ibm-933", - "933" - } ); - - aliasCheck("x-IBM1381" , - new String[] { - "cp1381", // JDK historical - "ibm1381", - "ibm-1381", - "1381" - } ); - - aliasCheck("x-IBM1383" , - new String[] { - "cp1383", // JDK historical - "ibm1383", - "ibm-1383", - "1383" - } ); - - aliasCheck("x-IBM970" , - new String[] { - "cp970", // JDK historical - "ibm970", - "ibm-970", - "ibm-eucKR", - "970" - } ); - - aliasCheck("x-IBM964" , - new String[] { - "cp964", // JDK historical - "ibm964", - "ibm-964", - "964" - } ); - - aliasCheck("x-IBM33722" , - new String[] { - "cp33722", // JDK historical - "ibm33722", - "ibm-33722", - "ibm-5050", // from IBM alias list - "ibm-33722_vascii_vpua", // from IBM alias list - "33722" - } ); - - aliasCheck("IBM01140" , - new String[] { - "cp1140", // JDK historical - "ccsid01140", - "cp01140", - // "ebcdic-us-037+euro" - } ); - - aliasCheck("IBM01141" , - new String[] { - "cp1141", // JDK historical - "ccsid01141", - "cp01141", - // "ebcdic-de-273+euro" - } ); - - aliasCheck("IBM01142" , - new String[] { - "cp1142", // JDK historical - "ccsid01142", - "cp01142", - // "ebcdic-no-277+euro", - // "ebcdic-dk-277+euro" - } ); - - aliasCheck("IBM01143" , - new String[] { - "cp1143", // JDK historical - "ccsid01143", - "cp01143", - // "ebcdic-fi-278+euro", - // "ebcdic-se-278+euro" - } ); - - aliasCheck("IBM01144" , - new String[] { - "cp1144", // JDK historical - "ccsid01144", - "cp01144", - // "ebcdic-it-280+euro" - } ); - - aliasCheck("IBM01145" , - new String[] { - "cp1145", // JDK historical - "ccsid01145", - "cp01145", - // "ebcdic-es-284+euro" - } ); - - aliasCheck("IBM01146" , - new String[] { - "cp1146", // JDK historical - "ccsid01146", - "cp01146", - // "ebcdic-gb-285+euro" - } ); - - aliasCheck("IBM01147" , - new String[] { - "cp1147", // JDK historical - "ccsid01147", - "cp01147", - // "ebcdic-fr-277+euro" - } ); - - aliasCheck("IBM01148" , - new String[] { - "cp1148", // JDK historical - "ccsid01148", - "cp01148", - // "ebcdic-international-500+euro" - } ); - - aliasCheck("IBM01149" , - new String[] { - "cp1149", // JDK historical - "ccsid01149", - "cp01149", - // "ebcdic-s-871+euro" - } ); - - aliasCheck("IBM00858" , - new String[] { - "cp858", // JDK historical - "ccsid00858", - "cp00858", - // "PC-Multilingual-850+euro" - } ); - - aliasCheck("x-MacRoman", - new String[] { - "MacRoman" // JDK historical - }); - - aliasCheck("x-MacCentralEurope", - new String[] { - "MacCentralEurope" // JDK historical - }); - - aliasCheck("x-MacCroatian", - new String[] { - "MacCroatian" // JDK historical - }); - - - aliasCheck("x-MacCroatian", - new String[] { - "MacCroatian" // JDK historical - }); - - - aliasCheck("x-MacGreek", - new String[] { - "MacGreek" // JDK historical - }); - - aliasCheck("x-MacCyrillic", - new String[] { - "MacCyrillic" // JDK historical - }); - - aliasCheck("x-MacUkraine", - new String[] { - "MacUkraine" // JDK historical - }); - - aliasCheck("x-MacTurkish", - new String[] { - "MacTurkish" // JDK historical - }); - - aliasCheck("x-MacArabic", - new String[] { - "MacArabic" // JDK historical - }); - - aliasCheck("x-MacHebrew", - new String[] { - "MacHebrew" // JDK historical - }); - - aliasCheck("x-MacIceland", - new String[] { - "MacIceland" // JDK historical - }); - - aliasCheck("x-MacRomania", - new String[] { - "MacRomania" // JDK historical - }); - - aliasCheck("x-MacThai", - new String[] { - "MacThai" // JDK historical - }); - - aliasCheck("x-MacSymbol", - new String[] { - "MacSymbol" // JDK historical - }); - - aliasCheck("x-MacDingbat", - new String[] { - "MacDingbat" // JDK historical - }); + return Stream.of( + Arguments.of("US-ASCII", + new String[] {"ascii","ANSI_X3.4-1968", + "iso-ir-6","ANSI_X3.4-1986", "ISO_646.irv:1991", + "ASCII", "ISO646-US","us","IBM367","cp367", + "csASCII", "default"}), + + Arguments.of("UTF-8", + new String[] { + "UTF8", + "unicode-1-1-utf-8" + }), + + Arguments.of("UTF-16", + new String[] { + "UTF_16", + "utf16" + }), + + Arguments.of("UTF-16BE", + new String[] { + "UTF_16BE", + "ISO-10646-UCS-2", + "X-UTF-16BE", + "UnicodeBigUnmarked" + }), + + Arguments.of("UTF-16LE", + new String[] { + "UTF_16LE", + "X-UTF-16LE", + "UnicodeLittleUnmarked" + }), + + Arguments.of("Big5", + new String[] { + "csBig5" + }), + + Arguments.of("Big5-HKSCS", + new String[] { + "Big5_HKSCS", + "big5hk", + "big5-hkscs", + "big5hkscs" + }), + + Arguments.of("x-MS950-HKSCS", + new String[] { + "MS950_HKSCS" + }), + + Arguments.of("GB18030", + "2000".equals(System.getProperty("jdk.charset.GB18030")) ? + new String[] { + "gb18030-2000" + } : + new String[] { + "gb18030-2022" + }), + + Arguments.of("ISO-2022-KR", new String[] {"csISO2022KR"}), + Arguments.of("ISO-2022-JP", new String[] {"csISO2022JP"}), + Arguments.of("EUC-KR", new String[] { "csEUCKR"}), + Arguments.of("ISO-8859-1", + new String[] { + + // IANA aliases + "iso-ir-100", + "ISO_8859-1", + "latin1", + "l1", + "IBM819", + "cp819", + "csISOLatin1", + + // JDK historical aliases + "819", + "IBM-819", + "ISO8859_1", + "ISO_8859-1:1987", + "ISO_8859_1", + "8859_1", + "ISO8859-1", + + }), + + Arguments.of("ISO-8859-2", + new String[] { + "ISO_8859-2", + "ISO_8859-2:1987", + "iso-ir-101", + "latin2", + "l2", + "8859_2", + "iso_8859-2:1987", + "iso8859-2", + "ibm912", + "ibm-912", + "cp912", + "912", + "csISOLatin2"}), + + Arguments.of("ISO-8859-3", + new String[] {"latin3", + "ISO_8859-3:1988", + "iso-ir-109", + "l3", + "8859_3", + "iso_8859-3:1988", + "iso8859-3", + "ibm913", + "ibm-913", + "cp913", + "913", + "csISOLatin3"}), + + Arguments.of("ISO-8859-4", + new String[] {"csISOLatin4", + "ISO_8859-4:1988", + "iso-ir-110", + "latin4", + "8859_4", + "iso_8859-4:1988", + "iso8859-4", + "ibm914", + "ibm-914", + "cp914", + "914", + "l4"}), + + Arguments.of("ISO-8859-5", + new String[] { + "iso8859_5", // JDK historical + "8859_5", + "iso-ir-144", + "ISO_8859-5", + "ISO_8859-5:1988", + "ISO8859-5", + "cyrillic", + "ibm915", + "ibm-915", + "915", + "cp915", + "csISOLatinCyrillic" + }), + + Arguments.of("ISO-8859-6", + new String[] {"ISO_8859-6:1987", + "iso-ir-127", + "ISO_8859-6", + "ECMA-114", + "ASMO-708", + "arabic", + "8859_6", + "iso_8859-6:1987", + "iso8859-6", + "ibm1089", + "ibm-1089", + "cp1089", + "1089", + "csISOLatinArabic"}), + + Arguments.of("ISO-8859-7", + new String[] {"ISO_8859-7:1987", + "iso-ir-126", + "ISO_8859-7", + "ELOT_928", + "ECMA-118", + "greek", + "greek8", + "8859_7", + "iso_8859-7:1987", + "iso8859-7", + "ibm813", + "ibm-813", + "cp813", + "813", + "csISOLatinGreek"}), + + Arguments.of("ISO-8859-8", + new String[] { + "ISO_8859-8:1988", + "iso-ir-138", + "ISO_8859-8", + "hebrew", + "8859_8", + "iso_8859-8:1988", + "iso8859-8", + "ibm916", + "ibm-916", + "cp916", + "916", + "csISOLatinHebrew"}), + + Arguments.of("ISO-8859-9", + new String[] {"ISO_8859-9:1989", + "iso-ir-148", + "ISO_8859-9", + "latin5", + "l5", + "8859_9", + "iso8859-9", + "ibm920", + "ibm-920", + "cp920", + "920", + "csISOLatin5"}), + + Arguments.of("ISO-8859-13", + new String[] { + "iso8859_13", // JDK historical + "iso_8859-13", + "8859_13", + "ISO8859-13" + }), + + Arguments.of("ISO-8859-15", + new String[] { + // IANA alias + "ISO_8859-15", + "Latin-9", + "csISO885915", + // JDK historical aliases + "8859_15", + "ISO-8859-15", + "ISO_8859-15", + "ISO8859-15", + "ISO8859_15", + "IBM923", + "IBM-923", + "cp923", + "923", + "LATIN0", + "LATIN9", + "L9", + "csISOlatin0", + "csISOlatin9", + "ISO8859_15_FDIS" + }), + + Arguments.of("ISO-8859-16", + new String[] { + "iso-ir-226", + "ISO_8859-16:2001", + "ISO_8859-16", + "ISO8859_16", + "latin10", + "l10", + "csISO885916" + }), + + Arguments.of("JIS_X0212-1990", + new String[] { + "iso-ir-159", + "csISO159JISX02121990"}), + + Arguments.of("JIS_X0201", + new String[]{ + "X0201", + "csHalfWidthKatakana"}), + + Arguments.of("KOI8-R", + new String[] { + "KOI8_R", + "csKOI8R"}), + + Arguments.of("GBK", + new String[] { + "windows-936"}), + + Arguments.of("Shift_JIS", + new String[] { + "MS_Kanji", + "csShiftJIS"}), + + Arguments.of("EUC-JP", + new String[] { + "Extended_UNIX_Code_Packed_Format_for_Japanese", + "csEUCPkdFmtJapanese"}), + + Arguments.of("Big5", new String[] {"csBig5"}), + + Arguments.of("windows-31j", new String[] {"csWindows31J"}), + + Arguments.of("x-iso-8859-11", + new String[] { "iso-8859-11", "iso8859_11" }), + + Arguments.of("windows-1250", + new String[] { + "cp1250", + "cp5346" + }), + + Arguments.of("windows-1251", + new String[] { + "cp1251", + "cp5347", + "ansi-1251" + }), + + Arguments.of("windows-1252", + new String[] { + "cp1252", + "cp5348" + }), + + Arguments.of("windows-1253", + new String[] { + "cp1253", + "cp5349" + }), + + Arguments.of("windows-1254", + new String[] { + "cp1254", + "cp5350" + }), + + Arguments.of("windows-1255", + new String[] { + "cp1255" + }), + + Arguments.of("windows-1256", + new String[] { + "cp1256" + }), + + Arguments.of("windows-1257", + new String[] { + "cp1257", + "cp5353" + }), + + Arguments.of("windows-1258", + new String[] { + "cp1258" + }), + + Arguments.of("x-windows-874", + new String[] { + "ms874", "ms-874", "windows-874" }), + + Arguments.of("GB2312", + new String[] { + "x-EUC-CN", + "gb2312-80", + "gb2312-1980", + "euc-cn", + "euccn" }), + + Arguments.of("x-IBM942" , + new String[] { + "cp942", // JDK historical + "ibm942", + "ibm-942", + "942" + }), + + Arguments.of("x-IBM942C" , + new String[] { + "cp942C", // JDK historical + "ibm942C", + "ibm-942C", + "942C" + } ), + + Arguments.of("x-IBM943" , + new String[] { + "cp943", // JDK historical + "ibm943", + "ibm-943", + "943" + } ), + + Arguments.of("x-IBM943C" , + new String[] { + "cp943c", // JDK historical + "ibm943C", + "ibm-943C", + "943C" + } ), + + Arguments.of("x-IBM948" , + new String[] { + "cp948", // JDK historical + "ibm948", + "ibm-948", + "948" + } ), + + Arguments.of("x-IBM950" , + new String[] { + "cp950", // JDK historical + "ibm950", + "ibm-950", + "950" + } ), + + Arguments.of("x-IBM930" , + new String[] { + "cp930", // JDK historical + "ibm930", + "ibm-930", + "930" + } ), + + Arguments.of("x-IBM935" , + new String[] { + "cp935", // JDK historical + "ibm935", + "ibm-935", + "935" + } ), + + Arguments.of("x-IBM937" , + new String[] { + "cp937", // JDK historical + "ibm937", + "ibm-937", + "937" + } ), + + Arguments.of("IBM850" , + new String[] { + "cp850", // JDK historical + "ibm-850", + "ibm850", + "850", + "cspc850multilingual" + } ), + + Arguments.of("IBM852" , + new String[] { + "cp852", // JDK historical + "ibm852", + "ibm-852", + "852", + "csPCp852" + } ), + + Arguments.of("IBM855" , + new String[] { + "cp855", // JDK historical + "ibm-855", + "ibm855", + "855", + "cspcp855" + } ), + + Arguments.of("x-IBM856" , + new String[] { + "cp856", // JDK historical + "ibm-856", + "ibm856", + "856" + } ), + + Arguments.of("IBM857" , + new String[] { + "cp857", // JDK historical + "ibm857", + "ibm-857", + "857", + "csIBM857" + } ), + + Arguments.of("IBM860" , + new String[] { + "cp860", // JDK historical + "ibm860", + "ibm-860", + "860", + "csIBM860" + } ), + Arguments.of("IBM861" , + new String[] { + "cp861", // JDK historical + "ibm861", + "ibm-861", + "861", + "csIBM861" + } ), + + Arguments.of("IBM862" , + new String[] { + "cp862", // JDK historical + "ibm862", + "ibm-862", + "862", + "csIBM862" + } ), + + Arguments.of("IBM863" , + new String[] { + "cp863", // JDK historical + "ibm863", + "ibm-863", + "863", + "csIBM863" + } ), + + Arguments.of("IBM864" , + new String[] { + "cp864", // JDK historical + "ibm864", + "ibm-864", + "864", + "csIBM864" + } ), + + Arguments.of("IBM865" , + new String[] { + "cp865", // JDK historical + "ibm865", + "ibm-865", + "865", + "csIBM865" + } ), + + Arguments.of("IBM866" , new String[] { + "cp866", // JDK historical + "ibm866", + "ibm-866", + "866", + "csIBM866" + } ), + Arguments.of("IBM868" , + new String[] { + "cp868", // JDK historical + "ibm868", + "ibm-868", + "868", + "cp-ar", + "csIBM868" + } ), + + Arguments.of("IBM869" , + new String[] { + "cp869", // JDK historical + "ibm869", + "ibm-869", + "869", + "cp-gr", + "csIBM869" + } ), + + Arguments.of("IBM437" , + new String[] { + "cp437", // JDK historical + "ibm437", + "ibm-437", + "437", + "cspc8codepage437", + "windows-437" + } ), + + Arguments.of("x-IBM874" , + new String[] { + "cp874", // JDK historical + "ibm874", + "ibm-874", + "874" + } ), + Arguments.of("x-IBM737" , + new String[] { + "cp737", // JDK historical + "ibm737", + "ibm-737", + "737" + } ), + + Arguments.of("IBM775" , + new String[] { + "cp775", // JDK historical + "ibm775", + "ibm-775", + "775" + } ), + + Arguments.of("x-IBM921" , + new String[] { + "cp921", // JDK historical + "ibm921", + "ibm-921", + "921" + } ), + + Arguments.of("x-IBM1006" , + new String[] { + "cp1006", // JDK historical + "ibm1006", + "ibm-1006", + "1006" + } ), + + Arguments.of("x-IBM1046" , + new String[] { + "cp1046", // JDK historical + "ibm1046", + "ibm-1046", + "1046" + } ), + + Arguments.of("IBM1047" , + new String[] { + "cp1047", // JDK historical + "ibm-1047", + "1047" + } ), + + Arguments.of("x-IBM1098" , + new String[] { + "cp1098", // JDK historical + "ibm1098", + "ibm-1098", + "1098", + } ), + + Arguments.of("IBM037" , + new String[] { + "cp037", // JDK historical + "ibm037", + "csIBM037", + "cs-ebcdic-cp-us", + "cs-ebcdic-cp-ca", + "cs-ebcdic-cp-wt", + "cs-ebcdic-cp-nl", + "ibm-037", + "ibm-37", + "cpibm37", + "037" + } ), + + Arguments.of("x-IBM1025" , + new String[] { + "cp1025", // JDK historical + "ibm1025", + "ibm-1025", + "1025" + } ), + + Arguments.of("IBM1026" , + new String[] { + "cp1026", // JDK historical + "ibm1026", + "ibm-1026", + "1026" + } ), + + Arguments.of("x-IBM1112" , + new String[] { + "cp1112", // JDK historical + "ibm1112", + "ibm-1112", + "1112" + } ), + + Arguments.of("x-IBM1122" , + new String[] { + "cp1122", // JDK historical + "ibm1122", + "ibm-1122", + "1122" + } ), + + Arguments.of("x-IBM1123" , + new String[] { + "cp1123", // JDK historical + "ibm1123", + "ibm-1123", + "1123" + } ), + + Arguments.of("x-IBM1124" , + new String[] { + "cp1124", // JDK historical + "ibm1124", + "ibm-1124", + "1124" + } ), + + Arguments.of("x-IBM1129" , + new String[] { + "cp1129", // JDK historical + "ibm1129", + "ibm-1129", + "1129" + } ), + + Arguments.of("x-IBM1166" , + new String[] { + "cp1166", // JDK historical + "ibm1166", + "ibm-1166", + "1166" + } ), + + Arguments.of("IBM273" , + new String[] { + "cp273", // JDK historical + "ibm273", + "ibm-273", + "273" + } ), + + Arguments.of("IBM277" , + new String[] { + "cp277", // JDK historical + "ibm277", + "ibm-277", + "277" + } ), + + Arguments.of("IBM278" , + new String[] { + "cp278", // JDK historical + "ibm278", + "ibm-278", + "278", + "ebcdic-sv", + "ebcdic-cp-se", + "csIBM278" + } ), + + Arguments.of("IBM280" , + new String[] { + "cp280", // JDK historical + "ibm280", + "ibm-280", + "280" + } ), + + Arguments.of("IBM284" , + new String[] { + "cp284", // JDK historical + "ibm284", + "ibm-284", + "284", + "csIBM284", + "cpibm284" + } ), + + Arguments.of("IBM285" , + new String[] { + "cp285", // JDK historical + "ibm285", + "ibm-285", + "285", + "ebcdic-cp-gb", + "ebcdic-gb", + "csIBM285", + "cpibm285" + } ), + + Arguments.of("IBM297" , + new String[] { + "cp297", // JDK historical + "ibm297", + "ibm-297", + "297", + "ebcdic-cp-fr", + "cpibm297", + "csIBM297", + } ), + + Arguments.of("IBM420" , + new String[] { + "cp420", // JDK historical + "ibm420", + "ibm-420", + "ebcdic-cp-ar1", + "420", + "csIBM420" + } ), + + Arguments.of("IBM424" , + new String[] { + "cp424", // JDK historical + "ibm424", + "ibm-424", + "424", + "ebcdic-cp-he", + "csIBM424" + } ), + + Arguments.of("IBM500" , + new String[] { + "cp500", // JDK historical + "ibm500", + "ibm-500", + "500", + "ebcdic-cp-ch", + "ebcdic-cp-bh", + "csIBM500" + } ), + + Arguments.of("IBM-Thai" , + new String[] { + "cp838", // JDK historical + "ibm838", + "ibm-838", + "ibm838", + "838" + } ), + + Arguments.of("IBM870" , + new String[] { + "cp870", // JDK historical + "ibm870", + "ibm-870", + "870", + "ebcdic-cp-roece", + "ebcdic-cp-yu", + "csIBM870" + } ), + + Arguments.of("IBM871" , + new String[] { + "cp871", // JDK historical + "ibm871", + "ibm-871", + "871", + "ebcdic-cp-is", + "csIBM871" + } ), + + Arguments.of("x-IBM875" , + new String[] { + "cp875", // JDK historical + "ibm875", + "ibm-875", + "875" + } ), + + Arguments.of("IBM918" , + new String[] { + "cp918", // JDK historical + "ibm-918", + "918", + "ebcdic-cp-ar2" + } ), + + Arguments.of("x-IBM922" , + new String[] { + "cp922", // JDK historical + "ibm922", + "ibm-922", + "922" + } ), + + Arguments.of("x-IBM1097" , + new String[] { + "cp1097", // JDK historical + "ibm1097", + "ibm-1097", + "1097" + } ), + + Arguments.of("x-IBM949" , + new String[] { + "cp949", // JDK historical + "ibm949", + "ibm-949", + "949" + } ), + + Arguments.of("x-IBM949C" , + new String[] { + "cp949C", // JDK historical + "ibm949C", + "ibm-949C", + "949C" + } ), + + Arguments.of("x-IBM939" , + new String[] { + "cp939", // JDK historical + "ibm939", + "ibm-939", + "939" + } ), + + Arguments.of("x-IBM933" , + new String[] { + "cp933", // JDK historical + "ibm933", + "ibm-933", + "933" + } ), + + Arguments.of("x-IBM1381" , + new String[] { + "cp1381", // JDK historical + "ibm1381", + "ibm-1381", + "1381" + } ), + + Arguments.of("x-IBM1383" , + new String[] { + "cp1383", // JDK historical + "ibm1383", + "ibm-1383", + "1383" + } ), + + Arguments.of("x-IBM970" , + new String[] { + "cp970", // JDK historical + "ibm970", + "ibm-970", + "ibm-eucKR", + "970" + } ), + + Arguments.of("x-IBM964" , + new String[] { + "cp964", // JDK historical + "ibm964", + "ibm-964", + "964" + } ), + + Arguments.of("x-IBM33722" , + new String[] { + "cp33722", // JDK historical + "ibm33722", + "ibm-33722", + "ibm-5050", // from IBM alias list + "ibm-33722_vascii_vpua", // from IBM alias list + "33722" + } ), + + Arguments.of("IBM01140" , + new String[] { + "cp1140", // JDK historical + "ccsid01140", + "cp01140", + // "ebcdic-us-037+euro" + } ), + + Arguments.of("IBM01141" , + new String[] { + "cp1141", // JDK historical + "ccsid01141", + "cp01141", + // "ebcdic-de-273+euro" + } ), + + Arguments.of("IBM01142" , + new String[] { + "cp1142", // JDK historical + "ccsid01142", + "cp01142", + // "ebcdic-no-277+euro", + // "ebcdic-dk-277+euro" + } ), + + Arguments.of("IBM01143" , + new String[] { + "cp1143", // JDK historical + "ccsid01143", + "cp01143", + // "ebcdic-fi-278+euro", + // "ebcdic-se-278+euro" + } ), + + Arguments.of("IBM01144" , + new String[] { + "cp1144", // JDK historical + "ccsid01144", + "cp01144", + // "ebcdic-it-280+euro" + } ), + + Arguments.of("IBM01145" , + new String[] { + "cp1145", // JDK historical + "ccsid01145", + "cp01145", + // "ebcdic-es-284+euro" + } ), + + Arguments.of("IBM01146" , + new String[] { + "cp1146", // JDK historical + "ccsid01146", + "cp01146", + // "ebcdic-gb-285+euro" + } ), + + Arguments.of("IBM01147" , + new String[] { + "cp1147", // JDK historical + "ccsid01147", + "cp01147", + // "ebcdic-fr-277+euro" + } ), + + Arguments.of("IBM01148" , + new String[] { + "cp1148", // JDK historical + "ccsid01148", + "cp01148", + // "ebcdic-international-500+euro" + } ), + + Arguments.of("IBM01149" , + new String[] { + "cp1149", // JDK historical + "ccsid01149", + "cp01149", + // "ebcdic-s-871+euro" + } ), + + Arguments.of("IBM00858" , + new String[] { + "cp858", // JDK historical + "ccsid00858", + "cp00858", + // "PC-Multilingual-850+euro" + } ), + + Arguments.of("x-MacRoman", + new String[] { + "MacRoman" // JDK historical + }), + + Arguments.of("x-MacCentralEurope", + new String[] { + "MacCentralEurope" // JDK historical + }), + + Arguments.of("x-MacCroatian", + new String[] { + "MacCroatian" // JDK historical + }), + + + Arguments.of("x-MacCroatian", + new String[] { + "MacCroatian" // JDK historical + }), + + + Arguments.of("x-MacGreek", + new String[] { + "MacGreek" // JDK historical + }), + + Arguments.of("x-MacCyrillic", + new String[] { + "MacCyrillic" // JDK historical + }), + + Arguments.of("x-MacUkraine", + new String[] { + "MacUkraine" // JDK historical + }), + + Arguments.of("x-MacTurkish", + new String[] { + "MacTurkish" // JDK historical + }), + + Arguments.of("x-MacArabic", + new String[] { + "MacArabic" // JDK historical + }), + + Arguments.of("x-MacHebrew", + new String[] { + "MacHebrew" // JDK historical + }), + + Arguments.of("x-MacIceland", + new String[] { + "MacIceland" // JDK historical + }), + + Arguments.of("x-MacRomania", + new String[] { + "MacRomania" // JDK historical + }), + + Arguments.of("x-MacThai", + new String[] { + "MacThai" // JDK historical + }), + + Arguments.of("x-MacSymbol", + new String[] { + "MacSymbol" // JDK historical + }), + + Arguments.of("x-MacDingbat", + new String[] { + "MacDingbat" // JDK historical + }) + ); } } diff --git a/test/jdk/java/nio/file/Files/probeContentType/Basic.java b/test/jdk/java/nio/file/Files/probeContentType/Basic.java index 28cda7b574e3c..8acfc97c738c0 100644 --- a/test/jdk/java/nio/file/Files/probeContentType/Basic.java +++ b/test/jdk/java/nio/file/Files/probeContentType/Basic.java @@ -38,7 +38,6 @@ import java.util.stream.Stream; import jdk.test.lib.Platform; -import jdk.test.lib.OSVersion; import jdk.internal.util.StaticProperty; /** @@ -188,18 +187,8 @@ public static void main(String[] args) throws IOException { exTypes.add(new ExType("xlsx", List.of("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"))); // exTypes.add(new ExType("wasm", List.of("application/wasm"))); Note. "wasm" is added via JDK-8297609 (Java 20) which not exist in current java version yet - // extensions with content type that differs on Windows 11+ and - // Windows Server 2025 - if (Platform.isWindows() && - (System.getProperty("os.name").matches("^.*[11|2025]$") || - new OSVersion(10, 0).compareTo(OSVersion.current()) > 0)) { - System.out.println("Windows 11+ detected: using different types"); - exTypes.add(new ExType("bz2", List.of("application/bz2", "application/x-bzip2", "application/x-bzip", "application/x-compressed"))); - exTypes.add(new ExType("csv", List.of("text/csv", "application/vnd.ms-excel"))); - exTypes.add(new ExType("rar", List.of("application/rar", "application/vnd.rar", "application/x-rar", "application/x-rar-compressed", "application/x-compressed"))); - exTypes.add(new ExType("rtf", List.of("application/rtf", "text/rtf", "application/msword"))); - exTypes.add(new ExType("7z", List.of("application/x-7z-compressed", "application/x-compressed"))); - } else { + // extensions with consistent content type on Unix (but not on Windows) + if (!Platform.isWindows()) { exTypes.add(new ExType("bz2", List.of("application/bz2", "application/x-bzip2", "application/x-bzip"))); exTypes.add(new ExType("csv", List.of("text/csv"))); exTypes.add(new ExType("rar", List.of("application/rar", "application/vnd.rar", "application/x-rar", "application/x-rar-compressed"))); diff --git a/test/jdk/java/rmi/transport/checkLeaseInfoLeak/CheckLeaseLeak.java b/test/jdk/java/rmi/transport/checkLeaseInfoLeak/CheckLeaseLeak.java index 2370dcd35c4f2..4de6598a0f467 100644 --- a/test/jdk/java/rmi/transport/checkLeaseInfoLeak/CheckLeaseLeak.java +++ b/test/jdk/java/rmi/transport/checkLeaseInfoLeak/CheckLeaseLeak.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -54,12 +54,11 @@ import java.rmi.*; import java.rmi.server.*; -import sun.rmi.transport.*; -import sun.rmi.*; import java.util.Map; import java.io.*; import java.lang.reflect.*; import java.rmi.registry.*; +import sun.rmi.transport.*; public class CheckLeaseLeak extends UnicastRemoteObject implements LeaseLeak { public CheckLeaseLeak() throws RemoteException { } @@ -102,8 +101,6 @@ public static void main (String[] args) { System.err.println("Created client: " + i); JavaVM jvm = new JavaVM("LeaseLeakClient", - " -Djava.security.policy=" + - TestParams.defaultPolicy + " -Drmi.registry.port=" + registryPort, ""); @@ -128,8 +125,8 @@ public static void main (String[] args) { } } - /* numLeft should be 2 - if 11 there is a problem. */ - if (numLeft > 2) { + /* numLeft should be 4 - if 11 there is a problem. */ + if (numLeft > 4) { TestLibrary.bomb("Too many objects in DGCImpl.leaseTable: "+ numLeft); } else { @@ -156,57 +153,51 @@ private static int getDGCLeaseTableSize () { Field f; try { - f = (Field) java.security.AccessController.doPrivileged - (new java.security.PrivilegedExceptionAction() { - public Object run() throws Exception { - - ObjID dgcID = new ObjID(DGC_ID); - - /* - * Construct an ObjectEndpoint containing DGC's - * ObjID. - */ - Class oeClass = - Class.forName("sun.rmi.transport.ObjectEndpoint"); - Class[] constrParams = - new Class[]{ ObjID.class, Transport.class }; - Constructor oeConstructor = - oeClass.getDeclaredConstructor(constrParams); - oeConstructor.setAccessible(true); - Object oe = - oeConstructor.newInstance( - new Object[]{ dgcID, null }); - - /* - * Get Target that contains DGCImpl in ObjectTable - */ - Class objTableClass = - Class.forName("sun.rmi.transport.ObjectTable"); - Class getTargetParams[] = new Class[] { oeClass }; - Method objTableGetTarget = - objTableClass.getDeclaredMethod("getTarget", - getTargetParams); - objTableGetTarget.setAccessible(true); - Target dgcTarget = (Target) - objTableGetTarget.invoke(null, new Object[]{ oe }); - - /* get the DGCImpl from its Target */ - Method targetGetImpl = - dgcTarget.getClass().getDeclaredMethod + ObjID dgcID = new ObjID(DGC_ID); + /* + * Construct an ObjectEndpoint containing DGC's + * ObjID. + */ + Class oeClass = + Class.forName("sun.rmi.transport.ObjectEndpoint"); + Class[] constrParams = + new Class[]{ ObjID.class, Transport.class }; + Constructor oeConstructor = + oeClass.getDeclaredConstructor(constrParams); + oeConstructor.setAccessible(true); + Object oe = + oeConstructor.newInstance( + new Object[]{ dgcID, null }); + + /* + * Get Target that contains DGCImpl in ObjectTable + */ + Class objTableClass = + Class.forName("sun.rmi.transport.ObjectTable"); + Class getTargetParams[] = new Class[] { oeClass }; + Method objTableGetTarget = + objTableClass.getDeclaredMethod("getTarget", + getTargetParams); + objTableGetTarget.setAccessible(true); + Target dgcTarget = (Target) + objTableGetTarget.invoke(null, new Object[]{ oe }); + + /* get the DGCImpl from its Target */ + Method targetGetImpl = + dgcTarget.getClass().getDeclaredMethod ("getImpl", null); - targetGetImpl.setAccessible(true); - dgcImpl[0] = - (Remote) targetGetImpl.invoke(dgcTarget, null); + targetGetImpl.setAccessible(true); + dgcImpl[0] = + (Remote) targetGetImpl.invoke(dgcTarget, null); - /* Get the lease table from the DGCImpl. */ - Field reflectedLeaseTable = - dgcImpl[0].getClass().getDeclaredField + /* Get the lease table from the DGCImpl. */ + Field reflectedLeaseTable = + dgcImpl[0].getClass().getDeclaredField ("leaseTable"); - reflectedLeaseTable.setAccessible(true); + reflectedLeaseTable.setAccessible(true); + + f = reflectedLeaseTable; - return reflectedLeaseTable; - } - }); /** * This is the leaseTable that will fill up with LeaseInfo @@ -217,9 +208,6 @@ public Object run() throws Exception { numLeaseInfosLeft = leaseTable.size(); } catch(Exception e) { - if (e instanceof java.security.PrivilegedActionException) - e = ((java.security.PrivilegedActionException) e). - getException(); TestLibrary.bomb(e); } diff --git a/test/jdk/java/rmi/transport/checkLeaseInfoLeak/LeaseLeakClient.java b/test/jdk/java/rmi/transport/checkLeaseInfoLeak/LeaseLeakClient.java index d9cf9fa9e9658..dd24b819146e3 100644 --- a/test/jdk/java/rmi/transport/checkLeaseInfoLeak/LeaseLeakClient.java +++ b/test/jdk/java/rmi/transport/checkLeaseInfoLeak/LeaseLeakClient.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,6 @@ public class LeaseLeakClient { public static void main(String args[]) { - TestLibrary.suggestSecurityManager("java.rmi.RMISecurityManager"); try { LeaseLeak leaseLeak = null; diff --git a/test/jdk/java/security/KeyFactory/Failover.java b/test/jdk/java/security/KeyFactory/Failover.java index 7107ef1ad0281..3fd69a3cbc323 100644 --- a/test/jdk/java/security/KeyFactory/Failover.java +++ b/test/jdk/java/security/KeyFactory/Failover.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,7 @@ /** * @test * @bug 4894125 7054918 8130181 - * @library ../testlibrary /test/lib + * @library /test/lib * @summary test that failover for KeyFactory works * @author Andreas Sterbenz */ @@ -32,8 +32,8 @@ import java.util.*; import java.security.*; -import java.security.interfaces.*; import java.security.spec.*; +import jdk.test.lib.security.ProvidersSnapshot; import jdk.test.lib.security.SecurityUtils; public class Failover { diff --git a/test/jdk/java/security/KeyPairGenerator/Failover.java b/test/jdk/java/security/KeyPairGenerator/Failover.java index 7fc8da8ccfc34..1bc2bfee2c8de 100644 --- a/test/jdk/java/security/KeyPairGenerator/Failover.java +++ b/test/jdk/java/security/KeyPairGenerator/Failover.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,7 @@ /** * @test * @bug 4894125 7054918 8130181 - * @library ../testlibrary + * @library /test/lib * @summary test that failover for KeyPairGenerator works * @author Andreas Sterbenz */ @@ -32,8 +32,8 @@ import java.util.*; import java.security.*; -import java.security.interfaces.*; import java.security.spec.*; +import jdk.test.lib.security.ProvidersSnapshot; public class Failover { diff --git a/test/jdk/java/security/Policy/ExtensiblePolicy/ExtensiblePolicyWithJarTest.java b/test/jdk/java/security/Policy/ExtensiblePolicy/ExtensiblePolicyWithJarTest.java index 8fdd787863927..fdbe44fb03c58 100644 --- a/test/jdk/java/security/Policy/ExtensiblePolicy/ExtensiblePolicyWithJarTest.java +++ b/test/jdk/java/security/Policy/ExtensiblePolicy/ExtensiblePolicyWithJarTest.java @@ -94,7 +94,7 @@ public static void main(String args[]) throws Throwable { "-Djava.security.manager", "-Djava.security.policy=" + POL, "ExtensiblePolicyTest_orig$TestMain"}; - ProcessTools.executeTestJvm(cmd).shouldHaveExitValue(0); + ProcessTools.executeTestJava(cmd).shouldHaveExitValue(0); } catch (Exception ex) { System.out.println("ExtensiblePolicyWithJarTest Failed"); } diff --git a/test/jdk/java/security/Policy/SignedJar/SignedJarTest.java b/test/jdk/java/security/Policy/SignedJar/SignedJarTest.java index c93337d73d0c3..cd06cc15691b0 100644 --- a/test/jdk/java/security/Policy/SignedJar/SignedJarTest.java +++ b/test/jdk/java/security/Policy/SignedJar/SignedJarTest.java @@ -121,7 +121,7 @@ public static void main(String args[]) throws Throwable { System.out.println("Test Case 1"); //copy policy file into current directory String[] cmd = constructCMD("first.jar", POLICY1, "false", "true"); - ProcessTools.executeTestJvm(cmd).shouldHaveExitValue(0); + ProcessTools.executeTestJava(cmd).shouldHaveExitValue(0); //test case 2, test with both.jar //setIO permission granted to code that was signed by first signer @@ -131,7 +131,7 @@ public static void main(String args[]) throws Throwable { //Expect no AccessControlException System.out.println("Test Case 2"); cmd = constructCMD("both.jar", POLICY1, "false", "false"); - ProcessTools.executeTestJvm(cmd).shouldHaveExitValue(0); + ProcessTools.executeTestJava(cmd).shouldHaveExitValue(0); //test case 3 //setIO permission granted to code that was signed by first signer @@ -141,7 +141,7 @@ public static void main(String args[]) throws Throwable { //Expect AccessControlException for setFactory permission System.out.println("Test Case 3"); cmd = constructCMD("both.jar", POLICY2, "false", "true"); - ProcessTools.executeTestJvm(cmd).shouldHaveExitValue(0); + ProcessTools.executeTestJava(cmd).shouldHaveExitValue(0); } diff --git a/test/jdk/java/security/Provider/ChangeProviders.java b/test/jdk/java/security/Provider/ChangeProviders.java index 2c699cd70b1a0..7c408d70fb62c 100644 --- a/test/jdk/java/security/Provider/ChangeProviders.java +++ b/test/jdk/java/security/Provider/ChangeProviders.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,14 +24,13 @@ /* * @test * @bug 4856968 7054918 8130181 - * @library ../testlibrary + * @library /test/lib * @summary make sure add/insert/removeProvider() work correctly * @author Andreas Sterbenz */ -import java.util.*; - import java.security.*; +import jdk.test.lib.security.ProvidersSnapshot; public class ChangeProviders extends Provider { diff --git a/test/jdk/java/security/Provider/GetInstance.java b/test/jdk/java/security/Provider/GetInstance.java index d0d6417f02b08..f0e1a149f5cb6 100644 --- a/test/jdk/java/security/Provider/GetInstance.java +++ b/test/jdk/java/security/Provider/GetInstance.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,7 @@ /* * @test * @bug 4856968 7054918 8130181 - * @library ../testlibrary + * @library /test/lib * @summary make sure getInstance() works correctly, including failover * and delayed provider selection for Signatures * @author Andreas Sterbenz @@ -34,6 +34,7 @@ import java.security.*; import java.security.cert.*; +import jdk.test.lib.security.ProvidersSnapshot; public class GetInstance { diff --git a/test/jdk/java/security/Provider/GetServiceRace.java b/test/jdk/java/security/Provider/GetServiceRace.java index 7f3f6b56ff722..427d555d37567 100644 --- a/test/jdk/java/security/Provider/GetServiceRace.java +++ b/test/jdk/java/security/Provider/GetServiceRace.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,6 @@ /* * @test * @bug 8231387 - * @library ../testlibrary * @summary make sure getService() avoids a race * @author Tianmin Shi */ diff --git a/test/jdk/java/security/Provider/InvalidServiceTest.java b/test/jdk/java/security/Provider/InvalidServiceTest.java new file mode 100644 index 0000000000000..fd56e6b3286da --- /dev/null +++ b/test/jdk/java/security/Provider/InvalidServiceTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8344361 + * @summary Restore null return for invalid services + */ + +import java.security.Provider; + +public class InvalidServiceTest { + + public static void main(String[] args) throws Exception { + Provider p1 = new LProvider("LegacyFormat"); + // this returns a service with null class name. Helps exercise the code path + Provider.Service s1 = p1.getService("MessageDigest", "SHA-1"); + if (s1 != null) + throw new RuntimeException("expecting null service"); + } + + private static class LProvider extends Provider { + LProvider(String name) { + super(name, "1.0", null); + put("Signature.MD5withRSA", "com.foo.Sig"); + put("MessageDigest.SHA-1 ImplementedIn", "Software"); + } + } +} diff --git a/test/jdk/java/security/Provider/RemoveProvider.java b/test/jdk/java/security/Provider/RemoveProvider.java index 05ab5eb0549c1..9983aa59b2a8f 100644 --- a/test/jdk/java/security/Provider/RemoveProvider.java +++ b/test/jdk/java/security/Provider/RemoveProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,13 +24,14 @@ /* * @test * @bug 4190873 7054918 8130181 - * @library ../testlibrary + * @library /test/lib * @summary Make sure provider instance can be removed from list of registered * providers, and "entrySet", "keySet", and "values" methods don't loop * indefinitely. */ import java.security.*; import java.util.*; +import jdk.test.lib.security.ProvidersSnapshot; public class RemoveProvider { diff --git a/test/jdk/java/security/Provider/SecurityProviderModularTest.java b/test/jdk/java/security/Provider/SecurityProviderModularTest.java index 560331e3ada94..c1fc454074ba5 100644 --- a/test/jdk/java/security/Provider/SecurityProviderModularTest.java +++ b/test/jdk/java/security/Provider/SecurityProviderModularTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,19 +38,22 @@ import java.io.OutputStream; import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleDescriptor.Builder; -import jdk.internal.module.ModuleInfoWriter; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.util.JarUtils; - +import jdk.test.lib.util.ModuleInfoWriter; /* * @test * @bug 8130360 8183310 * @summary Test security provider in different combination of modular option * defined with(out) service description. + * @modules java.base/jdk.internal.org.objectweb.asm + * java.base/jdk.internal.org.objectweb.asm.commons + * java.base/jdk.internal.module * @library /test/lib - * @modules java.base/jdk.internal.module - * @build jdk.test.lib.util.JarUtils TestProvider TestClient + * @build jdk.test.lib.util.JarUtils + * jdk.test.lib.util.ModuleInfoWriter + * TestProvider TestClient * @run main SecurityProviderModularTest CL true * @run main SecurityProviderModularTest CL false * @run main SecurityProviderModularTest SL true @@ -253,7 +256,7 @@ private void execute(String args, String msgKey) throws Exception { } return !s.isEmpty(); }).toArray(String[]::new); - String out = ProcessTools.executeTestJvm(safeArgs).getOutput(); + String out = ProcessTools.executeTestJava(safeArgs).getOutput(); // Handle response. if ((msgKey != null && out.contains(MSG_MAP.get(msgKey)))) { System.out.printf("PASS: Expected Result: %s.%n", diff --git a/test/jdk/java/security/Security/ClassLoaderDeadlock/ClassLoaderDeadlock.java b/test/jdk/java/security/Security/ClassLoaderDeadlock/ClassLoaderDeadlock.java index f5af071654c61..d2794e70a2d9e 100644 --- a/test/jdk/java/security/Security/ClassLoaderDeadlock/ClassLoaderDeadlock.java +++ b/test/jdk/java/security/Security/ClassLoaderDeadlock/ClassLoaderDeadlock.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,7 +21,14 @@ * questions. */ -// See bug 5094825 / ClassLoadDeadlock.sh +/* +* @test +* @bug 5094825 +* @summary verify no deadlock if crypto provider in other classloader is used to verify signed jars +* @library ./Deadlock.jar +* @compile provider/HashProvider.java +* @run main/othervm/timeout=30 ClassLoaderDeadlock +*/ import java.net.*; diff --git a/test/jdk/java/security/Security/ClassLoaderDeadlock/ClassLoaderDeadlock.sh b/test/jdk/java/security/Security/ClassLoaderDeadlock/ClassLoaderDeadlock.sh deleted file mode 100644 index e2e573928535e..0000000000000 --- a/test/jdk/java/security/Security/ClassLoaderDeadlock/ClassLoaderDeadlock.sh +++ /dev/null @@ -1,106 +0,0 @@ -# -# Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -# @test -# @bug 5094825 -# @summary verify no deadlock if crypto provider in other classloader is used to verify signed jars -# -# @run shell/timeout=30 ClassLoaderDeadlock.sh - -# set a few environment variables so that the shell-script can run stand-alone -# in the source directory -if [ "${TESTSRC}" = "" ] ; then - TESTSRC="." -fi - -if [ "${TESTCLASSES}" = "" ] ; then - TESTCLASSES="." -fi - -if [ "${TESTJAVA}" = "" ] ; then - echo "TESTJAVA not set. Test cannot execute." - echo "FAILED!!!" - exit 1 -fi - -if [ "${COMPILEJAVA}" = "" ]; then - COMPILEJAVA="${TESTJAVA}" -fi - -# set platform-dependent variables -OS=`uname -s` -case "$OS" in - Linux ) - PATHSEP=":" - FILESEP="/" - ;; - Darwin ) - PATHSEP=":" - FILESEP="/" - ;; - AIX ) - PATHSEP=":" - FILESEP="/" - ;; - CYGWIN* ) - PATHSEP=";" - FILESEP="/" - ;; - Windows* ) - PATHSEP=";" - FILESEP="\\" - ;; - * ) - echo "Unrecognized system!" - exit 1; - ;; -esac - -cd ${TESTCLASSES}${FILESEP} -if [ ! -d provider ] ; then - mkdir provider -fi - -# compile the test program -${COMPILEJAVA}${FILESEP}bin${FILESEP}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \ - -d ${TESTCLASSES}${FILESEP} \ - ${TESTSRC}${FILESEP}ClassLoaderDeadlock.java - -${COMPILEJAVA}${FILESEP}bin${FILESEP}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \ - -d ${TESTCLASSES}${FILESEP}provider${FILESEP} \ - ${TESTSRC}${FILESEP}provider${FILESEP}HashProvider.java - -# run the test -${TESTJAVA}${FILESEP}bin${FILESEP}java ${TESTVMOPTS} ${TESTJAVAOPTS} \ - -classpath "${TESTCLASSES}${PATHSEP}${TESTSRC}${FILESEP}Deadlock.jar" \ - -Djava.awt.headless=true \ - ClassLoaderDeadlock - -STATUS=$? - -# clean up -rm -f 'ClassLoaderDeadlock.class' 'ClassLoaderDeadlock$1.class' \ -'ClassLoaderDeadlock$DelayClassLoader.class' \ -provider${FILESEP}HashProvider.class - -exit $STATUS diff --git a/test/jdk/java/security/Security/ClassLoaderDeadlock/Deadlock.java b/test/jdk/java/security/Security/ClassLoaderDeadlock/Deadlock.java index 5693f4933aa96..0d46b5c8660a8 100644 --- a/test/jdk/java/security/Security/ClassLoaderDeadlock/Deadlock.java +++ b/test/jdk/java/security/Security/ClassLoaderDeadlock/Deadlock.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,8 +21,13 @@ * questions. */ - -// see Deadlock.sh +/* + * @test + * @bug 4944382 + * @summary make sure we do not deadlock loading signed JAR with getInstance() + * @library ./Deadlock.jar + * @run main/othervm/timeout=30 Deadlock + */ import java.security.*; diff --git a/test/jdk/java/security/Security/ClassLoaderDeadlock/Deadlock.sh b/test/jdk/java/security/Security/ClassLoaderDeadlock/Deadlock.sh deleted file mode 100644 index 7013c4cc33a1a..0000000000000 --- a/test/jdk/java/security/Security/ClassLoaderDeadlock/Deadlock.sh +++ /dev/null @@ -1,66 +0,0 @@ -#!/bin/sh - -# -# Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - - -# @test -# @bug 4944382 -# @summary make sure we do not deadlock loading signed JAR with getInstance() -# @author Andreas Sterbenz -# @build Deadlock -# @run shell/timeout=30 Deadlock.sh - -# set platform-dependent variables -OS=`uname -s` -case "$OS" in - Linux ) - PATHSEP=":" - FILESEP="/" - ;; - Darwin ) - PATHSEP=":" - FILESEP="/" - ;; - AIX ) - PATHSEP=":" - FILESEP="/" - ;; - CYGWIN* ) - PATHSEP=";" - FILESEP="/" - ;; - Windows* ) - PATHSEP=";" - FILESEP="\\" - ;; - * ) - echo "Unrecognized system!" - exit 1; - ;; -esac - -JAVA="${TESTJAVA}${FILESEP}bin${FILESEP}java" - -${JAVA} ${TESTVMOPTS} ${TESTJAVAOPTS} -cp "${TESTCLASSES}${PATHSEP}${TESTSRC}${FILESEP}Deadlock.jar" Deadlock - diff --git a/test/jdk/java/security/Security/NoInstalledProviders.java b/test/jdk/java/security/Security/NoInstalledProviders.java index 551f1896ecbe5..1e701f9a6e1f6 100644 --- a/test/jdk/java/security/Security/NoInstalledProviders.java +++ b/test/jdk/java/security/Security/NoInstalledProviders.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,12 +24,13 @@ /* * @test * @bug 4273454 7054918 7052537 - * @library ../testlibrary + * @library /test/lib * @summary Make sure getProviders(filter) doesn't throw NPE * @run main/othervm NoInstalledProviders */ import java.security.*; +import jdk.test.lib.security.ProvidersSnapshot; public class NoInstalledProviders { diff --git a/test/jdk/java/security/Security/SecurityPropFile/TEST.properties b/test/jdk/java/security/Security/SecurityPropFile/TEST.properties deleted file mode 100644 index 908b451f8ea69..0000000000000 --- a/test/jdk/java/security/Security/SecurityPropFile/TEST.properties +++ /dev/null @@ -1,2 +0,0 @@ -# disabled till JDK-8219408 is fixed -allowSmartActionArgs=false diff --git a/test/jdk/java/security/Security/SynchronizedAccess.java b/test/jdk/java/security/Security/SynchronizedAccess.java index 28019213b3c24..c1443529fe79c 100644 --- a/test/jdk/java/security/Security/SynchronizedAccess.java +++ b/test/jdk/java/security/Security/SynchronizedAccess.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,13 +24,14 @@ /* * @test * @bug 4162583 7054918 8130181 8028127 - * @library /test/lib ../testlibrary + * @library /test/lib * @summary Make sure Provider api implementations are synchronized properly */ import java.security.*; import jdk.test.lib.Asserts; +import jdk.test.lib.security.ProvidersSnapshot; public class SynchronizedAccess { diff --git a/test/jdk/java/security/Security/removing/RemoveProviders.java b/test/jdk/java/security/Security/removing/RemoveProviders.java index 0d9f6b207c595..38edf983fb020 100644 --- a/test/jdk/java/security/Security/removing/RemoveProviders.java +++ b/test/jdk/java/security/Security/removing/RemoveProviders.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,7 @@ /** * @test * @bug 4963416 7054918 - * @library ../../testlibrary + * @library /test/lib * @summary make sure removeProvider() always works correctly * @author Andreas Sterbenz */ @@ -32,6 +32,7 @@ import java.util.*; import java.security.*; +import jdk.test.lib.security.ProvidersSnapshot; public class RemoveProviders { diff --git a/test/jdk/java/security/Security/signedfirst/DynStatic.java b/test/jdk/java/security/Security/signedfirst/DynStatic.java index 5256564064b34..59e30de54623f 100644 --- a/test/jdk/java/security/Security/signedfirst/DynStatic.java +++ b/test/jdk/java/security/Security/signedfirst/DynStatic.java @@ -78,7 +78,7 @@ public static void main(String[] args) throws Exception { CompilerUtils.compile(DYN_SRC, TEST_CLASSES, "-classpath", "exp.jar"); // Run the DynSignedProvFirst test program - ProcessTools.executeTestJvm("-classpath", + ProcessTools.executeTestJava("-classpath", TEST_CLASSES.toString() + File.pathSeparator + "exp.jar", "DynSignedProvFirst") .shouldContain("test passed"); @@ -87,7 +87,7 @@ public static void main(String[] args) throws Exception { CompilerUtils.compile(STATIC_SRC, TEST_CLASSES, "-classpath", "exp.jar"); // Run the StaticSignedProvFirst test program - ProcessTools.executeTestJvm("-classpath", + ProcessTools.executeTestJava("-classpath", TEST_CLASSES.toString() + File.pathSeparator + "exp.jar", "-Djava.security.properties=file:" + STATIC_PROPS, "StaticSignedProvFirst") diff --git a/test/jdk/java/security/SignedJar/spi-calendar-provider/TestSPISigned.java b/test/jdk/java/security/SignedJar/spi-calendar-provider/TestSPISigned.java index 69fe8effe70aa..83c2d85f6e444 100644 --- a/test/jdk/java/security/SignedJar/spi-calendar-provider/TestSPISigned.java +++ b/test/jdk/java/security/SignedJar/spi-calendar-provider/TestSPISigned.java @@ -100,7 +100,7 @@ public static void main(String[] args) throws Throwable { testRun.add(classPath); testRun.add(TestSPISigned.class.getSimpleName()); testRun.add("run-test"); - OutputAnalyzer out = ProcessTools.executeTestJvm(testRun); + OutputAnalyzer out = ProcessTools.executeTestJava(testRun); out.shouldHaveExitValue(0); out.shouldContain("DEBUG: Getting xx language"); } diff --git a/test/jdk/java/security/cert/CertPathValidator/OCSP/GetAndPostTests.java b/test/jdk/java/security/cert/CertPathValidator/OCSP/GetAndPostTests.java index 478e94572bc73..d29ca878cd34d 100644 --- a/test/jdk/java/security/cert/CertPathValidator/OCSP/GetAndPostTests.java +++ b/test/jdk/java/security/cert/CertPathValidator/OCSP/GetAndPostTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,8 +25,7 @@ * @test * @bug 8179503 8328638 * @summary Java should support GET OCSP calls - * @library /javax/net/ssl/templates /java/security/testlibrary - * @build SimpleOCSPServer + * @library /javax/net/ssl/templates /test/lib * @modules java.base/sun.security.util * java.base/sun.security.provider.certpath * java.base/sun.security.x509 @@ -64,7 +63,7 @@ import java.util.Set; import java.util.concurrent.TimeUnit; -import sun.security.testlibrary.SimpleOCSPServer; +import jdk.test.lib.security.SimpleOCSPServer; import sun.security.util.DerOutputStream; import sun.security.util.DerValue; import sun.security.util.ObjectIdentifier; diff --git a/test/jdk/java/security/cert/CertPathValidator/OCSP/OCSPTimeout.java b/test/jdk/java/security/cert/CertPathValidator/OCSP/OCSPTimeout.java index dfd7dcdc81efe..342617a5fa93d 100644 --- a/test/jdk/java/security/cert/CertPathValidator/OCSP/OCSPTimeout.java +++ b/test/jdk/java/security/cert/CertPathValidator/OCSP/OCSPTimeout.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,8 +34,7 @@ * @modules java.base/sun.security.x509 * java.base/sun.security.provider.certpath * java.base/sun.security.util - * @library ../../../../../java/security/testlibrary - * @build CertificateBuilder SimpleOCSPServer + * @library /test/lib * @run main/othervm -Djava.security.debug=certpath OCSPTimeout 1000 true * @run main/othervm -Djava.security.debug=certpath * -Dcom.sun.security.ocsp.readtimeout=5 OCSPTimeout 1000 true @@ -59,8 +58,8 @@ import java.security.cert.*; import java.util.concurrent.TimeUnit; -import sun.security.testlibrary.SimpleOCSPServer; -import sun.security.testlibrary.CertificateBuilder; +import jdk.test.lib.security.SimpleOCSPServer; +import jdk.test.lib.security.CertificateBuilder; import static java.security.cert.PKIXRevocationChecker.Option.*; @@ -84,7 +83,7 @@ public class OCSPTimeout { public static void main(String[] args) throws Exception { int ocspTimeout = 15000; - boolean expected = false; + boolean expected; createPKI(); @@ -195,7 +194,6 @@ private static void createPKI() throws Exception { rootOcsp = new SimpleOCSPServer(rootKeystore, passwd, ROOT_ALIAS, null); rootOcsp.enableLog(debug); rootOcsp.setNextUpdateInterval(3600); - rootOcsp.setDisableContentLength(true); rootOcsp.start(); // Wait 60 seconds for server ready diff --git a/test/jdk/java/security/cert/CertPathValidator/trustAnchor/ValWithAnchorByName.java b/test/jdk/java/security/cert/CertPathValidator/trustAnchor/ValWithAnchorByName.java index 29abfb9f551d4..c57abdf243d31 100644 --- a/test/jdk/java/security/cert/CertPathValidator/trustAnchor/ValWithAnchorByName.java +++ b/test/jdk/java/security/cert/CertPathValidator/trustAnchor/ValWithAnchorByName.java @@ -52,8 +52,8 @@ public class ValWithAnchorByName { // The following certificates and OCSP responses were captured from // a test run that used certificates and responses generated by - // sun.security.testlibrary.CertificateBuilder and - // sun.security.testlibrary.SimpleOCSPServer. + // jdk.test.lib.security.CertificateBuilder and + // jdk.test.lib.security.SimpleOCSPServer. // Subject: CN=SSLCertificate, O=SomeCompany // Issuer: CN=Intermediate CA Cert, O=SomeCompany diff --git a/test/jdk/java/security/cert/CertificateFactory/SlowStream.java b/test/jdk/java/security/cert/CertificateFactory/SlowStream.java index 5de34e21340d5..ad59c7662117c 100644 --- a/test/jdk/java/security/cert/CertificateFactory/SlowStream.java +++ b/test/jdk/java/security/cert/CertificateFactory/SlowStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,31 +21,74 @@ * questions. */ -import java.io.*; -import java.security.cert.*; +/* + * @test + * @bug 6813340 + * @summary X509Factory should not depend on is.available()==0 + */ -class SlowStreamReader { +import java.io.File; +import java.io.FileInputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.security.cert.CertificateFactory; +import java.util.concurrent.atomic.AtomicBoolean; +public class SlowStream { public static void main(String[] args) throws Exception { - CertificateFactory factory = CertificateFactory.getInstance("X.509"); - if (factory.generateCertificates(System.in).size() != 5) { - throw new Exception("Not all certs read"); - } - } -} + final var outputStream = new PipedOutputStream(); + final var inputStream = new PipedInputStream(outputStream); -class SlowStreamWriter { - public static void main(String[] args) throws Exception { - for (int i=0; i<5; i++) { - FileInputStream fin = new FileInputStream(new File(new File( - System.getProperty("test.src", "."), "openssl"), "pem")); - byte[] buffer = new byte[4096]; - while (true) { - int len = fin.read(buffer); - if (len < 0) break; - System.out.write(buffer, 0, len); + final var failed = new AtomicBoolean(false); + + final var writer = new Thread(() -> { + try { + for (int i = 0; i < 5; i++) { + try (final var fin = new FileInputStream(new File( + new File(System.getProperty("test.src", "."), + "openssl"), + "pem"))) { + + fin.transferTo(outputStream); + + Thread.sleep(2000); + } + } + outputStream.close(); + } catch (final Exception e) { + System.out.println("Writer Thread Error: "); + e.printStackTrace(System.out); + failed.set(true); + } + }); + + final var reader = new Thread(() -> { + try { + final var factory = CertificateFactory.getInstance("X.509"); + final var numOfCerts = factory.generateCertificates(inputStream).size(); + if (numOfCerts != 5) { + throw new Exception( + String.format("Not all certs read. %d found 5 expected", numOfCerts) + ); + } + inputStream.close(); + } catch (final Exception e) { + System.out.println("Reader Thread Error: "); + e.printStackTrace(System.out); + failed.set(true); } - Thread.sleep(2000); + }); + + writer.start(); + reader.start(); + + writer.join(); + reader.join(); + + if (failed.get()) { + throw new RuntimeException( + "The test failed, please check the reader and writer threads output" + ); } } } diff --git a/test/jdk/java/security/cert/CertificateFactory/slowstream.sh b/test/jdk/java/security/cert/CertificateFactory/slowstream.sh deleted file mode 100644 index 8c1cbfed125e2..0000000000000 --- a/test/jdk/java/security/cert/CertificateFactory/slowstream.sh +++ /dev/null @@ -1,54 +0,0 @@ -# -# Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -# @test -# @bug 6813340 -# @summary X509Factory should not depend on is.available()==0 - -if [ "${TESTSRC}" = "" ] ; then - TESTSRC="." -fi -if [ "${TESTJAVA}" = "" ] ; then - echo "TESTJAVA not set. Test cannot execute." - echo "FAILED!!!" - exit 1 -fi -if [ "${COMPILEJAVA}" = "" ]; then - COMPILEJAVA="${TESTJAVA}" -fi - -# set platform-dependent variables -OS=`uname -s` -case "$OS" in - Windows_* ) - FS="\\" - ;; - * ) - FS="/" - ;; -esac - -${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . \ - ${TESTSRC}${FS}SlowStream.java -${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} ${TESTJAVAOPTS} -Dtest.src=${TESTSRC} SlowStreamWriter | \ - ${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} ${TESTJAVAOPTS} SlowStreamReader diff --git a/test/jdk/java/text/Format/ChoiceFormat/ParseTest.java b/test/jdk/java/text/Format/ChoiceFormat/ParseTest.java new file mode 100644 index 0000000000000..17ead8f450c99 --- /dev/null +++ b/test/jdk/java/text/Format/ChoiceFormat/ParseTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8353585 + * @summary Basic parse tests. Enforce regular behavior, no match, and multi match. + * @run junit ParseTest + */ + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.text.ChoiceFormat; +import java.text.ParsePosition; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ParseTest { + + // Ensure that the parsed text produces the expected number + // i.e. return limit corresponding to format matched + @ParameterizedTest + @MethodSource + void parseTest(String pattern, String text, Double expected, int index) { + var pp = new ParsePosition(index); + var fmt = new ChoiceFormat(pattern); + assertEquals(expected, fmt.parse(text, pp), "Incorrect limit returned"); + if (expected.equals(Double.NaN)) { // AKA failed parse + assertEquals(index, pp.getErrorIndex(), + "Failed parse produced incorrect error index"); + } else { + assertEquals(-1, pp.getErrorIndex(), + "Error index should remain -1 on match"); + } + } + + private static Stream parseTest() { + return Stream.of( + Arguments.of("1#foo", "foo", Double.NaN, -1), + Arguments.of("1#baz", "foo bar baz", Double.NaN, 20), + Arguments.of("1#baz", "foo bar baz", 1d, 8), + Arguments.of("1#baz", "foo baz quux", Double.NaN, 8), + Arguments.of("1#a", "", Double.NaN, 0), + Arguments.of("1#a", "a", 1d, 0), + Arguments.of("1# ", " ", 1d, 0), + Arguments.of("1#a|2#a", "a", 1d, 0), + Arguments.of("1#a|2#aa", "aa", 2d, 0), + Arguments.of("1#a|2#aa", "aabb", 2d, 0), + Arguments.of("1#a|2#aa", "bbaa", Double.NaN, 0), + Arguments.of("1#aa|2#aaa", "a", Double.NaN, 0) + ); + } +} diff --git a/test/jdk/java/text/Format/ChoiceFormat/PatternsTest.java b/test/jdk/java/text/Format/ChoiceFormat/PatternsTest.java new file mode 100644 index 0000000000000..dc514f5459f90 --- /dev/null +++ b/test/jdk/java/text/Format/ChoiceFormat/PatternsTest.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6801704 + * @summary Test the expected behavior for a wide range of patterns (both + * correct and incorrect). This test documents the behavior of incorrect + * ChoiceFormat patterns either throwing an exception, or discarding + * the incorrect portion of a pattern. + * @run junit PatternsTest + */ + +import java.text.ChoiceFormat; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +public class PatternsTest { + + private static final String ERR1 = + "Each interval must contain a number before a format"; + private static final String ERR2 = + "Incorrect order of intervals, must be in ascending order"; + + // Check that some valid patterns do not throw an exception. Check + // them against the expected values they should be formatted as. + @ParameterizedTest + @MethodSource + public void validPatternsTest(String pattern, String[] expectedValues) { + var fmt = new ChoiceFormat(pattern); + for (int i=1; i<=expectedValues.length; i++) { + assertEquals(expectedValues[i-1], fmt.format(i), + String.format("ChoiceFormat formatted %s incorrectly:", i)); + } + } + + // Valid patterns ranging from normal appearing to odd. These should not + // throw an exception or discard any portions of the pattern. + private static Arguments[] validPatternsTest() { + return new Arguments[] { + // Multi pattern with trailing empty string Format + arguments("1#foo|2#bar|3#", new String[]{"foo", "bar", ""}), + // Multi patten with trailing '|' + arguments("1#foo|2#bar|", new String[]{"foo", "bar"}), + // Using a '>' (not a Relation) within a Format + arguments("1#foo|2#bar>", new String[]{"foo", "bar>"}), + // Standard Multi Pattern + arguments("1#foo|2#bar", new String[]{"foo", "bar"}), + // Same numerical value Limits, different Relations + arguments("1#foo|1 new ChoiceFormat(pattern)); + assertEquals(errMsg, ex.getMessage()); + } + + // Variety of patterns that break the ChoiceFormat pattern syntax and throw + // an exception. + private static Arguments[] invalidPatternsThrowsTest() { + return new Arguments[] { + arguments("#foo", ERR1), // No Limit + arguments("0#foo|#|1#bar", ERR1), // Missing Relation in SubPattern + arguments("#|", ERR1), // Missing Limit + arguments("##|", ERR1), // Double Relations + arguments("0#foo1#", ERR1), // SubPattern not separated by '|' + arguments("0#foo#", ERR1), // Using a Relation in a format + arguments("0#test|#", ERR1), // SubPattern missing Limit + arguments("0#foo|3#bar|1#baz", ERR2), // Non-ascending Limits + }; + } + + // Check that the incorrect pattern discards the trailing incorrect portion. + // These incorrect patterns should ideally throw an exception, but for + // behavioral compatibility reasons do not. + @ParameterizedTest + @MethodSource + public void invalidPatternsDiscardedTest(String brokenPattern, String actualPattern) { + var cf1 = new ChoiceFormat(brokenPattern); + var cf2 = new ChoiceFormat(actualPattern); + assertEquals(cf2, cf1, + String.format("Expected %s, but got %s", cf2.toPattern(), cf1.toPattern())); + } + + // Variety of incorrect patterns with the actual expected pattern + // after discarding occurs. + private static Arguments[] invalidPatternsDiscardedTest() { + return new Arguments[] { + // Incomplete SubPattern at the end of the Pattern + arguments("0#foo|1#bar|baz", "0#foo|1#bar"), + + // --- These throw an ArrayIndexOutOfBoundsException + // when attempting to format with them --- + // SubPattern with only a Limit (which is interpreted as a Format) + arguments("0", ""), + // SubPattern with only a Format + arguments("foo", ""), + // empty string + arguments("", "") + }; + } + + // Calling format() with empty limits and formats + // throws an ArrayIndexOutOfBoundsException + @Test + public void emptyLimitsAndFormatsTest() { + var cf1 = new ChoiceFormat(""); + assertThrows(ArrayIndexOutOfBoundsException.class, + () -> cf1.format(1)); + + var cf2 = new ChoiceFormat(new double[]{}, new String[]{}); + assertThrows(ArrayIndexOutOfBoundsException.class, + () -> cf2.format(2)); + } +} diff --git a/test/jdk/java/text/Format/DateFormat/DateFormatRegression.java b/test/jdk/java/text/Format/DateFormat/DateFormatRegression.java index c60a95d065dcb..ce6e384001570 100644 --- a/test/jdk/java/text/Format/DateFormat/DateFormatRegression.java +++ b/test/jdk/java/text/Format/DateFormat/DateFormatRegression.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,7 +34,7 @@ * @bug 4029195 4052408 4056591 4059917 4060212 4061287 4065240 4071441 4073003 * 4089106 4100302 4101483 4103340 4103341 4104136 4104522 4106807 4108407 * 4134203 4138203 4148168 4151631 4151706 4153860 4162071 4182066 4209272 4210209 - * 4213086 4250359 4253490 4266432 4406615 4413980 8008577 8305853 + * 4213086 4250359 4253490 4266432 4406615 4413980 8008577 8305853 8347841 * @library /java/text/testlib * @run junit/othervm -Djava.locale.providers=COMPAT,SPI DateFormatRegression */ @@ -274,7 +274,7 @@ public void Test4065240() { try { Locale curLocale = new Locale("de","DE"); Locale.setDefault(curLocale); - TimeZone.setDefault(TimeZone.getTimeZone("EST")); + TimeZone.setDefault(TimeZone.getTimeZone("America/Panama")); curDate = new Date(98, 0, 1); shortdate = DateFormat.getDateInstance(DateFormat.SHORT); fulldate = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG @@ -453,7 +453,7 @@ public void Test4413980() { TimeZone savedTimeZone = TimeZone.getDefault(); try { boolean pass = true; - String[] IDs = new String[] {"Undefined", "PST", "US/Pacific", + String[] IDs = new String[] {"Undefined", "America/Los_Angeles", "US/Pacific", "GMT+3:00", "GMT-01:30"}; for (int i = 0; i < IDs.length; i++) { TimeZone tz = TimeZone.getTimeZone(IDs[i]); @@ -543,7 +543,7 @@ public void Test4103340() { public void Test4103341() { TimeZone saveZone =TimeZone.getDefault(); try { - TimeZone.setDefault(TimeZone.getTimeZone("CST")); + TimeZone.setDefault(TimeZone.getTimeZone("America/Chicago")); SimpleDateFormat simple = new SimpleDateFormat("MM/dd/yyyy HH:mm"); if (!simple.getTimeZone().equals(TimeZone.getDefault())) fail("Fail: SimpleDateFormat not using default zone"); @@ -794,7 +794,7 @@ public void Test4406615() { Locale savedLocale = Locale.getDefault(); TimeZone savedTimeZone = TimeZone.getDefault(); Locale.setDefault(Locale.US); - TimeZone.setDefault(TimeZone.getTimeZone("PST")); + TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles")); Date d1, d2; String dt = "Mon, 1 Jan 2001 00:00:00"; @@ -1096,7 +1096,7 @@ public void Test4261506() { // XXX: Test assumes "PST" is not TimeZoneNames_ja. Need to // pick up another time zone when L10N is done to that file. - TimeZone.setDefault(TimeZone.getTimeZone("PST")); + TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles")); SimpleDateFormat fmt = new SimpleDateFormat("yy/MM/dd hh:ss zzz", Locale.JAPAN); @SuppressWarnings("deprecation") String result = fmt.format(new Date(1999 - 1900, 0, 1)); diff --git a/test/jdk/java/text/Format/DateFormat/DateFormatTest.java b/test/jdk/java/text/Format/DateFormat/DateFormatTest.java index 9e8c8684dc90b..1c4ea66d8b36b 100644 --- a/test/jdk/java/text/Format/DateFormat/DateFormatTest.java +++ b/test/jdk/java/text/Format/DateFormat/DateFormatTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,14 +24,16 @@ /** * @test * @bug 4052223 4089987 4469904 4326988 4486735 8008577 8045998 8140571 - * 8190748 8216969 + * 8190748 8216969 8347841 * @summary test DateFormat and SimpleDateFormat. * @modules jdk.localedata * @run junit/othervm -Djava.locale.providers=COMPAT,SPI DateFormatTest */ -import java.util.*; +import java.time.ZoneId; import java.text.*; +import java.util.*; +import java.util.function.Predicate; import static java.util.GregorianCalendar.*; import org.junit.jupiter.api.Test; @@ -89,7 +91,9 @@ public void TestWallyWedel() /* * A String array for the time zone ids. */ - String[] ids = TimeZone.getAvailableIDs(); + String[] ids = Arrays.stream(TimeZone.getAvailableIDs()) + .filter(Predicate.not(ZoneId.SHORT_IDS::containsKey)) + .toArray(String[]::new); /* * How many ids do we have? */ @@ -179,7 +183,7 @@ public void TestTwoDigitYearDSTParse() //logln(fmt.format(date)); // This shows what the current locale format is //logln(((SimpleDateFormat)fmt).toPattern()); TimeZone save = TimeZone.getDefault(); - TimeZone PST = TimeZone.getTimeZone("PST"); + TimeZone PST = TimeZone.getTimeZone("America/Los_Angeles"); String s = "03-Apr-04 2:20:47 o'clock AM PST"; int hour = 2; try { @@ -271,7 +275,7 @@ public void TestFieldPosition() "0034", "0012", "0513", "Pacific Daylight Time", }; Date someDate = new Date(871508052513L); - TimeZone PST = TimeZone.getTimeZone("PST"); + TimeZone PST = TimeZone.getTimeZone("America/Los_Angeles"); for (int j = 0, exp = 0; j < dateFormats.length; ++j) { DateFormat df = dateFormats[j]; if (!(df instanceof SimpleDateFormat)) { diff --git a/test/jdk/java/text/Format/DateFormat/SDFTCKZoneNamesTest.java b/test/jdk/java/text/Format/DateFormat/SDFTCKZoneNamesTest.java index a9f2e30a4d05b..aea3706f70efe 100644 --- a/test/jdk/java/text/Format/DateFormat/SDFTCKZoneNamesTest.java +++ b/test/jdk/java/text/Format/DateFormat/SDFTCKZoneNamesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,12 +23,13 @@ /** * @test - * @bug 8218948 + * @bug 8218948 8347841 * @summary TCK tests that check the time zone names between DFS.getZoneStrings() * and SDF.format("z*") * @run main SDFTCKZoneNamesTest */ import java.text.*; +import java.time.ZoneId; import java.util.Calendar; import java.util.Date; import java.util.List; @@ -325,6 +326,9 @@ public void SimpleDateFormat0062() { SimpleDateFormat sdf = new SimpleDateFormat(); Date date = new Date(1234567890); for (String[] tz : sdf.getDateFormatSymbols().getZoneStrings()) { + if (ZoneId.SHORT_IDS.containsKey(tz[0])) { + continue; + } sdf.setTimeZone(TimeZone.getTimeZone(tz[0])); for (int i = 0; i < patterns.length && passed; i++) { StringBuffer result = new StringBuffer("qwerty"); diff --git a/test/jdk/java/text/Format/DateFormat/bug4358730.java b/test/jdk/java/text/Format/DateFormat/bug4358730.java index 3f0a0f969b68e..9e362315ad499 100644 --- a/test/jdk/java/text/Format/DateFormat/bug4358730.java +++ b/test/jdk/java/text/Format/DateFormat/bug4358730.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,7 +31,7 @@ /** * @test - * @bug 4358730 + * @bug 4358730 8347841 * @summary test that confirms Zero-Padding on year. * @run junit bug4358730 */ @@ -56,7 +56,7 @@ public void Test4358730() { Locale saveLocale = Locale.getDefault(); try { - TimeZone.setDefault(TimeZone.getTimeZone("PST")); + TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles")); Locale.setDefault(new Locale("en", "US")); SimpleDateFormat sdf = new SimpleDateFormat(); diff --git a/test/jdk/java/text/Format/MessageFormat/SerializationTest.java b/test/jdk/java/text/Format/MessageFormat/SerializationTest.java index 9191c5caef35f..ac5fd4d56a7c4 100644 --- a/test/jdk/java/text/Format/MessageFormat/SerializationTest.java +++ b/test/jdk/java/text/Format/MessageFormat/SerializationTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8331446 + * @bug 8331446 8340554 * @summary Check correctness of deserialization * @run junit SerializationTest */ @@ -70,7 +70,13 @@ private static Stream serializationRoundTrip() { // With null locale. (NPE not thrown, if no format defined) new MessageFormat("{1} {0} foo", null), // With formats - new MessageFormat("{0,number,short} {0} {1,date,long} foo") + new MessageFormat("{0,number,short} {0} {1,date,long} foo"), + // Offset equal to pattern length (0) + new MessageFormat("{0}"), + // Offset equal to pattern length (1) + new MessageFormat("X{0}"), + // Offset 1 under pattern length + new MessageFormat("X{0}X") ); } diff --git a/test/jdk/java/util/Base64/TestBase64.java b/test/jdk/java/util/Base64/TestBase64.java index e360a2c584d2b..81d2aa8580656 100644 --- a/test/jdk/java/util/Base64/TestBase64.java +++ b/test/jdk/java/util/Base64/TestBase64.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -146,7 +146,7 @@ public static void main(String args[]) throws Throwable { testDecoderKeepsAbstinence(Base64.getMimeDecoder()); // tests patch addressing JDK-8222187 - // https://bugs.openjdk.java.net/browse/JDK-8222187 + // https://bugs.openjdk.org/browse/JDK-8222187 testJDK_8222187(); } diff --git a/test/jdk/java/util/Calendar/CalendarRegression.java b/test/jdk/java/util/Calendar/CalendarRegression.java index f8018a7a26cec..6be8b50a63efe 100644 --- a/test/jdk/java/util/Calendar/CalendarRegression.java +++ b/test/jdk/java/util/Calendar/CalendarRegression.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,7 @@ * 4174361 4177484 4197699 4209071 4288792 4328747 4413980 4546637 4623997 * 4685354 4655637 4683492 4080631 4080631 4167995 4340146 4639407 * 4652815 4652830 4740554 4936355 4738710 4633646 4846659 4822110 4960642 - * 4973919 4980088 4965624 5013094 5006864 8152077 + * 4973919 4980088 4965624 5013094 5006864 8152077 8347841 * @library /java/text/testlib * @run junit CalendarRegression */ @@ -42,6 +42,8 @@ import java.text.DateFormat; import java.text.NumberFormat; import java.text.SimpleDateFormat; +import java.time.ZoneId; +import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; @@ -50,6 +52,7 @@ import java.util.Map; import java.util.SimpleTimeZone; import java.util.TimeZone; +import java.util.function.Predicate; import static java.util.Calendar.*; @@ -75,7 +78,9 @@ public class CalendarRegression { public void Test4031502() { // This bug actually occurs on Windows NT as well, and doesn't // require the host zone to be set; it can be set in Java. - String[] ids = TimeZone.getAvailableIDs(); + String[] ids = Arrays.stream(TimeZone.getAvailableIDs()) + .filter(Predicate.not(ZoneId.SHORT_IDS::containsKey)) + .toArray(String[]::new); boolean bad = false; for (int i = 0; i < ids.length; ++i) { TimeZone zone = TimeZone.getTimeZone(ids[i]); @@ -489,7 +494,7 @@ public void Test4095407() { @Test public void Test4096231() { TimeZone GMT = TimeZone.getTimeZone("GMT"); - TimeZone PST = TimeZone.getTimeZone("PST"); + TimeZone PST = TimeZone.getTimeZone("America/Los_Angeles"); int sec = 0, min = 0, hr = 0, day = 1, month = 10, year = 1997; Calendar cal1 = new GregorianCalendar(PST); @@ -838,7 +843,7 @@ public void Test4114578() { TimeZone saveZone = TimeZone.getDefault(); boolean fail = false; try { - TimeZone.setDefault(TimeZone.getTimeZone("PST")); + TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles")); Calendar cal = Calendar.getInstance(); long onset = new Date(98, APRIL, 5, 1, 0).getTime() + ONE_HOUR; long cease = new Date(98, OCTOBER, 25, 0, 0).getTime() + 2 * ONE_HOUR; @@ -1163,8 +1168,8 @@ public void Test4147269() { @Test public void Test4149677() { TimeZone[] zones = {TimeZone.getTimeZone("GMT"), - TimeZone.getTimeZone("PST"), - TimeZone.getTimeZone("EAT")}; + TimeZone.getTimeZone("America/Los_Angeles"), + TimeZone.getTimeZone("Africa/Addis_Ababa")}; for (int i = 0; i < zones.length; ++i) { GregorianCalendar calendar = new GregorianCalendar(zones[i]); @@ -1197,7 +1202,7 @@ public void Test4149677() { @Test public void Test4162587() { TimeZone savedTz = TimeZone.getDefault(); - TimeZone tz = TimeZone.getTimeZone("PST"); + TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles"); TimeZone.setDefault(tz); GregorianCalendar cal = new GregorianCalendar(tz); Date d; @@ -1511,8 +1516,8 @@ public void Test4174361() { */ @Test public void Test4177484() { - TimeZone PST = TimeZone.getTimeZone("PST"); - TimeZone EST = TimeZone.getTimeZone("EST"); + TimeZone PST = TimeZone.getTimeZone("America/Los_Angeles"); + TimeZone EST = TimeZone.getTimeZone("America/Panama"); Calendar cal = Calendar.getInstance(PST, Locale.US); cal.clear(); @@ -1770,7 +1775,7 @@ public void Test4413980() { TimeZone savedTimeZone = TimeZone.getDefault(); try { boolean pass = true; - String[] IDs = new String[]{"Undefined", "PST", "US/Pacific", + String[] IDs = new String[]{"Undefined", "America/Los_Angeles", "US/Pacific", "GMT+3:00", "GMT-01:30"}; for (int i = 0; i < IDs.length; i++) { TimeZone tz = TimeZone.getTimeZone(IDs[i]); diff --git a/test/jdk/java/util/Calendar/JavatimeTest.java b/test/jdk/java/util/Calendar/JavatimeTest.java index 8f32801a307d0..115b1e4e8b876 100644 --- a/test/jdk/java/util/Calendar/JavatimeTest.java +++ b/test/jdk/java/util/Calendar/JavatimeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* *@test - *@bug 8007520 8008254 + *@bug 8007520 8008254 8347841 *@summary Test those bridge methods to/from java.time date/time classes * @key randomness */ @@ -107,23 +107,22 @@ public static void main(String[] args) throws Throwable { ///////////// java.util.TimeZone ///////////////////////// for (String zidStr : TimeZone.getAvailableIDs()) { - // TBD: tzdt intergration + if (ZoneId.SHORT_IDS.containsKey(zidStr)) { + continue; + } + // TBD: tzdt integration if (zidStr.startsWith("SystemV") || zidStr.contains("Riyadh8") - || zidStr.equals("US/Pacific-New") - || zidStr.equals("EST") - || zidStr.equals("HST") - || zidStr.equals("MST")) { + || zidStr.equals("US/Pacific-New")) { continue; } - ZoneId zid = ZoneId.of(zidStr, ZoneId.SHORT_IDS); + ZoneId zid = ZoneId.of(zidStr); if (!zid.equals(TimeZone.getTimeZone(zid).toZoneId())) { throw new RuntimeException("FAILED: zid -> tz -> zid :" + zidStr); } TimeZone tz = TimeZone.getTimeZone(zidStr); // no round-trip for alias and "GMT" if (!tz.equals(TimeZone.getTimeZone(tz.toZoneId())) - && !ZoneId.SHORT_IDS.containsKey(zidStr) && !zidStr.startsWith("GMT")) { throw new RuntimeException("FAILED: tz -> zid -> tz :" + zidStr); } diff --git a/test/jdk/java/util/Calendar/bug4316678.java b/test/jdk/java/util/Calendar/bug4316678.java index f6bd4b87cae4f..b1c24a6a0c4a1 100644 --- a/test/jdk/java/util/Calendar/bug4316678.java +++ b/test/jdk/java/util/Calendar/bug4316678.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 4316678 + * @bug 4316678 8347841 * @summary test that Calendar's Serialization works correctly. * @run junit bug4316678 */ @@ -53,7 +53,7 @@ public class bug4316678 { // Set custom JVM default TimeZone @BeforeAll static void initAll() { - TimeZone.setDefault(TimeZone.getTimeZone("PST")); + TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles")); } // Restore JVM default Locale and TimeZone diff --git a/test/jdk/java/util/Calendar/bug4372743.java b/test/jdk/java/util/Calendar/bug4372743.java index 193ad53d813b5..d3d2b39b5fc63 100644 --- a/test/jdk/java/util/Calendar/bug4372743.java +++ b/test/jdk/java/util/Calendar/bug4372743.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 4372743 + * @bug 4372743 8347841 * @summary test that checks transitions of ERA and YEAR which are caused by add(MONTH). * @run junit bug4372743 */ @@ -67,7 +67,7 @@ public class bug4372743 { // Set custom JVM default timezone @BeforeAll static void initAll() { - TimeZone.setDefault(TimeZone.getTimeZone("PST")); + TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles")); } // Restore JVM default timezone diff --git a/test/jdk/java/util/Collections/RotateHuge.java b/test/jdk/java/util/Collections/RotateHuge.java new file mode 100644 index 0000000000000..44368aff266d2 --- /dev/null +++ b/test/jdk/java/util/Collections/RotateHuge.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8314236 + * @summary Overflow in Collections.rotate + */ + +import java.util.AbstractList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.RandomAccess; + +public class RotateHuge { + + private static final class MockList extends AbstractList + implements RandomAccess { + private final int size; + + public MockList(final int size) { + if (size < 0) + throw new IllegalArgumentException("Illegal size: " + size); + this.size = size; + } + + @Override + public Object get(final int index) { + Objects.checkIndex(index, size); + return null; + } + + @Override + public Object set(final int index, final Object element) { + Objects.checkIndex(index, size); + return null; + } + + @Override + public int size() { + return size; + } + } + + public static void main(final String[] args) { + testRotate((1 << 30) + 1, -(1 << 30) - 2); + testRotate((1 << 30) + 1, 1 << 30); + testRotate(Integer.MAX_VALUE, Integer.MIN_VALUE); + testRotate(Integer.MAX_VALUE, Integer.MIN_VALUE + 3); + testRotate(Integer.MAX_VALUE, 2); + testRotate(Integer.MAX_VALUE, Integer.MAX_VALUE - 1); + } + + /* + * This test covers only index computations. + * Correctness of elements rotation is not checked. + */ + private static void testRotate(final int size, final int distance) { + final List list = new MockList(size); + Collections.rotate(list, distance); + } +} diff --git a/test/jdk/java/util/Currency/CurrencyTest.java b/test/jdk/java/util/Currency/CurrencyTest.java index deb1854ffd897..16c329ef5356f 100644 --- a/test/jdk/java/util/Currency/CurrencyTest.java +++ b/test/jdk/java/util/Currency/CurrencyTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,7 @@ * @test * @bug 4290801 4692419 4693631 5101540 5104960 6296410 6336600 6371531 * 6488442 7036905 8008577 8039317 8074350 8074351 8150324 8167143 - * 8264792 8334653 + * 8264792 8334653 8353713 * @summary Basic tests for Currency class. * @modules java.base/java.util:open * jdk.localedata @@ -80,14 +80,41 @@ private static Stream validCurrencies() { // Calling getInstance() with an invalid currency code should throw an IAE @ParameterizedTest - @MethodSource("invalidCurrencies") + @MethodSource("non4217Currencies") public void invalidCurrencyTest(String currencyCode) { - assertThrows(IllegalArgumentException.class, () -> + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> Currency.getInstance(currencyCode), "getInstance() did not throw IAE"); + assertEquals("The input currency code: \"%s\" is not a valid ISO 4217 code" + .formatted(currencyCode), ex.getMessage()); } - private static Stream invalidCurrencies() { - return Stream.of("AQD", "US$", "\u20AC"); + private static Stream non4217Currencies() { + return Stream.of("AQD", "US$"); + } + + // Provide 3 length code, but first 2 chars should not be able to index + // the main table, thus resulting as incorrect country code + @Test + void invalidCountryInCodeTest() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> + Currency.getInstance("..A"), "getInstance() did not throw IAE"); + assertEquals("The country code: \"%s\" is not a valid ISO 3166 code" + .formatted(".."), ex.getMessage()); + } + + // Calling getInstance() with a currency code not 3 characters long should throw + // an IAE + @ParameterizedTest + @MethodSource("invalidLengthCurrencies") + public void invalidCurrencyLengthTest(String currencyCode) { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> + Currency.getInstance(currencyCode), "getInstance() did not throw IAE"); + assertEquals("The input currency code: \"%s\" must have a length of 3 characters" + .formatted(currencyCode), ex.getMessage()); + } + + private static Stream invalidLengthCurrencies() { + return Stream.of("\u20AC", "", "12345"); } } @@ -144,7 +171,10 @@ public void localeMappingTest() { ctryLength == 3 || // UN M.49 code ctryCode.matches("AA|Q[M-Z]|X[A-JL-Z]|ZZ" + // user defined codes, excluding "XK" (Kosovo) "AC|CP|DG|EA|EU|FX|IC|SU|TA|UK")) { // exceptional reservation codes - assertThrows(IllegalArgumentException.class, () -> Currency.getInstance(locale), "Did not throw IAE"); + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> Currency.getInstance(locale), "Did not throw IAE"); + assertEquals("The country of the input locale: \"%s\" is not a valid ISO 3166 country code" + .formatted(locale), ex.getMessage()); } else { goodCountries++; Currency currency = Currency.getInstance(locale); @@ -160,11 +190,16 @@ public void localeMappingTest() { } } - // Check an invalid country code + // Check an invalid country code supplied via the region override @Test - public void invalidCountryTest() { - assertThrows(IllegalArgumentException.class, ()-> - Currency.getInstance(new Locale("", "EU")), "Did not throw IAE"); + public void invalidCountryRegionOverrideTest() { + // Override US with nonsensical country + var loc = Locale.forLanguageTag("en-US-u-rg-XXzzzz"); + Locale l = new Locale("", "EU"); + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + ()-> Currency.getInstance(loc), "Did not throw IAE"); + assertEquals("The country of the input locale: \"%s\" is not a valid ISO 3166 country code" + .formatted(loc), ex.getMessage()); } // Ensure a selection of countries have the expected currency diff --git a/test/jdk/java/util/Currency/ISO4217-list-one.txt b/test/jdk/java/util/Currency/ISO4217-list-one.txt index 1912b5cc7dbb9..ca4bfd6dc94fb 100644 --- a/test/jdk/java/util/Currency/ISO4217-list-one.txt +++ b/test/jdk/java/util/Currency/ISO4217-list-one.txt @@ -1,12 +1,12 @@ # # -# Amendments up until ISO 4217 AMENDMENT NUMBER 177 -# (As of 20 June 2024) +# Amendments up until ISO 4217 AMENDMENT NUMBER 180 +# (As of 22 September 2025) # # Version FILEVERSION=3 -DATAVERSION=177 +DATAVERSION=180 # ISO 4217 currency data AF AFN 971 2 @@ -44,7 +44,7 @@ BV NOK 578 2 BR BRL 986 2 IO USD 840 2 BN BND 96 2 -BG BGN 975 2 +BG BGN 975 2 2025-12-31-22-00-00 EUR 978 2 BF XOF 952 0 BI BIF 108 0 KH KHR 116 2 @@ -69,7 +69,7 @@ CR CRC 188 2 CI XOF 952 0 HR EUR 978 2 CU CUP 192 2 -CW ANG 532 2 2025-04-01-04-00-00 XCG 532 2 +CW XCG 532 2 CY EUR 978 2 CZ CZK 203 2 DK DKK 208 2 @@ -233,7 +233,7 @@ LK LKR 144 2 SD SDG 938 2 SR SRD 968 2 SJ NOK 578 2 -SX ANG 532 2 2025-04-01-04-00-00 XCG 532 2 +SX XCG 532 2 SZ SZL 748 2 SE SEK 752 2 CH CHF 756 2 diff --git a/test/jdk/java/util/Currency/PropertiesTestRun.java b/test/jdk/java/util/Currency/PropertiesTestRun.java index 6f2ea28a90d3a..baec22f3e86f6 100644 --- a/test/jdk/java/util/Currency/PropertiesTestRun.java +++ b/test/jdk/java/util/Currency/PropertiesTestRun.java @@ -116,7 +116,7 @@ private static Stream PropertiesTestMethods() { // Launch a PropertiesTest method using the TEST JDK private static void executeTestJDKMethod(String... params) throws Throwable { - int exitStatus = ProcessTools.executeTestJvm(params).getExitValue(); + int exitStatus = ProcessTools.executeTestJava(params).getExitValue(); if (exitStatus != 0) { fail("Process started with: " + Arrays.toString(params) + " failed"); } @@ -126,7 +126,7 @@ private static void executeTestJDKMethod(String... params) throws Throwable { private static void executeWritableJDKMethod(String... params) throws Throwable { // Need to include WritableJDK javapath, TEST JDK classpath String[] allParams = new String[3+params.length+Utils.getTestJavaOpts().length]; - // We don't use executeTestJvm() because we want to point to separate JDK java path + // We don't use executeTestJava() because we want to point to separate JDK java path allParams[0] = WRITABLE_JDK_JAVA_PATH; allParams[1] = "-cp"; allParams[2] = System.getProperty("java.class.path"); diff --git a/test/jdk/java/util/Currency/ValidateISO4217.java b/test/jdk/java/util/Currency/ValidateISO4217.java index 01c225e531c67..24b81f8951c71 100644 --- a/test/jdk/java/util/Currency/ValidateISO4217.java +++ b/test/jdk/java/util/Currency/ValidateISO4217.java @@ -26,6 +26,7 @@ * @bug 4691089 4819436 4942982 5104960 6544471 6627549 7066203 7195759 * 8039317 8074350 8074351 8145952 8187946 8193552 8202026 8204269 * 8208746 8209775 8264792 8274658 8283277 8296239 8321480 8334653 + * 8356096 8368308 * @summary Validate ISO 4217 data for Currency class. * @modules java.base/java.util:open * jdk.localedata @@ -86,10 +87,10 @@ public class ValidateISO4217 { private static final Set testCurrencies = new HashSet<>(); // Codes that are obsolete, do not have related country, extra currency private static final String otherCodes = - "ADP-AFA-ATS-AYM-AZM-BEF-BGL-BOV-BYB-BYR-CHE-CHW-CLF-COU-CUC-CYP-" + "ADP-AFA-ATS-AYM-AZM-BEF-BGL-BGN-BOV-BYB-BYR-CHE-CHW-CLF-COU-CUC-CYP-" + "DEM-EEK-ESP-FIM-FRF-GHC-GRD-GWP-HRK-IEP-ITL-LTL-LUF-LVL-MGF-MRO-MTL-MXV-MZM-NLG-" + "PTE-ROL-RUR-SDD-SIT-SLL-SKK-SRG-STD-TMM-TPE-TRL-VEF-UYI-USN-USS-VEB-VED-" - + "XAG-XAU-XBA-XBB-XBC-XBD-XDR-XFO-XFU-XPD-XPT-XSU-XTS-XUA-XXX-" + + "XAD-XAG-XAU-XBA-XBB-XBC-XBD-XDR-XFO-XFU-XPD-XPT-XSU-XTS-XUA-XXX-" + "YUM-ZMK-ZWD-ZWL-ZWN-ZWR"; private static final String[][] extraCodes = { /* Defined in ISO 4217 list, but don't have code and minor unit info. */ diff --git a/test/jdk/java/util/Date/Bug4955000.java b/test/jdk/java/util/Date/Bug4955000.java index 4339f848f580a..f05e6ccd3f80a 100644 --- a/test/jdk/java/util/Date/Bug4955000.java +++ b/test/jdk/java/util/Date/Bug4955000.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 4955000 + * @bug 4955000 8347841 * @summary Make sure that a Date and a GregorianCalendar produce the * same date/time. Both are new implementations in 1.5. */ @@ -42,7 +42,7 @@ public class Bug4955000 { public static void main(String[] args) { TimeZone defaultTZ = TimeZone.getDefault(); try { - TimeZone.setDefault(TimeZone.getTimeZone("NST")); + TimeZone.setDefault(TimeZone.getTimeZone("Pacific/Auckland")); GregorianCalendar gc = new GregorianCalendar(TimeZone.getTimeZone("UTC")); // Date1025 int[] years1 = { diff --git a/test/jdk/java/util/Date/DateRegression.java b/test/jdk/java/util/Date/DateRegression.java index 8fe89d9d59bdf..eda575743e326 100644 --- a/test/jdk/java/util/Date/DateRegression.java +++ b/test/jdk/java/util/Date/DateRegression.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,8 @@ /* * @test - * @bug 4023247 4027685 4032037 4072029 4073003 4118010 4120606 4133833 4136916 6274757 6314387 + * @bug 4023247 4027685 4032037 4072029 4073003 4118010 4120606 4133833 + * 4136916 6274757 6314387 8347841 * @run junit DateRegression */ @@ -106,7 +107,7 @@ public void Test4072029() { TimeZone saveZone = TimeZone.getDefault(); try { - TimeZone.setDefault(TimeZone.getTimeZone("PST")); + TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles")); Date now = new Date(); String s = now.toString(); Date now2 = new Date(now.toString()); diff --git a/test/jdk/java/util/Date/DateTest.java b/test/jdk/java/util/Date/DateTest.java index 6740cb16faa3f..7dbb19409d37a 100644 --- a/test/jdk/java/util/Date/DateTest.java +++ b/test/jdk/java/util/Date/DateTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 4143459 + * @bug 4143459 8347841 * @summary test Date * @run junit DateTest */ @@ -56,7 +56,7 @@ public void TestDefaultZoneLite() { d.setMonth(Calendar.JANUARY); d.setDate(1); d.setHours(6); - TimeZone.setDefault(TimeZone.getTimeZone("PST")); + TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles")); if (d.getHours() != 22) { fail("Fail: Date.setHours()/getHours() ignoring default zone"); } @@ -79,7 +79,7 @@ public void TestDefaultZone() { Date ref = new Date(883634400000L); // This is Thu Jan 1 1998 6:00 am GMT String refstr = "Jan 1 1998 6:00"; TimeZone GMT = TimeZone.getTimeZone("GMT"); - TimeZone PST = TimeZone.getTimeZone("PST"); + TimeZone PST = TimeZone.getTimeZone("America/Los_Angeles"); String[] names = { "year", "month", "date", "day of week", "hour", "offset" }; int[] GMT_EXP = { 98, Calendar.JANUARY, 1, Calendar.THURSDAY - Calendar.SUNDAY, 6, 0 }; @@ -207,7 +207,7 @@ public void TestDate480() { TimeZone save = TimeZone.getDefault(); try { - TimeZone.setDefault(TimeZone.getTimeZone("PST")); + TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles")); Date d1=new java.util.Date(97,8,13,10,8,13); System.out.println("d = "+d1); Date d2=new java.util.Date(97,8,13,30,8,13); // 20 hours later diff --git a/test/jdk/java/util/Formatter/Basic-X.java.template b/test/jdk/java/util/Formatter/Basic-X.java.template index 599a3621b5984..9976badac22a1 100644 --- a/test/jdk/java/util/Formatter/Basic-X.java.template +++ b/test/jdk/java/util/Formatter/Basic-X.java.template @@ -43,103 +43,6 @@ import java.util.regex.Pattern; #end[datetime] public class Basic$Type$ extends Basic { - - private static void test(String fs, String exp, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), Locale.US); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(Locale l, String fs, String exp, Object ... args) - { - Formatter f = new Formatter(new StringBuilder(), l); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), l); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(String fs, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, "fail", f.toString()); - } - - private static void test(String fs) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, "fail"); - ck(fs, "fail", f.toString()); - } - - private static void testSysOut(String fs, String exp, Object ... args) { - FileOutputStream fos = null; - FileInputStream fis = null; - try { - PrintStream saveOut = System.out; - fos = new FileOutputStream("testSysOut"); - System.setOut(new PrintStream(fos)); - System.out.format(Locale.US, fs, args); - fos.close(); - - fis = new FileInputStream("testSysOut"); - byte [] ba = new byte[exp.length()]; - int len = fis.read(ba); - String got = new String(ba); - if (len != ba.length) - fail(fs, exp, got); - ck(fs, exp, got); - - System.setOut(saveOut); - } catch (FileNotFoundException ex) { - fail(fs, ex.getClass()); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } finally { - try { - if (fos != null) - fos.close(); - if (fis != null) - fis.close(); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } - } - } - - private static void tryCatch(String fs, Class ex) { - boolean caught = false; - try { - test(fs); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - private static void tryCatch(String fs, Class ex, Object ... args) { - boolean caught = false; - try { - test(fs, args); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - #if[datetime] private static void testDateTime(String fs, String exp, Calendar c) { testDateTime(fs, exp, c, true); diff --git a/test/jdk/java/util/Formatter/Basic.java b/test/jdk/java/util/Formatter/Basic.java index 099ff13b39dde..af8814328d235 100644 --- a/test/jdk/java/util/Formatter/Basic.java +++ b/test/jdk/java/util/Formatter/Basic.java @@ -21,18 +21,119 @@ * questions. */ +import java.io.*; +import java.util.Formatter; +import java.util.Locale; + public class Basic { private static int fail = 0; + private static int pass = 0; private static Throwable first; - static void pass() { + protected static void test(String fs, String exp, Object ... args) { + Formatter f = new Formatter(new StringBuilder(), Locale.US); + f.format(fs, args); + ck(fs, exp, f.toString()); + + f = new Formatter(new StringBuilder(), Locale.US); + f.format("foo " + fs + " bar", args); + ck(fs, "foo " + exp + " bar", f.toString()); + } + + protected static void test(Locale l, String fs, String exp, Object ... args) + { + Formatter f = new Formatter(new StringBuilder(), l); + f.format(fs, args); + ck(fs, exp, f.toString()); + + f = new Formatter(new StringBuilder(), l); + f.format("foo " + fs + " bar", args); + ck(fs, "foo " + exp + " bar", f.toString()); + } + + protected static void test(String fs, Object ... args) { + Formatter f = new Formatter(new StringBuilder(), Locale.US); + f.format(fs, args); + ck(fs, "fail", f.toString()); + } + + protected static void test(String fs) { + Formatter f = new Formatter(new StringBuilder(), Locale.US); + f.format(fs, "fail"); + ck(fs, "fail", f.toString()); + } + + protected static void testSysOut(String fs, String exp, Object ... args) { + FileOutputStream fos = null; + FileInputStream fis = null; + try { + PrintStream saveOut = System.out; + fos = new FileOutputStream("testSysOut"); + System.setOut(new PrintStream(fos)); + System.out.format(Locale.US, fs, args); + fos.close(); + + fis = new FileInputStream("testSysOut"); + byte [] ba = new byte[exp.length()]; + int len = fis.read(ba); + String got = new String(ba); + if (len != ba.length) + fail(fs, exp, got); + ck(fs, exp, got); + + System.setOut(saveOut); + } catch (FileNotFoundException ex) { + fail(fs, ex.getClass()); + } catch (IOException ex) { + fail(fs, ex.getClass()); + } finally { + try { + if (fos != null) + fos.close(); + if (fis != null) + fis.close(); + } catch (IOException ex) { + fail(fs, ex.getClass()); + } + } + } + + protected static void tryCatch(String fs, Class ex) { + boolean caught = false; + try { + test(fs); + } catch (Throwable x) { + if (ex.isAssignableFrom(x.getClass())) + caught = true; + } + if (!caught) + fail(fs, ex); + else + pass(); + } + + protected static void tryCatch(String fs, Class ex, Object ... args) { + boolean caught = false; + try { + test(fs, args); + } catch (Throwable x) { + if (ex.isAssignableFrom(x.getClass())) + caught = true; + } + if (!caught) + fail(fs, ex); + else + pass(); + } + + private static void pass() { pass++; } - static void fail(String fs, Class ex) { + private static void fail(String fs, Class ex) { String message = "'%s': %s not thrown".formatted(fs, ex.getName()); if (first == null) { setFirst(message); @@ -41,7 +142,7 @@ static void fail(String fs, Class ex) { fail++; } - static void fail(String fs, String exp, String got) { + private static void fail(String fs, String exp, String got) { String message = "'%s': Expected '%s', got '%s'".formatted(fs, exp, got); if (first == null) { setFirst(message); @@ -58,7 +159,7 @@ private static void setFirst(String s) { } } - static void ck(String fs, String exp, String got) { + private static void ck(String fs, String exp, String got) { if (!exp.equals(got)) { fail(fs, exp, got); } else { diff --git a/test/jdk/java/util/Formatter/BasicBigDecimal.java b/test/jdk/java/util/Formatter/BasicBigDecimal.java index b2c42b8173347..51cd19141287b 100644 --- a/test/jdk/java/util/Formatter/BasicBigDecimal.java +++ b/test/jdk/java/util/Formatter/BasicBigDecimal.java @@ -38,215 +38,7 @@ import static java.util.Calendar.*; - - - - public class BasicBigDecimal extends Basic { - - private static void test(String fs, String exp, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), Locale.US); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(Locale l, String fs, String exp, Object ... args) - { - Formatter f = new Formatter(new StringBuilder(), l); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), l); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(String fs, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, "fail", f.toString()); - } - - private static void test(String fs) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, "fail"); - ck(fs, "fail", f.toString()); - } - - private static void testSysOut(String fs, String exp, Object ... args) { - FileOutputStream fos = null; - FileInputStream fis = null; - try { - PrintStream saveOut = System.out; - fos = new FileOutputStream("testSysOut"); - System.setOut(new PrintStream(fos)); - System.out.format(Locale.US, fs, args); - fos.close(); - - fis = new FileInputStream("testSysOut"); - byte [] ba = new byte[exp.length()]; - int len = fis.read(ba); - String got = new String(ba); - if (len != ba.length) - fail(fs, exp, got); - ck(fs, exp, got); - - System.setOut(saveOut); - } catch (FileNotFoundException ex) { - fail(fs, ex.getClass()); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } finally { - try { - if (fos != null) - fos.close(); - if (fis != null) - fis.close(); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } - } - } - - private static void tryCatch(String fs, Class ex) { - boolean caught = false; - try { - test(fs); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - private static void tryCatch(String fs, Class ex, Object ... args) { - boolean caught = false; - try { - test(fs, args); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - private static BigDecimal create(double v) { @@ -265,76 +57,6 @@ private static BigDecimal recip(BigDecimal v) { return BigDecimal.ONE.divide(v); } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public static void test() { TimeZone.setDefault(TimeZone.getTimeZone("GMT-0800")); @@ -522,1152 +244,410 @@ public static void test() { + //--------------------------------------------------------------------- + // %s - BigDecimal + //--------------------------------------------------------------------- + BigDecimal one = BigDecimal.ONE; + BigDecimal ten = BigDecimal.TEN; + BigDecimal pi = new BigDecimal(Math.PI); + BigDecimal piToThe300 = pi.pow(300); + test("%s", "3.141592653589793115997963468544185161590576171875", pi); + //--------------------------------------------------------------------- + // flag/conversion errors + //--------------------------------------------------------------------- + tryCatch("%d", IllegalFormatConversionException.class, one); + tryCatch("%,.4e", FormatFlagsConversionMismatchException.class, one); + //--------------------------------------------------------------------- + // %e + // + // Floating-point conversions applicable to float, double, and + // BigDecimal. + //--------------------------------------------------------------------- + test("%e", "null", (Object)null); + //--------------------------------------------------------------------- + // %e - float and double + //--------------------------------------------------------------------- + // double PI = 3.141 592 653 589 793 238 46; + test("%e", "3.141593e+00", pi); + test("%.0e", "1e+01", ten); + test("%#.0e", "1.e+01", ten); + test("%E", "3.141593E+00", pi); + test("%10.3e", " 3.142e+00", pi); + test("%10.3e", "-3.142e+00", negate(pi)); + test("%010.3e", "03.142e+00", pi); + test("%010.3e", "-3.142e+00", negate(pi)); + test("%-12.3e", "3.142e+00 ", pi); + test("%-12.3e", "-3.142e+00 ", negate(pi)); + test("%.3e", "3.142e+00", pi); + test("%.3e", "-3.142e+00", negate(pi)); + test("%.3e", "3.142e+06", mult(pi, 1000000.0)); + test("%.3e", "-3.142e+06", mult(pi, -1000000.0)); + test(Locale.FRANCE, "%e", "3,141593e+00", pi); + // double PI^300 + // = 13962455701329742638131355433930076081862072808 ... e+149 + //--------------------------------------------------------------------- + // %e - BigDecimal + //--------------------------------------------------------------------- + test("%.3e", "1.396e+149", piToThe300); + test("%.3e", "-1.396e+149", piToThe300.negate()); + test("%.3e", "1.000e-100", recip(ten.pow(100))); + test("%.3e", "-1.000e-100", negate(recip(ten.pow(100)))); + test("%3.0e", "1e-06", new BigDecimal("0.000001")); + test("%3.0e", "1e-05", new BigDecimal("0.00001")); + test("%3.0e", "1e-04", new BigDecimal("0.0001")); + test("%3.0e", "1e-03", new BigDecimal("0.001")); + test("%3.0e", "1e-02", new BigDecimal("0.01")); + test("%3.0e", "1e-01", new BigDecimal("0.1")); + test("%3.0e", "9e-01", new BigDecimal("0.9")); + test("%3.1e", "9.0e-01", new BigDecimal("0.9")); + test("%3.0e", "1e+00", new BigDecimal("1.00")); + test("%3.0e", "1e+01", new BigDecimal("10.00")); + test("%3.0e", "1e+02", new BigDecimal("99.19")); + test("%3.1e", "9.9e+01", new BigDecimal("99.19")); + test("%3.0e", "1e+02", new BigDecimal("99.99")); + test("%3.0e", "1e+02", new BigDecimal("100.00")); + test("%#3.0e", "1.e+03", new BigDecimal("1000.00")); + test("%3.0e", "1e+04", new BigDecimal("10000.00")); + test("%3.0e", "1e+05", new BigDecimal("100000.00")); + test("%3.0e", "1e+06", new BigDecimal("1000000.00")); + test("%3.0e", "1e+07", new BigDecimal("10000000.00")); + test("%3.0e", "1e+08", new BigDecimal("100000000.00")); + test("%10.3e", " 1.000e+00", one); + test("%+.3e", "+3.142e+00", pi); + test("%+.3e", "-3.142e+00", negate(pi)); + test("% .3e", " 3.142e+00", pi); + test("% .3e", "-3.142e+00", negate(pi)); + test("%#.0e", "3.e+00", create(3.0)); + test("%#.0e", "-3.e+00", create(-3.0)); + test("%.0e", "3e+00", create(3.0)); + test("%.0e", "-3e+00", create(-3.0)); + test("%(.4e", "3.1416e+06", mult(pi, 1000000.0)); + test("%(.4e", "(3.1416e+06)", mult(pi, -1000000.0)); + //--------------------------------------------------------------------- + // %e - boundary problems + //--------------------------------------------------------------------- + test("%3.0e", "1e-06", 0.000001); + test("%3.0e", "1e-05", 0.00001); + test("%3.0e", "1e-04", 0.0001); + test("%3.0e", "1e-03", 0.001); + test("%3.0e", "1e-02", 0.01); + test("%3.0e", "1e-01", 0.1); + test("%3.0e", "9e-01", 0.9); + test("%3.1e", "9.0e-01", 0.9); + test("%3.0e", "1e+00", 1.00); + test("%3.0e", "1e+01", 10.00); + test("%3.0e", "1e+02", 99.19); + test("%3.1e", "9.9e+01", 99.19); + test("%3.0e", "1e+02", 99.99); + test("%3.0e", "1e+02", 100.00); + test("%#3.0e", "1.e+03", 1000.00); + test("%3.0e", "1e+04", 10000.00); + test("%3.0e", "1e+05", 100000.00); + test("%3.0e", "1e+06", 1000000.00); + test("%3.0e", "1e+07", 10000000.00); + test("%3.0e", "1e+08", 100000000.00); + //--------------------------------------------------------------------- + // %f + // + // Floating-point conversions applicable to float, double, and + // BigDecimal. + //--------------------------------------------------------------------- + test("%f", "null", (Object)null); + test("%f", "3.141593", pi); + test(Locale.FRANCE, "%f", "3,141593", pi); + test("%10.3f", " 3.142", pi); + test("%10.3f", " -3.142", negate(pi)); + test("%010.3f", "000003.142", pi); + test("%010.3f", "-00003.142", negate(pi)); + test("%-10.3f", "3.142 ", pi); + test("%-10.3f", "-3.142 ", negate(pi)); + test("%.3f", "3.142", pi); + test("%.3f", "-3.142", negate(pi)); + test("%+.3f", "+3.142", pi); + test("%+.3f", "-3.142", negate(pi)); + test("% .3f", " 3.142", pi); + test("% .3f", "-3.142", negate(pi)); + test("%#.0f", "3.", create(3.0)); + test("%#.0f", "-3.", create(-3.0)); + test("%.0f", "3", create(3.0)); + test("%.0f", "-3", create(-3.0)); + test("%.3f", "10.000", ten); + test("%.3f", "1.000", one); + test("%10.3f", " 1.000", one); + //--------------------------------------------------------------------- + // %f - boundary problems + //--------------------------------------------------------------------- + test("%3.0f", " 0", 0.000001); + test("%3.0f", " 0", 0.00001); + test("%3.0f", " 0", 0.0001); + test("%3.0f", " 0", 0.001); + test("%3.0f", " 0", 0.01); + test("%3.0f", " 0", 0.1); + test("%3.0f", " 1", 0.9); + test("%3.1f", "0.9", 0.9); + test("%3.0f", " 1", 1.00); + test("%3.0f", " 10", 10.00); + test("%3.0f", " 99", 99.19); + test("%3.1f", "99.2", 99.19); + test("%3.0f", "100", 99.99); + test("%3.0f", "100", 100.00); + test("%#3.0f", "1000.", 1000.00); + test("%3.0f", "10000", 10000.00); + test("%3.0f", "100000", 100000.00); + test("%3.0f", "1000000", 1000000.00); + test("%3.0f", "10000000", 10000000.00); + test("%3.0f", "100000000", 100000000.00); + test("%10.0f", " 1000000", 1000000.00); + test("%,10.0f", " 1,000,000", 1000000.00); + test("%,10.1f", "1,000,000.0", 1000000.00); + test("%,3.0f", "1,000,000", 1000000.00); + test("%,3.0f", "10,000,000", 10000000.00); + test("%,3.0f", "100,000,000", 100000000.00); + test("%,3.0f", "10,000,000", 10000000.00); + test("%,3.0f", "100,000,000", 100000000.00); + //--------------------------------------------------------------------- + // %f - BigDecimal + //--------------------------------------------------------------------- + test("%4.0f", " 99", new BigDecimal("99.19")); + test("%4.1f", "99.2", new BigDecimal("99.19")); + BigDecimal val = new BigDecimal("99.95"); + test("%4.0f", " 100", val); + test("%#4.0f", "100.", val); + test("%4.1f", "100.0", val); + test("%4.2f", "99.95", val); + test("%4.3f", "99.950", val); + val = new BigDecimal(".99"); + test("%4.1f", " 1.0", val); + test("%4.2f", "0.99", val); + test("%4.3f", "0.990", val); + // #6476425 + val = new BigDecimal("0.00001"); + test("%.0f", "0", val); + test("%.1f", "0.0", val); + test("%.2f", "0.00", val); + test("%.3f", "0.000", val); + test("%.4f", "0.0000", val); + test("%.5f", "0.00001", val); + val = new BigDecimal("1.00001"); + test("%.0f", "1", val); + test("%.1f", "1.0", val); + test("%.2f", "1.00", val); + test("%.3f", "1.000", val); + test("%.4f", "1.0000", val); + test("%.5f", "1.00001", val); + val = new BigDecimal("1.23456"); + test("%.0f", "1", val); + test("%.1f", "1.2", val); + test("%.2f", "1.23", val); + test("%.3f", "1.235", val); + test("%.4f", "1.2346", val); + test("%.5f", "1.23456", val); + test("%.6f", "1.234560", val); + val = new BigDecimal("9.99999"); + test("%.0f", "10", val); + test("%.1f", "10.0", val); + test("%.2f", "10.00", val); + test("%.3f", "10.000", val); + test("%.4f", "10.0000", val); + test("%.5f", "9.99999", val); + test("%.6f", "9.999990", val); + val = new BigDecimal("1.99999"); + test("%.0f", "2", val); + test("%.1f", "2.0", val); + test("%.2f", "2.00", val); + test("%.3f", "2.000", val); + test("%.4f", "2.0000", val); + test("%.5f", "1.99999", val); + test("%.6f", "1.999990", val); + val = new BigDecimal(0.9996); + test("%.0f", "1", val); + test("%.1f", "1.0", val); + test("%.2f", "1.00", val); + test("%.3f", "1.000", val); + test("%.4f", "0.9996", val); + test("%.5f", "0.99960", val); + test("%.6f", "0.999600", val); + val = new BigDecimal(BigInteger.ZERO, 6); + test("%.4f", "0.0000", val); + val = new BigDecimal(BigInteger.ZERO, -6); + test("%.4f", "0.0000", val); + //--------------------------------------------------------------------- + // %f - float, double, Double, BigDecimal + //--------------------------------------------------------------------- + test("%.3f", "3141592.654", mult(pi, 1000000.0)); + test("%.3f", "-3141592.654", mult(pi, -1000000.0)); + test("%,.4f", "3,141,592.6536", mult(pi, 1000000.0)); + test(Locale.FRANCE, "%,.4f", "3\u202f141\u202f592,6536", mult(pi, 1000000.0)); + test("%,.4f", "-3,141,592.6536", mult(pi, -1000000.0)); + test("%(.4f", "3141592.6536", mult(pi, 1000000.0)); + test("%(.4f", "(3141592.6536)", mult(pi, -1000000.0)); + test("%(,.4f", "3,141,592.6536", mult(pi, 1000000.0)); + test("%(,.4f", "(3,141,592.6536)", mult(pi, -1000000.0)); + //--------------------------------------------------------------------- + // %g + // + // Floating-point conversions applicable to float, double, and + // BigDecimal. + //--------------------------------------------------------------------- + test("%g", "null", (Object)null); + test("%g", "3.14159", pi); + test(Locale.FRANCE, "%g", "3,14159", pi); + test("%.0g", "1e+01", ten); + test("%G", "3.14159", pi); + test("%10.3g", " 3.14", pi); + test("%10.3g", " -3.14", negate(pi)); + test("%010.3g", "0000003.14", pi); + test("%010.3g", "-000003.14", negate(pi)); + test("%-12.3g", "3.14 ", pi); + test("%-12.3g", "-3.14 ", negate(pi)); + test("%.3g", "3.14", pi); + test("%.3g", "-3.14", negate(pi)); + test("%.3g", "3.14e+08", mult(pi, 100000000.0)); + test("%.3g", "-3.14e+08", mult(pi, -100000000.0)); + test("%.3g", "1.00e-05", recip(create(100000.0))); + test("%.3g", "-1.00e-05", recip(create(-100000.0))); + test("%.0g", "-1e-05", recip(create(-100000.0))); + test("%.0g", "1e+05", create(100000.0)); + test("%.3G", "1.00E-05", recip(create(100000.0))); + test("%.3G", "-1.00E-05", recip(create(-100000.0))); + test("%.1g", "-0", -0.0); + test("%3.0g", " -0", -0.0); + test("%.1g", "0", 0.0); + test("%3.0g", " 0", 0.0); + test("%.1g", "0", +0.0); + test("%3.0g", " 0", +0.0); + test("%3.0g", "1e-06", 0.000001); + test("%3.0g", "1e-05", 0.00001); + test("%3.0g", "1e-05", 0.0000099); + test("%3.1g", "1e-05", 0.0000099); + test("%3.2g", "9.9e-06", 0.0000099); + test("%3.0g", "0.0001", 0.0001); + test("%3.0g", "9e-05", 0.00009); + test("%3.0g", "0.0001", 0.000099); + test("%3.1g", "0.0001", 0.000099); + test("%3.2g", "9.9e-05", 0.000099); + test("%3.0g", "0.001", 0.001); + test("%3.0g", "0.001", 0.00099); + test("%3.1g", "0.001", 0.00099); + test("%3.2g", "0.00099", 0.00099); + test("%3.3g", "0.00100", 0.001); + test("%3.4g", "0.001000", 0.001); + test("%3.0g", "0.01", 0.01); + test("%3.0g", "0.1", 0.1); + test("%3.0g", "0.9", 0.9); + test("%3.1g", "0.9", 0.9); + test("%3.0g", " 1", 1.00); + test("%3.2g", " 10", 10.00); + test("%3.0g", "1e+01", 10.00); + test("%3.0g", "1e+02", 99.19); + test("%3.1g", "1e+02", 99.19); + test("%3.2g", " 99", 99.19); + test("%3.0g", "1e+02", 99.9); + test("%3.1g", "1e+02", 99.9); + test("%3.2g", "1.0e+02", 99.9); + test("%3.0g", "1e+02", 99.99); + test("%3.0g", "1e+02", 100.00); + test("%3.0g", "1e+03", 999.9); + test("%3.1g", "1e+03", 999.9); + test("%3.2g", "1.0e+03", 999.9); + test("%3.3g", "1.00e+03", 999.9); + test("%3.4g", "999.9", 999.9); + test("%3.4g", "1000", 999.99); + test("%3.0g", "1e+03", 1000.00); + test("%3.0g", "1e+04", 10000.00); + test("%3.0g", "1e+05", 100000.00); + test("%3.0g", "1e+06", 1000000.00); + test("%3.0g", "1e+07", 10000000.00); + test("%3.9g", "100000000", 100000000.00); + test("%3.10g", "100000000.0", 100000000.00); + tryCatch("%#3.0g", FormatFlagsConversionMismatchException.class, 1000.00); + // double PI^300 + // = 13962455701329742638131355433930076081862072808 ... e+149 + //--------------------------------------------------------------------- + // %g - BigDecimal + //--------------------------------------------------------------------- + test("%.3g", "1.40e+149", piToThe300); + test("%.3g", "-1.40e+149", piToThe300.negate()); + test(Locale.FRANCE, "%.3g", "-1,40e+149", piToThe300.negate()); + test("%.3g", "1.00e-100", recip(ten.pow(100))); + test("%.3g", "-1.00e-100", negate(recip(ten.pow(100)))); + test("%3.0g", "1e-06", new BigDecimal("0.000001")); + test("%3.0g", "1e-05", new BigDecimal("0.00001")); + test("%3.0g", "0.0001", new BigDecimal("0.0001")); + test("%3.0g", "0.001", new BigDecimal("0.001")); + test("%3.3g", "0.00100", new BigDecimal("0.001")); + test("%3.4g", "0.001000", new BigDecimal("0.001")); + test("%3.0g", "0.01", new BigDecimal("0.01")); + test("%3.0g", "0.1", new BigDecimal("0.1")); + test("%3.0g", "0.9", new BigDecimal("0.9")); + test("%3.1g", "0.9", new BigDecimal("0.9")); + test("%3.0g", " 1", new BigDecimal("1.00")); + test("%3.2g", " 10", new BigDecimal("10.00")); + test("%3.0g", "1e+01", new BigDecimal("10.00")); + test("%3.0g", "1e+02", new BigDecimal("99.19")); + test("%3.1g", "1e+02", new BigDecimal("99.19")); + test("%3.2g", " 99", new BigDecimal("99.19")); + test("%3.0g", "1e+02", new BigDecimal("99.99")); + test("%3.0g", "1e+02", new BigDecimal("100.00")); + test("%3.0g", "1e+03", new BigDecimal("1000.00")); + test("%3.0g", "1e+04", new BigDecimal("10000.00")); + test("%3.0g", "1e+05", new BigDecimal("100000.00")); + test("%3.0g", "1e+06", new BigDecimal("1000000.00")); + test("%3.0g", "1e+07", new BigDecimal("10000000.00")); + test("%3.9g", "100000000", new BigDecimal("100000000.00")); + test("%3.10g", "100000000.0", new BigDecimal("100000000.00")); + test("%.3g", "10.0", ten); + test("%.3g", "1.00", one); + test("%10.3g", " 1.00", one); + test("%+10.3g", " +3.14", pi); + test("%+10.3g", " -3.14", negate(pi)); + test("% .3g", " 3.14", pi); + test("% .3g", "-3.14", negate(pi)); + test("%.0g", "3", create(3.0)); + test("%.0g", "-3", create(-3.0)); + test("%(.4g", "3.142e+08", mult(pi, 100000000.0)); + test("%(.4g", "(3.142e+08)", mult(pi, -100000000.0)); + test("%,.11g", "3,141,592.6536", mult(pi, 1000000.0)); + test("%(,.11g", "(3,141,592.6536)", mult(pi, -1000000.0)); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %s - BigDecimal - //--------------------------------------------------------------------- - BigDecimal one = BigDecimal.ONE; - BigDecimal ten = BigDecimal.TEN; - BigDecimal pi = new BigDecimal(Math.PI); - BigDecimal piToThe300 = pi.pow(300); - - test("%s", "3.141592653589793115997963468544185161590576171875", pi); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // flag/conversion errors - //--------------------------------------------------------------------- - tryCatch("%d", IllegalFormatConversionException.class, one); - tryCatch("%,.4e", FormatFlagsConversionMismatchException.class, one); - - //--------------------------------------------------------------------- - // %e - // - // Floating-point conversions applicable to float, double, and - // BigDecimal. - //--------------------------------------------------------------------- - test("%e", "null", (Object)null); - - //--------------------------------------------------------------------- - // %e - float and double - //--------------------------------------------------------------------- - // double PI = 3.141 592 653 589 793 238 46; - test("%e", "3.141593e+00", pi); - test("%.0e", "1e+01", ten); - test("%#.0e", "1.e+01", ten); - test("%E", "3.141593E+00", pi); - test("%10.3e", " 3.142e+00", pi); - test("%10.3e", "-3.142e+00", negate(pi)); - test("%010.3e", "03.142e+00", pi); - test("%010.3e", "-3.142e+00", negate(pi)); - test("%-12.3e", "3.142e+00 ", pi); - test("%-12.3e", "-3.142e+00 ", negate(pi)); - test("%.3e", "3.142e+00", pi); - test("%.3e", "-3.142e+00", negate(pi)); - test("%.3e", "3.142e+06", mult(pi, 1000000.0)); - test("%.3e", "-3.142e+06", mult(pi, -1000000.0)); - - test(Locale.FRANCE, "%e", "3,141593e+00", pi); - - // double PI^300 - // = 13962455701329742638131355433930076081862072808 ... e+149 - - //--------------------------------------------------------------------- - // %e - BigDecimal - //--------------------------------------------------------------------- - test("%.3e", "1.396e+149", piToThe300); - test("%.3e", "-1.396e+149", piToThe300.negate()); - test("%.3e", "1.000e-100", recip(ten.pow(100))); - test("%.3e", "-1.000e-100", negate(recip(ten.pow(100)))); - - test("%3.0e", "1e-06", new BigDecimal("0.000001")); - test("%3.0e", "1e-05", new BigDecimal("0.00001")); - test("%3.0e", "1e-04", new BigDecimal("0.0001")); - test("%3.0e", "1e-03", new BigDecimal("0.001")); - test("%3.0e", "1e-02", new BigDecimal("0.01")); - test("%3.0e", "1e-01", new BigDecimal("0.1")); - test("%3.0e", "9e-01", new BigDecimal("0.9")); - test("%3.1e", "9.0e-01", new BigDecimal("0.9")); - test("%3.0e", "1e+00", new BigDecimal("1.00")); - test("%3.0e", "1e+01", new BigDecimal("10.00")); - test("%3.0e", "1e+02", new BigDecimal("99.19")); - test("%3.1e", "9.9e+01", new BigDecimal("99.19")); - test("%3.0e", "1e+02", new BigDecimal("99.99")); - test("%3.0e", "1e+02", new BigDecimal("100.00")); - test("%#3.0e", "1.e+03", new BigDecimal("1000.00")); - test("%3.0e", "1e+04", new BigDecimal("10000.00")); - test("%3.0e", "1e+05", new BigDecimal("100000.00")); - test("%3.0e", "1e+06", new BigDecimal("1000000.00")); - test("%3.0e", "1e+07", new BigDecimal("10000000.00")); - test("%3.0e", "1e+08", new BigDecimal("100000000.00")); - - - test("%10.3e", " 1.000e+00", one); - test("%+.3e", "+3.142e+00", pi); - test("%+.3e", "-3.142e+00", negate(pi)); - test("% .3e", " 3.142e+00", pi); - test("% .3e", "-3.142e+00", negate(pi)); - test("%#.0e", "3.e+00", create(3.0)); - test("%#.0e", "-3.e+00", create(-3.0)); - test("%.0e", "3e+00", create(3.0)); - test("%.0e", "-3e+00", create(-3.0)); - - test("%(.4e", "3.1416e+06", mult(pi, 1000000.0)); - test("%(.4e", "(3.1416e+06)", mult(pi, -1000000.0)); - - //--------------------------------------------------------------------- - // %e - boundary problems - //--------------------------------------------------------------------- - test("%3.0e", "1e-06", 0.000001); - test("%3.0e", "1e-05", 0.00001); - test("%3.0e", "1e-04", 0.0001); - test("%3.0e", "1e-03", 0.001); - test("%3.0e", "1e-02", 0.01); - test("%3.0e", "1e-01", 0.1); - test("%3.0e", "9e-01", 0.9); - test("%3.1e", "9.0e-01", 0.9); - test("%3.0e", "1e+00", 1.00); - test("%3.0e", "1e+01", 10.00); - test("%3.0e", "1e+02", 99.19); - test("%3.1e", "9.9e+01", 99.19); - test("%3.0e", "1e+02", 99.99); - test("%3.0e", "1e+02", 100.00); - test("%#3.0e", "1.e+03", 1000.00); - test("%3.0e", "1e+04", 10000.00); - test("%3.0e", "1e+05", 100000.00); - test("%3.0e", "1e+06", 1000000.00); - test("%3.0e", "1e+07", 10000000.00); - test("%3.0e", "1e+08", 100000000.00); - - //--------------------------------------------------------------------- - // %f - // - // Floating-point conversions applicable to float, double, and - // BigDecimal. - //--------------------------------------------------------------------- - test("%f", "null", (Object)null); - test("%f", "3.141593", pi); - test(Locale.FRANCE, "%f", "3,141593", pi); - test("%10.3f", " 3.142", pi); - test("%10.3f", " -3.142", negate(pi)); - test("%010.3f", "000003.142", pi); - test("%010.3f", "-00003.142", negate(pi)); - test("%-10.3f", "3.142 ", pi); - test("%-10.3f", "-3.142 ", negate(pi)); - test("%.3f", "3.142", pi); - test("%.3f", "-3.142", negate(pi)); - test("%+.3f", "+3.142", pi); - test("%+.3f", "-3.142", negate(pi)); - test("% .3f", " 3.142", pi); - test("% .3f", "-3.142", negate(pi)); - test("%#.0f", "3.", create(3.0)); - test("%#.0f", "-3.", create(-3.0)); - test("%.0f", "3", create(3.0)); - test("%.0f", "-3", create(-3.0)); - test("%.3f", "10.000", ten); - test("%.3f", "1.000", one); - test("%10.3f", " 1.000", one); - - //--------------------------------------------------------------------- - // %f - boundary problems - //--------------------------------------------------------------------- - test("%3.0f", " 0", 0.000001); - test("%3.0f", " 0", 0.00001); - test("%3.0f", " 0", 0.0001); - test("%3.0f", " 0", 0.001); - test("%3.0f", " 0", 0.01); - test("%3.0f", " 0", 0.1); - test("%3.0f", " 1", 0.9); - test("%3.1f", "0.9", 0.9); - test("%3.0f", " 1", 1.00); - test("%3.0f", " 10", 10.00); - test("%3.0f", " 99", 99.19); - test("%3.1f", "99.2", 99.19); - test("%3.0f", "100", 99.99); - test("%3.0f", "100", 100.00); - test("%#3.0f", "1000.", 1000.00); - test("%3.0f", "10000", 10000.00); - test("%3.0f", "100000", 100000.00); - test("%3.0f", "1000000", 1000000.00); - test("%3.0f", "10000000", 10000000.00); - test("%3.0f", "100000000", 100000000.00); - test("%10.0f", " 1000000", 1000000.00); - test("%,10.0f", " 1,000,000", 1000000.00); - test("%,10.1f", "1,000,000.0", 1000000.00); - test("%,3.0f", "1,000,000", 1000000.00); - test("%,3.0f", "10,000,000", 10000000.00); - test("%,3.0f", "100,000,000", 100000000.00); - test("%,3.0f", "10,000,000", 10000000.00); - test("%,3.0f", "100,000,000", 100000000.00); - - //--------------------------------------------------------------------- - // %f - BigDecimal - //--------------------------------------------------------------------- - test("%4.0f", " 99", new BigDecimal("99.19")); - test("%4.1f", "99.2", new BigDecimal("99.19")); - - BigDecimal val = new BigDecimal("99.95"); - test("%4.0f", " 100", val); - test("%#4.0f", "100.", val); - test("%4.1f", "100.0", val); - test("%4.2f", "99.95", val); - test("%4.3f", "99.950", val); - - val = new BigDecimal(".99"); - test("%4.1f", " 1.0", val); - test("%4.2f", "0.99", val); - test("%4.3f", "0.990", val); - - // #6476425 - val = new BigDecimal("0.00001"); - test("%.0f", "0", val); - test("%.1f", "0.0", val); - test("%.2f", "0.00", val); - test("%.3f", "0.000", val); - test("%.4f", "0.0000", val); - test("%.5f", "0.00001", val); - - val = new BigDecimal("1.00001"); - test("%.0f", "1", val); - test("%.1f", "1.0", val); - test("%.2f", "1.00", val); - test("%.3f", "1.000", val); - test("%.4f", "1.0000", val); - test("%.5f", "1.00001", val); - - val = new BigDecimal("1.23456"); - test("%.0f", "1", val); - test("%.1f", "1.2", val); - test("%.2f", "1.23", val); - test("%.3f", "1.235", val); - test("%.4f", "1.2346", val); - test("%.5f", "1.23456", val); - test("%.6f", "1.234560", val); - - val = new BigDecimal("9.99999"); - test("%.0f", "10", val); - test("%.1f", "10.0", val); - test("%.2f", "10.00", val); - test("%.3f", "10.000", val); - test("%.4f", "10.0000", val); - test("%.5f", "9.99999", val); - test("%.6f", "9.999990", val); - - - val = new BigDecimal("1.99999"); - test("%.0f", "2", val); - test("%.1f", "2.0", val); - test("%.2f", "2.00", val); - test("%.3f", "2.000", val); - test("%.4f", "2.0000", val); - test("%.5f", "1.99999", val); - test("%.6f", "1.999990", val); - - val = new BigDecimal(0.9996); - test("%.0f", "1", val); - test("%.1f", "1.0", val); - test("%.2f", "1.00", val); - test("%.3f", "1.000", val); - test("%.4f", "0.9996", val); - test("%.5f", "0.99960", val); - test("%.6f", "0.999600", val); - - val = new BigDecimal(BigInteger.ZERO, 6); - test("%.4f", "0.0000", val); - val = new BigDecimal(BigInteger.ZERO, -6); - test("%.4f", "0.0000", val); - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %f - float, double, Double, BigDecimal - //--------------------------------------------------------------------- - test("%.3f", "3141592.654", mult(pi, 1000000.0)); - test("%.3f", "-3141592.654", mult(pi, -1000000.0)); - test("%,.4f", "3,141,592.6536", mult(pi, 1000000.0)); - test(Locale.FRANCE, "%,.4f", "3\u202f141\u202f592,6536", mult(pi, 1000000.0)); - test("%,.4f", "-3,141,592.6536", mult(pi, -1000000.0)); - test("%(.4f", "3141592.6536", mult(pi, 1000000.0)); - test("%(.4f", "(3141592.6536)", mult(pi, -1000000.0)); - test("%(,.4f", "3,141,592.6536", mult(pi, 1000000.0)); - test("%(,.4f", "(3,141,592.6536)", mult(pi, -1000000.0)); - - - - - //--------------------------------------------------------------------- - // %g - // - // Floating-point conversions applicable to float, double, and - // BigDecimal. - //--------------------------------------------------------------------- - test("%g", "null", (Object)null); - test("%g", "3.14159", pi); - test(Locale.FRANCE, "%g", "3,14159", pi); - test("%.0g", "1e+01", ten); - test("%G", "3.14159", pi); - test("%10.3g", " 3.14", pi); - test("%10.3g", " -3.14", negate(pi)); - test("%010.3g", "0000003.14", pi); - test("%010.3g", "-000003.14", negate(pi)); - test("%-12.3g", "3.14 ", pi); - test("%-12.3g", "-3.14 ", negate(pi)); - test("%.3g", "3.14", pi); - test("%.3g", "-3.14", negate(pi)); - test("%.3g", "3.14e+08", mult(pi, 100000000.0)); - test("%.3g", "-3.14e+08", mult(pi, -100000000.0)); - - test("%.3g", "1.00e-05", recip(create(100000.0))); - test("%.3g", "-1.00e-05", recip(create(-100000.0))); - test("%.0g", "-1e-05", recip(create(-100000.0))); - test("%.0g", "1e+05", create(100000.0)); - test("%.3G", "1.00E-05", recip(create(100000.0))); - test("%.3G", "-1.00E-05", recip(create(-100000.0))); - - test("%.1g", "-0", -0.0); - test("%3.0g", " -0", -0.0); - test("%.1g", "0", 0.0); - test("%3.0g", " 0", 0.0); - test("%.1g", "0", +0.0); - test("%3.0g", " 0", +0.0); - - test("%3.0g", "1e-06", 0.000001); - test("%3.0g", "1e-05", 0.00001); - test("%3.0g", "1e-05", 0.0000099); - test("%3.1g", "1e-05", 0.0000099); - test("%3.2g", "9.9e-06", 0.0000099); - test("%3.0g", "0.0001", 0.0001); - test("%3.0g", "9e-05", 0.00009); - test("%3.0g", "0.0001", 0.000099); - test("%3.1g", "0.0001", 0.000099); - test("%3.2g", "9.9e-05", 0.000099); - test("%3.0g", "0.001", 0.001); - test("%3.0g", "0.001", 0.00099); - test("%3.1g", "0.001", 0.00099); - test("%3.2g", "0.00099", 0.00099); - test("%3.3g", "0.00100", 0.001); - test("%3.4g", "0.001000", 0.001); - test("%3.0g", "0.01", 0.01); - test("%3.0g", "0.1", 0.1); - test("%3.0g", "0.9", 0.9); - test("%3.1g", "0.9", 0.9); - test("%3.0g", " 1", 1.00); - test("%3.2g", " 10", 10.00); - test("%3.0g", "1e+01", 10.00); - test("%3.0g", "1e+02", 99.19); - test("%3.1g", "1e+02", 99.19); - test("%3.2g", " 99", 99.19); - test("%3.0g", "1e+02", 99.9); - test("%3.1g", "1e+02", 99.9); - test("%3.2g", "1.0e+02", 99.9); - test("%3.0g", "1e+02", 99.99); - test("%3.0g", "1e+02", 100.00); - test("%3.0g", "1e+03", 999.9); - test("%3.1g", "1e+03", 999.9); - test("%3.2g", "1.0e+03", 999.9); - test("%3.3g", "1.00e+03", 999.9); - test("%3.4g", "999.9", 999.9); - test("%3.4g", "1000", 999.99); - test("%3.0g", "1e+03", 1000.00); - test("%3.0g", "1e+04", 10000.00); - test("%3.0g", "1e+05", 100000.00); - test("%3.0g", "1e+06", 1000000.00); - test("%3.0g", "1e+07", 10000000.00); - test("%3.9g", "100000000", 100000000.00); - test("%3.10g", "100000000.0", 100000000.00); - - tryCatch("%#3.0g", FormatFlagsConversionMismatchException.class, 1000.00); - - // double PI^300 - // = 13962455701329742638131355433930076081862072808 ... e+149 - - //--------------------------------------------------------------------- - // %g - BigDecimal - //--------------------------------------------------------------------- - test("%.3g", "1.40e+149", piToThe300); - test("%.3g", "-1.40e+149", piToThe300.negate()); - test(Locale.FRANCE, "%.3g", "-1,40e+149", piToThe300.negate()); - test("%.3g", "1.00e-100", recip(ten.pow(100))); - test("%.3g", "-1.00e-100", negate(recip(ten.pow(100)))); - - test("%3.0g", "1e-06", new BigDecimal("0.000001")); - test("%3.0g", "1e-05", new BigDecimal("0.00001")); - test("%3.0g", "0.0001", new BigDecimal("0.0001")); - test("%3.0g", "0.001", new BigDecimal("0.001")); - test("%3.3g", "0.00100", new BigDecimal("0.001")); - test("%3.4g", "0.001000", new BigDecimal("0.001")); - test("%3.0g", "0.01", new BigDecimal("0.01")); - test("%3.0g", "0.1", new BigDecimal("0.1")); - test("%3.0g", "0.9", new BigDecimal("0.9")); - test("%3.1g", "0.9", new BigDecimal("0.9")); - test("%3.0g", " 1", new BigDecimal("1.00")); - test("%3.2g", " 10", new BigDecimal("10.00")); - test("%3.0g", "1e+01", new BigDecimal("10.00")); - test("%3.0g", "1e+02", new BigDecimal("99.19")); - test("%3.1g", "1e+02", new BigDecimal("99.19")); - test("%3.2g", " 99", new BigDecimal("99.19")); - test("%3.0g", "1e+02", new BigDecimal("99.99")); - test("%3.0g", "1e+02", new BigDecimal("100.00")); - test("%3.0g", "1e+03", new BigDecimal("1000.00")); - test("%3.0g", "1e+04", new BigDecimal("10000.00")); - test("%3.0g", "1e+05", new BigDecimal("100000.00")); - test("%3.0g", "1e+06", new BigDecimal("1000000.00")); - test("%3.0g", "1e+07", new BigDecimal("10000000.00")); - test("%3.9g", "100000000", new BigDecimal("100000000.00")); - test("%3.10g", "100000000.0", new BigDecimal("100000000.00")); - - - test("%.3g", "10.0", ten); - test("%.3g", "1.00", one); - test("%10.3g", " 1.00", one); - test("%+10.3g", " +3.14", pi); - test("%+10.3g", " -3.14", negate(pi)); - test("% .3g", " 3.14", pi); - test("% .3g", "-3.14", negate(pi)); - test("%.0g", "3", create(3.0)); - test("%.0g", "-3", create(-3.0)); - - test("%(.4g", "3.142e+08", mult(pi, 100000000.0)); - test("%(.4g", "(3.142e+08)", mult(pi, -100000000.0)); - - - - - - - - test("%,.11g", "3,141,592.6536", mult(pi, 1000000.0)); - test("%(,.11g", "(3,141,592.6536)", mult(pi, -1000000.0)); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %f, %e, %g, %a - Boundaries - //--------------------------------------------------------------------- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + //--------------------------------------------------------------------- + // %f, %e, %g, %a - Boundaries + //--------------------------------------------------------------------- @@ -1691,99 +671,6 @@ public static void test() { tryCatch("%-tB", MissingFormatWidthException.class); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %n //--------------------------------------------------------------------- diff --git a/test/jdk/java/util/Formatter/BasicBigInteger.java b/test/jdk/java/util/Formatter/BasicBigInteger.java index 18a069cee6d2c..cec882d6a0aea 100644 --- a/test/jdk/java/util/Formatter/BasicBigInteger.java +++ b/test/jdk/java/util/Formatter/BasicBigInteger.java @@ -38,300 +38,7 @@ import static java.util.Calendar.*; - - - - public class BasicBigInteger extends Basic { - - private static void test(String fs, String exp, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), Locale.US); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(Locale l, String fs, String exp, Object ... args) - { - Formatter f = new Formatter(new StringBuilder(), l); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), l); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(String fs, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, "fail", f.toString()); - } - - private static void test(String fs) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, "fail"); - ck(fs, "fail", f.toString()); - } - - private static void testSysOut(String fs, String exp, Object ... args) { - FileOutputStream fos = null; - FileInputStream fis = null; - try { - PrintStream saveOut = System.out; - fos = new FileOutputStream("testSysOut"); - System.setOut(new PrintStream(fos)); - System.out.format(Locale.US, fs, args); - fos.close(); - - fis = new FileInputStream("testSysOut"); - byte [] ba = new byte[exp.length()]; - int len = fis.read(ba); - String got = new String(ba); - if (len != ba.length) - fail(fs, exp, got); - ck(fs, exp, got); - - System.setOut(saveOut); - } catch (FileNotFoundException ex) { - fail(fs, ex.getClass()); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } finally { - try { - if (fos != null) - fos.close(); - if (fis != null) - fis.close(); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } - } - } - - private static void tryCatch(String fs, Class ex) { - boolean caught = false; - try { - test(fs); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - private static void tryCatch(String fs, Class ex, Object ... args) { - boolean caught = false; - try { - test(fs, args); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -521,1154 +228,87 @@ public static void test() { tryCatch("%#g", FormatFlagsConversionMismatchException.class); + //--------------------------------------------------------------------- + // BigInteger - errors + //--------------------------------------------------------------------- + tryCatch("%f", IllegalFormatConversionException.class, + new BigInteger("1")); + //--------------------------------------------------------------------- + // %d - BigInteger + //--------------------------------------------------------------------- + test("%d", "null", (Object)null); + test("%d", "1234567", new BigInteger("1234567", 10)); + test("%,d", "1,234,567", new BigInteger("1234567", 10)); + test(Locale.FRANCE, "%,d", "1\u202f234\u202f567", new BigInteger("1234567", 10)); + test("%,d", "-1,234,567", new BigInteger("-1234567", 10)); + test("%(d", "1234567", new BigInteger("1234567", 10)); + test("%(d", "(1234567)", new BigInteger("-1234567", 10)); + test("% d", " 1234567", new BigInteger("1234567", 10)); + test("% d", "-1234567", new BigInteger("-1234567", 10)); + test("%+d", "+1234567", new BigInteger("1234567", 10)); + test("%+d", "-1234567", new BigInteger("-1234567", 10)); + test("%010d", "0001234567", new BigInteger("1234567", 10)); + test("%010d", "-001234567", new BigInteger("-1234567", 10)); + test("%(10d", " (1234567)", new BigInteger("-1234567", 10)); + test("%+d", "+1234567", new BigInteger("1234567", 10)); + test("%+d", "-1234567", new BigInteger("-1234567", 10)); + test("%-10d", "1234567 ", new BigInteger("1234567", 10)); + test("%-10d", "-1234567 ", new BigInteger("-1234567", 10)); + // , variations: + test("%0,10d", "01,234,567", new BigInteger("1234567", 10)); + test("%0,10d", "-1,234,567", new BigInteger("-1234567", 10)); + test("%(,10d", "(1,234,567)", new BigInteger("-1234567", 10)); + test("%+,d", "+1,234,567", new BigInteger("1234567", 10)); + test("%+,d", "-1,234,567", new BigInteger("-1234567", 10)); + test("%-,10d", "1,234,567 ", new BigInteger("1234567", 10)); + test("%-,10d", "-1,234,567", new BigInteger("-1234567", 10)); + //--------------------------------------------------------------------- + // %o - BigInteger + //--------------------------------------------------------------------- + test("%o", "null", (Object)null); + test("%o", "1234567", new BigInteger("1234567", 8)); + test("%(o", "1234567", new BigInteger("1234567", 8)); + test("%(o", "(1234567)", new BigInteger("-1234567", 8)); + test("% o", " 1234567", new BigInteger("1234567", 8)); + test("% o", "-1234567", new BigInteger("-1234567", 8)); + test("%+o", "+1234567", new BigInteger("1234567", 8)); + test("%+o", "-1234567", new BigInteger("-1234567", 8)); + test("%010o", "0001234567", new BigInteger("1234567", 8)); + test("%010o", "-001234567", new BigInteger("-1234567", 8)); + test("%(10o", " (1234567)", new BigInteger("-1234567", 8)); + test("%+o", "+1234567", new BigInteger("1234567", 8)); + test("%+o", "-1234567", new BigInteger("-1234567", 8)); + test("%-10o", "1234567 ", new BigInteger("1234567", 8)); + test("%-10o", "-1234567 ", new BigInteger("-1234567", 8)); + test("%#10o", " 01234567", new BigInteger("1234567", 8)); + test("%#10o", " -01234567", new BigInteger("-1234567", 8)); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // BigInteger - errors - //--------------------------------------------------------------------- - tryCatch("%f", IllegalFormatConversionException.class, - new BigInteger("1")); - - //--------------------------------------------------------------------- - // %d - BigInteger - //--------------------------------------------------------------------- - test("%d", "null", (Object)null); - test("%d", "1234567", new BigInteger("1234567", 10)); - test("%,d", "1,234,567", new BigInteger("1234567", 10)); - test(Locale.FRANCE, "%,d", "1\u202f234\u202f567", new BigInteger("1234567", 10)); - test("%,d", "-1,234,567", new BigInteger("-1234567", 10)); - test("%(d", "1234567", new BigInteger("1234567", 10)); - test("%(d", "(1234567)", new BigInteger("-1234567", 10)); - test("% d", " 1234567", new BigInteger("1234567", 10)); - test("% d", "-1234567", new BigInteger("-1234567", 10)); - test("%+d", "+1234567", new BigInteger("1234567", 10)); - test("%+d", "-1234567", new BigInteger("-1234567", 10)); - test("%010d", "0001234567", new BigInteger("1234567", 10)); - test("%010d", "-001234567", new BigInteger("-1234567", 10)); - test("%(10d", " (1234567)", new BigInteger("-1234567", 10)); - test("%+d", "+1234567", new BigInteger("1234567", 10)); - test("%+d", "-1234567", new BigInteger("-1234567", 10)); - test("%-10d", "1234567 ", new BigInteger("1234567", 10)); - test("%-10d", "-1234567 ", new BigInteger("-1234567", 10)); - // , variations: - test("%0,10d", "01,234,567", new BigInteger("1234567", 10)); - test("%0,10d", "-1,234,567", new BigInteger("-1234567", 10)); - test("%(,10d", "(1,234,567)", new BigInteger("-1234567", 10)); - test("%+,d", "+1,234,567", new BigInteger("1234567", 10)); - test("%+,d", "-1,234,567", new BigInteger("-1234567", 10)); - test("%-,10d", "1,234,567 ", new BigInteger("1234567", 10)); - test("%-,10d", "-1,234,567", new BigInteger("-1234567", 10)); - - //--------------------------------------------------------------------- - // %o - BigInteger - //--------------------------------------------------------------------- - test("%o", "null", (Object)null); - test("%o", "1234567", new BigInteger("1234567", 8)); - test("%(o", "1234567", new BigInteger("1234567", 8)); - test("%(o", "(1234567)", new BigInteger("-1234567", 8)); - test("% o", " 1234567", new BigInteger("1234567", 8)); - test("% o", "-1234567", new BigInteger("-1234567", 8)); - test("%+o", "+1234567", new BigInteger("1234567", 8)); - test("%+o", "-1234567", new BigInteger("-1234567", 8)); - test("%010o", "0001234567", new BigInteger("1234567", 8)); - test("%010o", "-001234567", new BigInteger("-1234567", 8)); - test("%(10o", " (1234567)", new BigInteger("-1234567", 8)); - test("%+o", "+1234567", new BigInteger("1234567", 8)); - test("%+o", "-1234567", new BigInteger("-1234567", 8)); - test("%-10o", "1234567 ", new BigInteger("1234567", 8)); - test("%-10o", "-1234567 ", new BigInteger("-1234567", 8)); - test("%#10o", " 01234567", new BigInteger("1234567", 8)); - test("%#10o", " -01234567", new BigInteger("-1234567", 8)); - - //--------------------------------------------------------------------- - // %x - BigInteger - //--------------------------------------------------------------------- - test("%x", "null", (Object)null); - test("%x", "1234567", new BigInteger("1234567", 16)); - test("%(x", "1234567", new BigInteger("1234567", 16)); - test("%(x", "(1234567)", new BigInteger("-1234567", 16)); - test("% x", " 1234567", new BigInteger("1234567", 16)); - test("% x", "-1234567", new BigInteger("-1234567", 16)); - test("%+x", "+1234567", new BigInteger("1234567", 16)); - test("%+x", "-1234567", new BigInteger("-1234567", 16)); - test("%010x", "0001234567", new BigInteger("1234567", 16)); - test("%010x", "-001234567", new BigInteger("-1234567", 16)); - test("%(10x", " (1234567)", new BigInteger("-1234567", 16)); - test("%+x", "+1234567", new BigInteger("1234567", 16)); - test("%+x", "-1234567", new BigInteger("-1234567", 16)); - test("%-10x", "1234567 ", new BigInteger("1234567", 16)); - test("%-10x", "-1234567 ", new BigInteger("-1234567", 16)); - test("%#10x", " 0x1234567", new BigInteger("1234567", 16)); - test("%#10x", "-0x1234567", new BigInteger("-1234567", 16)); - test("%#10X", " 0X1234567", new BigInteger("1234567", 16)); - test("%#10X", "-0X1234567", new BigInteger("-1234567", 16)); - test("%X", "1234567A", new BigInteger("1234567a", 16)); - test("%X", "-1234567A", new BigInteger("-1234567a", 16)); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + //--------------------------------------------------------------------- + // %x - BigInteger + //--------------------------------------------------------------------- + test("%x", "null", (Object)null); + test("%x", "1234567", new BigInteger("1234567", 16)); + test("%(x", "1234567", new BigInteger("1234567", 16)); + test("%(x", "(1234567)", new BigInteger("-1234567", 16)); + test("% x", " 1234567", new BigInteger("1234567", 16)); + test("% x", "-1234567", new BigInteger("-1234567", 16)); + test("%+x", "+1234567", new BigInteger("1234567", 16)); + test("%+x", "-1234567", new BigInteger("-1234567", 16)); + test("%010x", "0001234567", new BigInteger("1234567", 16)); + test("%010x", "-001234567", new BigInteger("-1234567", 16)); + test("%(10x", " (1234567)", new BigInteger("-1234567", 16)); + test("%+x", "+1234567", new BigInteger("1234567", 16)); + test("%+x", "-1234567", new BigInteger("-1234567", 16)); + test("%-10x", "1234567 ", new BigInteger("1234567", 16)); + test("%-10x", "-1234567 ", new BigInteger("-1234567", 16)); + test("%#10x", " 0x1234567", new BigInteger("1234567", 16)); + test("%#10x", "-0x1234567", new BigInteger("-1234567", 16)); + test("%#10X", " 0X1234567", new BigInteger("1234567", 16)); + test("%#10X", "-0X1234567", new BigInteger("-1234567", 16)); + test("%X", "1234567A", new BigInteger("1234567a", 16)); + test("%X", "-1234567A", new BigInteger("-1234567a", 16)); //--------------------------------------------------------------------- @@ -1691,99 +331,6 @@ public static void test() { tryCatch("%-tB", MissingFormatWidthException.class); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %n //--------------------------------------------------------------------- diff --git a/test/jdk/java/util/Formatter/BasicBoolean.java b/test/jdk/java/util/Formatter/BasicBoolean.java index 7bc3ea6bb6a03..19ed3f372244c 100644 --- a/test/jdk/java/util/Formatter/BasicBoolean.java +++ b/test/jdk/java/util/Formatter/BasicBoolean.java @@ -38,300 +38,7 @@ import static java.util.Calendar.*; - - - - public class BasicBoolean extends Basic { - - private static void test(String fs, String exp, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), Locale.US); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(Locale l, String fs, String exp, Object ... args) - { - Formatter f = new Formatter(new StringBuilder(), l); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), l); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(String fs, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, "fail", f.toString()); - } - - private static void test(String fs) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, "fail"); - ck(fs, "fail", f.toString()); - } - - private static void testSysOut(String fs, String exp, Object ... args) { - FileOutputStream fos = null; - FileInputStream fis = null; - try { - PrintStream saveOut = System.out; - fos = new FileOutputStream("testSysOut"); - System.setOut(new PrintStream(fos)); - System.out.format(Locale.US, fs, args); - fos.close(); - - fis = new FileInputStream("testSysOut"); - byte [] ba = new byte[exp.length()]; - int len = fis.read(ba); - String got = new String(ba); - if (len != ba.length) - fail(fs, exp, got); - ck(fs, exp, got); - - System.setOut(saveOut); - } catch (FileNotFoundException ex) { - fail(fs, ex.getClass()); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } finally { - try { - if (fos != null) - fos.close(); - if (fis != null) - fis.close(); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } - } - } - - private static void tryCatch(String fs, Class ex) { - boolean caught = false; - try { - test(fs); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - private static void tryCatch(String fs, Class ex, Object ... args) { - boolean caught = false; - try { - test(fs, args); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -523,1161 +230,13 @@ public static void test() { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %t - // - // Date/Time conversions applicable to Calendar, Date, and long. - //--------------------------------------------------------------------- - test("%tA", "null", (Object)null); - test("%TA", "NULL", (Object)null); + //--------------------------------------------------------------------- + // %t + // + // Date/Time conversions applicable to Calendar, Date, and long. + //--------------------------------------------------------------------- + test("%tA", "null", (Object)null); + test("%TA", "NULL", (Object)null); //--------------------------------------------------------------------- // %t - errors @@ -1691,99 +250,6 @@ public static void test() { tryCatch("%-tB", MissingFormatWidthException.class); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %n //--------------------------------------------------------------------- diff --git a/test/jdk/java/util/Formatter/BasicBooleanObject.java b/test/jdk/java/util/Formatter/BasicBooleanObject.java index 28c3f0fe17343..a92643f45d787 100644 --- a/test/jdk/java/util/Formatter/BasicBooleanObject.java +++ b/test/jdk/java/util/Formatter/BasicBooleanObject.java @@ -38,300 +38,7 @@ import static java.util.Calendar.*; - - - - public class BasicBooleanObject extends Basic { - - private static void test(String fs, String exp, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), Locale.US); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(Locale l, String fs, String exp, Object ... args) - { - Formatter f = new Formatter(new StringBuilder(), l); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), l); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(String fs, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, "fail", f.toString()); - } - - private static void test(String fs) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, "fail"); - ck(fs, "fail", f.toString()); - } - - private static void testSysOut(String fs, String exp, Object ... args) { - FileOutputStream fos = null; - FileInputStream fis = null; - try { - PrintStream saveOut = System.out; - fos = new FileOutputStream("testSysOut"); - System.setOut(new PrintStream(fos)); - System.out.format(Locale.US, fs, args); - fos.close(); - - fis = new FileInputStream("testSysOut"); - byte [] ba = new byte[exp.length()]; - int len = fis.read(ba); - String got = new String(ba); - if (len != ba.length) - fail(fs, exp, got); - ck(fs, exp, got); - - System.setOut(saveOut); - } catch (FileNotFoundException ex) { - fail(fs, ex.getClass()); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } finally { - try { - if (fos != null) - fos.close(); - if (fis != null) - fis.close(); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } - } - } - - private static void tryCatch(String fs, Class ex) { - boolean caught = false; - try { - test(fs); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - private static void tryCatch(String fs, Class ex, Object ... args) { - boolean caught = false; - try { - test(fs, args); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -523,1161 +230,13 @@ public static void test() { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %t - // - // Date/Time conversions applicable to Calendar, Date, and long. - //--------------------------------------------------------------------- - test("%tA", "null", (Object)null); - test("%TA", "NULL", (Object)null); + //--------------------------------------------------------------------- + // %t + // + // Date/Time conversions applicable to Calendar, Date, and long. + //--------------------------------------------------------------------- + test("%tA", "null", (Object)null); + test("%TA", "NULL", (Object)null); //--------------------------------------------------------------------- // %t - errors @@ -1691,99 +250,6 @@ public static void test() { tryCatch("%-tB", MissingFormatWidthException.class); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %n //--------------------------------------------------------------------- diff --git a/test/jdk/java/util/Formatter/BasicByte.java b/test/jdk/java/util/Formatter/BasicByte.java index 7da7c8929c831..c90e03a47c35a 100644 --- a/test/jdk/java/util/Formatter/BasicByte.java +++ b/test/jdk/java/util/Formatter/BasicByte.java @@ -38,303 +38,13 @@ import static java.util.Calendar.*; - - - - public class BasicByte extends Basic { - - private static void test(String fs, String exp, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), Locale.US); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(Locale l, String fs, String exp, Object ... args) - { - Formatter f = new Formatter(new StringBuilder(), l); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), l); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(String fs, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, "fail", f.toString()); - } - - private static void test(String fs) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, "fail"); - ck(fs, "fail", f.toString()); - } - - private static void testSysOut(String fs, String exp, Object ... args) { - FileOutputStream fos = null; - FileInputStream fis = null; - try { - PrintStream saveOut = System.out; - fos = new FileOutputStream("testSysOut"); - System.setOut(new PrintStream(fos)); - System.out.format(Locale.US, fs, args); - fos.close(); - - fis = new FileInputStream("testSysOut"); - byte [] ba = new byte[exp.length()]; - int len = fis.read(ba); - String got = new String(ba); - if (len != ba.length) - fail(fs, exp, got); - ck(fs, exp, got); - - System.setOut(saveOut); - } catch (FileNotFoundException ex) { - fail(fs, ex.getClass()); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } finally { - try { - if (fos != null) - fos.close(); - if (fis != null) - fis.close(); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } - } - } - - private static void tryCatch(String fs, Class ex) { - boolean caught = false; - try { - test(fs); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - private static void tryCatch(String fs, Class ex, Object ... args) { - boolean caught = false; - try { - test(fs, args); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - private static byte negate(byte v) { return (byte) -v; } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public static void test() { TimeZone.setDefault(TimeZone.getTimeZone("GMT-0800")); @@ -520,24 +230,8 @@ public static void test() { tryCatch("%#g", FormatFlagsConversionMismatchException.class); - - byte minByte = Byte.MIN_VALUE; // -128 - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %d // @@ -546,8 +240,6 @@ public static void test() { //--------------------------------------------------------------------- test("%d", "null", (Object)null); - - //--------------------------------------------------------------------- // %d - byte //--------------------------------------------------------------------- @@ -566,132 +258,29 @@ public static void test() { test("%(10d", " (17)", negate(seventeen)); test("%-10d", "17 ", seventeen); test("%-10d", "-17 ", negate(seventeen)); + //--------------------------------------------------------------------- + // %d - errors + //--------------------------------------------------------------------- + tryCatch("%#d", FormatFlagsConversionMismatchException.class); + tryCatch("%D", UnknownFormatConversionException.class); + tryCatch("%0d", MissingFormatWidthException.class); + tryCatch("%-d", MissingFormatWidthException.class); + tryCatch("%7.3d", IllegalFormatPrecisionException.class); + //--------------------------------------------------------------------- + // %o + // + // Numeric conversion applicable to byte, short, int, long, and + // BigInteger. + //--------------------------------------------------------------------- + test("%o", "null", (Object)null); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %d - errors - //--------------------------------------------------------------------- - tryCatch("%#d", FormatFlagsConversionMismatchException.class); - tryCatch("%D", UnknownFormatConversionException.class); - tryCatch("%0d", MissingFormatWidthException.class); - tryCatch("%-d", MissingFormatWidthException.class); - tryCatch("%7.3d", IllegalFormatPrecisionException.class); - - //--------------------------------------------------------------------- - // %o - // - // Numeric conversion applicable to byte, short, int, long, and - // BigInteger. - //--------------------------------------------------------------------- - test("%o", "null", (Object)null); - - - //--------------------------------------------------------------------- - // %o - byte - //--------------------------------------------------------------------- - test("%010o", "0000000200", minByte); - test("%-10o", "200 ", minByte); - test("%#10o", " 0200", minByte); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + //--------------------------------------------------------------------- + // %o - byte + //--------------------------------------------------------------------- + test("%010o", "0000000200", minByte); + test("%-10o", "200 ", minByte); + test("%#10o", " 0200", minByte); //--------------------------------------------------------------------- // %o - errors @@ -715,7 +304,6 @@ public static void test() { //--------------------------------------------------------------------- test("%x", "null", (Object)null); - //--------------------------------------------------------------------- // %x - byte //--------------------------------------------------------------------- @@ -725,58 +313,6 @@ public static void test() { test("%0#10x","0x00000080", minByte); test("%#10X", " 0X80", minByte); test("%X", "80", minByte); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %x - errors //--------------------------------------------------------------------- @@ -787,897 +323,13 @@ public static void test() { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %t - // - // Date/Time conversions applicable to Calendar, Date, and long. - //--------------------------------------------------------------------- - test("%tA", "null", (Object)null); - test("%TA", "NULL", (Object)null); + //--------------------------------------------------------------------- + // %t + // + // Date/Time conversions applicable to Calendar, Date, and long. + //--------------------------------------------------------------------- + test("%tA", "null", (Object)null); + test("%TA", "NULL", (Object)null); //--------------------------------------------------------------------- // %t - errors @@ -1691,99 +343,6 @@ public static void test() { tryCatch("%-tB", MissingFormatWidthException.class); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %n //--------------------------------------------------------------------- diff --git a/test/jdk/java/util/Formatter/BasicByteObject.java b/test/jdk/java/util/Formatter/BasicByteObject.java index cf28afc79d9b2..25e7524098759 100644 --- a/test/jdk/java/util/Formatter/BasicByteObject.java +++ b/test/jdk/java/util/Formatter/BasicByteObject.java @@ -38,303 +38,13 @@ import static java.util.Calendar.*; - - - - public class BasicByteObject extends Basic { - - private static void test(String fs, String exp, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), Locale.US); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(Locale l, String fs, String exp, Object ... args) - { - Formatter f = new Formatter(new StringBuilder(), l); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), l); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(String fs, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, "fail", f.toString()); - } - - private static void test(String fs) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, "fail"); - ck(fs, "fail", f.toString()); - } - - private static void testSysOut(String fs, String exp, Object ... args) { - FileOutputStream fos = null; - FileInputStream fis = null; - try { - PrintStream saveOut = System.out; - fos = new FileOutputStream("testSysOut"); - System.setOut(new PrintStream(fos)); - System.out.format(Locale.US, fs, args); - fos.close(); - - fis = new FileInputStream("testSysOut"); - byte [] ba = new byte[exp.length()]; - int len = fis.read(ba); - String got = new String(ba); - if (len != ba.length) - fail(fs, exp, got); - ck(fs, exp, got); - - System.setOut(saveOut); - } catch (FileNotFoundException ex) { - fail(fs, ex.getClass()); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } finally { - try { - if (fos != null) - fos.close(); - if (fis != null) - fis.close(); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } - } - } - - private static void tryCatch(String fs, Class ex) { - boolean caught = false; - try { - test(fs); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - private static void tryCatch(String fs, Class ex, Object ... args) { - boolean caught = false; - try { - test(fs, args); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - private static Byte negate(Byte v) { return (byte) -v.byteValue(); } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public static void test() { TimeZone.setDefault(TimeZone.getTimeZone("GMT-0800")); @@ -520,24 +230,8 @@ public static void test() { tryCatch("%#g", FormatFlagsConversionMismatchException.class); - - - - - - - - Byte minByte = Byte.MIN_VALUE; - - - - - - - - //--------------------------------------------------------------------- // %d // @@ -546,1127 +240,52 @@ public static void test() { //--------------------------------------------------------------------- test("%d", "null", (Object)null); + //--------------------------------------------------------------------- + // %d - errors + //--------------------------------------------------------------------- + tryCatch("%#d", FormatFlagsConversionMismatchException.class); + tryCatch("%D", UnknownFormatConversionException.class); + tryCatch("%0d", MissingFormatWidthException.class); + tryCatch("%-d", MissingFormatWidthException.class); + tryCatch("%7.3d", IllegalFormatPrecisionException.class); + //--------------------------------------------------------------------- + // %o + // + // Numeric conversion applicable to byte, short, int, long, and + // BigInteger. + //--------------------------------------------------------------------- + test("%o", "null", (Object)null); + //--------------------------------------------------------------------- + // %o - errors + //--------------------------------------------------------------------- + tryCatch("%(o", FormatFlagsConversionMismatchException.class, + minByte); + tryCatch("%+o", FormatFlagsConversionMismatchException.class, + minByte); + tryCatch("% o", FormatFlagsConversionMismatchException.class, + minByte); + tryCatch("%0o", MissingFormatWidthException.class); + tryCatch("%-o", MissingFormatWidthException.class); + tryCatch("%,o", FormatFlagsConversionMismatchException.class); + tryCatch("%O", UnknownFormatConversionException.class); + //--------------------------------------------------------------------- + // %x + // + // Numeric conversion applicable to byte, short, int, long, and + // BigInteger. + //--------------------------------------------------------------------- + test("%x", "null", (Object)null); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %d - errors - //--------------------------------------------------------------------- - tryCatch("%#d", FormatFlagsConversionMismatchException.class); - tryCatch("%D", UnknownFormatConversionException.class); - tryCatch("%0d", MissingFormatWidthException.class); - tryCatch("%-d", MissingFormatWidthException.class); - tryCatch("%7.3d", IllegalFormatPrecisionException.class); - - //--------------------------------------------------------------------- - // %o - // - // Numeric conversion applicable to byte, short, int, long, and - // BigInteger. - //--------------------------------------------------------------------- - test("%o", "null", (Object)null); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %o - errors - //--------------------------------------------------------------------- - tryCatch("%(o", FormatFlagsConversionMismatchException.class, - minByte); - tryCatch("%+o", FormatFlagsConversionMismatchException.class, - minByte); - tryCatch("% o", FormatFlagsConversionMismatchException.class, - minByte); - tryCatch("%0o", MissingFormatWidthException.class); - tryCatch("%-o", MissingFormatWidthException.class); - tryCatch("%,o", FormatFlagsConversionMismatchException.class); - tryCatch("%O", UnknownFormatConversionException.class); - - //--------------------------------------------------------------------- - // %x - // - // Numeric conversion applicable to byte, short, int, long, and - // BigInteger. - //--------------------------------------------------------------------- - test("%x", "null", (Object)null); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %x - errors - //--------------------------------------------------------------------- - tryCatch("%,x", FormatFlagsConversionMismatchException.class); - tryCatch("%0x", MissingFormatWidthException.class); - tryCatch("%-x", MissingFormatWidthException.class); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + //--------------------------------------------------------------------- + // %x - errors + //--------------------------------------------------------------------- + tryCatch("%,x", FormatFlagsConversionMismatchException.class); + tryCatch("%0x", MissingFormatWidthException.class); + tryCatch("%-x", MissingFormatWidthException.class); @@ -1691,99 +310,6 @@ public static void test() { tryCatch("%-tB", MissingFormatWidthException.class); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %n //--------------------------------------------------------------------- diff --git a/test/jdk/java/util/Formatter/BasicChar.java b/test/jdk/java/util/Formatter/BasicChar.java index c17dfb7b9351d..da990f1c998b2 100644 --- a/test/jdk/java/util/Formatter/BasicChar.java +++ b/test/jdk/java/util/Formatter/BasicChar.java @@ -38,300 +38,7 @@ import static java.util.Calendar.*; - - - - public class BasicChar extends Basic { - - private static void test(String fs, String exp, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), Locale.US); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(Locale l, String fs, String exp, Object ... args) - { - Formatter f = new Formatter(new StringBuilder(), l); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), l); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(String fs, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, "fail", f.toString()); - } - - private static void test(String fs) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, "fail"); - ck(fs, "fail", f.toString()); - } - - private static void testSysOut(String fs, String exp, Object ... args) { - FileOutputStream fos = null; - FileInputStream fis = null; - try { - PrintStream saveOut = System.out; - fos = new FileOutputStream("testSysOut"); - System.setOut(new PrintStream(fos)); - System.out.format(Locale.US, fs, args); - fos.close(); - - fis = new FileInputStream("testSysOut"); - byte [] ba = new byte[exp.length()]; - int len = fis.read(ba); - String got = new String(ba); - if (len != ba.length) - fail(fs, exp, got); - ck(fs, exp, got); - - System.setOut(saveOut); - } catch (FileNotFoundException ex) { - fail(fs, ex.getClass()); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } finally { - try { - if (fos != null) - fos.close(); - if (fis != null) - fis.close(); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } - } - } - - private static void tryCatch(String fs, Class ex) { - boolean caught = false; - try { - test(fs); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - private static void tryCatch(String fs, Class ex, Object ... args) { - boolean caught = false; - try { - test(fs, args); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -523,1161 +230,13 @@ public static void test() { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %t - // - // Date/Time conversions applicable to Calendar, Date, and long. - //--------------------------------------------------------------------- - test("%tA", "null", (Object)null); - test("%TA", "NULL", (Object)null); + //--------------------------------------------------------------------- + // %t + // + // Date/Time conversions applicable to Calendar, Date, and long. + //--------------------------------------------------------------------- + test("%tA", "null", (Object)null); + test("%TA", "NULL", (Object)null); //--------------------------------------------------------------------- // %t - errors @@ -1691,99 +250,6 @@ public static void test() { tryCatch("%-tB", MissingFormatWidthException.class); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %n //--------------------------------------------------------------------- diff --git a/test/jdk/java/util/Formatter/BasicCharObject.java b/test/jdk/java/util/Formatter/BasicCharObject.java index 3b0c2b012df4c..44d16860da655 100644 --- a/test/jdk/java/util/Formatter/BasicCharObject.java +++ b/test/jdk/java/util/Formatter/BasicCharObject.java @@ -38,300 +38,7 @@ import static java.util.Calendar.*; - - - - public class BasicCharObject extends Basic { - - private static void test(String fs, String exp, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), Locale.US); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(Locale l, String fs, String exp, Object ... args) - { - Formatter f = new Formatter(new StringBuilder(), l); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), l); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(String fs, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, "fail", f.toString()); - } - - private static void test(String fs) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, "fail"); - ck(fs, "fail", f.toString()); - } - - private static void testSysOut(String fs, String exp, Object ... args) { - FileOutputStream fos = null; - FileInputStream fis = null; - try { - PrintStream saveOut = System.out; - fos = new FileOutputStream("testSysOut"); - System.setOut(new PrintStream(fos)); - System.out.format(Locale.US, fs, args); - fos.close(); - - fis = new FileInputStream("testSysOut"); - byte [] ba = new byte[exp.length()]; - int len = fis.read(ba); - String got = new String(ba); - if (len != ba.length) - fail(fs, exp, got); - ck(fs, exp, got); - - System.setOut(saveOut); - } catch (FileNotFoundException ex) { - fail(fs, ex.getClass()); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } finally { - try { - if (fos != null) - fos.close(); - if (fis != null) - fis.close(); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } - } - } - - private static void tryCatch(String fs, Class ex) { - boolean caught = false; - try { - test(fs); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - private static void tryCatch(String fs, Class ex, Object ... args) { - boolean caught = false; - try { - test(fs, args); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -523,1161 +230,13 @@ public static void test() { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %t - // - // Date/Time conversions applicable to Calendar, Date, and long. - //--------------------------------------------------------------------- - test("%tA", "null", (Object)null); - test("%TA", "NULL", (Object)null); + //--------------------------------------------------------------------- + // %t + // + // Date/Time conversions applicable to Calendar, Date, and long. + //--------------------------------------------------------------------- + test("%tA", "null", (Object)null); + test("%TA", "NULL", (Object)null); //--------------------------------------------------------------------- // %t - errors @@ -1691,99 +250,6 @@ public static void test() { tryCatch("%-tB", MissingFormatWidthException.class); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %n //--------------------------------------------------------------------- diff --git a/test/jdk/java/util/Formatter/BasicDateTime.java b/test/jdk/java/util/Formatter/BasicDateTime.java index 7fac4860692d2..4a65918b5b44f 100644 --- a/test/jdk/java/util/Formatter/BasicDateTime.java +++ b/test/jdk/java/util/Formatter/BasicDateTime.java @@ -37,110 +37,10 @@ import java.util.*; import static java.util.Calendar.*; - import static java.util.SimpleTimeZone.*; import java.util.regex.Pattern; - public class BasicDateTime extends Basic { - - private static void test(String fs, String exp, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), Locale.US); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(Locale l, String fs, String exp, Object ... args) - { - Formatter f = new Formatter(new StringBuilder(), l); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), l); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(String fs, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, "fail", f.toString()); - } - - private static void test(String fs) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, "fail"); - ck(fs, "fail", f.toString()); - } - - private static void testSysOut(String fs, String exp, Object ... args) { - FileOutputStream fos = null; - FileInputStream fis = null; - try { - PrintStream saveOut = System.out; - fos = new FileOutputStream("testSysOut"); - System.setOut(new PrintStream(fos)); - System.out.format(Locale.US, fs, args); - fos.close(); - - fis = new FileInputStream("testSysOut"); - byte [] ba = new byte[exp.length()]; - int len = fis.read(ba); - String got = new String(ba); - if (len != ba.length) - fail(fs, exp, got); - ck(fs, exp, got); - - System.setOut(saveOut); - } catch (FileNotFoundException ex) { - fail(fs, ex.getClass()); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } finally { - try { - if (fos != null) - fos.close(); - if (fis != null) - fis.close(); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } - } - } - - private static void tryCatch(String fs, Class ex) { - boolean caught = false; - try { - test(fs); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - private static void tryCatch(String fs, Class ex, Object ... args) { - boolean caught = false; - try { - test(fs, args); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - private static void testDateTime(String fs, String exp, Calendar c) { testDateTime(fs, exp, c, true); } @@ -221,120 +121,6 @@ private static void testHours() { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public static void test() { TimeZone.setDefault(TimeZone.getTimeZone("GMT-0800")); @@ -523,1264 +309,105 @@ public static void test() { + //--------------------------------------------------------------------- + // %t + // + // Date/Time conversions applicable to Calendar, Date, and long. + //--------------------------------------------------------------------- + test("%tA", "null", (Object)null); + test("%TA", "NULL", (Object)null); + //--------------------------------------------------------------------- + // %t - errors + //--------------------------------------------------------------------- + tryCatch("%t", UnknownFormatConversionException.class); + tryCatch("%T", UnknownFormatConversionException.class); + tryCatch("%tP", UnknownFormatConversionException.class); + tryCatch("%TP", UnknownFormatConversionException.class); + tryCatch("%.5tB", IllegalFormatPrecisionException.class); + tryCatch("%#tB", FormatFlagsConversionMismatchException.class); + tryCatch("%-tB", MissingFormatWidthException.class); + //--------------------------------------------------------------------- + // %t - create test Calendar + //--------------------------------------------------------------------- + // Get the supported ids for GMT-08:00 (Pacific Standard Time) + String[] ids = TimeZone.getAvailableIDs(-8 * 60 * 60 * 1000); + // Create a Pacific Standard Time time zone + SimpleTimeZone tz = new SimpleTimeZone(-8 * 60 * 60 * 1000, ids[0]); + // public GregorianCalendar(TimeZone zone, Locale aLocale); + Calendar c0 = new GregorianCalendar(tz, Locale.US); + // public final void set(int year, int month, int date, + // int hourOfDay, int minute, int second); + c0.set(1995, MAY, 23, 19, 48, 34); + c0.set(Calendar.MILLISECOND, 584); + //--------------------------------------------------------------------- + // %t - Minutes, {nano,milli}*seconds + // + // testDateTime() verifies the expected output for all applicable types + // (Calendar, Date, and long). It also verifies output for "%t" and + // "%T". Thus it is sufficient to invoke that method once per + // conversion/expected output. + //--------------------------------------------------------------------- + testDateTime("%tM", "48", c0); + testDateTime("%tN", "584000000", c0); + testDateTime("%tL", "584", c0); +// testDateTime("%tQ", "801283714584", c0); + testDateTime("%ts", String.valueOf(c0.getTimeInMillis() / 1000), c0); + testDateTime("%tS", "34", c0); + testDateTime("%tT", "19:48:34", c0); + //--------------------------------------------------------------------- + // %t - Hours, morning/afternoon markers + // + // testHours() iterates through all twenty-four hours to verify + // numeric return value and morning/afternoon markers. + //--------------------------------------------------------------------- + testHours(); + //--------------------------------------------------------------------- + // %t - Portions of date [ day, month, dates, weeks ] + //--------------------------------------------------------------------- + testDateTime("%ta", "Tue", c0); + testDateTime("%tA", "Tuesday", c0); + testDateTime("%tb", "May", c0); + testDateTime("%tB", "May", c0); + testDateTime("%tC", "19", c0); + testDateTime("%td", "23", c0); + testDateTime("%te", "23", c0); + testDateTime("%th", "May", c0); + testDateTime("%tj", "143", c0); + testDateTime("%tm", "05", c0); + testDateTime("%ty", "95", c0); + testDateTime("%tY", "1995", c0); + //--------------------------------------------------------------------- + // %t - TimeZone + //--------------------------------------------------------------------- + testDateTime("%tz", "-0800", c0); + testDateTime("%tZ", "PST", c0); + //--------------------------------------------------------------------- + // %tz should always adjust for DST + //--------------------------------------------------------------------- + TimeZone dtz = TimeZone.getDefault(); + // Artificial TimeZone based on PST with 3:15 DST always in effect + TimeZone atz = new SimpleTimeZone(-8 * 60 * 60 * 1000, "AlwaysDST", + JANUARY, 1, 0, 0, STANDARD_TIME, + // 24hrs - 1m = 60 * 60 * 1000 * 24 - 1 + DECEMBER, 31, 0, 60 * 60 * 1000 * 24 - 1, STANDARD_TIME, + (int)(60 * 60 * 1000 * 3.25)); + TimeZone.setDefault(atz); + testDateTime("%tz", "-0445", Calendar.getInstance(atz)); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %t - // - // Date/Time conversions applicable to Calendar, Date, and long. - //--------------------------------------------------------------------- - test("%tA", "null", (Object)null); - test("%TA", "NULL", (Object)null); - - //--------------------------------------------------------------------- - // %t - errors - //--------------------------------------------------------------------- - tryCatch("%t", UnknownFormatConversionException.class); - tryCatch("%T", UnknownFormatConversionException.class); - tryCatch("%tP", UnknownFormatConversionException.class); - tryCatch("%TP", UnknownFormatConversionException.class); - tryCatch("%.5tB", IllegalFormatPrecisionException.class); - tryCatch("%#tB", FormatFlagsConversionMismatchException.class); - tryCatch("%-tB", MissingFormatWidthException.class); - - - //--------------------------------------------------------------------- - // %t - create test Calendar - //--------------------------------------------------------------------- - - // Get the supported ids for GMT-08:00 (Pacific Standard Time) - String[] ids = TimeZone.getAvailableIDs(-8 * 60 * 60 * 1000); - // With tzdata2020b, the test fails for the mentioned zones expecting - // "PST" but JDK has Zone name "MST" for JRE locale provider. - // Therefore excluding them as the test is only looking for a GMT-08:00 - // time zone ID. See JDK-8254865. - final List list = new ArrayList(); - Collections.addAll(list, ids); - list.remove("America/Dawson"); - list.remove("America/WhiteHorse"); - list.remove("Canada/Yukon"); - ids = list.toArray(new String[list.size()]); - // Create a Pacific Standard Time time zone - SimpleTimeZone tz = new SimpleTimeZone(-8 * 60 * 60 * 1000, ids[0]); - // public GregorianCalendar(TimeZone zone, Locale aLocale); - Calendar c0 = new GregorianCalendar(tz, Locale.US); - // public final void set(int year, int month, int date, - // int hourOfDay, int minute, int second); - c0.set(1995, MAY, 23, 19, 48, 34); - c0.set(Calendar.MILLISECOND, 584); - - //--------------------------------------------------------------------- - // %t - Minutes, {nano,milli}*seconds - // - // testDateTime() verifies the expected output for all applicable types - // (Calendar, Date, and long). It also verifies output for "%t" and - // "%T". Thus it is sufficient to invoke that method once per - // conversion/expected output. - //--------------------------------------------------------------------- - testDateTime("%tM", "48", c0); - testDateTime("%tN", "584000000", c0); - testDateTime("%tL", "584", c0); -// testDateTime("%tQ", "801283714584", c0); - - testDateTime("%ts", String.valueOf(c0.getTimeInMillis() / 1000), c0); - testDateTime("%tS", "34", c0); - testDateTime("%tT", "19:48:34", c0); - - //--------------------------------------------------------------------- - // %t - Hours, morning/afternoon markers - // - // testHours() iterates through all twenty-four hours to verify - // numeric return value and morning/afternoon markers. - //--------------------------------------------------------------------- - testHours(); - - //--------------------------------------------------------------------- - // %t - Portions of date [ day, month, dates, weeks ] - //--------------------------------------------------------------------- - testDateTime("%ta", "Tue", c0); - testDateTime("%tA", "Tuesday", c0); - testDateTime("%tb", "May", c0); - testDateTime("%tB", "May", c0); - testDateTime("%tC", "19", c0); - testDateTime("%td", "23", c0); - testDateTime("%te", "23", c0); - testDateTime("%th", "May", c0); - testDateTime("%tj", "143", c0); - testDateTime("%tm", "05", c0); - testDateTime("%ty", "95", c0); - testDateTime("%tY", "1995", c0); - - //--------------------------------------------------------------------- - // %t - TimeZone - //--------------------------------------------------------------------- - testDateTime("%tz", "-0800", c0); - testDateTime("%tZ", "PST", c0); - - //--------------------------------------------------------------------- - // %tz should always adjust for DST - //--------------------------------------------------------------------- - TimeZone dtz = TimeZone.getDefault(); - - // Artificial TimeZone based on PST with 3:15 DST always in effect - TimeZone atz = new SimpleTimeZone(-8 * 60 * 60 * 1000, "AlwaysDST", - JANUARY, 1, 0, 0, STANDARD_TIME, - // 24hrs - 1m = 60 * 60 * 1000 * 24 - 1 - DECEMBER, 31, 0, 60 * 60 * 1000 * 24 - 1, STANDARD_TIME, - (int)(60 * 60 * 1000 * 3.25)); - TimeZone.setDefault(atz); - testDateTime("%tz", "-0445", Calendar.getInstance(atz)); - - // Restore the TimeZone and verify - TimeZone.setDefault(dtz); - if (atz.hasSameRules(TimeZone.getDefault())) - throw new RuntimeException("Default TimeZone not restored"); + // Restore the TimeZone and verify + TimeZone.setDefault(dtz); + if (atz.hasSameRules(TimeZone.getDefault())) + throw new RuntimeException("Default TimeZone not restored"); //--------------------------------------------------------------------- // %t - Composites @@ -1793,7 +420,6 @@ public static void test() { testDateTime("%-12tF", "1995-05-23 ", c0); testDateTime("%12tF", " 1995-05-23", c0); - //--------------------------------------------------------------------- // %n //--------------------------------------------------------------------- diff --git a/test/jdk/java/util/Formatter/BasicDouble.java b/test/jdk/java/util/Formatter/BasicDouble.java index 5637d10f21dc2..493840839c43d 100644 --- a/test/jdk/java/util/Formatter/BasicDouble.java +++ b/test/jdk/java/util/Formatter/BasicDouble.java @@ -38,266 +38,7 @@ import static java.util.Calendar.*; - - - - public class BasicDouble extends Basic { - - private static void test(String fs, String exp, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), Locale.US); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(Locale l, String fs, String exp, Object ... args) - { - Formatter f = new Formatter(new StringBuilder(), l); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), l); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(String fs, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, "fail", f.toString()); - } - - private static void test(String fs) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, "fail"); - ck(fs, "fail", f.toString()); - } - - private static void testSysOut(String fs, String exp, Object ... args) { - FileOutputStream fos = null; - FileInputStream fis = null; - try { - PrintStream saveOut = System.out; - fos = new FileOutputStream("testSysOut"); - System.setOut(new PrintStream(fos)); - System.out.format(Locale.US, fs, args); - fos.close(); - - fis = new FileInputStream("testSysOut"); - byte [] ba = new byte[exp.length()]; - int len = fis.read(ba); - String got = new String(ba); - if (len != ba.length) - fail(fs, exp, got); - ck(fs, exp, got); - - System.setOut(saveOut); - } catch (FileNotFoundException ex) { - fail(fs, ex.getClass()); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } finally { - try { - if (fos != null) - fos.close(); - if (fis != null) - fis.close(); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } - } - } - - private static void tryCatch(String fs, Class ex) { - boolean caught = false; - try { - test(fs); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - private static void tryCatch(String fs, Class ex, Object ... args) { - boolean caught = false; - try { - test(fs, args); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - private static double create(double v) { @@ -317,24 +58,6 @@ private static double recip(double v) { return 1.0 / v; } - - - - - - - - - - - - - - - - - - public static void test() { TimeZone.setDefault(TimeZone.getTimeZone("GMT-0800")); @@ -522,1088 +245,354 @@ public static void test() { + //--------------------------------------------------------------------- + // %s - double + //--------------------------------------------------------------------- + double one = 1.0; + double ten = 10.0; + double pi = Math.PI; + test("%s", "3.141592653589793", pi); + //--------------------------------------------------------------------- + // flag/conversion errors + //--------------------------------------------------------------------- + tryCatch("%d", IllegalFormatConversionException.class, one); + tryCatch("%,.4e", FormatFlagsConversionMismatchException.class, one); + //--------------------------------------------------------------------- + // %e + // + // Floating-point conversions applicable to float, double, and + // BigDecimal. + //--------------------------------------------------------------------- + test("%e", "null", (Object)null); + //--------------------------------------------------------------------- + // %e - float and double + //--------------------------------------------------------------------- + // double PI = 3.141 592 653 589 793 238 46; + test("%e", "3.141593e+00", pi); + test("%.0e", "1e+01", ten); + test("%#.0e", "1.e+01", ten); + test("%E", "3.141593E+00", pi); + test("%10.3e", " 3.142e+00", pi); + test("%10.3e", "-3.142e+00", negate(pi)); + test("%010.3e", "03.142e+00", pi); + test("%010.3e", "-3.142e+00", negate(pi)); + test("%-12.3e", "3.142e+00 ", pi); + test("%-12.3e", "-3.142e+00 ", negate(pi)); + test("%.3e", "3.142e+00", pi); + test("%.3e", "-3.142e+00", negate(pi)); + test("%.3e", "3.142e+06", mult(pi, 1000000.0)); + test("%.3e", "-3.142e+06", mult(pi, -1000000.0)); + test(Locale.FRANCE, "%e", "3,141593e+00", pi); + // double PI^300 + // = 13962455701329742638131355433930076081862072808 ... e+149 + test("%10.3e", " 1.000e+00", one); + test("%+.3e", "+3.142e+00", pi); + test("%+.3e", "-3.142e+00", negate(pi)); + test("% .3e", " 3.142e+00", pi); + test("% .3e", "-3.142e+00", negate(pi)); + test("%#.0e", "3.e+00", create(3.0)); + test("%#.0e", "-3.e+00", create(-3.0)); + test("%.0e", "3e+00", create(3.0)); + test("%.0e", "-3e+00", create(-3.0)); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %s - double - //--------------------------------------------------------------------- - double one = 1.0; - double ten = 10.0; - double pi = Math.PI; - - test("%s", "3.141592653589793", pi); - - - - - - - - - - - - - //--------------------------------------------------------------------- - // flag/conversion errors - //--------------------------------------------------------------------- - tryCatch("%d", IllegalFormatConversionException.class, one); - tryCatch("%,.4e", FormatFlagsConversionMismatchException.class, one); - - //--------------------------------------------------------------------- - // %e - // - // Floating-point conversions applicable to float, double, and - // BigDecimal. - //--------------------------------------------------------------------- - test("%e", "null", (Object)null); - - //--------------------------------------------------------------------- - // %e - float and double - //--------------------------------------------------------------------- - // double PI = 3.141 592 653 589 793 238 46; - test("%e", "3.141593e+00", pi); - test("%.0e", "1e+01", ten); - test("%#.0e", "1.e+01", ten); - test("%E", "3.141593E+00", pi); - test("%10.3e", " 3.142e+00", pi); - test("%10.3e", "-3.142e+00", negate(pi)); - test("%010.3e", "03.142e+00", pi); - test("%010.3e", "-3.142e+00", negate(pi)); - test("%-12.3e", "3.142e+00 ", pi); - test("%-12.3e", "-3.142e+00 ", negate(pi)); - test("%.3e", "3.142e+00", pi); - test("%.3e", "-3.142e+00", negate(pi)); - test("%.3e", "3.142e+06", mult(pi, 1000000.0)); - test("%.3e", "-3.142e+06", mult(pi, -1000000.0)); - - test(Locale.FRANCE, "%e", "3,141593e+00", pi); - - // double PI^300 - // = 13962455701329742638131355433930076081862072808 ... e+149 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - test("%10.3e", " 1.000e+00", one); - test("%+.3e", "+3.142e+00", pi); - test("%+.3e", "-3.142e+00", negate(pi)); - test("% .3e", " 3.142e+00", pi); - test("% .3e", "-3.142e+00", negate(pi)); - test("%#.0e", "3.e+00", create(3.0)); - test("%#.0e", "-3.e+00", create(-3.0)); - test("%.0e", "3e+00", create(3.0)); - test("%.0e", "-3e+00", create(-3.0)); - - test("%(.4e", "3.1416e+06", mult(pi, 1000000.0)); - test("%(.4e", "(3.1416e+06)", mult(pi, -1000000.0)); - - //--------------------------------------------------------------------- - // %e - boundary problems - //--------------------------------------------------------------------- - test("%3.0e", "1e-06", 0.000001); - test("%3.0e", "1e-05", 0.00001); - test("%3.0e", "1e-04", 0.0001); - test("%3.0e", "1e-03", 0.001); - test("%3.0e", "1e-02", 0.01); - test("%3.0e", "1e-01", 0.1); - test("%3.0e", "9e-01", 0.9); - test("%3.1e", "9.0e-01", 0.9); - test("%3.0e", "1e+00", 1.00); - test("%3.0e", "1e+01", 10.00); - test("%3.0e", "1e+02", 99.19); - test("%3.1e", "9.9e+01", 99.19); - test("%3.0e", "1e+02", 99.99); - test("%3.0e", "1e+02", 100.00); - test("%#3.0e", "1.e+03", 1000.00); - test("%3.0e", "1e+04", 10000.00); - test("%3.0e", "1e+05", 100000.00); - test("%3.0e", "1e+06", 1000000.00); - test("%3.0e", "1e+07", 10000000.00); - test("%3.0e", "1e+08", 100000000.00); - - //--------------------------------------------------------------------- - // %f - // - // Floating-point conversions applicable to float, double, and - // BigDecimal. - //--------------------------------------------------------------------- - test("%f", "null", (Object)null); - test("%f", "3.141593", pi); - test(Locale.FRANCE, "%f", "3,141593", pi); - test("%10.3f", " 3.142", pi); - test("%10.3f", " -3.142", negate(pi)); - test("%010.3f", "000003.142", pi); - test("%010.3f", "-00003.142", negate(pi)); - test("%-10.3f", "3.142 ", pi); - test("%-10.3f", "-3.142 ", negate(pi)); - test("%.3f", "3.142", pi); - test("%.3f", "-3.142", negate(pi)); - test("%+.3f", "+3.142", pi); - test("%+.3f", "-3.142", negate(pi)); - test("% .3f", " 3.142", pi); - test("% .3f", "-3.142", negate(pi)); - test("%#.0f", "3.", create(3.0)); - test("%#.0f", "-3.", create(-3.0)); - test("%.0f", "3", create(3.0)); - test("%.0f", "-3", create(-3.0)); - test("%.3f", "10.000", ten); - test("%.3f", "1.000", one); - test("%10.3f", " 1.000", one); - - //--------------------------------------------------------------------- - // %f - boundary problems - //--------------------------------------------------------------------- - test("%3.0f", " 0", 0.000001); - test("%3.0f", " 0", 0.00001); - test("%3.0f", " 0", 0.0001); - test("%3.0f", " 0", 0.001); - test("%3.0f", " 0", 0.01); - test("%3.0f", " 0", 0.1); - test("%3.0f", " 1", 0.9); - test("%3.1f", "0.9", 0.9); - test("%3.0f", " 1", 1.00); - test("%3.0f", " 10", 10.00); - test("%3.0f", " 99", 99.19); - test("%3.1f", "99.2", 99.19); - test("%3.0f", "100", 99.99); - test("%3.0f", "100", 100.00); - test("%#3.0f", "1000.", 1000.00); - test("%3.0f", "10000", 10000.00); - test("%3.0f", "100000", 100000.00); - test("%3.0f", "1000000", 1000000.00); - test("%3.0f", "10000000", 10000000.00); - test("%3.0f", "100000000", 100000000.00); - test("%10.0f", " 1000000", 1000000.00); - test("%,10.0f", " 1,000,000", 1000000.00); - test("%,10.1f", "1,000,000.0", 1000000.00); - test("%,3.0f", "1,000,000", 1000000.00); - test("%,3.0f", "10,000,000", 10000000.00); - test("%,3.0f", "100,000,000", 100000000.00); - test("%,3.0f", "10,000,000", 10000000.00); - test("%,3.0f", "100,000,000", 100000000.00); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + test("%(.4e", "3.1416e+06", mult(pi, 1000000.0)); + test("%(.4e", "(3.1416e+06)", mult(pi, -1000000.0)); //--------------------------------------------------------------------- - // %f - float, double, Double, BigDecimal + // %e - boundary problems //--------------------------------------------------------------------- - test("%.3f", "3141592.654", mult(pi, 1000000.0)); - test("%.3f", "-3141592.654", mult(pi, -1000000.0)); - test("%,.4f", "3,141,592.6536", mult(pi, 1000000.0)); - test(Locale.FRANCE, "%,.4f", "3\u202f141\u202f592,6536", mult(pi, 1000000.0)); - test("%,.4f", "-3,141,592.6536", mult(pi, -1000000.0)); - test("%(.4f", "3141592.6536", mult(pi, 1000000.0)); - test("%(.4f", "(3141592.6536)", mult(pi, -1000000.0)); - test("%(,.4f", "3,141,592.6536", mult(pi, 1000000.0)); - test("%(,.4f", "(3,141,592.6536)", mult(pi, -1000000.0)); - - - + test("%3.0e", "1e-06", 0.000001); + test("%3.0e", "1e-05", 0.00001); + test("%3.0e", "1e-04", 0.0001); + test("%3.0e", "1e-03", 0.001); + test("%3.0e", "1e-02", 0.01); + test("%3.0e", "1e-01", 0.1); + test("%3.0e", "9e-01", 0.9); + test("%3.1e", "9.0e-01", 0.9); + test("%3.0e", "1e+00", 1.00); + test("%3.0e", "1e+01", 10.00); + test("%3.0e", "1e+02", 99.19); + test("%3.1e", "9.9e+01", 99.19); + test("%3.0e", "1e+02", 99.99); + test("%3.0e", "1e+02", 100.00); + test("%#3.0e", "1.e+03", 1000.00); + test("%3.0e", "1e+04", 10000.00); + test("%3.0e", "1e+05", 100000.00); + test("%3.0e", "1e+06", 1000000.00); + test("%3.0e", "1e+07", 10000000.00); + test("%3.0e", "1e+08", 100000000.00); //--------------------------------------------------------------------- - // %g + // %f // // Floating-point conversions applicable to float, double, and // BigDecimal. //--------------------------------------------------------------------- - test("%g", "null", (Object)null); - test("%g", "3.14159", pi); - test(Locale.FRANCE, "%g", "3,14159", pi); - test("%.0g", "1e+01", ten); - test("%G", "3.14159", pi); - test("%10.3g", " 3.14", pi); - test("%10.3g", " -3.14", negate(pi)); - test("%010.3g", "0000003.14", pi); - test("%010.3g", "-000003.14", negate(pi)); - test("%-12.3g", "3.14 ", pi); - test("%-12.3g", "-3.14 ", negate(pi)); - test("%.3g", "3.14", pi); - test("%.3g", "-3.14", negate(pi)); - test("%.3g", "3.14e+08", mult(pi, 100000000.0)); - test("%.3g", "-3.14e+08", mult(pi, -100000000.0)); - - test("%.3g", "1.00e-05", recip(create(100000.0))); - test("%.3g", "-1.00e-05", recip(create(-100000.0))); - test("%.0g", "-1e-05", recip(create(-100000.0))); - test("%.0g", "1e+05", create(100000.0)); - test("%.3G", "1.00E-05", recip(create(100000.0))); - test("%.3G", "-1.00E-05", recip(create(-100000.0))); - - test("%.1g", "-0", -0.0); - test("%3.0g", " -0", -0.0); - test("%.1g", "0", 0.0); - test("%3.0g", " 0", 0.0); - test("%.1g", "0", +0.0); - test("%3.0g", " 0", +0.0); - - test("%3.0g", "1e-06", 0.000001); - test("%3.0g", "1e-05", 0.00001); - test("%3.0g", "1e-05", 0.0000099); - test("%3.1g", "1e-05", 0.0000099); - test("%3.2g", "9.9e-06", 0.0000099); - test("%3.0g", "0.0001", 0.0001); - test("%3.0g", "9e-05", 0.00009); - test("%3.0g", "0.0001", 0.000099); - test("%3.1g", "0.0001", 0.000099); - test("%3.2g", "9.9e-05", 0.000099); - test("%3.0g", "0.001", 0.001); - test("%3.0g", "0.001", 0.00099); - test("%3.1g", "0.001", 0.00099); - test("%3.2g", "0.00099", 0.00099); - test("%3.3g", "0.00100", 0.001); - test("%3.4g", "0.001000", 0.001); - test("%3.0g", "0.01", 0.01); - test("%3.0g", "0.1", 0.1); - test("%3.0g", "0.9", 0.9); - test("%3.1g", "0.9", 0.9); - test("%3.0g", " 1", 1.00); - test("%3.2g", " 10", 10.00); - test("%3.0g", "1e+01", 10.00); - test("%3.0g", "1e+02", 99.19); - test("%3.1g", "1e+02", 99.19); - test("%3.2g", " 99", 99.19); - test("%3.0g", "1e+02", 99.9); - test("%3.1g", "1e+02", 99.9); - test("%3.2g", "1.0e+02", 99.9); - test("%3.0g", "1e+02", 99.99); - test("%3.0g", "1e+02", 100.00); - test("%3.0g", "1e+03", 999.9); - test("%3.1g", "1e+03", 999.9); - test("%3.2g", "1.0e+03", 999.9); - test("%3.3g", "1.00e+03", 999.9); - test("%3.4g", "999.9", 999.9); - test("%3.4g", "1000", 999.99); - test("%3.0g", "1e+03", 1000.00); - test("%3.0g", "1e+04", 10000.00); - test("%3.0g", "1e+05", 100000.00); - test("%3.0g", "1e+06", 1000000.00); - test("%3.0g", "1e+07", 10000000.00); - test("%3.9g", "100000000", 100000000.00); - test("%3.10g", "100000000.0", 100000000.00); - - tryCatch("%#3.0g", FormatFlagsConversionMismatchException.class, 1000.00); - - // double PI^300 - // = 13962455701329742638131355433930076081862072808 ... e+149 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - test("%.3g", "10.0", ten); - test("%.3g", "1.00", one); - test("%10.3g", " 1.00", one); - test("%+10.3g", " +3.14", pi); - test("%+10.3g", " -3.14", negate(pi)); - test("% .3g", " 3.14", pi); - test("% .3g", "-3.14", negate(pi)); - test("%.0g", "3", create(3.0)); - test("%.0g", "-3", create(-3.0)); - - test("%(.4g", "3.142e+08", mult(pi, 100000000.0)); - test("%(.4g", "(3.142e+08)", mult(pi, -100000000.0)); - - - - - - - - test("%,.11g", "3,141,592.6536", mult(pi, 1000000.0)); - test("%(,.11g", "(3,141,592.6536)", mult(pi, -1000000.0)); + test("%f", "null", (Object)null); + test("%f", "3.141593", pi); + test(Locale.FRANCE, "%f", "3,141593", pi); + test("%10.3f", " 3.142", pi); + test("%10.3f", " -3.142", negate(pi)); + test("%010.3f", "000003.142", pi); + test("%010.3f", "-00003.142", negate(pi)); + test("%-10.3f", "3.142 ", pi); + test("%-10.3f", "-3.142 ", negate(pi)); + test("%.3f", "3.142", pi); + test("%.3f", "-3.142", negate(pi)); + test("%+.3f", "+3.142", pi); + test("%+.3f", "-3.142", negate(pi)); + test("% .3f", " 3.142", pi); + test("% .3f", "-3.142", negate(pi)); + test("%#.0f", "3.", create(3.0)); + test("%#.0f", "-3.", create(-3.0)); + test("%.0f", "3", create(3.0)); + test("%.0f", "-3", create(-3.0)); + test("%.3f", "10.000", ten); + test("%.3f", "1.000", one); + test("%10.3f", " 1.000", one); + //--------------------------------------------------------------------- + // %f - boundary problems + //--------------------------------------------------------------------- + test("%3.0f", " 0", 0.000001); + test("%3.0f", " 0", 0.00001); + test("%3.0f", " 0", 0.0001); + test("%3.0f", " 0", 0.001); + test("%3.0f", " 0", 0.01); + test("%3.0f", " 0", 0.1); + test("%3.0f", " 1", 0.9); + test("%3.1f", "0.9", 0.9); + test("%3.0f", " 1", 1.00); + test("%3.0f", " 10", 10.00); + test("%3.0f", " 99", 99.19); + test("%3.1f", "99.2", 99.19); + test("%3.0f", "100", 99.99); + test("%3.0f", "100", 100.00); + test("%#3.0f", "1000.", 1000.00); + test("%3.0f", "10000", 10000.00); + test("%3.0f", "100000", 100000.00); + test("%3.0f", "1000000", 1000000.00); + test("%3.0f", "10000000", 10000000.00); + test("%3.0f", "100000000", 100000000.00); + test("%10.0f", " 1000000", 1000000.00); + test("%,10.0f", " 1,000,000", 1000000.00); + test("%,10.1f", "1,000,000.0", 1000000.00); + test("%,3.0f", "1,000,000", 1000000.00); + test("%,3.0f", "10,000,000", 10000000.00); + test("%,3.0f", "100,000,000", 100000000.00); + test("%,3.0f", "10,000,000", 10000000.00); + test("%,3.0f", "100,000,000", 100000000.00); + //--------------------------------------------------------------------- + // %f - float, double, Double, BigDecimal + //--------------------------------------------------------------------- + test("%.3f", "3141592.654", mult(pi, 1000000.0)); + test("%.3f", "-3141592.654", mult(pi, -1000000.0)); + test("%,.4f", "3,141,592.6536", mult(pi, 1000000.0)); + test(Locale.FRANCE, "%,.4f", "3\u202f141\u202f592,6536", mult(pi, 1000000.0)); + test("%,.4f", "-3,141,592.6536", mult(pi, -1000000.0)); + test("%(.4f", "3141592.6536", mult(pi, 1000000.0)); + test("%(.4f", "(3141592.6536)", mult(pi, -1000000.0)); + test("%(,.4f", "3,141,592.6536", mult(pi, 1000000.0)); + test("%(,.4f", "(3,141,592.6536)", mult(pi, -1000000.0)); //--------------------------------------------------------------------- - // %a + // %g // // Floating-point conversions applicable to float, double, and // BigDecimal. //--------------------------------------------------------------------- - test("%a", "null", (Object)null); - test("%.11a", "0x0.00000000000p0", 0.0); - test(Locale.FRANCE, "%.11a", "0x0.00000000000p0", 0.0); // no localization - test("%.1a", "0x0.0p0", 0.0); - test("%.11a", "-0x0.00000000000p0", -0.0); - test("%.1a", "-0x0.0p0", -0.0); - test("%.11a", "0x1.00000000000p0", 1.0); - test("%.1a", "0x1.0p0", 1.0); - test("%.11a", "-0x1.00000000000p0", -1.0); - test("%.1a", "-0x1.0p0", -1.0); - test("%.11a", "0x1.80000000000p1", 3.0); - test("%.1a", "0x1.8p1", 3.0); - test("%.11a", "0x1.00000000000p-1022", Double.MIN_NORMAL); - test("%.1a", "0x1.0p-1022", Double.MIN_NORMAL); - test("%.11a", "0x1.00000000000p-1022", - Math.nextDown(Double.MIN_NORMAL)); - test("%.1a", "0x1.0p-1022", - Math.nextDown(Double.MIN_NORMAL)); - test("%.11a", "0x1.ffffffffffep-1023", 0x0.fffffffffffp-1022); - test("%.1a", "0x1.0p-1022", 0x0.fffffffffffp-1022); - test("%.30a", "0x0.000000000000100000000000000000p-1022", Double.MIN_VALUE); - test("%.13a", "0x0.0000000000001p-1022", Double.MIN_VALUE); - test("%.11a", "0x1.00000000000p-1074", Double.MIN_VALUE); - test("%.1a", "0x1.0p-1074", Double.MIN_VALUE); - - test("%.11a", "0x1.08000000000p-1069", - Double.MIN_VALUE + Double.MIN_VALUE*32); - test("%.1a", "0x1.0p-1069", - Double.MIN_VALUE + Double.MIN_VALUE*32); - test("%.30a", "0x1.fffffffffffff00000000000000000p1023", Double.MAX_VALUE); - test("%.13a", "0x1.fffffffffffffp1023", Double.MAX_VALUE); - test("%.11a", "0x1.00000000000p1024", Double.MAX_VALUE); - test("%.1a", "0x1.0p1024", Double.MAX_VALUE); - test("%.11a", "0x1.18000000000p0", 0x1.18p0); - test("%.1a", "0x1.2p0", 0x1.18p0); - - test("%.11a", "0x1.18000000000p0", 0x1.180000000001p0); - test("%.1a", "0x1.2p0", 0x1.180000000001p0); - test("%.11a", "0x1.28000000000p0", 0x1.28p0); - test("%.1a", "0x1.2p0", 0x1.28p0); - - test("%.11a", "0x1.28000000000p0", 0x1.280000000001p0); - test("%.1a", "0x1.3p0", 0x1.280000000001p0); - - test("%a", "0x0.123p-1022", 0x0.123p-1022); - test("%1.3a", "0x1.230p-1026", 0x0.123p-1022); - test("%1.12a", "0x1.230000000000p-1026", 0x0.123p-1022); - test("%1.15a", "0x0.123000000000000p-1022", 0x0.123p-1022); - test("%1.5a", "0x1.00000p-1074", 0x0.0000000000001p-1022); - test("%1.7a", "0x1.0000000p-1022", 0x0.fffffffffffffp-1022); - - test("%1.6a", "0x1.230000p-1026", 0x0.123000057p-1022); - test("%1.7a", "0x1.2300005p-1026", 0x0.123000057p-1022); - test("%1.8a", "0x1.23000057p-1026", 0x0.123000057p-1022); - test("%1.9a", "0x1.230000570p-1026", 0x0.123000057p-1022); - - test("%1.6a", "0x1.230000p-1026", 0x0.123000058p-1022); - test("%1.7a", "0x1.2300006p-1026", 0x0.123000058p-1022); - test("%1.8a", "0x1.23000058p-1026", 0x0.123000058p-1022); - test("%1.9a", "0x1.230000580p-1026", 0x0.123000058p-1022); - - test("%1.6a", "0x1.230000p-1026", 0x0.123000059p-1022); - test("%1.7a", "0x1.2300006p-1026", 0x0.123000059p-1022); - test("%1.8a", "0x1.23000059p-1026", 0x0.123000059p-1022); - test("%1.9a", "0x1.230000590p-1026", 0x0.123000059p-1022); - - test("%1.4a", "0x1.0000p-1022", Math.nextDown(Double.MIN_NORMAL)); - - test("%a", "0x1.fffffffffffffp1023", Double.MAX_VALUE); - test("%1.1a", "0x1.0p1024", Double.MAX_VALUE); - test("%1.2a", "0x1.00p1024", Double.MAX_VALUE); - test("%1.6a", "0x1.000000p1024", Double.MAX_VALUE); - test("%1.9a", "0x1.000000000p1024", Double.MAX_VALUE); - test("%1.11a", "0x1.00000000000p1024", Double.MAX_VALUE); - test("%1.12a", "0x1.000000000000p1024", Double.MAX_VALUE); - test("%1.13a", "0x1.fffffffffffffp1023", Double.MAX_VALUE); - - - - //--------------------------------------------------------------------- - // %f, %e, %g, %a - Boundaries - //--------------------------------------------------------------------- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + test("%g", "null", (Object)null); + test("%g", "3.14159", pi); + test(Locale.FRANCE, "%g", "3,14159", pi); + test("%.0g", "1e+01", ten); + test("%G", "3.14159", pi); + test("%10.3g", " 3.14", pi); + test("%10.3g", " -3.14", negate(pi)); + test("%010.3g", "0000003.14", pi); + test("%010.3g", "-000003.14", negate(pi)); + test("%-12.3g", "3.14 ", pi); + test("%-12.3g", "-3.14 ", negate(pi)); + test("%.3g", "3.14", pi); + test("%.3g", "-3.14", negate(pi)); + test("%.3g", "3.14e+08", mult(pi, 100000000.0)); + test("%.3g", "-3.14e+08", mult(pi, -100000000.0)); + test("%.3g", "1.00e-05", recip(create(100000.0))); + test("%.3g", "-1.00e-05", recip(create(-100000.0))); + test("%.0g", "-1e-05", recip(create(-100000.0))); + test("%.0g", "1e+05", create(100000.0)); + test("%.3G", "1.00E-05", recip(create(100000.0))); + test("%.3G", "-1.00E-05", recip(create(-100000.0))); + test("%.1g", "-0", -0.0); + test("%3.0g", " -0", -0.0); + test("%.1g", "0", 0.0); + test("%3.0g", " 0", 0.0); + test("%.1g", "0", +0.0); + test("%3.0g", " 0", +0.0); + test("%3.0g", "1e-06", 0.000001); + test("%3.0g", "1e-05", 0.00001); + test("%3.0g", "1e-05", 0.0000099); + test("%3.1g", "1e-05", 0.0000099); + test("%3.2g", "9.9e-06", 0.0000099); + test("%3.0g", "0.0001", 0.0001); + test("%3.0g", "9e-05", 0.00009); + test("%3.0g", "0.0001", 0.000099); + test("%3.1g", "0.0001", 0.000099); + test("%3.2g", "9.9e-05", 0.000099); + test("%3.0g", "0.001", 0.001); + test("%3.0g", "0.001", 0.00099); + test("%3.1g", "0.001", 0.00099); + test("%3.2g", "0.00099", 0.00099); + test("%3.3g", "0.00100", 0.001); + test("%3.4g", "0.001000", 0.001); + test("%3.0g", "0.01", 0.01); + test("%3.0g", "0.1", 0.1); + test("%3.0g", "0.9", 0.9); + test("%3.1g", "0.9", 0.9); + test("%3.0g", " 1", 1.00); + test("%3.2g", " 10", 10.00); + test("%3.0g", "1e+01", 10.00); + test("%3.0g", "1e+02", 99.19); + test("%3.1g", "1e+02", 99.19); + test("%3.2g", " 99", 99.19); + test("%3.0g", "1e+02", 99.9); + test("%3.1g", "1e+02", 99.9); + test("%3.2g", "1.0e+02", 99.9); + test("%3.0g", "1e+02", 99.99); + test("%3.0g", "1e+02", 100.00); + test("%3.0g", "1e+03", 999.9); + test("%3.1g", "1e+03", 999.9); + test("%3.2g", "1.0e+03", 999.9); + test("%3.3g", "1.00e+03", 999.9); + test("%3.4g", "999.9", 999.9); + test("%3.4g", "1000", 999.99); + test("%3.0g", "1e+03", 1000.00); + test("%3.0g", "1e+04", 10000.00); + test("%3.0g", "1e+05", 100000.00); + test("%3.0g", "1e+06", 1000000.00); + test("%3.0g", "1e+07", 10000000.00); + test("%3.9g", "100000000", 100000000.00); + test("%3.10g", "100000000.0", 100000000.00); + tryCatch("%#3.0g", FormatFlagsConversionMismatchException.class, 1000.00); + // double PI^300 + // = 13962455701329742638131355433930076081862072808 ... e+149 + test("%.3g", "10.0", ten); + test("%.3g", "1.00", one); + test("%10.3g", " 1.00", one); + test("%+10.3g", " +3.14", pi); + test("%+10.3g", " -3.14", negate(pi)); + test("% .3g", " 3.14", pi); + test("% .3g", "-3.14", negate(pi)); + test("%.0g", "3", create(3.0)); + test("%.0g", "-3", create(-3.0)); + test("%(.4g", "3.142e+08", mult(pi, 100000000.0)); + test("%(.4g", "(3.142e+08)", mult(pi, -100000000.0)); + test("%,.11g", "3,141,592.6536", mult(pi, 1000000.0)); + test("%(,.11g", "(3,141,592.6536)", mult(pi, -1000000.0)); + //--------------------------------------------------------------------- + // %a + // + // Floating-point conversions applicable to float, double, and + // BigDecimal. + //--------------------------------------------------------------------- + test("%a", "null", (Object)null); + test("%.11a", "0x0.00000000000p0", 0.0); + test(Locale.FRANCE, "%.11a", "0x0.00000000000p0", 0.0); // no localization + test("%.1a", "0x0.0p0", 0.0); + test("%.11a", "-0x0.00000000000p0", -0.0); + test("%.1a", "-0x0.0p0", -0.0); + test("%.11a", "0x1.00000000000p0", 1.0); + test("%.1a", "0x1.0p0", 1.0); + test("%.11a", "-0x1.00000000000p0", -1.0); + test("%.1a", "-0x1.0p0", -1.0); + test("%.11a", "0x1.80000000000p1", 3.0); + test("%.1a", "0x1.8p1", 3.0); + test("%.11a", "0x1.00000000000p-1022", Double.MIN_NORMAL); + test("%.1a", "0x1.0p-1022", Double.MIN_NORMAL); + test("%.11a", "0x1.00000000000p-1022", + Math.nextDown(Double.MIN_NORMAL)); + test("%.1a", "0x1.0p-1022", + Math.nextDown(Double.MIN_NORMAL)); + test("%.11a", "0x1.ffffffffffep-1023", 0x0.fffffffffffp-1022); + test("%.1a", "0x1.0p-1022", 0x0.fffffffffffp-1022); + test("%.30a", "0x0.000000000000100000000000000000p-1022", Double.MIN_VALUE); + test("%.13a", "0x0.0000000000001p-1022", Double.MIN_VALUE); + test("%.11a", "0x1.00000000000p-1074", Double.MIN_VALUE); + test("%.1a", "0x1.0p-1074", Double.MIN_VALUE); + test("%.11a", "0x1.08000000000p-1069", + Double.MIN_VALUE + Double.MIN_VALUE*32); + test("%.1a", "0x1.0p-1069", + Double.MIN_VALUE + Double.MIN_VALUE*32); + test("%.30a", "0x1.fffffffffffff00000000000000000p1023", Double.MAX_VALUE); + test("%.13a", "0x1.fffffffffffffp1023", Double.MAX_VALUE); + test("%.11a", "0x1.00000000000p1024", Double.MAX_VALUE); + test("%.1a", "0x1.0p1024", Double.MAX_VALUE); + test("%.11a", "0x1.18000000000p0", 0x1.18p0); + test("%.1a", "0x1.2p0", 0x1.18p0); + test("%.11a", "0x1.18000000000p0", 0x1.180000000001p0); + test("%.1a", "0x1.2p0", 0x1.180000000001p0); + test("%.11a", "0x1.28000000000p0", 0x1.28p0); + test("%.1a", "0x1.2p0", 0x1.28p0); + test("%.11a", "0x1.28000000000p0", 0x1.280000000001p0); + test("%.1a", "0x1.3p0", 0x1.280000000001p0); + test("%a", "0x0.123p-1022", 0x0.123p-1022); + test("%1.3a", "0x1.230p-1026", 0x0.123p-1022); + test("%1.12a", "0x1.230000000000p-1026", 0x0.123p-1022); + test("%1.15a", "0x0.123000000000000p-1022", 0x0.123p-1022); + test("%1.5a", "0x1.00000p-1074", 0x0.0000000000001p-1022); + test("%1.7a", "0x1.0000000p-1022", 0x0.fffffffffffffp-1022); + test("%1.6a", "0x1.230000p-1026", 0x0.123000057p-1022); + test("%1.7a", "0x1.2300005p-1026", 0x0.123000057p-1022); + test("%1.8a", "0x1.23000057p-1026", 0x0.123000057p-1022); + test("%1.9a", "0x1.230000570p-1026", 0x0.123000057p-1022); + test("%1.6a", "0x1.230000p-1026", 0x0.123000058p-1022); + test("%1.7a", "0x1.2300006p-1026", 0x0.123000058p-1022); + test("%1.8a", "0x1.23000058p-1026", 0x0.123000058p-1022); + test("%1.9a", "0x1.230000580p-1026", 0x0.123000058p-1022); + test("%1.6a", "0x1.230000p-1026", 0x0.123000059p-1022); + test("%1.7a", "0x1.2300006p-1026", 0x0.123000059p-1022); + test("%1.8a", "0x1.23000059p-1026", 0x0.123000059p-1022); + test("%1.9a", "0x1.230000590p-1026", 0x0.123000059p-1022); + test("%1.4a", "0x1.0000p-1022", Math.nextDown(Double.MIN_NORMAL)); + test("%a", "0x1.fffffffffffffp1023", Double.MAX_VALUE); + test("%1.1a", "0x1.0p1024", Double.MAX_VALUE); + test("%1.2a", "0x1.00p1024", Double.MAX_VALUE); + test("%1.6a", "0x1.000000p1024", Double.MAX_VALUE); + test("%1.9a", "0x1.000000000p1024", Double.MAX_VALUE); + test("%1.11a", "0x1.00000000000p1024", Double.MAX_VALUE); + test("%1.12a", "0x1.000000000000p1024", Double.MAX_VALUE); + test("%1.13a", "0x1.fffffffffffffp1023", Double.MAX_VALUE); + //--------------------------------------------------------------------- + // %f, %e, %g, %a - Boundaries + //--------------------------------------------------------------------- //--------------------------------------------------------------------- // %f, %e, %g, %a - Double.MIN_VALUE @@ -1669,8 +658,6 @@ public static void test() { test("%30a", " 0x1.fffffffffffffp1023", Double.MAX_VALUE); - - //--------------------------------------------------------------------- // %t // @@ -1691,99 +678,6 @@ public static void test() { tryCatch("%-tB", MissingFormatWidthException.class); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %n //--------------------------------------------------------------------- diff --git a/test/jdk/java/util/Formatter/BasicDoubleObject.java b/test/jdk/java/util/Formatter/BasicDoubleObject.java index dd88af2ee16f3..a62a10400db9a 100644 --- a/test/jdk/java/util/Formatter/BasicDoubleObject.java +++ b/test/jdk/java/util/Formatter/BasicDoubleObject.java @@ -38,284 +38,7 @@ import static java.util.Calendar.*; - - - - public class BasicDoubleObject extends Basic { - - private static void test(String fs, String exp, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), Locale.US); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(Locale l, String fs, String exp, Object ... args) - { - Formatter f = new Formatter(new StringBuilder(), l); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), l); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(String fs, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, "fail", f.toString()); - } - - private static void test(String fs) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, "fail"); - ck(fs, "fail", f.toString()); - } - - private static void testSysOut(String fs, String exp, Object ... args) { - FileOutputStream fos = null; - FileInputStream fis = null; - try { - PrintStream saveOut = System.out; - fos = new FileOutputStream("testSysOut"); - System.setOut(new PrintStream(fos)); - System.out.format(Locale.US, fs, args); - fos.close(); - - fis = new FileInputStream("testSysOut"); - byte [] ba = new byte[exp.length()]; - int len = fis.read(ba); - String got = new String(ba); - if (len != ba.length) - fail(fs, exp, got); - ck(fs, exp, got); - - System.setOut(saveOut); - } catch (FileNotFoundException ex) { - fail(fs, ex.getClass()); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } finally { - try { - if (fos != null) - fos.close(); - if (fis != null) - fis.close(); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } - } - } - - private static void tryCatch(String fs, Class ex) { - boolean caught = false; - try { - test(fs); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - private static void tryCatch(String fs, Class ex, Object ... args) { - boolean caught = false; - try { - test(fs, args); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - private static Double create(double v) { @@ -334,7 +57,6 @@ private static Double recip(Double v) { return 1.0 / v.doubleValue(); } - public static void test() { TimeZone.setDefault(TimeZone.getTimeZone("GMT-0800")); @@ -522,1152 +244,271 @@ public static void test() { + //--------------------------------------------------------------------- + // %s - Double + //--------------------------------------------------------------------- + Double one = 1.0d; + Double ten = 10.0d; + Double pi = (double) Math.PI; + test("%s", "3.141592653589793", pi); + //--------------------------------------------------------------------- + // flag/conversion errors + //--------------------------------------------------------------------- + tryCatch("%d", IllegalFormatConversionException.class, one); + tryCatch("%,.4e", FormatFlagsConversionMismatchException.class, one); + //--------------------------------------------------------------------- + // %e + // + // Floating-point conversions applicable to float, double, and + // BigDecimal. + //--------------------------------------------------------------------- + test("%e", "null", (Object)null); + //--------------------------------------------------------------------- + // %e - float and double + //--------------------------------------------------------------------- + // double PI = 3.141 592 653 589 793 238 46; + test("%e", "3.141593e+00", pi); + test("%.0e", "1e+01", ten); + test("%#.0e", "1.e+01", ten); + test("%E", "3.141593E+00", pi); + test("%10.3e", " 3.142e+00", pi); + test("%10.3e", "-3.142e+00", negate(pi)); + test("%010.3e", "03.142e+00", pi); + test("%010.3e", "-3.142e+00", negate(pi)); + test("%-12.3e", "3.142e+00 ", pi); + test("%-12.3e", "-3.142e+00 ", negate(pi)); + test("%.3e", "3.142e+00", pi); + test("%.3e", "-3.142e+00", negate(pi)); + test("%.3e", "3.142e+06", mult(pi, 1000000.0)); + test("%.3e", "-3.142e+06", mult(pi, -1000000.0)); + test(Locale.FRANCE, "%e", "3,141593e+00", pi); + // double PI^300 + // = 13962455701329742638131355433930076081862072808 ... e+149 + test("%10.3e", " 1.000e+00", one); + test("%+.3e", "+3.142e+00", pi); + test("%+.3e", "-3.142e+00", negate(pi)); + test("% .3e", " 3.142e+00", pi); + test("% .3e", "-3.142e+00", negate(pi)); + test("%#.0e", "3.e+00", create(3.0)); + test("%#.0e", "-3.e+00", create(-3.0)); + test("%.0e", "3e+00", create(3.0)); + test("%.0e", "-3e+00", create(-3.0)); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %s - Double - //--------------------------------------------------------------------- - Double one = 1.0d; - Double ten = 10.0d; - Double pi = (double) Math.PI; - - test("%s", "3.141592653589793", pi); - - - //--------------------------------------------------------------------- - // flag/conversion errors - //--------------------------------------------------------------------- - tryCatch("%d", IllegalFormatConversionException.class, one); - tryCatch("%,.4e", FormatFlagsConversionMismatchException.class, one); - - //--------------------------------------------------------------------- - // %e - // - // Floating-point conversions applicable to float, double, and - // BigDecimal. - //--------------------------------------------------------------------- - test("%e", "null", (Object)null); - - //--------------------------------------------------------------------- - // %e - float and double - //--------------------------------------------------------------------- - // double PI = 3.141 592 653 589 793 238 46; - test("%e", "3.141593e+00", pi); - test("%.0e", "1e+01", ten); - test("%#.0e", "1.e+01", ten); - test("%E", "3.141593E+00", pi); - test("%10.3e", " 3.142e+00", pi); - test("%10.3e", "-3.142e+00", negate(pi)); - test("%010.3e", "03.142e+00", pi); - test("%010.3e", "-3.142e+00", negate(pi)); - test("%-12.3e", "3.142e+00 ", pi); - test("%-12.3e", "-3.142e+00 ", negate(pi)); - test("%.3e", "3.142e+00", pi); - test("%.3e", "-3.142e+00", negate(pi)); - test("%.3e", "3.142e+06", mult(pi, 1000000.0)); - test("%.3e", "-3.142e+06", mult(pi, -1000000.0)); - - test(Locale.FRANCE, "%e", "3,141593e+00", pi); - - // double PI^300 - // = 13962455701329742638131355433930076081862072808 ... e+149 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - test("%10.3e", " 1.000e+00", one); - test("%+.3e", "+3.142e+00", pi); - test("%+.3e", "-3.142e+00", negate(pi)); - test("% .3e", " 3.142e+00", pi); - test("% .3e", "-3.142e+00", negate(pi)); - test("%#.0e", "3.e+00", create(3.0)); - test("%#.0e", "-3.e+00", create(-3.0)); - test("%.0e", "3e+00", create(3.0)); - test("%.0e", "-3e+00", create(-3.0)); - - test("%(.4e", "3.1416e+06", mult(pi, 1000000.0)); - test("%(.4e", "(3.1416e+06)", mult(pi, -1000000.0)); - - //--------------------------------------------------------------------- - // %e - boundary problems - //--------------------------------------------------------------------- - test("%3.0e", "1e-06", 0.000001); - test("%3.0e", "1e-05", 0.00001); - test("%3.0e", "1e-04", 0.0001); - test("%3.0e", "1e-03", 0.001); - test("%3.0e", "1e-02", 0.01); - test("%3.0e", "1e-01", 0.1); - test("%3.0e", "9e-01", 0.9); - test("%3.1e", "9.0e-01", 0.9); - test("%3.0e", "1e+00", 1.00); - test("%3.0e", "1e+01", 10.00); - test("%3.0e", "1e+02", 99.19); - test("%3.1e", "9.9e+01", 99.19); - test("%3.0e", "1e+02", 99.99); - test("%3.0e", "1e+02", 100.00); - test("%#3.0e", "1.e+03", 1000.00); - test("%3.0e", "1e+04", 10000.00); - test("%3.0e", "1e+05", 100000.00); - test("%3.0e", "1e+06", 1000000.00); - test("%3.0e", "1e+07", 10000000.00); - test("%3.0e", "1e+08", 100000000.00); - - //--------------------------------------------------------------------- - // %f - // - // Floating-point conversions applicable to float, double, and - // BigDecimal. - //--------------------------------------------------------------------- - test("%f", "null", (Object)null); - test("%f", "3.141593", pi); - test(Locale.FRANCE, "%f", "3,141593", pi); - test("%10.3f", " 3.142", pi); - test("%10.3f", " -3.142", negate(pi)); - test("%010.3f", "000003.142", pi); - test("%010.3f", "-00003.142", negate(pi)); - test("%-10.3f", "3.142 ", pi); - test("%-10.3f", "-3.142 ", negate(pi)); - test("%.3f", "3.142", pi); - test("%.3f", "-3.142", negate(pi)); - test("%+.3f", "+3.142", pi); - test("%+.3f", "-3.142", negate(pi)); - test("% .3f", " 3.142", pi); - test("% .3f", "-3.142", negate(pi)); - test("%#.0f", "3.", create(3.0)); - test("%#.0f", "-3.", create(-3.0)); - test("%.0f", "3", create(3.0)); - test("%.0f", "-3", create(-3.0)); - test("%.3f", "10.000", ten); - test("%.3f", "1.000", one); - test("%10.3f", " 1.000", one); - - //--------------------------------------------------------------------- - // %f - boundary problems - //--------------------------------------------------------------------- - test("%3.0f", " 0", 0.000001); - test("%3.0f", " 0", 0.00001); - test("%3.0f", " 0", 0.0001); - test("%3.0f", " 0", 0.001); - test("%3.0f", " 0", 0.01); - test("%3.0f", " 0", 0.1); - test("%3.0f", " 1", 0.9); - test("%3.1f", "0.9", 0.9); - test("%3.0f", " 1", 1.00); - test("%3.0f", " 10", 10.00); - test("%3.0f", " 99", 99.19); - test("%3.1f", "99.2", 99.19); - test("%3.0f", "100", 99.99); - test("%3.0f", "100", 100.00); - test("%#3.0f", "1000.", 1000.00); - test("%3.0f", "10000", 10000.00); - test("%3.0f", "100000", 100000.00); - test("%3.0f", "1000000", 1000000.00); - test("%3.0f", "10000000", 10000000.00); - test("%3.0f", "100000000", 100000000.00); - test("%10.0f", " 1000000", 1000000.00); - test("%,10.0f", " 1,000,000", 1000000.00); - test("%,10.1f", "1,000,000.0", 1000000.00); - test("%,3.0f", "1,000,000", 1000000.00); - test("%,3.0f", "10,000,000", 10000000.00); - test("%,3.0f", "100,000,000", 100000000.00); - test("%,3.0f", "10,000,000", 10000000.00); - test("%,3.0f", "100,000,000", 100000000.00); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %f - float, double, Double, BigDecimal - //--------------------------------------------------------------------- - test("%.3f", "3141592.654", mult(pi, 1000000.0)); - test("%.3f", "-3141592.654", mult(pi, -1000000.0)); - test("%,.4f", "3,141,592.6536", mult(pi, 1000000.0)); - test(Locale.FRANCE, "%,.4f", "3\u202f141\u202f592,6536", mult(pi, 1000000.0)); - test("%,.4f", "-3,141,592.6536", mult(pi, -1000000.0)); - test("%(.4f", "3141592.6536", mult(pi, 1000000.0)); - test("%(.4f", "(3141592.6536)", mult(pi, -1000000.0)); - test("%(,.4f", "3,141,592.6536", mult(pi, 1000000.0)); - test("%(,.4f", "(3,141,592.6536)", mult(pi, -1000000.0)); - - - - - //--------------------------------------------------------------------- - // %g - // - // Floating-point conversions applicable to float, double, and - // BigDecimal. - //--------------------------------------------------------------------- - test("%g", "null", (Object)null); - test("%g", "3.14159", pi); - test(Locale.FRANCE, "%g", "3,14159", pi); - test("%.0g", "1e+01", ten); - test("%G", "3.14159", pi); - test("%10.3g", " 3.14", pi); - test("%10.3g", " -3.14", negate(pi)); - test("%010.3g", "0000003.14", pi); - test("%010.3g", "-000003.14", negate(pi)); - test("%-12.3g", "3.14 ", pi); - test("%-12.3g", "-3.14 ", negate(pi)); - test("%.3g", "3.14", pi); - test("%.3g", "-3.14", negate(pi)); - test("%.3g", "3.14e+08", mult(pi, 100000000.0)); - test("%.3g", "-3.14e+08", mult(pi, -100000000.0)); - - test("%.3g", "1.00e-05", recip(create(100000.0))); - test("%.3g", "-1.00e-05", recip(create(-100000.0))); - test("%.0g", "-1e-05", recip(create(-100000.0))); - test("%.0g", "1e+05", create(100000.0)); - test("%.3G", "1.00E-05", recip(create(100000.0))); - test("%.3G", "-1.00E-05", recip(create(-100000.0))); - - test("%.1g", "-0", -0.0); - test("%3.0g", " -0", -0.0); - test("%.1g", "0", 0.0); - test("%3.0g", " 0", 0.0); - test("%.1g", "0", +0.0); - test("%3.0g", " 0", +0.0); - - test("%3.0g", "1e-06", 0.000001); - test("%3.0g", "1e-05", 0.00001); - test("%3.0g", "1e-05", 0.0000099); - test("%3.1g", "1e-05", 0.0000099); - test("%3.2g", "9.9e-06", 0.0000099); - test("%3.0g", "0.0001", 0.0001); - test("%3.0g", "9e-05", 0.00009); - test("%3.0g", "0.0001", 0.000099); - test("%3.1g", "0.0001", 0.000099); - test("%3.2g", "9.9e-05", 0.000099); - test("%3.0g", "0.001", 0.001); - test("%3.0g", "0.001", 0.00099); - test("%3.1g", "0.001", 0.00099); - test("%3.2g", "0.00099", 0.00099); - test("%3.3g", "0.00100", 0.001); - test("%3.4g", "0.001000", 0.001); - test("%3.0g", "0.01", 0.01); - test("%3.0g", "0.1", 0.1); - test("%3.0g", "0.9", 0.9); - test("%3.1g", "0.9", 0.9); - test("%3.0g", " 1", 1.00); - test("%3.2g", " 10", 10.00); - test("%3.0g", "1e+01", 10.00); - test("%3.0g", "1e+02", 99.19); - test("%3.1g", "1e+02", 99.19); - test("%3.2g", " 99", 99.19); - test("%3.0g", "1e+02", 99.9); - test("%3.1g", "1e+02", 99.9); - test("%3.2g", "1.0e+02", 99.9); - test("%3.0g", "1e+02", 99.99); - test("%3.0g", "1e+02", 100.00); - test("%3.0g", "1e+03", 999.9); - test("%3.1g", "1e+03", 999.9); - test("%3.2g", "1.0e+03", 999.9); - test("%3.3g", "1.00e+03", 999.9); - test("%3.4g", "999.9", 999.9); - test("%3.4g", "1000", 999.99); - test("%3.0g", "1e+03", 1000.00); - test("%3.0g", "1e+04", 10000.00); - test("%3.0g", "1e+05", 100000.00); - test("%3.0g", "1e+06", 1000000.00); - test("%3.0g", "1e+07", 10000000.00); - test("%3.9g", "100000000", 100000000.00); - test("%3.10g", "100000000.0", 100000000.00); - - tryCatch("%#3.0g", FormatFlagsConversionMismatchException.class, 1000.00); - - // double PI^300 - // = 13962455701329742638131355433930076081862072808 ... e+149 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - test("%.3g", "10.0", ten); - test("%.3g", "1.00", one); - test("%10.3g", " 1.00", one); - test("%+10.3g", " +3.14", pi); - test("%+10.3g", " -3.14", negate(pi)); - test("% .3g", " 3.14", pi); - test("% .3g", "-3.14", negate(pi)); - test("%.0g", "3", create(3.0)); - test("%.0g", "-3", create(-3.0)); - - test("%(.4g", "3.142e+08", mult(pi, 100000000.0)); - test("%(.4g", "(3.142e+08)", mult(pi, -100000000.0)); - - - - - - - - test("%,.11g", "3,141,592.6536", mult(pi, 1000000.0)); - test("%(,.11g", "(3,141,592.6536)", mult(pi, -1000000.0)); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + test("%(.4e", "3.1416e+06", mult(pi, 1000000.0)); + test("%(.4e", "(3.1416e+06)", mult(pi, -1000000.0)); //--------------------------------------------------------------------- - // %f, %e, %g, %a - Boundaries + // %e - boundary problems //--------------------------------------------------------------------- + test("%3.0e", "1e-06", 0.000001); + test("%3.0e", "1e-05", 0.00001); + test("%3.0e", "1e-04", 0.0001); + test("%3.0e", "1e-03", 0.001); + test("%3.0e", "1e-02", 0.01); + test("%3.0e", "1e-01", 0.1); + test("%3.0e", "9e-01", 0.9); + test("%3.1e", "9.0e-01", 0.9); + test("%3.0e", "1e+00", 1.00); + test("%3.0e", "1e+01", 10.00); + test("%3.0e", "1e+02", 99.19); + test("%3.1e", "9.9e+01", 99.19); + test("%3.0e", "1e+02", 99.99); + test("%3.0e", "1e+02", 100.00); + test("%#3.0e", "1.e+03", 1000.00); + test("%3.0e", "1e+04", 10000.00); + test("%3.0e", "1e+05", 100000.00); + test("%3.0e", "1e+06", 1000000.00); + test("%3.0e", "1e+07", 10000000.00); + test("%3.0e", "1e+08", 100000000.00); + //--------------------------------------------------------------------- + // %f + // + // Floating-point conversions applicable to float, double, and + // BigDecimal. + //--------------------------------------------------------------------- + test("%f", "null", (Object)null); + test("%f", "3.141593", pi); + test(Locale.FRANCE, "%f", "3,141593", pi); + test("%10.3f", " 3.142", pi); + test("%10.3f", " -3.142", negate(pi)); + test("%010.3f", "000003.142", pi); + test("%010.3f", "-00003.142", negate(pi)); + test("%-10.3f", "3.142 ", pi); + test("%-10.3f", "-3.142 ", negate(pi)); + test("%.3f", "3.142", pi); + test("%.3f", "-3.142", negate(pi)); + test("%+.3f", "+3.142", pi); + test("%+.3f", "-3.142", negate(pi)); + test("% .3f", " 3.142", pi); + test("% .3f", "-3.142", negate(pi)); + test("%#.0f", "3.", create(3.0)); + test("%#.0f", "-3.", create(-3.0)); + test("%.0f", "3", create(3.0)); + test("%.0f", "-3", create(-3.0)); + test("%.3f", "10.000", ten); + test("%.3f", "1.000", one); + test("%10.3f", " 1.000", one); + //--------------------------------------------------------------------- + // %f - boundary problems + //--------------------------------------------------------------------- + test("%3.0f", " 0", 0.000001); + test("%3.0f", " 0", 0.00001); + test("%3.0f", " 0", 0.0001); + test("%3.0f", " 0", 0.001); + test("%3.0f", " 0", 0.01); + test("%3.0f", " 0", 0.1); + test("%3.0f", " 1", 0.9); + test("%3.1f", "0.9", 0.9); + test("%3.0f", " 1", 1.00); + test("%3.0f", " 10", 10.00); + test("%3.0f", " 99", 99.19); + test("%3.1f", "99.2", 99.19); + test("%3.0f", "100", 99.99); + test("%3.0f", "100", 100.00); + test("%#3.0f", "1000.", 1000.00); + test("%3.0f", "10000", 10000.00); + test("%3.0f", "100000", 100000.00); + test("%3.0f", "1000000", 1000000.00); + test("%3.0f", "10000000", 10000000.00); + test("%3.0f", "100000000", 100000000.00); + test("%10.0f", " 1000000", 1000000.00); + test("%,10.0f", " 1,000,000", 1000000.00); + test("%,10.1f", "1,000,000.0", 1000000.00); + test("%,3.0f", "1,000,000", 1000000.00); + test("%,3.0f", "10,000,000", 10000000.00); + test("%,3.0f", "100,000,000", 100000000.00); + test("%,3.0f", "10,000,000", 10000000.00); + test("%,3.0f", "100,000,000", 100000000.00); + //--------------------------------------------------------------------- + // %f - float, double, Double, BigDecimal + //--------------------------------------------------------------------- + test("%.3f", "3141592.654", mult(pi, 1000000.0)); + test("%.3f", "-3141592.654", mult(pi, -1000000.0)); + test("%,.4f", "3,141,592.6536", mult(pi, 1000000.0)); + test(Locale.FRANCE, "%,.4f", "3\u202f141\u202f592,6536", mult(pi, 1000000.0)); + test("%,.4f", "-3,141,592.6536", mult(pi, -1000000.0)); + test("%(.4f", "3141592.6536", mult(pi, 1000000.0)); + test("%(.4f", "(3141592.6536)", mult(pi, -1000000.0)); + test("%(,.4f", "3,141,592.6536", mult(pi, 1000000.0)); + test("%(,.4f", "(3,141,592.6536)", mult(pi, -1000000.0)); + //--------------------------------------------------------------------- + // %g + // + // Floating-point conversions applicable to float, double, and + // BigDecimal. + //--------------------------------------------------------------------- + test("%g", "null", (Object)null); + test("%g", "3.14159", pi); + test(Locale.FRANCE, "%g", "3,14159", pi); + test("%.0g", "1e+01", ten); + test("%G", "3.14159", pi); + test("%10.3g", " 3.14", pi); + test("%10.3g", " -3.14", negate(pi)); + test("%010.3g", "0000003.14", pi); + test("%010.3g", "-000003.14", negate(pi)); + test("%-12.3g", "3.14 ", pi); + test("%-12.3g", "-3.14 ", negate(pi)); + test("%.3g", "3.14", pi); + test("%.3g", "-3.14", negate(pi)); + test("%.3g", "3.14e+08", mult(pi, 100000000.0)); + test("%.3g", "-3.14e+08", mult(pi, -100000000.0)); + test("%.3g", "1.00e-05", recip(create(100000.0))); + test("%.3g", "-1.00e-05", recip(create(-100000.0))); + test("%.0g", "-1e-05", recip(create(-100000.0))); + test("%.0g", "1e+05", create(100000.0)); + test("%.3G", "1.00E-05", recip(create(100000.0))); + test("%.3G", "-1.00E-05", recip(create(-100000.0))); + test("%.1g", "-0", -0.0); + test("%3.0g", " -0", -0.0); + test("%.1g", "0", 0.0); + test("%3.0g", " 0", 0.0); + test("%.1g", "0", +0.0); + test("%3.0g", " 0", +0.0); + test("%3.0g", "1e-06", 0.000001); + test("%3.0g", "1e-05", 0.00001); + test("%3.0g", "1e-05", 0.0000099); + test("%3.1g", "1e-05", 0.0000099); + test("%3.2g", "9.9e-06", 0.0000099); + test("%3.0g", "0.0001", 0.0001); + test("%3.0g", "9e-05", 0.00009); + test("%3.0g", "0.0001", 0.000099); + test("%3.1g", "0.0001", 0.000099); + test("%3.2g", "9.9e-05", 0.000099); + test("%3.0g", "0.001", 0.001); + test("%3.0g", "0.001", 0.00099); + test("%3.1g", "0.001", 0.00099); + test("%3.2g", "0.00099", 0.00099); + test("%3.3g", "0.00100", 0.001); + test("%3.4g", "0.001000", 0.001); + test("%3.0g", "0.01", 0.01); + test("%3.0g", "0.1", 0.1); + test("%3.0g", "0.9", 0.9); + test("%3.1g", "0.9", 0.9); + test("%3.0g", " 1", 1.00); + test("%3.2g", " 10", 10.00); + test("%3.0g", "1e+01", 10.00); + test("%3.0g", "1e+02", 99.19); + test("%3.1g", "1e+02", 99.19); + test("%3.2g", " 99", 99.19); + test("%3.0g", "1e+02", 99.9); + test("%3.1g", "1e+02", 99.9); + test("%3.2g", "1.0e+02", 99.9); + test("%3.0g", "1e+02", 99.99); + test("%3.0g", "1e+02", 100.00); + test("%3.0g", "1e+03", 999.9); + test("%3.1g", "1e+03", 999.9); + test("%3.2g", "1.0e+03", 999.9); + test("%3.3g", "1.00e+03", 999.9); + test("%3.4g", "999.9", 999.9); + test("%3.4g", "1000", 999.99); + test("%3.0g", "1e+03", 1000.00); + test("%3.0g", "1e+04", 10000.00); + test("%3.0g", "1e+05", 100000.00); + test("%3.0g", "1e+06", 1000000.00); + test("%3.0g", "1e+07", 10000000.00); + test("%3.9g", "100000000", 100000000.00); + test("%3.10g", "100000000.0", 100000000.00); + tryCatch("%#3.0g", FormatFlagsConversionMismatchException.class, 1000.00); + // double PI^300 + // = 13962455701329742638131355433930076081862072808 ... e+149 + test("%.3g", "10.0", ten); + test("%.3g", "1.00", one); + test("%10.3g", " 1.00", one); + test("%+10.3g", " +3.14", pi); + test("%+10.3g", " -3.14", negate(pi)); + test("% .3g", " 3.14", pi); + test("% .3g", "-3.14", negate(pi)); + test("%.0g", "3", create(3.0)); + test("%.0g", "-3", create(-3.0)); + test("%(.4g", "3.142e+08", mult(pi, 100000000.0)); + test("%(.4g", "(3.142e+08)", mult(pi, -100000000.0)); + test("%,.11g", "3,141,592.6536", mult(pi, 1000000.0)); + test("%(,.11g", "(3,141,592.6536)", mult(pi, -1000000.0)); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + //--------------------------------------------------------------------- + // %f, %e, %g, %a - Boundaries + //--------------------------------------------------------------------- @@ -1691,99 +532,6 @@ public static void test() { tryCatch("%-tB", MissingFormatWidthException.class); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %n //--------------------------------------------------------------------- diff --git a/test/jdk/java/util/Formatter/BasicFloat.java b/test/jdk/java/util/Formatter/BasicFloat.java index 3f5aa78f226c8..ba61b3f52d04b 100644 --- a/test/jdk/java/util/Formatter/BasicFloat.java +++ b/test/jdk/java/util/Formatter/BasicFloat.java @@ -38,232 +38,7 @@ import static java.util.Calendar.*; - - - - public class BasicFloat extends Basic { - - private static void test(String fs, String exp, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), Locale.US); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(Locale l, String fs, String exp, Object ... args) - { - Formatter f = new Formatter(new StringBuilder(), l); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), l); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(String fs, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, "fail", f.toString()); - } - - private static void test(String fs) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, "fail"); - ck(fs, "fail", f.toString()); - } - - private static void testSysOut(String fs, String exp, Object ... args) { - FileOutputStream fos = null; - FileInputStream fis = null; - try { - PrintStream saveOut = System.out; - fos = new FileOutputStream("testSysOut"); - System.setOut(new PrintStream(fos)); - System.out.format(Locale.US, fs, args); - fos.close(); - - fis = new FileInputStream("testSysOut"); - byte [] ba = new byte[exp.length()]; - int len = fis.read(ba); - String got = new String(ba); - if (len != ba.length) - fail(fs, exp, got); - ck(fs, exp, got); - - System.setOut(saveOut); - } catch (FileNotFoundException ex) { - fail(fs, ex.getClass()); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } finally { - try { - if (fos != null) - fos.close(); - if (fis != null) - fis.close(); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } - } - } - - private static void tryCatch(String fs, Class ex) { - boolean caught = false; - try { - test(fs); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - private static void tryCatch(String fs, Class ex, Object ... args) { - boolean caught = false; - try { - test(fs, args); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - private static float create(double v) { @@ -282,59 +57,6 @@ private static float recip(float v) { return 1.0f / v; } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public static void test() { TimeZone.setDefault(TimeZone.getTimeZone("GMT-0800")); @@ -522,487 +244,65 @@ public static void test() { + //--------------------------------------------------------------------- + // %s - float + //--------------------------------------------------------------------- + float one = 1.0f; + float ten = 10.0f; + float pi = (float) Math.PI; + test("%s", "3.1415927", pi); + //--------------------------------------------------------------------- + // flag/conversion errors + //--------------------------------------------------------------------- + tryCatch("%d", IllegalFormatConversionException.class, one); + tryCatch("%,.4e", FormatFlagsConversionMismatchException.class, one); + //--------------------------------------------------------------------- + // %e + // + // Floating-point conversions applicable to float, double, and + // BigDecimal. + //--------------------------------------------------------------------- + test("%e", "null", (Object)null); + //--------------------------------------------------------------------- + // %e - float and double + //--------------------------------------------------------------------- + // double PI = 3.141 592 653 589 793 238 46; + test("%e", "3.141593e+00", pi); + test("%.0e", "1e+01", ten); + test("%#.0e", "1.e+01", ten); + test("%E", "3.141593E+00", pi); + test("%10.3e", " 3.142e+00", pi); + test("%10.3e", "-3.142e+00", negate(pi)); + test("%010.3e", "03.142e+00", pi); + test("%010.3e", "-3.142e+00", negate(pi)); + test("%-12.3e", "3.142e+00 ", pi); + test("%-12.3e", "-3.142e+00 ", negate(pi)); + test("%.3e", "3.142e+00", pi); + test("%.3e", "-3.142e+00", negate(pi)); + test("%.3e", "3.142e+06", mult(pi, 1000000.0)); + test("%.3e", "-3.142e+06", mult(pi, -1000000.0)); + test(Locale.FRANCE, "%e", "3,141593e+00", pi); + // double PI^300 + // = 13962455701329742638131355433930076081862072808 ... e+149 + test("%10.3e", " 1.000e+00", one); + test("%+.3e", "+3.142e+00", pi); + test("%+.3e", "-3.142e+00", negate(pi)); + test("% .3e", " 3.142e+00", pi); + test("% .3e", "-3.142e+00", negate(pi)); + test("%#.0e", "3.e+00", create(3.0)); + test("%#.0e", "-3.e+00", create(-3.0)); + test("%.0e", "3e+00", create(3.0)); + test("%.0e", "-3e+00", create(-3.0)); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %s - float - //--------------------------------------------------------------------- - float one = 1.0f; - float ten = 10.0f; - float pi = (float) Math.PI; - - test("%s", "3.1415927", pi); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // flag/conversion errors - //--------------------------------------------------------------------- - tryCatch("%d", IllegalFormatConversionException.class, one); - tryCatch("%,.4e", FormatFlagsConversionMismatchException.class, one); - - //--------------------------------------------------------------------- - // %e - // - // Floating-point conversions applicable to float, double, and - // BigDecimal. - //--------------------------------------------------------------------- - test("%e", "null", (Object)null); - - //--------------------------------------------------------------------- - // %e - float and double - //--------------------------------------------------------------------- - // double PI = 3.141 592 653 589 793 238 46; - test("%e", "3.141593e+00", pi); - test("%.0e", "1e+01", ten); - test("%#.0e", "1.e+01", ten); - test("%E", "3.141593E+00", pi); - test("%10.3e", " 3.142e+00", pi); - test("%10.3e", "-3.142e+00", negate(pi)); - test("%010.3e", "03.142e+00", pi); - test("%010.3e", "-3.142e+00", negate(pi)); - test("%-12.3e", "3.142e+00 ", pi); - test("%-12.3e", "-3.142e+00 ", negate(pi)); - test("%.3e", "3.142e+00", pi); - test("%.3e", "-3.142e+00", negate(pi)); - test("%.3e", "3.142e+06", mult(pi, 1000000.0)); - test("%.3e", "-3.142e+06", mult(pi, -1000000.0)); - - test(Locale.FRANCE, "%e", "3,141593e+00", pi); - - // double PI^300 - // = 13962455701329742638131355433930076081862072808 ... e+149 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - test("%10.3e", " 1.000e+00", one); - test("%+.3e", "+3.142e+00", pi); - test("%+.3e", "-3.142e+00", negate(pi)); - test("% .3e", " 3.142e+00", pi); - test("% .3e", "-3.142e+00", negate(pi)); - test("%#.0e", "3.e+00", create(3.0)); - test("%#.0e", "-3.e+00", create(-3.0)); - test("%.0e", "3e+00", create(3.0)); - test("%.0e", "-3e+00", create(-3.0)); - - test("%(.4e", "3.1416e+06", mult(pi, 1000000.0)); - test("%(.4e", "(3.1416e+06)", mult(pi, -1000000.0)); + test("%(.4e", "3.1416e+06", mult(pi, 1000000.0)); + test("%(.4e", "(3.1416e+06)", mult(pi, -1000000.0)); //--------------------------------------------------------------------- // %e - boundary problems @@ -1089,85 +389,6 @@ public static void test() { test("%,3.0f", "10,000,000", 10000000.00); test("%,3.0f", "100,000,000", 100000000.00); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %f - float //--------------------------------------------------------------------- @@ -1185,22 +406,6 @@ public static void test() { - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %g // @@ -1280,48 +485,12 @@ public static void test() { test("%3.0g", "1e+06", 1000000.00); test("%3.0g", "1e+07", 10000000.00); test("%3.9g", "100000000", 100000000.00); - test("%3.10g", "100000000.0", 100000000.00); - - tryCatch("%#3.0g", FormatFlagsConversionMismatchException.class, 1000.00); - - // double PI^300 - // = 13962455701329742638131355433930076081862072808 ... e+149 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + test("%3.10g", "100000000.0", 100000000.00); + tryCatch("%#3.0g", FormatFlagsConversionMismatchException.class, 1000.00); + // double PI^300 + // = 13962455701329742638131355433930076081862072808 ... e+149 test("%.3g", "10.0", ten); test("%.3g", "1.00", one); @@ -1336,107 +505,14 @@ public static void test() { test("%(.4g", "3.142e+08", mult(pi, 100000000.0)); test("%(.4g", "(3.142e+08)", mult(pi, -100000000.0)); - // Float can not accurately store 1e6 * PI. test("%,.6g", "3,141.59", mult(pi, 1000.0)); test("%(,.6g", "(3,141.59)", mult(pi, -1000.0)); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %f, %e, %g, %a - Boundaries //--------------------------------------------------------------------- - //--------------------------------------------------------------------- // %f, %e, %g, %a - NaN //--------------------------------------------------------------------- @@ -1605,72 +681,6 @@ public static void test() { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %t // @@ -1691,99 +701,6 @@ public static void test() { tryCatch("%-tB", MissingFormatWidthException.class); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %n //--------------------------------------------------------------------- diff --git a/test/jdk/java/util/Formatter/BasicFloatObject.java b/test/jdk/java/util/Formatter/BasicFloatObject.java index ac08d0eab4fd7..9bfe1dcc036c8 100644 --- a/test/jdk/java/util/Formatter/BasicFloatObject.java +++ b/test/jdk/java/util/Formatter/BasicFloatObject.java @@ -38,249 +38,7 @@ import static java.util.Calendar.*; - - - - public class BasicFloatObject extends Basic { - - private static void test(String fs, String exp, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), Locale.US); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(Locale l, String fs, String exp, Object ... args) - { - Formatter f = new Formatter(new StringBuilder(), l); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), l); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(String fs, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, "fail", f.toString()); - } - - private static void test(String fs) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, "fail"); - ck(fs, "fail", f.toString()); - } - - private static void testSysOut(String fs, String exp, Object ... args) { - FileOutputStream fos = null; - FileInputStream fis = null; - try { - PrintStream saveOut = System.out; - fos = new FileOutputStream("testSysOut"); - System.setOut(new PrintStream(fos)); - System.out.format(Locale.US, fs, args); - fos.close(); - - fis = new FileInputStream("testSysOut"); - byte [] ba = new byte[exp.length()]; - int len = fis.read(ba); - String got = new String(ba); - if (len != ba.length) - fail(fs, exp, got); - ck(fs, exp, got); - - System.setOut(saveOut); - } catch (FileNotFoundException ex) { - fail(fs, ex.getClass()); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } finally { - try { - if (fos != null) - fos.close(); - if (fis != null) - fis.close(); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } - } - } - - private static void tryCatch(String fs, Class ex) { - boolean caught = false; - try { - test(fs); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - private static void tryCatch(String fs, Class ex, Object ... args) { - boolean caught = false; - try { - test(fs, args); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - private static Float create(double v) { @@ -299,42 +57,6 @@ private static Float recip(Float v) { return 1.0f / v.floatValue(); } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public static void test() { TimeZone.setDefault(TimeZone.getTimeZone("GMT-0800")); @@ -522,1152 +244,257 @@ public static void test() { + //--------------------------------------------------------------------- + // %s - Float + //--------------------------------------------------------------------- + Float one = 1.0f; + Float ten = 10.0f; + Float pi = (float) Math.PI; + test("%s", "3.1415927", pi); + //--------------------------------------------------------------------- + // flag/conversion errors + //--------------------------------------------------------------------- + tryCatch("%d", IllegalFormatConversionException.class, one); + tryCatch("%,.4e", FormatFlagsConversionMismatchException.class, one); + //--------------------------------------------------------------------- + // %e + // + // Floating-point conversions applicable to float, double, and + // BigDecimal. + //--------------------------------------------------------------------- + test("%e", "null", (Object)null); + //--------------------------------------------------------------------- + // %e - float and double + //--------------------------------------------------------------------- + // double PI = 3.141 592 653 589 793 238 46; + test("%e", "3.141593e+00", pi); + test("%.0e", "1e+01", ten); + test("%#.0e", "1.e+01", ten); + test("%E", "3.141593E+00", pi); + test("%10.3e", " 3.142e+00", pi); + test("%10.3e", "-3.142e+00", negate(pi)); + test("%010.3e", "03.142e+00", pi); + test("%010.3e", "-3.142e+00", negate(pi)); + test("%-12.3e", "3.142e+00 ", pi); + test("%-12.3e", "-3.142e+00 ", negate(pi)); + test("%.3e", "3.142e+00", pi); + test("%.3e", "-3.142e+00", negate(pi)); + test("%.3e", "3.142e+06", mult(pi, 1000000.0)); + test("%.3e", "-3.142e+06", mult(pi, -1000000.0)); + test(Locale.FRANCE, "%e", "3,141593e+00", pi); + // double PI^300 + // = 13962455701329742638131355433930076081862072808 ... e+149 + test("%10.3e", " 1.000e+00", one); + test("%+.3e", "+3.142e+00", pi); + test("%+.3e", "-3.142e+00", negate(pi)); + test("% .3e", " 3.142e+00", pi); + test("% .3e", "-3.142e+00", negate(pi)); + test("%#.0e", "3.e+00", create(3.0)); + test("%#.0e", "-3.e+00", create(-3.0)); + test("%.0e", "3e+00", create(3.0)); + test("%.0e", "-3e+00", create(-3.0)); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %s - Float - //--------------------------------------------------------------------- - Float one = 1.0f; - Float ten = 10.0f; - Float pi = (float) Math.PI; - - test("%s", "3.1415927", pi); - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // flag/conversion errors - //--------------------------------------------------------------------- - tryCatch("%d", IllegalFormatConversionException.class, one); - tryCatch("%,.4e", FormatFlagsConversionMismatchException.class, one); - - //--------------------------------------------------------------------- - // %e - // - // Floating-point conversions applicable to float, double, and - // BigDecimal. - //--------------------------------------------------------------------- - test("%e", "null", (Object)null); - - //--------------------------------------------------------------------- - // %e - float and double - //--------------------------------------------------------------------- - // double PI = 3.141 592 653 589 793 238 46; - test("%e", "3.141593e+00", pi); - test("%.0e", "1e+01", ten); - test("%#.0e", "1.e+01", ten); - test("%E", "3.141593E+00", pi); - test("%10.3e", " 3.142e+00", pi); - test("%10.3e", "-3.142e+00", negate(pi)); - test("%010.3e", "03.142e+00", pi); - test("%010.3e", "-3.142e+00", negate(pi)); - test("%-12.3e", "3.142e+00 ", pi); - test("%-12.3e", "-3.142e+00 ", negate(pi)); - test("%.3e", "3.142e+00", pi); - test("%.3e", "-3.142e+00", negate(pi)); - test("%.3e", "3.142e+06", mult(pi, 1000000.0)); - test("%.3e", "-3.142e+06", mult(pi, -1000000.0)); - - test(Locale.FRANCE, "%e", "3,141593e+00", pi); - - // double PI^300 - // = 13962455701329742638131355433930076081862072808 ... e+149 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - test("%10.3e", " 1.000e+00", one); - test("%+.3e", "+3.142e+00", pi); - test("%+.3e", "-3.142e+00", negate(pi)); - test("% .3e", " 3.142e+00", pi); - test("% .3e", "-3.142e+00", negate(pi)); - test("%#.0e", "3.e+00", create(3.0)); - test("%#.0e", "-3.e+00", create(-3.0)); - test("%.0e", "3e+00", create(3.0)); - test("%.0e", "-3e+00", create(-3.0)); - - test("%(.4e", "3.1416e+06", mult(pi, 1000000.0)); - test("%(.4e", "(3.1416e+06)", mult(pi, -1000000.0)); - - //--------------------------------------------------------------------- - // %e - boundary problems - //--------------------------------------------------------------------- - test("%3.0e", "1e-06", 0.000001); - test("%3.0e", "1e-05", 0.00001); - test("%3.0e", "1e-04", 0.0001); - test("%3.0e", "1e-03", 0.001); - test("%3.0e", "1e-02", 0.01); - test("%3.0e", "1e-01", 0.1); - test("%3.0e", "9e-01", 0.9); - test("%3.1e", "9.0e-01", 0.9); - test("%3.0e", "1e+00", 1.00); - test("%3.0e", "1e+01", 10.00); - test("%3.0e", "1e+02", 99.19); - test("%3.1e", "9.9e+01", 99.19); - test("%3.0e", "1e+02", 99.99); - test("%3.0e", "1e+02", 100.00); - test("%#3.0e", "1.e+03", 1000.00); - test("%3.0e", "1e+04", 10000.00); - test("%3.0e", "1e+05", 100000.00); - test("%3.0e", "1e+06", 1000000.00); - test("%3.0e", "1e+07", 10000000.00); - test("%3.0e", "1e+08", 100000000.00); - - //--------------------------------------------------------------------- - // %f - // - // Floating-point conversions applicable to float, double, and - // BigDecimal. - //--------------------------------------------------------------------- - test("%f", "null", (Object)null); - test("%f", "3.141593", pi); - test(Locale.FRANCE, "%f", "3,141593", pi); - test("%10.3f", " 3.142", pi); - test("%10.3f", " -3.142", negate(pi)); - test("%010.3f", "000003.142", pi); - test("%010.3f", "-00003.142", negate(pi)); - test("%-10.3f", "3.142 ", pi); - test("%-10.3f", "-3.142 ", negate(pi)); - test("%.3f", "3.142", pi); - test("%.3f", "-3.142", negate(pi)); - test("%+.3f", "+3.142", pi); - test("%+.3f", "-3.142", negate(pi)); - test("% .3f", " 3.142", pi); - test("% .3f", "-3.142", negate(pi)); - test("%#.0f", "3.", create(3.0)); - test("%#.0f", "-3.", create(-3.0)); - test("%.0f", "3", create(3.0)); - test("%.0f", "-3", create(-3.0)); - test("%.3f", "10.000", ten); - test("%.3f", "1.000", one); - test("%10.3f", " 1.000", one); - - //--------------------------------------------------------------------- - // %f - boundary problems - //--------------------------------------------------------------------- - test("%3.0f", " 0", 0.000001); - test("%3.0f", " 0", 0.00001); - test("%3.0f", " 0", 0.0001); - test("%3.0f", " 0", 0.001); - test("%3.0f", " 0", 0.01); - test("%3.0f", " 0", 0.1); - test("%3.0f", " 1", 0.9); - test("%3.1f", "0.9", 0.9); - test("%3.0f", " 1", 1.00); - test("%3.0f", " 10", 10.00); - test("%3.0f", " 99", 99.19); - test("%3.1f", "99.2", 99.19); - test("%3.0f", "100", 99.99); - test("%3.0f", "100", 100.00); - test("%#3.0f", "1000.", 1000.00); - test("%3.0f", "10000", 10000.00); - test("%3.0f", "100000", 100000.00); - test("%3.0f", "1000000", 1000000.00); - test("%3.0f", "10000000", 10000000.00); - test("%3.0f", "100000000", 100000000.00); - test("%10.0f", " 1000000", 1000000.00); - test("%,10.0f", " 1,000,000", 1000000.00); - test("%,10.1f", "1,000,000.0", 1000000.00); - test("%,3.0f", "1,000,000", 1000000.00); - test("%,3.0f", "10,000,000", 10000000.00); - test("%,3.0f", "100,000,000", 100000000.00); - test("%,3.0f", "10,000,000", 10000000.00); - test("%,3.0f", "100,000,000", 100000000.00); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %g - // - // Floating-point conversions applicable to float, double, and - // BigDecimal. - //--------------------------------------------------------------------- - test("%g", "null", (Object)null); - test("%g", "3.14159", pi); - test(Locale.FRANCE, "%g", "3,14159", pi); - test("%.0g", "1e+01", ten); - test("%G", "3.14159", pi); - test("%10.3g", " 3.14", pi); - test("%10.3g", " -3.14", negate(pi)); - test("%010.3g", "0000003.14", pi); - test("%010.3g", "-000003.14", negate(pi)); - test("%-12.3g", "3.14 ", pi); - test("%-12.3g", "-3.14 ", negate(pi)); - test("%.3g", "3.14", pi); - test("%.3g", "-3.14", negate(pi)); - test("%.3g", "3.14e+08", mult(pi, 100000000.0)); - test("%.3g", "-3.14e+08", mult(pi, -100000000.0)); - - test("%.3g", "1.00e-05", recip(create(100000.0))); - test("%.3g", "-1.00e-05", recip(create(-100000.0))); - test("%.0g", "-1e-05", recip(create(-100000.0))); - test("%.0g", "1e+05", create(100000.0)); - test("%.3G", "1.00E-05", recip(create(100000.0))); - test("%.3G", "-1.00E-05", recip(create(-100000.0))); - - test("%.1g", "-0", -0.0); - test("%3.0g", " -0", -0.0); - test("%.1g", "0", 0.0); - test("%3.0g", " 0", 0.0); - test("%.1g", "0", +0.0); - test("%3.0g", " 0", +0.0); - - test("%3.0g", "1e-06", 0.000001); - test("%3.0g", "1e-05", 0.00001); - test("%3.0g", "1e-05", 0.0000099); - test("%3.1g", "1e-05", 0.0000099); - test("%3.2g", "9.9e-06", 0.0000099); - test("%3.0g", "0.0001", 0.0001); - test("%3.0g", "9e-05", 0.00009); - test("%3.0g", "0.0001", 0.000099); - test("%3.1g", "0.0001", 0.000099); - test("%3.2g", "9.9e-05", 0.000099); - test("%3.0g", "0.001", 0.001); - test("%3.0g", "0.001", 0.00099); - test("%3.1g", "0.001", 0.00099); - test("%3.2g", "0.00099", 0.00099); - test("%3.3g", "0.00100", 0.001); - test("%3.4g", "0.001000", 0.001); - test("%3.0g", "0.01", 0.01); - test("%3.0g", "0.1", 0.1); - test("%3.0g", "0.9", 0.9); - test("%3.1g", "0.9", 0.9); - test("%3.0g", " 1", 1.00); - test("%3.2g", " 10", 10.00); - test("%3.0g", "1e+01", 10.00); - test("%3.0g", "1e+02", 99.19); - test("%3.1g", "1e+02", 99.19); - test("%3.2g", " 99", 99.19); - test("%3.0g", "1e+02", 99.9); - test("%3.1g", "1e+02", 99.9); - test("%3.2g", "1.0e+02", 99.9); - test("%3.0g", "1e+02", 99.99); - test("%3.0g", "1e+02", 100.00); - test("%3.0g", "1e+03", 999.9); - test("%3.1g", "1e+03", 999.9); - test("%3.2g", "1.0e+03", 999.9); - test("%3.3g", "1.00e+03", 999.9); - test("%3.4g", "999.9", 999.9); - test("%3.4g", "1000", 999.99); - test("%3.0g", "1e+03", 1000.00); - test("%3.0g", "1e+04", 10000.00); - test("%3.0g", "1e+05", 100000.00); - test("%3.0g", "1e+06", 1000000.00); - test("%3.0g", "1e+07", 10000000.00); - test("%3.9g", "100000000", 100000000.00); - test("%3.10g", "100000000.0", 100000000.00); - - tryCatch("%#3.0g", FormatFlagsConversionMismatchException.class, 1000.00); - - // double PI^300 - // = 13962455701329742638131355433930076081862072808 ... e+149 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - test("%.3g", "10.0", ten); - test("%.3g", "1.00", one); - test("%10.3g", " 1.00", one); - test("%+10.3g", " +3.14", pi); - test("%+10.3g", " -3.14", negate(pi)); - test("% .3g", " 3.14", pi); - test("% .3g", "-3.14", negate(pi)); - test("%.0g", "3", create(3.0)); - test("%.0g", "-3", create(-3.0)); - - test("%(.4g", "3.142e+08", mult(pi, 100000000.0)); - test("%(.4g", "(3.142e+08)", mult(pi, -100000000.0)); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + test("%(.4e", "3.1416e+06", mult(pi, 1000000.0)); + test("%(.4e", "(3.1416e+06)", mult(pi, -1000000.0)); //--------------------------------------------------------------------- - // %f, %e, %g, %a - Boundaries + // %e - boundary problems //--------------------------------------------------------------------- + test("%3.0e", "1e-06", 0.000001); + test("%3.0e", "1e-05", 0.00001); + test("%3.0e", "1e-04", 0.0001); + test("%3.0e", "1e-03", 0.001); + test("%3.0e", "1e-02", 0.01); + test("%3.0e", "1e-01", 0.1); + test("%3.0e", "9e-01", 0.9); + test("%3.1e", "9.0e-01", 0.9); + test("%3.0e", "1e+00", 1.00); + test("%3.0e", "1e+01", 10.00); + test("%3.0e", "1e+02", 99.19); + test("%3.1e", "9.9e+01", 99.19); + test("%3.0e", "1e+02", 99.99); + test("%3.0e", "1e+02", 100.00); + test("%#3.0e", "1.e+03", 1000.00); + test("%3.0e", "1e+04", 10000.00); + test("%3.0e", "1e+05", 100000.00); + test("%3.0e", "1e+06", 1000000.00); + test("%3.0e", "1e+07", 10000000.00); + test("%3.0e", "1e+08", 100000000.00); + //--------------------------------------------------------------------- + // %f + // + // Floating-point conversions applicable to float, double, and + // BigDecimal. + //--------------------------------------------------------------------- + test("%f", "null", (Object)null); + test("%f", "3.141593", pi); + test(Locale.FRANCE, "%f", "3,141593", pi); + test("%10.3f", " 3.142", pi); + test("%10.3f", " -3.142", negate(pi)); + test("%010.3f", "000003.142", pi); + test("%010.3f", "-00003.142", negate(pi)); + test("%-10.3f", "3.142 ", pi); + test("%-10.3f", "-3.142 ", negate(pi)); + test("%.3f", "3.142", pi); + test("%.3f", "-3.142", negate(pi)); + test("%+.3f", "+3.142", pi); + test("%+.3f", "-3.142", negate(pi)); + test("% .3f", " 3.142", pi); + test("% .3f", "-3.142", negate(pi)); + test("%#.0f", "3.", create(3.0)); + test("%#.0f", "-3.", create(-3.0)); + test("%.0f", "3", create(3.0)); + test("%.0f", "-3", create(-3.0)); + test("%.3f", "10.000", ten); + test("%.3f", "1.000", one); + test("%10.3f", " 1.000", one); + //--------------------------------------------------------------------- + // %f - boundary problems + //--------------------------------------------------------------------- + test("%3.0f", " 0", 0.000001); + test("%3.0f", " 0", 0.00001); + test("%3.0f", " 0", 0.0001); + test("%3.0f", " 0", 0.001); + test("%3.0f", " 0", 0.01); + test("%3.0f", " 0", 0.1); + test("%3.0f", " 1", 0.9); + test("%3.1f", "0.9", 0.9); + test("%3.0f", " 1", 1.00); + test("%3.0f", " 10", 10.00); + test("%3.0f", " 99", 99.19); + test("%3.1f", "99.2", 99.19); + test("%3.0f", "100", 99.99); + test("%3.0f", "100", 100.00); + test("%#3.0f", "1000.", 1000.00); + test("%3.0f", "10000", 10000.00); + test("%3.0f", "100000", 100000.00); + test("%3.0f", "1000000", 1000000.00); + test("%3.0f", "10000000", 10000000.00); + test("%3.0f", "100000000", 100000000.00); + test("%10.0f", " 1000000", 1000000.00); + test("%,10.0f", " 1,000,000", 1000000.00); + test("%,10.1f", "1,000,000.0", 1000000.00); + test("%,3.0f", "1,000,000", 1000000.00); + test("%,3.0f", "10,000,000", 10000000.00); + test("%,3.0f", "100,000,000", 100000000.00); + test("%,3.0f", "10,000,000", 10000000.00); + test("%,3.0f", "100,000,000", 100000000.00); + //--------------------------------------------------------------------- + // %g + // + // Floating-point conversions applicable to float, double, and + // BigDecimal. + //--------------------------------------------------------------------- + test("%g", "null", (Object)null); + test("%g", "3.14159", pi); + test(Locale.FRANCE, "%g", "3,14159", pi); + test("%.0g", "1e+01", ten); + test("%G", "3.14159", pi); + test("%10.3g", " 3.14", pi); + test("%10.3g", " -3.14", negate(pi)); + test("%010.3g", "0000003.14", pi); + test("%010.3g", "-000003.14", negate(pi)); + test("%-12.3g", "3.14 ", pi); + test("%-12.3g", "-3.14 ", negate(pi)); + test("%.3g", "3.14", pi); + test("%.3g", "-3.14", negate(pi)); + test("%.3g", "3.14e+08", mult(pi, 100000000.0)); + test("%.3g", "-3.14e+08", mult(pi, -100000000.0)); + test("%.3g", "1.00e-05", recip(create(100000.0))); + test("%.3g", "-1.00e-05", recip(create(-100000.0))); + test("%.0g", "-1e-05", recip(create(-100000.0))); + test("%.0g", "1e+05", create(100000.0)); + test("%.3G", "1.00E-05", recip(create(100000.0))); + test("%.3G", "-1.00E-05", recip(create(-100000.0))); + test("%.1g", "-0", -0.0); + test("%3.0g", " -0", -0.0); + test("%.1g", "0", 0.0); + test("%3.0g", " 0", 0.0); + test("%.1g", "0", +0.0); + test("%3.0g", " 0", +0.0); + test("%3.0g", "1e-06", 0.000001); + test("%3.0g", "1e-05", 0.00001); + test("%3.0g", "1e-05", 0.0000099); + test("%3.1g", "1e-05", 0.0000099); + test("%3.2g", "9.9e-06", 0.0000099); + test("%3.0g", "0.0001", 0.0001); + test("%3.0g", "9e-05", 0.00009); + test("%3.0g", "0.0001", 0.000099); + test("%3.1g", "0.0001", 0.000099); + test("%3.2g", "9.9e-05", 0.000099); + test("%3.0g", "0.001", 0.001); + test("%3.0g", "0.001", 0.00099); + test("%3.1g", "0.001", 0.00099); + test("%3.2g", "0.00099", 0.00099); + test("%3.3g", "0.00100", 0.001); + test("%3.4g", "0.001000", 0.001); + test("%3.0g", "0.01", 0.01); + test("%3.0g", "0.1", 0.1); + test("%3.0g", "0.9", 0.9); + test("%3.1g", "0.9", 0.9); + test("%3.0g", " 1", 1.00); + test("%3.2g", " 10", 10.00); + test("%3.0g", "1e+01", 10.00); + test("%3.0g", "1e+02", 99.19); + test("%3.1g", "1e+02", 99.19); + test("%3.2g", " 99", 99.19); + test("%3.0g", "1e+02", 99.9); + test("%3.1g", "1e+02", 99.9); + test("%3.2g", "1.0e+02", 99.9); + test("%3.0g", "1e+02", 99.99); + test("%3.0g", "1e+02", 100.00); + test("%3.0g", "1e+03", 999.9); + test("%3.1g", "1e+03", 999.9); + test("%3.2g", "1.0e+03", 999.9); + test("%3.3g", "1.00e+03", 999.9); + test("%3.4g", "999.9", 999.9); + test("%3.4g", "1000", 999.99); + test("%3.0g", "1e+03", 1000.00); + test("%3.0g", "1e+04", 10000.00); + test("%3.0g", "1e+05", 100000.00); + test("%3.0g", "1e+06", 1000000.00); + test("%3.0g", "1e+07", 10000000.00); + test("%3.9g", "100000000", 100000000.00); + test("%3.10g", "100000000.0", 100000000.00); + tryCatch("%#3.0g", FormatFlagsConversionMismatchException.class, 1000.00); + // double PI^300 + // = 13962455701329742638131355433930076081862072808 ... e+149 + test("%.3g", "10.0", ten); + test("%.3g", "1.00", one); + test("%10.3g", " 1.00", one); + test("%+10.3g", " +3.14", pi); + test("%+10.3g", " -3.14", negate(pi)); + test("% .3g", " 3.14", pi); + test("% .3g", "-3.14", negate(pi)); + test("%.0g", "3", create(3.0)); + test("%.0g", "-3", create(-3.0)); + test("%(.4g", "3.142e+08", mult(pi, 100000000.0)); + test("%(.4g", "(3.142e+08)", mult(pi, -100000000.0)); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + //--------------------------------------------------------------------- + // %f, %e, %g, %a - Boundaries + //--------------------------------------------------------------------- @@ -1691,99 +518,6 @@ public static void test() { tryCatch("%-tB", MissingFormatWidthException.class); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %n //--------------------------------------------------------------------- diff --git a/test/jdk/java/util/Formatter/BasicInt.java b/test/jdk/java/util/Formatter/BasicInt.java index 4cab1f29b6a7c..4d849534293c5 100644 --- a/test/jdk/java/util/Formatter/BasicInt.java +++ b/test/jdk/java/util/Formatter/BasicInt.java @@ -38,303 +38,13 @@ import static java.util.Calendar.*; - - - - public class BasicInt extends Basic { - - private static void test(String fs, String exp, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), Locale.US); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(Locale l, String fs, String exp, Object ... args) - { - Formatter f = new Formatter(new StringBuilder(), l); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), l); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(String fs, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, "fail", f.toString()); - } - - private static void test(String fs) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, "fail"); - ck(fs, "fail", f.toString()); - } - - private static void testSysOut(String fs, String exp, Object ... args) { - FileOutputStream fos = null; - FileInputStream fis = null; - try { - PrintStream saveOut = System.out; - fos = new FileOutputStream("testSysOut"); - System.setOut(new PrintStream(fos)); - System.out.format(Locale.US, fs, args); - fos.close(); - - fis = new FileInputStream("testSysOut"); - byte [] ba = new byte[exp.length()]; - int len = fis.read(ba); - String got = new String(ba); - if (len != ba.length) - fail(fs, exp, got); - ck(fs, exp, got); - - System.setOut(saveOut); - } catch (FileNotFoundException ex) { - fail(fs, ex.getClass()); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } finally { - try { - if (fos != null) - fos.close(); - if (fis != null) - fis.close(); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } - } - } - - private static void tryCatch(String fs, Class ex) { - boolean caught = false; - try { - test(fs); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - private static void tryCatch(String fs, Class ex, Object ... args) { - boolean caught = false; - try { - test(fs, args); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - private static int negate(int v) { return (int) -v; } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public static void test() { TimeZone.setDefault(TimeZone.getTimeZone("GMT-0800")); @@ -520,24 +230,8 @@ public static void test() { tryCatch("%#g", FormatFlagsConversionMismatchException.class); - - int minByte = Byte.MIN_VALUE; // -128 - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %d // @@ -546,89 +240,34 @@ public static void test() { //--------------------------------------------------------------------- test("%d", "null", (Object)null); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %d - int and long - //--------------------------------------------------------------------- - int oneToSeven = (int) 1234567; - test("%d", "1234567", oneToSeven); - test("%,d", "1,234,567", oneToSeven); - test(Locale.FRANCE, "%,d", "1\u202f234\u202f567", oneToSeven); - test("%,d", "-1,234,567", negate(oneToSeven)); - test("%(d", "1234567", oneToSeven); - test("%(d", "(1234567)", negate(oneToSeven)); - test("% d", " 1234567", oneToSeven); - test("% d", "-1234567", negate(oneToSeven)); - test("%+d", "+1234567", oneToSeven); - test("%+d", "-1234567", negate(oneToSeven)); - test("%010d", "0001234567", oneToSeven); - test("%010d", "-001234567", negate(oneToSeven)); - test("%(10d", " (1234567)", negate(oneToSeven)); - test("%-10d", "1234567 ", oneToSeven); - test("%-10d", "-1234567 ", negate(oneToSeven)); - // , variations: - test("% ,d", " 1,234,567", oneToSeven); - test("% ,d", "-1,234,567", negate(oneToSeven)); - test("%+,10d", "+1,234,567", oneToSeven); - test("%0,10d", "01,234,567", oneToSeven); - test("%0,10d", "-1,234,567", negate(oneToSeven)); - test("%(,10d", "(1,234,567)", negate(oneToSeven)); - test("%-,10d", "1,234,567 ", oneToSeven); - test("%-,10d", "-1,234,567", negate(oneToSeven)); - - - + //--------------------------------------------------------------------- + // %d - int and long + //--------------------------------------------------------------------- + int oneToSeven = (int) 1234567; + test("%d", "1234567", oneToSeven); + test("%,d", "1,234,567", oneToSeven); + test(Locale.FRANCE, "%,d", "1\u202f234\u202f567", oneToSeven); + test("%,d", "-1,234,567", negate(oneToSeven)); + test("%(d", "1234567", oneToSeven); + test("%(d", "(1234567)", negate(oneToSeven)); + test("% d", " 1234567", oneToSeven); + test("% d", "-1234567", negate(oneToSeven)); + test("%+d", "+1234567", oneToSeven); + test("%+d", "-1234567", negate(oneToSeven)); + test("%010d", "0001234567", oneToSeven); + test("%010d", "-001234567", negate(oneToSeven)); + test("%(10d", " (1234567)", negate(oneToSeven)); + test("%-10d", "1234567 ", oneToSeven); + test("%-10d", "-1234567 ", negate(oneToSeven)); + // , variations: + test("% ,d", " 1,234,567", oneToSeven); + test("% ,d", "-1,234,567", negate(oneToSeven)); + test("%+,10d", "+1,234,567", oneToSeven); + test("%0,10d", "01,234,567", oneToSeven); + test("%0,10d", "-1,234,567", negate(oneToSeven)); + test("%(,10d", "(1,234,567)", negate(oneToSeven)); + test("%-,10d", "1,234,567 ", oneToSeven); + test("%-,10d", "-1,234,567", negate(oneToSeven)); //--------------------------------------------------------------------- // %d - errors @@ -647,24 +286,6 @@ public static void test() { //--------------------------------------------------------------------- test("%o", "null", (Object)null); - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %o - int //--------------------------------------------------------------------- @@ -678,21 +299,6 @@ public static void test() { test("%-10o", "4553207 ", oneToSevenOct); test("%#10o", " 04553207", oneToSevenOct); - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %o - errors //--------------------------------------------------------------------- @@ -715,29 +321,6 @@ public static void test() { //--------------------------------------------------------------------- test("%x", "null", (Object)null); - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %x - int //--------------------------------------------------------------------- @@ -756,27 +339,6 @@ public static void test() { test("%0#12x","0x00ffffff80", minByte); test("%#12X", " 0XFFFFFF80", minByte); test("%X", "FFFFFF80", minByte); - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %x - errors //--------------------------------------------------------------------- @@ -787,897 +349,13 @@ public static void test() { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %t - // - // Date/Time conversions applicable to Calendar, Date, and long. - //--------------------------------------------------------------------- - test("%tA", "null", (Object)null); - test("%TA", "NULL", (Object)null); + //--------------------------------------------------------------------- + // %t + // + // Date/Time conversions applicable to Calendar, Date, and long. + //--------------------------------------------------------------------- + test("%tA", "null", (Object)null); + test("%TA", "NULL", (Object)null); //--------------------------------------------------------------------- // %t - errors @@ -1691,99 +369,6 @@ public static void test() { tryCatch("%-tB", MissingFormatWidthException.class); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %n //--------------------------------------------------------------------- diff --git a/test/jdk/java/util/Formatter/BasicIntObject.java b/test/jdk/java/util/Formatter/BasicIntObject.java index 3e22d7973824a..b07eef69c214b 100644 --- a/test/jdk/java/util/Formatter/BasicIntObject.java +++ b/test/jdk/java/util/Formatter/BasicIntObject.java @@ -38,303 +38,13 @@ import static java.util.Calendar.*; - - - - public class BasicIntObject extends Basic { - - private static void test(String fs, String exp, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), Locale.US); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(Locale l, String fs, String exp, Object ... args) - { - Formatter f = new Formatter(new StringBuilder(), l); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), l); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(String fs, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, "fail", f.toString()); - } - - private static void test(String fs) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, "fail"); - ck(fs, "fail", f.toString()); - } - - private static void testSysOut(String fs, String exp, Object ... args) { - FileOutputStream fos = null; - FileInputStream fis = null; - try { - PrintStream saveOut = System.out; - fos = new FileOutputStream("testSysOut"); - System.setOut(new PrintStream(fos)); - System.out.format(Locale.US, fs, args); - fos.close(); - - fis = new FileInputStream("testSysOut"); - byte [] ba = new byte[exp.length()]; - int len = fis.read(ba); - String got = new String(ba); - if (len != ba.length) - fail(fs, exp, got); - ck(fs, exp, got); - - System.setOut(saveOut); - } catch (FileNotFoundException ex) { - fail(fs, ex.getClass()); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } finally { - try { - if (fos != null) - fos.close(); - if (fis != null) - fis.close(); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } - } - } - - private static void tryCatch(String fs, Class ex) { - boolean caught = false; - try { - test(fs); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - private static void tryCatch(String fs, Class ex, Object ... args) { - boolean caught = false; - try { - test(fs, args); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - private static Integer negate(Integer v) { return -v.intValue(); } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public static void test() { TimeZone.setDefault(TimeZone.getTimeZone("GMT-0800")); @@ -520,24 +230,8 @@ public static void test() { tryCatch("%#g", FormatFlagsConversionMismatchException.class); - - - - - - - - - - - Integer minByte = (int) Byte.MIN_VALUE; - - - - - //--------------------------------------------------------------------- // %d // @@ -546,1127 +240,52 @@ public static void test() { //--------------------------------------------------------------------- test("%d", "null", (Object)null); + //--------------------------------------------------------------------- + // %d - errors + //--------------------------------------------------------------------- + tryCatch("%#d", FormatFlagsConversionMismatchException.class); + tryCatch("%D", UnknownFormatConversionException.class); + tryCatch("%0d", MissingFormatWidthException.class); + tryCatch("%-d", MissingFormatWidthException.class); + tryCatch("%7.3d", IllegalFormatPrecisionException.class); + //--------------------------------------------------------------------- + // %o + // + // Numeric conversion applicable to byte, short, int, long, and + // BigInteger. + //--------------------------------------------------------------------- + test("%o", "null", (Object)null); + //--------------------------------------------------------------------- + // %o - errors + //--------------------------------------------------------------------- + tryCatch("%(o", FormatFlagsConversionMismatchException.class, + minByte); + tryCatch("%+o", FormatFlagsConversionMismatchException.class, + minByte); + tryCatch("% o", FormatFlagsConversionMismatchException.class, + minByte); + tryCatch("%0o", MissingFormatWidthException.class); + tryCatch("%-o", MissingFormatWidthException.class); + tryCatch("%,o", FormatFlagsConversionMismatchException.class); + tryCatch("%O", UnknownFormatConversionException.class); + //--------------------------------------------------------------------- + // %x + // + // Numeric conversion applicable to byte, short, int, long, and + // BigInteger. + //--------------------------------------------------------------------- + test("%x", "null", (Object)null); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %d - errors - //--------------------------------------------------------------------- - tryCatch("%#d", FormatFlagsConversionMismatchException.class); - tryCatch("%D", UnknownFormatConversionException.class); - tryCatch("%0d", MissingFormatWidthException.class); - tryCatch("%-d", MissingFormatWidthException.class); - tryCatch("%7.3d", IllegalFormatPrecisionException.class); - - //--------------------------------------------------------------------- - // %o - // - // Numeric conversion applicable to byte, short, int, long, and - // BigInteger. - //--------------------------------------------------------------------- - test("%o", "null", (Object)null); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %o - errors - //--------------------------------------------------------------------- - tryCatch("%(o", FormatFlagsConversionMismatchException.class, - minByte); - tryCatch("%+o", FormatFlagsConversionMismatchException.class, - minByte); - tryCatch("% o", FormatFlagsConversionMismatchException.class, - minByte); - tryCatch("%0o", MissingFormatWidthException.class); - tryCatch("%-o", MissingFormatWidthException.class); - tryCatch("%,o", FormatFlagsConversionMismatchException.class); - tryCatch("%O", UnknownFormatConversionException.class); - - //--------------------------------------------------------------------- - // %x - // - // Numeric conversion applicable to byte, short, int, long, and - // BigInteger. - //--------------------------------------------------------------------- - test("%x", "null", (Object)null); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %x - errors - //--------------------------------------------------------------------- - tryCatch("%,x", FormatFlagsConversionMismatchException.class); - tryCatch("%0x", MissingFormatWidthException.class); - tryCatch("%-x", MissingFormatWidthException.class); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + //--------------------------------------------------------------------- + // %x - errors + //--------------------------------------------------------------------- + tryCatch("%,x", FormatFlagsConversionMismatchException.class); + tryCatch("%0x", MissingFormatWidthException.class); + tryCatch("%-x", MissingFormatWidthException.class); @@ -1691,99 +310,6 @@ public static void test() { tryCatch("%-tB", MissingFormatWidthException.class); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %n //--------------------------------------------------------------------- diff --git a/test/jdk/java/util/Formatter/BasicLong.java b/test/jdk/java/util/Formatter/BasicLong.java index 32b25b3462134..750b07f1d1207 100644 --- a/test/jdk/java/util/Formatter/BasicLong.java +++ b/test/jdk/java/util/Formatter/BasicLong.java @@ -38,303 +38,13 @@ import static java.util.Calendar.*; - - - - public class BasicLong extends Basic { - - private static void test(String fs, String exp, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), Locale.US); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(Locale l, String fs, String exp, Object ... args) - { - Formatter f = new Formatter(new StringBuilder(), l); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), l); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(String fs, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, "fail", f.toString()); - } - - private static void test(String fs) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, "fail"); - ck(fs, "fail", f.toString()); - } - - private static void testSysOut(String fs, String exp, Object ... args) { - FileOutputStream fos = null; - FileInputStream fis = null; - try { - PrintStream saveOut = System.out; - fos = new FileOutputStream("testSysOut"); - System.setOut(new PrintStream(fos)); - System.out.format(Locale.US, fs, args); - fos.close(); - - fis = new FileInputStream("testSysOut"); - byte [] ba = new byte[exp.length()]; - int len = fis.read(ba); - String got = new String(ba); - if (len != ba.length) - fail(fs, exp, got); - ck(fs, exp, got); - - System.setOut(saveOut); - } catch (FileNotFoundException ex) { - fail(fs, ex.getClass()); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } finally { - try { - if (fos != null) - fos.close(); - if (fis != null) - fis.close(); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } - } - } - - private static void tryCatch(String fs, Class ex) { - boolean caught = false; - try { - test(fs); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - private static void tryCatch(String fs, Class ex, Object ... args) { - boolean caught = false; - try { - test(fs, args); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - private static long negate(long v) { return (long) -v; } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public static void test() { TimeZone.setDefault(TimeZone.getTimeZone("GMT-0800")); @@ -520,24 +230,8 @@ public static void test() { tryCatch("%#g", FormatFlagsConversionMismatchException.class); - - long minByte = Byte.MIN_VALUE; // -128 - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %d // @@ -546,89 +240,34 @@ public static void test() { //--------------------------------------------------------------------- test("%d", "null", (Object)null); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %d - int and long - //--------------------------------------------------------------------- - long oneToSeven = (long) 1234567; - test("%d", "1234567", oneToSeven); - test("%,d", "1,234,567", oneToSeven); - test(Locale.FRANCE, "%,d", "1\u202f234\u202f567", oneToSeven); - test("%,d", "-1,234,567", negate(oneToSeven)); - test("%(d", "1234567", oneToSeven); - test("%(d", "(1234567)", negate(oneToSeven)); - test("% d", " 1234567", oneToSeven); - test("% d", "-1234567", negate(oneToSeven)); - test("%+d", "+1234567", oneToSeven); - test("%+d", "-1234567", negate(oneToSeven)); - test("%010d", "0001234567", oneToSeven); - test("%010d", "-001234567", negate(oneToSeven)); - test("%(10d", " (1234567)", negate(oneToSeven)); - test("%-10d", "1234567 ", oneToSeven); - test("%-10d", "-1234567 ", negate(oneToSeven)); - // , variations: - test("% ,d", " 1,234,567", oneToSeven); - test("% ,d", "-1,234,567", negate(oneToSeven)); - test("%+,10d", "+1,234,567", oneToSeven); - test("%0,10d", "01,234,567", oneToSeven); - test("%0,10d", "-1,234,567", negate(oneToSeven)); - test("%(,10d", "(1,234,567)", negate(oneToSeven)); - test("%-,10d", "1,234,567 ", oneToSeven); - test("%-,10d", "-1,234,567", negate(oneToSeven)); - - - + //--------------------------------------------------------------------- + // %d - int and long + //--------------------------------------------------------------------- + long oneToSeven = (long) 1234567; + test("%d", "1234567", oneToSeven); + test("%,d", "1,234,567", oneToSeven); + test(Locale.FRANCE, "%,d", "1\u202f234\u202f567", oneToSeven); + test("%,d", "-1,234,567", negate(oneToSeven)); + test("%(d", "1234567", oneToSeven); + test("%(d", "(1234567)", negate(oneToSeven)); + test("% d", " 1234567", oneToSeven); + test("% d", "-1234567", negate(oneToSeven)); + test("%+d", "+1234567", oneToSeven); + test("%+d", "-1234567", negate(oneToSeven)); + test("%010d", "0001234567", oneToSeven); + test("%010d", "-001234567", negate(oneToSeven)); + test("%(10d", " (1234567)", negate(oneToSeven)); + test("%-10d", "1234567 ", oneToSeven); + test("%-10d", "-1234567 ", negate(oneToSeven)); + // , variations: + test("% ,d", " 1,234,567", oneToSeven); + test("% ,d", "-1,234,567", negate(oneToSeven)); + test("%+,10d", "+1,234,567", oneToSeven); + test("%0,10d", "01,234,567", oneToSeven); + test("%0,10d", "-1,234,567", negate(oneToSeven)); + test("%(,10d", "(1,234,567)", negate(oneToSeven)); + test("%-,10d", "1,234,567 ", oneToSeven); + test("%-,10d", "-1,234,567", negate(oneToSeven)); //--------------------------------------------------------------------- // %d - errors @@ -647,38 +286,6 @@ public static void test() { //--------------------------------------------------------------------- test("%o", "null", (Object)null); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %o - long //--------------------------------------------------------------------- @@ -692,7 +299,6 @@ public static void test() { test("%-10o", "4553207 ", oneToSevenOct); test("%#10o", " 04553207", oneToSevenOct); - //--------------------------------------------------------------------- // %o - errors //--------------------------------------------------------------------- @@ -715,49 +321,6 @@ public static void test() { //--------------------------------------------------------------------- test("%x", "null", (Object)null); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %x - long //--------------------------------------------------------------------- @@ -776,7 +339,6 @@ public static void test() { test("%0#20x", "0x00ffffffffffffff80", minByte); test("%#20X", " 0XFFFFFFFFFFFFFF80", minByte); test("%X", "FFFFFFFFFFFFFF80", minByte); - //--------------------------------------------------------------------- // %x - errors //--------------------------------------------------------------------- @@ -787,897 +349,13 @@ public static void test() { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %t - // - // Date/Time conversions applicable to Calendar, Date, and long. - //--------------------------------------------------------------------- - test("%tA", "null", (Object)null); - test("%TA", "NULL", (Object)null); + //--------------------------------------------------------------------- + // %t + // + // Date/Time conversions applicable to Calendar, Date, and long. + //--------------------------------------------------------------------- + test("%tA", "null", (Object)null); + test("%TA", "NULL", (Object)null); //--------------------------------------------------------------------- // %t - errors @@ -1691,99 +369,6 @@ public static void test() { tryCatch("%-tB", MissingFormatWidthException.class); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %n //--------------------------------------------------------------------- diff --git a/test/jdk/java/util/Formatter/BasicLongObject.java b/test/jdk/java/util/Formatter/BasicLongObject.java index a7f382d9846eb..b8e1a787b366f 100644 --- a/test/jdk/java/util/Formatter/BasicLongObject.java +++ b/test/jdk/java/util/Formatter/BasicLongObject.java @@ -38,303 +38,13 @@ import static java.util.Calendar.*; - - - - public class BasicLongObject extends Basic { - - private static void test(String fs, String exp, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), Locale.US); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(Locale l, String fs, String exp, Object ... args) - { - Formatter f = new Formatter(new StringBuilder(), l); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), l); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(String fs, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, "fail", f.toString()); - } - - private static void test(String fs) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, "fail"); - ck(fs, "fail", f.toString()); - } - - private static void testSysOut(String fs, String exp, Object ... args) { - FileOutputStream fos = null; - FileInputStream fis = null; - try { - PrintStream saveOut = System.out; - fos = new FileOutputStream("testSysOut"); - System.setOut(new PrintStream(fos)); - System.out.format(Locale.US, fs, args); - fos.close(); - - fis = new FileInputStream("testSysOut"); - byte [] ba = new byte[exp.length()]; - int len = fis.read(ba); - String got = new String(ba); - if (len != ba.length) - fail(fs, exp, got); - ck(fs, exp, got); - - System.setOut(saveOut); - } catch (FileNotFoundException ex) { - fail(fs, ex.getClass()); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } finally { - try { - if (fos != null) - fos.close(); - if (fis != null) - fis.close(); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } - } - } - - private static void tryCatch(String fs, Class ex) { - boolean caught = false; - try { - test(fs); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - private static void tryCatch(String fs, Class ex, Object ... args) { - boolean caught = false; - try { - test(fs, args); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - private static Long negate(Long v) { return -v.longValue(); } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public static void test() { TimeZone.setDefault(TimeZone.getTimeZone("GMT-0800")); @@ -520,24 +230,8 @@ public static void test() { tryCatch("%#g", FormatFlagsConversionMismatchException.class); - - - - - - - - - - - - - - Long minByte = (long) Byte.MIN_VALUE; - - //--------------------------------------------------------------------- // %d // @@ -546,1127 +240,52 @@ public static void test() { //--------------------------------------------------------------------- test("%d", "null", (Object)null); + //--------------------------------------------------------------------- + // %d - errors + //--------------------------------------------------------------------- + tryCatch("%#d", FormatFlagsConversionMismatchException.class); + tryCatch("%D", UnknownFormatConversionException.class); + tryCatch("%0d", MissingFormatWidthException.class); + tryCatch("%-d", MissingFormatWidthException.class); + tryCatch("%7.3d", IllegalFormatPrecisionException.class); + //--------------------------------------------------------------------- + // %o + // + // Numeric conversion applicable to byte, short, int, long, and + // BigInteger. + //--------------------------------------------------------------------- + test("%o", "null", (Object)null); + //--------------------------------------------------------------------- + // %o - errors + //--------------------------------------------------------------------- + tryCatch("%(o", FormatFlagsConversionMismatchException.class, + minByte); + tryCatch("%+o", FormatFlagsConversionMismatchException.class, + minByte); + tryCatch("% o", FormatFlagsConversionMismatchException.class, + minByte); + tryCatch("%0o", MissingFormatWidthException.class); + tryCatch("%-o", MissingFormatWidthException.class); + tryCatch("%,o", FormatFlagsConversionMismatchException.class); + tryCatch("%O", UnknownFormatConversionException.class); + //--------------------------------------------------------------------- + // %x + // + // Numeric conversion applicable to byte, short, int, long, and + // BigInteger. + //--------------------------------------------------------------------- + test("%x", "null", (Object)null); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %d - errors - //--------------------------------------------------------------------- - tryCatch("%#d", FormatFlagsConversionMismatchException.class); - tryCatch("%D", UnknownFormatConversionException.class); - tryCatch("%0d", MissingFormatWidthException.class); - tryCatch("%-d", MissingFormatWidthException.class); - tryCatch("%7.3d", IllegalFormatPrecisionException.class); - - //--------------------------------------------------------------------- - // %o - // - // Numeric conversion applicable to byte, short, int, long, and - // BigInteger. - //--------------------------------------------------------------------- - test("%o", "null", (Object)null); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %o - errors - //--------------------------------------------------------------------- - tryCatch("%(o", FormatFlagsConversionMismatchException.class, - minByte); - tryCatch("%+o", FormatFlagsConversionMismatchException.class, - minByte); - tryCatch("% o", FormatFlagsConversionMismatchException.class, - minByte); - tryCatch("%0o", MissingFormatWidthException.class); - tryCatch("%-o", MissingFormatWidthException.class); - tryCatch("%,o", FormatFlagsConversionMismatchException.class); - tryCatch("%O", UnknownFormatConversionException.class); - - //--------------------------------------------------------------------- - // %x - // - // Numeric conversion applicable to byte, short, int, long, and - // BigInteger. - //--------------------------------------------------------------------- - test("%x", "null", (Object)null); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %x - errors - //--------------------------------------------------------------------- - tryCatch("%,x", FormatFlagsConversionMismatchException.class); - tryCatch("%0x", MissingFormatWidthException.class); - tryCatch("%-x", MissingFormatWidthException.class); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + //--------------------------------------------------------------------- + // %x - errors + //--------------------------------------------------------------------- + tryCatch("%,x", FormatFlagsConversionMismatchException.class); + tryCatch("%0x", MissingFormatWidthException.class); + tryCatch("%-x", MissingFormatWidthException.class); @@ -1691,99 +310,6 @@ public static void test() { tryCatch("%-tB", MissingFormatWidthException.class); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %n //--------------------------------------------------------------------- diff --git a/test/jdk/java/util/Formatter/BasicShort.java b/test/jdk/java/util/Formatter/BasicShort.java index 21fe93f3339a7..771120e2183bf 100644 --- a/test/jdk/java/util/Formatter/BasicShort.java +++ b/test/jdk/java/util/Formatter/BasicShort.java @@ -38,303 +38,13 @@ import static java.util.Calendar.*; - - - - public class BasicShort extends Basic { - - private static void test(String fs, String exp, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), Locale.US); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(Locale l, String fs, String exp, Object ... args) - { - Formatter f = new Formatter(new StringBuilder(), l); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), l); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(String fs, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, "fail", f.toString()); - } - - private static void test(String fs) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, "fail"); - ck(fs, "fail", f.toString()); - } - - private static void testSysOut(String fs, String exp, Object ... args) { - FileOutputStream fos = null; - FileInputStream fis = null; - try { - PrintStream saveOut = System.out; - fos = new FileOutputStream("testSysOut"); - System.setOut(new PrintStream(fos)); - System.out.format(Locale.US, fs, args); - fos.close(); - - fis = new FileInputStream("testSysOut"); - byte [] ba = new byte[exp.length()]; - int len = fis.read(ba); - String got = new String(ba); - if (len != ba.length) - fail(fs, exp, got); - ck(fs, exp, got); - - System.setOut(saveOut); - } catch (FileNotFoundException ex) { - fail(fs, ex.getClass()); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } finally { - try { - if (fos != null) - fos.close(); - if (fis != null) - fis.close(); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } - } - } - - private static void tryCatch(String fs, Class ex) { - boolean caught = false; - try { - test(fs); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - private static void tryCatch(String fs, Class ex, Object ... args) { - boolean caught = false; - try { - test(fs, args); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - private static short negate(short v) { return (short) -v; } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public static void test() { TimeZone.setDefault(TimeZone.getTimeZone("GMT-0800")); @@ -520,24 +230,8 @@ public static void test() { tryCatch("%#g", FormatFlagsConversionMismatchException.class); - - short minByte = Byte.MIN_VALUE; // -128 - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %d // @@ -546,29 +240,6 @@ public static void test() { //--------------------------------------------------------------------- test("%d", "null", (Object)null); - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %d - short //--------------------------------------------------------------------- @@ -596,40 +267,6 @@ public static void test() { test("%-,10d", "12,345 ", oneToFive); test("%-,10d", "-12,345 ", negate(oneToFive)); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %d - errors //--------------------------------------------------------------------- @@ -647,15 +284,6 @@ public static void test() { //--------------------------------------------------------------------- test("%o", "null", (Object)null); - - - - - - - - - //--------------------------------------------------------------------- // %o - short //--------------------------------------------------------------------- @@ -664,35 +292,6 @@ public static void test() { test("%-10o", "177600 ", minByte); test("%#10o", " 0177600", minByte); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %o - errors //--------------------------------------------------------------------- @@ -715,18 +314,6 @@ public static void test() { //--------------------------------------------------------------------- test("%x", "null", (Object)null); - - - - - - - - - - - - //--------------------------------------------------------------------- // %x - short //--------------------------------------------------------------------- @@ -736,47 +323,6 @@ public static void test() { test("%0#10x","0x0000ff80", minByte); test("%#10X", " 0XFF80", minByte); test("%X", "FF80", minByte); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %x - errors //--------------------------------------------------------------------- @@ -787,897 +333,13 @@ public static void test() { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %t - // - // Date/Time conversions applicable to Calendar, Date, and long. - //--------------------------------------------------------------------- - test("%tA", "null", (Object)null); - test("%TA", "NULL", (Object)null); + //--------------------------------------------------------------------- + // %t + // + // Date/Time conversions applicable to Calendar, Date, and long. + //--------------------------------------------------------------------- + test("%tA", "null", (Object)null); + test("%TA", "NULL", (Object)null); //--------------------------------------------------------------------- // %t - errors @@ -1691,99 +353,6 @@ public static void test() { tryCatch("%-tB", MissingFormatWidthException.class); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %n //--------------------------------------------------------------------- diff --git a/test/jdk/java/util/Formatter/BasicShortObject.java b/test/jdk/java/util/Formatter/BasicShortObject.java index 6d2fe4a792ea9..b73e3d84e646c 100644 --- a/test/jdk/java/util/Formatter/BasicShortObject.java +++ b/test/jdk/java/util/Formatter/BasicShortObject.java @@ -38,303 +38,13 @@ import static java.util.Calendar.*; - - - - public class BasicShortObject extends Basic { - - private static void test(String fs, String exp, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), Locale.US); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(Locale l, String fs, String exp, Object ... args) - { - Formatter f = new Formatter(new StringBuilder(), l); - f.format(fs, args); - ck(fs, exp, f.toString()); - - f = new Formatter(new StringBuilder(), l); - f.format("foo " + fs + " bar", args); - ck(fs, "foo " + exp + " bar", f.toString()); - } - - private static void test(String fs, Object ... args) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, args); - ck(fs, "fail", f.toString()); - } - - private static void test(String fs) { - Formatter f = new Formatter(new StringBuilder(), Locale.US); - f.format(fs, "fail"); - ck(fs, "fail", f.toString()); - } - - private static void testSysOut(String fs, String exp, Object ... args) { - FileOutputStream fos = null; - FileInputStream fis = null; - try { - PrintStream saveOut = System.out; - fos = new FileOutputStream("testSysOut"); - System.setOut(new PrintStream(fos)); - System.out.format(Locale.US, fs, args); - fos.close(); - - fis = new FileInputStream("testSysOut"); - byte [] ba = new byte[exp.length()]; - int len = fis.read(ba); - String got = new String(ba); - if (len != ba.length) - fail(fs, exp, got); - ck(fs, exp, got); - - System.setOut(saveOut); - } catch (FileNotFoundException ex) { - fail(fs, ex.getClass()); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } finally { - try { - if (fos != null) - fos.close(); - if (fis != null) - fis.close(); - } catch (IOException ex) { - fail(fs, ex.getClass()); - } - } - } - - private static void tryCatch(String fs, Class ex) { - boolean caught = false; - try { - test(fs); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - private static void tryCatch(String fs, Class ex, Object ... args) { - boolean caught = false; - try { - test(fs, args); - } catch (Throwable x) { - if (ex.isAssignableFrom(x.getClass())) - caught = true; - } - if (!caught) - fail(fs, ex); - else - pass(); - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - private static Short negate(Short v) { return (short) -v.shortValue(); } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public static void test() { TimeZone.setDefault(TimeZone.getTimeZone("GMT-0800")); @@ -520,24 +230,8 @@ public static void test() { tryCatch("%#g", FormatFlagsConversionMismatchException.class); - - - - - Short minByte = (short) Byte.MIN_VALUE; - - - - - - - - - - - //--------------------------------------------------------------------- // %d // @@ -546,1127 +240,52 @@ public static void test() { //--------------------------------------------------------------------- test("%d", "null", (Object)null); + //--------------------------------------------------------------------- + // %d - errors + //--------------------------------------------------------------------- + tryCatch("%#d", FormatFlagsConversionMismatchException.class); + tryCatch("%D", UnknownFormatConversionException.class); + tryCatch("%0d", MissingFormatWidthException.class); + tryCatch("%-d", MissingFormatWidthException.class); + tryCatch("%7.3d", IllegalFormatPrecisionException.class); + //--------------------------------------------------------------------- + // %o + // + // Numeric conversion applicable to byte, short, int, long, and + // BigInteger. + //--------------------------------------------------------------------- + test("%o", "null", (Object)null); + //--------------------------------------------------------------------- + // %o - errors + //--------------------------------------------------------------------- + tryCatch("%(o", FormatFlagsConversionMismatchException.class, + minByte); + tryCatch("%+o", FormatFlagsConversionMismatchException.class, + minByte); + tryCatch("% o", FormatFlagsConversionMismatchException.class, + minByte); + tryCatch("%0o", MissingFormatWidthException.class); + tryCatch("%-o", MissingFormatWidthException.class); + tryCatch("%,o", FormatFlagsConversionMismatchException.class); + tryCatch("%O", UnknownFormatConversionException.class); + //--------------------------------------------------------------------- + // %x + // + // Numeric conversion applicable to byte, short, int, long, and + // BigInteger. + //--------------------------------------------------------------------- + test("%x", "null", (Object)null); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %d - errors - //--------------------------------------------------------------------- - tryCatch("%#d", FormatFlagsConversionMismatchException.class); - tryCatch("%D", UnknownFormatConversionException.class); - tryCatch("%0d", MissingFormatWidthException.class); - tryCatch("%-d", MissingFormatWidthException.class); - tryCatch("%7.3d", IllegalFormatPrecisionException.class); - - //--------------------------------------------------------------------- - // %o - // - // Numeric conversion applicable to byte, short, int, long, and - // BigInteger. - //--------------------------------------------------------------------- - test("%o", "null", (Object)null); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %o - errors - //--------------------------------------------------------------------- - tryCatch("%(o", FormatFlagsConversionMismatchException.class, - minByte); - tryCatch("%+o", FormatFlagsConversionMismatchException.class, - minByte); - tryCatch("% o", FormatFlagsConversionMismatchException.class, - minByte); - tryCatch("%0o", MissingFormatWidthException.class); - tryCatch("%-o", MissingFormatWidthException.class); - tryCatch("%,o", FormatFlagsConversionMismatchException.class); - tryCatch("%O", UnknownFormatConversionException.class); - - //--------------------------------------------------------------------- - // %x - // - // Numeric conversion applicable to byte, short, int, long, and - // BigInteger. - //--------------------------------------------------------------------- - test("%x", "null", (Object)null); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- - // %x - errors - //--------------------------------------------------------------------- - tryCatch("%,x", FormatFlagsConversionMismatchException.class); - tryCatch("%0x", MissingFormatWidthException.class); - tryCatch("%-x", MissingFormatWidthException.class); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + //--------------------------------------------------------------------- + // %x - errors + //--------------------------------------------------------------------- + tryCatch("%,x", FormatFlagsConversionMismatchException.class); + tryCatch("%0x", MissingFormatWidthException.class); + tryCatch("%-x", MissingFormatWidthException.class); @@ -1691,99 +310,6 @@ public static void test() { tryCatch("%-tB", MissingFormatWidthException.class); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--------------------------------------------------------------------- // %n //--------------------------------------------------------------------- diff --git a/test/jdk/java/util/Formatter/genBasic.sh b/test/jdk/java/util/Formatter/genBasic.sh index 809873fbfd431..247f42a4edeae 100644 --- a/test/jdk/java/util/Formatter/genBasic.sh +++ b/test/jdk/java/util/Formatter/genBasic.sh @@ -32,7 +32,7 @@ gen() { # fi out=Basic$2.java rm -f $out - java build.tools.spp.Spp -K$1 -Dtype=$1 -DType=$2 -K$3 -K$4 -K$5 -K$6 -iBasic-X.java.template -o$out + java build.tools.spp.Spp -K$1 -Dtype=$1 -DType=$2 -K$3 -K$4 -K$5 -K$6 -iBasic-X.java.template -nel -o$out } gen boolean Boolean prim "" "" "" diff --git a/test/jdk/java/util/Locale/LanguageSubtagRegistryTest.java b/test/jdk/java/util/Locale/LanguageSubtagRegistryTest.java index d143e025dd5d1..12f5a96d3fb51 100644 --- a/test/jdk/java/util/Locale/LanguageSubtagRegistryTest.java +++ b/test/jdk/java/util/Locale/LanguageSubtagRegistryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,9 +25,9 @@ * @test * @bug 8025703 8040211 8191404 8203872 8222980 8225435 8241082 8242010 8247432 * 8258795 8267038 8287180 8302512 8304761 8306031 8308021 8313702 8318322 - * 8327631 8332424 8334418 8344589 + * 8327631 8332424 8334418 8344589 8348328 * @summary Checks the IANA language subtag registry data update - * (LSR Revision: 2024-11-19) with Locale and Locale.LanguageRange + * (LSR Revision: 2025-05-15) with Locale and Locale.LanguageRange * class methods. * @run main LanguageSubtagRegistryTest */ @@ -45,9 +45,9 @@ public class LanguageSubtagRegistryTest { static boolean err = false; private static final String ACCEPT_LANGUAGE = - "Accept-Language: aam, adp, aeb, ajs, aog, apc, ajp, aue, bcg, bic, bpp, cey, cbr, cnp, cqu, crr, csp, csx, dif, dmw, dsz, ehs, ema," - + " en-gb-oed, gti, iba, ilw, jks, kdz, kjh, kmb, koj, kru, ksp, kwq, kxe, kzk, lgs, lii, lmm, lsb, lsc, lsn, lsv, lsw, lvi, meg, mtm," - + " ngv, nns, ola, oyb, pat, pcr, phr, plu, pnd, pub, rib, rnb, rsn, scv, snz, sqx, suj, szy, taj, tdg, tjj, tjp, tpn, tvx," + "Accept-Language: aam, adp, aeb, ajs, aog, apc, ajp, aue, bcg, bic, bpp, cey, cbr, cnp, cqu, crr, csp, csx, dif, dmw, dsz, ehs, eko, ema," + + " en-gb-oed, gti, hnm, iba, ilw, jks, kdz, kjh, kmb, koj, kru, ksp, kwq, kxe, kzk, lgs, lii, lmm, lsb, lsc, lsn, lsv, lsw, luh, lvi, meg, mtm," + + " ngv, nns, ola, oyb, pat, pcr, phr, plu, pnd, pub, rib, rnb, rsn, scv, sjc, snz, sqm, sqx, suj, szy, taj, tdg, tjj, tjp, tpn, tvx," + " umi, uss, uth, xia, yos, ysm, zko, wkr;q=0.9, ar-hyw;q=0.8, yug;q=0.5, gfx;q=0.4"; private static final List EXPECTED_RANGE_LIST = List.of( new LanguageRange("aam", 1.0), @@ -94,12 +94,16 @@ public class LanguageSubtagRegistryTest { new LanguageRange("sgn-dsz", 1.0), new LanguageRange("ehs", 1.0), new LanguageRange("sgn-ehs", 1.0), + new LanguageRange("eko", 1.0), + new LanguageRange("nte", 1.0), new LanguageRange("ema", 1.0), new LanguageRange("uok", 1.0), new LanguageRange("en-gb-oed", 1.0), new LanguageRange("en-gb-oxendict", 1.0), new LanguageRange("gti", 1.0), new LanguageRange("nyc", 1.0), + new LanguageRange("hnm", 1.0), + new LanguageRange("zh-hnm", 1.0), new LanguageRange("iba", 1.0), new LanguageRange("snb", 1.0), new LanguageRange("blg", 1.0), @@ -142,6 +146,8 @@ public class LanguageSubtagRegistryTest { new LanguageRange("sgn-lsv", 1.0), new LanguageRange("lsw", 1.0), new LanguageRange("sgn-lsw", 1.0), + new LanguageRange("luh", 1.0), + new LanguageRange("zh-luh", 1.0), new LanguageRange("lvi", 1.0), new LanguageRange("meg", 1.0), new LanguageRange("cir", 1.0), @@ -176,8 +182,12 @@ public class LanguageSubtagRegistryTest { new LanguageRange("sgn-rsn", 1.0), new LanguageRange("scv", 1.0), new LanguageRange("zir", 1.0), + new LanguageRange("sjc", 1.0), + new LanguageRange("zh-sjc", 1.0), new LanguageRange("snz", 1.0), new LanguageRange("asd", 1.0), + new LanguageRange("sqm", 1.0), + new LanguageRange("dek", 1.0), new LanguageRange("sqx", 1.0), new LanguageRange("sgn-sqx", 1.0), new LanguageRange("suj", 1.0), @@ -264,12 +274,18 @@ private static boolean areEqual(List expected, System.err.println(" Expected size=" + expectedSize); for (LanguageRange lr : expected) { + if (!got.contains(lr)) { + System.err.print("Error - Actual does not contain:"); + } System.err.println(" range=" + lr.getRange() + ", weight=" + lr.getWeight()); } System.err.println(" Actual size=" + actualSize); for (LanguageRange lr : got) { + if (!expected.contains(lr)) { + System.err.print("Error - Expected does not contain:"); + } System.err.println(" range=" + lr.getRange() + ", weight=" + lr.getWeight()); } diff --git a/test/jdk/java/util/Locale/UseOldISOCodesTest.java b/test/jdk/java/util/Locale/UseOldISOCodesTest.java index 38008e9f66294..d4f7b53f0c6b8 100644 --- a/test/jdk/java/util/Locale/UseOldISOCodesTest.java +++ b/test/jdk/java/util/Locale/UseOldISOCodesTest.java @@ -41,7 +41,7 @@ public class UseOldISOCodesTest { // Ensure java.locale.useOldISOCodes is only interpreted at runtime startup @Test public void staticInitializationTest() throws Exception { - ProcessTools.executeTestJvm("-Djava.locale.useOldISOCodes=true", "UseOldISOCodesTest$Runner") + ProcessTools.executeTestJava("-Djava.locale.useOldISOCodes=true", "UseOldISOCodesTest$Runner") .outputTo(System.out) .errorTo(System.err) .shouldHaveExitValue(0); diff --git a/test/jdk/java/util/PluggableLocale/LocaleNameProviderTest.java b/test/jdk/java/util/PluggableLocale/LocaleNameProviderTest.java index 8ed72427e3afc..a460c50de0e65 100644 --- a/test/jdk/java/util/PluggableLocale/LocaleNameProviderTest.java +++ b/test/jdk/java/util/PluggableLocale/LocaleNameProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 4052440 8000273 8062588 8210406 + * @bug 4052440 8000273 8062588 8210406 8356040 * @summary LocaleNameProvider tests * @library providersrc/foobarutils * providersrc/barprovider @@ -31,97 +31,112 @@ * java.base/sun.util.resources * @build com.foobar.Utils * com.bar.* - * @run main/othervm -Djava.locale.providers=JRE,SPI LocaleNameProviderTest + * @run junit/othervm -Djava.locale.providers=JRE,SPI LocaleNameProviderTest */ +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.MissingResourceException; +import java.util.ResourceBundle; import com.bar.LocaleNameProviderImpl; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import sun.util.locale.provider.LocaleProviderAdapter; import sun.util.locale.provider.ResourceBundleBasedAdapter; import sun.util.resources.OpenListResourceBundle; public class LocaleNameProviderTest extends ProviderTest { - public static void main(String[] s) { - new LocaleNameProviderTest(); - } + private static final LocaleNameProviderImpl LNP = new LocaleNameProviderImpl(); + + /* + * This is not an exhaustive test. Such a test would require iterating (1000x1000)+ + * inputs. Instead, we check against Japanese lang locales which guarantees + * we will run into cases where the CLDR is not the preferred provider as the + * SPI has defined variants of the Japanese locale (E.g. osaka). + * See LocaleNameProviderImpl and LocaleNames ResourceBundle. + */ + @ParameterizedTest + @MethodSource + void checkAvailLocValidityTest(Locale target, Locale test, ResourceBundle rb, + boolean jreSupports, boolean spiSupports) { + // codes + String lang = test.getLanguage(); + String ctry = test.getCountry(); + String vrnt = test.getVariant(); + + // the localized name + String langresult = test.getDisplayLanguage(target); + String ctryresult = test.getDisplayCountry(target); + String vrntresult = test.getDisplayVariant(target); + + // provider's name (if any) + String providerslang = null; + String providersctry = null; + String providersvrnt = null; + if (spiSupports) { + providerslang = LNP.getDisplayLanguage(lang, target); + providersctry = LNP.getDisplayCountry(ctry, target); + providersvrnt = LNP.getDisplayVariant(vrnt, target); + } - LocaleNameProviderTest() { - checkAvailLocValidityTest(); - variantFallbackTest(); - } + // JRE's name + String jreslang = null; + String jresctry = null; + String jresvrnt = null; + if (!lang.isEmpty()) { + try { + jreslang = rb.getString(lang); + } catch (MissingResourceException mre) {} + } + if (!ctry.isEmpty()) { + try { + jresctry = rb.getString(ctry); + } catch (MissingResourceException mre) {} + } + if (!vrnt.isEmpty()) { + try { + jresvrnt = rb.getString("%%"+vrnt); + } catch (MissingResourceException mre) {} + } - void checkAvailLocValidityTest() { - LocaleNameProviderImpl lnp = new LocaleNameProviderImpl(); - Locale[] availloc = Locale.getAvailableLocales(); - Locale[] testloc = availloc.clone(); - List jreimplloc = Arrays.asList(LocaleProviderAdapter.forJRE().getLocaleNameProvider().getAvailableLocales()); - List providerloc = Arrays.asList(lnp.getAvailableLocales()); + checkValidity(target, jreslang, providerslang, langresult, + jreSupports && jreslang != null); + checkValidity(target, jresctry, providersctry, ctryresult, + jreSupports && jresctry != null); + checkValidity(target, jresvrnt, providersvrnt, vrntresult, + jreSupports && jresvrnt != null); + } - for (Locale target: availloc) { + public static List checkAvailLocValidityTest() { + var args = new ArrayList(); + Locale[] availloc = Arrays.stream(Locale.getAvailableLocales()) + .filter(l -> l.getLanguage().equals("ja")) + .toArray(Locale[]::new); + List jreimplloc = Arrays.stream(LocaleProviderAdapter.forJRE().getLocaleNameProvider().getAvailableLocales()) + .filter(l -> l.getLanguage().equals("ja")) + .toList(); + List providerloc = Arrays.asList(LNP.getAvailableLocales()); + + for (Locale target : availloc) { // pure JRE implementation - OpenListResourceBundle rb = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forJRE()).getLocaleData().getLocaleNames(target); + OpenListResourceBundle rb = ((ResourceBundleBasedAdapter) LocaleProviderAdapter.forJRE()).getLocaleData().getLocaleNames(target); boolean jreSupportsTarget = jreimplloc.contains(target); - - for (Locale test: testloc) { - // codes - String lang = test.getLanguage(); - String ctry = test.getCountry(); - String vrnt = test.getVariant(); - - // the localized name - String langresult = test.getDisplayLanguage(target); - String ctryresult = test.getDisplayCountry(target); - String vrntresult = test.getDisplayVariant(target); - - // provider's name (if any) - String providerslang = null; - String providersctry = null; - String providersvrnt = null; - if (providerloc.contains(target)) { - providerslang = lnp.getDisplayLanguage(lang, target); - providersctry = lnp.getDisplayCountry(ctry, target); - providersvrnt = lnp.getDisplayVariant(vrnt, target); - } - - // JRE's name - String jreslang = null; - String jresctry = null; - String jresvrnt = null; - if (!lang.equals("")) { - try { - jreslang = rb.getString(lang); - } catch (MissingResourceException mre) {} - } - if (!ctry.equals("")) { - try { - jresctry = rb.getString(ctry); - } catch (MissingResourceException mre) {} - } - if (!vrnt.equals("")) { - try { - jresvrnt = rb.getString("%%"+vrnt); - } catch (MissingResourceException mre) {} - } - - System.out.print("For key: "+lang+" "); - checkValidity(target, jreslang, providerslang, langresult, - jreSupportsTarget && jreslang != null); - System.out.print("For key: "+ctry+" "); - checkValidity(target, jresctry, providersctry, ctryresult, - jreSupportsTarget && jresctry != null); - System.out.print("For key: "+vrnt+" "); - checkValidity(target, jresvrnt, providersvrnt, vrntresult, - jreSupportsTarget && jresvrnt != null); + boolean providerSupportsTarget = providerloc.contains(target); + for (Locale test : availloc) { + args.add(Arguments.of(target, test, rb, jreSupportsTarget, providerSupportsTarget)); } } + return args; } + @Test void variantFallbackTest() { Locale YY = new Locale("yy", "YY", "YYYY"); Locale YY_suffix = new Locale("yy", "YY", "YYYY_suffix"); @@ -143,4 +158,4 @@ void variantFallbackTest() { throw new RuntimeException(message); } -} \ No newline at end of file +} diff --git a/test/jdk/java/util/PluggableLocale/TimeZoneNameProviderTest.java b/test/jdk/java/util/PluggableLocale/TimeZoneNameProviderTest.java index d71b33edab3b4..11acf1be29c23 100644 --- a/test/jdk/java/util/PluggableLocale/TimeZoneNameProviderTest.java +++ b/test/jdk/java/util/PluggableLocale/TimeZoneNameProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 4052440 8003267 8062588 8210406 + * @bug 4052440 8003267 8062588 8210406 8327434 8347841 * @summary TimeZoneNameProvider tests * @library providersrc/foobarutils * providersrc/barprovider @@ -37,6 +37,7 @@ import java.text.DateFormatSymbols; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.time.ZoneId; import java.time.format.TextStyle; import java.util.Arrays; import java.util.Calendar; @@ -45,6 +46,8 @@ import java.util.Locale; import java.util.MissingResourceException; import java.util.TimeZone; +import java.util.function.Predicate; +import java.util.stream.Stream; import com.bar.TimeZoneNameProviderImpl; @@ -69,12 +72,14 @@ public static void main(String[] s) { } void test1() { - Locale[] available = Locale.getAvailableLocales(); List jreimplloc = Arrays.asList(LocaleProviderAdapter.forJRE().getTimeZoneNameProvider().getAvailableLocales()); List providerLocales = Arrays.asList(tznp.getAvailableLocales()); - String[] ids = TimeZone.getAvailableIDs(); + String[] ids = Arrays.stream(TimeZone.getAvailableIDs()) + .filter(Predicate.not(ZoneId.SHORT_IDS::containsKey)) + .toArray(String[]::new); - for (Locale target: available) { + // Sampling relevant locales + Stream.concat(Stream.of(Locale.ROOT, Locale.US, Locale.JAPAN), providerLocales.stream()).forEach(target -> { // pure JRE implementation OpenListResourceBundle rb = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forJRE()).getLocaleData().getTimeZoneNames(target); boolean jreSupportsTarget = jreimplloc.contains(target); @@ -111,7 +116,7 @@ void test1() { jreSupportsTarget && jresname != null); } } - } + }); } final String pattern = "z"; @@ -175,7 +180,7 @@ void test2() { df.parse(DISPLAY_NAMES_KYOTO[i]); } } catch (ParseException pe) { - throw new RuntimeException("parse error occured" + pe); + throw new RuntimeException("parse error occurred" + pe); } finally { // restore the reserved locale and time zone Locale.setDefault(defaultLocale); @@ -185,8 +190,8 @@ void test2() { void test3() { final String[] TZNAMES = { - LATIME, PST, PST8PDT, US_PACIFIC, - TOKYOTIME, JST, JAPAN, + LATIME, PST8PDT, US_PACIFIC, + TOKYOTIME, JAPAN, }; for (String tzname : TZNAMES) { TimeZone tz = TimeZone.getTimeZone(tzname); @@ -207,17 +212,13 @@ void test3() { } final String LATIME = "America/Los_Angeles"; - final String PST = "PST"; final String PST8PDT = "PST8PDT"; final String US_PACIFIC = "US/Pacific"; final String LATIME_IN_OSAKA = tznp.getDisplayName(LATIME, false, TimeZone.LONG, OSAKA); final String TOKYOTIME = "Asia/Tokyo"; - final String JST = "JST"; final String JAPAN = "Japan"; - final String JST_IN_OSAKA = - tznp.getDisplayName(JST, false, TimeZone.LONG, OSAKA); void aliasTest() { // Check that provider's name for a standard id (America/Los_Angeles) is @@ -227,32 +228,10 @@ void aliasTest() { throw new RuntimeException("Could not get provider's localized name. result: "+latime+" expected: "+LATIME_IN_OSAKA); } - String pst = TimeZone.getTimeZone(PST).getDisplayName(OSAKA); - if (!LATIME_IN_OSAKA.equals(pst)) { - throw new RuntimeException("Provider's localized name is not available for an alias ID: "+PST+". result: "+pst+" expected: "+LATIME_IN_OSAKA); - } - String us_pacific = TimeZone.getTimeZone(US_PACIFIC).getDisplayName(OSAKA); if (!LATIME_IN_OSAKA.equals(us_pacific)) { throw new RuntimeException("Provider's localized name is not available for an alias ID: "+US_PACIFIC+". result: "+us_pacific+" expected: "+LATIME_IN_OSAKA); } - - // Check that provider's name for an alias id (JST) is - // propagated to its standard id and alias ids. - String jstime = TimeZone.getTimeZone(JST).getDisplayName(OSAKA); - if (!JST_IN_OSAKA.equals(jstime)) { - throw new RuntimeException("Could not get provider's localized name. result: "+jstime+" expected: "+JST_IN_OSAKA); - } - - String tokyotime = TimeZone.getTimeZone(TOKYOTIME).getDisplayName(OSAKA); - if (!JST_IN_OSAKA.equals(tokyotime)) { - throw new RuntimeException("Provider's localized name is not available for a standard ID: "+TOKYOTIME+". result: "+tokyotime+" expected: "+JST_IN_OSAKA); - } - - String japan = TimeZone.getTimeZone(JAPAN).getDisplayName(OSAKA); - if (!JST_IN_OSAKA.equals(japan)) { - throw new RuntimeException("Provider's localized name is not available for an alias ID: "+JAPAN+". result: "+japan+" expected: "+JST_IN_OSAKA); - } } /* diff --git a/test/jdk/java/util/Properties/StoreDeadlock.java b/test/jdk/java/util/Properties/StoreDeadlock.java index d59a444525187..0eb3e930a81c3 100644 --- a/test/jdk/java/util/Properties/StoreDeadlock.java +++ b/test/jdk/java/util/Properties/StoreDeadlock.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 6199320 + * @bug 6199320 8347841 * @summary Properties.store() causes deadlock when concurrently calling TimeZone apis * @run main/timeout=20 StoreDeadlock * @author Xueming Shen @@ -59,7 +59,7 @@ public void run() { } class Thread2 extends Thread { public void run() { - System.out.println("tz=" + TimeZone.getTimeZone("PST")); + System.out.println("tz=" + TimeZone.getTimeZone("America/Los_Angeles")); } } } diff --git a/test/jdk/java/util/Random/T8282144.java b/test/jdk/java/util/Random/T8282144.java new file mode 100644 index 0000000000000..c911ab686677d --- /dev/null +++ b/test/jdk/java/util/Random/T8282144.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.random.*; +import jdk.internal.util.random.RandomSupport; + +/** + * @test + * @summary RandomSupport.convertSeedBytesToLongs sign extension overwrites previous bytes. + * @bug 8282144 8294509 + * @modules java.base/jdk.internal.util.random + * @run main T8282144 + * @key randomness + */ + + +public class T8282144 { + public static void main(String[] args) { + testLongs(); + testInts(); + } + + private static void testLongs() { + RandomGenerator rng = RandomGeneratorFactory.of("L64X128MixRandom").create(42); + + for (int i = 1; i < 8; i++) { + byte[] seed = new byte[i]; + + for (int j = 0; j < 10; j++) { + rng.nextBytes(seed); + + long[] existing = RandomSupport.convertSeedBytesToLongs(seed, 1, 1); + long[] testing = convertSeedBytesToLongsFixed(seed, 1, 1); + + for (int k = 0; k < existing.length; k++) { + if (existing[k] != testing[k]) { + throw new RuntimeException("convertSeedBytesToLongs incorrect"); + } + } + } + } + } + + private static void testInts() { + RandomGenerator rng = RandomGeneratorFactory.of("L64X128MixRandom").create(42); + + for (int i = 1; i < 8; i++) { + byte[] seed = new byte[i]; + + for (int j = 0; j < 10; j++) { + rng.nextBytes(seed); + + int[] existing = RandomSupport.convertSeedBytesToInts(seed, 1, 1); + int[] testing = convertSeedBytesToIntsFixed(seed, 1, 1); + + for (int k = 0; k < existing.length; k++) { + if (existing[k] != testing[k]) { + throw new RuntimeException("convertSeedBytesToInts incorrect"); + } + } + } + } + } + + + public static long[] convertSeedBytesToLongsFixed(byte[] seed, int n, int z) { + final long[] result = new long[n]; + final int m = Math.min(seed.length, n << 3); + + // Distribute seed bytes into the words to be formed. + for (int j = 0; j < m; j++) { + result[j >> 3] = (result[j >> 3] << 8) | (seed[j] & 0xff); + } + + return result; + } + + public static int[] convertSeedBytesToIntsFixed(byte[] seed, int n, int z) { + final int[] result = new int[n]; + final int m = Math.min(seed.length, n << 2); + + // Distribute seed bytes into the words to be formed. + for (int j = 0; j < m; j++) { + result[j >> 2] = (result[j >> 2] << 8) | (seed[j] & 0xff); + } + + return result; + } + +} diff --git a/test/jdk/java/util/TimeZone/Bug5097350.java b/test/jdk/java/util/TimeZone/Bug5097350.java index ff0894bf3d740..862d7f90a155f 100644 --- a/test/jdk/java/util/TimeZone/Bug5097350.java +++ b/test/jdk/java/util/TimeZone/Bug5097350.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,16 +23,19 @@ /* * @test - * @bug 5097350 + * @bug 5097350 8347841 * @summary Make sure that TimeZone.getTimeZone returns a clone of a cached TimeZone instance. */ +import java.time.ZoneId; import java.util.*; -import java.text.*; +import java.util.function.Predicate; public class Bug5097350 { public static void main(String[] args) { - String[] tzids = TimeZone.getAvailableIDs(); + String[] tzids = Arrays.stream(TimeZone.getAvailableIDs()) + .filter(Predicate.not(ZoneId.SHORT_IDS::containsKey)) + .toArray(String[]::new); List ids = new ArrayList<>(tzids.length + 10); ids.addAll(Arrays.asList(tzids)); // add some custom ids diff --git a/test/jdk/java/util/TimeZone/Bug6329116.java b/test/jdk/java/util/TimeZone/Bug6329116.java index 6e85fe8cb5b66..f005b63446426 100644 --- a/test/jdk/java/util/TimeZone/Bug6329116.java +++ b/test/jdk/java/util/TimeZone/Bug6329116.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,14 +25,16 @@ * @test * @bug 6329116 6756569 6757131 6758988 6764308 6796489 6834474 6609737 6507067 * 7039469 7090843 7103108 7103405 7158483 8008577 8059206 8064560 8072042 - * 8077685 8151876 8166875 8169191 8170316 8176044 + * 8077685 8151876 8166875 8169191 8170316 8176044 8347841 * @summary Make sure that timezone short display names are idenical to Olson's data. * @run junit/othervm -Djava.locale.providers=COMPAT,SPI Bug6329116 */ import java.io.*; import java.text.*; +import java.time.ZoneId; import java.util.*; +import java.util.function.Predicate; import org.junit.jupiter.api.Test; @@ -41,7 +43,9 @@ public class Bug6329116 { static Locale[] locales = Locale.getAvailableLocales(); - static String[] timezones = TimeZone.getAvailableIDs(); + static String[] timezones = Arrays.stream(TimeZone.getAvailableIDs()) + .filter(Predicate.not(ZoneId.SHORT_IDS::containsKey)) + .toArray(String[]::new); @Test public void bug6329116() throws IOException { @@ -98,6 +102,9 @@ public void bug6329116() throws IOException { tzs[0] = timezoneID; for (int j = 0; j < tzs.length; j++) { + if (ZoneId.SHORT_IDS.containsKey(tzs[j])) { + continue; + } tz = TimeZone.getTimeZone(tzs[j]); if (!tzs[j].equals(tz.getID())) { diff --git a/test/jdk/java/util/TimeZone/Bug6772689.java b/test/jdk/java/util/TimeZone/Bug6772689.java index f730567013d35..b2ce085fc226f 100644 --- a/test/jdk/java/util/TimeZone/Bug6772689.java +++ b/test/jdk/java/util/TimeZone/Bug6772689.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,15 +23,18 @@ /* * @test - * @bug 6772689 + * @bug 6772689 8347841 * @summary Test for standard-to-daylight transitions at midnight: * date stays on the given day. */ +import java.time.ZoneId; +import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.TimeZone; +import java.util.function.Predicate; import static java.util.GregorianCalendar.*; public class Bug6772689 { @@ -43,7 +46,9 @@ public static void main(String[] args) { int errors = 0; Calendar cal = new GregorianCalendar(BEGIN_YEAR, MARCH, 1); - String[] tzids = TimeZone.getAvailableIDs(); + String[] tzids = Arrays.stream(TimeZone.getAvailableIDs()) + .filter(Predicate.not(ZoneId.SHORT_IDS::containsKey)) + .toArray(String[]::new); try { for (String id : tzids) { TimeZone tz = TimeZone.getTimeZone(id); diff --git a/test/jdk/java/util/TimeZone/CLDRDisplayNamesTest.java b/test/jdk/java/util/TimeZone/CLDRDisplayNamesTest.java index 67a082410ecbf..7b2b7dc600da1 100644 --- a/test/jdk/java/util/TimeZone/CLDRDisplayNamesTest.java +++ b/test/jdk/java/util/TimeZone/CLDRDisplayNamesTest.java @@ -130,7 +130,7 @@ public static void main(String[] args) { String displayName = zi.getDisplayName(false, TimeZone.SHORT, Locale.US); Locale.setDefault(originalLocale); if (!displayName.equals("GMT+05:00")) { - System.err.printf("Wrong display name for timezone Etc/GMT-5 : expected GMT+05:00, Actual " + displayName); + System.err.println("Wrong display name for timezone Etc/GMT-5 : expected GMT+05:00, Actual " + displayName); errors++; } diff --git a/test/jdk/java/util/TimeZone/DaylightTimeTest.java b/test/jdk/java/util/TimeZone/DaylightTimeTest.java index 4b637136f96b2..006f03424eea4 100644 --- a/test/jdk/java/util/TimeZone/DaylightTimeTest.java +++ b/test/jdk/java/util/TimeZone/DaylightTimeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,17 +23,21 @@ /* * @test - * @bug 6936350 + * @bug 6936350 8347841 * @summary Test case for TimeZone.observesDaylightTime() */ +import java.time.ZoneId; import java.util.*; +import java.util.function.Predicate; import static java.util.GregorianCalendar.*; public class DaylightTimeTest { private static final int ONE_HOUR = 60 * 60 * 1000; // one hour private static final int INTERVAL = 24 * ONE_HOUR; // one day - private static final String[] ZONES = TimeZone.getAvailableIDs(); + private static final String[] ZONES = Arrays.stream(TimeZone.getAvailableIDs()) + .filter(Predicate.not(ZoneId.SHORT_IDS::containsKey)) + .toArray(String[]::new); private static int errors = 0; public static void main(String[] args) { diff --git a/test/jdk/java/util/TimeZone/IDTest.java b/test/jdk/java/util/TimeZone/IDTest.java index d5396b619b8ea..97bf971abc905 100644 --- a/test/jdk/java/util/TimeZone/IDTest.java +++ b/test/jdk/java/util/TimeZone/IDTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,24 +23,30 @@ /* * @test - * @bug 4509255 5055567 6176318 7090844 + * @bug 4509255 5055567 6176318 7090844 8347841 * @summary Tests consistencies of time zone IDs. */ +import java.time.ZoneId; import java.util.Arrays; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.TimeZone; import java.util.TreeMap; +import java.util.function.Predicate; public class IDTest { public static void main(String[] args) { Set ids = new HashSet<>(); Map> tree = new TreeMap<>(); - String[] tzs = TimeZone.getAvailableIDs(); - String[] tzs2 = TimeZone.getAvailableIDs(); + String[] tzs = Arrays.stream(TimeZone.getAvailableIDs()) + .filter(Predicate.not(ZoneId.SHORT_IDS::containsKey)) + .toArray(String[]::new); + String[] tzs2 = Arrays.stream(TimeZone.getAvailableIDs()) + .filter(Predicate.not(ZoneId.SHORT_IDS::containsKey)) + .toArray(String[]::new); if (tzs.length != tzs2.length) { throw new RuntimeException("tzs.length(" + tzs.length + ") != tzs2.length(" + tzs2.length + ")"); @@ -83,8 +89,12 @@ public static void main(String[] args) { // Check the getAvailableIDs(int) call to return the same // set of IDs int offset = key.intValue(); - tzs = TimeZone.getAvailableIDs(offset); - tzs2 = TimeZone.getAvailableIDs(offset); + tzs = Arrays.stream(TimeZone.getAvailableIDs(offset)) + .filter(Predicate.not(ZoneId.SHORT_IDS::containsKey)) + .toArray(String[]::new); + tzs2 = Arrays.stream(TimeZone.getAvailableIDs(offset)) + .filter(Predicate.not(ZoneId.SHORT_IDS::containsKey)) + .toArray(String[]::new); if (!Arrays.equals(tzs, tzs2)) { throw new RuntimeException("inconsistent tzs from getAvailableIDs("+offset+")"); } diff --git a/test/jdk/java/util/TimeZone/ListTimeZones.java b/test/jdk/java/util/TimeZone/ListTimeZones.java index 7dd309473e641..dad59a91a95e5 100644 --- a/test/jdk/java/util/TimeZone/ListTimeZones.java +++ b/test/jdk/java/util/TimeZone/ListTimeZones.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,17 +23,21 @@ /** * @test - * @bug 6851214 + * @bug 6851214 8347841 * @summary Allow 24:00 as a valid end/start DST time stamp * @run main ListTimeZones */ +import java.time.ZoneId; import java.util.*; +import java.util.function.Predicate; public class ListTimeZones{ public static void main(String[] args){ Date date = new Date(); - String TimeZoneIds[] = TimeZone.getAvailableIDs(); + String[] TimeZoneIds = Arrays.stream(TimeZone.getAvailableIDs()) + .filter(Predicate.not(ZoneId.SHORT_IDS::containsKey)) + .toArray(String[]::new); for(int i = 0; i < TimeZoneIds.length; i++){ TimeZone tz = TimeZone.getTimeZone(TimeZoneIds[i]); Calendar calendar = new GregorianCalendar(tz); diff --git a/test/jdk/java/util/TimeZone/NegativeDSTTest.java b/test/jdk/java/util/TimeZone/NegativeDSTTest.java index b237f0fd4eeea..eb46b8d4b29f0 100644 --- a/test/jdk/java/util/TimeZone/NegativeDSTTest.java +++ b/test/jdk/java/util/TimeZone/NegativeDSTTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,7 +37,7 @@ /** * @test - * @bug 8212970 + * @bug 8212970 8324065 * @summary Test whether the savings are positive in time zones that have * negative savings in the source TZ files. * @run testng NegativeDSTTest @@ -81,7 +81,10 @@ private Object[][] negativeDST () { {CASABLANCA, LocalDate.of(2019, 5, 6), 0, false}, {CASABLANCA, LocalDate.of(2037, 10, 5), 0, false}, {CASABLANCA, LocalDate.of(2037, 11, 16), ONE_HOUR, true}, + {CASABLANCA, LocalDate.of(2038, 9, 27), 0, false}, {CASABLANCA, LocalDate.of(2038, 11, 1), ONE_HOUR, true}, + {CASABLANCA, LocalDate.of(2087, 3, 31), 0, false}, + {CASABLANCA, LocalDate.of(2087, 5, 12), ONE_HOUR, true}, }; } diff --git a/test/jdk/java/util/TimeZone/SimpleTimeZoneEqualsHashCodeTest.java b/test/jdk/java/util/TimeZone/SimpleTimeZoneEqualsHashCodeTest.java new file mode 100644 index 0000000000000..586c7bdf5cc42 --- /dev/null +++ b/test/jdk/java/util/TimeZone/SimpleTimeZoneEqualsHashCodeTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8369184 + * @summary Checks if equals()/hashCode() of SimpleTimeZone works correctly + * @run junit SimpleTimeZoneEqualsHashCodeTest + */ + +import java.util.SimpleTimeZone; +import static java.util.Calendar.MARCH; +import static java.util.Calendar.NOVEMBER; +import static java.util.Calendar.SUNDAY; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class SimpleTimeZoneEqualsHashCodeTest { + private static final SimpleTimeZone STZ_WITH_DST = + new SimpleTimeZone(-288_000_000, "America/Los_Angeles", + MARCH, 8, -SUNDAY, 7_200_000, + NOVEMBER, 1, -SUNDAY, 7_200_000); + private static final SimpleTimeZone STZ_WITHOUT_DST = + new SimpleTimeZone(0, "foo"); + + @Test + void withDSTTest() { + var stz = (SimpleTimeZone)STZ_WITH_DST.clone(); + assertEquals(STZ_WITH_DST, stz); + assertEquals(STZ_WITH_DST.hashCode(), stz.hashCode()); + + stz.setEndRule(NOVEMBER, 8, -SUNDAY, 7_200_000); + assertNotEquals(STZ_WITH_DST, stz); + // From the contract point, hash codes may be the same. + // This tests the implementation which considers DST + // related fields for calculating the hash code. + assertNotEquals(STZ_WITH_DST.hashCode(), stz.hashCode()); + } + + @Test + void withoutDSTTest() { + var stz = (SimpleTimeZone)STZ_WITHOUT_DST.clone(); + + // Only setting start rule. Still considered non-DST zone + stz.setStartRule(MARCH, 8, -SUNDAY, 7_200_000); + assertTrue(!stz.useDaylightTime()); + assertEquals(STZ_WITHOUT_DST, stz); + assertEquals(STZ_WITHOUT_DST.hashCode(), stz.hashCode()); + + // Setting end rule as well. Now it is considered DST zone + stz.setEndRule(NOVEMBER, 8, -SUNDAY, 7_200_000); + assertTrue(stz.useDaylightTime()); + assertNotEquals(STZ_WITHOUT_DST, stz); + // From the contract point, hash codes may be the same. + // This tests the implementation which considers DST + // related fields for calculating the hash code. + assertNotEquals(STZ_WITHOUT_DST.hashCode(), stz.hashCode()); + } +} diff --git a/test/jdk/java/util/TimeZone/TimeZoneBoundaryTest.java b/test/jdk/java/util/TimeZone/TimeZoneBoundaryTest.java index c70ccc204de55..9e4ca02731d3b 100644 --- a/test/jdk/java/util/TimeZone/TimeZoneBoundaryTest.java +++ b/test/jdk/java/util/TimeZone/TimeZoneBoundaryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,7 @@ /* * @test + * @bug 8347841 * @summary test Time Zone Boundary * @run junit TimeZoneBoundaryTest */ @@ -251,7 +252,7 @@ void verifyDST(Date d, TimeZone time_zone, @Test public void TestBoundaries() { - TimeZone pst = TimeZone.getTimeZone("PST"); + TimeZone pst = TimeZone.getTimeZone("America/Los_Angeles"); TimeZone save = TimeZone.getDefault(); try { TimeZone.setDefault(pst); @@ -410,11 +411,8 @@ else if (!z.useDaylightTime()) @Test public void TestStepwise() { - findBoundariesStepwise(1997, ONE_DAY, TimeZone.getTimeZone("ACT"), 0); - // "EST" is disabled because its behavior depends on the mapping property. (6466476). - //findBoundariesStepwise(1997, ONE_DAY, TimeZone.getTimeZone("EST"), 2); - findBoundariesStepwise(1997, ONE_DAY, TimeZone.getTimeZone("HST"), 0); - findBoundariesStepwise(1997, ONE_DAY, TimeZone.getTimeZone("PST"), 2); + findBoundariesStepwise(1997, ONE_DAY, TimeZone.getTimeZone("Australia/Darwin"), 0); + findBoundariesStepwise(1997, ONE_DAY, TimeZone.getTimeZone("Pacific/Honolulu"), 0); findBoundariesStepwise(1997, ONE_DAY, TimeZone.getTimeZone("PST8PDT"), 2); findBoundariesStepwise(1997, ONE_DAY, TimeZone.getTimeZone("SystemV/PST"), 0); findBoundariesStepwise(1997, ONE_DAY, TimeZone.getTimeZone("SystemV/PST8PDT"), 2); diff --git a/test/jdk/java/util/TimeZone/TimeZoneData/VERSION b/test/jdk/java/util/TimeZone/TimeZoneData/VERSION index 5159b3786e385..750d9fae2b135 100644 --- a/test/jdk/java/util/TimeZone/TimeZoneData/VERSION +++ b/test/jdk/java/util/TimeZone/TimeZoneData/VERSION @@ -1 +1 @@ -tzdata2025a +tzdata2025b diff --git a/test/jdk/java/util/TimeZone/TimeZoneRegression.java b/test/jdk/java/util/TimeZone/TimeZoneRegression.java index 6343279689fcc..bb191a47cd9c0 100644 --- a/test/jdk/java/util/TimeZone/TimeZoneRegression.java +++ b/test/jdk/java/util/TimeZone/TimeZoneRegression.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,7 @@ * @test * @bug 4052967 4073209 4073215 4084933 4096952 4109314 4126678 4151406 4151429 * 4154525 4154537 4154542 4154650 4159922 4162593 4173604 4176686 4184229 4208960 - * 4966229 6433179 6851214 8007520 8008577 + * 4966229 6433179 6851214 8007520 8008577 8347841 * @library /java/text/testlib * @run junit/othervm -Djava.locale.providers=COMPAT,SPI TimeZoneRegression */ @@ -42,8 +42,8 @@ public class TimeZoneRegression { @Test public void Test4073209() { - TimeZone z1 = TimeZone.getTimeZone("PST"); - TimeZone z2 = TimeZone.getTimeZone("PST"); + TimeZone z1 = TimeZone.getTimeZone("America/Los_Angeles"); + TimeZone z2 = TimeZone.getTimeZone("America/Los_Angeles"); if (z1 == z2) { fail("Fail: TimeZone should return clones"); } @@ -81,7 +81,7 @@ public void Test4084933() { // test both SimpleTimeZone and ZoneInfo objects. // @since 1.4 sub4084933(getPST()); - sub4084933(TimeZone.getTimeZone("PST")); + sub4084933(TimeZone.getTimeZone("America/Los_Angeles")); } private void sub4084933(TimeZone tz) { @@ -122,7 +122,7 @@ private void sub4084933(TimeZone tz) { @Test public void Test4096952() { - String[] ZONES = { "GMT", "MET", "IST" }; + String[] ZONES = { "GMT", "MET", "Asia/Kolkata" }; boolean pass = true; try { for (int i=0; i ensureLoaded = LockSupport.class; } @@ -84,7 +84,7 @@ public void run() { static class Unparker implements Runnable { static { // Reduce the risk of rare disastrous classloading in first call to - // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773 + // LockSupport.park: https://bugs.openjdk.org/browse/JDK-8074773 Class ensureLoaded = LockSupport.class; } diff --git a/test/jdk/java/util/concurrent/tck/ConcurrentLinkedDequeTest.java b/test/jdk/java/util/concurrent/tck/ConcurrentLinkedDequeTest.java index 58e4a32143bd7..62041e1d88763 100644 --- a/test/jdk/java/util/concurrent/tck/ConcurrentLinkedDequeTest.java +++ b/test/jdk/java/util/concurrent/tck/ConcurrentLinkedDequeTest.java @@ -941,7 +941,7 @@ void runAsync(Runnable r1, Runnable r2) { /** * Non-traversing Deque operations are linearizable. - * https://bugs.openjdk.java.net/browse/JDK-8188900 + * https://bugs.openjdk.org/browse/JDK-8188900 * ant -Djsr166.expensiveTests=true -Djsr166.tckTestClass=ConcurrentLinkedDequeTest -Djsr166.methodFilter=testBug8188900 tck */ public void testBug8188900() { @@ -998,7 +998,7 @@ public void testBug8188900_reverse() { /** * Non-traversing Deque operations (that return null) are linearizable. * Don't return null when the deque is observably never empty. - * https://bugs.openjdk.java.net/browse/JDK-8189387 + * https://bugs.openjdk.org/browse/JDK-8189387 * ant -Djsr166.expensiveTests=true -Djsr166.tckTestClass=ConcurrentLinkedDequeTest -Djsr166.methodFilter=testBug8189387 tck */ public void testBug8189387() { diff --git a/test/jdk/java/util/concurrent/tck/JSR166TestCase.java b/test/jdk/java/util/concurrent/tck/JSR166TestCase.java index 2cfa99866c573..43e8cfe94272c 100644 --- a/test/jdk/java/util/concurrent/tck/JSR166TestCase.java +++ b/test/jdk/java/util/concurrent/tck/JSR166TestCase.java @@ -309,7 +309,7 @@ private static float systemPropertyValue(String name) { * May be initialized from any of: * - the "jsr166.delay.factor" system property * - the "test.timeout.factor" system property (as used by jtreg) - * See: http://openjdk.java.net/jtreg/tag-spec.html + * See: https://openjdk.org/jtreg/tag-spec.html * - hard-coded fuzz factor when using a known slowpoke VM */ private static final float delayFactor = delayFactor(); @@ -776,7 +776,7 @@ T chooseRandomly(T... choices) { * Returns the shortest timed delay. This can be scaled up for * slow machines using the jsr166.delay.factor system property, * or via jtreg's -timeoutFactor: flag. - * http://openjdk.java.net/jtreg/command-help.html + * https://openjdk.org/jtreg/command-help.html */ protected long getShortDelay() { return (long) (50 * delayFactor); diff --git a/test/jdk/java/util/concurrent/tck/LockSupportTest.java b/test/jdk/java/util/concurrent/tck/LockSupportTest.java index 653e3591f7f93..1010ef0f77aa6 100644 --- a/test/jdk/java/util/concurrent/tck/LockSupportTest.java +++ b/test/jdk/java/util/concurrent/tck/LockSupportTest.java @@ -54,7 +54,7 @@ public static Test suite() { static { // Reduce the risk of rare disastrous classloading in first call to - // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773 + // LockSupport.park: https://bugs.openjdk.org/browse/JDK-8074773 Class ensureLoaded = LockSupport.class; } diff --git a/test/jdk/java/util/concurrent/tck/MapTest.java b/test/jdk/java/util/concurrent/tck/MapTest.java index 61579f3e7c378..8f3a2bc418253 100644 --- a/test/jdk/java/util/concurrent/tck/MapTest.java +++ b/test/jdk/java/util/concurrent/tck/MapTest.java @@ -121,7 +121,7 @@ public void testImplSanity() { /** * Tests and extends the scenario reported in - * https://bugs.openjdk.java.net/browse/JDK-8186171 + * https://bugs.openjdk.org/browse/JDK-8186171 * HashMap: Entry.setValue may not work after Iterator.remove() called for previous entries * ant -Djsr166.tckTestClass=HashMapTest -Djsr166.methodFilter=testBug8186171 -Djsr166.runsPerTest=1000 tck */ diff --git a/test/jdk/java/util/concurrent/tck/ScheduledExecutorTest.java b/test/jdk/java/util/concurrent/tck/ScheduledExecutorTest.java index c100ea0e8f04b..7daedeaf4a42f 100644 --- a/test/jdk/java/util/concurrent/tck/ScheduledExecutorTest.java +++ b/test/jdk/java/util/concurrent/tck/ScheduledExecutorTest.java @@ -700,11 +700,13 @@ public void testShutdownNow() throws InterruptedException { public void testShutdownNow_delayedTasks() throws InterruptedException { final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); List> tasks = new ArrayList<>(); + final int DELAY = 100; + for (int i = 0; i < 3; i++) { Runnable r = new NoOpRunnable(); - tasks.add(p.schedule(r, 9, SECONDS)); - tasks.add(p.scheduleAtFixedRate(r, 9, 9, SECONDS)); - tasks.add(p.scheduleWithFixedDelay(r, 9, 9, SECONDS)); + tasks.add(p.schedule(r, DELAY, SECONDS)); + tasks.add(p.scheduleAtFixedRate(r, DELAY, DELAY, SECONDS)); + tasks.add(p.scheduleWithFixedDelay(r, DELAY, DELAY, SECONDS)); } if (testImplementationDetails) assertEquals(new HashSet(tasks), new HashSet(p.getQueue())); @@ -1337,7 +1339,7 @@ public String realCall() { /** * A fixed delay task with overflowing period should not prevent a * one-shot task from executing. - * https://bugs.openjdk.java.net/browse/JDK-8051859 + * https://bugs.openjdk.org/browse/JDK-8051859 */ @SuppressWarnings("FutureReturnValueIgnored") public void testScheduleWithFixedDelay_overflow() throws Exception { diff --git a/test/jdk/java/util/jar/Manifest/ValueUtf8Coding.java b/test/jdk/java/util/jar/Manifest/ValueUtf8Coding.java index 533007dcf7a44..28921bba86816 100644 --- a/test/jdk/java/util/jar/Manifest/ValueUtf8Coding.java +++ b/test/jdk/java/util/jar/Manifest/ValueUtf8Coding.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,7 +37,7 @@ /** * @test - * @bug 8066619 + * @bug 8066619 8351567 * @run testng ValueUtf8Coding * @summary Tests encoding and decoding manifest header values to and from * UTF-8 with the complete Unicode character set. @@ -201,10 +201,6 @@ static Manifest writeAndRead(Manifest mf) throws IOException { mf.write(out); byte[] mfBytes = out.toByteArray(); - System.out.println("-".repeat(72)); - System.out.print(new String(mfBytes, UTF_8)); - System.out.println("-".repeat(72)); - ByteArrayInputStream in = new ByteArrayInputStream(mfBytes); return new Manifest(in); } diff --git a/test/jdk/java/util/logging/LocalizedLevelName.java b/test/jdk/java/util/logging/LocalizedLevelName.java index aaaf6f22a54ed..157e965f1a919 100644 --- a/test/jdk/java/util/logging/LocalizedLevelName.java +++ b/test/jdk/java/util/logging/LocalizedLevelName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -52,6 +52,7 @@ public class LocalizedLevelName { public static void main(String args[]) throws Exception { Locale defaultLocale = Locale.getDefault(); for (int i=0; i + // See // for more details. Logger.getLogger("main").info("starting..."); try { diff --git a/test/jdk/java/util/logging/SimpleFormatterFormat.java b/test/jdk/java/util/logging/SimpleFormatterFormat.java index 882a3414175c8..26abcc2f913e8 100644 --- a/test/jdk/java/util/logging/SimpleFormatterFormat.java +++ b/test/jdk/java/util/logging/SimpleFormatterFormat.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,7 @@ */ import java.io.*; +import java.util.Locale; import java.util.logging.*; import java.util.regex.*; @@ -38,6 +39,8 @@ public class SimpleFormatterFormat { private static final String origFormat = System.getProperty(key); private static final PrintStream err = System.err; public static void main(String[] args) throws Exception { + Locale savedLocale = Locale.getDefault(); + Locale.setDefault(Locale.US); try { File dir = new File(System.getProperty("user.dir", ".")); File log = new File(dir, "simpleformat.txt"); @@ -53,7 +56,8 @@ public static void main(String[] args) throws Exception { System.setProperty(key, origFormat); } System.setErr(err); - } + } + Locale.setDefault(savedLocale); } private static String[] loggers = new String[] { diff --git a/test/jdk/java/util/prefs/CheckUserPrefsStorage.java b/test/jdk/java/util/prefs/CheckUserPrefsStorage.java index 51ecae932d17b..1f772f4811aaa 100644 --- a/test/jdk/java/util/prefs/CheckUserPrefsStorage.java +++ b/test/jdk/java/util/prefs/CheckUserPrefsStorage.java @@ -42,7 +42,7 @@ public static void main(String[] args) throws Throwable { } public static void run(String testName) throws Exception { - ProcessTools.executeTestJvm("-Djava.util.prefs.userRoot=.", testName) + ProcessTools.executeTestJava("-Djava.util.prefs.userRoot=.", testName) .outputTo(System.out) .errorTo(System.out) .shouldHaveExitValue(0); diff --git a/test/jdk/java/util/zip/EntryCount64k.java b/test/jdk/java/util/zip/EntryCount64k.java index 08d896a124a2a..8830a87ea647b 100644 --- a/test/jdk/java/util/zip/EntryCount64k.java +++ b/test/jdk/java/util/zip/EntryCount64k.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2013 Google Inc. All rights reserved. + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -47,9 +48,12 @@ import jdk.test.lib.process.ProcessTools; public class EntryCount64k { + + private static final String MAIN_CLASS_MSG = "foo bar hello world Main"; + public static class Main { public static void main(String[] args) { - System.out.print("Main"); + System.out.println(MAIN_CLASS_MSG); } } @@ -160,9 +164,12 @@ static void checkCanRead(File zipFile, int entryCount) throws Throwable { } // Check java -jar - OutputAnalyzer a = ProcessTools.executeTestJvm("-jar", zipFile.getName()); + OutputAnalyzer a = ProcessTools.executeTestJava("-jar", zipFile.getName()); a.shouldHaveExitValue(0); - a.stdoutShouldMatch("\\AMain\\Z"); - a.stderrShouldMatch("\\A\\Z"); + // expect the message from the application on stdout + a.stdoutContains(MAIN_CLASS_MSG); + // nothing is expected on stderr (apart from any probable deprecation + // warnings from the launcher/JVM) + a.stderrShouldMatchIgnoreDeprecatedWarnings("\\A\\Z"); } } diff --git a/test/jdk/java/util/zip/ZipFile/DeleteTempJarTest.java b/test/jdk/java/util/zip/ZipFile/DeleteTempJarTest.java index e0a987f8bdc31..cafc17accf8d4 100644 --- a/test/jdk/java/util/zip/ZipFile/DeleteTempJarTest.java +++ b/test/jdk/java/util/zip/ZipFile/DeleteTempJarTest.java @@ -42,7 +42,7 @@ public class DeleteTempJarTest { public static void main(String[] args) throws Exception { - String tmpFile = ProcessTools.executeTestJvm(DeleteTempJar.class.getName()) + String tmpFile = ProcessTools.executeTestJava(DeleteTempJar.class.getName()) .shouldHaveExitValue(0) .getStdout(); diff --git a/test/jdk/java/util/zip/ZipFile/TestCleaner.java b/test/jdk/java/util/zip/ZipFile/TestCleaner.java index d24aa5045fd0a..103191c96dd72 100644 --- a/test/jdk/java/util/zip/ZipFile/TestCleaner.java +++ b/test/jdk/java/util/zip/ZipFile/TestCleaner.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,16 +23,24 @@ /* @test * @bug 8185582 8197989 - * @modules java.base/java.util.zip:open java.base/jdk.internal.vm.annotation * @summary Check the resources of Inflater, Deflater and ZipFile are always * cleaned/released when the instance is not unreachable + * @modules java.base/java.util.zip:open java.base/jdk.internal.vm.annotation + * @library /test/lib + * @comment The test relies on the Cleaner to invoke the cleaning actions. So + * we use "othervm" to prevent any Cleaner delays that could be contributed by any + * other tests or code that might have executed on the agentvm prior to this test + * execution + * @run main/othervm TestCleaner */ import java.io.*; import java.lang.reflect.*; import java.util.*; +import java.util.concurrent.atomic.AtomicLong; import java.util.zip.*; import jdk.internal.vm.annotation.DontInline; +import jdk.test.lib.util.ForceGC; import static java.nio.charset.StandardCharsets.US_ASCII; public class TestCleaner { @@ -59,11 +67,11 @@ private static void testDeInflater() throws Throwable { Field zsRefDef = Deflater.class.getDeclaredField("zsRef"); Field zsRefInf = Inflater.class.getDeclaredField("zsRef"); if (!zsRefDef.trySetAccessible() || !zsRefInf.trySetAccessible()) { - throw new RuntimeException("'zsRef' is not accesible"); + throw new RuntimeException("'zsRef' is not accessible"); } if (addrOf(zsRefDef.get(new Deflater())) == -1 || addrOf(zsRefInf.get(new Inflater())) == -1) { - throw new RuntimeException("'addr' is not accesible"); + throw new RuntimeException("'addr' is not accessible"); } List list = new ArrayList<>(); byte[] buf1 = new byte[1024]; @@ -84,16 +92,17 @@ private static void testDeInflater() throws Throwable { } } - int n = 10; - long cnt = list.size(); - while (n-- > 0 && cnt != 0) { - Thread.sleep(100); - System.gc(); - cnt = list.stream().filter(o -> addrOf(o) != 0).count(); + final AtomicLong numNotYetCleaned = new AtomicLong(); + // trigger GC + final boolean resourcesCleaned = ForceGC.wait(() -> { + final long remaining = list.stream().filter(o -> addrOf(o) != 0).count(); + numNotYetCleaned.set(remaining); + return remaining == 0; + }); + if (!resourcesCleaned) { + throw new RuntimeException(numNotYetCleaned.get() + + " resources haven't yet been cleaned"); } - if (cnt != 0) - throw new RuntimeException("cleaner failed to clean : " + cnt); - } @DontInline @@ -139,17 +148,18 @@ private static void testZipFile() throws Throwable { if (zsrc != null) { Field zfileField = zsrc.getClass().getDeclaredField("zfile"); if (!zfileField.trySetAccessible()) { - throw new RuntimeException("'ZipFile.Source.zfile' is not accesible"); - } - //System.out.println("zffile: " + zfileField.get(zsrc)); - int n = 10; - while (n-- > 0 && zfileField.get(zsrc) != null) { - System.out.println("waiting gc ... " + n); - System.gc(); - Thread.sleep(100); + throw new RuntimeException("'ZipFile.Source.zfile' is not accessible"); } - if (zfileField.get(zsrc) != null) { - throw new RuntimeException("cleaner failed to clean zipfile."); + final boolean resourceCleaned = ForceGC.wait(() -> { + try { + return zfileField.get(zsrc) == null; + } catch (IllegalAccessException e) { + // shouldn't happen + throw new RuntimeException(e); + } + }); + if (!resourceCleaned) { + throw new RuntimeException("cleaner failed to clean zipfile " + zip); } } } diff --git a/test/jdk/javax/accessibility/TestJCheckBoxToggleAccessibility.java b/test/jdk/javax/accessibility/TestJCheckBoxToggleAccessibility.java new file mode 100644 index 0000000000000..2a1d03117b738 --- /dev/null +++ b/test/jdk/javax/accessibility/TestJCheckBoxToggleAccessibility.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.GridLayout; + +import javax.swing.JCheckBox; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JToggleButton; + +/* + * @test + * @bug 8348936 8345728 + * @summary Verifies that VoiceOver announces the untick state of CheckBox and + * ToggleButton when space key is pressed. Also verifies that CheckBox + * and ToggleButton untick state is magnified with Screen Magnifier. + * @requires os.family == "mac" + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual TestJCheckBoxToggleAccessibility + */ + +public class TestJCheckBoxToggleAccessibility { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + +

    Testing with VoiceOver

    + +
      +
    1. Start the VoiceOver application + (Press Command + F5) +
    2. Click on the Frame with CheckBox and ToggleButton + window to move focus +
    3. Press Spacebar +
    4. VO should announce the checked state +
    5. Press Spacebar again +
    6. VO should announce the unchecked state +
    7. Press Tab to move focus to ToggleButton +
    8. Repeat steps 3 to 6 and listen the announcement +
    9. If announcements are incorrect, press Fail +
    10. Stop the VoiceOver application + (Press Command + F5 again) +
    + +

    Testing with Screen Magnifier

    +
      +
    1. Enable Screen magnifier on the Mac: + System Settings -> Accessibility -> + Hover Text -> Enable Hover Text
      + Default Hover Text Activation Modifier is Command key +
    2. Move focus back to the test application and perform the following tests + +
        +
      • Test CheckBox states with Screen Magnifier +
          +
        1. Click on CheckBox to select it +
        2. Press the Command key and + hover mouse over CheckBox +
        3. CheckBox ticked state along with its label should be magnified +
        4. Keep the Command key pressed and + click CheckBox to deselect it +
        5. CheckBox unticked state along with its label should be magnified +
        6. Release the Command key +
        7. If Screen Magnifier behaviour is incorrect, press Fail +
        +
      • Test ToggleButton states with Screen Magnifier +
          +
        1. Click on ToggleButton to select it +
        2. Press the Command key and + hover mouse over ToggleButton +
        3. Ticked state along with label should be magnified +
        4. Keep the Command button pressed and + click ToggleButton to deselect it +
        5. Unticked state along with its label should be magnified +
        6. Release the Command key +
        7. If Screen Magnifier behaviour is incorrect, press Fail +
        +
      +
    3. Disable Hover Text (optionally) in the Settings +
    + +

    Press Pass if you are able to hear correct VoiceOver announcements and + able to see the correct screen magnifier behaviour.

    """; + + PassFailJFrame.builder() + .title("TestJCheckBoxToggleAccessibility Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .rows(25) + .testUI(TestJCheckBoxToggleAccessibility::createUI) + .testTimeOut(8) + .build() + .awaitAndCheck(); + } + + private static JFrame createUI() { + JFrame frame = new JFrame("A Frame with CheckBox and ToggleButton"); + JCheckBox cb = new JCheckBox("CheckBox", false); + JToggleButton tb = new JToggleButton("ToggleButton"); + + JPanel p = new JPanel(new GridLayout(2, 1)); + p.add(cb); + p.add(tb); + frame.getContentPane().add(p, BorderLayout.CENTER); + frame.setSize(400, 400); + return frame; + } +} diff --git a/test/jdk/javax/accessibility/TestJSpinnerAccessibility.java b/test/jdk/javax/accessibility/TestJSpinnerAccessibility.java new file mode 100644 index 0000000000000..4926e5b17fab2 --- /dev/null +++ b/test/jdk/javax/accessibility/TestJSpinnerAccessibility.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; + +import javax.swing.JFrame; +import javax.swing.JSpinner; +import javax.swing.SpinnerModel; +import javax.swing.SpinnerNumberModel; + +/* + * @test + * @bug 8286204 + * @summary Verifies that VoiceOver announces the JSpinner's value correctly + * @requires os.family == "mac" + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual TestJSpinnerAccessibility + */ + +public class TestJSpinnerAccessibility { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Test UI contains a JSpinner with minimum value 0, maximum value 20 + and current value 5. On press of up / down arrow, value will be + incremented / decremented by 1. + + Follow these steps to test the behaviour: + + 1. Start the VoiceOver (Press Command + F5) application + 2. Move focus on test window if it is not focused + 3. Press Up / Down arrow to increase / decrease Spinner value + 4. VO should announce correct values in terms of percentage + (e.g. For JSpinner's value 10, VO should announce 50%) + 5. Press Pass if you are able to hear correct announcements + else Fail"""; + + PassFailJFrame.builder() + .title("TestJSpinnerAccessibility Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(TestJSpinnerAccessibility::createUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createUI() { + JFrame frame = new JFrame("A Frame with JSpinner"); + SpinnerModel spinnerModel = new SpinnerNumberModel(5, 0, 20, 1); + JSpinner spinner = new JSpinner(spinnerModel); + frame.getContentPane().add(spinner, BorderLayout.CENTER); + frame.setSize(200, 100); + return frame; + } +} diff --git a/test/jdk/javax/accessibility/TestPopupMenuChildCount.java b/test/jdk/javax/accessibility/TestPopupMenuChildCount.java new file mode 100644 index 0000000000000..f59b700714567 --- /dev/null +++ b/test/jdk/javax/accessibility/TestPopupMenuChildCount.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; + +/* + * @test + * @bug 8341311 + * @summary Verifies that VoiceOver announces correct number of child for PopupMenu on macOS + * @requires os.family == "mac" + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual TestPopupMenuChildCount + */ + +public class TestPopupMenuChildCount { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This test is applicable only on macOS. + + Test UI contains an empty JFrame. On press of left/right mouse button, + a PopupMenu will be visible. + + Follow these steps to test the behaviour: + + 1. Start the VoiceOver (Press Command + F5) application + 2. Press Left/Right mouse button inside test frame window to open + the PopupMenu + 3. VO should announce "Menu" with number of child items of the Popupmenu + 4. Press Up/Down arrow to traverse popupmenu child items + 5. Press Right arrow key to open submenu + 6. VO should announce "Menu" with correct number of child items + for the submenu (For e.g. When Submenu-1 is open, VO should announce + "Menu 4 items") + 7. Repeat the process for other submenus + 8. Press Pass if you are able to hear correct announcements + else Fail"""; + + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(45) + .testUI(TestPopupMenuChildCount::createUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createUI() { + JFrame frame = new JFrame("Test Frame"); + + JPopupMenu popupmenu = new JPopupMenu(); + JMenuItem mi1 = new JMenuItem("MenuItem-1"); + JMenuItem mi2 = new JMenuItem("MenuItem-2"); + JMenuItem mi3 = new JMenuItem("MenuItem-3"); + popupmenu.add(mi1); + popupmenu.add(mi2); + popupmenu.add(mi3); + + JMenu submenu1 = new JMenu("Submenu-1"); + submenu1.add("subOne"); + submenu1.add("subTwo"); + submenu1.add("subThree"); + + JMenu submenu2 = new JMenu("Submenu-2"); + submenu2.add("subOne"); + submenu2.add("subTwo"); + + JMenu submenu3 = new JMenu ("Submenu-3"); + submenu3.add("subOne"); + submenu1.add(submenu3); + + popupmenu.add(submenu1); + popupmenu.add(submenu2); + + frame.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + popupmenu.show(e.getComponent(), e.getX(), e.getY()); + } + }); + frame.setSize(300, 300); + return frame; + } +} diff --git a/test/jdk/javax/crypto/EncryptedPrivateKeyInfo/GetKeySpecException.java b/test/jdk/javax/crypto/EncryptedPrivateKeyInfo/GetKeySpecException.java index f55f1eea42ce6..5385beb9999fd 100644 --- a/test/jdk/javax/crypto/EncryptedPrivateKeyInfo/GetKeySpecException.java +++ b/test/jdk/javax/crypto/EncryptedPrivateKeyInfo/GetKeySpecException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,19 +24,20 @@ /** * @test * @bug 4508341 7055362 - * @library ../../../java/security/testlibrary + * @library /test/lib * @summary Test the error conditions of * EncryptedPrivateKeyInfo.getKeySpec(...) methods. * @author Valerie Peng * @run main/othervm -DcipherAlg=PBEWithMD5AndDES GetKeySpecException * @run main/othervm -DcipherAlg=PBEWithSHA1AndDESede GetKeySpecException */ + import java.security.*; -import java.util.Arrays; import java.util.Vector; import java.security.spec.*; import javax.crypto.*; import javax.crypto.spec.*; +import jdk.test.lib.security.ProvidersSnapshot; public class GetKeySpecException { private static String cipherAlg; diff --git a/test/jdk/javax/crypto/JceSecurity/SunJCE_BC_LoadOrdering.java b/test/jdk/javax/crypto/JceSecurity/SunJCE_BC_LoadOrdering.java index 50a1c188d0fa5..82ac4e70899f7 100644 --- a/test/jdk/javax/crypto/JceSecurity/SunJCE_BC_LoadOrdering.java +++ b/test/jdk/javax/crypto/JceSecurity/SunJCE_BC_LoadOrdering.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,7 @@ /* * @test * @bug 6377058 7055362 - * @library ../../../java/security/testlibrary + * @library /test/lib * @summary SunJCE depends on sun.security.provider.SignatureImpl * behaviour, BC can't load into 1st slot. * @author Brad R. Wetmore @@ -33,7 +33,7 @@ import java.security.*; import javax.crypto.*; -import java.io.*; +import jdk.test.lib.security.ProvidersSnapshot; public class SunJCE_BC_LoadOrdering { diff --git a/test/jdk/javax/imageio/stream/DeleteOnExitTest.sh b/test/jdk/javax/imageio/stream/DeleteOnExitTest.sh index feafda7bf250b..0e66b03d3d394 100644 --- a/test/jdk/javax/imageio/stream/DeleteOnExitTest.sh +++ b/test/jdk/javax/imageio/stream/DeleteOnExitTest.sh @@ -22,7 +22,7 @@ # @test # @bug 6291034 # @run shell DeleteOnExitTest.sh -# @summary Verify that temporary imageio files files are deleted on VM exit. +# @summary Verify that temporary imageio files are deleted on VM exit. if [ -z "${TESTSRC}" ]; then echo "TESTSRC undefined: defaulting to ." diff --git a/test/jdk/javax/management/ImplementationVersion/ImplVersionTest.java b/test/jdk/javax/management/ImplementationVersion/ImplVersionTest.java index cc77fced68331..16f0c8f262f7d 100644 --- a/test/jdk/javax/management/ImplementationVersion/ImplVersionTest.java +++ b/test/jdk/javax/management/ImplementationVersion/ImplVersionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,70 +30,48 @@ * system property. * @author Luis-Miguel Alventosa * - * @run clean ImplVersionTest ImplVersionCommand + * @library /test/lib * @run build ImplVersionTest ImplVersionCommand ImplVersionReader * @run main ImplVersionTest */ +import jdk.test.lib.process.ProcessTools; + import java.io.File; public class ImplVersionTest { - public static void main(String[] args) { - try { - // Get OS name - // - String osName = System.getProperty("os.name"); - System.out.println("osName = " + osName); - if ("Windows 98".equals(osName)) { - // Disable this test on Win98 due to parsing - // errors (bad handling of white spaces) when - // J2SE is installed under "Program Files". - // - System.out.println("This test is disabled on Windows 98."); - System.out.println("Bye! Bye!"); - return; - } - // Get Java Home - // - String javaHome = System.getProperty("java.home"); - System.out.println("javaHome = " + javaHome); - // Get test src - // - String testSrc = System.getProperty("test.src"); - System.out.println("testSrc = " + testSrc); - // Get test classes - // - String testClasses = System.getProperty("test.classes"); - System.out.println("testClasses = " + testClasses); - // Get boot class path - // - String command = - javaHome + File.separator + "bin" + File.separator + "java " + - " -classpath " + testClasses + - " -Djava.security.manager -Djava.security.policy==" + testSrc + - File.separator + "policy -Dtest.classes=" + testClasses + - " ImplVersionCommand " + - System.getProperty("java.runtime.version"); - System.out.println("ImplVersionCommand Exec Command = " +command); - Process proc = Runtime.getRuntime().exec(command); - new ImplVersionReader(proc, proc.getInputStream()).start(); - new ImplVersionReader(proc, proc.getErrorStream()).start(); - int exitValue = proc.waitFor(); - System.out.println("ImplVersionCommand Exit Value = " + - exitValue); - if (exitValue != 0) { - System.out.println("TEST FAILED: Incorrect exit value " + - "from ImplVersionCommand"); - System.exit(exitValue); - } - // Test OK! - // - System.out.println("Bye! Bye!"); - } catch (Exception e) { - System.out.println("Unexpected exception caught = " + e); - e.printStackTrace(); - System.exit(1); + public static void main(String[] args) throws Exception { + // Get test src + // + String testSrc = System.getProperty("test.src"); + System.out.println("testSrc = " + testSrc); + // Get test classes + // + String testClasses = System.getProperty("test.classes"); + System.out.println("testClasses = " + testClasses); + // Get boot class path + // + String[] command = new String[] { + "-Djava.security.manager", + "-Djava.security.policy==" + testSrc + File.separator + "policy", + "-Dtest.classes=" + testClasses, + "ImplVersionCommand", + System.getProperty("java.runtime.version") + }; + + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(command); + Process proc = pb.start(); + new ImplVersionReader(proc, proc.getInputStream()).start(); + new ImplVersionReader(proc, proc.getErrorStream()).start(); + int exitValue = proc.waitFor(); + System.out.println("ImplVersionCommand Exit Value = " + exitValue); + if (exitValue != 0) { + throw new RuntimeException("TEST FAILED: Incorrect exit value " + + "from ImplVersionCommand " + exitValue); } + // Test OK! + // + System.out.println("Bye! Bye!"); } } diff --git a/test/jdk/javax/management/monitor/DerivedGaugeMonitorTest.java b/test/jdk/javax/management/monitor/DerivedGaugeMonitorTest.java index 36b9dd40b502c..1452de5c3ce19 100644 --- a/test/jdk/javax/management/monitor/DerivedGaugeMonitorTest.java +++ b/test/jdk/javax/management/monitor/DerivedGaugeMonitorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,8 @@ * @summary Test that the initial derived gauge is (Integer)0 * @author Daniel Fuchs * + * @library /test/lib + * * @run clean DerivedGaugeMonitorTest * @run build DerivedGaugeMonitorTest * @run main DerivedGaugeMonitorTest @@ -40,9 +42,12 @@ import javax.management.ObjectName; import javax.management.monitor.CounterMonitor; import javax.management.monitor.GaugeMonitor; +import jdk.test.lib.Utils; public class DerivedGaugeMonitorTest { + public static final int WAIT_TIME = 1000; + public static interface Things { public long getALong(); public int getAnInt(); @@ -239,7 +244,7 @@ public static void test(String attr) throws Exception { mon1.setGranularityPeriod(5); mon2.setGranularityPeriod(5); - my.cdl.await(1000, TimeUnit.MILLISECONDS); + my.cdl.await(Utils.adjustTimeout(WAIT_TIME), TimeUnit.MILLISECONDS); if (my.cdl.getCount() > 0) throw new Exception(attr+": Count down not reached!"); diff --git a/test/jdk/javax/management/remote/mandatory/connection/DefaultAgentFilterTest.java b/test/jdk/javax/management/remote/mandatory/connection/DefaultAgentFilterTest.java index 1b036ce2f563e..6b4aa5e67dfa4 100644 --- a/test/jdk/javax/management/remote/mandatory/connection/DefaultAgentFilterTest.java +++ b/test/jdk/javax/management/remote/mandatory/connection/DefaultAgentFilterTest.java @@ -189,11 +189,9 @@ private static void testDefaultAgent(String propertyFile) throws Exception { private static void testDefaultAgent(String propertyFile, int port) throws Exception { String propFile = System.getProperty("test.src") + File.separator + propertyFile; - List pbArgs = new ArrayList<>(Arrays.asList( - "-cp", - System.getProperty("test.class.path"), - "-XX:+UsePerfData" - )); + List pbArgs = new ArrayList<>(); + pbArgs.add("-XX:+UsePerfData"); + String[] args = new String[]{ "-Dcom.sun.management.jmxremote.port=" + port, "-Dcom.sun.management.jmxremote.authenticate=false", @@ -203,7 +201,7 @@ private static void testDefaultAgent(String propertyFile, int port) throws Excep pbArgs.addAll(Arrays.asList(args)); pbArgs.add(TEST_APP_NAME); - ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( pbArgs.toArray(new String[pbArgs.size()]) ); diff --git a/test/jdk/javax/management/remote/mandatory/notif/ListenerScaleTest.java b/test/jdk/javax/management/remote/mandatory/notif/ListenerScaleTest.java index c03c75adee5c0..e5e0c0493cb1a 100644 --- a/test/jdk/javax/management/remote/mandatory/notif/ListenerScaleTest.java +++ b/test/jdk/javax/management/remote/mandatory/notif/ListenerScaleTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -77,6 +77,7 @@ public class ListenerScaleTest { private static final int WARMUP_WITH_ONE_MBEAN = 1000; private static final int NOTIFS_TO_TIME = 100; private static final int EXTRA_MBEANS = 20000; + private static final double RATIO_FAIL_VALUE = 2500.0; private static final ObjectName testObjectName; static { @@ -187,8 +188,9 @@ private static void test(MBeanServer mbs, JMXConnectorServer cs, long manyMBeansTime = timeNotif(mbs); System.out.println("Time with many MBeans: " + manyMBeansTime + "ns"); double ratio = (double) manyMBeansTime / singleMBeanTime; - if (ratio > 500.0) + if (ratio > RATIO_FAIL_VALUE) { throw new Exception("Failed: ratio=" + ratio); + } System.out.println("Test passed: ratio=" + ratio); } } diff --git a/test/jdk/javax/management/remote/mandatory/version/ImplVersionTest.java b/test/jdk/javax/management/remote/mandatory/version/ImplVersionTest.java index 8ee4e769cd85c..0a1793584666c 100644 --- a/test/jdk/javax/management/remote/mandatory/version/ImplVersionTest.java +++ b/test/jdk/javax/management/remote/mandatory/version/ImplVersionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,69 +30,48 @@ * system property. * @author Luis-Miguel Alventosa, Joel Feraud * - * @run clean ImplVersionTest ImplVersionCommand + * @library /test/lib * @run build ImplVersionTest ImplVersionCommand ImplVersionReader * @run main ImplVersionTest */ +import jdk.test.lib.process.ProcessTools; + import java.io.File; public class ImplVersionTest { - public static void main(String[] args) { - try { - // Get OS name - // - String osName = System.getProperty("os.name"); - System.out.println("osName = " + osName); - if ("Windows 98".equals(osName)) { - // Disable this test on Win98 due to parsing - // errors (bad handling of white spaces) when - // J2SE is installed under "Program Files". - // - System.out.println("This test is disabled on Windows 98."); - System.out.println("Bye! Bye!"); - return; - } + public static void main(String[] args) throws Exception { - // Get Java Home - String javaHome = System.getProperty("java.home"); + // Get test src + // + String testSrc = System.getProperty("test.src"); - // Get test src - // - String testSrc = System.getProperty("test.src"); + // Get test classes + String testClasses = System.getProperty("test.classes"); - // Get test classes - String testClasses = System.getProperty("test.classes"); + // Build command string - // Build command string - String command = - javaHome + File.separator + "bin" + File.separator + "java " + - " -classpath " + testClasses + - " -Djava.security.manager -Djava.security.policy==" + testSrc + - File.separator + "policy -Dtest.classes=" + testClasses + - " ImplVersionCommand " + System.getProperty("java.runtime.version"); - System.out.println("ImplVersionCommand Exec Command = " + command); + String[] command = new String[] { + "-Djava.security.manager", + "-Djava.security.policy==" + testSrc + File.separator + "policy", + "-Dtest.classes=" + testClasses, + "ImplVersionCommand", + System.getProperty("java.runtime.version") + }; - // Exec command - Process proc = Runtime.getRuntime().exec(command); - new ImplVersionReader(proc, proc.getInputStream()).start(); - new ImplVersionReader(proc, proc.getErrorStream()).start(); - int exitValue = proc.waitFor(); + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(command); + Process proc = pb.start(); + new ImplVersionReader(proc, proc.getInputStream()).start(); + new ImplVersionReader(proc, proc.getErrorStream()).start(); + int exitValue = proc.waitFor(); - System.out.println("ImplVersionCommand Exit Value = " + - exitValue); - if (exitValue != 0) { - System.out.println("TEST FAILED: Incorrect exit value " + - "from ImplVersionCommand"); - System.exit(exitValue); - } - // Test OK! - System.out.println("Bye! Bye!"); - } catch (Exception e) { - System.out.println("Unexpected exception caught = " + e); - e.printStackTrace(); - System.exit(1); + System.out.println("ImplVersionCommand Exit Value = " + exitValue); + if (exitValue != 0) { + throw new RuntimeException("TEST FAILED: Incorrect exit value " + + "from ImplVersionCommand " + exitValue); } + // Test OK! + System.out.println("Bye! Bye!"); } } diff --git a/test/jdk/javax/management/security/HashedPasswordFileTest.java b/test/jdk/javax/management/security/HashedPasswordFileTest.java index 7a3f02bc7c845..f84e00abd4ede 100644 --- a/test/jdk/javax/management/security/HashedPasswordFileTest.java +++ b/test/jdk/javax/management/security/HashedPasswordFileTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -110,9 +110,7 @@ private String[] getHash(String algorithm, String password) { } private String getPasswordFilePath() { - String testDir = System.getProperty("test.src"); - String testFileName = "jmxremote.password"; - return testDir + File.separator + testFileName; + return "jmxremote.password"; } private File createNewPasswordFile() throws IOException { @@ -468,10 +466,6 @@ public void testDefaultAgentNoHash() throws Exception { perms.add(PosixFilePermission.OWNER_READ); perms.add(PosixFilePermission.OWNER_WRITE); Files.setPosixFilePermissions(file.toPath(), perms); - - pbArgs.add("-cp"); - pbArgs.add(System.getProperty("test.class.path")); - pbArgs.add("-Dcom.sun.management.jmxremote.port=0"); pbArgs.add("-Dcom.sun.management.jmxremote.authenticate=true"); pbArgs.add("-Dcom.sun.management.jmxremote.password.file=" + file.getAbsolutePath()); @@ -481,7 +475,7 @@ public void testDefaultAgentNoHash() throws Exception { pbArgs.add("jdk.management.agent/jdk.internal.agent=ALL-UNNAMED"); pbArgs.add(TestApp.class.getSimpleName()); - ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( pbArgs.toArray(new String[0])); Process process = ProcessTools.startProcess( TestApp.class.getSimpleName(), diff --git a/test/jdk/javax/management/security/SecurityTest.java b/test/jdk/javax/management/security/SecurityTest.java index 835c340dd9924..7212aea883fd0 100644 --- a/test/jdk/javax/management/security/SecurityTest.java +++ b/test/jdk/javax/management/security/SecurityTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -142,6 +142,9 @@ static void setTrustStoreProperties(Map map) { * map (argName, value) format, then calls original test's run method. */ public static void main(String args[]) throws Exception { + // Disable default KeyManager's certificate checking so we can use + // a certificate signed with MD5withRSA algorithm. + System.setProperty("jdk.tls.SunX509KeyManager.certChecking", "false"); System.out.println("================================================="); @@ -529,6 +532,10 @@ private static class ClientSide { private MBeanServerConnection mbsc = null; public static void main(String args[]) throws Exception { + // Disable default KeyManager's certificate checking so we can use + // a certificate signed with MD5withRSA algorithm. + System.setProperty("jdk.tls.SunX509KeyManager.certChecking", + "false"); // Parses parameters Utils.parseDebugProperties(); diff --git a/test/jdk/javax/naming/module/RunBasic.java b/test/jdk/javax/naming/module/RunBasic.java index 512062de409ac..6a22b7f3515bf 100644 --- a/test/jdk/javax/naming/module/RunBasic.java +++ b/test/jdk/javax/naming/module/RunBasic.java @@ -98,7 +98,8 @@ public static void main(String[] args) throws Throwable { runTest("person", "test.StorePerson", "-Dcom.sun.jndi.ldap.object.trustSerialData=true"); runTest("fruit", "test.StoreFruit", - "-Dcom.sun.jndi.ldap.object.trustSerialData=true"); + "-Dcom.sun.jndi.ldap.object.trustSerialData=true", + "-Djdk.jndi.ldap.object.factoriesFilter=org.example.fruit.FruitFactory"); runTest("hello", "test.StoreRemote", "-Dcom.sun.jndi.ldap.object.trustSerialData=true"); runTest("foo", "test.ConnectWithFoo"); diff --git a/test/jdk/javax/net/ssl/DTLS/CipherSuite.java b/test/jdk/javax/net/ssl/DTLS/CipherSuite.java index 0b27779276651..2291d50104c01 100644 --- a/test/jdk/javax/net/ssl/DTLS/CipherSuite.java +++ b/test/jdk/javax/net/ssl/DTLS/CipherSuite.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,16 +33,16 @@ * jdk.crypto.ec * @library /test/lib * @build DTLSOverDatagram - * @run main/othervm CipherSuite TLS_RSA_WITH_AES_128_CBC_SHA + * @run main/othervm CipherSuite TLS_RSA_WITH_AES_128_CBC_SHA re-enable * @run main/othervm CipherSuite TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 - * @run main/othervm CipherSuite TLS_RSA_WITH_AES_128_CBC_SHA256 + * @run main/othervm CipherSuite TLS_RSA_WITH_AES_128_CBC_SHA256 re-enable * @run main/othervm CipherSuite TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 * @run main/othervm CipherSuite TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA * @run main/othervm CipherSuite TLS_DHE_RSA_WITH_AES_128_CBC_SHA * @run main/othervm CipherSuite TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA re-enable * @run main/othervm CipherSuite TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 * @run main/othervm CipherSuite TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 - * @run main/othervm CipherSuite TLS_RSA_WITH_AES_128_GCM_SHA256 + * @run main/othervm CipherSuite TLS_RSA_WITH_AES_128_GCM_SHA256 re-enable * @run main/othervm CipherSuite TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 re-enable * @run main/othervm CipherSuite TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 * @run main/othervm CipherSuite TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 diff --git a/test/jdk/javax/net/ssl/DTLS/DTLSOverDatagram.java b/test/jdk/javax/net/ssl/DTLS/DTLSOverDatagram.java index 1820dbe54237a..9780586cd57f0 100644 --- a/test/jdk/javax/net/ssl/DTLS/DTLSOverDatagram.java +++ b/test/jdk/javax/net/ssl/DTLS/DTLSOverDatagram.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -52,7 +52,6 @@ */ public class DTLSOverDatagram { - private static final int SOCKET_TIMEOUT = 10 * 1000; // in millis private static final int BUFFER_SIZE = 1024; private static final int MAXIMUM_PACKET_SIZE = 1024; @@ -78,6 +77,7 @@ public class DTLSOverDatagram { private final AtomicBoolean exceptionOccurred = new AtomicBoolean(false); private final CountDownLatch serverStarted = new CountDownLatch(1); + private int socketTimeout = 10 * 1000; // in millis /* * ============================================================= * The test case @@ -476,6 +476,10 @@ static DatagramPacket getPacket( return null; } + public void setSocketTimeout(int socketTimeout) { + this.socketTimeout = socketTimeout; + } + // run delegated tasks void runDelegatedTasks(SSLEngine engine) throws Exception { Runnable runnable; @@ -529,8 +533,8 @@ public final void runTest(DTLSOverDatagram testCase) throws Exception { try (DatagramSocket serverSocket = new DatagramSocket(serverSocketAddress); DatagramSocket clientSocket = new DatagramSocket(clientSocketAddress)) { - serverSocket.setSoTimeout(SOCKET_TIMEOUT); - clientSocket.setSoTimeout(SOCKET_TIMEOUT); + serverSocket.setSoTimeout(socketTimeout); + clientSocket.setSoTimeout(socketTimeout); InetSocketAddress serverSocketAddr = new InetSocketAddress( InetAddress.getLoopbackAddress(), serverSocket.getLocalPort()); diff --git a/test/jdk/javax/net/ssl/DTLS/FragmentedFinished.java b/test/jdk/javax/net/ssl/DTLS/FragmentedFinished.java new file mode 100644 index 0000000000000..2b6fd16005c0c --- /dev/null +++ b/test/jdk/javax/net/ssl/DTLS/FragmentedFinished.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// SunJSSE does not support dynamic system properties, no way to re-use +// system properties in samevm/agentvm mode. + +/* + * @test + * @bug 8367133 + * @summary Verify that handshake succeeds when Finished message is fragmented + * @modules java.base/sun.security.util + * @library /test/lib + * @build DTLSOverDatagram + * @run main/othervm FragmentedFinished + */ + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLParameters; +import java.net.DatagramPacket; +import java.net.SocketAddress; +import java.util.ArrayList; +import java.util.List; + +public class FragmentedFinished extends DTLSOverDatagram { + private SSLEngine serverSSLEngine; + public static void main(String[] args) throws Exception { + FragmentedFinished testCase = new FragmentedFinished(); + testCase.runTest(testCase); + } + + @Override + SSLEngine createSSLEngine(boolean isClient) throws Exception { + SSLEngine sslEngine = super.createSSLEngine(isClient); + if (!isClient) { + serverSSLEngine = sslEngine; + } + return sslEngine; + } + + @Override + DatagramPacket createHandshakePacket(byte[] ba, SocketAddress socketAddr) { + if (ba.length < 30) { // detect ChangeCipherSpec + // Reduce the maximumPacketSize to force fragmentation + // of the Finished message + SSLParameters params = serverSSLEngine.getSSLParameters(); + params.setMaximumPacketSize(53); + serverSSLEngine.setSSLParameters(params); + } + + return super.createHandshakePacket(ba, socketAddr); + } +} diff --git a/test/jdk/javax/net/ssl/DTLS/PacketLossRetransmission.java b/test/jdk/javax/net/ssl/DTLS/PacketLossRetransmission.java index 24a12600087f2..2dd0de830f1a1 100644 --- a/test/jdk/javax/net/ssl/DTLS/PacketLossRetransmission.java +++ b/test/jdk/javax/net/ssl/DTLS/PacketLossRetransmission.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,37 +32,16 @@ * @library /test/lib * @build DTLSOverDatagram * - * @run main/othervm PacketLossRetransmission client 0 hello_request * @run main/othervm PacketLossRetransmission client 1 client_hello - * @run main/othervm PacketLossRetransmission client 2 server_hello - * @run main/othervm PacketLossRetransmission client 3 hello_verify_request - * @run main/othervm -Djdk.tls.client.enableSessionTicketExtension=false PacketLossRetransmission client 4 new_session_ticket - * @run main/othervm PacketLossRetransmission client 11 certificate - * @run main/othervm PacketLossRetransmission client 12 server_key_exchange - * @run main/othervm PacketLossRetransmission client 13 certificate_request - * @run main/othervm PacketLossRetransmission client 14 server_hello_done - * @run main/othervm PacketLossRetransmission client 15 certificate_verify * @run main/othervm PacketLossRetransmission client 16 client_key_exchange * @run main/othervm PacketLossRetransmission client 20 finished - * @run main/othervm PacketLossRetransmission client 21 certificate_url - * @run main/othervm PacketLossRetransmission client 22 certificate_status - * @run main/othervm PacketLossRetransmission client 23 supplemental_data * @run main/othervm PacketLossRetransmission client -1 change_cipher_spec - * @run main/othervm PacketLossRetransmission server 0 hello_request - * @run main/othervm PacketLossRetransmission server 1 client_hello * @run main/othervm PacketLossRetransmission server 2 server_hello * @run main/othervm PacketLossRetransmission server 3 hello_verify_request - * @run main/othervm -Djdk.tls.client.enableSessionTicketExtension=false PacketLossRetransmission server 4 new_session_ticket * @run main/othervm PacketLossRetransmission server 11 certificate * @run main/othervm PacketLossRetransmission server 12 server_key_exchange - * @run main/othervm PacketLossRetransmission server 13 certificate_request * @run main/othervm PacketLossRetransmission server 14 server_hello_done - * @run main/othervm PacketLossRetransmission server 15 certificate_verify - * @run main/othervm PacketLossRetransmission server 16 client_key_exchange * @run main/othervm PacketLossRetransmission server 20 finished - * @run main/othervm PacketLossRetransmission server 21 certificate_url - * @run main/othervm PacketLossRetransmission server 22 certificate_status - * @run main/othervm PacketLossRetransmission server 23 supplemental_data * @run main/othervm PacketLossRetransmission server -1 change_cipher_spec */ @@ -79,6 +58,7 @@ public class PacketLossRetransmission extends DTLSOverDatagram { private static boolean isClient; private static byte handshakeType; + private static final int TIMEOUT = 500; private boolean needPacketLoss = true; @@ -87,6 +67,7 @@ public static void main(String[] args) throws Exception { handshakeType = Byte.parseByte(args[1]); PacketLossRetransmission testCase = new PacketLossRetransmission(); + testCase.setSocketTimeout(TIMEOUT); testCase.runTest(testCase); } @@ -102,7 +83,7 @@ boolean produceHandshakePackets(SSLEngine engine, SocketAddress socketAddr, if (packet != null) { needPacketLoss = false; - System.out.println("Loss a packet of handshake messahe"); + System.out.println("Loss a packet of handshake message"); packets.remove(packet); } } diff --git a/test/jdk/javax/net/ssl/HttpsURLConnection/CriticalSubjectAltName.java b/test/jdk/javax/net/ssl/HttpsURLConnection/CriticalSubjectAltName.java index d4eca8b577646..cb11b17ebb5f3 100644 --- a/test/jdk/javax/net/ssl/HttpsURLConnection/CriticalSubjectAltName.java +++ b/test/jdk/javax/net/ssl/HttpsURLConnection/CriticalSubjectAltName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -105,6 +105,7 @@ void doServerSide() throws Exception { (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); SSLServerSocket sslServerSocket = (SSLServerSocket) sslssf.createServerSocket(serverPort); + sslServerSocket.setEnabledProtocols(new String[]{"TLSv1.2"}); serverPort = sslServerSocket.getLocalPort(); /* diff --git a/test/jdk/javax/net/ssl/SSLEngine/Basics.java b/test/jdk/javax/net/ssl/SSLEngine/Basics.java index 3239bfd4ce99d..0ee7bfd7738d8 100644 --- a/test/jdk/javax/net/ssl/SSLEngine/Basics.java +++ b/test/jdk/javax/net/ssl/SSLEngine/Basics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -57,7 +57,8 @@ public class Basics { "/" + TRUSTSTORE_FILE; public static void main(String[] args) throws Exception { - SecurityUtils.removeFromDisabledTlsAlgs("TLSv1.1"); + // Re-enable TLSv1.1 and TLS_RSA_* since test depends on it. + SecurityUtils.removeFromDisabledTlsAlgs("TLSv1.1", "TLS_RSA_*"); runTest("TLSv1.3", "TLS_AES_256_GCM_SHA384"); runTest("TLSv1.2", "TLS_RSA_WITH_AES_256_GCM_SHA384"); diff --git a/test/jdk/javax/net/ssl/SSLEngine/EngineCloseOnAlert.java b/test/jdk/javax/net/ssl/SSLEngine/EngineCloseOnAlert.java index 7a4f71d8171dc..57eda1c2a42e8 100644 --- a/test/jdk/javax/net/ssl/SSLEngine/EngineCloseOnAlert.java +++ b/test/jdk/javax/net/ssl/SSLEngine/EngineCloseOnAlert.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +26,8 @@ * @bug 8133632 * @summary javax.net.ssl.SSLEngine does not properly handle received * SSL fatal alerts - * @run main EngineCloseOnAlert + * @library /test/lib + * @run main/othervm EngineCloseOnAlert */ import java.io.FileInputStream; @@ -36,6 +37,7 @@ import java.util.*; import java.security.*; import static javax.net.ssl.SSLEngineResult.HandshakeStatus.*; +import jdk.test.lib.security.SecurityUtils; public class EngineCloseOnAlert { @@ -61,6 +63,8 @@ public interface TestCase { } public static void main(String[] args) throws Exception { + // Re-enable TLS_RSA_* since test depends on it. + SecurityUtils.removeFromDisabledTlsAlgs("TLS_RSA_*"); int failed = 0; List testMatrix = new LinkedList() {{ add(clientReceivesAlert); diff --git a/test/jdk/javax/net/ssl/ServerName/EndingDotHostname.java b/test/jdk/javax/net/ssl/ServerName/EndingDotHostname.java index bac110aa0338b..5438f42945586 100644 --- a/test/jdk/javax/net/ssl/ServerName/EndingDotHostname.java +++ b/test/jdk/javax/net/ssl/ServerName/EndingDotHostname.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -248,4 +248,3 @@ protected void runClientApplication(SSLSocket socket) throws Exception { sslIS.read(); } } - diff --git a/test/jdk/javax/net/ssl/Stapling/HttpsUrlConnClient.java b/test/jdk/javax/net/ssl/Stapling/HttpsUrlConnClient.java index 72a68cdddb06d..b9829621d686b 100644 --- a/test/jdk/javax/net/ssl/Stapling/HttpsUrlConnClient.java +++ b/test/jdk/javax/net/ssl/Stapling/HttpsUrlConnClient.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,8 +28,7 @@ * @test * @bug 8046321 8153829 * @summary OCSP Stapling for TLS - * @library ../../../../java/security/testlibrary - * @build CertificateBuilder SimpleOCSPServer + * @library /test/lib * @run main/othervm HttpsUrlConnClient RSA SHA256withRSA * @run main/othervm HttpsUrlConnClient RSASSA-PSS RSASSA-PSS */ @@ -59,8 +58,8 @@ import java.util.*; import java.util.concurrent.TimeUnit; -import sun.security.testlibrary.SimpleOCSPServer; -import sun.security.testlibrary.CertificateBuilder; +import jdk.test.lib.security.SimpleOCSPServer; +import jdk.test.lib.security.CertificateBuilder; public class HttpsUrlConnClient { diff --git a/test/jdk/javax/net/ssl/Stapling/SSLEngineWithStapling.java b/test/jdk/javax/net/ssl/Stapling/SSLEngineWithStapling.java index ec869f8343118..506e7b00e6d28 100644 --- a/test/jdk/javax/net/ssl/Stapling/SSLEngineWithStapling.java +++ b/test/jdk/javax/net/ssl/Stapling/SSLEngineWithStapling.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,8 +28,7 @@ * @test * @bug 8046321 8153829 * @summary OCSP Stapling for TLS - * @library ../../../../java/security/testlibrary - * @build CertificateBuilder SimpleOCSPServer + * @library /test/lib * @run main/othervm SSLEngineWithStapling */ @@ -87,8 +86,8 @@ import java.util.Map; import java.util.concurrent.TimeUnit; -import sun.security.testlibrary.SimpleOCSPServer; -import sun.security.testlibrary.CertificateBuilder; +import jdk.test.lib.security.SimpleOCSPServer; +import jdk.test.lib.security.CertificateBuilder; public class SSLEngineWithStapling { diff --git a/test/jdk/javax/net/ssl/Stapling/SSLSocketWithStapling.java b/test/jdk/javax/net/ssl/Stapling/SSLSocketWithStapling.java index 7e1473c2828ad..30f5e5898899e 100644 --- a/test/jdk/javax/net/ssl/Stapling/SSLSocketWithStapling.java +++ b/test/jdk/javax/net/ssl/Stapling/SSLSocketWithStapling.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,8 +28,7 @@ * @test * @bug 8046321 8153829 * @summary OCSP Stapling for TLS - * @library ../../../../java/security/testlibrary - * @build CertificateBuilder SimpleOCSPServer + * @library /test/lib * @run main/othervm SSLSocketWithStapling */ @@ -63,8 +62,8 @@ import java.util.HashMap; import java.util.concurrent.TimeUnit; -import sun.security.testlibrary.SimpleOCSPServer; -import sun.security.testlibrary.CertificateBuilder; +import jdk.test.lib.security.SimpleOCSPServer; +import jdk.test.lib.security.CertificateBuilder; public class SSLSocketWithStapling { diff --git a/test/jdk/javax/net/ssl/Stapling/StapleEnableProps.java b/test/jdk/javax/net/ssl/Stapling/StapleEnableProps.java index 14651cf3db926..90b01d19c03b6 100644 --- a/test/jdk/javax/net/ssl/Stapling/StapleEnableProps.java +++ b/test/jdk/javax/net/ssl/Stapling/StapleEnableProps.java @@ -28,8 +28,7 @@ * @test * @bug 8145854 8153829 * @summary SSLContextImpl.statusResponseManager should be generated if required - * @library ../../../../java/security/testlibrary - * @build CertificateBuilder SimpleOCSPServer + * @library /test/lib * @run main/othervm StapleEnableProps */ @@ -49,8 +48,8 @@ import java.util.Objects; import java.util.concurrent.TimeUnit; -import sun.security.testlibrary.SimpleOCSPServer; -import sun.security.testlibrary.CertificateBuilder; +import jdk.test.lib.security.SimpleOCSPServer; +import jdk.test.lib.security.CertificateBuilder; public class StapleEnableProps { diff --git a/test/jdk/javax/net/ssl/TLSCommon/TLSTest.java b/test/jdk/javax/net/ssl/TLSCommon/TLSTest.java index d132271574912..0a9efc1ba07f0 100644 --- a/test/jdk/javax/net/ssl/TLSCommon/TLSTest.java +++ b/test/jdk/javax/net/ssl/TLSCommon/TLSTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -52,91 +52,91 @@ * @test * @bug 8205111 * @summary Test TLS with different types of supported keys. - * @run main/othervm TLSTest TLSv1.3 rsa_pkcs1_sha1 TLS_AES_128_GCM_SHA256 - * @run main/othervm TLSTest TLSv1.3 rsa_pkcs1_sha256 TLS_AES_128_GCM_SHA256 - * @run main/othervm TLSTest TLSv1.3 rsa_pkcs1_sha384 TLS_AES_128_GCM_SHA256 - * @run main/othervm TLSTest TLSv1.3 rsa_pkcs1_sha512 TLS_AES_128_GCM_SHA256 - * @run main/othervm TLSTest TLSv1.3 ec_rsa_pkcs1_sha256 TLS_AES_128_GCM_SHA256 - * @run main/othervm TLSTest TLSv1.3 ecdsa_sha1 TLS_AES_128_GCM_SHA256 - * @run main/othervm TLSTest TLSv1.3 ecdsa_secp384r1_sha384 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.3 rsa_pkcs1_sha1 TLS_AES_128_GCM_SHA256 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.3 rsa_pkcs1_sha256 TLS_AES_128_GCM_SHA256 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.3 rsa_pkcs1_sha384 TLS_AES_128_GCM_SHA256 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.3 rsa_pkcs1_sha512 TLS_AES_128_GCM_SHA256 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.3 ec_rsa_pkcs1_sha256 TLS_AES_128_GCM_SHA256 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.3 ecdsa_sha1 TLS_AES_128_GCM_SHA256 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.3 ecdsa_secp384r1_sha384 * TLS_AES_128_GCM_SHA256 - * @run main/othervm TLSTest TLSv1.3 ecdsa_secp521r1_sha512 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.3 ecdsa_secp521r1_sha512 * TLS_AES_128_GCM_SHA256 - * @run main/othervm TLSTest TLSv1.3 rsa_pss_rsae_sha256 TLS_AES_128_GCM_SHA256 - * @run main/othervm TLSTest TLSv1.3 rsa_pss_rsae_sha384 TLS_AES_128_GCM_SHA256 - * @run main/othervm TLSTest TLSv1.3 rsa_pss_rsae_sha512 TLS_AES_128_GCM_SHA256 - * @run main/othervm TLSTest TLSv1.3 rsa_pss_pss_sha256 TLS_AES_128_GCM_SHA256 - * @run main/othervm TLSTest TLSv1.3 rsa_pss_pss_sha384 TLS_AES_128_GCM_SHA256 - * @run main/othervm TLSTest TLSv1.3 rsa_pss_pss_sha512 TLS_AES_128_GCM_SHA256 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.3 rsa_pss_rsae_sha256 TLS_AES_128_GCM_SHA256 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.3 rsa_pss_rsae_sha384 TLS_AES_128_GCM_SHA256 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.3 rsa_pss_rsae_sha512 TLS_AES_128_GCM_SHA256 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.3 rsa_pss_pss_sha256 TLS_AES_128_GCM_SHA256 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.3 rsa_pss_pss_sha384 TLS_AES_128_GCM_SHA256 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.3 rsa_pss_pss_sha512 TLS_AES_128_GCM_SHA256 * - * @run main/othervm TLSTest TLSv1.3 rsa_pkcs1_sha1 TLS_AES_256_GCM_SHA384 - * @run main/othervm TLSTest TLSv1.3 rsa_pkcs1_sha256 TLS_AES_256_GCM_SHA384 - * @run main/othervm TLSTest TLSv1.3 rsa_pkcs1_sha384 TLS_AES_256_GCM_SHA384 - * @run main/othervm TLSTest TLSv1.3 rsa_pkcs1_sha512 TLS_AES_256_GCM_SHA384 - * @run main/othervm TLSTest TLSv1.3 ec_rsa_pkcs1_sha256 TLS_AES_256_GCM_SHA384 - * @run main/othervm TLSTest TLSv1.3 ecdsa_sha1 TLS_AES_256_GCM_SHA384 - * @run main/othervm TLSTest TLSv1.3 ecdsa_secp384r1_sha384 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.3 rsa_pkcs1_sha1 TLS_AES_256_GCM_SHA384 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.3 rsa_pkcs1_sha256 TLS_AES_256_GCM_SHA384 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.3 rsa_pkcs1_sha384 TLS_AES_256_GCM_SHA384 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.3 rsa_pkcs1_sha512 TLS_AES_256_GCM_SHA384 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.3 ec_rsa_pkcs1_sha256 TLS_AES_256_GCM_SHA384 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.3 ecdsa_sha1 TLS_AES_256_GCM_SHA384 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.3 ecdsa_secp384r1_sha384 * TLS_AES_256_GCM_SHA384 - * @run main/othervm TLSTest TLSv1.3 ecdsa_secp521r1_sha512 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.3 ecdsa_secp521r1_sha512 * TLS_AES_256_GCM_SHA384 - * @run main/othervm TLSTest TLSv1.3 rsa_pss_rsae_sha256 TLS_AES_256_GCM_SHA384 - * @run main/othervm TLSTest TLSv1.3 rsa_pss_rsae_sha384 TLS_AES_256_GCM_SHA384 - * @run main/othervm TLSTest TLSv1.3 rsa_pss_rsae_sha512 TLS_AES_256_GCM_SHA384 - * @run main/othervm TLSTest TLSv1.3 rsa_pss_pss_sha256 TLS_AES_256_GCM_SHA384 - * @run main/othervm TLSTest TLSv1.3 rsa_pss_pss_sha384 TLS_AES_256_GCM_SHA384 - * @run main/othervm TLSTest TLSv1.3 rsa_pss_pss_sha512 TLS_AES_256_GCM_SHA384 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.3 rsa_pss_rsae_sha256 TLS_AES_256_GCM_SHA384 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.3 rsa_pss_rsae_sha384 TLS_AES_256_GCM_SHA384 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.3 rsa_pss_rsae_sha512 TLS_AES_256_GCM_SHA384 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.3 rsa_pss_pss_sha256 TLS_AES_256_GCM_SHA384 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.3 rsa_pss_pss_sha384 TLS_AES_256_GCM_SHA384 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.3 rsa_pss_pss_sha512 TLS_AES_256_GCM_SHA384 * - * @run main/othervm TLSTest TLSv1.2 rsa_pkcs1_sha1 TLS_RSA_WITH_AES_128_CBC_SHA - * @run main/othervm TLSTest TLSv1.2 rsa_pkcs1_sha256 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.2 rsa_pkcs1_sha1 TLS_RSA_WITH_AES_128_CBC_SHA + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.2 rsa_pkcs1_sha256 * TLS_RSA_WITH_AES_128_CBC_SHA - * @run main/othervm TLSTest TLSv1.2 rsa_pkcs1_sha384 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.2 rsa_pkcs1_sha384 * TLS_RSA_WITH_AES_256_GCM_SHA384 - * @run main/othervm TLSTest TLSv1.2 rsa_pkcs1_sha512 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.2 rsa_pkcs1_sha512 * TLS_RSA_WITH_AES_128_GCM_SHA256 - * @run main/othervm TLSTest TLSv1.2 ec_rsa_pkcs1_sha256 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.2 ec_rsa_pkcs1_sha256 * TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 - * @run main/othervm TLSTest TLSv1.2 ecdsa_sha1 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.2 ecdsa_sha1 * TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 - * @run main/othervm TLSTest TLSv1.2 ecdsa_secp384r1_sha384 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.2 ecdsa_secp384r1_sha384 * TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 - * @run main/othervm TLSTest TLSv1.2 ecdsa_secp521r1_sha512 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.2 ecdsa_secp521r1_sha512 * TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA - * @run main/othervm TLSTest TLSv1.2 rsa_pss_rsae_sha256 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.2 rsa_pss_rsae_sha256 * TLS_RSA_WITH_AES_256_CBC_SHA256 - * @run main/othervm TLSTest TLSv1.2 rsa_pss_rsae_sha384 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.2 rsa_pss_rsae_sha384 * TLS_RSA_WITH_AES_256_CBC_SHA - * @run main/othervm TLSTest TLSv1.2 rsa_pss_rsae_sha512 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.2 rsa_pss_rsae_sha512 * TLS_RSA_WITH_AES_128_CBC_SHA256 - * @run main/othervm TLSTest TLSv1.2 rsa_pss_pss_sha256 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.2 rsa_pss_pss_sha256 * TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 - * @run main/othervm TLSTest TLSv1.2 rsa_pss_pss_sha384 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.2 rsa_pss_pss_sha384 * TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 - * @run main/othervm TLSTest TLSv1.2 rsa_pss_pss_sha512 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.2 rsa_pss_pss_sha512 * TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 * - * @run main/othervm TLSTest TLSv1.1 rsa_pkcs1_sha1 TLS_RSA_WITH_AES_128_CBC_SHA - * @run main/othervm TLSTest TLSv1.1 rsa_pkcs1_sha256 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.1 rsa_pkcs1_sha1 TLS_RSA_WITH_AES_128_CBC_SHA + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.1 rsa_pkcs1_sha256 * TLS_RSA_WITH_AES_256_CBC_SHA - * @run main/othervm TLSTest TLSv1.1 rsa_pkcs1_sha384 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.1 rsa_pkcs1_sha384 * TLS_RSA_WITH_AES_128_CBC_SHA - * @run main/othervm TLSTest TLSv1.1 rsa_pkcs1_sha512 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.1 rsa_pkcs1_sha512 * TLS_RSA_WITH_AES_256_CBC_SHA - * @run main/othervm TLSTest TLSv1.1 rsa_pss_rsae_sha256 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.1 rsa_pss_rsae_sha256 * TLS_RSA_WITH_AES_128_CBC_SHA - * @run main/othervm TLSTest TLSv1.1 rsa_pss_rsae_sha384 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.1 rsa_pss_rsae_sha384 * TLS_RSA_WITH_AES_256_CBC_SHA - * @run main/othervm TLSTest TLSv1.1 rsa_pss_rsae_sha512 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1.1 rsa_pss_rsae_sha512 * TLS_RSA_WITH_AES_128_CBC_SHA * - * @run main/othervm TLSTest TLSv1 rsa_pkcs1_sha1 TLS_RSA_WITH_AES_128_CBC_SHA - * @run main/othervm TLSTest TLSv1 rsa_pkcs1_sha256 TLS_RSA_WITH_AES_256_CBC_SHA - * @run main/othervm TLSTest TLSv1 rsa_pkcs1_sha384 TLS_RSA_WITH_AES_128_CBC_SHA - * @run main/othervm TLSTest TLSv1 rsa_pkcs1_sha512 TLS_RSA_WITH_AES_256_CBC_SHA - * @run main/othervm TLSTest TLSv1 rsa_pss_rsae_sha256 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1 rsa_pkcs1_sha1 TLS_RSA_WITH_AES_128_CBC_SHA + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1 rsa_pkcs1_sha256 TLS_RSA_WITH_AES_256_CBC_SHA + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1 rsa_pkcs1_sha384 TLS_RSA_WITH_AES_128_CBC_SHA + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1 rsa_pkcs1_sha512 TLS_RSA_WITH_AES_256_CBC_SHA + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1 rsa_pss_rsae_sha256 * TLS_RSA_WITH_AES_128_CBC_SHA - * @run main/othervm TLSTest TLSv1 rsa_pss_rsae_sha384 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1 rsa_pss_rsae_sha384 * TLS_RSA_WITH_AES_256_CBC_SHA - * @run main/othervm TLSTest TLSv1 rsa_pss_rsae_sha512 + * @run main/othervm -Djavax.net.debug=ssl,handshake TLSTest TLSv1 rsa_pss_rsae_sha512 * TLS_RSA_WITH_AES_128_CBC_SHA */ public class TLSTest { @@ -281,7 +281,7 @@ void doClientSide() throws Exception { keyType.getTrustedCert(), null, null, keyType.getKeyType()); SSLSocketFactory sslsf = ctx.getSocketFactory(); try (SSLSocket sslSocket - = (SSLSocket) sslsf.createSocket("localhost", serverPort)) { + = (SSLSocket) sslsf.createSocket(InetAddress.getLoopbackAddress(), serverPort)) { // Specify the client cipher suites sslSocket.setEnabledCipherSuites(new String[]{this.cipher}); sslSocket.setEnabledProtocols(new String[]{this.tlsProtocol}); diff --git a/test/jdk/javax/net/ssl/TLSv11/GenericBlockCipher.java b/test/jdk/javax/net/ssl/TLSv11/GenericBlockCipher.java index 24933838e0589..033356d47c3bf 100644 --- a/test/jdk/javax/net/ssl/TLSv11/GenericBlockCipher.java +++ b/test/jdk/javax/net/ssl/TLSv11/GenericBlockCipher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -174,8 +174,8 @@ void doClientSide() throws Exception { volatile Exception clientException = null; public static void main(String[] args) throws Exception { - // Re-enable TLSv1.1 since test depends on it. - SecurityUtils.removeFromDisabledTlsAlgs("TLSv1.1"); + // Re-enable TLSv1.1 and TLS_RSA_* since test depends on it. + SecurityUtils.removeFromDisabledTlsAlgs("TLSv1.1", "TLS_RSA_*"); String keyFilename = System.getProperty("test.src", ".") + "/" + pathToStores + diff --git a/test/jdk/javax/net/ssl/TLSv12/ProtocolFilter.java b/test/jdk/javax/net/ssl/TLSv12/ProtocolFilter.java index ec58bc74d0cf5..3291096af4852 100644 --- a/test/jdk/javax/net/ssl/TLSv12/ProtocolFilter.java +++ b/test/jdk/javax/net/ssl/TLSv12/ProtocolFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +28,7 @@ * @test * @bug 8052406 * @summary SSLv2Hello protocol may be filter out unexpectedly + * @library /test/lib * @run main/othervm ProtocolFilter */ @@ -35,6 +36,8 @@ import java.net.*; import javax.net.ssl.*; +import jdk.test.lib.security.SecurityUtils; + public class ProtocolFilter { /* @@ -156,6 +159,8 @@ void doClientSide() throws Exception { volatile Exception clientException = null; public static void main(String[] args) throws Exception { + // Re-enable TLS_RSA_* since test depends on it. + SecurityUtils.removeFromDisabledTlsAlgs("TLS_RSA_*"); String keyFilename = System.getProperty("test.src", ".") + "/" + pathToStores + "/" + keyStoreFile; diff --git a/test/jdk/javax/net/ssl/ciphersuites/DisabledAlgorithms.java b/test/jdk/javax/net/ssl/ciphersuites/DisabledAlgorithms.java index 1dd8be8478537..b2484be1142fb 100644 --- a/test/jdk/javax/net/ssl/ciphersuites/DisabledAlgorithms.java +++ b/test/jdk/javax/net/ssl/ciphersuites/DisabledAlgorithms.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 8076221 8211883 8279164 + * @bug 8076221 8211883 8279164 8245545 * @summary Check if weak cipher suites are disabled * @library /javax/net/ssl/templates * @modules jdk.crypto.ec @@ -110,7 +110,13 @@ public class DisabledAlgorithms { "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA", "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA", "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA", - "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA" + "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA", + "TLS_RSA_WITH_AES_256_GCM_SHA384", + "TLS_RSA_WITH_AES_128_GCM_SHA256", + "TLS_RSA_WITH_AES_256_CBC_SHA256", + "TLS_RSA_WITH_AES_128_CBC_SHA256", + "TLS_RSA_WITH_AES_256_CBC_SHA", + "TLS_RSA_WITH_AES_128_CBC_SHA" }; public static void main(String[] args) throws Exception { diff --git a/test/jdk/javax/net/ssl/sanity/ciphersuites/CheckCipherSuites.java b/test/jdk/javax/net/ssl/sanity/ciphersuites/CheckCipherSuites.java index caa96cdb224e2..847c69112a0c3 100644 --- a/test/jdk/javax/net/ssl/sanity/ciphersuites/CheckCipherSuites.java +++ b/test/jdk/javax/net/ssl/sanity/ciphersuites/CheckCipherSuites.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 4750141 4895631 8217579 8163326 8279164 + * @bug 4750141 4895631 8217579 8163326 8279164 8245545 * @summary Check enabled and supported ciphersuites are correct * @run main/othervm CheckCipherSuites default * @run main/othervm CheckCipherSuites limited @@ -99,12 +99,6 @@ public class CheckCipherSuites { "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", // deprecated - "TLS_RSA_WITH_AES_256_GCM_SHA384", - "TLS_RSA_WITH_AES_128_GCM_SHA256", - "TLS_RSA_WITH_AES_256_CBC_SHA256", - "TLS_RSA_WITH_AES_128_CBC_SHA256", - "TLS_RSA_WITH_AES_256_CBC_SHA", - "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_EMPTY_RENEGOTIATION_INFO_SCSV" }; @@ -124,9 +118,6 @@ public class CheckCipherSuites { "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", - "TLS_RSA_WITH_AES_128_GCM_SHA256", - "TLS_RSA_WITH_AES_128_CBC_SHA256", - "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_EMPTY_RENEGOTIATION_INFO_SCSV" }; @@ -194,12 +185,6 @@ public class CheckCipherSuites { "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", // deprecated - "TLS_RSA_WITH_AES_256_GCM_SHA384", - "TLS_RSA_WITH_AES_128_GCM_SHA256", - "TLS_RSA_WITH_AES_256_CBC_SHA256", - "TLS_RSA_WITH_AES_128_CBC_SHA256", - "TLS_RSA_WITH_AES_256_CBC_SHA", - "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_EMPTY_RENEGOTIATION_INFO_SCSV" }; @@ -219,9 +204,6 @@ public class CheckCipherSuites { "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", - "TLS_RSA_WITH_AES_128_GCM_SHA256", - "TLS_RSA_WITH_AES_128_CBC_SHA256", - "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_EMPTY_RENEGOTIATION_INFO_SCSV" }; diff --git a/test/jdk/javax/net/ssl/sanity/ciphersuites/SystemPropCipherSuitesOrder.java b/test/jdk/javax/net/ssl/sanity/ciphersuites/SystemPropCipherSuitesOrder.java index 2817e3400ab38..6492e6508de2e 100644 --- a/test/jdk/javax/net/ssl/sanity/ciphersuites/SystemPropCipherSuitesOrder.java +++ b/test/jdk/javax/net/ssl/sanity/ciphersuites/SystemPropCipherSuitesOrder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,6 +21,7 @@ * questions. */ import java.util.Arrays; +import java.util.stream.Stream; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLSocket; @@ -86,8 +87,20 @@ public static void main(String[] args) { clientcipherSuites = toArray(System.getProperty("jdk.tls.client.cipherSuites")); System.out.printf("SYSTEM PROPERTIES: ServerProp:%s - ClientProp:%s%n", - Arrays.deepToString(servercipherSuites), - Arrays.deepToString(clientcipherSuites)); + Arrays.deepToString(servercipherSuites), + Arrays.deepToString(clientcipherSuites)); + + // Re-enable TLS_RSA_* cipher suites if needed since test depends on it. + if (Stream.concat( + Arrays.stream( + servercipherSuites == null + ? new String[0] : servercipherSuites), + Arrays.stream( + clientcipherSuites == null + ? new String[0] : clientcipherSuites)) + .anyMatch(s -> s.startsWith("TLS_RSA_"))) { + SecurityUtils.removeFromDisabledTlsAlgs("TLS_RSA_*"); + } try { new SystemPropCipherSuitesOrder(args[0]).run(); diff --git a/test/jdk/javax/net/ssl/sanity/ciphersuites/TLSCipherSuitesOrder.java b/test/jdk/javax/net/ssl/sanity/ciphersuites/TLSCipherSuitesOrder.java index c2171d808891b..2ce46eb847131 100644 --- a/test/jdk/javax/net/ssl/sanity/ciphersuites/TLSCipherSuitesOrder.java +++ b/test/jdk/javax/net/ssl/sanity/ciphersuites/TLSCipherSuitesOrder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -58,6 +58,8 @@ public class TLSCipherSuitesOrder extends SSLSocketTemplate { private final String[] clientcipherSuites; public static void main(String[] args) { + // Re-enable TLS_RSA_* since test depends on it. + SecurityUtils.removeFromDisabledTlsAlgs("TLS_RSA_*"); PROTOCOL protocol = PROTOCOL.valueOf(args[0]); try { new TLSCipherSuitesOrder(protocol.getProtocol(), diff --git a/test/jdk/javax/net/ssl/templates/SSLEngineTemplate.java b/test/jdk/javax/net/ssl/templates/SSLEngineTemplate.java index 9c044f32ebf81..2c1cfaa44ef08 100644 --- a/test/jdk/javax/net/ssl/templates/SSLEngineTemplate.java +++ b/test/jdk/javax/net/ssl/templates/SSLEngineTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,8 @@ * @build SSLContextTemplate * @run main/othervm SSLEngineTemplate */ + +import java.util.Objects; import javax.net.ssl.*; import javax.net.ssl.SSLEngineResult.HandshakeStatus; import java.nio.ByteBuffer; @@ -264,4 +266,75 @@ static void checkTransfer(ByteBuffer a, ByteBuffer b) a.limit(a.capacity()); b.limit(b.capacity()); } + + /** + * Given a TLS record containing one or more handshake messages, return + * the specific handshake message as a ByteBuffer (a slice of the record) + * + * @param tlsRecord A ByteBuffer containing a TLS record. It is + * assumed that the position of the ByteBuffer is on the + * first byte of the TLS record header. + * @param hsMsgId The message identifier for the handshake message + * being sought. + * + * Message Identifiers + * + * @param isDtls Indicates whether DTLS protocol being used. + * @return a ByteBuffer containing the TLS handshake message. + * The position of the returned ByteBuffer will be on the + * first byte of the TLS handshake message data, + * immediately following the handshake header. + * If the message is not found, null will be returned. + * @throws SSLException if the incoming ByteBuffer does not contain + * a well-formed TLS message. + */ + protected static ByteBuffer extractHandshakeMsg( + ByteBuffer tlsRecord, int hsMsgId, boolean isDtls) + throws SSLException { + Objects.requireNonNull(tlsRecord); + tlsRecord.mark(); + + // Process the TLS record header + int type = Byte.toUnsignedInt(tlsRecord.get()); + int ver_major = Byte.toUnsignedInt(tlsRecord.get()); + int ver_minor = Byte.toUnsignedInt(tlsRecord.get()); + // Skip DTLS-specific bytes + if (isDtls) { + tlsRecord.position(tlsRecord.position() + 8); + } + int recLen = Short.toUnsignedInt(tlsRecord.getShort()); + + if (recLen > tlsRecord.remaining()) { + throw new SSLException("Incomplete record in buffer: " + + "Record length = " + recLen + + ", Remaining = " + + tlsRecord.remaining()); + } + + while (tlsRecord.hasRemaining()) { + // Grab the handshake message header. + int msgHdr = tlsRecord.getInt(); + int msgType = (msgHdr >> 24) & 0x000000FF; + int msgLen = msgHdr & 0x00FFFFFF; + // Skip DTLS-specific bytes + if (isDtls) { + tlsRecord.position(tlsRecord.position() + 8); + } + + if (msgType == hsMsgId) { + // Slice the buffer such that it contains the entire + // handshake message (less the handshake header). + ByteBuffer buf = tlsRecord.slice(tlsRecord.position(), msgLen); + tlsRecord.reset(); + return buf; + } else { + // Skip to the next handshake message, if there is one + tlsRecord.position(tlsRecord.position() + msgLen); + } + } + + tlsRecord.reset(); + return null; + } } diff --git a/test/jdk/javax/net/ssl/templates/SSLExampleCert.java b/test/jdk/javax/net/ssl/templates/SSLExampleCert.java index 0b82aed3b7bd8..46f69a62e41a5 100644 --- a/test/jdk/javax/net/ssl/templates/SSLExampleCert.java +++ b/test/jdk/javax/net/ssl/templates/SSLExampleCert.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/jdk/javax/net/ssl/templates/SSLSocketTemplate.java b/test/jdk/javax/net/ssl/templates/SSLSocketTemplate.java index 631577fd63cf9..73a3bcde81483 100644 --- a/test/jdk/javax/net/ssl/templates/SSLSocketTemplate.java +++ b/test/jdk/javax/net/ssl/templates/SSLSocketTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -168,7 +168,7 @@ protected void configureServerSocket(SSLServerSocket socket) { /* * What's the server address? null means binding to the wildcard. */ - protected volatile InetAddress serverAddress = null; + protected volatile InetAddress serverAddress = InetAddress.getLoopbackAddress(); /* * Define the server side of the test. @@ -177,13 +177,8 @@ protected void doServerSide() throws Exception { // kick start the server side service SSLContext context = createServerSSLContext(); SSLServerSocketFactory sslssf = context.getServerSocketFactory(); - InetAddress serverAddress = this.serverAddress; - SSLServerSocket sslServerSocket = serverAddress == null ? - (SSLServerSocket)sslssf.createServerSocket(serverPort) - : (SSLServerSocket)sslssf.createServerSocket(); - if (serverAddress != null) { - sslServerSocket.bind(new InetSocketAddress(serverAddress, serverPort)); - } + SSLServerSocket sslServerSocket = (SSLServerSocket)sslssf.createServerSocket( + serverPort, 0, serverAddress); configureServerSocket(sslServerSocket); serverPort = sslServerSocket.getLocalPort(); @@ -245,7 +240,7 @@ protected void doClientSide() throws Exception { // // The server side takes care of the issue if the server cannot // get started in 90 seconds. The client side would just ignore - // the test case if the serer is not ready. + // the test case if the server is not ready. boolean serverIsReady = serverCondition.await(90L, TimeUnit.SECONDS); if (!serverIsReady) { @@ -271,10 +266,8 @@ protected void doClientSide() throws Exception { try (SSLSocket sslSocket = (SSLSocket)sslsf.createSocket()) { try { configureClientSocket(sslSocket); - InetAddress serverAddress = this.serverAddress; - InetSocketAddress connectAddress = serverAddress == null - ? new InetSocketAddress(InetAddress.getLoopbackAddress(), serverPort) - : new InetSocketAddress(serverAddress, serverPort); + InetSocketAddress connectAddress = new InetSocketAddress(serverAddress, + serverPort); sslSocket.connect(connectAddress, 15000); } catch (IOException ioe) { // The server side may be impacted by naughty test cases or @@ -378,8 +371,9 @@ private void bootup() throws Exception { * Check various exception conditions. */ if ((local != null) && (remote != null)) { - // If both failed, return the curthread's exception. + // If both failed, return the current thread's exception. local.initCause(remote); + local.addSuppressed(remote); exception = local; } else if (local != null) { exception = local; diff --git a/test/jdk/javax/print/PrintServiceLookup/CountPrintServices.java b/test/jdk/javax/print/PrintServiceLookup/CountPrintServices.java index 5be9f0297d28d..4da1ed59a27e4 100644 --- a/test/jdk/javax/print/PrintServiceLookup/CountPrintServices.java +++ b/test/jdk/javax/print/PrintServiceLookup/CountPrintServices.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,25 +25,21 @@ import java.io.InputStreamReader; import javax.print.PrintService; import javax.print.PrintServiceLookup; -import javax.print.attribute.AttributeSet; -import javax.print.attribute.HashAttributeSet; -import javax.print.attribute.standard.PrinterName; +import java.io.IOException; + +import jtreg.SkippedException; /* * @test * @bug 8032693 * @key printer + * @library /test/lib/ + * @requires (os.family == "linux") * @summary Test that lpstat and JDK agree whether there are printers. */ public class CountPrintServices { public static void main(String[] args) throws Exception { - String os = System.getProperty("os.name").toLowerCase(); - System.out.println("OS is " + os); - if (!os.equals("linux")) { - System.out.println("Linux specific test. No need to continue"); - return; - } PrintService services[] = PrintServiceLookup.lookupPrintServices(null, null); if (services.length > 0) { @@ -51,7 +47,16 @@ public static void main(String[] args) throws Exception { return; } String[] lpcmd = { "lpstat", "-a" }; - Process proc = Runtime.getRuntime().exec(lpcmd); + Process proc; + try { + proc = Runtime.getRuntime().exec(lpcmd); + } catch (IOException e) { + if (e.getMessage().contains("No such file or directory")) { + throw new SkippedException("Cannot find lpstat"); + } else { + throw e; + } + } proc.waitFor(); InputStreamReader ir = new InputStreamReader(proc.getInputStream()); BufferedReader br = new BufferedReader(ir); @@ -66,4 +71,3 @@ public static void main(String[] args) throws Exception { } } } - diff --git a/test/jdk/javax/print/StreamPrintingOrientation.java b/test/jdk/javax/print/StreamPrintingOrientation.java index d7736aa4f620e..77dd8f4cae04f 100644 --- a/test/jdk/javax/print/StreamPrintingOrientation.java +++ b/test/jdk/javax/print/StreamPrintingOrientation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,95 +24,109 @@ /* * @test * @bug 4904236 - * @summary You would see a cross-platform print dialog being popped up. Check whether orientation is shown as LANDSCAPE. Click 'OK'. 'streamexample.ps' will be created in the same dir where this application was executed. Pass if the orientation in the ps file is landscape. - * @run main/manual StreamPrintingOrientation + * @key printer + * @summary StreamPrintService ignores the PrintReqAttrSet when printing through 2D Printing + * @run main StreamPrintingOrientation */ -import java.awt.*; -import java.awt.print.*; -import javax.print.*; -import javax.print.attribute.standard.*; -import javax.print.attribute.*; -import java.io.FileOutputStream; import java.io.File; -import java.util.Locale; +import java.io.FileOutputStream; +import java.nio.file.Files; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.print.PageFormat; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.awt.print.PrinterJob; +import javax.print.attribute.Attribute; +import javax.print.PrintService; +import javax.print.StreamPrintServiceFactory; +import javax.print.attribute.standard.Copies; +import javax.print.attribute.standard.JobName; +import javax.print.attribute.standard.OrientationRequested; +import javax.print.attribute.HashPrintRequestAttributeSet; + +public class StreamPrintingOrientation implements Printable { + + public static void main(String[] args) throws Exception { + StreamPrintingOrientation pd = new StreamPrintingOrientation(); + PrinterJob pj = PrinterJob.getPrinterJob(); + HashPrintRequestAttributeSet prSet = new HashPrintRequestAttributeSet(); + PrintService service = null; + + FileOutputStream fos = null; + String mType = "application/postscript"; -class StreamPrintingOrientation implements Printable { - /** - * Constructor - */ - public StreamPrintingOrientation() { - super(); + File fl = new File("stream_landscape.ps"); + fl.deleteOnExit(); + fos = new FileOutputStream(fl); + StreamPrintServiceFactory[] factories = PrinterJob.lookupStreamPrintServices(mType); + if (factories.length > 0) { + service = factories[0].getPrintService(fos); } - /** - * Starts the application. - */ - public static void main(java.lang.String[] args) { - StreamPrintingOrientation pd = new StreamPrintingOrientation(); - PrinterJob pj = PrinterJob.getPrinterJob(); - HashPrintRequestAttributeSet prSet = new HashPrintRequestAttributeSet(); - PrintService service = null; - FileOutputStream fos = null; - File f = null, f1 = null; - String mType = "application/postscript"; + if (service != null) { + System.out.println("Stream Print Service " + service); + pj.setPrintService(service); + } else { + throw new RuntimeException("No stream Print Service available."); + } + + pj.setPrintable(pd); + prSet.add(OrientationRequested.LANDSCAPE); + prSet.add(new Copies(1)); + prSet.add(new JobName("orientation test", null)); + System.out.println("open PrintDialog.."); - try { - f = new File("streamexample.ps"); - fos = new FileOutputStream(f); - StreamPrintServiceFactory[] factories = PrinterJob.lookupStreamPrintServices(mType); - if (factories.length > 0) - service = factories[0].getPrintService(fos); + System.out.println("\nValues in attr set passed to print method"); + Attribute attr[] = prSet.toArray(); + for (int x = 0; x < attr.length; x++) { + System.out.println("Name " + attr[x].getName() + " " + attr[x]); + } + System.out.println("About to print the data ..."); + if (service != null) { + System.out.println("TEST: calling Print"); + pj.print(prSet); + System.out.println("TEST: Printed"); + } - if (service != null) { - System.out.println("Stream Print Service "+service); - pj.setPrintService(service); - } else { - throw new RuntimeException("No stream Print Service available."); - } - } catch (Exception e) { - e.printStackTrace(); - } + File fp = new File("stream_portrait.ps"); + fp.deleteOnExit(); + fos = new FileOutputStream(fp); + if (factories.length > 0) { + service = factories[0].getPrintService(fos); + } - pj.setPrintable(pd); - prSet.add(OrientationRequested.LANDSCAPE); - prSet.add(new Copies(3)); - prSet.add(new JobName("orientation test", null)); - System.out.println("open PrintDialog.."); - if (pj.printDialog(prSet)) { - try { - System.out.println("\nValues in attr set passed to print method"); - Attribute attr[] = prSet.toArray(); - for (int x = 0; x < attr.length; x ++) { - System.out.println("Name "+attr[x].getName()+" "+attr[x]); - } - System.out.println("About to print the data ..."); - if (service != null) { - System.out.println("TEST: calling Print"); - pj.print(prSet); - System.out.println("TEST: Printed"); - } - } - catch (PrinterException pe) { - pe.printStackTrace(); - } - } + pj.setPrintService(service); + pj.setPrintable(pd); + prSet.add(OrientationRequested.PORTRAIT); + prSet.add(new Copies(1)); + prSet.add(new JobName("orientation test", null)); + if (service != null) { + pj.print(prSet); + } + if (Files.mismatch(fl.toPath(), fp.toPath()) == -1) { + throw new RuntimeException("Printing stream orientation is same " + + "for both PORTRAIT and LANDSCAPE"); } + } - //printable interface - public int print(Graphics g, PageFormat pf, int pi) throws PrinterException { + //printable interface + public int print(Graphics g, PageFormat pf, int pi) throws PrinterException { - if (pi > 0) { - return Printable.NO_SUCH_PAGE; - } - // Simply draw two rectangles - Graphics2D g2 = (Graphics2D)g; - g2.setColor(Color.black); - g2.translate(pf.getImageableX(), pf.getImageableY()); - System.out.println("StreamPrinting Test Width "+pf.getWidth()+" Height "+pf.getHeight()); - g2.drawRect(1,1,200,300); - g2.drawRect(1,1,25,25); - return Printable.PAGE_EXISTS; + if (pi > 0) { + return Printable.NO_SUCH_PAGE; } + // Simply draw two rectangles + Graphics2D g2 = (Graphics2D) g; + g2.setColor(Color.black); + g2.translate(pf.getImageableX(), pf.getImageableY()); + System.out.println("StreamPrinting Test Width " + pf.getWidth() + " Height " + pf.getHeight()); + g2.drawRect(1, 1, 200, 300); + g2.drawRect(1, 1, 25, 25); + return Printable.PAGE_EXISTS; + } } diff --git a/test/jdk/javax/print/attribute/PageRangesException.java b/test/jdk/javax/print/attribute/PageRangesException.java new file mode 100644 index 0000000000000..81f9eb1a59289 --- /dev/null +++ b/test/jdk/javax/print/attribute/PageRangesException.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.print.attribute.standard.PageRanges; + +/* + * @test + * @bug 4433126 4433096 + * @key printer + * @summary The line "ERROR: " should NOT appear. + * @run main PageRangesException + */ + +public class PageRangesException { + public static void main(String[] args) throws Exception { + // test 4433126 + try { + PageRanges pr = new PageRanges("0:22"); + throw new RuntimeException("ERROR: no exceptions"); + } catch (IllegalArgumentException ie) { + System.out.println("OKAY: IllegalArgumentException " + ie); + } + + // test 4433096 + try { + int[][] m = null; + PageRanges pr = new PageRanges(m); + throw new RuntimeException("ERROR: NullPointerException expected"); + } catch (IllegalArgumentException ie) { + throw new RuntimeException("ERROR: IllegalArgumentException", ie); + } catch (NullPointerException e) { + System.out.println("OKAY: NullPointerException"); + } + } +} diff --git a/test/jdk/javax/print/attribute/ServiceDlgPageRangeTest.java b/test/jdk/javax/print/attribute/ServiceDlgPageRangeTest.java index 5d38d4b1e11dc..f2982a13669bb 100644 --- a/test/jdk/javax/print/attribute/ServiceDlgPageRangeTest.java +++ b/test/jdk/javax/print/attribute/ServiceDlgPageRangeTest.java @@ -100,7 +100,7 @@ public static void main(java.lang.String[] args) throws Exception { } catch (InterruptedException e) { if (!testPassed && testGeneratedInterrupt) { throw new RuntimeException("PageRanges option is not disabled " - + "for for Non serv-formatted flvrs"); + + "for Non serv-formatted flvrs"); } } if (!testGeneratedInterrupt) { diff --git a/test/jdk/javax/rmi/ssl/SSLSocketParametersTest.java b/test/jdk/javax/rmi/ssl/SSLSocketParametersTest.java index 63d9332f3537e..6da3289458751 100644 --- a/test/jdk/javax/rmi/ssl/SSLSocketParametersTest.java +++ b/test/jdk/javax/rmi/ssl/SSLSocketParametersTest.java @@ -24,6 +24,7 @@ /* * @test * @bug 5016500 + * @library /test/lib/ * @summary Test SslRmi[Client|Server]SocketFactory SSL socket parameters. * @run main/othervm SSLSocketParametersTest 1 * @run main/othervm SSLSocketParametersTest 2 @@ -33,14 +34,15 @@ * @run main/othervm SSLSocketParametersTest 6 * @run main/othervm SSLSocketParametersTest 7 */ +import jdk.test.lib.Asserts; + import java.io.IOException; import java.io.File; import java.io.Serializable; -import java.net.ServerSocket; -import java.net.Socket; +import java.lang.ref.Reference; +import java.rmi.ConnectIOException; import java.rmi.Remote; import java.rmi.RemoteException; -import java.rmi.server.RMIClientSocketFactory; import java.rmi.server.RMIServerSocketFactory; import java.rmi.server.UnicastRemoteObject; import javax.net.ssl.SSLContext; @@ -50,121 +52,30 @@ public class SSLSocketParametersTest implements Serializable { public interface Hello extends Remote { - public String sayHello() throws RemoteException; + String sayHello() throws RemoteException; } - public class HelloImpl extends UnicastRemoteObject implements Hello { - - public HelloImpl(int port, - RMIClientSocketFactory csf, - RMIServerSocketFactory ssf) - throws RemoteException { - super(port, csf, ssf); - } - + public class HelloImpl implements Hello { public String sayHello() { return "Hello World!"; } - - public Remote runServer() throws IOException { - System.out.println("Inside HelloImpl::runServer"); - // Get a remote stub for this RMI object - // - Remote stub = toStub(this); - System.out.println("Stub = " + stub); - return stub; - } - } - - public class HelloClient { - - public void runClient(Remote stub) throws IOException { - System.out.println("Inside HelloClient::runClient"); - // "obj" is the identifier that we'll use to refer - // to the remote object that implements the "Hello" - // interface - Hello obj = (Hello) stub; - String message = obj.sayHello(); - System.out.println(message); - } - } - - public static class ClientFactory extends SslRMIClientSocketFactory { - - public ClientFactory() { - super(); - } - - public Socket createSocket(String host, int port) throws IOException { - System.out.println("ClientFactory::Calling createSocket(" + - host + "," + port + ")"); - return super.createSocket(host, port); - } - } - - public static class ServerFactory extends SslRMIServerSocketFactory { - - public ServerFactory() { - super(); - } - - public ServerFactory(String[] ciphers, - String[] protocols, - boolean need) { - super(ciphers, protocols, need); - } - - public ServerFactory(SSLContext context, - String[] ciphers, - String[] protocols, - boolean need) { - super(context, ciphers, protocols, need); - } - - public ServerSocket createServerSocket(int port) throws IOException { - System.out.println("ServerFactory::Calling createServerSocket(" + - port + ")"); - return super.createServerSocket(port); - } } - public void testRmiCommunication(RMIServerSocketFactory serverFactory, boolean expectException) { - - HelloImpl server = null; + public void testRmiCommunication(RMIServerSocketFactory serverSocketFactory) throws Exception { + HelloImpl server = new HelloImpl(); + Hello stub = (Hello)UnicastRemoteObject.exportObject(server, + 0, new SslRMIClientSocketFactory(), serverSocketFactory); try { - server = new HelloImpl(0, - new ClientFactory(), - serverFactory); - Remote stub = server.runServer(); - HelloClient client = new HelloClient(); - client.runClient(stub); - if (expectException) { - throw new RuntimeException("Test completed without throwing an expected exception."); - } - - } catch (IOException exc) { - if (!expectException) { - throw new RuntimeException("An error occurred during test execution", exc); - } else { - System.out.println("Caught expected exception: " + exc); - } - + String msg = stub.sayHello(); + Asserts.assertEquals("Hello World!", msg); + } finally { + Reference.reachabilityFence(server); } } - private static void testServerFactory(String[] cipherSuites, String[] protocol, String expectedMessage) throws Exception { - try { - new ServerFactory(SSLContext.getDefault(), + private static void testSslServerSocketFactory(String[] cipherSuites, String[] protocol) throws Exception { + new SslRMIServerSocketFactory(SSLContext.getDefault(), cipherSuites, protocol, false); - throw new RuntimeException( - "The expected exception for "+ expectedMessage + " was not thrown."); - } catch (IllegalArgumentException exc) { - // expecting an exception with a specific message - // anything else is an error - if (!exc.getMessage().toLowerCase().contains(expectedMessage)) { - throw exc; - } - } } public void runTest(int testNumber) throws Exception { @@ -172,34 +83,49 @@ public void runTest(int testNumber) throws Exception { switch (testNumber) { /* default constructor - default config */ - case 1 -> testRmiCommunication(new ServerFactory(), false); + case 1 -> + testRmiCommunication(new SslRMIServerSocketFactory()); /* non-default constructor - default config */ - case 2 -> testRmiCommunication(new ServerFactory(null, null, false), false); + case 2 -> + testRmiCommunication(new SslRMIServerSocketFactory(null, null, false)); /* needClientAuth=true */ - case 3 -> testRmiCommunication(new ServerFactory(null, null, null, true), false); + case 3 -> + testRmiCommunication(new SslRMIServerSocketFactory(null, null, null, true)); /* server side dummy_ciphersuite */ - case 4 -> - testServerFactory(new String[]{"dummy_ciphersuite"}, null, "unsupported ciphersuite"); + case 4 -> { + Exception exc = Asserts.assertThrows(IllegalArgumentException.class, + () -> testSslServerSocketFactory(new String[]{"dummy_ciphersuite"}, null)); + if (!exc.getMessage().toLowerCase().contains("unsupported ciphersuite")) { + throw exc; + } + } /* server side dummy_protocol */ - case 5 -> - testServerFactory(null, new String[]{"dummy_protocol"}, "unsupported protocol"); + case 5 -> { + Exception thrown = Asserts.assertThrows(IllegalArgumentException.class, + () -> testSslServerSocketFactory(null, new String[]{"dummy_protocol"})); + if (!thrown.getMessage().toLowerCase().contains("unsupported protocol")) { + throw thrown; + } + } /* client side dummy_ciphersuite */ case 6 -> { System.setProperty("javax.rmi.ssl.client.enabledCipherSuites", "dummy_ciphersuite"); - testRmiCommunication(new ServerFactory(), true); + Asserts.assertThrows(ConnectIOException.class, + () -> testRmiCommunication(new SslRMIServerSocketFactory())); } /* client side dummy_protocol */ case 7 -> { System.setProperty("javax.rmi.ssl.client.enabledProtocols", "dummy_protocol"); - testRmiCommunication(new ServerFactory(), true); + Asserts.assertThrows(ConnectIOException.class, + () -> testRmiCommunication(new SslRMIServerSocketFactory())); } default -> diff --git a/test/jdk/javax/security/auth/callback/PasswordCallback/CheckCleanerBound.java b/test/jdk/javax/security/auth/callback/PasswordCallback/CheckCleanerBound.java index 8f68773398c59..6d500fea65618 100644 --- a/test/jdk/javax/security/auth/callback/PasswordCallback/CheckCleanerBound.java +++ b/test/jdk/javax/security/auth/callback/PasswordCallback/CheckCleanerBound.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -55,4 +55,3 @@ public static void main(String[] args) throws Exception { } } } - diff --git a/test/jdk/javax/security/auth/callback/PasswordCallback/PasswordCleanup.java b/test/jdk/javax/security/auth/callback/PasswordCallback/PasswordCleanup.java index ea8b1d1c14596..64db7de2b1845 100644 --- a/test/jdk/javax/security/auth/callback/PasswordCallback/PasswordCleanup.java +++ b/test/jdk/javax/security/auth/callback/PasswordCallback/PasswordCleanup.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -49,4 +49,3 @@ public static void main(String[] args) throws Exception { } } } - diff --git a/test/jdk/javax/security/auth/login/TEST.properties b/test/jdk/javax/security/auth/login/TEST.properties deleted file mode 100644 index 908b451f8ea69..0000000000000 --- a/test/jdk/javax/security/auth/login/TEST.properties +++ /dev/null @@ -1,2 +0,0 @@ -# disabled till JDK-8219408 is fixed -allowSmartActionArgs=false diff --git a/test/jdk/javax/security/auth/login/modules/JaasModularClientTest.java b/test/jdk/javax/security/auth/login/modules/JaasModularClientTest.java index 987c0e2e66adf..edeb58c98d794 100644 --- a/test/jdk/javax/security/auth/login/modules/JaasModularClientTest.java +++ b/test/jdk/javax/security/auth/login/modules/JaasModularClientTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,19 +33,21 @@ import java.io.OutputStream; import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleDescriptor.Builder; -import jdk.internal.module.ModuleInfoWriter; import java.util.stream.Stream; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.util.JarUtils; +import jdk.test.lib.util.ModuleInfoWriter; /* * @test * @bug 8078813 8183310 * @summary Test custom JAAS login module with all possible modular option. + * @modules java.base/jdk.internal.org.objectweb.asm + * java.base/jdk.internal.org.objectweb.asm.commons + * java.base/jdk.internal.module * @library /test/lib - * @modules java.base/jdk.internal.module - * @build jdk.test.lib.util.JarUtils + * @build jdk.test.lib.util.JarUtils jdk.test.lib.util.ModuleInfoWriter * @build TestLoginModule JaasClient * @run main JaasModularClientTest false * @run main JaasModularClientTest true @@ -183,7 +185,7 @@ private void execute(String args) throws Exception { } return !s.isEmpty(); }).toArray(String[]::new); - OutputAnalyzer out = ProcessTools.executeTestJvm(safeArgs); + OutputAnalyzer out = ProcessTools.executeTestJava(safeArgs); // Handle response. if (out.getExitValue() != 0) { System.out.printf("OUTPUT: %s", out.getOutput()); diff --git a/test/jdk/javax/security/auth/login/modules/JaasModularDefaultHandlerTest.java b/test/jdk/javax/security/auth/login/modules/JaasModularDefaultHandlerTest.java index 5752c4e3c5d7d..3be7037c4329e 100644 --- a/test/jdk/javax/security/auth/login/modules/JaasModularDefaultHandlerTest.java +++ b/test/jdk/javax/security/auth/login/modules/JaasModularDefaultHandlerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,19 +32,21 @@ import java.io.OutputStream; import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleDescriptor.Builder; -import jdk.internal.module.ModuleInfoWriter; import java.util.stream.Stream; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.util.JarUtils; +import jdk.test.lib.util.ModuleInfoWriter; /* * @test * @bug 8151654 8183310 * @summary Test default callback handler with all possible modular option. + * @modules java.base/jdk.internal.org.objectweb.asm + * java.base/jdk.internal.org.objectweb.asm.commons + * java.base/jdk.internal.module * @library /test/lib - * @modules java.base/jdk.internal.module - * @build jdk.test.lib.util.JarUtils + * @build jdk.test.lib.util.JarUtils jdk.test.lib.util.ModuleInfoWriter * @build TestCallbackHandler TestLoginModule JaasClientWithDefaultHandler * @run main JaasModularDefaultHandlerTest */ @@ -167,7 +169,7 @@ private void execute(String args) throws Exception { } return !s.isEmpty(); }).toArray(String[]::new); - OutputAnalyzer out = ProcessTools.executeTestJvm(safeArgs); + OutputAnalyzer out = ProcessTools.executeTestJava(safeArgs); // Handle response. if (out.getExitValue() != 0) { System.out.printf("OUTPUT: %s", out.getOutput()); diff --git a/test/jdk/javax/sound/midi/BulkSoundBank/BulkSoundBank.java b/test/jdk/javax/sound/midi/BulkSoundBank/BulkSoundBank.java new file mode 100644 index 0000000000000..259b5b5283c4f --- /dev/null +++ b/test/jdk/javax/sound/midi/BulkSoundBank/BulkSoundBank.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.sound.midi.InvalidMidiDataException; +import javax.sound.midi.MidiSystem; +import java.io.ByteArrayInputStream; +import java.io.IOException; + +/** + * @test + * @bug 8350813 + * @summary Rendering of bulky sound bank from MIDI sequence can cause OutOfMemoryError. + * @run main/othervm -Xmx1g BulkSoundBank + */ + +public class BulkSoundBank { + static final byte[] midi = {77, 84, 104, 100, 0, 0, 0, 6, 0, 0, 0, 1, 1, + -32, 77, 84, 114, 107, 0, 0, 0, 50, 0, -1, 88, 4, 4, 2, 24, 8, 0, -1, + 81, 3, 7, -95, 32, 0, -112, 60, 64, -125, 96, -128, 60, 64, -125, -44, + -51, 32, -112, 48, 64, 1, -128, 48, 64, -127, -64, -45, 127, -112, 60, + 64, 1, -128, 60, 64, 0, -1, 47, 0}; + + public static void main(String[] args) throws IOException { + try (ByteArrayInputStream bis = new ByteArrayInputStream(midi)) { + MidiSystem.getSoundbank(bis); + throw new RuntimeException("Test should throw InvalidMidiDataException" + + " but it did not."); + } catch (InvalidMidiDataException imda) { + System.out.println("Caught InvalidMidiDataException as expected"); + } + } +} + diff --git a/test/jdk/javax/sound/midi/Sequencer/SequencerState.java b/test/jdk/javax/sound/midi/Sequencer/SequencerState.java index 01b49e2e47124..01b1c6a45d8a0 100644 --- a/test/jdk/javax/sound/midi/Sequencer/SequencerState.java +++ b/test/jdk/javax/sound/midi/Sequencer/SequencerState.java @@ -55,7 +55,7 @@ private static boolean hasSequencer() { public static void main(String[] args) throws Exception { - out("4913027: several Sequencer methods should should specify behaviour on closed Sequencer"); + out("4913027: several Sequencer methods should specify behaviour on closed Sequencer"); if (hasSequencer()) { boolean passed = testAll(); if (passed) { diff --git a/test/jdk/javax/swing/AbstractButton/bug6298940.java b/test/jdk/javax/swing/AbstractButton/bug6298940.java new file mode 100644 index 0000000000000..c2857c68ae305 --- /dev/null +++ b/test/jdk/javax/swing/AbstractButton/bug6298940.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6298940 + * @key headful + * @summary Tests that mnemonic keystroke fires an action + * @library /javax/swing/regtesthelpers + * @build Util + * @run main bug6298940 + */ + +import java.awt.Robot; +import java.awt.event.KeyEvent; +import java.util.concurrent.CountDownLatch; + +import javax.swing.ButtonModel; +import javax.swing.DefaultButtonModel; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +import static java.util.concurrent.TimeUnit.SECONDS; + +public final class bug6298940 { + private static JFrame frame; + + private static final CountDownLatch actionEvent = new CountDownLatch(1); + + private static void createAndShowGUI() { + ButtonModel model = new DefaultButtonModel(); + model.addActionListener(event -> { + System.out.println("ActionEvent"); + actionEvent.countDown(); + }); + model.setMnemonic('T'); + + JButton button = new JButton("Test"); + button.setModel(model); + + frame = new JFrame("bug6298940"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.add(button); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + frame.toFront(); + } + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + + SwingUtilities.invokeAndWait(bug6298940::createAndShowGUI); + + robot.waitForIdle(); + robot.delay(500); + + Util.hitMnemonics(robot, KeyEvent.VK_T); + + try { + if (!actionEvent.await(1, SECONDS)) { + throw new RuntimeException("Mnemonic didn't fire an action"); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } +} diff --git a/test/jdk/javax/swing/AncestorNotifier/4817630/bug4817630.java b/test/jdk/javax/swing/AncestorNotifier/4817630/bug4817630.java new file mode 100644 index 0000000000000..35c42f6e78be2 --- /dev/null +++ b/test/jdk/javax/swing/AncestorNotifier/4817630/bug4817630.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4817630 + * @summary AncestorEvent ancestorAdded thrown at JFrame creation, not at show() + * @key headful + * @run main bug4817630 + */ + +import java.lang.reflect.InvocationTargetException; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.SwingUtilities; +import javax.swing.event.AncestorEvent; +import javax.swing.event.AncestorListener; + +public class bug4817630 { + + JFrame fr; + + volatile boolean ancestorAdded = false; + volatile boolean passed = true; + + public void init() { + fr = new JFrame("bug4817630"); + JLabel label = new JLabel("Label"); + + label.addAncestorListener(new AncestorListener() { + public void ancestorAdded(AncestorEvent e) { + if (!fr.isVisible()) { + setPassed(false); + } + synchronized (bug4817630.this) { + ancestorAdded = true; + bug4817630.this.notifyAll(); + } + } + public void ancestorRemoved(AncestorEvent e) { + } + public void ancestorMoved(AncestorEvent e) { + } + }); + + fr.setLocationRelativeTo(null); + fr.getContentPane().add(label); + fr.pack(); + fr.setVisible(true); + } + + public void start() { + try { + synchronized (bug4817630.this) { + while (!ancestorAdded) { + bug4817630.this.wait(); + } + } + } catch(Exception e) { + throw new RuntimeException("Test failed because of " + + e.getLocalizedMessage()); + } + } + + public void destroy() { + if (fr != null) { + fr.setVisible(false); + fr.dispose(); + } + if (!isPassed()) { + throw new RuntimeException("ancestorAdded() method shouldn't be " + + "called before the frame is shown."); + } + } + + synchronized void setPassed(boolean passed) { + this.passed = passed; + } + + synchronized boolean isPassed() { + return passed; + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + bug4817630 test = new bug4817630(); + try { + SwingUtilities.invokeAndWait(test::init); + test.start(); + } finally { + SwingUtilities.invokeAndWait(test::destroy); + } + } +} diff --git a/test/jdk/javax/swing/BoxLayout/4191948/bug4191948.java b/test/jdk/javax/swing/BoxLayout/4191948/bug4191948.java new file mode 100644 index 0000000000000..a7a1f792341ab --- /dev/null +++ b/test/jdk/javax/swing/BoxLayout/4191948/bug4191948.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4191948 + * @summary BoxLayout doesn't ignore invisible components + * @key headful + * @run main bug4191948 + */ + +import java.awt.BorderLayout; +import java.lang.reflect.InvocationTargetException; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; + +public class bug4191948 { + JFrame frame; + JPanel p; + JButton foo1; + JButton foo2; + JButton foo3; + + public void init() { + frame = new JFrame("bug4191948"); + p = new JPanel(); + p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS)); + foo1 = (JButton)p.add(new JButton("Foo1")); + foo2 = (JButton)p.add(new JButton("Foo2")); + foo3 = (JButton)p.add(new JButton("Foo3")); + + foo2.setVisible(false); + frame.setLocationRelativeTo(null); + frame.setLayout(new BorderLayout()); + frame.add(p, BorderLayout.CENTER); + frame.pack(); + frame.setVisible(true); + } + + public void start() { + try { + int totalWidth = p.getPreferredSize().width; + int foo1Width = foo1.getPreferredSize().width; + int foo2Width = foo2.getPreferredSize().width; + int foo3Width = foo3.getPreferredSize().width; + if (totalWidth >= (foo1Width + foo2Width + foo3Width)) { + throw new RuntimeException("Panel is too wide"); + } + } finally { + if (frame != null) { + frame.setVisible(false); + frame.dispose(); + } + } + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + bug4191948 test = new bug4191948(); + SwingUtilities.invokeAndWait(test::init); + SwingUtilities.invokeAndWait(test::start); + } +} diff --git a/test/jdk/javax/swing/ComponentInputMap/4248723/bug4248723.java b/test/jdk/javax/swing/ComponentInputMap/4248723/bug4248723.java new file mode 100644 index 0000000000000..0218b2fbc108f --- /dev/null +++ b/test/jdk/javax/swing/ComponentInputMap/4248723/bug4248723.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4248723 + * @summary Tests that ComponentInputMap doesn't throw NPE when deserializing + * @run main bug4248723 + */ + +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import javax.swing.ComponentInputMap; +import javax.swing.JButton; +import javax.swing.KeyStroke; + +public class bug4248723 { + public static Object serializeAndDeserialize(Object toWrite) + throws ClassNotFoundException, IOException { + ByteArrayOutputStream ostream = new ByteArrayOutputStream(); + ObjectOutputStream p = new ObjectOutputStream(ostream); + p.writeObject(toWrite); + p.flush(); + byte[] data = ostream.toByteArray(); + ostream.close(); + + ByteArrayInputStream istream = new ByteArrayInputStream(data); + ObjectInputStream q = new ObjectInputStream(istream); + Object retValue = q.readObject(); + istream.close(); + return retValue; + } + + public static void main(String[] argv) { + ComponentInputMap cim = new ComponentInputMap(new JButton()); + cim.put(KeyStroke.getKeyStroke( + KeyEvent.VK_B, InputEvent.CTRL_DOWN_MASK), "A"); + try { + cim = (ComponentInputMap)serializeAndDeserialize(cim); + } catch (ClassNotFoundException|IOException ignore) { + // Should not cause test to fail so silently ignore these + } + } +} diff --git a/test/jdk/javax/swing/DataTransfer/DragOverFeedbackTest.java b/test/jdk/javax/swing/DataTransfer/DragOverFeedbackTest.java new file mode 100644 index 0000000000000..824f651a1a097 --- /dev/null +++ b/test/jdk/javax/swing/DataTransfer/DragOverFeedbackTest.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4514071 + * @summary Tests that JTable, JList and JTree provide drag-over feedback. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DragOverFeedbackTest + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.datatransfer.DataFlavor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import javax.swing.BorderFactory; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JTable; +import javax.swing.JTree; +import javax.swing.TransferHandler; + +public class DragOverFeedbackTest { + private static final String INSTRUCTIONS = """ + This test is designed to make sure that JTable, JTree, and JList + provide visual feedback when a DnD drag operation occurs over them. + + Click on the label where it says "DRAG FROM HERE" and begin dragging. + Drag over each of the three components (JTable, JTree, JList). + While you're dragging over them, they should visually indicate the + location where a drop would occur. This visual indication may use the + selection but could be some other visual. + + If above is true press PASS else press FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(DragOverFeedbackTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static final TransferHandler handler = new TransferHandler() { + public boolean canImport(JComponent comp, DataFlavor[] flavors) { + return true; + } + }; + + private static JFrame createTestUI() { + JFrame frame = new JFrame("DragOverFeedbackTest"); + final JLabel label = new JLabel("DRAG FROM HERE"); + label.setPreferredSize(new Dimension(400, 25)); + label.setTransferHandler(new TransferHandler("text")); + label.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent me) { + label.getTransferHandler().exportAsDrag(label, me, TransferHandler.COPY); + } + }); + JTable table = new JTable( + new String[][] {{"one"}, {"two"}, {"three"}, {"four"}}, + new String[] {"1"}); + table.setRowSelectionInterval(1, 1); + table.setTransferHandler(handler); + + JList list = new JList(new String[] {"one", "two", "three", "four"}); + list.setSelectedIndex(1); + list.setTransferHandler(handler); + + JTree tree = new JTree(); + tree.setSelectionRow(1); + tree.setTransferHandler(handler); + + frame.add(label, BorderLayout.NORTH); + + JPanel wrapper = new JPanel(); + wrapper.setLayout(new GridLayout(3, 1)); + table.setBorder(BorderFactory.createLineBorder(Color.BLACK)); + wrapper.add(table); + list.setBorder(BorderFactory.createLineBorder(Color.BLACK)); + wrapper.add(list); + tree.setBorder(BorderFactory.createLineBorder(Color.BLACK)); + wrapper.add(tree); + frame.add(wrapper); + frame.setSize(500, 500); + return frame; + } +} diff --git a/test/jdk/javax/swing/DataTransfer/ListDragOverFeedbackTest.java b/test/jdk/javax/swing/DataTransfer/ListDragOverFeedbackTest.java new file mode 100644 index 0000000000000..b41327a106ed6 --- /dev/null +++ b/test/jdk/javax/swing/DataTransfer/ListDragOverFeedbackTest.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4546134 + * @summary Tests that JList shows the right drop location when it has multiple columns. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ListDragOverFeedbackTest + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.datatransfer.DataFlavor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import javax.swing.BorderFactory; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.TransferHandler; + +public class ListDragOverFeedbackTest { + private static final String INSTRUCTIONS = """ + JList should provide visual feedback when a DnD drag operation is + occurring over it. This test is to check that it provides the + feedback about the drop location correctly. + + Click on the label where it says "DRAG FROM HERE" and begin dragging. + Drag over each column in each of the three JLists and make sure that + the drop location indicated is appropriate for the mouse location. For + instance, if the mouse is in the first column, the drop location should + also be indicated in that first column. + + If above is true press PASS else press FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(ListDragOverFeedbackTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static final TransferHandler handler = new TransferHandler() { + public boolean canImport(JComponent comp, DataFlavor[] flavors) { + return true; + } + }; + + private static JFrame createTestUI() { + String[] vals = new String[] { + "one", "two", "three", "four", "five", "six", "seven", "eight", + "nine", "ten", "eleven", "twelve", "thirteen", "fourteen"}; + + JFrame frame = new JFrame("ListDragOverFeedbackTest"); + final JLabel label = new JLabel("DRAG FROM HERE"); + label.setPreferredSize(new Dimension(400, 25)); + label.setTransferHandler(new TransferHandler("text")); + label.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent me) { + label.getTransferHandler().exportAsDrag(label, me, + TransferHandler.COPY); + } + }); + + JList list1 = new JList(vals); + list1.setTransferHandler(handler); + list1.setBorder(BorderFactory.createLineBorder(Color.BLACK)); + + JList list2 = new JList(vals); + list2.setLayoutOrientation(JList.VERTICAL_WRAP); + list2.setTransferHandler(handler); + list2.setBorder(BorderFactory.createLineBorder(Color.BLACK)); + + JList list3 = new JList(vals); + list3.setLayoutOrientation(JList.HORIZONTAL_WRAP); + list3.setTransferHandler(handler); + list3.setBorder(BorderFactory.createLineBorder(Color.BLACK)); + + JPanel wrapper = new JPanel(); + wrapper.setLayout(new GridLayout(3, 1)); + wrapper.add(list1); + wrapper.add(list2); + wrapper.add(list3); + + frame.add(label, BorderLayout.NORTH); + frame.add(wrapper); + frame.setSize(400, 500); + return frame; + } +} diff --git a/test/jdk/javax/swing/DataTransfer/bug4655513.java b/test/jdk/javax/swing/DataTransfer/bug4655513.java new file mode 100644 index 0000000000000..c9f49f98939eb --- /dev/null +++ b/test/jdk/javax/swing/DataTransfer/bug4655513.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @key headful + * @bug 4655513 + * @summary TransferHandler doesn't recognize ACTION_LINK + as a valid drop action + * @library /javax/swing/regtesthelpers + * @build Util + * @run main bug4655513 + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Robot; +import java.awt.datatransfer.StringSelection; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureRecognizer; +import java.awt.dnd.DragSource; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; + +public class bug4655513 { + private static final String LINK_URL = "http://www.example.com"; + private static volatile JEditorPane editor; + private static volatile JLabel dragSource; + private static JFrame frame; + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + SwingUtilities.invokeAndWait(bug4655513::createAndShowGUI); + robot.waitForIdle(); + robot.delay(1000); + + Point dragStartLoc = Util.getCenterPoint(dragSource); + Point dragEndLoc = Util.getCenterPoint(editor); + robot.mouseMove(dragStartLoc.x, dragStartLoc.y); + robot.mousePress(MouseEvent.BUTTON1_DOWN_MASK); + for (int y = dragStartLoc.y; y < dragEndLoc.y; y += 3) { + robot.mouseMove(dragStartLoc.x, y); + robot.delay(50); + } + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.delay(500); + + SwingUtilities.invokeAndWait(() -> { + if (!editor.getText().contains(LINK_URL)) { + throw new RuntimeException("Test Failed! Drag & Drop did not work."); + } + }); + } finally { + SwingUtilities.invokeAndWait(frame::dispose); + } + } + + private static void createAndShowGUI() { + frame = new JFrame("Bug4655513 - Data Transfer"); + dragSource = new JLabel("To Test DnD, drag this label."); + dragSource.setForeground(Color.RED); + dragSource.setPreferredSize(new Dimension(250, 50)); + frame.add(dragSource, BorderLayout.NORTH); + + editor = new JEditorPane("text/plain", "Drop here."); + editor.setPreferredSize(new Dimension(250, 50)); + frame.add(new JScrollPane(editor), BorderLayout.CENTER); + + DragSource ds = new DragSource(); + DragGestureRecognizer rec = + ds.createDefaultDragGestureRecognizer(dragSource, + DnDConstants.ACTION_LINK, + dge -> dge.startDrag(null, new StringSelection(LINK_URL))); + frame.setSize(300, 150); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } +} diff --git a/test/jdk/javax/swing/DefaultBoundedRangeModel/4297953/bug4297953.java b/test/jdk/javax/swing/DefaultBoundedRangeModel/4297953/bug4297953.java new file mode 100644 index 0000000000000..a19487490720b --- /dev/null +++ b/test/jdk/javax/swing/DefaultBoundedRangeModel/4297953/bug4297953.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4297953 + * @summary Tests that DefaultBoundedRangeModel doesn't zero out the + * extent value when maximum changes + * @run main bug4297953 + */ + +import javax.swing.JScrollBar; + +public class bug4297953 { + public static void main(String[] args) { + JScrollBar sb = new JScrollBar(JScrollBar.HORIZONTAL, 90, 10, 0, 100); + sb.setMaximum(80); + if (sb.getVisibleAmount() != 10) { + throw new RuntimeException("Failed: extent is " + sb.getVisibleAmount()); + } + } +} diff --git a/test/jdk/javax/swing/DefaultButtonModel/4097723/bug4097723.java b/test/jdk/javax/swing/DefaultButtonModel/4097723/bug4097723.java new file mode 100644 index 0000000000000..5ffebe3e8ff28 --- /dev/null +++ b/test/jdk/javax/swing/DefaultButtonModel/4097723/bug4097723.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4097723 + * @summary Tests that method DefaultButtonModel.getGroup() exists + * @run main bug4097723 + */ + +import javax.swing.ButtonGroup; +import javax.swing.DefaultButtonModel; + +public class bug4097723 { + public static void main(String[] argv) { + DefaultButtonModel dbm = new DefaultButtonModel(); + ButtonGroup group = new ButtonGroup(); + dbm.setGroup(group); + ButtonGroup g = dbm.getGroup(); + if (g != group) { + throw new RuntimeException("Failure: getGroup() returned wrong thing"); + } + } +} diff --git a/test/jdk/javax/swing/DefaultListCellRenderer/4180943/bug4180943.java b/test/jdk/javax/swing/DefaultListCellRenderer/4180943/bug4180943.java new file mode 100644 index 0000000000000..24a6257c155b6 --- /dev/null +++ b/test/jdk/javax/swing/DefaultListCellRenderer/4180943/bug4180943.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 4180943 + * @summary Extra borders created by DefaultListCellRenderer + * @run main bug4180943 + */ + +import javax.swing.DefaultListCellRenderer; + +public class bug4180943 { + public static void main(String[] argv) { + DefaultListCellRenderer lcr1 = new DefaultListCellRenderer(); + DefaultListCellRenderer lcr2 = new DefaultListCellRenderer(); + if (lcr1.getBorder() != lcr2.getBorder()) { + throw new RuntimeException("Extra borders created by DefaultListCellRenderer"); + } + } +} diff --git a/test/jdk/javax/swing/DefaultListModel/4466250/bug4466250.java b/test/jdk/javax/swing/DefaultListModel/4466250/bug4466250.java new file mode 100644 index 0000000000000..16c3899ea6d35 --- /dev/null +++ b/test/jdk/javax/swing/DefaultListModel/4466250/bug4466250.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 4466250 + * @summary DefaultListModel.removeRange does not throw IllegalArgumentException + * @run main bug4466250 +*/ + +import javax.swing.DefaultListModel; +import javax.swing.JLabel; + +public class bug4466250 { + public static void main(String[] args) { + DefaultListModel model = new DefaultListModel(); + int size = 16; + for (int i = 0; i < size; i++ ) { + model.addElement(new JLabel("wow")); + } + + try { + model.removeRange(3, 1); + throw new RuntimeException("IllegalArgumentException has not been thrown"); + } catch (IllegalArgumentException e) { + } + } +} diff --git a/test/jdk/javax/swing/DefaultListSelectionModel/4140619/bug4140619.java b/test/jdk/javax/swing/DefaultListSelectionModel/4140619/bug4140619.java new file mode 100644 index 0000000000000..3ea55270b7a0e --- /dev/null +++ b/test/jdk/javax/swing/DefaultListSelectionModel/4140619/bug4140619.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4140619 + * @summary Breaks SINGLE_SELECTION in DefaultListSelectionModel.setLeadSelectionIndex() + * @run main bug4140619 + */ + +import javax.swing.DefaultListSelectionModel; +import javax.swing.ListSelectionModel; + +public class bug4140619 { + public static void main(String[] args) { + DefaultListSelectionModel selection = new DefaultListSelectionModel(); + selection.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + selection.setSelectionInterval(10, 10); + selection.removeSelectionInterval(10, 10); + selection.setLeadSelectionIndex(2); + selection.setLeadSelectionIndex(30); + selection.setLeadSelectionIndex(5); + + if (selection.getMinSelectionIndex()!=5 + || selection.getMaxSelectionIndex()!=5) { + throw new RuntimeException("DefaultListSelectionModel: breaks SINGLE_SELECTION " + + "in setLeadSelectionIndex()"); + } + } +} diff --git a/test/hotspot/jtreg/vmTestbase/vm/share/transform/AbstractClassFileTransformer.java b/test/jdk/javax/swing/DefaultListSelectionModel/4177723/bug4177723.java similarity index 55% rename from test/hotspot/jtreg/vmTestbase/vm/share/transform/AbstractClassFileTransformer.java rename to test/jdk/javax/swing/DefaultListSelectionModel/4177723/bug4177723.java index 95906789fda31..c247429dc198d 100644 --- a/test/hotspot/jtreg/vmTestbase/vm/share/transform/AbstractClassFileTransformer.java +++ b/test/jdk/javax/swing/DefaultListSelectionModel/4177723/bug4177723.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -20,25 +20,32 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package vm.share.transform; -import java.lang.instrument.ClassFileTransformer; -import java.lang.instrument.IllegalClassFormatException; -import java.security.ProtectionDomain; +/* @test + * @bug 4177723 + * @summary ListSelectionEvents fired on model changes affecting JList selection + * @run main bug4177723 + */ + +import javax.swing.DefaultListModel; +import javax.swing.JList; -public abstract class AbstractClassFileTransformer - implements ClassFileTransformer { - protected abstract boolean shouldBeTransformed(String name); +public class bug4177723 { + static int count = 0; + + public static void main (String[] args) { + DefaultListModel model = new DefaultListModel(); + for (int i = 0; i < 10; i++) { + model.addElement("item " + i); + } - protected abstract byte[] transformClass(byte[] bytes); + JList list = new JList(model); + list.addListSelectionListener(e -> count++); - @Override - public byte[] transform(ClassLoader loader, String className, - Class classBeingRedefined, ProtectionDomain protectionDomain, - byte[] classfileBuffer) throws IllegalClassFormatException { - if (shouldBeTransformed(className)) { - return transformClass(classfileBuffer); + list.getSelectionModel().setSelectionInterval(3, 8); + model.removeRange(4, 7); + if (count != 2) { + throw new RuntimeException("ListSelectionEvent wasn't generated"); } - return null; } } diff --git a/test/jdk/javax/swing/ImageIcon/4827074/bug4827074.java b/test/jdk/javax/swing/ImageIcon/4827074/bug4827074.java new file mode 100644 index 0000000000000..44a80b520804a --- /dev/null +++ b/test/jdk/javax/swing/ImageIcon/4827074/bug4827074.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 4827074 + * @summary ImageIcon serialization does not preload restored images + * @run main bug4827074 + */ + +import javax.swing.ImageIcon; +import java.awt.Image; +import java.awt.Panel; +import java.awt.image.MemoryImageSource; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +public class bug4827074 extends Panel { + + static ImageIcon testIcon = null; + private volatile static boolean passed = false; + + public void init() { + testIcon = new TestImageIcon(); + ImageIcon icon = saveAndLoad(testIcon); + + if (!passed) { + throw new RuntimeException("Image was not loaded properly"); + } + } + + synchronized static void setPassed(boolean p) { + passed = p; + } + + static ImageIcon saveAndLoad(ImageIcon ii) { + ImageIcon _ii = null; + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(baos); + out.writeObject(ii); + out.flush(); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream in = new ObjectInputStream(bais); + _ii = (ImageIcon)in.readObject(); + } catch (Exception ex) { + ex.printStackTrace(); + } + return _ii; + } + + class TestImageIcon extends ImageIcon { + public TestImageIcon() { + super(); + setImage(buildImage()); + } + + private Image buildImage() { + int w = 32, h = 32; + float halfW = w / 2 , halfH = h / 2; + int col = 0xff0000; + int[] pixels = new int[w * h]; + for(int y = 0; y < h; y++) { + for(int x = 0; x < w; x++) { + float cx = 1f - (float)x / halfW; + float cy = 1f - (float)y / halfH; + double ray = Math.sqrt(cx * cx + cy * cy); + pixels[y * w + x] = ray < 1 ? col | (255 - (int)(ray * 255)) << 24:0; + } + } + MemoryImageSource mis = new MemoryImageSource(w, h, pixels, 0, w); + Image image = createImage(mis); + return image; + } + + protected void loadImage(Image image) { + super.loadImage(image); + if (testIcon != null && image != testIcon.getImage()) { + setPassed(true); + } + } + } + + public static void main(String[] args) { + bug4827074 bug = new bug4827074(); + bug.init(); + } +} diff --git a/test/jdk/javax/swing/InputVerifier/bug4774166.java b/test/jdk/javax/swing/InputVerifier/bug4774166.java new file mode 100644 index 0000000000000..644845611b928 --- /dev/null +++ b/test/jdk/javax/swing/InputVerifier/bug4774166.java @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.FlowLayout; +import java.awt.event.KeyEvent; +import java.lang.reflect.InvocationTargetException; +import javax.swing.InputVerifier; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.JWindow; +import javax.swing.SwingUtilities; + +import static java.awt.event.InputEvent.BUTTON1_DOWN_MASK; + +/* + * @test + * @bug 4774166 + * @key headful + * @summary InputVerifier should be called after a window loses and then regains focus + * @library /javax/swing/regtesthelpers + * @build JRobot + * @run main bug4774166 + */ + +class TestPanel extends JPanel { + JTextField tf1 = null; + JTextField tf2 = null; + volatile boolean verifierCalled; + + public void init() { + tf1 = new JTextField(10); + tf2 = new JTextField(10); + + InputVerifier verifier = new InputVerifier() { + public boolean verify(JComponent input) { + verifierCalled = true; + return false; + } + }; + + setLayout(new FlowLayout()); + tf1.setInputVerifier(verifier); + add(tf1); + add(tf2); + validate(); + } +} + +public class bug4774166 { + private static JRobot robot = JRobot.getRobot(); + + JFrame testframe; + JFrame testwindowframe; + JFrame customframe; + JWindow testwindow; + JDialog testdialog; + JTextField frametf1, frametf2, windowtf1, windowtf2, dialogtf1, dialogtf2; + TestPanel testpanel; + + volatile boolean isFocused; + volatile boolean verifierCalled; + + public void setupGUI() { + testframe = new JFrame("Test 4774166"); + testframe.setLayout(new FlowLayout()); + testframe.setBounds(100, 100, 200, 100); + + testwindowframe = new JFrame("Owner of JWindow"); + testwindowframe.setBounds(0, 0, 0, 0); + + testwindow = new JWindow(testwindowframe); + testwindow.setBounds(175, 325, 200, 100); + testwindow.setLayout(new FlowLayout()); + + testdialog = new JDialog((JFrame)null, "Test dialog"); + testdialog.setBounds(420, 100, 200, 100); + testdialog.setLayout(new FlowLayout()); + + InputVerifier verifier = new InputVerifier() { + public boolean verify(JComponent input) { + verifierCalled = true; + return false; + } + }; + + frametf1 = new JTextField(10); + frametf2 = new JTextField(10); + frametf1.setInputVerifier(verifier); + testframe.add(frametf1); + testframe.add(frametf2); + testframe.setVisible(true); + + windowtf1 = new JTextField(10); + windowtf2 = new JTextField(10); + windowtf1.setInputVerifier(verifier); + testwindow.add(windowtf1); + testwindow.add(windowtf2); + testwindowframe.setVisible(true); + testwindow.setVisible(true); + + dialogtf1 = new JTextField(10); + dialogtf2 = new JTextField(10); + dialogtf1.setInputVerifier(verifier); + testdialog.add(dialogtf1); + testdialog.add(dialogtf2); + testdialog.setVisible(true); + + customframe = new JFrame("Frame with custom panel"); + customframe.setLayout(new FlowLayout()); + testpanel = new TestPanel(); + testpanel.init(); + + customframe.add(testpanel); + customframe.setBounds(420, 250, 200, 100); + customframe.pack(); + customframe.setVisible(true); + } + + public void performTest() throws InterruptedException, InvocationTargetException { + robot.setAutoDelay(100); + robot.delay(2000); + + robot.clickMouseOn(frametf1, BUTTON1_DOWN_MASK); + robot.hitKey(KeyEvent.VK_A); + robot.hitKey(KeyEvent.VK_B); + robot.hitKey(KeyEvent.VK_C); + robot.hitKey(KeyEvent.VK_D); + robot.hitKey(KeyEvent.VK_E); + + robot.clickMouseOn(windowtf1, BUTTON1_DOWN_MASK); + robot.hitKey(KeyEvent.VK_F); + robot.hitKey(KeyEvent.VK_G); + robot.hitKey(KeyEvent.VK_H); + robot.hitKey(KeyEvent.VK_I); + robot.hitKey(KeyEvent.VK_J); + + robot.clickMouseOn(dialogtf1, BUTTON1_DOWN_MASK); + robot.hitKey(KeyEvent.VK_K); + robot.hitKey(KeyEvent.VK_L); + robot.hitKey(KeyEvent.VK_M); + robot.hitKey(KeyEvent.VK_N); + robot.hitKey(KeyEvent.VK_O); + + robot.clickMouseOn(testpanel.tf1, BUTTON1_DOWN_MASK); + robot.hitKey(KeyEvent.VK_P); + robot.hitKey(KeyEvent.VK_Q); + robot.hitKey(KeyEvent.VK_R); + robot.hitKey(KeyEvent.VK_S); + robot.hitKey(KeyEvent.VK_T); + + verifierCalled = false; + robot.clickMouseOn(frametf2, BUTTON1_DOWN_MASK); + robot.delay(2000); + SwingUtilities.invokeAndWait(() -> { + isFocused = frametf1.isFocusOwner(); + }); + if (!isFocused) { + throw new RuntimeException("Focus error. Test failed!"); + } + if (!verifierCalled) { + throw new RuntimeException("Verifier was not called upon regaining focus"); + } + + verifierCalled = false; + robot.clickMouseOn(windowtf2, BUTTON1_DOWN_MASK); + robot.delay(2000); + SwingUtilities.invokeAndWait(() -> { + isFocused = windowtf1.isFocusOwner(); + }); + if (!isFocused) { + throw new RuntimeException("Focus error. Test failed!"); + } + if (!verifierCalled) { + throw new RuntimeException("Verifier was not called upon regaining focus"); + } + + testpanel.verifierCalled = false; + robot.clickMouseOn(testpanel.tf2, BUTTON1_DOWN_MASK); + robot.delay(2000); + SwingUtilities.invokeAndWait(() -> { + isFocused = testpanel.tf1.isFocusOwner(); + }); + if (!isFocused) { + throw new RuntimeException("Focus error. Test failed!"); + } + if (!testpanel.verifierCalled) { + throw new RuntimeException("Verifier was not called upon regaining focus"); + } + + verifierCalled = false; + robot.clickMouseOn(dialogtf2, BUTTON1_DOWN_MASK); + robot.delay(2000); + SwingUtilities.invokeAndWait(() -> { + isFocused = dialogtf1.isFocusOwner(); + }); + if (!isFocused) { + throw new RuntimeException("Focus error. Test failed!"); + } + if (!verifierCalled) { + throw new RuntimeException("Verifier was not called upon regaining focus"); + } + } + + public void cleanupGUI() { + if (testframe != null) { + testframe.dispose(); + } + if (testwindowframe != null) { + testwindowframe.dispose(); + } + if (testwindow != null) { + testwindow.dispose(); + } + if (customframe != null) { + customframe.dispose(); + } + if (testdialog != null) { + testdialog.dispose(); + } + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + bug4774166 b = new bug4774166(); + SwingUtilities.invokeAndWait(b::setupGUI); + try { + b.performTest(); + } finally { + SwingUtilities.invokeAndWait(b::cleanupGUI); + } + } +} diff --git a/test/jdk/javax/swing/JButton/4796987/bug4796987.java b/test/jdk/javax/swing/JButton/4796987/bug4796987.java index 62d11fc18c08b..bfa6ef8d129ef 100644 --- a/test/jdk/javax/swing/JButton/4796987/bug4796987.java +++ b/test/jdk/javax/swing/JButton/4796987/bug4796987.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,67 +26,91 @@ * @bug 4796987 * @key headful * @requires (os.family == "windows") - * @summary XP Only: JButton.setBorderPainted() does not work with XP L&F - * @author Alexander Scherbatiy + * @summary Verify JButton.setBorderPainted(false) removes border + * for Windows visual styles (Windows XP and later) * @library ../../regtesthelpers - * @library /test/lib - * @modules java.desktop/com.sun.java.swing.plaf.windows - * java.desktop/sun.awt - * @build jdk.test.lib.OSVersion jdk.test.lib.Platform * @build Util * @run main bug4796987 */ -import jdk.test.lib.Platform; -import jdk.test.lib.OSVersion; -import java.awt.*; -import javax.swing.*; -import com.sun.java.swing.plaf.windows.WindowsLookAndFeel; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +import javax.imageio.ImageIO; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; public class bug4796987 { private static JButton button1; private static JButton button2; private static JFrame frame; + private static JPanel panel; public static void main(String[] args) throws Exception { try { - if (Platform.isWindows() - && OSVersion.current().equals(OSVersion.WINDOWS_XP)) { - UIManager.setLookAndFeel(new WindowsLookAndFeel()); - testButtonBorder(); - } + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + testButtonBorder(); } finally { - if (frame != null) SwingUtilities.invokeAndWait(() -> frame.dispose()); + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); } } private static void testButtonBorder() throws Exception { Robot robot = new Robot(); - robot.setAutoDelay(50); - SwingUtilities.invokeAndWait(new Runnable() { - - public void run() { - createAndShowGUI(); - } - }); + SwingUtilities.invokeAndWait(bug4796987::createAndShowGUI); + robot.waitForIdle(); + robot.delay(500); + // Hover over button1 + Point b1Center = Util.getCenterPoint(button1); + robot.mouseMove(b1Center.x, b1Center.y); robot.waitForIdle(); - Thread.sleep(500); - Point p1 = Util.getCenterPoint(button1); - Point p2 = Util.getCenterPoint(button2); + Rectangle panelBounds = Util.invokeOnEDT(() -> + new Rectangle(panel.getLocationOnScreen(), + panel.getSize())); + BufferedImage image = robot.createScreenCapture(panelBounds); + + final Point p1 = Util.invokeOnEDT(() -> getCenterPoint(button1)); + final Point p2 = Util.invokeOnEDT(() -> getCenterPoint(button2)); - Color color = robot.getPixelColor(p1.x, p2.x); - for (int dx = p1.x; dx < p2.x - p1.x; dx++) { - robot.mouseMove(p1.x + dx, p1.y); - if (!color.equals(robot.getPixelColor(p1.x + dx, p1.y))) { + final int color = image.getRGB(p1.x, p1.y); + for (int dx = 0; p1.x + dx < p2.x; dx++) { + if (color != image.getRGB(p1.x + dx, p1.y)) { + System.err.println("Wrong color at " + (p1.x + dx) + ", " + p1.y + + " - expected " + Integer.toHexString(color)); + saveImage(image); throw new RuntimeException("Button has border and background!"); } } } + /** + * {@return the center point of a button relative to its parent} + * @param button the button to calculate the center point + */ + private static Point getCenterPoint(JButton button) { + Point location = button.getLocation(); + Dimension size = button.getSize(); + location.translate(size.width / 2, size.height / 2); + return location; + } + private static JButton getButton() { JButton button = new JButton(); button.setBorderPainted(false); @@ -95,18 +119,24 @@ private static JButton getButton() { } private static void createAndShowGUI() { - frame = new JFrame("Test"); + frame = new JFrame("bug4796987"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(200, 200); - JButton button = new JButton(); - button.setBorder(null); - - JPanel panel = new JPanel(new BorderLayout(50, 50)); + panel = new JPanel(new BorderLayout(50, 50)); panel.add(getButton(), BorderLayout.CENTER); panel.add(button1 = getButton(), BorderLayout.WEST); panel.add(button2 = getButton(), BorderLayout.EAST); frame.getContentPane().add(panel); + frame.setLocationRelativeTo(null); frame.setVisible(true); } + + private static void saveImage(BufferedImage image) { + try { + ImageIO.write(image, "png", + new File("frame.png")); + } catch (IOException ignored) { + } + } } diff --git a/test/jdk/javax/swing/JButton/DefaultButtonLeak.java b/test/jdk/javax/swing/JButton/DefaultButtonLeak.java new file mode 100644 index 0000000000000..5e03902e64bea --- /dev/null +++ b/test/jdk/javax/swing/JButton/DefaultButtonLeak.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.GridLayout; +import java.lang.reflect.InvocationTargetException; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; + +/* + * @test + * @bug 4134035 + * @key headful + * @summary Ensure default button reference is removed from the RootPane + * when a hierarchy containing the RootPane's default button is removed + * from the RootPane. (a memory leak) + * @run main DefaultButtonLeak + */ + +public class DefaultButtonLeak { + public static void main(String[] args) throws InterruptedException, InvocationTargetException { + SwingUtilities.invokeAndWait(() -> { + JFrame frame = new JFrame("DefaultButtonLeak"); + try { + JPanel bigPanel = new JPanel(); + bigPanel.setLayout(new GridLayout(10, 10)); + for (int i = 0; i < 100; i++) { + JButton button = new JButton("Button" + i); + bigPanel.add(button); + if (i == 0) { + frame.getRootPane().setDefaultButton(button); + } + } + frame.add(bigPanel, BorderLayout.CENTER); + frame.pack(); + frame.setVisible(true); + frame.remove(bigPanel); + if (frame.getRootPane().getDefaultButton() != null) { + throw new RuntimeException("RootPane default button reference not removed."); + } + } finally { + frame.setVisible(false); + frame.dispose(); + } + }); + } +} diff --git a/test/jdk/javax/swing/JButton/TestMnemonicAction.java b/test/jdk/javax/swing/JButton/TestMnemonicAction.java new file mode 100644 index 0000000000000..b26e2a4933f87 --- /dev/null +++ b/test/jdk/javax/swing/JButton/TestMnemonicAction.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @key headful + * @bug 4850101 + * @summary Verifies if Setting mnemonic to VK_F4 underlines the letter S. + * @run main TestMnemonicAction + */ + +import java.io.File; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.event.KeyEvent; +import java.awt.image.BufferedImage; +import javax.imageio.ImageIO; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +public class TestMnemonicAction { + private static JFrame f; + private static JButton b; + + public static boolean compareBufferedImages(BufferedImage bufferedImage0, + BufferedImage bufferedImage1) { + int width = bufferedImage0.getWidth(); + int height = bufferedImage0.getHeight(); + + if (width != bufferedImage1.getWidth() || height != bufferedImage1.getHeight()) { + return false; + } + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + if (bufferedImage0.getRGB(x, y) != bufferedImage1.getRGB(x, y)) { + return false; + } + } + } + + return true; + } + + public static void main(String[] args) throws Exception { + + SwingUtilities.invokeAndWait(() -> { + f = new JFrame(); + b = new JButton("Shutdown"); + b.setMnemonic(KeyEvent.VK_F4); + b.setPreferredSize(new Dimension(100, 25)); + b.setToolTipText("Shutdown"); + f.getContentPane().add(b); + f.setDefaultCloseOperation(f.EXIT_ON_CLOSE); + f.setUndecorated(true); + f.setLocationRelativeTo(null); + f.pack(); + f.setVisible(true); + }); + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(1000); + Point p = b.getLocationOnScreen(); + BufferedImage imgmnemonic = robot.createScreenCapture( + new Rectangle(p.x, p.y, b.getWidth(), b.getHeight())); + robot.delay(1000); + SwingUtilities.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + SwingUtilities.invokeAndWait(() -> { + f = new JFrame(); + b = new JButton("Shutdown"); + b.setPreferredSize(new Dimension(100, 25)); + f.getContentPane().add(b); + f.setDefaultCloseOperation(f.EXIT_ON_CLOSE); + f.setUndecorated(true); + f.setLocationRelativeTo(null); + f.pack(); + f.setVisible(true); + }); + robot.waitForIdle(); + robot.delay(1000); + p = b.getLocationOnScreen(); + BufferedImage buttonimage = robot.createScreenCapture( + new Rectangle(p.x, p.y, b.getWidth(), b.getHeight())); + robot.delay(1000); + SwingUtilities.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + if (!compareBufferedImages(imgmnemonic, buttonimage)) { + ImageIO.write(imgmnemonic, "png", new File("imgmnemonic.png")); + ImageIO.write(buttonimage, "png", new File("buttonimage.png")); + throw new RuntimeException("F4 mnemonic underlines S"); + } + } +} diff --git a/test/jdk/javax/swing/JButton/bug4151763.java b/test/jdk/javax/swing/JButton/bug4151763.java new file mode 100644 index 0000000000000..d04b623ec4eb1 --- /dev/null +++ b/test/jdk/javax/swing/JButton/bug4151763.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4151763 + * @summary Tests that button icon is not drawn upon button border + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame jtreg.SkippedException + * @run main/manual bug4151763 + */ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.UIManager; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.LineBorder; +import jtreg.SkippedException; + +public class bug4151763 { + private static final int IMAGE_SIZE = 150; + private static final String INSTRUCTIONS = """ + Verify that image icon is NOT painted outside + the black rectangle. + + If above is true press PASS else FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug4151763::createAndShowUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createAndShowUI() { + try { + UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel"); + } catch (Exception e) { + throw new SkippedException("Unsupported LaF", e); + } + + JFrame frame = new JFrame("bug4151763"); + final JButton b = new JButton(createImageIcon()); + b.setBorder(new CompoundBorder( + new EmptyBorder(20, 20, 20, 20), + new LineBorder(Color.BLACK))); + b.setPreferredSize(new Dimension(100, 100)); + + frame.setLayout(new FlowLayout()); + frame.add(b); + frame.setSize(400, 300); + return frame; + } + + private static ImageIcon createImageIcon() { + BufferedImage redImg = new BufferedImage(IMAGE_SIZE, IMAGE_SIZE, + BufferedImage.TYPE_INT_RGB); + Graphics2D g = redImg.createGraphics(); + g.setColor(Color.RED); + g.fillRect(0, 0, IMAGE_SIZE, IMAGE_SIZE); + g.dispose(); + return new ImageIcon(redImg); + } +} diff --git a/test/jdk/javax/swing/JButton/bug4385611.java b/test/jdk/javax/swing/JButton/bug4385611.java new file mode 100644 index 0000000000000..b7360f273601b --- /dev/null +++ b/test/jdk/javax/swing/JButton/bug4385611.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.image.BufferedImage; +import java.lang.reflect.InvocationTargetException; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; + +/* + * @test + * @bug 4385611 8078655 + * @requires (os.family == "windows") + * @summary The button's preferred width/height calculation. + * @run main bug4385611 + */ + +public class bug4385611 { + static JButton bt1, bt2; + static final ImageIcon icon32x32 = generateImageIcon(); + static final Dimension DIM_32X32 = new Dimension(32, 32); + static final Dimension DIM_33X33 = new Dimension(33, 33); + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + SwingUtilities.invokeAndWait(() -> { + try { + UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); + bt1 = new JButton(icon32x32); + bt1.setMargin(new Insets(0, 0, 0, 0)); + bt1.setBorder(null); + bt1.setFocusPainted(true); + + bt2 = new JButton(icon32x32); + bt2.setMargin(new Insets(0, 0, 0, 0)); + bt2.setBorder(null); + bt2.setFocusPainted(false); + + if (!bt1.getPreferredSize().equals(DIM_32X32) || + !bt2.getPreferredSize().equals(DIM_32X32)) { + throw new RuntimeException("The button's preferred size should be 32x32"); + } + } catch (Exception e) { + throw new RuntimeException("Can not initialize Metal LnF", e); + } + + try { + UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); + bt1.updateUI(); + bt1.setBorder(null); + bt2.updateUI(); + bt2.setBorder(null); + if (!bt1.getPreferredSize().equals(DIM_33X33) || + !bt2.getPreferredSize().equals(DIM_32X32)) { + throw new RuntimeException("The button's preferred size should be " + + "33x33 and 32x32 correspondingly."); + } + } catch (Exception e) { + throw new RuntimeException("Can not initialize Windows LnF", e); + } + }); + } + + private static ImageIcon generateImageIcon() { + BufferedImage image = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB); + Graphics g = image.createGraphics(); + g.setColor(Color.YELLOW); + g.fillRect(0, 0, 32, 32); + g.dispose(); + return new ImageIcon(image); + } +} diff --git a/test/jdk/javax/swing/JButton/bug4415505.java b/test/jdk/javax/swing/JButton/bug4415505.java new file mode 100644 index 0000000000000..58ba56e1b74e8 --- /dev/null +++ b/test/jdk/javax/swing/JButton/bug4415505.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4415505 + * @requires (os.family == "windows") + * @summary Tests JButton appearance under Windows LAF + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4415505 + */ + +import java.awt.FlowLayout; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JToggleButton; +import javax.swing.UIManager; + +public class bug4415505 { + private static final String INSTRUCTIONS = """ + +

    This test is for Windows LaF. + Press the button named "Button" using mouse and check that it has + "pressed" look. It should look like the selected toggle button + near it.

    + +

    If above is true press PASS else FAIL.

    + + """; + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); + + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .rows(5) + .columns(40) + .testUI(bug4415505::createAndShowUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createAndShowUI() { + JButton button = new JButton("Button"); + button.setFocusPainted(false); + JToggleButton tbutton = new JToggleButton("ToggleButton"); + tbutton.setSelected(true); + + JFrame jFrame = new JFrame("bug4415505"); + jFrame.setLayout(new FlowLayout(FlowLayout.CENTER)); + jFrame.add(button); + jFrame.add(tbutton); + jFrame.setSize(300, 100); + return jFrame; + } +} diff --git a/test/jdk/javax/swing/JButton/bug4978274.java b/test/jdk/javax/swing/JButton/bug4978274.java new file mode 100644 index 0000000000000..6b152259caaed --- /dev/null +++ b/test/jdk/javax/swing/JButton/bug4978274.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4978274 + * @summary Tests that JButton is painted with same visible height + * as toggle buttons + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4978274 + */ + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JToggleButton; +import javax.swing.UIManager; +import javax.swing.border.EmptyBorder; +import javax.swing.plaf.metal.MetalLookAndFeel; +import javax.swing.plaf.metal.OceanTheme; + +public class bug4978274 { + private static final String INSTRUCTIONS = """ + The toggle buttons must be painted to the same visible + height as button. In addition to that verify the following: + + a) All three buttons - "Button", "Toggle Btn" and + "Selected Toggle Btn" have the same border. + + b) Verify that when "Button" is pressed and moused over + it has the EXACT same border as "Toggle Btn" and + "Selected Toggle Btn" on press & mouse over. + + c) Click to the test window (panel) to disable/enable all + three buttons. In disabled state verify that all three + buttons have the exact same border. + + If all of the above conditions are true press PASS, else FAIL. + """; + + public static void main(String[] args) throws Exception { + MetalLookAndFeel.setCurrentTheme(new OceanTheme()); + UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); + + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(createAndShowUI()) + .build() + .awaitAndCheck(); + } + + private static JFrame createAndShowUI() { + JFrame frame = new JFrame("bug4978274"); + frame.setLayout(new BorderLayout()); + + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + panel.setBorder(new EmptyBorder(12, 12, 12, 12)); + JButton jButton = new JButton("Button"); + JToggleButton jToggleButton = new JToggleButton("Selected Toggle Btn"); + jToggleButton.setSelected(true); + + panel.add(jButton); + panel.add(new JToggleButton("Toggle Btn")); + panel.add(jToggleButton); + + panel.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent event) { + jButton.setEnabled(!jButton.isEnabled()); + jToggleButton.setEnabled(jButton.isEnabled()); + for(int i = 0; i < panel.getComponentCount(); i++) { + panel.getComponent(i).setEnabled(jButton.isEnabled()); + } + } + }); + + frame.add(panel); + frame.pack(); + return frame; + } +} diff --git a/test/jdk/javax/swing/JCheckBox/4449413/bug4449413.java b/test/jdk/javax/swing/JCheckBox/4449413/bug4449413.java index 1efcfe58a31b9..d854961ea0699 100644 --- a/test/jdk/javax/swing/JCheckBox/4449413/bug4449413.java +++ b/test/jdk/javax/swing/JCheckBox/4449413/bug4449413.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,6 @@ /* @test * @bug 4449413 * @summary Tests that checkbox and radiobuttons' check marks are visible when background is black - * @author Ilya Boyandin * @run main/manual bug4449413 */ @@ -56,8 +55,16 @@ public class bug4449413 extends JFrame { + private static boolean isWindowsLF; + + private static String INSTRUCTIONS_WINDOWSLF = + "There are eight controls, JCheckBox/JRadioButton with black background\n" + + "and JRadioButtonMenuItem/JCheckboxMenuItem with gray background\n"; + private static final String INSTRUCTIONS = - "There are eight controls with black backgrounds.\n" + + "There are eight controls with black backgrounds.\n"; + + private static final String INSTRUCTIONS_COMMON = "Four enabled (on the left side) and four disabled (on the right side)\n" + "checkboxes and radiobuttons.\n\n" + "1. If at least one of the controls' check marks is not visible:\n" + @@ -82,6 +89,8 @@ boolean isMetalLookAndFeel() { } public static void main(String[] args) throws Exception { + isWindowsLF = "Windows".equals(UIManager.getLookAndFeel().getID()); + SwingUtilities.invokeLater(() -> { instance = new bug4449413(); instance.createAndShowGUI(); @@ -150,8 +159,10 @@ public void addComponentsToPane() { JTextArea instructionArea = new JTextArea( isMetalLookAndFeel() - ? INSTRUCTIONS + INSTRUCTIONS_ADDITIONS_METAL - : INSTRUCTIONS + ? INSTRUCTIONS + INSTRUCTIONS_COMMON + INSTRUCTIONS_ADDITIONS_METAL + : isWindowsLF + ? (INSTRUCTIONS_WINDOWSLF + INSTRUCTIONS_COMMON) + : (INSTRUCTIONS + INSTRUCTIONS_COMMON) ); instructionArea.setEditable(false); @@ -189,7 +200,13 @@ static AbstractButton createButton(int enabled, int type) { }; b.setOpaque(true); - b.setBackground(Color.black); + if (isWindowsLF + && ((b instanceof JRadioButtonMenuItem) + || (b instanceof JCheckBoxMenuItem))) { + b.setBackground(Color.lightGray); + } else { + b.setBackground(Color.black); + } b.setForeground(Color.white); b.setEnabled(enabled == 1); b.setSelected(true); diff --git a/test/jdk/javax/swing/JColorChooser/Test4193384.java b/test/jdk/javax/swing/JColorChooser/Test4193384.java index 826e9893696ed..b32006e905c30 100644 --- a/test/jdk/javax/swing/JColorChooser/Test4193384.java +++ b/test/jdk/javax/swing/JColorChooser/Test4193384.java @@ -51,7 +51,7 @@ private static void test(Color[] colors) { float[] hsb = new float[3]; for (int i = 0; i < colors.length; i++) { Color color = colors[i]; - // Make sure sure that there wasn't a regression + // Make sure that there wasn't a regression // in java.awt.Color and the conversion methods Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), hsb); if (!color.equals(Color.getHSBColor(hsb[0], hsb[1], hsb[2]))) { diff --git a/test/jdk/javax/swing/JColorChooser/Test6977726.java b/test/jdk/javax/swing/JColorChooser/Test6977726.java index a79931c93eb55..1ec72036f14d3 100644 --- a/test/jdk/javax/swing/JColorChooser/Test6977726.java +++ b/test/jdk/javax/swing/JColorChooser/Test6977726.java @@ -41,7 +41,14 @@ public static void main(String[] args) throws Exception { String instructions = """ Check that there is a panel with "Text Preview Panel" text and with title "Preview" in the JColorChooser. - Test passes if the panel is as described, test fails otherwise."""; + Test passes if the panel is as described, test fails otherwise. + + Note: "Preview" title is not applicable for GTK Look and Feel."""; + + // In case this test is run with GTK L&F, the preview panel title + // is missing due to the "ColorChooser.showPreviewPanelText" property + // which is set to "Boolean.FALSE" for GTK L&F. Test instructions are + // modified to reflect that "Preview" title is not applicable for GTK L&F. PassFailJFrame.builder() .title("Test6977726") diff --git a/test/jdk/javax/swing/JComboBox/6406264/bug6406264.java b/test/jdk/javax/swing/JComboBox/6406264/bug6406264.java index 75de8c97e2eef..6435e81e2ac7a 100644 --- a/test/jdk/javax/swing/JComboBox/6406264/bug6406264.java +++ b/test/jdk/javax/swing/JComboBox/6406264/bug6406264.java @@ -4,9 +4,7 @@ * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/test/jdk/javax/swing/JComboBox/6559152/bug6559152.java b/test/jdk/javax/swing/JComboBox/6559152/bug6559152.java index 5540bb306148c..c0747f8b1e47d 100644 --- a/test/jdk/javax/swing/JComboBox/6559152/bug6559152.java +++ b/test/jdk/javax/swing/JComboBox/6559152/bug6559152.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -48,8 +48,8 @@ public class bug6559152 { private static JFrame frame; private static JComboBox cb; private static Robot robot; - private static Point p = null; - private static Dimension d; + private static volatile Point p = null; + private static volatile Dimension d; public static void main(String[] args) throws Exception { robot = new Robot(); @@ -84,7 +84,7 @@ static void blockTillDisplayed(JComponent comp) throws Exception { } private static void setupUI() { - frame = new JFrame(); + frame = new JFrame("bug6559152"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); DefaultTableModel model = new DefaultTableModel(1, 1); diff --git a/test/jdk/javax/swing/JComboBox/8182031/ComboPopupTest.java b/test/jdk/javax/swing/JComboBox/8182031/ComboPopupTest.java index 9321bd154be34..5bbf613268967 100644 --- a/test/jdk/javax/swing/JComboBox/8182031/ComboPopupTest.java +++ b/test/jdk/javax/swing/JComboBox/8182031/ComboPopupTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -46,22 +46,6 @@ public class ComboPopupTest { private volatile Point p = null; private volatile Dimension d = null; - void blockTillDisplayed(JComponent comp) throws Exception { - while (p == null) { - try { - SwingUtilities.invokeAndWait(() -> { - p = comp.getLocationOnScreen(); - d = comboBox.getSize(); - }); - } catch (IllegalStateException e) { - try { - Thread.sleep(1000); - } catch (InterruptedException ie) { - } - } - } - } - public static void main(String[] args) throws Exception { new ComboPopupTest(); } @@ -69,13 +53,18 @@ public static void main(String[] args) throws Exception { public ComboPopupTest() throws Exception { try { Robot robot = new Robot(); - robot.setAutoDelay(200); + robot.setAutoDelay(100); SwingUtilities.invokeAndWait(() -> start()); - blockTillDisplayed(comboBox); + robot.delay(1000); + SwingUtilities.invokeAndWait(() -> { + p = comboBox.getLocationOnScreen(); + d = comboBox.getSize(); + }); + robot.waitForIdle(); + robot.mouseMove(p.x + d.width - 1, p.y + d.height/2); robot.waitForIdle(); - robot.mouseMove(p.x + d.width-1, p.y + d.height/2); - robot.mousePress(InputEvent.BUTTON1_MASK); - robot.mouseRelease(InputEvent.BUTTON1_MASK); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); robot.waitForIdle(); System.out.println("popmenu visible " + comboBox.isPopupVisible()); diff --git a/test/jdk/javax/swing/JComboBox/TestComboBoxComponentRendering.java b/test/jdk/javax/swing/JComboBox/TestComboBoxComponentRendering.java new file mode 100644 index 0000000000000..da914314670ad --- /dev/null +++ b/test/jdk/javax/swing/JComboBox/TestComboBoxComponentRendering.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8218474 + * @key headful + * @requires (os.family == "linux") + * @summary Verifies if combobox components are rendered correctly. + * @run main TestComboBoxComponentRendering + */ + +import java.awt.Color; +import java.awt.Component; +import java.awt.image.BufferedImage; +import java.awt.Font; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.io.File; +import javax.imageio.ImageIO; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.ListCellRenderer; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; + +public class TestComboBoxComponentRendering { + private static JFrame frame; + private static JComboBox cb; + private static Robot robot; + + public static void main(String[] args) throws Exception { + robot = new Robot(); + robot.setAutoDelay(100); + + for (UIManager.LookAndFeelInfo laf : + UIManager.getInstalledLookAndFeels()) { + if (!laf.getClassName().contains("MotifLookAndFeel") && + !laf.getClassName().contains("MetalLookAndFeel")) { + System.out.println("Testing LAF: " + laf.getClassName()); + SwingUtilities.invokeAndWait(() -> setLookAndFeel(laf)); + doTesting(laf); + } + } + } + + private static void setLookAndFeel(UIManager.LookAndFeelInfo laf) { + try { + UIManager.setLookAndFeel(laf.getClassName()); + } catch (UnsupportedLookAndFeelException ignored) { + System.out.println("Unsupported LAF: " + laf.getClassName()); + } catch (ClassNotFoundException | InstantiationException + | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + private static void doTesting(UIManager.LookAndFeelInfo laf) + throws Exception { + try { + SwingUtilities.invokeAndWait(() -> { + createAndShowUI(); + }); + boolean passed = false; + robot.waitForIdle(); + robot.delay(1000); + + Point pt = cb.getLocationOnScreen(); + BufferedImage img = robot.createScreenCapture( + new Rectangle(pt.x, pt.y, cb.getWidth(), cb.getHeight())); + for (int x = 20; x < img.getWidth()-20; ++x) { + for (int y = 20; y < img.getHeight()-20; ++y) { + if (img.getRGB(x,y) == Color.RED.getRGB()) { + passed = true; + break; + } + } + if (passed) + break; + } + + if (passed) { + System.out.println("Passed"); + } else { + ImageIO.write(img, "png", + new File("ComboBox.png")); + throw new RuntimeException("ComboBox components not rendered" + + " correctly for: " + laf.getClassName()); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void createAndShowUI() { + String[] petStrings = { "Bird", "Cat", "Dog", "Rabbit", "Pig" }; + frame = new JFrame(); + cb = new JComboBox(petStrings); + cb.setRenderer(new ComboBoxCustomRenderer()); + frame.pack(); + frame.add(cb); + frame.setSize(200,250); + frame.setLocationRelativeTo(null); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setVisible(true); + } +} + +class ComboBoxCustomRenderer extends JLabel + implements ListCellRenderer { + + public ComboBoxCustomRenderer() { + setFont(new Font("SansSerif", Font.BOLD, 32)); + setOpaque(true); + setHorizontalAlignment(CENTER); + setVerticalAlignment(CENTER); + } + + public Component getListCellRendererComponent(JList list, Object value, + int index, boolean isSelected, boolean cellHasFocus) { + setText(value.toString()); + setForeground(Color.RED); + return this; + } +} diff --git a/test/jdk/javax/swing/JComboBox/TestComboBoxHeight.java b/test/jdk/javax/swing/JComboBox/TestComboBoxHeight.java index 5cd5edcbbcc65..b06dcdce3e9c0 100644 --- a/test/jdk/javax/swing/JComboBox/TestComboBoxHeight.java +++ b/test/jdk/javax/swing/JComboBox/TestComboBoxHeight.java @@ -24,7 +24,7 @@ /* * @test * @key headful - * @bug 4517214 + * @bug 4517214 8218474 * @summary Tests that comboBox is not doubleheight if editable and has TitledBorder */ diff --git a/test/jdk/javax/swing/JComboBox/bug4139900.java b/test/jdk/javax/swing/JComboBox/bug4139900.java new file mode 100644 index 0000000000000..a9ed685d5ab2f --- /dev/null +++ b/test/jdk/javax/swing/JComboBox/bug4139900.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4139900 + * @summary height of combobox may differ + * @key headful + * @run main bug4139900 +*/ + +import java.awt.Dimension; +import java.awt.Robot; +import java.awt.event.ActionListener; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; + +public class bug4139900 { + static JButton button; + static JFrame frame; + static JComboBox comboBox; + static int initialHeight; + + public static void main(String[] args) throws Exception { + try { + SwingUtilities.invokeAndWait(bug4139900::init); + test(); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void test() throws Exception { + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(500); + + SwingUtilities.invokeAndWait(() -> initialHeight = comboBox.getHeight()); + + for (int i = 0; i < 10; i++) { + SwingUtilities.invokeAndWait(() -> button.doClick()); + robot.waitForIdle(); + robot.delay(200); + SwingUtilities.invokeAndWait(() -> { + if (comboBox.getHeight() != initialHeight) { + throw new RuntimeException( + "Test failed: height differs from initial %d != %d" + .formatted(comboBox.getHeight(), initialHeight) + ); + } + }); + } + } + + public static void init() { + frame = new JFrame("bug4139900"); + + DefaultComboBoxModel model = + new DefaultComboBoxModel<>(new String[]{ + "Coma Berenices", + "Triangulum", + "Camelopardis", + "Cassiopea" + }); + + comboBox = new JComboBox<>(); + comboBox.setEditable(true); + + button = new JButton("Add/Remove Items"); + + ActionListener actionListener = e -> { + if (comboBox.getModel() == model) { + comboBox.setModel(new DefaultComboBoxModel<>()); + } else { + comboBox.setModel(model); + } + }; + + button.addActionListener(actionListener); + + JPanel panel = new JPanel(); + panel.setPreferredSize(new Dimension(300, 100)); + panel.add(comboBox); + panel.add(button); + + frame.add(panel); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } +} diff --git a/test/jdk/javax/swing/JComboBox/bug4166593.java b/test/jdk/javax/swing/JComboBox/bug4166593.java new file mode 100644 index 0000000000000..850aab2261f96 --- /dev/null +++ b/test/jdk/javax/swing/JComboBox/bug4166593.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Robot; +import java.awt.event.ActionListener; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; + +/* + * @test + * @bug 4166593 + * @summary Tests that JComboBox fires action events every time the user does an action + * @key headful + * @run main bug4166593 + */ + +public class bug4166593 { + static JFrame frame; + static JComboBox comboBox; + static volatile int numberOfActionEvents = 0; + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + SwingUtilities.invokeAndWait(() -> createTestUI()); + robot.waitForIdle(); + robot.delay(250); + + // change selected index 3 times + SwingUtilities.invokeAndWait(() -> { + comboBox.setSelectedIndex(1); + comboBox.setSelectedIndex(3); + comboBox.setSelectedIndex(2); + }); + robot.waitForIdle(); + robot.delay(250); + + if (numberOfActionEvents != 3) { + throw new RuntimeException("Unexpected number of Action Events!\n" + + "Expected: 3\nActual: " + numberOfActionEvents); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + public static void createTestUI() { + comboBox = new JComboBox(new Object[]{ + "Bob", "Fred", "Hank", "Joe", "Mildred", "Agatha", "Buffy" + }); + JPanel panel = new JPanel(); + JLabel label = new JLabel("0"); + frame = new JFrame("bug4166593"); + comboBox.setEditable(true); + + ActionListener actionCounter = e -> { + ++numberOfActionEvents; + label.setText(Integer.toString(numberOfActionEvents)); + }; + + comboBox.addActionListener(actionCounter); + + panel.add(comboBox); + panel.add(label); + + frame.add(panel); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } +} diff --git a/test/jdk/javax/swing/JComboBox/bug4174876.java b/test/jdk/javax/swing/JComboBox/bug4174876.java new file mode 100644 index 0000000000000..349dfa4b159b8 --- /dev/null +++ b/test/jdk/javax/swing/JComboBox/bug4174876.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4174876 + * @summary JComboBox tooltips do not work properly + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4174876 + */ + +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JPanel; + +public class bug4174876 { + private static final String INSTRUCTIONS = """ + Hold the mouse over both combo boxes. + A tool tip should appear over every area of both of them. + Notably, if you hold the mouse over the button on the right one, + a tool tip should appear. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("TransparentTitleTest Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .splitUIBottom(bug4174876::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JComponent createTestUI() { + JComboBox comboBox1 = new JComboBox<>(new String[]{ + "Coma Berenices", + "Triangulum", + "Camelopardis", + "Cassiopea" + }); + JComboBox comboBox2 = new JComboBox<>(new String[]{ + "Coma Berenices", + "Triangulum", + "Camelopardis", + "Cassiopea" + }); + + comboBox1.setToolTipText("Combo Box #1"); + comboBox2.setToolTipText("Combo Box #2"); + comboBox2.setEditable(true); + + JPanel panel = new JPanel(); + panel.add(comboBox1); + panel.add(comboBox2); + return panel; + } +} diff --git a/test/jdk/javax/swing/JComboBox/bug4180054.java b/test/jdk/javax/swing/JComboBox/bug4180054.java new file mode 100644 index 0000000000000..cee68dfcb9ce6 --- /dev/null +++ b/test/jdk/javax/swing/JComboBox/bug4180054.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Robot; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import javax.swing.event.ListDataEvent; +import javax.swing.event.ListDataListener; + +/* + * @test + * @bug 4180054 + * @summary Tests that DefaultComboBoxModel doesn't fire a "contents changed" unnecessarily + * @key headful + * @run main bug4180054 + */ + +public class bug4180054 { + static JFrame frame; + static JComboBox comboBox; + static volatile int numberOfContentsChangedEvents = 0; + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + SwingUtilities.invokeAndWait(() -> createTestUI()); + robot.waitForIdle(); + robot.delay(250); + + // change selected index 3 times + SwingUtilities.invokeAndWait(() -> { + comboBox.setSelectedIndex(1); + comboBox.setSelectedIndex(3); + comboBox.setSelectedIndex(2); + comboBox.setSelectedIndex(2); + }); + robot.waitForIdle(); + robot.delay(250); + + if (numberOfContentsChangedEvents != 3) { + throw new RuntimeException("Unexpected number of Contents Changed Events!\n" + + "Expected: 3\nActual: " + numberOfContentsChangedEvents); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + public static void createTestUI() { + frame = new JFrame("bug4180054"); + JPanel panel = new JPanel(); + JLabel label = new JLabel("0"); + + DefaultComboBoxModel model = new DefaultComboBoxModel(); + for (int i = 0; i < 100; ++i) { + model.addElement(Integer.toString(i)); + } + comboBox = new JComboBox(model); + comboBox.setEditable(true); + + ListDataListener contentsCounter = new ListDataListener() { + public void contentsChanged(ListDataEvent e) { + ++numberOfContentsChangedEvents; + label.setText(Integer.toString(numberOfContentsChangedEvents)); + } + + public void intervalAdded(ListDataEvent e) { + } + + public void intervalRemoved(ListDataEvent e) { + } + }; + + comboBox.getModel().addListDataListener(contentsCounter); + + panel.add(comboBox); + panel.add(label); + + frame.add(panel); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } +} diff --git a/test/jdk/javax/swing/JComboBox/bug4185024.java b/test/jdk/javax/swing/JComboBox/bug4185024.java new file mode 100644 index 0000000000000..da0360143d66e --- /dev/null +++ b/test/jdk/javax/swing/JComboBox/bug4185024.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Point; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JDesktopPane; +import javax.swing.JFrame; +import javax.swing.JInternalFrame; +import javax.swing.JPanel; + +/* + * @test + * @bug 4185024 + * @summary Tests that Heavyweight combo boxes on JDesktop work correctly + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4185024 + */ + +public class bug4185024 { + private static final String INSTRUCTIONS = """ + Click on the JComboBox button inside the JInternalFrame to bring up the menu. + Select one of the menu items and verify that the choice is highlighted correctly. + Click and drag the menu's scroll bar down and verify that it causes the menu to scroll down. + + Inside JInternalFrame: + This test is for the JComboBox in the JInternalFrame. + To test, please click on the combobox and check the following: + Does the selection in the popup list follow the mouse? + Does the popup list respond to clicking and dragging the scroll bar? + If so, press PASS, otherwise, press FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(bug4185024::createTestUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createTestUI() { + JFrame frame = new JFrame("bug4185024"); + JPanel p = new JPanel(); + p.setLayout(new BorderLayout()); + JDesktopPane desktop = new JDesktopPane(); + p.add(desktop); + frame.add(p); + + JComboBox months = new JComboBox(); + months.addItem("January"); + months.addItem("February"); + months.addItem("March"); + months.addItem("April"); + months.addItem("May"); + months.addItem("June"); + months.addItem("July"); + months.addItem("August"); + months.addItem("September"); + months.addItem("October"); + months.addItem("November"); + months.addItem("December"); + months.getAccessibleContext().setAccessibleName("Months"); + months.getAccessibleContext().setAccessibleDescription("Choose a month of the year"); + + // Set this to true and the popup works correctly... + months.setLightWeightPopupEnabled(false); + + addFrame("Months", desktop, months); + + frame.setSize(300, 300); + return frame; + } + + private static void addFrame(String title, JDesktopPane desktop, JComponent component) { + JInternalFrame jf = new JInternalFrame(title); + Point newPos = new Point(20, 20); + jf.setResizable(true); + jf.add(component); + jf.setLocation(newPos); + desktop.add(jf); + + jf.pack(); + jf.show(); + } +} diff --git a/test/jdk/javax/swing/JComboBox/bug4201964.java b/test/jdk/javax/swing/JComboBox/bug4201964.java new file mode 100644 index 0000000000000..5c3a0f3019931 --- /dev/null +++ b/test/jdk/javax/swing/JComboBox/bug4201964.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.UIManager; + +/* + * @test + * @bug 4201964 + * @summary Tests that JComboBox's arrow button isn't drawn too wide in Windows Look&Feel + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4201964 + */ + +public class bug4201964 { + private static final String INSTRUCTIONS = """ + Does the arrow look too large? If not, it passes. + """; + + public static void main(String[] args) throws Exception { + try { + UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); + } catch (Exception e) { + PassFailJFrame.forceFail("Couldn't load the Windows look and feel."); + } + + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .rows(8) + .columns(30) + .testUI(bug4201964::createTestUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createTestUI() { + JFrame frame = new JFrame("bug4201964"); + JPanel panel = new JPanel(); + JComboBox comboBox; + + comboBox = new JComboBox(new Object[]{ + "Coma Berenices", + "Triangulum", + "Camelopardis", + "Cassiopea"}); + + panel.add(comboBox); + + frame.add(panel); + frame.pack(); + return frame; + } +} diff --git a/test/jdk/javax/swing/JComboBox/bug4212498.java b/test/jdk/javax/swing/JComboBox/bug4212498.java new file mode 100644 index 0000000000000..b60470fdd327c --- /dev/null +++ b/test/jdk/javax/swing/JComboBox/bug4212498.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.event.ActionListener; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; + +/* + * @test + * @bug 4212498 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4212498 + */ + +public class bug4212498 { + static JPanel panel = new JPanel(); + static JComboBox comboBox = new JComboBox(new Object[]{ + "Coma Berenices", + "Triangulum", + "Camelopardis", + "Cassiopea"}); + + private static final String INSTRUCTIONS = """ + Edit the value in the text field (without using the popup) + and then press the tab key. If the number doesn't increase, + then test fails. + + Also, try tabbing out without making a change. The number + should NOT increase unless the user changes something. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Instructions") + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(bug4212498::createTestUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createTestUI() { + JFrame frame = new JFrame("bug4212498"); + comboBox.setEditable(true); + + final JLabel label = new JLabel("0"); + + ActionListener actionListener = + e -> label.setText("" + (Integer.parseInt(label.getText()) + 1)); + + comboBox.addActionListener(actionListener); + + panel.add(comboBox); + panel.add(label); + panel.add(new JButton("B")); + + frame.getContentPane().add(panel); + frame.pack(); + frame.setLocationRelativeTo(null); + return frame; + } +} diff --git a/test/jdk/javax/swing/JComboBox/bug4249732.java b/test/jdk/javax/swing/JComboBox/bug4249732.java new file mode 100644 index 0000000000000..b8de7fa48b785 --- /dev/null +++ b/test/jdk/javax/swing/JComboBox/bug4249732.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.UIManager; + +/* + * @test + * @bug 4249732 + * @requires (os.family == "windows") + * @summary Tests that Windows editable combo box selects text picked from its list + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4249732 + */ + +public class bug4249732 { + private static final String INSTRUCTIONS = """ + Click on combo box arrow button to open its dropdown list, and + select an item from there. The text in the combo box editor should + be selected, otherwise test fails. + """; + + public static void main(String[] args) throws Exception { + try { + UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); + } catch (Exception e) { + PassFailJFrame.forceFail("Couldn't load the Windows look and feel."); + } + + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .rows(8) + .columns(40) + .testUI(bug4249732::createTestUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createTestUI() { + JFrame frame = new JFrame("bug4249732"); + + JComboBox cb = new JComboBox(new Object[]{"foo", "bar", "baz"}); + cb.setEditable(true); + + frame.add(cb, BorderLayout.NORTH); + frame.pack(); + return frame; + } +} diff --git a/test/jdk/javax/swing/JComboBox/bug4368848.java b/test/jdk/javax/swing/JComboBox/bug4368848.java new file mode 100644 index 0000000000000..1673c458cac05 --- /dev/null +++ b/test/jdk/javax/swing/JComboBox/bug4368848.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.DefaultCellEditor; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.table.AbstractTableModel; + +/* + * @test + * @bug 4368848 + * @summary Tests that mouse wheel events cancel popups + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4368848 + */ + +public class bug4368848 { + static final String[] names = {"First Name", "Last Name", "Veggy"}; + static Object[][] data = { + {"Mark", "Andrews", false}, + {"Tom", "Ball", false}, + {"Alan", "Chung", false}, + {"Jeff", "Dinkins", false}, + {"Amy", "Fowler", false}, + {"Brian", "Gerhold", false}, + {"James", "Gosling", false}, + {"David", "Karlton", false}, + {"Dave", "Kloba", false}, + {"Peter", "Korn", false}, + {"Phil", "Milne", false}, + {"Dave", "Moore", false}, + {"Hans", "Muller", false}, + {"Rick", "Levenson", false}, + {"Tim", "Prinzing", false}, + {"Chester", "Rose", false}, + {"Ray", "Ryan", false}, + {"Georges", "Saab", false}, + {"Willie", "Walker", false}, + {"Kathy", "Walrath", false}, + {"Arnaud", "Weber", false} + }; + + private static final String INSTRUCTIONS = """ + Click any cell in the 'Veggy' column, so that combo box appears. + Make sure mouse pointer is over the table, but _not_ over the combo + box. Try scrolling the table using the mouse wheel. The combo popup + should disappear. If it stays visible, test fails. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(bug4368848::createTestUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createTestUI() { + JFrame frame = new JFrame("bug4368848"); + ExampleTableModel dataModel = new ExampleTableModel(); + + JComboBox _editor = new JComboBox(); + _editor.addItem(false); + _editor.addItem(true); + + JTable tableView = new JTable(dataModel); + tableView.setDefaultEditor(Boolean.class, new DefaultCellEditor(_editor)); + + frame.add(new JScrollPane(tableView)); + frame.setSize(200, 200); + return frame; + } + + static class ExampleTableModel extends AbstractTableModel { + // These methods always need to be implemented. + @Override + public int getColumnCount() { + return names.length; + } + + @Override + public int getRowCount() { + return data.length; + } + + public Object getValueAt(int row, int col) { + return data[row][col]; + } + + @Override + public boolean isCellEditable(int row, int col) { + return true; + } + + @Override + public String getColumnName(int column) { + return names[column]; + } + + @Override + public Class getColumnClass(int col) { + return getValueAt(0, col).getClass(); + } + } +} diff --git a/test/jdk/javax/swing/JComboBox/bug4459267.java b/test/jdk/javax/swing/JComboBox/bug4459267.java new file mode 100644 index 0000000000000..85049936ea7fd --- /dev/null +++ b/test/jdk/javax/swing/JComboBox/bug4459267.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Robot; +import java.awt.event.KeyEvent; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +/* + * @test + * @bug 4459267 + * @summary Tests that pressing PageUp in combo popup list doesn't cause + * stack overflow + * @key headful + * @run main bug4459267 + */ + +public class bug4459267 { + static JFrame frame; + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + robot.setAutoDelay(250); + + SwingUtilities.invokeAndWait(() -> createTestUI()); + robot.waitForIdle(); + robot.delay(1000); + + robot.keyPress(KeyEvent.VK_PAGE_UP); + robot.keyRelease(KeyEvent.VK_PAGE_UP); + robot.waitForIdle(); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + public static void createTestUI() { + frame = new JFrame("bug4459267"); + JComboBox jcmb = new JComboBox(); + jcmb.addItem("JComobo1"); + jcmb.addItem("Item2"); + jcmb.addItem("Item3"); + frame.getContentPane().add(jcmb, BorderLayout.NORTH); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } +} diff --git a/test/jdk/javax/swing/JComboBox/bug4474400.java b/test/jdk/javax/swing/JComboBox/bug4474400.java new file mode 100644 index 0000000000000..ea52d0c71b541 --- /dev/null +++ b/test/jdk/javax/swing/JComboBox/bug4474400.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4474400 + * @summary Tests JTextArea wrapping with font change + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4474400 + */ + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Font; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.JTextArea; + +public class bug4474400 { + private static final String INSTRUCTIONS = """ + Press the "Change Font" button. The two lines of text should be + properly drawn using the larger font, there should be empty line + between them. If display is garbled, test fails. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug4474400 Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .splitUIRight(bug4474400::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JComponent createTestUI() { + Font smallFont = new Font("SansSerif", Font.PLAIN, 12); + Font largeFont = new Font("SansSerif", Font.PLAIN, 36); + + JTextArea textArea = new JTextArea("This is the first line\n\nThis is the third line"); + textArea.setFont(smallFont); + textArea.setEditable(false); + textArea.setLineWrap(true); + textArea.setWrapStyleWord(true); + + JButton b = new JButton("Change Font"); + b.addActionListener((e) -> textArea.setFont(largeFont)); + + JPanel panel = new JPanel(new BorderLayout()); + panel.setPreferredSize(new Dimension(200, 300)); + panel.add(textArea, BorderLayout.CENTER); + panel.add(b, BorderLayout.SOUTH); + + return panel; + } +} diff --git a/test/jdk/javax/swing/JComboBox/bug4519269.java b/test/jdk/javax/swing/JComboBox/bug4519269.java new file mode 100644 index 0000000000000..29147998d0a4c --- /dev/null +++ b/test/jdk/javax/swing/JComboBox/bug4519269.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +/* + * @test + * @bug 4519269 + * @summary Tests that DefaultKeySelectionManager doesn't throw NPE + * @key headful + * @run main bug4519269 + */ + +public class bug4519269 { + static JFrame frame; + static JComboBox combo; + static Point p; + static Object[] data = {new CustomString("Item 1"), new CustomString("Item 2"), + new CustomString("Item 3"), new CustomString("Item 4")}; + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + robot.setAutoDelay(250); + + SwingUtilities.invokeAndWait(() -> createTestUI()); + robot.waitForIdle(); + robot.delay(1000); + + SwingUtilities.invokeAndWait(() -> p = combo.getLocationOnScreen()); + robot.mouseMove(p.x + 5, p.y + 5); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + + robot.keyPress(KeyEvent.VK_SHIFT); + robot.keyRelease(KeyEvent.VK_SHIFT); + robot.waitForIdle(); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + public static void createTestUI() { + frame = new JFrame("bug4519269"); + combo = new JComboBox(data); + frame.getContentPane().add(combo); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + static class CustomString { + String string; + + public CustomString(String s) { + string = s; + } + + public String toString() { + return null; + } + } +} diff --git a/test/jdk/javax/swing/JComboBox/bug4530952.java b/test/jdk/javax/swing/JComboBox/bug4530952.java new file mode 100644 index 0000000000000..cf960d64c9a90 --- /dev/null +++ b/test/jdk/javax/swing/JComboBox/bug4530952.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +/* + * @test + * @bug 4530952 + * @summary Tests that double mouse clicks invoke Event + * @key headful + * @run main bug4530952 + */ + +public class bug4530952 { + static JFrame frame; + static JButton btnAction; + static JComboBox cmbAction; + static volatile Point loc; + static volatile Dimension btnSize; + + private static volatile boolean flag; + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + SwingUtilities.invokeAndWait(() -> createTestUI()); + robot.waitForIdle(); + robot.delay(1000); + + // enter some text in combo box + robot.keyPress(KeyEvent.VK_A); + robot.keyRelease(KeyEvent.VK_A); + robot.keyPress(KeyEvent.VK_A); + robot.keyRelease(KeyEvent.VK_A); + robot.waitForIdle(); + robot.delay(250); + + // find and click action button + SwingUtilities.invokeAndWait(() -> { + loc = btnAction.getLocationOnScreen(); + btnSize = btnAction.getSize(); + }); + robot.waitForIdle(); + robot.delay(250); + + robot.mouseMove(loc.x + btnSize.width / 2, + loc.y + btnSize.height / 2); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(1000); + + if (!flag) { + throw new RuntimeException("Failed: button action was not fired"); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + public static void createTestUI() { + frame = new JFrame("bug4530952"); + frame.setLayout(new FlowLayout()); + + btnAction = new JButton("Action"); + cmbAction = new JComboBox(); + + flag = false; + + ActionListener al = e -> flag = true; + DocumentListener dl = new DocumentListener() { + @Override + public void changedUpdate(DocumentEvent evt) { + resetButtons(); + } + + @Override + public void insertUpdate(DocumentEvent evt) { + resetButtons(); + } + + @Override + public void removeUpdate(DocumentEvent evt) { + resetButtons(); + } + }; + + // Add an editable combo box + cmbAction.setEditable(true); + frame.add(cmbAction); + + btnAction.setEnabled(false); + frame.add(btnAction); + + btnAction.addActionListener(al); + ((JTextField) cmbAction.getEditor().getEditorComponent()). + getDocument().addDocumentListener(dl); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + public static void resetButtons() { + int length = ((JTextField) cmbAction.getEditor().getEditorComponent()). + getDocument().getLength(); + btnAction.setEnabled(length > 0); + } +} diff --git a/test/jdk/javax/swing/JComboBox/bug4530953.java b/test/jdk/javax/swing/JComboBox/bug4530953.java new file mode 100644 index 0000000000000..a9f0c70b9bc2c --- /dev/null +++ b/test/jdk/javax/swing/JComboBox/bug4530953.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.FlowLayout; +import java.awt.Robot; +import java.awt.event.KeyEvent; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +/* + * @test + * @bug 4530953 + * @summary Tests that highlighted Item appears after automatically scrolling to the item + * @key headful + * @run main bug4530953 + */ + +public class bug4530953 { + static JFrame frame; + static JComboBox combo; + static String[] data = {"Apple", "Orange", "Cherry"}; + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + SwingUtilities.invokeAndWait(() -> createTestUI()); + robot.waitForIdle(); + robot.delay(250); + + // enter some text in combo box editor + robot.keyPress(KeyEvent.VK_A); + robot.keyRelease(KeyEvent.VK_A); + robot.keyPress(KeyEvent.VK_A); + robot.keyRelease(KeyEvent.VK_A); + robot.keyPress(KeyEvent.VK_ENTER); + robot.keyRelease(KeyEvent.VK_ENTER); + robot.waitForIdle(); + robot.delay(250); + + // select orange in combo box + robot.keyPress(KeyEvent.VK_DOWN); + robot.keyRelease(KeyEvent.VK_DOWN); + robot.keyPress(KeyEvent.VK_DOWN); + robot.keyRelease(KeyEvent.VK_DOWN); + robot.keyPress(KeyEvent.VK_DOWN); + robot.keyRelease(KeyEvent.VK_DOWN); + robot.keyPress(KeyEvent.VK_ENTER); + robot.keyRelease(KeyEvent.VK_ENTER); + robot.waitForIdle(); + robot.delay(250); + + String currSelection = (String) combo.getEditor().getItem(); + if (!currSelection.equals("Orange")) { + throw new RuntimeException("Unexpected Selection.\n" + + "Expected: Orange\nActual: " + currSelection); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + public static void createTestUI() { + frame = new JFrame("bug4530953"); + combo = new JComboBox(data); + combo.setEditable(true); + combo.setSelectedIndex(1); + frame.setLayout(new FlowLayout()); + frame.add(combo); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } +} diff --git a/test/jdk/javax/swing/JComponent/7154030/bug7154030.java b/test/jdk/javax/swing/JComponent/7154030/bug7154030.java index 5a10be26840f0..9e41a011b9ae2 100644 --- a/test/jdk/javax/swing/JComponent/7154030/bug7154030.java +++ b/test/jdk/javax/swing/JComponent/7154030/bug7154030.java @@ -74,6 +74,8 @@ public static void main(String[] args) throws Exception { @Override public void run() { JDesktopPane desktop = new JDesktopPane(); + desktop.setBackground(Color.WHITE); + desktop.setForeground(Color.BLACK); button = new JButton("button"); frame = new JFrame(); frame.setUndecorated(true); diff --git a/test/jdk/javax/swing/JComponent/bug4235215.java b/test/jdk/javax/swing/JComponent/bug4235215.java new file mode 100644 index 0000000000000..471f713ee4679 --- /dev/null +++ b/test/jdk/javax/swing/JComponent/bug4235215.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4235215 + * @summary Tests that Toolkit.getPrintJob() do not throw NPE + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4235215 + */ + +import java.awt.Toolkit; +import javax.swing.JButton; +import javax.swing.JFrame; + +public class bug4235215 { + + private static final String INSTRUCTIONS = """ + Press "Print Dialog" button. + If you see a print dialog, test passes. + Click "Cancel" button to close it."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug4235215 Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(bug4235215::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + JFrame frame = new JFrame("bug4235215"); + JButton button = new JButton("Print Dialog"); + button.addActionListener(ev -> { + Toolkit.getDefaultToolkit().getPrintJob(frame, "Test Printing", null); + }); + frame.add(button); + frame.pack(); + return frame; + } +} diff --git a/test/jdk/javax/swing/JComponent/bug4247610.java b/test/jdk/javax/swing/JComponent/bug4247610.java new file mode 100644 index 0000000000000..e5470606f6e05 --- /dev/null +++ b/test/jdk/javax/swing/JComponent/bug4247610.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4247610 + * @summary Tests an unnecessary repaint issue + * @key headful + * @run main bug4247610 + */ + +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Graphics; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.InputEvent; +import javax.swing.JButton; +import javax.swing.JDesktopPane; +import javax.swing.JFrame; +import javax.swing.JInternalFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Random; + +public class bug4247610 { + + private static JFrame frame; + private static JButton damager; + private static volatile Point loc; + private static volatile Dimension size; + private static volatile boolean traced; + private static volatile boolean failed; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame("bug4247610"); + JDesktopPane pane = new JDesktopPane(); + + JInternalFrame jif = new JInternalFrame( + "Damager", true, true, true, true); + InternalFramePanel ifp = new InternalFramePanel(); + damager = new JButton("Damage!"); + ifp.add(damager); + jif.setContentPane(ifp); + jif.setBounds(0, 0, 300, 300); + jif.setVisible(true); + pane.add(jif); + + jif = new JInternalFrame("Damagee", true, true, true, true); + JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + final JLabel damagee = new JLabel(""); + panel.add(damagee); + jif.setContentPane(panel); + jif.setBounds(60, 220, 300, 100); + jif.setVisible(true); + pane.add(jif); + + final Random random = new Random(); + + damager.addActionListener((e) -> { + System.out.println("trace paints enabled"); + traced = true; + damagee.setText(Integer.toString(random.nextInt())); + }); + frame.setContentPane(pane); + frame.setSize(500, 500); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + }); + robot.waitForIdle(); + robot.delay(1000); + SwingUtilities.invokeAndWait(() -> { + loc = damager.getLocationOnScreen(); + size = damager.getSize(); + }); + robot.mouseMove(loc.x + size.width / 2, loc.y + size.height / 2); + robot.waitForIdle(); + robot.delay(200); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + if (failed) { + throw new RuntimeException("Failed: unnecessary repaint occured"); + } + } + + + static class InternalFramePanel extends JPanel { + final AtomicInteger repaintCounter = new AtomicInteger(0); + InternalFramePanel() { + super(new FlowLayout()); + setOpaque(true); + } + + public synchronized void paintComponent(Graphics g) { + super.paintComponent(g); + repaintCounter.incrementAndGet(); + System.out.println("repaintCounter " + repaintCounter.intValue()); + if (traced) { + failed = true; + } + } + } +} diff --git a/test/jdk/javax/swing/JComponent/bug4254995.java b/test/jdk/javax/swing/JComponent/bug4254995.java new file mode 100644 index 0000000000000..8947558f128bf --- /dev/null +++ b/test/jdk/javax/swing/JComponent/bug4254995.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4254995 + * @summary Tests that html in renderer works correctly + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4254995 + */ + +import javax.swing.JFrame; +import javax.swing.JList; +import javax.swing.JScrollPane; + +public class bug4254995 { + + private static final String INSTRUCTIONS = """ + If you see a list containing digits from one to seven, test passes. + Otherwise it fails."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug4254995 Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(bug4254995::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + JFrame frame = new JFrame("bug4254995"); + String[] data = { "1", "2", "3", "4", "5", "6", "7" }; + frame.add(new JScrollPane(new JList(data))); + frame.pack(); + return frame; + } +} diff --git a/test/jdk/javax/swing/JComponent/bug4706883.java b/test/jdk/javax/swing/JComponent/bug4706883.java new file mode 100644 index 0000000000000..7375342c7a45d --- /dev/null +++ b/test/jdk/javax/swing/JComponent/bug4706883.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.lang.reflect.InvocationTargetException; +import java.util.Date; +import javax.swing.AbstractAction; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.KeyStroke; +import javax.swing.SwingUtilities; + +/* + * @test + * @bug 4706883 + * @summary REGRESSION: ActionMap misses VK_PRINTSCREEN + * @key headful + * @run main bug4706883 + */ + +public class bug4706883 { + + MyPanel panel; + JFrame fr; + boolean passed = false; + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + bug4706883 test = new bug4706883(); + SwingUtilities.invokeAndWait(test::init); + SwingUtilities.invokeAndWait(test::test); + } + public void init() { + fr = new JFrame("Test"); + + panel = new MyPanel(); + fr.add(panel); + + panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put + (KeyStroke.getKeyStroke(KeyEvent.VK_PRINTSCREEN, 0, true), + "RELEASED"); + + panel.getActionMap().put("RELEASED", new AbstractAction() { + public void actionPerformed(ActionEvent ev) { + setPassed(true); + } + }); + + fr.setSize(200, 200); + fr.setVisible(true); + } + + public void test() { + panel.doTest(); + try { + Thread.sleep(1000); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (fr != null) { + fr.setVisible(false); + fr.dispose(); + } + } + if (!isPassed()) { + throw new RuntimeException("The key binding for VK_PRINTSCREEN wasn't processed"); + } + } + + class MyPanel extends JPanel { + public void doTest() { + KeyEvent e = new KeyEvent(this, KeyEvent.KEY_RELEASED, + (new Date()).getTime(), + 0, KeyEvent.VK_PRINTSCREEN, + KeyEvent.CHAR_UNDEFINED); + processKeyEvent(e); + } + } + + synchronized void setPassed(boolean passed) { + this.passed = passed; + } + + synchronized boolean isPassed() { + return passed; + } +} diff --git a/test/jdk/javax/swing/JDesktopPane/bug4132993.java b/test/jdk/javax/swing/JDesktopPane/bug4132993.java new file mode 100644 index 0000000000000..58530ae91e605 --- /dev/null +++ b/test/jdk/javax/swing/JDesktopPane/bug4132993.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 4132993 + * @summary JDesktopPane.getAllFramesInLayer(..) return iconified frame + * @run main bug4132993 + */ + +import javax.swing.JDesktopPane; +import javax.swing.JInternalFrame; +import javax.swing.JLayeredPane; + + +public class bug4132993 { + public static void main(String[] args) throws Exception { + JDesktopPane mDesktop = new JDesktopPane(); + JInternalFrame jif = new JInternalFrame("My Frame"); + jif.setIconifiable(true); + mDesktop.add(jif); + jif.setIcon(true); + JInternalFrame[] ji = + mDesktop.getAllFramesInLayer(JLayeredPane.DEFAULT_LAYER); + for (int i = 0; i < ji.length; i++) { + if (jif == ji[i]) { + return; + } + } + throw new RuntimeException("JDesktopPane.getAllFramesInLayer() failed..."); + } +} diff --git a/test/jdk/javax/swing/JDesktopPane/bug4773378.java b/test/jdk/javax/swing/JDesktopPane/bug4773378.java new file mode 100644 index 0000000000000..b961c77d40456 --- /dev/null +++ b/test/jdk/javax/swing/JDesktopPane/bug4773378.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 4773378 + * @summary BasicDesktopPaneUI.desktopManager nulled after JDesktopPane.updateUI() + * @key headful + * @run main bug4773378 + */ + +import java.awt.AWTException; +import java.awt.BorderLayout; +import java.awt.Robot; +import java.awt.event.KeyEvent; +import java.beans.PropertyVetoException; +import java.lang.reflect.InvocationTargetException; +import java.util.concurrent.CountDownLatch; +import javax.swing.DefaultDesktopManager; +import javax.swing.JDesktopPane; +import javax.swing.JFrame; +import javax.swing.JInternalFrame; +import javax.swing.SwingUtilities; +import javax.swing.event.InternalFrameAdapter; +import javax.swing.event.InternalFrameEvent; + +public class bug4773378 { + JFrame frame; + + JDesktopPane desktop; + DefaultDesktopManager desktopManager; + JInternalFrame jif; + + Robot robot; + private final CountDownLatch frameActivated = new CountDownLatch(1); + public void setupGUI() { + frame = new JFrame("bug4773378"); + frame.setLayout(new BorderLayout()); + desktop = new JDesktopPane(); + frame.add(desktop, BorderLayout.CENTER); + + jif = new JInternalFrame("jif", true, false, true, true); + jif.setSize(150, 100); + jif.setVisible(true); + desktop.add(jif); + + jif.addInternalFrameListener(new InternalFrameAdapter() { + public void internalFrameActivated(InternalFrameEvent e) { + frameActivated.countDown(); + } + }); + + desktopManager = new MyDesktopManager(); + desktop.setDesktopManager(desktopManager); + desktop.updateUI(); + + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + jif.requestFocus(); + } + + public void performTest() throws InterruptedException, + InvocationTargetException, AWTException { + SwingUtilities.invokeAndWait(() -> { + try { + jif.setSelected(true); + } catch (PropertyVetoException e) { + throw new RuntimeException(e); + } + }); + + frameActivated.await(); + + robot = new Robot(); + robot.keyPress(KeyEvent.VK_CONTROL); + robot.keyPress(KeyEvent.VK_F6); + robot.keyRelease(KeyEvent.VK_F6); + robot.keyRelease(KeyEvent.VK_CONTROL); + + robot.waitForIdle(); + } + + public void cleanupGUI() { + if (frame != null) { + frame.setVisible(false); + frame.dispose(); + } + } + + private static class MyDesktopManager extends DefaultDesktopManager { + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, AWTException { + bug4773378 b = new bug4773378(); + SwingUtilities.invokeAndWait(b::setupGUI); + b.performTest(); + SwingUtilities.invokeAndWait(b::cleanupGUI); + } +} diff --git a/test/jdk/javax/swing/JEditorPane/ScrollToReferenceTest/ScrollToReferenceTest.java b/test/jdk/javax/swing/JEditorPane/ScrollToReferenceTest/ScrollToReferenceTest.java new file mode 100644 index 0000000000000..5d96dd70dd174 --- /dev/null +++ b/test/jdk/javax/swing/JEditorPane/ScrollToReferenceTest/ScrollToReferenceTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4194428 + * @summary Checks that scrolling an href to visible scrolls it to the top of the page. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ScrollToReferenceTest + */ + +import java.awt.Container; +import java.awt.Dimension; +import java.io.IOException; +import java.net.URL; +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; + +public class ScrollToReferenceTest { + + static final String INSTRUCTIONS = """ + Wait for the html document to finish loading, click on the anchor + with text 'CLICK ME'. If 'should be at top of editor pane' is + scrolled to the top of the visible region of the text, click PASS, + otherwise click FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(ScrollToReferenceTest::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + JFrame frame = new JFrame("ScrollToReferenceTest"); + JEditorPane pane = new JEditorPane(); + + try { + pane.setPage(ScrollToReferenceTest.class.getResource("test.html")); + } catch (IOException ioe) { + PassFailJFrame.forceFail("Couldn't find html file"); + } + + + pane.addHyperlinkListener(new HyperlinkListener() { + public void hyperlinkUpdate(HyperlinkEvent e) { + if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED && + e.getURL() != null) { + try { + pane.setPage(e.getURL()); + } catch (IOException ioe) { + pane.setText("error finding url, click fail!"); + } + } + } + }); + pane.setEditable(false); + JScrollPane sp = new JScrollPane(pane); + sp.setPreferredSize(new Dimension(400, 400)); + frame.add(sp); + frame.setSize(400, 400); + return frame; + } +} diff --git a/test/jdk/javax/swing/JEditorPane/ScrollToReferenceTest/test.html b/test/jdk/javax/swing/JEditorPane/ScrollToReferenceTest/test.html new file mode 100644 index 0000000000000..d7e82e516db0b --- /dev/null +++ b/test/jdk/javax/swing/JEditorPane/ScrollToReferenceTest/test.html @@ -0,0 +1,93 @@ + + +

    Click ME! +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    should be at top of editor pane +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    a +

    TEST! +

    a + + diff --git a/test/jdk/javax/swing/JEditorPane/bug4325606.java b/test/jdk/javax/swing/JEditorPane/bug4325606.java new file mode 100644 index 0000000000000..fa82e3f9e0acb --- /dev/null +++ b/test/jdk/javax/swing/JEditorPane/bug4325606.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 4325606 + * @summary Tests getting row start + * @key headful + * @run main bug4325606 + */ + +import java.awt.AWTException; +import java.awt.BorderLayout; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.InputEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.lang.reflect.InvocationTargetException; +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; +import javax.swing.text.BadLocationException; +import javax.swing.text.Utilities; + +public class bug4325606 { + + public volatile boolean passed = true; + private JFrame frame; + private JEditorPane pane; + + public void setupGUI() { + frame = new JFrame("Click Bug"); + frame.setLayout(new BorderLayout()); + + pane = new JEditorPane(); + pane.addMouseListener(new ClickListener()); + pane.setContentType("text/html"); + pane.setText("" + + "

    Here is line one

    " + + "

    Here is line two

    " + + ""); + + frame.add(new JScrollPane(pane), BorderLayout.CENTER); + + frame.addWindowListener(new TestStateListener()); + frame.setLocation(50, 50); + frame.setSize(400, 300); + frame.setVisible(true); + } + + class TestStateListener extends WindowAdapter { + public void windowOpened(WindowEvent ev) { + Robot robo; + try { + robo = new Robot(); + } catch (AWTException e) { + throw new RuntimeException("Robot could not be created", e); + } + robo.setAutoDelay(100); + robo.delay(1000); + Point p = frame.getLocationOnScreen(); + robo.mouseMove(p.x + 50, p.y + 50); + robo.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robo.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robo.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robo.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robo.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robo.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + } + } + + class ClickListener extends MouseAdapter { + public void mouseClicked(MouseEvent event) { + try { + Utilities.getRowStart(pane, pane.getCaretPosition()); + } catch (BadLocationException blex) { + throw new RuntimeException("Test failed.", blex); + } + } + } + + public void cleanupGUI() { + if (frame != null) { + frame.setVisible(false); + frame.dispose(); + } + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, AWTException { + bug4325606 b = new bug4325606(); + try { + Robot robot = new Robot(); + SwingUtilities.invokeAndWait(b::setupGUI); + robot.waitForIdle(); + } finally { + SwingUtilities.invokeAndWait(b::cleanupGUI); + } + } +} diff --git a/test/hotspot/jtreg/runtime/signal/TestSigusr2.java b/test/jdk/javax/swing/JEditorPane/bug4330998.java similarity index 73% rename from test/hotspot/jtreg/runtime/signal/TestSigusr2.java rename to test/jdk/javax/swing/JEditorPane/bug4330998.java index fc5bff9d67d5f..cb8a348b591b3 100644 --- a/test/hotspot/jtreg/runtime/signal/TestSigusr2.java +++ b/test/jdk/javax/swing/JEditorPane/bug4330998.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,15 +21,17 @@ * questions. */ - -/* - * @test - * @requires os.family != "windows" & os.family != "aix" - * - * @summary converted from VM testbase runtime/signal/sigusr201. - * VM testbase keywords: [signal, runtime, linux, macosx] - * - * @library /test/lib - * @run main/native SigTestDriver SIGUSR2 +/* @test + * @bug 4330998 + * @summary JEditorPane.setText(null) throws NullPointerException. + * @run main bug4330998 */ +import javax.swing.JEditorPane; + +public class bug4330998 { + public static void main(String[] args) { + JEditorPane jep = new JEditorPane(); + jep.setText(null); + } +} diff --git a/test/jdk/javax/swing/JEditorPane/bug4694598.java b/test/jdk/javax/swing/JEditorPane/bug4694598.java new file mode 100644 index 0000000000000..c0dcc34b62b90 --- /dev/null +++ b/test/jdk/javax/swing/JEditorPane/bug4694598.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 4694598 + * @key headful + * @summary JEditor pane throws NullPointerException on mouse movement. + * @run main bug4694598 + */ + +import java.awt.AWTException; +import java.awt.Robot; +import java.io.IOException; +import java.io.Writer; +import java.lang.reflect.InvocationTargetException; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +public class bug4694598 { + JFrame frame; + volatile int bottom; + final Path frameContentFile = Path.of("FrameContent.html"); + + public void setupGUI() { + frame = new JFrame("Test 4694598"); + JEditorPane jep = new JEditorPane(); + jep.setEditable(false); + frame.getContentPane().add(jep); + frame.setLocation(50, 50); + frame.setSize(400, 400); + + String frameContentString = "\n" + + " Frame content.\n" + + ""; + try (Writer writer = Files.newBufferedWriter(frameContentFile)) { + writer.write(frameContentString); + } catch (IOException ioe){ + throw new RuntimeException("Could not create html file to embed", ioe); + } + URL frameContentUrl; + try { + frameContentUrl = frameContentFile.toUri().toURL(); + } catch (MalformedURLException mue) { + throw new RuntimeException("Can not convert path to URL", mue); + } + jep.setContentType("text/html"); + String html = " " + + "" + + "" + + "" + + // ! Without bug is not reproducable + "<NOFRAMES>" + + "" + + " "; + jep.setText(html); + + frame.setVisible(true); + } + + public void performTest() throws InterruptedException, + InvocationTargetException, AWTException { + Robot robo = new Robot(); + robo.waitForIdle(); + + final int range = 20; + SwingUtilities.invokeAndWait(() -> { + bottom = frame.getLocationOnScreen().y + + frame.getSize().height - range; + }); + for (int i = 0; i < range; i++) { + robo.mouseMove(300, bottom + i); + robo.waitForIdle(); + robo.delay(50); + } + } + + public void cleanupGUI() { + if (frame != null) { + frame.setVisible(false); + frame.dispose(); + } + try { + Files.deleteIfExists(frameContentFile); + } catch (IOException ignore) {} + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, AWTException { + bug4694598 app = new bug4694598(); + SwingUtilities.invokeAndWait(app::setupGUI); + app.performTest(); + SwingUtilities.invokeAndWait(app::cleanupGUI); + } +} diff --git a/test/jdk/javax/swing/JFileChooser/4150029/bug4150029.html b/test/jdk/javax/swing/JFileChooser/4150029/bug4150029.html deleted file mode 100644 index 9677de3fc54ad..0000000000000 --- a/test/jdk/javax/swing/JFileChooser/4150029/bug4150029.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - -Follow the instructions below. -1.Go into 'subDir' folder. -2.Press BACKSPACE key. -3.Push OPEN button. -4.Push DONE button. - - diff --git a/test/jdk/javax/swing/JFileChooser/4150029/bug4150029.java b/test/jdk/javax/swing/JFileChooser/4150029/bug4150029.java index 5388ba106a637..d5c7dab4d8ec1 100644 --- a/test/jdk/javax/swing/JFileChooser/4150029/bug4150029.java +++ b/test/jdk/javax/swing/JFileChooser/4150029/bug4150029.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,73 +21,117 @@ * questions. */ -/* - bug 4150029 8006087 - summary BackSpace keyboard button does not lead to parent directory - author Oleg Mokhovikov -*/ +import java.awt.BorderLayout; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.io.File; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; import jdk.test.lib.Platform; -import javax.swing.*; -import java.io.File; -import java.io.IOException; +/* + * @test + * @bug 4150029 8006087 + * @key headful + * @summary BackSpace keyboard button does not lead to parent directory + * @library /test/lib + * @build jdk.test.lib.Platform + * @run main bug4150029 + */ + +public class bug4150029 { + private static JFrame frame; + private static JFileChooser fileChooser; + private static Robot robot; + private static File prevDir; + private static File crntDir; + private static volatile Point p; -public class bug4150029 extends JApplet { - private boolean res; + public static void main(String[] args) throws Exception { + robot = new Robot(); + robot.setAutoDelay(100); - public void init() { - if (Platform.isOSX()) { - try { - UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); - } catch (Exception e) { - throw new RuntimeException(e); + try { + if (Platform.isOSX()) { + try { + UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); + } catch (Exception e) { + throw new RuntimeException(e); + } } - } - String tmpDir = System.getProperty("java.io.tmpdir"); + String tmpDir = System.getProperty("java.io.tmpdir"); - if (tmpDir.length() == 0) {//'java.io.tmpdir' isn't guaranteed to be defined - tmpDir = System.getProperty("user.home"); + //'java.io.tmpdir' isn't guaranteed to be defined + if (tmpDir.length() == 0) { + tmpDir = System.getProperty("user.home"); + } + System.out.println("Temp directory: " + tmpDir); + + File testDir = new File(tmpDir, "testDir"); + testDir.mkdir(); + testDir.deleteOnExit(); + System.out.println("Created directory: " + testDir); + + File subDir = new File(testDir, "subDir"); + subDir.mkdir(); + subDir.deleteOnExit(); + System.out.println("Created sub-directory: " + subDir); + + SwingUtilities.invokeAndWait(() -> { + createAndShowUI(); + fileChooser.setCurrentDirectory(subDir); + }); + + doTesting(); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); } + } - System.out.println("Temp directory: " + tmpDir); - - File testDir = new File(tmpDir, "testDir"); - - testDir.mkdir(); - - File subDir = new File(testDir, "subDir"); - - subDir.mkdir(); - - System.out.println("Created directory: " + testDir); - System.out.println("Created sub-directory: " + subDir); - - JFileChooser fileChooser = new JFileChooser(testDir); + private static void createAndShowUI() { + frame = new JFrame("Backspace Shortcut for Directory Navigation Test"); + frame.setLayout(new BorderLayout()); + fileChooser = new JFileChooser(); + fileChooser.setControlButtonsAreShown(false); + frame.add(fileChooser, BorderLayout.CENTER); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setVisible(true); + } - fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + private static void doTesting() throws Exception { + SwingUtilities.invokeAndWait(() -> { + p = frame.getLocationOnScreen(); + }); + robot.mouseMove(p.x + 200, p.y + 200); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); - try { - res = fileChooser.showOpenDialog(this) != JFileChooser.APPROVE_OPTION || - testDir.getCanonicalPath().equals(fileChooser.getSelectedFile().getCanonicalPath()); - } catch (IOException e) { - res = false; + robot.waitForIdle(); - e.printStackTrace(); - } + // check backspace key at subDir level + clickBackSpace(); - try { - subDir.delete(); - testDir.delete(); - } catch (SecurityException e) { - e.printStackTrace(); + if (prevDir.equals(crntDir)) { + throw new RuntimeException("BackSpace does not lead to parent directory"); } } - public void destroy() { - if (!res) { - throw new RuntimeException("BackSpace keyboard button does not lead to parent directory"); - } + private static void clickBackSpace() { + prevDir = fileChooser.getCurrentDirectory(); + robot.keyPress(KeyEvent.VK_BACK_SPACE); + robot.keyRelease(KeyEvent.VK_BACK_SPACE); + crntDir = fileChooser.getCurrentDirectory(); } } diff --git a/test/jdk/javax/swing/JFileChooser/6798062/bug6798062.html b/test/jdk/javax/swing/JFileChooser/6798062/bug6798062.html deleted file mode 100644 index 077034e654779..0000000000000 --- a/test/jdk/javax/swing/JFileChooser/6798062/bug6798062.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - -The test is suitable only for Windows - -1. Create a link -2. Copy path to the link into TextField -3. Run the Windows Task Manager. Select the Processes tab and find the java process -4. Press the Start button in the test window -5. Wait several minutes and observe in the Windows Task Manager -that Memory Usage of java process is not increasing - - diff --git a/test/jdk/javax/swing/JFileChooser/EnterEmptyDirectory.java b/test/jdk/javax/swing/JFileChooser/EnterEmptyDirectory.java new file mode 100644 index 0000000000000..5a9a7945f601a --- /dev/null +++ b/test/jdk/javax/swing/JFileChooser/EnterEmptyDirectory.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.JButton; +import javax.swing.JFileChooser; +import javax.swing.JPanel; +import javax.swing.UIManager; + +/* + * @test + * @bug 4913368 + * @requires (os.family == "linux") + * @summary Test repainting when entering an empty directory w/ GTK LAF + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual EnterEmptyDirectory + */ + +public class EnterEmptyDirectory { + + private static final String INSTRUCTIONS = """ + This test is only for the GTK Look & Feel. + + Step 1: + Find or create an empty directory. This directory should + be in a directory with other files and directories, such that + there are items in both the Folders and Files lists of the + JFileChooser. + + Step 2: + Click the "Show JFileChooser" button and enter the empty directory. + If both lists are correctly repainted such that they are both empty + (except for the ./ and ../) then the test passes. + + If the contents of the Folders or Files lists are unchanged, test FAILS. """; + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel("com.sun.java.swing.plaf.gtk.GTKLookAndFeel"); + PassFailJFrame.builder() + .title("JFileChooser Instructions") + .instructions(INSTRUCTIONS) + .rows(15) + .columns(40) + .splitUI(EnterEmptyDirectory::createAndShowUI) + .build() + .awaitAndCheck(); + } + + public static JPanel createAndShowUI() { + JButton button = new JButton("Show JFileChooser"); + button.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + JFileChooser jfc = new JFileChooser(); + jfc.setMultiSelectionEnabled(true); + jfc.showOpenDialog(null); + } + }); + JPanel p = new JPanel(); + p.setLayout(new BorderLayout()); + p.setSize(200, 200); + p.add(button); + return p; + } +} diff --git a/test/jdk/javax/swing/JFileChooser/FileFilterDescription/FileFilterDescription.java b/test/jdk/javax/swing/JFileChooser/FileFilterDescription.java similarity index 63% rename from test/jdk/javax/swing/JFileChooser/FileFilterDescription/FileFilterDescription.java rename to test/jdk/javax/swing/JFileChooser/FileFilterDescription.java index 2bca535fe8d0c..9da801d6af749 100644 --- a/test/jdk/javax/swing/JFileChooser/FileFilterDescription/FileFilterDescription.java +++ b/test/jdk/javax/swing/JFileChooser/FileFilterDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,44 +21,57 @@ * questions. */ -import java.applet.Applet; import java.io.File; - +import java.awt.BorderLayout; import javax.swing.JFileChooser; +import javax.swing.JFrame; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; import javax.swing.filechooser.FileFilter; -public final class FileFilterDescription extends Applet { +/* + * @test + * @bug 8029536 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FileFilterDescription + */ +public final class FileFilterDescription { - @Override - public void init() { - } - - @Override - public void start() { - try { - test(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } + private static final String INSTRUCTIONS = """ + 1) Check that current filter in the opened JFileChooser is a "CustomFileFilter". + 2) Close the JFileChooser. + 3) Test will repeat steps 1 - 2 for all supported look and feels. + 4) If it's true for all look and feels then click Pass else click Fail. """; + public static void main(String[] args) throws Exception { + PassFailJFrame passFailJFrame = PassFailJFrame.builder() + .title("JFileChooser Filefilter Instructions") + .instructions(INSTRUCTIONS) + .rows(10) + .columns(35) + .position(PassFailJFrame.Position.TOP_LEFT_CORNER) + .build(); - public static void test() throws Exception { final UIManager.LookAndFeelInfo[] infos = UIManager .getInstalledLookAndFeels(); for (final UIManager.LookAndFeelInfo info : infos) { SwingUtilities.invokeAndWait(() -> { - final JFileChooser chooser = new JFileChooser(); setLookAndFeel(info); + JFrame frame = new JFrame("JFileChooser FileFilter test"); + final JFileChooser chooser = new JFileChooser(); chooser.setAcceptAllFileFilterUsed(false); chooser.setFileFilter(new CustomFileFilter()); SwingUtilities.updateComponentTreeUI(chooser); + frame.add(chooser, BorderLayout.CENTER); + frame.pack(); + PassFailJFrame.addTestWindow(frame); + PassFailJFrame.positionTestWindow(frame, PassFailJFrame.Position.TOP_LEFT_CORNER); chooser.showDialog(null, "Open"); }); } + passFailJFrame.awaitAndCheck(); } private static void setLookAndFeel(final UIManager.LookAndFeelInfo info) { diff --git a/test/jdk/javax/swing/JFileChooser/FileFilterDescription/FileFilterDescription.html b/test/jdk/javax/swing/JFileChooser/FileFilterDescription/FileFilterDescription.html deleted file mode 100644 index 99133770bffd7..0000000000000 --- a/test/jdk/javax/swing/JFileChooser/FileFilterDescription/FileFilterDescription.html +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - -Follow the instructions below. -1) Check that current filter in the opened JFileChooser is a "CustomFileFilter". -2) Close the JFileChooser. -3) Test will repeat steps 1 - 2 for all supported look and feels. -4) If it's true for all look and feels then the test passed, otherwise it failed. - - diff --git a/test/jdk/javax/swing/JFileChooser/FileSelectionTests.java b/test/jdk/javax/swing/JFileChooser/FileSelectionTests.java new file mode 100644 index 0000000000000..728f7dc2b22e1 --- /dev/null +++ b/test/jdk/javax/swing/JFileChooser/FileSelectionTests.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.JButton; +import javax.swing.JFileChooser; +import javax.swing.JPanel; +import javax.swing.UIManager; + +/* + * @test + * @bug 4835633 + * @requires (os.family == "windows") + * @summary Test various file selection scenarios + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FileSelectionTests + */ + +public class FileSelectionTests { + private static final String INSTRUCTIONS = """ + This test is only for the Windows Look & Feel. + This is a test of file selection/deselection using the mouse. + There are quite a few steps. If any step doesn't behave as + expected, press Fail else press Pass. + + Make sure that you are in a directory with at least a few files. + Note that if you don't wait long enough between mouse buttons presses + that the action will be interpreted as a double-click and will dismiss + the dialog. Just re-show the dialog in this case. + + Press "Show Windows JFileChooser" button to show the JFileChooser. + + TEST 1: + Click on a filename. The file should become selected. + TEST 2: + Clear any selection. Click to right of a filename, + in the space between the filename and the file's icon in the next column. + The file should NOT be selected. If it becomes selected, press Fail. + TEST 3: + Select a filename. As in TEST 2, click in the empty space to the right of + the filename. The file should be deselected. + TEST 4: + Clear any selection. If necessary, resize the file dialog and/or change to + a directory with only a couple files, so that there is some space between + the list of files and the bottom of the file pane. + Click below the file list, in the empty space between the last file and + bottom of the file pane. The last file in the column above the cursor + should NOT become selected. If any file becomes selected, press Fail. + TEST 5: + Select a file. As in TEST 4, click in the empty space below the file list. + The selected file should become deselected. + TEST 6: + Clear any selection. As in TEST 4, click below the file list. + Then click on the last filename in the list. It should NOT go into edit mode. + TEST 7: + Clear any selection. Double-click below file list. The dialog should not be + dismissed, and no exception should be thrown. + TEST 8: + Clear any selection. As in TEST 2, press the mouse button in the empty space + to the right of a filename, but this time drag the mouse onto the filename. + The file should NOT become selected. + TEST 9: + Clear any selection. As in TEST 4, press the mouse button in the empty space + below the file list, but this time drag onto the last filename in the column. + The file should NOT become selected. + TEST 10: + Click on a filename, and then click again to go into rename mode. + Modify the filename, and then click to the right of the edit box. + The filename should be the new filename. + TEST 11: + As in TEST 10, rename a file, but this time end the editing by clicking below + the file list. Again, the file should retain the new name. + TEST 12: + Use shift-click to select several files. Hold "shift down" and click in + (1) the empty space to the right of a file name and + (2) in the empty space below the list of files. + The files should remain selected. If the selection is cleared press Fail. + TEST 13: + Switch to Details view. Repeat TESTS 1-11. + TEST 14: + Details view. Clear any selection. Click in the Size column. + No file should become selected. + TEST 15: + Details view. Select a file. Click in the Size column. + The file should be deselected. + TEST 16: + Details view. Shift-click to select several files. Shift-click in + (1) the empty space to the right of a filename + (2) in the Size column and + (3) below the list of files. + The files should remain selected. If the selection is cleared, press Fail. """; + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); + PassFailJFrame.builder() + .title("JFileChooser Instructions") + .instructions(INSTRUCTIONS) + .rows(25) + .columns(50) + .testTimeOut(10) + .splitUI(FileSelectionTests::createAndShowUI) + .build() + .awaitAndCheck(); + } + + public static JPanel createAndShowUI() { + JButton button = new JButton("Show Windows JFileChooser"); + button.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + JFileChooser jfc = new JFileChooser(); + jfc.setMultiSelectionEnabled(true); + jfc.showOpenDialog(null); + } + }); + JPanel p = new JPanel(); + p.setLayout(new BorderLayout()); + p.setSize(200, 200); + p.add(button); + return p; + } +} diff --git a/test/jdk/javax/swing/JFileChooser/HTMLFileName.java b/test/jdk/javax/swing/JFileChooser/HTMLFileName.java new file mode 100644 index 0000000000000..a8bc9525cca79 --- /dev/null +++ b/test/jdk/javax/swing/JFileChooser/HTMLFileName.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.File; +import java.util.List; +import javax.swing.Icon; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.filechooser.FileSystemView; + +/* + * @test id=metal + * @bug 8139228 + * @summary JFileChooser should not render Directory names in HTML format + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual HTMLFileName metal + */ + +/* + * @test id=system + * @bug 8139228 8358532 + * @summary JFileChooser should not render Directory names in HTML format + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual HTMLFileName system + */ + +public class HTMLFileName { + private static final String INSTRUCTIONS = """ + +
      +
    1. JFileChooser shows a virtual directory. + The first file in the list has the following name: + <html><h1 color=#ff00ff><font + face="Serif">Swing Rocks! +
      +
      +
    2. In HTML disabled frame: +
        +
      1. Verify that the first file name displays + as plain text, + that is you see the HTML tags in the file name. +
      2. If the file name in the file pane and + in the navigation combo box above is displayed + as HTML, that is in large font and magenta color, + then press Fail. +
      + +
    3. In HTML enabled frame: +
        +
      1. Verify that the first file name displays as HTML, + that is Swing Rocks! in large font + and magenta color.
        + Note: On macOS in Aqua L&F, the file name with + HTML displays as an empty file name. It is not an error. +
      2. If the file name in the file pane and + in the navigation combo box above is displayed + as HTML, then press Pass.
        + If it is in plain text, then press Fail. +
      +
    + + """; + + public static void main(String[] args) throws Exception { + if (args.length < 1) { + throw new IllegalArgumentException("Look-and-Feel keyword is required"); + } + + final String lafClassName; + switch (args[0]) { + case "metal" -> lafClassName = UIManager.getCrossPlatformLookAndFeelClassName(); + case "system" -> lafClassName = UIManager.getSystemLookAndFeelClassName(); + default -> throw new IllegalArgumentException("Unsupported Look-and-Feel keyword: " + args[0]); + } + + SwingUtilities.invokeAndWait(() -> { + try { + UIManager.setLookAndFeel(lafClassName); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + + System.out.println("Test for LookAndFeel " + lafClassName); + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(45) + .rows(20) + .testUI(HTMLFileName::initialize) + .positionTestUIBottomRowCentered() + .build() + .awaitAndCheck(); + System.out.println("Test passed for LookAndFeel " + lafClassName); + } + + private static List initialize() { + return List.of(createFileChooser(true), createFileChooser(false)); + } + + private static JFrame createFileChooser(boolean htmlDisabled) { + JFileChooser jfc = new JFileChooser(new VirtualFileSystemView()); + jfc.putClientProperty("html.disable", htmlDisabled); + jfc.setControlButtonsAreShown(false); + + JFrame frame = new JFrame(htmlDisabled ? "HTML disabled" : "HTML enabled"); + frame.add(jfc); + frame.pack(); + return frame; + } + + private static class VirtualFileSystemView extends FileSystemView { + private final File[] files = { + new File("/", "

    Swing Rocks!"), + new File("/", "virtualFile1.txt"), + new File("/", "virtualFile2.log") + }; + + @Override + public File createNewFolder(File containingDir) { + return null; + } + + @Override + public File[] getRoots() { + return files; + } + + @Override + public File getHomeDirectory() { + return new File("/"); + } + + @Override + public File getDefaultDirectory() { + return new File("/"); + } + + @Override + public File[] getFiles(File dir, boolean useFileHiding) { + // Simulate a virtual folder structure + return files; + } + + @Override + public Icon getSystemIcon(File f) { + return null; + } + } +} diff --git a/test/jdk/javax/swing/JFileChooser/JFileChooserSetLocationTest.java b/test/jdk/javax/swing/JFileChooser/JFileChooserSetLocationTest.java index 0e1831afb315a..c1d49127f76e4 100644 --- a/test/jdk/javax/swing/JFileChooser/JFileChooserSetLocationTest.java +++ b/test/jdk/javax/swing/JFileChooser/JFileChooserSetLocationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ import java.awt.Point; import java.awt.Rectangle; import java.awt.Robot; +import java.awt.Toolkit; import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; @@ -209,7 +210,11 @@ private static void hitKeys(int... keys) { } public static void createUI() { - frame = new JFrame(); + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + + int xPos = (int) screenSize.getWidth() / 2; + int yPos = (int) screenSize.getHeight() / 2; + frame = new JFrame("FileChooser set location test"); panel = new JPanel(); btn = new JButton(SHOW_DIALOG_OUTSIDE_THE_PANEL); btn1 = new JButton(SHOW_DIALOG_OVER_THE_PANEL); @@ -238,6 +243,7 @@ public static void createUI() { frame.setLocationRelativeTo(null); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.pack(); + frame.setLocation(xPos, yPos - 200); frame.setVisible(true); } @@ -280,7 +286,6 @@ protected JDialog createDialog(Component parent) System.out.println( "createDialog and set location to (" + x + ", " + y + ")"); dialog.setLocation(x, y); - return dialog; } @@ -289,5 +294,4 @@ public Point getDialogLocation() { } } - } diff --git a/test/jdk/javax/swing/JFileChooser/ShowHiddenFiles.java b/test/jdk/javax/swing/JFileChooser/ShowHiddenFiles.java new file mode 100644 index 0000000000000..ddca136755591 --- /dev/null +++ b/test/jdk/javax/swing/JFileChooser/ShowHiddenFiles.java @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeEvent; +import javax.swing.JComponent; +import javax.swing.JButton; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; + +/* + * @test + * @bug 4835479 + * @requires (os.family == "windows") + * @summary JFileChooser should respect native setting for showing hidden files + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ShowHiddenFiles + */ + +public class ShowHiddenFiles +{ + private static final String INSTRUCTIONS = """ + This tests JFileChooser's ability to track the native setting for + displaying of hidden files. + This test has four parts. If any portion of any of the tests don't + behave as specified, press FAIL else press PASS. + Before beginning the tests, you'll want to find the Folder Options + dialog on your Windows platform. Open an Explorer window for c:/ + and select Tools->Folder Options. Under the View tab, locate + the option to show hidden files. You will need this for the tests. + + TEST 1: + This tests that JFileChooser tracks the native platform setting. + Configure windows to Show Hidden Files, and in an Explorer window + locate a hidden file that is now shown (there are usually hidden + files in c:/). + Click on the TEST 1 button to display a JFileChooser. + Confirm that the hidden files are shown in the JFileChooser. + On Windows 98, TEST 1 is now complete. + On Windows 2000 and later, configure Folder Options to hide hidden + files. Confirm that + (1) the files are hidden in the JFileChooser and + (2) "PropertyChangeEvent for FILE_HIDING_CHANGED_PROPERTY" + appears in the accessory text field. + Re-enable showing of hidden files and confirm that + (1) the hidden files are again shown and + (2) you get another PropertyChangeEvent. + Press "Cancel" button to close JFileChooser window. + + TEST 2: + This tests that JFileChooser.setFileHidingEnabled(true) overrides the + native platform setting. + Make sure Windows is configured to Show Hidden Files. + Click on the TEST 2 button. + Confirm that hidden files are NOT displayed in the JFileChooser. + Press "Cancel" button to close JFileChooser window. + + TEST 3: + This tests that JFileChooser.setFileHidingEnabled(false) overrides the + Make sure Windows is configured to NOT show hidden files. + Click on the TEST 3 button. + Confirm that hidden files ARE displayed in the JFileChooser. + Press "Cancel" button to close JFileChooser window. + + TEST 4: + This tests that calling setFileHidingEnabled() on a showing + JFileChooser will cause it to ignore further changes in the + native platform setting. + Click on the TEST 4 button. As in TEST 1, confirm that the + JFileChooser tracks the native setting. + Click on the "Show Hidden Files" button. + Confirm that hidden files remain visible, even when you change + the native setting. + Repeat the test for the "Hide Hidden Files" button. + Press "Cancel" button to close JFileChooser window. + """; + private static JButton test1, test2, test3, test4; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("JFileChooser Instructions") + .instructions(INSTRUCTIONS) + .rows(25) + .columns(50) + .splitUI(ShowHiddenFiles::createAndShowUI) + .build() + .awaitAndCheck(); + } + + public static JPanel createAndShowUI() { + test1 = new JButton("TEST 1: Track native setting"); + test2 = new JButton("TEST 2: setFileHidingEnabled(true)"); + test3 = new JButton("TEST 3: setFileHidingEnabled(false)"); + test4 = new JButton("TEST 4: setFileHidingEnabled() on showing JFC"); + + ButtonListener bl = new ButtonListener(); + test1.addActionListener(bl); + test2.addActionListener(bl); + test3.addActionListener(bl); + test4.addActionListener(bl); + + JPanel p = new JPanel(); + p.setLayout(new GridLayout(4,1)); + p.setSize(200, 200); + p.add(test1); + p.add(test2); + p.add(test3); + p.add(test4); + return p; + } + + private static class ButtonListener implements ActionListener { + public void actionPerformed(ActionEvent e) { + JFileChooser jfc = new JFileChooser("c:/"); + if (e.getSource() == test1) { + jfc.setAccessory(createTest1Acc(jfc)); + } + else if (e.getSource() == test2) { + jfc.setAccessory(null); + jfc.setFileHidingEnabled(true); + } + else if (e.getSource() == test3) { + jfc.setAccessory(null); + jfc.setFileHidingEnabled(false); + } + else if (e.getSource() == test4) { + jfc.setAccessory(createTest4Acc(jfc)); + } + else { + return; + } + jfc.showOpenDialog(new JFrame()); + } + } + + private static class JFCHideButton extends JButton implements ActionListener { + JFileChooser jfc; + boolean setVal; + + public JFCHideButton(String label, JFileChooser jfc, boolean setVal) { + super(label); + this.jfc = jfc; + this.setVal = setVal; + addActionListener(this); + } + public void actionPerformed(ActionEvent e) { + jfc.setFileHidingEnabled(setVal); + } + } + + private static JPanel createTest1Acc(JFileChooser jfc) { + JPanel jpl = new JPanel(); + jpl.add(createTAListener(jfc)); + return jpl; + } + + private static JPanel createTest4Acc(JFileChooser jfc) { + JPanel jpl = new JPanel(); + jpl.setLayout(new BorderLayout()); + + JPanel north = new JPanel(); + north.setLayout(new GridLayout(2,1)); + north.add(new JFCHideButton("Show Hidden Files", jfc, false)); + north.add(new JFCHideButton("Hide Hidden Files", jfc, true)); + jpl.add(BorderLayout.NORTH, north); + jpl.add(BorderLayout.CENTER, createTAListener(jfc)); + return jpl; + } + + private static JComponent createTAListener(JFileChooser jfc) { + final JTextArea jta = new JTextArea(10,20); + PropertyChangeListener pcl = new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent e) { + jta.append("PropertyChangeEvent for FILE_HIDING_CHANGED_PROPERTY\n"); + } + }; + jfc.addPropertyChangeListener(JFileChooser.FILE_HIDING_CHANGED_PROPERTY, pcl); + JScrollPane jsp = new JScrollPane(jta); + return jsp; + } +} diff --git a/test/jdk/javax/swing/JFileChooser/bug4357012.java b/test/jdk/javax/swing/JFileChooser/bug4357012.java new file mode 100644 index 0000000000000..2dfdcd336d5fd --- /dev/null +++ b/test/jdk/javax/swing/JFileChooser/bug4357012.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4357012 + * @requires (os.family == "windows") + * @summary JFileChooser.showSaveDialog inconsistent with Windows Save Dialog + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4357012 + */ + +import java.io.File; +import java.io.IOException; +import javax.swing.JComponent; +import javax.swing.JFileChooser; +import javax.swing.UIManager; + +public class bug4357012 { + private static File workDir = null; + private static File dir = null; + private static File file = null; + private static final String INSTRUCTIONS = """ + + Test is for Windows LAF only +

    In JFileChooser's files list : +

      +
    1. Select directory. Verify that the directory name doesn't + appear in "file name" field.
    2. +
    3. Select file. Verify that the file name appears in + "file name" field.
    4. +
    5. Select directory again. Verify that the previous file name + remains in file name field.
    6. +
    +

    + + """; + + public static void main(String[] argv) throws Exception { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + createTestDir(); + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .rows(10) + .columns(40) + .testUI(bug4357012::createTestUI) + .build() + .awaitAndCheck(); + } finally { + if (workDir != null) { + System.out.println("Deleting '" + file + "': " + file.delete()); + System.out.println("Deleting '" + dir + "': " + dir.delete()); + System.out.println("Deleting '" + workDir + "': " + workDir.delete()); + } + } + } + + private static void createTestDir() throws IOException { + String tempDir = "."; + String fs = System.getProperty("file.separator"); + + workDir = new File(tempDir + fs + "bug4357012"); + System.out.println("Creating '" + workDir + "': " + workDir.mkdir()); + + dir = new File(workDir + fs + "Directory"); + System.out.println("Creating '" + dir + "': " + dir.mkdir()); + + file = new File(workDir + fs + "File.txt"); + System.out.println("Creating '" + file + "': " + file.createNewFile()); + } + + private static JComponent createTestUI() { + JFileChooser fc = new JFileChooser(workDir); + fc.setDialogType(JFileChooser.SAVE_DIALOG); + return fc; + } +} diff --git a/test/jdk/javax/swing/JFileChooser/bug4464774.java b/test/jdk/javax/swing/JFileChooser/bug4464774.java new file mode 100644 index 0000000000000..368063e46cd8d --- /dev/null +++ b/test/jdk/javax/swing/JFileChooser/bug4464774.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4464774 + * @requires (os.family == "windows") + * @summary JFileChooser: selection of left-side folder buttons shown incorrectly + in Windows L&F + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4464774 + */ + +import javax.swing.JFileChooser; +import javax.swing.UIManager; + +public class bug4464774 { + private static final String INSTRUCTIONS = """ + Click any button from the buttons to the left + ("Documents", "Desktop", "My Computer" etc.) in FileChooser dialog. + When the button is toggled, it should be lowered and + should NOT have focus painted inside it (black dotted frame). + + If the above is true, press PASS else FAIL. + """; + + public static void main(String[] argv) throws Exception { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(65) + .rows(10) + .testUI(() -> { + JFileChooser jfc = new JFileChooser(); + jfc.setControlButtonsAreShown(false); + return jfc; + }) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/javax/swing/JFileChooser/bug4522756.java b/test/jdk/javax/swing/JFileChooser/bug4522756.java new file mode 100644 index 0000000000000..87c430cfb1d67 --- /dev/null +++ b/test/jdk/javax/swing/JFileChooser/bug4522756.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4522756 + * @requires (os.family == "windows") + * @summary Verifies that the Desktop icon is not missing when + JFileChooser is opened for the first time. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4522756 + */ + +import javax.swing.JFileChooser; +import javax.swing.UIManager; + +public class bug4522756 { + private static final String INSTRUCTIONS = """ + Verify the following: + + 1. If Desktop icon image is present on the Desktop button + on the left panel of JFileChooser. + 2. Press Desktop button. Check that you actually + go up to the desktop. + + If the above is true, press PASS else FAIL. + """; + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(50) + .rows(12) + .testUI(() -> { + JFileChooser jfc = new JFileChooser(); + jfc.setControlButtonsAreShown(false); + return jfc; + }) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/javax/swing/JFileChooser/bug4759934.java b/test/jdk/javax/swing/JFileChooser/bug4759934.java new file mode 100644 index 0000000000000..08ccdebfb2be0 --- /dev/null +++ b/test/jdk/javax/swing/JFileChooser/bug4759934.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @key headful + * @bug 4759934 + * @summary windows activation problem + * @library /javax/swing/regtesthelpers + * @build Util + * @run main bug4759934 + */ + +import java.awt.Dialog; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +public class bug4759934 { + private static JFrame fr; + private static Dialog dlg; + private static JFileChooser jfc; + + private static JButton frameBtn; + private static JButton dialogBtn; + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + robot.setAutoWaitForIdle(true); + robot.setAutoDelay(50); + + SwingUtilities.invokeAndWait(bug4759934::createTestUI); + robot.waitForIdle(); + robot.delay(1000); + + Point frameBtnLoc = Util.getCenterPoint(frameBtn); + robot.mouseMove(frameBtnLoc.x, frameBtnLoc.y); + robot.mousePress(MouseEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(MouseEvent.BUTTON1_DOWN_MASK); + robot.delay(500); + + Point dlgBtnLoc = Util.getCenterPoint(dialogBtn); + robot.mouseMove(dlgBtnLoc.x , dlgBtnLoc.y); + robot.mousePress(MouseEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(MouseEvent.BUTTON1_DOWN_MASK); + robot.delay(500); + + robot.keyPress(KeyEvent.VK_ESCAPE); + robot.keyRelease(KeyEvent.VK_ESCAPE); + robot.delay(500); + + SwingUtilities.invokeAndWait(() -> { + if (frameBtn.hasFocus() && !dialogBtn.hasFocus()) { + throw new RuntimeException("Test failed! Focus was passed back" + + " to Frame instead of Dialog"); + } + }); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (dlg != null) { + dlg.dispose(); + } + if (fr != null) { + fr.dispose(); + } + }); + } + } + + private static void createTestUI() { + fr = new JFrame("bug4759934 - JFrame"); + + frameBtn = new JButton("Show Dialog"); + frameBtn.addActionListener(e -> createDialog()); + fr.add(frameBtn); + + fr.setSize(300, 200); + fr.setLocationRelativeTo(null); + fr.setVisible(true); + } + + private static void createDialog() { + dlg = new JDialog(fr, "bug4759934 - JDialog"); + + dialogBtn = new JButton("Show FileChooser"); + dlg.add(dialogBtn); + + dialogBtn.addActionListener(e -> { + jfc = new JFileChooser(); + jfc.showOpenDialog(dlg); + }); + + dlg.setSize(300, 200); + dlg.setLocation(fr.getX() + fr.getWidth() + 10, fr.getY()); + dlg.setVisible(true); + } +} diff --git a/test/jdk/javax/swing/JFileChooser/bug4926884.java b/test/jdk/javax/swing/JFileChooser/bug4926884.java new file mode 100644 index 0000000000000..29d6c4e913450 --- /dev/null +++ b/test/jdk/javax/swing/JFileChooser/bug4926884.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4926884 + * @requires (os.family == "windows") + * @summary Win L&F: JFileChooser problems with "My Documents" folder + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4926884 + */ + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import javax.swing.JFileChooser; +import javax.swing.UIManager; + +public class bug4926884 { + private static final String INSTRUCTIONS = """ + Validate next statements step by step: + + 1. In the file list there are several dirs and files (like "ski", + "Snowboard" etc.) + 2. Select "Details" view mode. + 3. Make file list in focus (e.g. by pressing mouse button) + 4. Press key "w" several times with delay LESS than 1 second. + Selection should be changed across files started with letter "w" + (without case sensitive). + 5. Press key "w" several times with delay MORE than 1 second. + Selection should be changed across files started with letter "w" + (without case sensitive). + 6. Type "winnt" (with delay less than 1 second between letters) - + directory "winnt" should be selected. + 7. Change conditions: + - Move column "Name" to the second position + - Change sort mode by clicking column "Size" + 8. Repeat items 4-6 + + If above is true press PASS else FAIL + """; + + private static final String[] DIRS = {"www", "winnt", "ski"}; + private static final String[] FILES = {"Window", "weel", "mice", + "Wall", "Snowboard", "wood"}; + private static final File testDir = new File("."); + + public static void main(String[] argv) throws Exception { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + try { + createTestDir(); + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(() -> new JFileChooser(testDir)) + .build() + .awaitAndCheck(); + } finally { + deleteTempDir(); + } + } + + private static void createTestDir() throws IOException { + testDir.mkdir(); + + for (String dir : DIRS) { + new File(testDir, dir).mkdir(); + } + + for (int i = 0; i < FILES.length; i++) { + + try (OutputStream outputStream = new FileOutputStream( + new File(testDir, FILES[i]))) { + for (int j = 0; j < i * 1024; j++) { + outputStream.write('#'); + } + } + } + } + + private static void deleteTempDir() { + File[] files = testDir.listFiles(); + + for (File file : files) { + if (file != null) { + file.delete(); + } + } + + testDir.delete(); + } +} diff --git a/test/jdk/javax/swing/JFileChooser/bug4943900.java b/test/jdk/javax/swing/JFileChooser/bug4943900.java new file mode 100644 index 0000000000000..0cea17e764ccc --- /dev/null +++ b/test/jdk/javax/swing/JFileChooser/bug4943900.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4943900 + * @summary Tests that FileFilter combo box is shown in FileChooser + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4943900 + */ + +import java.io.File; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.UIManager; +import javax.swing.filechooser.FileFilter; + +public class bug4943900 { + private static final String INSTRUCTIONS = """ + +
      +
    1. When the test runs, a JFileChooser will be displayed. +
    2. Ensure that there is a filter combo box with these two items: +
        +
      • Text Files (*.txt) + — [must be selected when the dialog opens] +
      • All Files +
      +
    3. Leave the Text files item selected and check that the + filter works: only *.txt files can appear in the file list. + You can navigate directories in the file chooser and find one + that contains some *.txt files to ensure they are shown in + the file list. On macOS when the text filter is applied verify + that the non-text files are greyed out. +
    4. Try switching the filters and ensure that the file list + is updated properly. +
    5. If the FileFilter works correctly, + press Pass else press Fail. +
    + + """; + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + + PassFailJFrame.builder() + .title("bug4943900 Test Instructions") + .instructions(INSTRUCTIONS) + .rows(14) + .columns(50) + .testUI(bug4943900::createAndShowUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createAndShowUI() { + JFileChooser fc = new JFileChooser(); + fc.setControlButtonsAreShown(false); + TextFileFilter filter = new TextFileFilter(); + fc.setFileFilter(filter); + + JFrame frame = new JFrame("bug4943900 - JFileChooser"); + frame.add(fc); + frame.pack(); + return frame; + } + + private static final class TextFileFilter extends FileFilter { + @Override + public boolean accept(File f) { + if (f != null) { + if (f.isDirectory()) { + return true; + } + String extension = getExtension(f); + return extension != null && extension.equals("txt"); + } + return false; + } + + @Override + public String getDescription() { + return "Text Files (*.txt)"; + } + + private static String getExtension(File f) { + if (f != null) { + String filename = f.getName(); + int i = filename.lastIndexOf('.'); + if (i > 0 && i < filename.length() - 1) { + return filename.substring(i + 1).toLowerCase(); + } + } + return null; + } + } +} diff --git a/test/jdk/javax/swing/JFileChooser/bug5045464.java b/test/jdk/javax/swing/JFileChooser/bug5045464.java new file mode 100644 index 0000000000000..0fbeedcec6bd4 --- /dev/null +++ b/test/jdk/javax/swing/JFileChooser/bug5045464.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 5045464 + * @requires (os.family == "linux") + * @summary Regression: GTK L&F, JFileChooser shows "null/" in folder list + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug5045464 + */ + +import javax.swing.JComponent; +import javax.swing.JFileChooser; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; + +public class bug5045464 { + private static final String INSTRUCTIONS = """ + When the filechooser appears check the directory list (the left list). + If it starts with two items: "./" (current directory) + and "../" (parent directory) press PASS. + If something else is here (e.g. "null/" instead of "./") + press FAIL. + """; + + public static void main(String[] argv) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug5045464::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JComponent createTestUI() { + JFileChooser fc = new JFileChooser(); + fc.setControlButtonsAreShown(false); + try { + UIManager.setLookAndFeel("com.sun.java.swing.plaf.gtk.GTKLookAndFeel"); + } catch (Exception ex) { + throw new RuntimeException("Test Failed!", ex); + } + SwingUtilities.updateComponentTreeUI(fc); + return fc; + } +} diff --git a/test/jdk/javax/swing/JFileChooser/bug6515169.java b/test/jdk/javax/swing/JFileChooser/bug6515169.java new file mode 100644 index 0000000000000..98cd813eefb26 --- /dev/null +++ b/test/jdk/javax/swing/JFileChooser/bug6515169.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6515169 + * @requires (os.family == "windows") + * @summary wrong grid header in JFileChooser + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug6515169 + */ + +import javax.swing.ButtonGroup; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; + +public class bug6515169 { + private static JFrame frame; + private static final String INSTRUCTIONS = """ + This test is to verify JFileChooser on Windows and Metal LAF. + Use the "Change LaF" menu to switch between the 2 LaF + and verify the following. + + a. Change view mode to "Details" + b. Check that 4 columns appear: Name, Size, Type and Date Modified + c. Change current directory by pressing any available subdirectory + or by pressing button "Up One Level". + d. Check that still four columns exist. + + Change LaF and repeat the steps a-d. + If all conditions are true press PASS, else FAIL. + """; + + public static void main(String[] argv) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug6515169::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + frame = new JFrame("bug6515169"); + JMenuBar bar = new JMenuBar(); + JMenu lafMenu = new JMenu("Change LaF"); + ButtonGroup lafGroup = new ButtonGroup(); + JCheckBoxMenuItem lafItem1 = new JCheckBoxMenuItem("Window LaF"); + lafItem1.addActionListener(e -> + setLaF(UIManager.getSystemLookAndFeelClassName())); + lafGroup.add(lafItem1); + lafMenu.add(lafItem1); + + JCheckBoxMenuItem lafItem2 = new JCheckBoxMenuItem("Metal LaF"); + lafItem2.addActionListener(e -> + setLaF(UIManager.getCrossPlatformLookAndFeelClassName())); + lafGroup.add(lafItem2); + lafMenu.add(lafItem2); + + bar.add(lafMenu); + frame.setJMenuBar(bar); + + String dir = "."; + JFileChooser fc = new JFileChooser(dir); + fc.setControlButtonsAreShown(false); + frame.add(fc); + frame.pack(); + + return frame; + } + + private static void setLaF(String laf) { + try { + UIManager.setLookAndFeel(laf); + SwingUtilities.updateComponentTreeUI(frame); + } catch (Exception e) { + throw new RuntimeException("Test Failed!", e); + } + } +} diff --git a/test/jdk/javax/swing/JFileChooser/6798062/bug6798062.java b/test/jdk/javax/swing/JFileChooser/bug6798062.java similarity index 69% rename from test/jdk/javax/swing/JFileChooser/6798062/bug6798062.java rename to test/jdk/javax/swing/JFileChooser/bug6798062.java index 2c32f5f2b5ebc..b3a313b3ca9b0 100644 --- a/test/jdk/javax/swing/JFileChooser/6798062/bug6798062.java +++ b/test/jdk/javax/swing/JFileChooser/bug6798062.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,54 +21,82 @@ * questions. */ -/* @test %W% %E% - @bug 6798062 - @summary Memory Leak on using getFiles of FileSystemView - @author Pavel Porvatov - @modules java.desktop/sun.awt - java.desktop/sun.awt.shell - @run applet/manual=done bug6798062.html -*/ +/* + * @test + * @bug 6798062 + * @requires (os.family == "windows") + * @summary Memory Leak on using getFiles of FileSystemView + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @modules java.desktop/sun.awt + * java.desktop/sun.awt.shell + * @run main/manual bug6798062 + */ import sun.awt.OSInfo; import sun.awt.shell.ShellFolder; -import javax.swing.*; +import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.*; import java.io.File; import java.io.FileNotFoundException; - -public class bug6798062 extends JApplet { - - private final JSlider slider = new JSlider(0, 100); - - private final JTextField tfLink = new JTextField(); - - private final JButton btnStart = new JButton("Start"); - - private final JButton btnStop = new JButton("Stop"); - - private final JButton btnGC = new JButton("Run System.gc()"); +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.JTextField; + +public class bug6798062 { + + private static final String INSTRUCTIONS = """ + The test is suitable only for Windows. + + 1. Create a shortcut (.lnk) file + 2. Copy path to the shortcut (.lnk file) into TextField + 3. Run the Windows Task Manager. Select the Processes tab and find the java process + 4. Press the Start button in the test window + 5. Wait several minutes and observe in the Windows Task Manager + that Memory Usage of java process is not increasing + If memory usage is increasing, click Fail else click Pass."""; + + private static JSlider slider; + private static JTextField tfLink; + private static JButton btnStart; + private static JButton btnStop; + private static JButton btnGC; private ShellFolder folder; - private Thread thread; - public static void main(String[] args) { + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("JFileChooser Instructions") + .instructions(INSTRUCTIONS) + .testTimeOut(10) + .rows(10) + .columns(35) + .testUI(bug6798062::createUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createUI() { + slider = new JSlider(0, 100); + tfLink = new JTextField(); + btnStart = new JButton("Start"); + btnStop = new JButton("Stop"); + btnGC = new JButton("Run System.gc()"); JFrame frame = new JFrame("bug6798062"); frame.setSize(400, 300); - frame.setLocationRelativeTo(null); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(new bug6798062().initialize()); - frame.setVisible(true); - } - - public void init() { - add(initialize()); + return frame; } private JComponent initialize() { @@ -87,7 +115,7 @@ private JComponent initialize() { try { folder = ShellFolder.getShellFolder(new File(tempDir)); } catch (FileNotFoundException e) { - fail("Directory " + tempDir + " not found"); + fail("Directory not found"); } slider.setMajorTickSpacing(10); @@ -153,7 +181,7 @@ private void setEnabledState(boolean enabled) { } private static void fail(String msg) { - throw new RuntimeException(msg); + PassFailJFrame.forceFail(msg); } private class MyThread extends Thread { @@ -169,7 +197,7 @@ private MyThread(int delay, String link) { try { linkFolder = ShellFolder.getShellFolder(new File(link)); } catch (FileNotFoundException e) { - e.printStackTrace(); + fail("File not found"); linkFolder = null; } @@ -184,7 +212,7 @@ public void run() { try { link.getLinkLocation(); } catch (FileNotFoundException e) { - e.printStackTrace(); + fail("File not found"); } } diff --git a/test/jdk/javax/swing/JFrame/bug4419914.java b/test/jdk/javax/swing/JFrame/bug4419914.java index 4574b1ac3d6ff..f87dcc8c46889 100644 --- a/test/jdk/javax/swing/JFrame/bug4419914.java +++ b/test/jdk/javax/swing/JFrame/bug4419914.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,55 +28,141 @@ * @library /java/awt/regtesthelpers * @build PassFailJFrame * @run main/manual bug4419914 -*/ + */ import java.awt.BorderLayout; import java.awt.ComponentOrientation; +import java.awt.Frame; +import java.awt.Window; +import java.util.List; +import java.util.Locale; import javax.swing.JButton; +import javax.swing.JDialog; import javax.swing.JFrame; -import java.util.Locale; +import javax.swing.JLabel; +import javax.swing.JWindow; public class bug4419914 { + private static JFrame frame; private static final String INSTRUCTIONS = """ - 1. You will see a frame with five buttons. - 2. Confirm that each button is placed as follows: - NORTH - END CENTER START - SOUTH - 3. Press the "NORTH" button and confirm the button is focused. - 4. Press TAB repeatedly and confirm that the TAB focus moves from right to left. - (NORTH - START - CENTER - END - SOUTH - NORTH - START - CENTER - ...) + This test verifies tab movement on RTL component orientation + in JWindow, JFrame and JDialog. + + When test starts 3 test windows are displayed - JFrame, JWindow and JDialog. + Follow the instructions below and if any condition does not hold + press FAIL. + + 1. Confirm that each button in the child window is placed as follows: + + For JFrame: + NORTH + END CENTER START + SOUTH + + For JWindow: + END CENTER START + QUIT + + For JDialog: + END CENTER START + + 3. Press on the "START" button in case of JWindow & JDialog and "NORTH" + in case of JFrame, confirm that the respective button is focused. + + 4. Press TAB repeatedly and confirm that the TAB focus moves + from right to left. + + For JFrame: + (NORTH - START - CENTER - END - SOUTH - NORTH - START - CENTER - ...) - If there's anything different from the above items, click Fail else click Pass."""; + For JWindow: + (START - CENTER - END - QUIT - START - CENTER - END - QUIT - ...) + + For JDialog: + (START - CENTER - END - START - CENTER - END - ...) + + If all of the above conditions are true press PASS else FAIL. + """; public static void main(String[] args) throws Exception { PassFailJFrame.builder() - .title("Tab movement Instructions") .instructions(INSTRUCTIONS) - .rows((int) INSTRUCTIONS.lines().count() + 2) - .columns(48) - .testUI(bug4419914::createTestUI) + .columns(45) + .testTimeOut(10) + .testUI(bug4419914::createAndShowUI) + .positionTestUI(WindowLayouts::rightOneColumn) .build() .awaitAndCheck(); } - private static JFrame createTestUI() { - JFrame frame = new JFrame("bug4419914"); + private static List createAndShowUI() { + return List.of(createJFrame(), createJWindow(), createJDialog()); + } + + private static JFrame createJFrame() { + frame = new JFrame("bug4419914 JFrame"); frame.setFocusCycleRoot(true); + // Tab movement set to RTL frame.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); frame.setLocale(Locale.ENGLISH); frame.enableInputMethods(false); + // Component placement within content pane set to RTL frame.getContentPane().setComponentOrientation( - ComponentOrientation.RIGHT_TO_LEFT); + ComponentOrientation.RIGHT_TO_LEFT); frame.getContentPane().setLocale(Locale.ENGLISH); - frame.getContentPane().setLayout(new BorderLayout()); + frame.setLayout(new BorderLayout()); frame.add(new JButton("SOUTH"), BorderLayout.SOUTH); frame.add(new JButton("CENTER"), BorderLayout.CENTER); frame.add(new JButton("END"), BorderLayout.LINE_END); frame.add(new JButton("START"), BorderLayout.LINE_START); frame.add(new JButton("NORTH"), BorderLayout.NORTH); - frame.setSize(300, 150); + frame.setSize(300, 160); return frame; } + + private static JWindow createJWindow() { + JWindow window = new JWindow(frame); + window.setFocusableWindowState(true); + // Tab movement set to RTL + window.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); + window.setLocale(new Locale("en")); + window.enableInputMethods(false); + + // Component placement within content pane set to RTL + window.getContentPane().setComponentOrientation( + ComponentOrientation.RIGHT_TO_LEFT); + window.getContentPane().setLocale(new Locale("en")); + window.setLayout(new BorderLayout()); + window.add(new JLabel("bug4419914 JWindow"), BorderLayout.NORTH); + window.add(new JButton("START"), BorderLayout.LINE_START); + window.add(new JButton("CENTER"), BorderLayout.CENTER); + window.add(new JButton("END"), BorderLayout.LINE_END); + + JButton quitButton = new JButton("QUIT"); + quitButton.addActionListener(e1 -> window.dispose()); + window.add(quitButton, BorderLayout.SOUTH); + window.setSize(300, 153); + window.requestFocus(); + return window; + } + + private static JDialog createJDialog() { + JDialog dialog = new JDialog((Frame) null, "bug4419914 JDialog"); + // Tab movement set to RTL + dialog.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); + dialog.setLocale(new Locale("en")); + dialog.enableInputMethods(false); + + // Component placement within content pane set to RTL + dialog.getContentPane().setComponentOrientation( + ComponentOrientation.RIGHT_TO_LEFT); + dialog.getContentPane().setLocale(new Locale("en")); + dialog.setLayout(new BorderLayout()); + dialog.add(new JButton("CENTER"), BorderLayout.CENTER); + dialog.add(new JButton("END"), BorderLayout.LINE_END); + dialog.add(new JButton("START"), BorderLayout.LINE_START); + dialog.setSize(300, 160); + return dialog; + } } diff --git a/test/jdk/javax/swing/JFrame/bug4614881.java b/test/jdk/javax/swing/JFrame/bug4614881.java new file mode 100644 index 0000000000000..bbefc1ebf3c29 --- /dev/null +++ b/test/jdk/javax/swing/JFrame/bug4614881.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4614881 + * @summary Ensure that client decorated frames can be brought to the front + * via mouse click on the title pane. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4614881 + */ + +import java.awt.FlowLayout; +import java.awt.Toolkit; +import javax.swing.JDialog; +import javax.swing.JFrame; + +public class bug4614881 { + + private static final String INSTRUCTIONS = """ + Select a native window so that it obscures the client decorated frame. + Select the decorated frame by clicking on the title pane. + If the decorated frame is brought to the front, then test passes else fails."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug4614881 Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(bug4614881::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + Toolkit.getDefaultToolkit().setDynamicLayout(true); + JFrame.setDefaultLookAndFeelDecorated(true); + JDialog.setDefaultLookAndFeelDecorated(true); + final JFrame frame = new JFrame("4614881 - Decorated Frame"); + frame.setSize(600, 400); + frame.setResizable(false); + frame.getContentPane().setLayout(new FlowLayout()); + return frame; + } +} diff --git a/test/jdk/javax/swing/JInternalFrame/6726866/bug6726866.html b/test/jdk/javax/swing/JInternalFrame/6726866/bug6726866.html deleted file mode 100644 index 0db8864e8d8cd..0000000000000 --- a/test/jdk/javax/swing/JInternalFrame/6726866/bug6726866.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - -Drag the internal frame inside the green undecorated window, -if you can drag it the test passes, otherwise fails. - - diff --git a/test/jdk/javax/swing/JInternalFrame/8020708/bug8020708.java b/test/jdk/javax/swing/JInternalFrame/8020708/bug8020708.java index efd13ff768680..8f78178ada62e 100644 --- a/test/jdk/javax/swing/JInternalFrame/8020708/bug8020708.java +++ b/test/jdk/javax/swing/JInternalFrame/8020708/bug8020708.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,7 +41,7 @@ * @summary NLS: mnemonics missing in SwingSet2/JInternalFrame demo * @library ../../regtesthelpers * @build Util - * @run main bug8020708 + * @run main/timeout=300 bug8020708 */ public class bug8020708 { @@ -68,6 +68,7 @@ public class bug8020708 { public static void main(String[] args) throws Exception { for (Locale locale : SUPPORTED_LOCALES) { + System.out.println("locale: " + locale); for (String laf : LOOK_AND_FEELS) { Locale.setDefault(locale); if (!installLookAndFeel(laf)) { @@ -80,7 +81,7 @@ public static void main(String[] args) throws Exception { static void testInternalFrameMnemonic(Locale locale) throws Exception { Robot robot = new Robot(); - robot.setAutoDelay(250); + robot.setAutoDelay(100); robot.setAutoWaitForIdle(true); SwingUtilities.invokeAndWait(new Runnable() { @@ -142,6 +143,7 @@ static final boolean installLookAndFeel(String lafName) throws Exception { UIManager.LookAndFeelInfo[] infos = UIManager.getInstalledLookAndFeels(); for (UIManager.LookAndFeelInfo info : infos) { if (info.getClassName().contains(lafName)) { + System.out.println("LookAndFeel: " + info.getClassName()); UIManager.setLookAndFeel(info.getClassName()); return true; } diff --git a/test/jdk/javax/swing/JInternalFrame/8160248/JInternalFrameDraggingTest.java b/test/jdk/javax/swing/JInternalFrame/8160248/JInternalFrameDraggingTest.java index 81f5e0d08020d..7c3732925bee8 100644 --- a/test/jdk/javax/swing/JInternalFrame/8160248/JInternalFrameDraggingTest.java +++ b/test/jdk/javax/swing/JInternalFrame/8160248/JInternalFrameDraggingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,6 +21,7 @@ * questions. */ +import java.io.File; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Point; @@ -28,6 +29,7 @@ import java.awt.Robot; import java.awt.event.InputEvent; import java.awt.image.BufferedImage; +import javax.imageio.ImageIO; import javax.swing.JDesktopPane; import javax.swing.JFrame; import javax.swing.JInternalFrame; @@ -51,6 +53,7 @@ public class JInternalFrameDraggingTest { private static JInternalFrame internalFrame; private static int FRAME_SIZE = 500; private static Color BACKGROUND_COLOR = Color.ORANGE; + private static final int tolerance = 10; public static void main(String[] args) throws Exception { try { @@ -69,14 +72,24 @@ public static void main(String[] args) throws Exception { BufferedImage img = robot.createScreenCapture(rect); int testRGB = BACKGROUND_COLOR.getRGB(); + Color testColor = new Color(testRGB); for (int i = 1; i < size; i++) { int rgbCW = img.getRGB(i, size / 2); int rgbCH = img.getRGB(size / 2, i); - if (rgbCW != testRGB || rgbCH != testRGB) { + Color rgbCWColor = new Color(rgbCW); + Color rgbCHColor = new Color(rgbCH); + + if (Math.abs(rgbCWColor.getRed() - testColor.getRed()) > tolerance + || Math.abs(rgbCWColor.getGreen() - testColor.getGreen()) > tolerance + || Math.abs(rgbCWColor.getBlue() - testColor.getBlue()) > tolerance + || Math.abs(rgbCHColor.getRed() - testColor.getRed()) > tolerance + || Math.abs(rgbCHColor.getGreen() - testColor.getGreen()) > tolerance + || Math.abs(rgbCHColor.getBlue() - testColor.getBlue()) > tolerance) { System.out.println("i " + i + " rgbCW " + Integer.toHexString(rgbCW) + " testRGB " + Integer.toHexString(testRGB) + " rgbCH " + Integer.toHexString(rgbCH)); + ImageIO.write(img, "png", new File("JInternalFrameDraggingTest.png")); throw new RuntimeException("Background color is wrong!"); } } diff --git a/test/jdk/javax/swing/JInternalFrame/Ctrli.java b/test/jdk/javax/swing/JInternalFrame/Ctrli.java new file mode 100644 index 0000000000000..c97e87b7952b4 --- /dev/null +++ b/test/jdk/javax/swing/JInternalFrame/Ctrli.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +import javax.swing.JFrame; +import javax.swing.JComponent; +import javax.swing.SwingUtilities; + +/* + * @test + * @bug 4199401 + * @summary DefaultFocusManager interferes with comps that + * return true to isManagingFocus(). + * @key headful + * @run main Ctrli + */ + +public class Ctrli { + private static JFrame frame; + private static JComponent keyecho; + private static volatile boolean iPressed = false; + private static volatile Point compLoc; + private static volatile int compWidth; + private static volatile int compHeight; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + robot.setAutoDelay(50); + robot.setAutoWaitForIdle(true); + try { + SwingUtilities.invokeAndWait(Ctrli::createAndShowUI); + robot.waitForIdle(); + robot.delay(1000); + + SwingUtilities.invokeAndWait(() -> { + compLoc = keyecho.getLocationOnScreen(); + compWidth = keyecho.getWidth(); + compHeight = keyecho.getHeight(); + }); + + robot.mouseMove(compLoc.x + compWidth / 2, compLoc.y + compHeight / 2); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + + robot.keyPress(KeyEvent.VK_CONTROL); + robot.keyPress(KeyEvent.VK_I); + robot.waitForIdle(); + robot.keyRelease(KeyEvent.VK_I); + robot.keyRelease(KeyEvent.VK_CONTROL); + robot.waitForIdle(); + + if (!iPressed) { + throw new RuntimeException("Test failed: CTRL+I not pressed."); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void createAndShowUI() { + frame = new JFrame("Test Ctrl+I operation"); + keyecho = new JComponent() { + public boolean isManagingFocus() { + return true; + } + }; + KeyListener keyListener = new KeyAdapter() { + public void keyPressed(KeyEvent e) { + if (((e.getModifiers() & ActionEvent.CTRL_MASK) == ActionEvent.CTRL_MASK) + && (e.getKeyCode() == 73)) + iPressed = true; + } + + public void keyTyped(KeyEvent e) { + if (!iPressed) { + throw new RuntimeException("Test failed: CTRL+I not pressed."); + } + } + }; + + MouseListener mouseListener = new MouseAdapter() { + public void mousePressed(MouseEvent e) { + keyecho.requestFocus(); + } + }; + + keyecho.addKeyListener(keyListener); + keyecho.addMouseListener(mouseListener); + frame.setLayout(new BorderLayout()); + frame.add(keyecho); + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setVisible(true); + } +} diff --git a/test/jdk/javax/swing/JInternalFrame/InternalFrameTitleButtonTest.java b/test/jdk/javax/swing/JInternalFrame/InternalFrameTitleButtonTest.java new file mode 100644 index 0000000000000..55ea19de00fc6 --- /dev/null +++ b/test/jdk/javax/swing/JInternalFrame/InternalFrameTitleButtonTest.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8140527 + * @key headful + * @requires (os.family == "windows") + * @summary InternalFrame has incorrect title button width + * @run main InternalFrameTitleButtonTest + */ + +import java.awt.Component; +import java.awt.Robot; + +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JDesktopPane; +import javax.swing.JFrame; +import javax.swing.JInternalFrame; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.plaf.basic.BasicInternalFrameUI; + +public class InternalFrameTitleButtonTest { + + private static JFrame frame; + private static JInternalFrame iframe; + + public static void main(String[] args) throws Exception { + String osName = System.getProperty("os.name"); + if (!osName.toLowerCase().contains("win")) { + System.out.println("The test is applicable only for Windows."); + return; + } + + UIManager.setLookAndFeel( + "com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel"); + try { + test(2); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + + UIManager.setLookAndFeel( + "com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); + try { + test(14); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + + System.out.println("ok"); + } + + private static void test(final int widthAdd) throws Exception { + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame(); + + JDesktopPane pane = new JDesktopPane(); + frame.setContentPane(pane); + frame.setSize(400, 400); + frame.setVisible(true); + + iframe = new JInternalFrame("Mail Reader", true, + true, true, true); + iframe.setSize(200, 200); + pane.add(iframe); + iframe.setVisible(true); + }); + + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(1000); + + SwingUtilities.invokeAndWait(() -> { + JComponent title = ((BasicInternalFrameUI) iframe.getUI()).getNorthPane(); + for (int i = 0; i < title.getComponentCount(); i++) { + Component c = title.getComponent(i); + if (c instanceof JButton button + && !testButtonSize(button, widthAdd)) { + throw new RuntimeException("Wrong title icon size"); + } + } + }); + } + + private static boolean testButtonSize(final JButton button, + final int widthAdd) { + int height = UIManager.getInt("InternalFrame.titleButtonHeight") - 4; + Icon icon = button.getIcon(); + return height == button.getHeight() + && (height + widthAdd) == button.getWidth() + && height == icon.getIconHeight() + && (height + widthAdd) == icon.getIconWidth(); + } +} + diff --git a/test/jdk/javax/swing/JInternalFrame/bug4131008.java b/test/jdk/javax/swing/JInternalFrame/bug4131008.java new file mode 100644 index 0000000000000..9f45175a7857d --- /dev/null +++ b/test/jdk/javax/swing/JInternalFrame/bug4131008.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4131008 + * @summary JInternalFrame should refresh title after it changing + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4131008 +*/ + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JDesktopPane; +import javax.swing.JFrame; +import javax.swing.JInternalFrame; +import javax.swing.UIManager; + +public class bug4131008 { + + private static final String INSTRUCTIONS = """ + Press button "Change title" at the internal frame "Old". + If title of this frame will replaced by "New", + then test passed, else test fails."""; + + public static void main(String[] args) throws Exception { + + UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel"); + + PassFailJFrame.builder() + .title("bug4131008 Instructions") + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(bug4131008::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + JFrame frame = new JFrame("bug4131008"); + JInternalFrame jif = new JInternalFrame("Old"); + JDesktopPane jdp = new JDesktopPane(); + frame.setContentPane(jdp); + + jif.setSize(150, 100); + jif.setVisible(true); + JButton bt = new JButton("Change title"); + bt.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + jif.setTitle("New"); + } + }); + jif.getContentPane().add(bt); + jdp.add(jif); + try { + jif.setSelected(true); + } catch (Exception e) { + throw new RuntimeException(e); + } + frame.setSize(300, 200); + return frame; + } +} diff --git a/test/jdk/javax/swing/JInternalFrame/bug4151444.java b/test/jdk/javax/swing/JInternalFrame/bug4151444.java new file mode 100644 index 0000000000000..26b64142e28df --- /dev/null +++ b/test/jdk/javax/swing/JInternalFrame/bug4151444.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4151444 + * @summary The maximize button acts like the restore button + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4151444 +*/ + +import javax.swing.JDesktopPane; +import javax.swing.JFrame; +import javax.swing.JInternalFrame; +import javax.swing.JLayeredPane; +import javax.swing.UIManager; + + +public class bug4151444 { + + private static JFrame frame; + private static JInternalFrame interFrame; + + private static final String INSTRUCTIONS = """ + - maximize the internal frame + - then minimize the internal frame + - then maximize the internal frame again + - Check whether internal frame is maximized + - Test will fail automatically even if "Pass" is pressed + if internal frame is not maximized."""; + + public static void main(String[] args) throws Exception { + + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + + PassFailJFrame pfj = PassFailJFrame.builder() + .title("bug4151444 Instructions") + .instructions(INSTRUCTIONS) + .columns(45) + .testUI(bug4151444::createTestUI) + .build(); + try { + pfj.awaitAndCheck(); + } finally { + if (!interFrame.isMaximum()) { + throw new RuntimeException ("Test failed. The maximize button acts like the restore button"); + } + } + } + + private static JFrame createTestUI() { + JFrame frame = new JFrame("bug4151444 frame"); + JDesktopPane desktop = new JDesktopPane(); + frame.setContentPane(desktop); + interFrame = new JInternalFrame( + "Internal frame", true, true, true, true); + desktop.add(interFrame, JLayeredPane.DEFAULT_LAYER); + interFrame.setBounds(0, 0, 200, 100); + interFrame.setVisible(true); + frame.setSize(300, 200); + return frame; + } +} diff --git a/test/jdk/javax/swing/JInternalFrame/bug4176136.java b/test/jdk/javax/swing/JInternalFrame/bug4176136.java new file mode 100644 index 0000000000000..019e453750c64 --- /dev/null +++ b/test/jdk/javax/swing/JInternalFrame/bug4176136.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4176136 + * @summary Default close operation JInternalFrame.DO_NOTHING_ON_CLOSE works correctly + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4176136 + */ + + +import javax.swing.JDesktopPane; +import javax.swing.JFrame; +import javax.swing.JInternalFrame; + +public class bug4176136 { + + private static final String INSTRUCTIONS = """ + Click the close button of the internal frame. + You will see the close button activate, + but nothing else should happen. + If the internal frame closes, the test fails. + If it doesn't close, the test passes."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug4176136 Instructions") + .instructions(INSTRUCTIONS) + .columns(25) + .testUI(bug4176136::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + JFrame frame = new JFrame("bug4176136"); + JDesktopPane dp = new JDesktopPane(); + frame.add(dp); + JInternalFrame inf = new JInternalFrame(); + dp.add(inf); + inf.setDefaultCloseOperation(JInternalFrame.DO_NOTHING_ON_CLOSE); + inf.setSize(100, 100); + inf.setClosable(true); + inf.setVisible(true); + frame.setSize(200, 200); + return frame; + } +} diff --git a/test/jdk/javax/swing/JInternalFrame/bug4190516.java b/test/jdk/javax/swing/JInternalFrame/bug4190516.java new file mode 100644 index 0000000000000..e9a44e4b0c6f1 --- /dev/null +++ b/test/jdk/javax/swing/JInternalFrame/bug4190516.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4190516 + * @summary JInternalFrame should be maximized when Desktop resized + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4190516 + */ + +import javax.swing.JDesktopPane; +import javax.swing.JFrame; +import javax.swing.JInternalFrame; +import javax.swing.SwingUtilities; + +public class bug4190516 { + + private static final String INSTRUCTIONS = """ + Try to resize frame "bug4190516 Frame". + If the internal frame remains maximized + inside this frame then test passes, else test fails."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug4190516 Instructions") + .instructions(INSTRUCTIONS) + .columns(25) + .testUI(bug4190516::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + JFrame fr = new JFrame("bug4190516 Frame"); + JDesktopPane jdp = new JDesktopPane(); + fr.getContentPane().add(jdp); + + JInternalFrame jif = new JInternalFrame("Title", true, true, true, true); + jdp.add(jif); + jif.setSize(150, 150); + jif.setVisible(true); + + fr.setSize(300, 200); + try { + jif.setMaximum(true); + } catch (Exception e) { + throw new RuntimeException(e); + } + + SwingUtilities.updateComponentTreeUI(fr); + return fr; + } +} diff --git a/test/jdk/javax/swing/JInternalFrame/bug4215380.java b/test/jdk/javax/swing/JInternalFrame/bug4215380.java new file mode 100644 index 0000000000000..f9cd18dfb752f --- /dev/null +++ b/test/jdk/javax/swing/JInternalFrame/bug4215380.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4215380 + * @summary Internal Frame should get focus + * @key headful + * @run main bug4215380 + */ + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.event.InputEvent; +import java.awt.Point; +import java.awt.Robot; + +import javax.swing.JButton; +import javax.swing.JDesktopPane; +import javax.swing.JFrame; +import javax.swing.JInternalFrame; +import javax.swing.JLayeredPane; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; + +public class bug4215380 { + + private static String button; + private static JButton b; + private static JFrame frame; + private static JInternalFrame jif; + private static volatile Point loc; + private static volatile Dimension size; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + try { + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame("bug4215380"); + JDesktopPane desktop = new JDesktopPane(); + frame.add(desktop, BorderLayout.CENTER); + + jif = iFrame(1); + desktop.add(jif, JLayeredPane.DEFAULT_LAYER); + desktop.add(iFrame(2), JLayeredPane.DEFAULT_LAYER); + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + }); + robot.waitForIdle(); + robot.delay(1000); + SwingUtilities.invokeAndWait(() -> { + loc = b.getLocationOnScreen(); + size = b.getSize(); + }); + robot.mouseMove(loc.x + size.width / 2, loc.y + size.height / 2); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(500); + if (!(jif.isSelected()) && !button.equals("Frame 1")) { + throw new RuntimeException("Internal frame \"Frame 1\" should be selected..."); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static JInternalFrame iFrame(int i) { + JInternalFrame frame = new JInternalFrame("Frame " + i); + JPanel panel = new JPanel(); + JButton bt = new JButton("Button " + i); + if (i == 1) { + b = bt; + } + bt.addActionListener(e -> button = ((JButton)e.getSource()).getText()); + + panel.add(bt); + + frame.getContentPane().add(panel); + frame.setBounds(10, i * 80 - 70, 120, 90); + frame.setVisible(true); + return frame; + } +} diff --git a/test/jdk/javax/swing/JInternalFrame/bug4242045.java b/test/jdk/javax/swing/JInternalFrame/bug4242045.java new file mode 100644 index 0000000000000..5f812da1da50a --- /dev/null +++ b/test/jdk/javax/swing/JInternalFrame/bug4242045.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4242045 + * @requires (os.family == "windows") + * @summary JInternalFrame titlepane icons should be restored after attribute change + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4242045 + */ + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; + +import javax.swing.JButton; +import javax.swing.JDesktopPane; +import javax.swing.JFrame; +import javax.swing.JInternalFrame; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; + +public class bug4242045 { + + private static JFrame frame; + + private static final String INSTRUCTIONS = """ + Add and remove iconify/maximize/close buttons using the buttons + "Iconifiable", "Maximizable", "Closable" under different LookAndFeels. + If they appears and disappears correctly then test passes. If any + button does not appear or disappear as expected or appear with incorrect + placement then test fails."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug4242045 Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(bug4242045::createTestUI) + .build() + .awaitAndCheck(); + } + + private static void setLF(ActionEvent e) { + try { + UIManager.setLookAndFeel(((JButton)e.getSource()).getActionCommand()); + SwingUtilities.updateComponentTreeUI(frame); + } catch (ClassNotFoundException | InstantiationException + | UnsupportedLookAndFeelException + | IllegalAccessException ex) { + throw new RuntimeException(ex); + } + } + + private static JFrame createTestUI() { + + frame = new JFrame("bug4242045"); + JDesktopPane jdp = new JDesktopPane(); + JInternalFrame jif = new JInternalFrame("Test", true); + frame.add(jdp); + + jdp.add(jif); + jif.setSize(150, 150); + jif.setVisible(true); + + JPanel p = new JPanel(); + + JButton metal = new JButton("Metal"); + metal.setActionCommand("javax.swing.plaf.metal.MetalLookAndFeel"); + metal.addActionListener((ActionEvent e) -> setLF(e)); + p.add(metal); + + JButton windows = new JButton("Windows"); + windows.setActionCommand("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); + windows.addActionListener((ActionEvent e) -> setLF(e)); + p.add(windows); + + JButton motif = new JButton("Motif"); + motif.setActionCommand("com.sun.java.swing.plaf.motif.MotifLookAndFeel"); + motif.addActionListener((ActionEvent e) -> setLF(e)); + p.add(motif); + + JButton clo = new JButton("Closable"); + clo.addActionListener(e -> jif.setClosable(!jif.isClosable())); + p.add(clo); + + JButton ico = new JButton("Iconifiable"); + ico.addActionListener(e -> jif.setIconifiable(!jif.isIconifiable())); + p.add(ico); + + JButton max = new JButton("Maximizable"); + max.addActionListener(e -> jif.setMaximizable(!jif.isMaximizable())); + p.add(max); + + frame.add(p, BorderLayout.SOUTH); + frame.setSize(650, 250); + return frame; + } + +} diff --git a/test/jdk/javax/swing/JInternalFrame/bug4244536.java b/test/jdk/javax/swing/JInternalFrame/bug4244536.java new file mode 100644 index 0000000000000..85acb833d2aa8 --- /dev/null +++ b/test/jdk/javax/swing/JInternalFrame/bug4244536.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4244536 + * @summary Tests that Motif JInternalFrame can be maximized + * after it was iconified. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4244536 + */ + +import javax.swing.JDesktopPane; +import javax.swing.JFrame; +import javax.swing.JInternalFrame; +import javax.swing.UIManager; + +public class bug4244536 { + + private static final String INSTRUCTIONS = """ + Minimize the internal frame using the minimize button. + Then double-click on it to restore its size. + Then press the maximize button. + If the frame gets maximized, test passes. + If its size don't change, test fails."""; + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel( + "com.sun.java.swing.plaf.motif.MotifLookAndFeel"); + + PassFailJFrame.builder() + .title("bug4244536 Instructions") + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(bug4244536::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + JFrame frame = new JFrame("bug4244536"); + JDesktopPane desktop = new JDesktopPane(); + JInternalFrame jif = new JInternalFrame("Internal Frame"); + jif.setSize(150, 150); + jif.setMaximizable(true); + jif.setIconifiable(true); + jif.setVisible(true); + desktop.add(jif); + frame.add("Center", desktop); + frame.setSize(300, 300); + return frame; + } + +} diff --git a/test/jdk/javax/swing/JInternalFrame/bug4305284.java b/test/jdk/javax/swing/JInternalFrame/bug4305284.java new file mode 100644 index 0000000000000..8801c8c678c6f --- /dev/null +++ b/test/jdk/javax/swing/JInternalFrame/bug4305284.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4305284 + * @summary JInternalFrames can't be sized off of the desktop + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4305284 + */ + +import javax.swing.JDesktopPane; +import javax.swing.JFrame; +import javax.swing.JInternalFrame; + +public class bug4305284 { + + private static final String INSTRUCTIONS = """ + Try to resize the shown internal frame. + If it can't be sized of the desktop bounds, + then test passes, else test fails."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug4305284 Instructions") + .instructions(INSTRUCTIONS) + .columns(25) + .testUI(bug4305284::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + JFrame frame = new JFrame("bug4305284"); + JInternalFrame jif = new JInternalFrame("Test", + true, true, true, true); + JDesktopPane dp = new JDesktopPane(); + frame.setContentPane(dp); + dp.add(jif); + + try { + jif.setBounds(50, 50, 200, 200); + jif.setMaximum(false); + jif.setVisible(true); + } catch (Exception e) { + e.printStackTrace(); + } + frame.setSize(300, 300); + return frame; + } + +} diff --git a/test/jdk/javax/swing/JInternalFrame/bug4321312.java b/test/jdk/javax/swing/JInternalFrame/bug4321312.java new file mode 100644 index 0000000000000..1b527acf74584 --- /dev/null +++ b/test/jdk/javax/swing/JInternalFrame/bug4321312.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4321312 + * @summary Verifies no Exception thrown from BasicInternalFrameUI$BorderListener + * @key headful + * @run main bug4321312 + */ + +import java.awt.Dimension; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; +import java.awt.Point; +import java.awt.Robot; + +import javax.swing.JDesktopPane; +import javax.swing.JFrame; +import javax.swing.JInternalFrame; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; + +public class bug4321312 { + + private static JFrame frame; + private static MyInternalFrame jif; + private static volatile Point loc; + private static volatile Dimension size; + + static boolean fails; + static Exception exc; + + private static synchronized boolean isFails() { + return fails; + } + + private static synchronized void setFails(Exception e) { + fails = true; + exc = e; + } + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + try { + SwingUtilities.invokeAndWait(() -> { + try { + UIManager.setLookAndFeel( + "com.sun.java.swing.plaf.motif.MotifLookAndFeel"); + } catch (ClassNotFoundException | InstantiationException + | UnsupportedLookAndFeelException + | IllegalAccessException e) { + throw new RuntimeException(e); + } + + frame = new JFrame("bug4321312"); + JDesktopPane jdp = new JDesktopPane(); + frame.add(jdp); + + jif = new MyInternalFrame("Internal Frame", true); + jdp.add(jif); + jif.setSize(150, 150); + jif.setVisible(true); + + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + }); + robot.waitForIdle(); + robot.delay(1000); + SwingUtilities.invokeAndWait(() -> { + loc = jif.getLocationOnScreen(); + size = jif.getSize(); + }); + robot.mouseMove(loc.x + size.width / 2, loc.y + size.height / 2); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(200); + if (isFails()) { + throw new RuntimeException(exc); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + static class MyInternalFrame extends JInternalFrame { + MyInternalFrame(String str, boolean b) { + super(str, b); + } + + protected void processMouseEvent(MouseEvent e) { + try { + super.processMouseEvent(e); + } catch (Exception exc) { + setFails(exc); + } + } + } +} diff --git a/test/jdk/javax/swing/JInternalFrame/bug4322726.java b/test/jdk/javax/swing/JInternalFrame/bug4322726.java new file mode 100644 index 0000000000000..861f9357fba6c --- /dev/null +++ b/test/jdk/javax/swing/JInternalFrame/bug4322726.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4322726 + * @summary Tests that JInternalFrame throws ArrayIndexOutOfBoundsException when Control-F4 pressed + * @key headful + * @run main bug4322726 + */ + +import java.awt.event.KeyEvent; +import java.awt.Robot; + +import javax.swing.JDesktopPane; +import javax.swing.JFrame; +import javax.swing.JInternalFrame; +import javax.swing.KeyStroke; +import javax.swing.SwingUtilities; +import java.beans.PropertyVetoException; + +public class bug4322726 { + + private static JFrame frame; + private static JInternalFrame internalFrame; + private static volatile boolean failed; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + try { + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame("bug4322726"); + frame.setSize(600, 400); + TestDesktopPane desktopPane = new TestDesktopPane(); + frame.setContentPane(desktopPane); + internalFrame = new JInternalFrame(); + internalFrame.setClosable(true); + internalFrame.setMaximizable(true); + internalFrame.setIconifiable(true); + internalFrame.setResizable(true); + internalFrame.setTitle("Internal Frame"); + internalFrame.setSize(300, 200); + internalFrame.setVisible(true); + desktopPane.add(internalFrame); + + frame.setSize(400, 400); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + }); + robot.waitForIdle(); + robot.delay(1000); + SwingUtilities.invokeAndWait(() -> { + try { + internalFrame.setSelected(true); + } catch (PropertyVetoException e) { + throw new RuntimeException("PropertyVetoException thrown"); + } + }); + robot.waitForIdle(); + robot.delay(200); + robot.keyPress(KeyEvent.VK_CONTROL); + robot.keyPress(KeyEvent.VK_F4); + robot.keyRelease(KeyEvent.VK_F4); + robot.keyRelease(KeyEvent.VK_CONTROL); + robot.waitForIdle(); + robot.delay(200); + if (failed) { + throw new RuntimeException("Failed: index is out of bounds"); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + static class TestDesktopPane extends JDesktopPane { + protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) { + try { + return super.processKeyBinding(ks, e, condition, pressed); + } catch (ArrayIndexOutOfBoundsException ex) { + failed = true; + } + return failed; + } + } +} diff --git a/test/jdk/javax/swing/JInternalFrame/6726866/bug6726866.java b/test/jdk/javax/swing/JInternalFrame/bug6726866.java similarity index 68% rename from test/jdk/javax/swing/JInternalFrame/6726866/bug6726866.java rename to test/jdk/javax/swing/JInternalFrame/bug6726866.java index d56f10d39ef12..88b890d7b2ca2 100644 --- a/test/jdk/javax/swing/JInternalFrame/6726866/bug6726866.java +++ b/test/jdk/javax/swing/JInternalFrame/bug6726866.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,11 +21,14 @@ * questions. */ -/* @test - @bug 6726866 8186617 - @summary Repainting artifacts when resizing or dragging JInternalFrames in +/* + * @test + * @bug 6726866 8186617 + * @summary Repainting artifacts when resizing or dragging JInternalFrames in non-opaque toplevel - @run applet/manual=yesno bug6726866.html + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug6726866 */ import java.awt.Color; @@ -36,10 +39,26 @@ import javax.swing.JFrame; import javax.swing.JInternalFrame; import javax.swing.JLabel; +import javax.swing.SwingUtilities; + +public class bug6726866 { -public class bug6726866 extends JApplet { + private static final String INSTRUCTIONS = """ + Drag the internal frame inside the green undecorated window, + if you can drag it the test passes, otherwise fails. """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("JInternalFrame Instructions") + .instructions(INSTRUCTIONS) + .rows(5) + .columns(35) + .testUI(bug6726866::createUI) + .build() + .awaitAndCheck(); + } - public void init() { + private static JFrame createUI() { JFrame frame = new JFrame("bug6726866"); frame.setUndecorated(true); setWindowNonOpaque(frame); @@ -53,10 +72,8 @@ public void init() { desktop.add(iFrame); frame.add(desktop); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(400, 400); - frame.setVisible(true); - frame.toFront(); + return frame; } public static void setWindowNonOpaque(Window window) { diff --git a/test/jdk/javax/swing/JLabel/bug4106007.java b/test/jdk/javax/swing/JLabel/bug4106007.java new file mode 100644 index 0000000000000..b78813809c134 --- /dev/null +++ b/test/jdk/javax/swing/JLabel/bug4106007.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 4106007 + * @summary Multi-line JLabel is now supported for HTML labels. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4106007 + * */ + +import java.awt.BorderLayout; +import javax.swing.JFrame; +import javax.swing.JLabel; + +public class bug4106007 { + + static final String INSTRUCTIONS = """ + The test window should have a label spanning multiple lines. + If it is press PASS, else press FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug4106007::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + JFrame frame = new JFrame("Bug4106007"); + frame.setLayout(new BorderLayout()); + String str = ""; + String longLine = + "I hope multi-line JLabel is now supported and you can see several lines instead of one long line. "; + str += longLine; + str += longLine; + str += ""; + JLabel lab = new JLabel(str); + frame.add(lab, BorderLayout.NORTH); + frame.setSize(400, 400); + return frame; + } +} diff --git a/test/jdk/javax/swing/JLabel/bug4945795.java b/test/jdk/javax/swing/JLabel/bug4945795.java new file mode 100644 index 0000000000000..56f6e0eb60f5b --- /dev/null +++ b/test/jdk/javax/swing/JLabel/bug4945795.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 4945795 + * @summary With mnemonic hiding turned on, Java does not display all mnemonics with ALT key + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4945795 + * */ + +import java.awt.BorderLayout; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.UIManager; + +public class bug4945795 { + + static final String INSTRUCTIONS = """ + This test is for the Swing Windows Look And Feel. + A test window will be displayed with the label 'Mnemonic Test' + Click the mouse in the test window to make sure it has keyboard focus. + Now press and hold the 'Alt' key. + An underline should be displayed below the initial 'M' character. + If it is press PASS, else press FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug4945795::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + JFrame mainFrame = new JFrame("Bug4945795"); + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception ex) { + throw new RuntimeException("Can not set system look and feel"); + } + mainFrame.setLayout(new BorderLayout()); + mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + JLabel label = new JLabel("Mnemonic test"); + label.setDisplayedMnemonic('M'); + mainFrame.add(label, BorderLayout.NORTH); + mainFrame.setSize(400, 300); + return mainFrame; + } +} diff --git a/test/jdk/javax/swing/JList/bug4193267.java b/test/jdk/javax/swing/JList/bug4193267.java new file mode 100644 index 0000000000000..cb447644d154d --- /dev/null +++ b/test/jdk/javax/swing/JList/bug4193267.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4193267 + * @summary Tests that JList first and last visible indices are + * updated properly + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4193267 + */ + +import java.awt.Color; +import java.awt.FlowLayout; +import java.awt.GridLayout; +import java.util.List; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +public class bug4193267 { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Resize the frame "JList" with a different ways and scroll the list + (if it possible). The indices of first and last visible elements + should be indicated in the corresponding fields in "Index" frame. + If the indicated indices is not right then test fails. + + Note: + - the first and last visible indices should be -1 if nothing + is visible; + - the first or last visible cells may only be partially visible. + """; + PassFailJFrame.builder() + .title("bug4193267 Instructions") + .instructions(INSTRUCTIONS) + .positionTestUI(WindowLayouts::rightOneRow) + .columns(35) + .testUI(bug4193267::initialize) + .build() + .awaitAndCheck(); + } + + private static List initialize() { + String[] data = {"000000000000000", "111111111111111", + "222222222222222", "333333333333333", + "444444444444444", "555555555555555", + "666666666666666", "777777777777777", + "888888888888888", "999999999999999"}; + + JFrame[] fr = new JFrame[2]; + fr[0] = new JFrame("JList"); + JList lst = new JList(data); + lst.setLayoutOrientation(JList.VERTICAL_WRAP); + lst.setVisibleRowCount(4); + JScrollPane jsp = new JScrollPane(lst); + fr[0].add(jsp); + fr[0].setSize(400, 200); + + JPanel pL = new JPanel(); + pL.setLayout(new GridLayout(2, 1)); + pL.add(new JLabel("First Visible Index")); + pL.add(new JLabel("Last Visible Index")); + + JPanel p = new JPanel(); + p.setLayout(new GridLayout(2, 1)); + JTextField first = new JTextField("0", 2); + first.setEditable(false); + first.setBackground(Color.white); + p.add(first); + JTextField last = new JTextField("9", 2); + last.setEditable(false); + last.setBackground(Color.white); + p.add(last); + + fr[1] = new JFrame("Index"); + fr[1].setSize(200, 200); + fr[1].setLayout(new FlowLayout()); + fr[1].add(pL); + fr[1].add(p); + + jsp.getViewport().addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + first.setText(String.valueOf(lst.getFirstVisibleIndex())); + last.setText(String.valueOf(lst.getLastVisibleIndex())); + } + }); + List frameList = List.of(fr[0], fr[1]); + return frameList; + } +} diff --git a/test/jdk/javax/swing/JList/bug4249161.java b/test/jdk/javax/swing/JList/bug4249161.java new file mode 100644 index 0000000000000..09bdd93600f07 --- /dev/null +++ b/test/jdk/javax/swing/JList/bug4249161.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4249161 + * @summary Tests that JList.setComponentOrientation() works correctly + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4249161 + */ + +import java.awt.BorderLayout; +import java.awt.ComponentOrientation; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JList; +import javax.swing.JScrollPane; + +public class bug4249161 { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. With a scroll bar, confirm that all words ("one" - "twenty") are + aligned at the left side of a list. + 2. Press "Change!" button. All words on the list should be moved + to the right side. + 3. Press the same button again. All words should be moved to the + left side. + + If all items in a list are moved as soon as "Change!" button is + pressed, test passes. + """; + PassFailJFrame.builder() + .title("bug4249161 Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(bug4249161::initialize) + .build() + .awaitAndCheck(); + } + + private static JFrame initialize() { + JFrame fr = new JFrame("bug4249161"); + + String[] data = {"one", "two", "three", "four", "five", + "six", "seven", "eight", "nine", "ten", + "eleven", "twelve", "thirteen", "fourteen", "fifteen", + "sixteen", "seventeen", "eighteen", "nineteen", "twenty" + }; + final JList list = new JList(data); + list.setSize(200, 200); + list.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); + JScrollPane pane = new JScrollPane(list); + fr.add(pane); + + JButton button = new JButton("Change!"); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + if (list.getComponentOrientation() != + ComponentOrientation.RIGHT_TO_LEFT) { + list.setComponentOrientation + (ComponentOrientation.RIGHT_TO_LEFT); + } else { + list.setComponentOrientation + (ComponentOrientation.LEFT_TO_RIGHT); + } + } + }); + fr.add(button, BorderLayout.SOUTH); + fr.setSize(200, 300); + fr.setAlwaysOnTop(true); + return fr; + } +} diff --git a/test/jdk/javax/swing/JList/bug4618767.java b/test/jdk/javax/swing/JList/bug4618767.java new file mode 100644 index 0000000000000..f01e20f365c9e --- /dev/null +++ b/test/jdk/javax/swing/JList/bug4618767.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4618767 + * @summary First letter navigation in JList interferes with mnemonics + * @key headful + * @run main bug4618767 + */ + +import java.awt.Robot; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.KeyEvent; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import javax.swing.JFrame; +import javax.swing.JList; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.SwingUtilities; +import javax.swing.event.MenuEvent; +import javax.swing.event.MenuListener; + +public class bug4618767 { + private static JFrame f; + private static final JList list = new + JList(new String[] {"one", "two", "three", "four"}); + private static boolean menuSelected; + private static volatile boolean failed; + private static CountDownLatch listGainedFocusLatch = new CountDownLatch(1); + + public static void main(String[] args) throws Exception { + try { + createUI(); + runTest(); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + } + } + + private static void createUI() throws Exception { + SwingUtilities.invokeAndWait(() -> { + f = new JFrame("bug4618767"); + JMenu menu = new JMenu("File"); + menu.setMnemonic('F'); + JMenuItem menuItem = new JMenuItem("item"); + menu.add(menuItem); + JMenuBar menuBar = new JMenuBar(); + menuBar.add(menu); + f.setJMenuBar(menuBar); + + menu.addMenuListener(new MenuListener() { + public void menuCanceled(MenuEvent e) {} + public void menuDeselected(MenuEvent e) {} + public void menuSelected(MenuEvent e) { + menuSelected = true; + } + }); + + list.addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + listGainedFocusLatch.countDown(); + } + }); + f.add(list); + f.pack(); + f.setLocationRelativeTo(null); + f.setAlwaysOnTop(true); + f.setVisible(true); + }); + } + + private static void runTest() throws Exception { + if (!listGainedFocusLatch.await(3, TimeUnit.SECONDS)) { + throw new RuntimeException("Waited too long, but can't gain" + + " focus for list"); + } + Robot robot = new Robot(); + robot.setAutoDelay(200); + robot.waitForIdle(); + robot.keyPress(KeyEvent.VK_O); + robot.keyRelease(KeyEvent.VK_O); + robot.waitForIdle(); + robot.keyPress(KeyEvent.VK_ALT); + robot.keyPress(KeyEvent.VK_F); + robot.keyRelease(KeyEvent.VK_F); + robot.keyRelease(KeyEvent.VK_ALT); + + SwingUtilities.invokeAndWait(() -> { + if (menuSelected && list.getSelectedIndex()!= 0) { + failed = true; + } + }); + if (failed) { + throw new RuntimeException("Mnemonics interferes with Jlist" + + " item selection using KeyEvent"); + } + } +} diff --git a/test/jdk/javax/swing/JMenu/4213634/bug4213634.java b/test/jdk/javax/swing/JMenu/4213634/bug4213634.java index dd699b7e5bc1f..ad26ff2bc5950 100644 --- a/test/jdk/javax/swing/JMenu/4213634/bug4213634.java +++ b/test/jdk/javax/swing/JMenu/4213634/bug4213634.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,103 +21,91 @@ * questions. */ -import java.awt.AWTException; import java.awt.Robot; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.KeyEvent; -import java.lang.reflect.InvocationTargetException; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; -import javax.swing.JTextArea; import javax.swing.SwingUtilities; /* * @test * @key headful * @bug 4213634 8017187 - * @author Scott Violet * @library ../../regtesthelpers * @build Util + * @summary Verifies if Alt+mnemonic char works when + * menu & menuitem have same mnemonic char * @run main bug4213634 */ - public class bug4213634 { - private JMenu menu; - - private JFrame frame; + private static JMenu menu; + private static JFrame frame; + private static Robot robot; - public static void main(String[] args) throws Throwable { - new bug4213634(); - } + public static void main(String[] args) throws Exception { + try { + robot = new Robot(); + SwingUtilities.invokeAndWait(bug4213634::createAndShowGUI); - bug4213634() throws AWTException, InterruptedException, InvocationTargetException { - SwingUtilities.invokeAndWait(new Runnable() { - @Override - public void run() { - createAndShowGUI(); - } - }); - - test(); + robot.waitForIdle(); + robot.delay(1000); + test(); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } } - public void createAndShowGUI() { - frame = new JFrame("TEST"); + public static void createAndShowGUI() { + frame = new JFrame("bug4213634"); JMenuBar mb = new JMenuBar(); menu = mb.add(createMenu("1 - First Menu", true)); mb.add(createMenu("2 - Second Menu", false)); frame.setJMenuBar(mb); - JTextArea ta = new JTextArea("This test dedicated to Nancy and Kathleen, testers and bowlers extraordinaire\n\n\nNo exception means pass."); - frame.getContentPane().add("Center", ta); JButton button = new JButton("Test"); frame.getContentPane().add("South", button); - frame.setBounds(100, 100, 400, 400); + frame.setSize(300, 300); + frame.setLocationRelativeTo(null); frame.setVisible(true); button.requestFocusInWindow(); } - private void test() throws AWTException, InterruptedException, InvocationTargetException { - Robot robot = new Robot(); - robot.setAutoDelay(50); - robot.waitForIdle(); - + private static void test() throws Exception { Util.hitMnemonics(robot, KeyEvent.VK_1); robot.waitForIdle(); + robot.delay(100); - SwingUtilities.invokeAndWait(new Runnable() { - @Override - public void run() { - if (!menu.isSelected()) { - throw new RuntimeException( - "Failed: Menu didn't remain posted at end of test"); - } else { - System.out.println("Test passed!"); - frame.dispose(); - } + SwingUtilities.invokeAndWait(() -> { + if (!menu.isSelected()) { + throw new RuntimeException( + "Failed: Menu didn't remain posted at end of test"); + } else { + System.out.println("Test passed!"); } }); } - private JMenu createMenu(String str, boolean bFlag) { + + private static JMenu createMenu(String str, boolean bFlag) { JMenuItem menuitem; JMenu menu = new JMenu(str); menu.setMnemonic(str.charAt(0)); - for(int i = 0; i < 10; i ++) { + for (int i = 0; i < 10; i++) { menuitem = new JMenuItem("JMenuItem" + i); - menuitem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - throw new RuntimeException( - "Failed: Mnemonic activated"); - } + menuitem.addActionListener(e -> { + throw new RuntimeException("Failed: Mnemonic activated"); }); - if(bFlag) + if (bFlag) { menuitem.setMnemonic('0' + i); + } menu.add(menuitem); } return menu; diff --git a/test/jdk/javax/swing/JMenu/TestUngrab.java b/test/jdk/javax/swing/JMenu/TestUngrab.java new file mode 100644 index 0000000000000..64326009c6c77 --- /dev/null +++ b/test/jdk/javax/swing/JMenu/TestUngrab.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8267374 + * @key headful + * @requires (os.family == "mac") + * @summary Verifies menu closes when main window is resized + * @run main TestUngrab + */ + +import java.awt.Point; +import java.awt.Dimension; +import java.awt.Robot; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.SwingUtilities; + +public class TestUngrab { + /** + * Menu (JMenuBar and JPopupMenu) not hidden when resizing the window. + * -> UngrabEvent not sent. + */ + static JMenu menu; + static JFrame frame; + static volatile Point loc; + static volatile Point point; + static volatile Dimension dim; + static volatile int width; + static volatile int height; + static volatile boolean popupVisible; + + private static void createAndShowGUI() { + frame = new JFrame(); + JMenuBar mb = new JMenuBar(); + menu = new JMenu("Menu"); + menu.add(new JMenuItem("Item 1")); + menu.add(new JMenuItem("Item 2")); + mb.add(menu); + + frame.setJMenuBar(mb); + frame.setSize(300, 300); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + robot.setAutoDelay(100); + SwingUtilities.invokeAndWait(() -> createAndShowGUI()); + robot.waitForIdle(); + robot.delay(1000); + + SwingUtilities.invokeAndWait(() -> { + point = menu.getLocationOnScreen(); + dim = menu.getSize(); + }); + robot.mouseMove(point.x + dim.width / 2, point.y + dim.height / 2); + robot.waitForIdle(); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + + SwingUtilities.invokeAndWait(() -> { + loc = frame.getLocationOnScreen(); + width = frame.getWidth(); + height = frame.getHeight(); + }); + robot.mouseMove(loc.x + width, loc.y + height); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.delay(1000); + + SwingUtilities.invokeAndWait(() -> { + popupVisible = menu.isPopupMenuVisible(); + }); + System.out.println("isPopupMenuVisible " + popupVisible); + if (popupVisible) { + throw new RuntimeException("popup menu not closed on resize"); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + +} diff --git a/test/jdk/javax/swing/JMenu/bug4140643.java b/test/jdk/javax/swing/JMenu/bug4140643.java new file mode 100644 index 0000000000000..e9205dfcfc633 --- /dev/null +++ b/test/jdk/javax/swing/JMenu/bug4140643.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4140643 + * @summary Tests that Motif menus open with both Alt-key and Meta-key + * @key headful + * @run main bug4140643 + */ + +import java.awt.Robot; +import java.awt.event.KeyEvent; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; + +public class bug4140643 { + private static JFrame frame; + private static JMenu menu; + private static volatile boolean isPopMenuVisible; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + try { + SwingUtilities.invokeAndWait(() -> { + try { + UIManager.setLookAndFeel( + "com.sun.java.swing.plaf.motif.MotifLookAndFeel"); + } catch (ClassNotFoundException | InstantiationException + | UnsupportedLookAndFeelException + | IllegalAccessException e) { + throw new RuntimeException(e); + } + frame = new JFrame("bug4140643"); + + menu = new JMenu("File"); + menu.setMnemonic(KeyEvent.VK_F); + menu.add(new JMenuItem("Open...")); + menu.add(new JMenuItem("Save")); + + JMenuBar mbar = new JMenuBar(); + mbar.add(menu); + frame.setJMenuBar(mbar); + + frame.add(new JButton("Click Here")); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + }); + robot.waitForIdle(); + robot.delay(1000); + if (System.getProperty("os.name").contains("OS X")) { + robot.keyPress(KeyEvent.VK_CONTROL); + robot.keyPress(KeyEvent.VK_ALT); + robot.keyPress(KeyEvent.VK_F); + robot.keyRelease(KeyEvent.VK_F); + robot.keyRelease(KeyEvent.VK_ALT); + robot.keyRelease(KeyEvent.VK_CONTROL); + } else { + robot.keyPress(KeyEvent.VK_ALT); + robot.keyPress(KeyEvent.VK_F); + robot.keyRelease(KeyEvent.VK_F); + robot.keyRelease(KeyEvent.VK_ALT); + } + robot.waitForIdle(); + robot.delay(1000); + SwingUtilities.invokeAndWait(() -> { + isPopMenuVisible = menu.isPopupMenuVisible(); + }); + if (!isPopMenuVisible) { + throw new RuntimeException("Menu popup is not shown"); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } +} diff --git a/test/jdk/javax/swing/JMenu/bug4146588.java b/test/jdk/javax/swing/JMenu/bug4146588.java new file mode 100644 index 0000000000000..a27380daa230a --- /dev/null +++ b/test/jdk/javax/swing/JMenu/bug4146588.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4146588 + * @summary JMenu.setMenuLocation has no effect + * @key headful + * @run main bug4146588 + */ + +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.SwingUtilities; + +public class bug4146588 { + + private static JFrame fr; + private static JMenu menu; + private static volatile Point loc; + private static volatile Point popupLoc; + private static volatile Point menuLoc; + private static volatile Rectangle frameBounds; + private static volatile Rectangle popupBounds; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + try { + SwingUtilities.invokeAndWait(() -> { + fr = new JFrame("bug4146588 frame"); + + JMenuBar menubar = new JMenuBar(); + menu = new JMenu("Menu"); + menu.add("Item 1"); + menu.add("Item 2"); + menu.add("Item 3"); + menu.setMenuLocation(150, 150); + menubar.add(menu); + fr.setJMenuBar(menubar); + + fr.setSize(400, 400); + fr.setLocationRelativeTo(null); + fr.setVisible(true); + }); + robot.waitForIdle(); + robot.delay(1000); + SwingUtilities.invokeAndWait(() -> { + menu.doClick(0); + }); + robot.waitForIdle(); + robot.delay(200); + SwingUtilities.invokeAndWait(() -> { + popupLoc = menu.getPopupMenu().getLocationOnScreen(); + menuLoc = menu.getLocationOnScreen(); + frameBounds = fr.getBounds(); + popupBounds = menu.getPopupMenu().getBounds(); + }); + System.out.println(popupLoc); + System.out.println(popupBounds); + System.out.println(frameBounds); + System.out.println(menuLoc); + if (!(popupLoc.getX() + > ((frameBounds.getX() + frameBounds.getWidth() / 2) - popupBounds.getWidth())) + && (popupLoc.getY() + > ((frameBounds.getY() + frameBounds.getHeight() / 2) - popupBounds.getHeight()))) { + throw new RuntimeException("popup is not at center of frame"); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (fr != null) { + fr.dispose(); + } + }); + } + } +} diff --git a/test/jdk/javax/swing/JMenu/bug4187996.java b/test/jdk/javax/swing/JMenu/bug4187996.java new file mode 100644 index 0000000000000..f536a47c8a64f --- /dev/null +++ b/test/jdk/javax/swing/JMenu/bug4187996.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4187996 + * @summary Tests that Metal submenus overlap menu + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4187996 + */ + +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.UIManager; + +public class bug4187996 { + + private static final String INSTRUCTIONS = """ + Open the menu "Menu", then "Submenu". + The submenu should be top-aligned with the menu, + and slightly overlap it horizontally. Otherwise test fails."""; + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); + PassFailJFrame.builder() + .title("bug4187996 Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(bug4187996::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + JFrame frame = new JFrame("bug4187996"); + JMenu submenu = new JMenu("Submenu"); + submenu.add(new JMenuItem("Sub 1")); + submenu.add(new JMenuItem("Sub 2")); + + JMenu menu = new JMenu("Menu"); + menu.add(submenu); + menu.add(new JMenuItem("Item 1")); + menu.add(new JMenuItem("Item 2")); + + JMenuBar mbar = new JMenuBar(); + mbar.add(menu); + frame.setJMenuBar(mbar); + frame.setSize(300, 100); + return frame; + } +} diff --git a/test/jdk/javax/swing/JMenu/bug4342646.java b/test/jdk/javax/swing/JMenu/bug4342646.java new file mode 100644 index 0000000000000..610eb349bcfde --- /dev/null +++ b/test/jdk/javax/swing/JMenu/bug4342646.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4342646 + * @summary Tests that very long menus are properly placed on the screen. + * @key headful + * @run main bug4342646 + */ + +import java.awt.Point; +import java.awt.Robot; + +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.SwingUtilities; + +public class bug4342646 { + + private static JFrame frame; + private static JMenu menu; + private static volatile Point popupLoc; + private static volatile Point menuLoc; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + try { + SwingUtilities.invokeAndWait(() -> { + JMenuBar mbar = new JMenuBar(); + menu = new JMenu("Menu"); + menu.add(new JMenuItem( + "AAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAA")); + mbar.add(menu); + + frame = new JFrame("4342646"); + frame.setJMenuBar(mbar); + frame.setBounds(10, 10, 200, 100); + frame.setVisible(true); + }); + robot.waitForIdle(); + robot.delay(1000); + SwingUtilities.invokeAndWait(() -> { + menu.doClick(); + }); + robot.waitForIdle(); + robot.delay(200); + SwingUtilities.invokeAndWait(() -> { + popupLoc = menu.getPopupMenu().getLocationOnScreen(); + menuLoc = menu.getLocationOnScreen(); + }); + System.out.println(menuLoc); + System.out.println(popupLoc); + if (popupLoc.getX() < menuLoc.getX()) { + throw new RuntimeException("PopupMenu is incorrectly placed at left of menu"); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } +} diff --git a/test/jdk/javax/swing/JMenu/bug6471949.java b/test/jdk/javax/swing/JMenu/bug6471949.java new file mode 100644 index 0000000000000..d196502ed21d7 --- /dev/null +++ b/test/jdk/javax/swing/JMenu/bug6471949.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6471949 + * @summary JMenu should stay selected after escape is pressed + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug6471949 +*/ + +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; + +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JTextArea; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; + +public class bug6471949 { + + private static final String INSTRUCTIONS = """ + Test the menu and its submenus for different LaF: + + Click on "Menu" and then click on "Inner" submenu + and then click on "One more" submenu. + + For Metal, Nimbus and Aqua Laf the Escape key hides the last open submenu, + Press Esc till the last menu "Inner" is closed. + If the last menu is closed then the menu button (in menubar) gets unselected. + + For Windows Laf the Escape key hides the last open submenu + if the last menu is closed then the menu button remains selected, + until the Escape key is pressed again or any other key letter pressed. + + For GTK and Motif menu, all open submenus must hide when the Escape key is pressed. + + If everything works as described, the test passes and fails otherwise."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug6471949 Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(bug6471949::createTestUI) + .logArea() + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + + PassFailJFrame.log("Menu.cancelMode = " + + UIManager.getString("Menu.cancelMode")); + PassFailJFrame.log("Menu.preserveTopLevelSelection = " + + UIManager.getBoolean("Menu.preserveTopLevelSelection")); + PassFailJFrame.log(""); + + JFrame frame = new JFrame("bug6471949"); + JMenuBar bar = new JMenuBar(); + JMenu menu = new JMenu("Menu"); + menu.setMnemonic('m'); + + JMenuItem item = new JMenuItem("Item"); + menu.add(item); + JMenu inner = new JMenu("Inner"); + inner.add(new JMenuItem("Test")); + JMenu oneMore = new JMenu("One more"); + oneMore.add(new JMenuItem("Lala")); + inner.add(oneMore); + menu.add(inner); + + JMenu lafMenu = new JMenu("Change LaF"); + + UIManager.LookAndFeelInfo[] lafs = UIManager.getInstalledLookAndFeels(); + for (final UIManager.LookAndFeelInfo lafInfo : lafs) { + JMenuItem lafItem = new JMenuItem(lafInfo.getName()); + lafItem.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + setLaf(frame, lafInfo.getClassName()); + } + }); + lafMenu.add(lafItem); + } + + frame.setJMenuBar(bar); + bar.add(menu); + bar.add(lafMenu); + + JTextArea field = new JTextArea(); + frame.add(field); + field.requestFocusInWindow(); + frame.pack(); + return frame; + } + + private static void setLaf(JFrame frame, String laf) { + try { + UIManager.setLookAndFeel(laf); + SwingUtilities.updateComponentTreeUI(frame); + PassFailJFrame.log("Menu.cancelMode = " + + UIManager.getString("Menu.cancelMode")); + PassFailJFrame.log("Menu.preserveTopLevelSelection = " + + UIManager.getBoolean("Menu.preserveTopLevelSelection")); + PassFailJFrame.log(""); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/test/jdk/javax/swing/JMenu/bug6513492.java b/test/jdk/javax/swing/JMenu/bug6513492.java new file mode 100644 index 0000000000000..a84b93fe74b0e --- /dev/null +++ b/test/jdk/javax/swing/JMenu/bug6513492.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* +* @test +* @bug 6513492 +* @summary Escape key needs to be pressed twice to remove focus from an empty/diabled Menu. +* @library /java/awt/regtesthelpers +* @build PassFailJFrame +* @run main/manual bug6513492 +*/ + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JTextArea; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; + +public class bug6513492 { + + private static final String INSTRUCTIONS = """ + Test Menu for different LaF: + + * For Windows Laf: + Click the editor + Click EmpyMenu, press Escape -> focus must go to the editor + Click EmpyMenu, press right arrow button, press Escape -> focus must go to the editor + Click SubMenuTest, highlight the first disabled submenu, press Escape + -> focus must stay at the topLevelMenu + + * For Metal, Nimbus and Aqua Laf + Click the editor + Click SubMenuTest, highlight the EmptySubmenu, press Escape -> focus must go to the editor + Click SubMenuTest, highlight the EnabledItem, press Escape -> focus must go to the editor + + * For GTK and Motif + Click the editor + Open any menu or submenu, press Escape -> focus must go to the editor."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug6513492 Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(bug6513492::createTestUI) + .logArea() + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + PassFailJFrame.log("Menu.cancelMode = " + + UIManager.getString("Menu.cancelMode")); + PassFailJFrame.log("Menu.preserveTopLevelSelection = " + + UIManager.getBoolean("Menu.preserveTopLevelSelection")); + PassFailJFrame.log(""); + + JFrame frame = new JFrame("bug6513492"); + JMenuBar bar = new JMenuBar(); + bar.add(new JMenu("EmptyMenu")); + + JMenu disabledMenu = new JMenu("NotEmpyButDisabled"); + disabledMenu.add(new JMenuItem("item")); + disabledMenu.setEnabled(false); + bar.add(disabledMenu); + + JMenu menu = new JMenu("SubMenuTest"); + JMenu disabledSubmenu = new JMenu("Submenu"); + disabledSubmenu.add(new JMenuItem("item")); + disabledSubmenu.setEnabled(false); + menu.add(disabledSubmenu); + + JMenu enabledSubmenu = new JMenu("Submenu"); + enabledSubmenu.add(new JMenuItem("item")); + menu.add(enabledSubmenu); + + JMenu emptySubmenu = new JMenu("EmptySubmenu"); + menu.add(emptySubmenu); + + menu.add(new JMenuItem("EnabledItem")); + JMenuItem item = new JMenuItem("DisabledItem"); + item.setEnabled(false); + menu.add(item); + bar.add(menu); + + JMenu lafMenu = new JMenu("Change LaF"); + + UIManager.LookAndFeelInfo[] lafs = UIManager.getInstalledLookAndFeels(); + for (final UIManager.LookAndFeelInfo lafInfo : lafs) { + JMenuItem lafItem = new JMenuItem(lafInfo.getName()); + lafItem.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + setLaf(frame, lafInfo.getClassName()); + } + }); + lafMenu.add(lafItem); + } + + frame.setJMenuBar(bar); + bar.add(menu); + bar.add(lafMenu); + + JTextArea field = new JTextArea("The editor"); + frame.add(field); + field.requestFocusInWindow(); + frame.pack(); + return frame; + } + + private static void setLaf(JFrame frame, String laf) { + try { + UIManager.setLookAndFeel(laf); + SwingUtilities.updateComponentTreeUI(frame); + PassFailJFrame.log("Menu.cancelMode = " + + UIManager.getString("Menu.cancelMode")); + PassFailJFrame.log("Menu.preserveTopLevelSelection = " + + UIManager.getBoolean("Menu.preserveTopLevelSelection")); + PassFailJFrame.log(""); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/test/jdk/javax/swing/JMenuBar/RightLeftOrientation.java b/test/jdk/javax/swing/JMenuBar/RightLeftOrientation.java index 80779c9ce1d07..8308034d0601a 100644 --- a/test/jdk/javax/swing/JMenuBar/RightLeftOrientation.java +++ b/test/jdk/javax/swing/JMenuBar/RightLeftOrientation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -20,75 +20,76 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + /* * @test * @bug 4211731 4214512 * @summary * This test checks if menu bars lay out correctly when their - * ComponentOrientation property is set to RIGHT_TO_LEFT. This test is - * manual. The tester is asked to compare left-to-right and - * right-to-left menu bars and judge whether they are mirror images of each - * other. + * ComponentOrientation property is set to RIGHT_TO_LEFT. + * The tester is asked to compare left-to-right and + * right-to-left menu bars and decide whether they are mirror + * images of each other. * @library /test/jdk/java/awt/regtesthelpers * @build PassFailJFrame * @run main/manual RightLeftOrientation */ import java.awt.ComponentOrientation; -import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.util.List; + import javax.swing.ButtonGroup; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; +import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.SwingUtilities; import javax.swing.UIManager; -public class RightLeftOrientation { +public final class RightLeftOrientation { - static JFrame ltrFrame; - static JFrame rtlFrame; + private static List frames; private static final String INSTRUCTIONS = """ - This test checks menu bars for correct Right-To-Left Component Orientation. - - You should see two frames, each containing a menu bar. + This test checks menu bars for correct Right-To-Left component orientation. - One frame will be labelled "Left To Right" and will contain + You should see two frames, each contains a menu bar. + One frame is labelled "Left To Right" and contains a menu bar with menus starting on its left side. - The other frame will be labelled "Right To Left" and will - contain a menu bar with menus starting on its right side. + The other frame is labelled "Right To Left" and + contains a menu bar with menus starting on its right side. - The test will also contain radio buttons that can be used to set - the look and feel of the menu bars. - For each look and feel, you should compare the two menu - bars and make sure they are mirror images of each other. """; + The test also displays a frame with radio buttons + to change the look and feel of the menu bars. + For each look and feel, compare the two menu bars + in LTR and RTL orientation and make sure they are mirror + images of each other."""; public static void main(String[] args) throws Exception { PassFailJFrame.builder() - .title("RTL test Instructions") + .title("Menu Bar RTL Instructions") .instructions(INSTRUCTIONS) - .rows((int) INSTRUCTIONS.lines().count() + 2) .columns(30) .testUI(RightLeftOrientation::createTestUI) + .positionTestUIRightColumn() .build() .awaitAndCheck(); } - private static JFrame createTestUI() { - JFrame frame = new JFrame("RightLeftOrientation"); + private static JFrame createPlafChangerFrame() { + JFrame frame = new JFrame("Change Look and Feel"); JPanel panel = new JPanel(); ButtonGroup group = new ButtonGroup(); - JRadioButton rb; ActionListener plafChanger = new PlafChanger(); UIManager.LookAndFeelInfo[] lafInfos = UIManager.getInstalledLookAndFeels(); for (int i = 0; i < lafInfos.length; i++) { - rb = new JRadioButton(lafInfos[i].getName()); + JRadioButton rb = new JRadioButton(lafInfos[i].getName()); rb.setActionCommand(lafInfos[i].getClassName()); rb.addActionListener(plafChanger); group.add(rb); @@ -99,33 +100,39 @@ private static JFrame createTestUI() { } frame.add(panel); + frame.pack(); + return frame; + } - ltrFrame = new JFrame("Left To Right"); + private static List createTestUI() { + JFrame plafFrame = createPlafChangerFrame(); + + JFrame ltrFrame = new JFrame("Left To Right"); ltrFrame.setJMenuBar(createMenuBar(ComponentOrientation.LEFT_TO_RIGHT)); ltrFrame.setSize(400, 100); - ltrFrame.setLocation(new Point(10, 10)); - ltrFrame.setVisible(true); - rtlFrame = new JFrame("Right To Left"); + JFrame rtlFrame = new JFrame("Right To Left"); rtlFrame.setJMenuBar(createMenuBar(ComponentOrientation.RIGHT_TO_LEFT)); rtlFrame.setSize(400, 100); - rtlFrame.setLocation(new Point(10, 120)); - rtlFrame.setVisible(true); - frame.pack(); - return frame; + + return (frames = List.of(plafFrame, ltrFrame, rtlFrame)); } - static class PlafChanger implements ActionListener { + private static final class PlafChanger implements ActionListener { + @Override public void actionPerformed(ActionEvent e) { String lnfName = e.getActionCommand(); try { UIManager.setLookAndFeel(lnfName); - SwingUtilities.updateComponentTreeUI(ltrFrame); - SwingUtilities.updateComponentTreeUI(rtlFrame); - } - catch (Exception exc) { - System.err.println("Could not load LookAndFeel: " + lnfName); + frames.forEach(SwingUtilities::updateComponentTreeUI); + } catch (Exception exc) { + String message = "Could not set Look and Feel to " + lnfName; + System.err.println(message); + JOptionPane.showMessageDialog(frames.get(0), + message, + "Look and Feel Error", + JOptionPane.ERROR_MESSAGE); } } diff --git a/test/jdk/javax/swing/JMenuItem/JActionCommandTest.java b/test/jdk/javax/swing/JMenuItem/JActionCommandTest.java new file mode 100644 index 0000000000000..0041a509a2f1b --- /dev/null +++ b/test/jdk/javax/swing/JMenuItem/JActionCommandTest.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JTextField; +import javax.swing.KeyStroke; +import javax.swing.SwingUtilities; + +/* + * @test + * @bug 4159610 + * @key headful + * @summary Verifies that JMenuItem's shortcuts are not inserted in JTextField + * @run main JActionCommandTest + */ + +public class JActionCommandTest { + + private static Robot robot; + private static JMenu m; + private static JMenuItem mi; + private static JFrame f; + private static JTextField tf; + private static volatile Point menuLoc; + private static volatile Point menuItemLoc; + private static volatile Point textFieldLoc; + private static volatile int menuWidth; + private static volatile int menuHeight; + private static volatile int menuItemWidth; + private static volatile int menuItemHeight; + private static volatile int textFieldWidth; + private static volatile int textFieldHeight; + private static volatile boolean passed = false; + + public static void main(String[] args) throws Exception { + robot = new Robot(); + robot.setAutoDelay(50); + robot.setAutoWaitForIdle(true); + try { + SwingUtilities.invokeAndWait(JActionCommandTest::createAndShowUI); + robot.waitForIdle(); + robot.delay(1000); + SwingUtilities.invokeAndWait(() -> { + menuLoc = m.getLocationOnScreen(); + menuWidth = m.getWidth(); + menuHeight = m.getHeight(); + + textFieldLoc = tf.getLocationOnScreen(); + textFieldWidth = tf.getWidth(); + textFieldHeight = tf.getHeight(); + }); + moveAndPressMouse(menuLoc.x, menuLoc.y, menuWidth, menuHeight); + + SwingUtilities.invokeAndWait(() -> { + menuItemLoc = mi.getLocationOnScreen(); + menuItemWidth = mi.getWidth(); + menuItemHeight = mi.getHeight(); + }); + moveAndPressMouse(menuItemLoc.x, menuItemLoc.y, menuItemWidth, menuItemHeight); + System.out.println("passed is: "+passed); + if (!passed) { + throw new RuntimeException("Test Failed: JMenuItem label is not" + + " equals to 'Testitem'."); + } + passed = false; + moveAndPressMouse(textFieldLoc.x, textFieldLoc.y, textFieldWidth, textFieldHeight); + robot.keyPress(KeyEvent.VK_ALT); + robot.keyPress(KeyEvent.VK_T); + robot.keyRelease(KeyEvent.VK_T); + robot.keyRelease(KeyEvent.VK_ALT); + robot.waitForIdle(); + + System.out.println("passed is: "+passed); + System.out.println("tf.getText() is: "+tf.getText()); + if (!passed && tf.getText().equals("t")) { + throw new RuntimeException("Test Failed: Either JMenuItem label is not" + + " equal to 'Testitem' or JTextField contains text 't'. "); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + } + } + + private static void createAndShowUI() { + CustomActionListener customListener = new CustomActionListener(); + f = new JFrame("Test JMenuItem Shortcut"); + f.setLayout(new BorderLayout()); + tf = new JTextField(12); + tf.addActionListener(customListener); + JMenuBar mb = new JMenuBar(); + m = new JMenu("Test"); + mi = new JMenuItem("Testitem"); + KeyStroke ks = KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_T, + java.awt.Event.ALT_MASK, false); + mi.setAccelerator(ks); + mi.addActionListener(customListener); + m.add(mi); + mb.add(m); + f.setJMenuBar(mb); + f.add("South", tf); + f.setSize(200, 200); + f.setLocationRelativeTo(null); + f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + f.setVisible(true); + } + + public static void moveAndPressMouse(int x, int y, int width, int height) { + robot.mouseMove(x + width / 2, y + height / 2); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + } + + static class CustomActionListener implements ActionListener { + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == mi && e.getActionCommand().equals("Testitem")) { + System.out.println("MenuItem's label: " + e.getActionCommand()); + passed = true; + } + } + } +} diff --git a/test/jdk/javax/swing/JMenuItem/MenuItemTest/MenuItemTestHelper.java b/test/jdk/javax/swing/JMenuItem/MenuItemTest/MenuItemTestHelper.java new file mode 100644 index 0000000000000..16b8749058555 --- /dev/null +++ b/test/jdk/javax/swing/JMenuItem/MenuItemTest/MenuItemTestHelper.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.ComponentOrientation; +import java.awt.Graphics; +import java.awt.event.KeyEvent; +import javax.swing.Icon; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JRadioButtonMenuItem; +import javax.swing.KeyStroke; +import javax.swing.UIManager; + +final class MenuItemTestHelper { + + public static JFrame getMenuItemTestFrame(boolean isLeft, String lafName) { + boolean applyLookAndFeel = lafName != null; + if (applyLookAndFeel) { + try { + UIManager.setLookAndFeel(lafName); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + Icon myIcon = new ColoredIcon(Color.RED, 10, 10); + Icon myIcon2 = new ColoredIcon(Color.GREEN, 15, 10); + + JMenuBar menuBar = new JMenuBar(); + menuBar.add(createViewMenu(myIcon, myIcon2)); + menuBar.add(createNoNothingMenu()); + menuBar.add(createSomeIconsMenu(myIcon, myIcon2)); + + String title = (isLeft ? "(Left-to-right)" : "(Right-to-left)") + " - Menu Item Test"; + JFrame frame = new JFrame(title); + frame.setJMenuBar(menuBar); + frame.applyComponentOrientation(isLeft + ? ComponentOrientation.LEFT_TO_RIGHT + : ComponentOrientation.RIGHT_TO_LEFT); + + if (applyLookAndFeel) { + String shortName = lafName.substring(lafName.lastIndexOf('.') + 1); + JLabel label = new JLabel("

    " + shortName + "

    "); + frame.setLayout(new BorderLayout()); + frame.add(label, BorderLayout.CENTER); + } + + frame.setSize(300, 300); + return frame; + } + + public static JFrame getMenuItemTestFrame(boolean isLeft) { + return getMenuItemTestFrame(isLeft, null); + } + + private static JMenu createViewMenu(Icon myIcon, Icon myIcon2) { + JMenu menu = new JMenu("View"); + menu.setMnemonic('V'); + menu.add(new JMenuItem("Refresh")); + menu.add(new JMenuItem("Customize...")); + menu.add(new JCheckBoxMenuItem("Show Toolbar")); + menu.addSeparator(); + menu.add(new JRadioButtonMenuItem("List")); + menu.add(new JRadioButtonMenuItem("Icons")); + + JRadioButtonMenuItem rm2 = new JRadioButtonMenuItem("And icon."); + rm2.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F1, KeyEvent.SHIFT_DOWN_MASK)); + rm2.setIcon(myIcon2); + menu.add(rm2); + + JRadioButtonMenuItem mi3 = new JRadioButtonMenuItem("Radio w/icon"); + mi3.setIcon(myIcon); + menu.add(mi3); + + menu.add(new JMenuItem(myIcon2)); + + JMenuItem mi4 = new JMenuItem("Item with icon"); + mi4.setIcon(myIcon); + menu.addSeparator(); + menu.add(mi4); + + return menu; + } + + private static JMenu createNoNothingMenu() { + final JMenu noMenu = new JMenu("No nothing"); + + for (String label : new String[]{"One", "Two", "Threeee"}) { + JMenuItem item = new JMenuItem(label); + item.addActionListener((e) -> + PassFailJFrame.log("menu.width = " + + noMenu.getPopupMenu().getWidth())); + noMenu.add(item); + } + + return noMenu; + } + + private static JMenu createSomeIconsMenu(Icon myIcon, Icon myIcon2) { + JMenu someIcons = new JMenu("Some icons"); + + JMenuItem imi1 = new JMenuItem("Icon!"); + imi1.setIcon(myIcon); + someIcons.add(imi1); + + JMenuItem imi2 = new JMenuItem("Wide icon!"); + imi2.setIcon(myIcon2); + someIcons.add(imi2); + + someIcons.add(new JCheckBoxMenuItem("CheckBox")); + someIcons.add(new JRadioButtonMenuItem("RadioButton")); + + return someIcons; + } + + private record ColoredIcon(Color color, int width, int height) + implements Icon { + @Override + public void paintIcon(Component c, Graphics g, int x, int y) { + Color oldColor = g.getColor(); + g.setColor(color); + g.fillRect(x, y, width, height); + g.setColor(oldColor); + } + + @Override + public int getIconWidth() { + return width; + } + + @Override + public int getIconHeight() { + return height; + } + } +} diff --git a/test/jdk/javax/swing/JMenuItem/MenuItemTest/bug4729669.java b/test/jdk/javax/swing/JMenuItem/MenuItemTest/bug4729669.java new file mode 100644 index 0000000000000..6518b6b8fae53 --- /dev/null +++ b/test/jdk/javax/swing/JMenuItem/MenuItemTest/bug4729669.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4729669 + * @summary 1.4 REGRESSION: Text edge of different types of JMenuItems are not aligned + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4729669 + */ + +import java.util.List; +import javax.swing.JFrame; + +public class bug4729669 { + + private static final String INSTRUCTIONS = """ + Two windows should appear: Left-to-right and Right-to-left. + Check that text on all the menu items of all menus + is properly vertically aligned."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug4729669 Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(bug4729669::createTestUI) + .positionTestUIRightColumn() + .logArea() + .build() + .awaitAndCheck(); + } + + private static List createTestUI() { + JFrame f1 = MenuItemTestHelper.getMenuItemTestFrame(true); + JFrame f2 = MenuItemTestHelper.getMenuItemTestFrame(false); + return List.of(f1, f2); + } +} + diff --git a/test/jdk/javax/swing/JMenuItem/MenuItemTest/bug6197830.java b/test/jdk/javax/swing/JMenuItem/MenuItemTest/bug6197830.java new file mode 100644 index 0000000000000..afb1c7bb33f95 --- /dev/null +++ b/test/jdk/javax/swing/JMenuItem/MenuItemTest/bug6197830.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6197830 + * @requires (os.family == "linux") + * @summary Fix for 4729669 does not work on Motif and GTK look and feels + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug6197830 + */ + +import java.util.List; +import javax.swing.JFrame; + +public class bug6197830 { + + private static final String INSTRUCTIONS = """ + Four windows should appear: Left-to-right and Right-to-left for + the two different Look and Feels (Motif and GTK). + Check that text on all the menu items of all menus is properly + vertically aligned."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug6197830 Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(bug6197830::createTestUI) + .positionTestUIBottomRowCentered() + .build() + .awaitAndCheck(); + } + + private static List createTestUI() { + JFrame frame1 = MenuItemTestHelper.getMenuItemTestFrame(true, + "com.sun.java.swing.plaf.motif.MotifLookAndFeel"); + JFrame frame2 = MenuItemTestHelper.getMenuItemTestFrame(false, + "com.sun.java.swing.plaf.motif.MotifLookAndFeel"); + JFrame frame3 = MenuItemTestHelper.getMenuItemTestFrame(true, + "com.sun.java.swing.plaf.gtk.GTKLookAndFeel"); + JFrame frame4 = MenuItemTestHelper.getMenuItemTestFrame(false, + "com.sun.java.swing.plaf.gtk.GTKLookAndFeel"); + return List.of(frame1, frame2, frame3, frame4); + } +} diff --git a/test/jdk/javax/swing/JMenuItem/RightLeftOrientation.java b/test/jdk/javax/swing/JMenuItem/RightLeftOrientation.java new file mode 100644 index 0000000000000..247d8b52541b8 --- /dev/null +++ b/test/jdk/javax/swing/JMenuItem/RightLeftOrientation.java @@ -0,0 +1,199 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test id=metal + * @bug 4211052 + * @requires (os.family == "windows") + * @summary Verifies if menu items lay out correctly when their + * ComponentOrientation property is set to RIGHT_TO_LEFT. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual RightLeftOrientation metal + */ + +/* + * @test id=motif + * @bug 4211052 + * @requires (os.family == "windows") + * @summary Verifies if menu items lay out correctly when their + * ComponentOrientation property is set to RIGHT_TO_LEFT. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual RightLeftOrientation motif + */ + +/* + * @test id=windows + * @bug 4211052 8370465 + * @requires (os.family == "windows") + * @summary Verifies if menu items lay out correctly when their + * ComponentOrientation property is set to RIGHT_TO_LEFT. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual RightLeftOrientation windows + */ + +import java.awt.Color; +import java.awt.Component; +import java.awt.ComponentOrientation; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; + +import javax.swing.Icon; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JRadioButtonMenuItem; +import javax.swing.KeyStroke; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; + +public class RightLeftOrientation { + + private static final String INSTRUCTIONS = """ + A menu bar is shown with a menu. + + The menu is divided into two halves. The upper half is oriented + left-to-right and the lower half is oriented right-to-left. + Ensure that the lower half mirrors the upper half. + + Note that when checking the positioning of the sub-menus, it + helps to position the frame away from the screen edges."""; + + public static void main(String[] args) throws Exception { + if (args.length < 1) { + throw new IllegalArgumentException("Look-and-Feel keyword is required"); + } + + final String lafClassName; + switch (args[0]) { + case "metal" -> lafClassName = UIManager.getCrossPlatformLookAndFeelClassName(); + case "motif" -> lafClassName = "com.sun.java.swing.plaf.motif.MotifLookAndFeel"; + case "windows" -> lafClassName = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel"; + default -> throw new IllegalArgumentException( + "Unsupported Look-and-Feel keyword for this test: " + args[0]); + } + + SwingUtilities.invokeAndWait(() -> { + try { + UIManager.setLookAndFeel(lafClassName); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + + System.out.println("Test for LookAndFeel " + lafClassName); + + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(RightLeftOrientation::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + JFrame frame = new JFrame("RightLeftOrientation"); + JMenuBar menuBar = new JMenuBar(); + + menuBar.add(createMenu()); + + frame.setJMenuBar(menuBar); + frame.setSize(250, 70); + return frame; + } + + + static JMenu createMenu() { + JMenu menu = new JMenu(UIManager.getLookAndFeel().getID()); + addMenuItems(menu, ComponentOrientation.LEFT_TO_RIGHT); + menu.addSeparator(); + addMenuItems(menu, ComponentOrientation.RIGHT_TO_LEFT); + return menu; + } + + static void addMenuItems(JMenu menu, ComponentOrientation o) { + + JMenuItem menuItem; + + menuItem = new JMenuItem("Menu Item"); + menuItem.setComponentOrientation(o); + menu.add(menuItem); + + menuItem = new JMenuItem("Text trails icon", new MyMenuItemIcon()); + menuItem.setComponentOrientation(o); + menu.add(menuItem); + + menuItem = new JMenuItem("Text leads icon", new MyMenuItemIcon()); + menuItem.setComponentOrientation(o); + menuItem.setHorizontalTextPosition(SwingConstants.LEADING); + menu.add(menuItem); + + menuItem = new JMenuItem("Text to the left", new MyMenuItemIcon()); + menuItem.setComponentOrientation(o); + menuItem.setHorizontalTextPosition(SwingConstants.LEFT); + menu.add(menuItem); + + menuItem = new JMenuItem("Text to the right", new MyMenuItemIcon()); + menuItem.setComponentOrientation(o); + menuItem.setHorizontalTextPosition(SwingConstants.RIGHT); + menu.add(menuItem); + + menuItem = new JRadioButtonMenuItem("Radio Button Menu Item"); + menuItem.setComponentOrientation(o); + menuItem.setSelected(true); + menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_1, ActionEvent.ALT_MASK)); + menu.add(menuItem); + + menuItem = new JCheckBoxMenuItem("Check box Menu Item"); + menuItem.setComponentOrientation(o); + menuItem.setSelected(true); + menuItem.setFont( new Font("Serif",Font.PLAIN, 24) ); + menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_2, ActionEvent.ALT_MASK)); + menu.add(menuItem); + + menuItem = new JMenu("Sub Menu"); + menuItem.setComponentOrientation(o); + menuItem.add(new JMenuItem("Sub Menu One")).setComponentOrientation(o); + menuItem.add(new JMenuItem("Sub Menu Two")).setComponentOrientation(o); + menu.add(menuItem); + } + + + private static class MyMenuItemIcon implements Icon { + public void paintIcon(Component c, Graphics g, int x, int y) { + Color oldColor = g.getColor(); + g.setColor(Color.orange); + g.fill3DRect(x, y, getIconWidth(), getIconHeight(), true); + g.setColor(oldColor); + } + public int getIconWidth() { return 15; } + public int getIconHeight() { return 15; } + } +} diff --git a/test/jdk/javax/swing/JMenuItem/TestRadioAndCheckMenuItemWithIcon.java b/test/jdk/javax/swing/JMenuItem/TestRadioAndCheckMenuItemWithIcon.java new file mode 100644 index 0000000000000..15b1c7fe2176a --- /dev/null +++ b/test/jdk/javax/swing/JMenuItem/TestRadioAndCheckMenuItemWithIcon.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8348760 + * @summary Verify if RadioButtonMenuItem bullet and + * JCheckboxMenuItem checkmark is shown if + * JRadioButtonMenuItem and JCheckboxMenuItem + * is rendered with ImageIcon in WindowsLookAndFeel + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual TestRadioAndCheckMenuItemWithIcon + */ + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.awt.image.BufferedImage; + +import javax.swing.AbstractButton; +import javax.swing.ButtonGroup; +import javax.swing.ImageIcon; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JRadioButtonMenuItem; +import javax.swing.KeyStroke; +import javax.swing.UIManager; + +public class TestRadioAndCheckMenuItemWithIcon { + + private static final String INSTRUCTIONS = """ + A top level Menu will be shown. + + Clicking on the Menu will show a + JRadioButtonMenuItem group with 3 radiobutton menuitems + and a JCheckBoxMenuItem group with 3 checkbox menuitems. + + First radiobutton menuitem is selected with imageicon of a red square. + Second radiobutton menuitem is unselected with imageicon. + Third radiobutton menuItem is unselected without imageicon. + + First checkbox menuitem is selected with imageicon. + Second checkbox menuitem is unselected with imageicon. + Third checkbox menuItem is unselected without imageicon. + + Verify that for first JRadioButtonMenuItem with imageicon, + a bullet is shown alongside the imageicon and + for first JCheckBoxMenuItem with imageicon + a checkmark is shown alongside the imageicon. + + If bullet and checkmark is shown, test passes else fails."""; + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + PassFailJFrame.builder() + .title("JRadioButtonMenuItem Instructions") + .instructions(INSTRUCTIONS) + .columns(60) + .testUI(TestRadioAndCheckMenuItemWithIcon::doTest) + .build() + .awaitAndCheck(); + } + + public static JFrame doTest() { + BufferedImage img = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB); + Graphics g = img.getGraphics(); + g.setColor(Color.red); + g.fillRect(0, 0, img.getWidth(), img.getHeight()); + g.dispose(); + + BufferedImage img2 = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB); + Graphics g2 = img2.getGraphics(); + g2.setColor(Color.red); + g2.fillRect(0, 0, img2.getWidth(), img2.getHeight()); + g2.dispose(); + + JFrame frame = new JFrame("RadioButtonWithImageIcon"); + ImageIcon imageIcon1 = new ImageIcon(img); + ImageIcon imageIcon2 = new ImageIcon(img2); + AbstractButton button1; + JRadioButtonMenuItem m1 = new JRadioButtonMenuItem("JRadioButtonMenuItem 1", + imageIcon1); + m1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4, ActionEvent.ALT_MASK|ActionEvent.CTRL_MASK|ActionEvent.SHIFT_MASK)); + button1 = m1; + button1.setSelected(true); + AbstractButton button2 = new JRadioButtonMenuItem("JRadioButtonMenuItem 2", imageIcon2); + AbstractButton button3 = new JRadioButtonMenuItem("JRadioButtonMenuItem 3"); + + ButtonGroup buttonGroup = new ButtonGroup(); + buttonGroup.add(button1); + buttonGroup.add(button2); + buttonGroup.add(button3); + + AbstractButton check1 = new JCheckBoxMenuItem("JCheckBoxMenuItem 1", + imageIcon1); + check1.setSelected(true); + AbstractButton check2 = new JCheckBoxMenuItem("JCheckBoxMenuItem 2", imageIcon1); + JCheckBoxMenuItem c3; + AbstractButton check3 = c3 = new JCheckBoxMenuItem("JCheckBoxMenuItem 3"); + c3.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F5, ActionEvent.ALT_MASK|ActionEvent.CTRL_MASK|ActionEvent.SHIFT_MASK)); + + JMenu topLevel = new JMenu("Menu"); + + topLevel.add(button1); + topLevel.add(button2); + topLevel.add(button3); + + topLevel.addSeparator(); + + topLevel.add(check1); + topLevel.add(check2); + topLevel.add(check3); + + AbstractButton menuitem1 = new JMenuItem("MenuItem1"); + AbstractButton menuitem2 = new JMenuItem("MenuItem2", imageIcon1); + topLevel.addSeparator(); + topLevel.add(menuitem1); + topLevel.add(menuitem2); + + JMenuBar menuBar = new JMenuBar(); + menuBar.add(topLevel); + + frame.setJMenuBar(menuBar); + frame.setSize(300, 300); + return frame; + + } +} diff --git a/test/jdk/javax/swing/JMenuItem/bug4207339.java b/test/jdk/javax/swing/JMenuItem/bug4207339.java new file mode 100644 index 0000000000000..e2e3ef4e3cd67 --- /dev/null +++ b/test/jdk/javax/swing/JMenuItem/bug4207339.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4207339 + * @summary Verifies HTML label support for MenuItems + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4207339 + */ + +import javax.swing.JPanel; +import javax.swing.JMenuItem; + +public class bug4207339 { + + private static final String INSTRUCTIONS = """ + This tests html support in menuItem. + A MenuItem will be shown. + If the MenuItem is showing "big" text bigger than rest + and "red" text in red color and the text are in multiple lines, + and text "Yo" in blue color, + then press Pass else press Fail."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug4207339 Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .splitUI(bug4207339::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JPanel createTestUI() { + JPanel panel = new JPanel(); + JMenuItem mi = new JMenuItem("
    Is this text big" + + "and red?" + + "
    And on multiple lines?

    " + + "Yo!" + + "Then press PASS!
    "); + panel.add(mi); + return panel; + } + +} diff --git a/test/jdk/javax/swing/JMenuItem/bug4327146.java b/test/jdk/javax/swing/JMenuItem/bug4327146.java new file mode 100644 index 0000000000000..88d2b9618f0aa --- /dev/null +++ b/test/jdk/javax/swing/JMenuItem/bug4327146.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4327146 + * @summary Tests menu width after removeAll() + * @key headful + * @run main bug4327146 + */ + +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.event.InputEvent; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.SwingUtilities; + +public class bug4327146 { + private static JButton b; + private static JMenu m; + private static JFrame frame; + private static volatile Point loc; + private static volatile Dimension dim; + private static volatile Rectangle old_popupBounds; + private static volatile Rectangle new_popupBounds; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + try { + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame("bug4327146"); + m = new JMenu("Menu"); + m.add(new JMenuItem("I'm an ugly bug, fix me right now please!")); + + JMenuBar mbar = new JMenuBar(); + mbar.add(m); + frame.setJMenuBar(mbar); + + b = new JButton("Cut"); + b.addActionListener(e -> { + m.removeAll(); + m.add(new JMenuItem("Fixed :)")); + }); + mbar.add(b); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + }); + robot.waitForIdle(); + robot.delay(1000); + SwingUtilities.invokeAndWait(() -> { + loc = b.getLocationOnScreen(); + dim = b.getSize(); + m.doClick(); + }); + robot.waitForIdle(); + robot.delay(500); + SwingUtilities.invokeAndWait(() -> { + old_popupBounds = m.getPopupMenu().getBounds(); + }); + robot.mouseMove(loc.x + dim.width / 2, loc.y + dim.height / 2); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(500); + SwingUtilities.invokeAndWait(() -> { + m.doClick(); + }); + robot.waitForIdle(); + robot.delay(500); + SwingUtilities.invokeAndWait(() -> { + new_popupBounds = m.getPopupMenu().getBounds(); + }); + if (new_popupBounds.getWidth() >= old_popupBounds.getWidth()) { + System.out.println("before cut popup Bounds " + old_popupBounds); + System.out.println("after cut popupBounds " + new_popupBounds); + throw new RuntimeException("JMenu popup width is wrong"); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + +} diff --git a/test/jdk/javax/swing/JMenuItem/bug4402082.java b/test/jdk/javax/swing/JMenuItem/bug4402082.java new file mode 100644 index 0000000000000..546a2d3218bc5 --- /dev/null +++ b/test/jdk/javax/swing/JMenuItem/bug4402082.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4402082 + * @requires (os.family == "windows") + * @summary Tests that JMenuItem accelerator is rendered correctly. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4402082 + */ + +import java.awt.Container; +import java.awt.GridLayout; +import java.awt.event.KeyEvent; +import javax.swing.JFrame; +import javax.swing.JMenuItem; +import javax.swing.KeyStroke; +import javax.swing.UIManager; + +public class bug4402082 { + + private static final String INSTRUCTIONS = """ + You see three menu items, each having different look-and-feels. + Each menu item should display accelerator "F1" on its right side. + The accelerator should be fully visible. If it is partially + offscreen, or not visible at all, test fails."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug4402082 Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(bug4402082::createTestUI) + .build() + .awaitAndCheck(); + } + + static JMenuItem getMenuItem(String lnf) { + try { + UIManager.setLookAndFeel(lnf); + } catch (Exception exc) { + System.err.println("Could not load LookAndFeel: " + lnf); + } + JMenuItem mi = new JMenuItem("Bad Item"); + mi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0)); + return mi; + } + + private static JFrame createTestUI() { + JFrame frame = new JFrame("bug4402082"); + Container pane = frame.getContentPane(); + pane.setLayout(new GridLayout(3,1)); + pane.add(getMenuItem("javax.swing.plaf.metal.MetalLookAndFeel")); + pane.add(getMenuItem("com.sun.java.swing.plaf.motif.MotifLookAndFeel")); + pane.add(getMenuItem("com.sun.java.swing.plaf.windows.WindowsLookAndFeel")); + frame.pack(); + return frame; + } +} diff --git a/test/jdk/javax/swing/JOptionPane/4174551/bug4174551.html b/test/jdk/javax/swing/JOptionPane/4174551/bug4174551.html deleted file mode 100644 index ca7bac5dac5c8..0000000000000 --- a/test/jdk/javax/swing/JOptionPane/4174551/bug4174551.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - Message Dialog should pop up - with a button font size 10 - and message font size 24. - - It should be true even on OS X. - If it is not so press "Fail" else press "Pass". - - - - - diff --git a/test/jdk/javax/swing/JOptionPane/8024926/bug8024926.html b/test/jdk/javax/swing/JOptionPane/8024926/bug8024926.html deleted file mode 100644 index e63e0ea768be7..0000000000000 --- a/test/jdk/javax/swing/JOptionPane/8024926/bug8024926.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - - High resolution icon test, bug ID 8024926 - - - -

    See the dialog box (usually in upper left corner) for instructions

    - - diff --git a/test/jdk/javax/swing/JOptionPane/8024926/bug8024926.java b/test/jdk/javax/swing/JOptionPane/8024926/bug8024926.java deleted file mode 100644 index 89aa754be884b..0000000000000 --- a/test/jdk/javax/swing/JOptionPane/8024926/bug8024926.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -import java.awt.BorderLayout; -import java.awt.Dialog; -import java.awt.EventQueue; -import java.awt.Frame; -import java.awt.TextArea; -import javax.swing.JApplet; -import javax.swing.JOptionPane; - -import jdk.test.lib.Platform; - -/** - * @test - * @bug 8024926 8040279 - * @summary [macosx] AquaIcon HiDPI support - * @author Alexander Scherbatiy - * @library /test/lib - * @build jdk.test.lib.Platform - * @run applet/manual=yesno bug8024926.html - */ -public class bug8024926 extends JApplet { - //Declare things used in the test, like buttons and labels here - - public void init() { - //Create instructions for the user here, as well as set up - // the environment -- set the layout manager, add buttons, - // etc. - this.setLayout(new BorderLayout()); - - - if (Platform.isOSX()) { - String[] instructions = { - "Verify that high resolution system icons are used" - + " in JOptionPane on HiDPI displays.", - "1) Run the test on Retina display or enable the Quartz Debug" - + " and select the screen resolution with (HiDPI) label", - "2) Check that the error icon on the JOptionPane is smooth", - "If so, press PASS, else press FAIL." - }; - Sysout.createDialogWithInstructions(instructions); - - } else { - String[] instructions = { - "This test is not applicable to the current platform. Press PASS." - }; - Sysout.createDialogWithInstructions(instructions); - } - - - }//End init() - - public void start() { - //Get things going. Request focus, set size, et cetera - setSize(200, 200); - setVisible(true); - validate(); - EventQueue.invokeLater(new Runnable() { - - public void run() { - createAndShowGUI(); - } - }); - }// start() - - //The rest of this class is the actions which perform the test... - //Use Sysout.println to communicate with the user NOT System.out!! - //Sysout.println ("Something Happened!"); - private static void createAndShowGUI() { - JOptionPane.showMessageDialog(null, - "Icons should have high resolution.", - "High resolution icon test.", - JOptionPane.ERROR_MESSAGE); - } -}// class BlockedWindowTest - -/* Place other classes related to the test after this line */ -/** - * ************************************************** - * Standard Test Machinery DO NOT modify anything below -- it's a standard chunk - * of code whose purpose is to make user interaction uniform, and thereby make - * it simpler to read and understand someone else's test. - * ************************************************** - */ -/** - * This is part of the standard test machinery. It creates a dialog (with the - * instructions), and is the interface for sending text messages to the user. To - * print the instructions, send an array of strings to Sysout.createDialog - * WithInstructions method. Put one line of instructions per array entry. To - * display a message for the tester to see, simply call Sysout.println with the - * string to be displayed. This mimics System.out.println but works within the - * test harness as well as standalone. - */ -class Sysout { - - private static TestDialog dialog; - - public static void createDialogWithInstructions(String[] instructions) { - dialog = new TestDialog(new Frame(), "Instructions"); - dialog.printInstructions(instructions); - dialog.setVisible(true); - println("Any messages for the tester will display here."); - } - - public static void createDialog() { - dialog = new TestDialog(new Frame(), "Instructions"); - String[] defInstr = {"Instructions will appear here. ", ""}; - dialog.printInstructions(defInstr); - dialog.setVisible(true); - println("Any messages for the tester will display here."); - } - - public static void printInstructions(String[] instructions) { - dialog.printInstructions(instructions); - } - - public static void println(String messageIn) { - dialog.displayMessage(messageIn); - } -}// Sysout class - -/** - * This is part of the standard test machinery. It provides a place for the test - * instructions to be displayed, and a place for interactive messages to the - * user to be displayed. To have the test instructions displayed, see Sysout. To - * have a message to the user be displayed, see Sysout. Do not call anything in - * this dialog directly. - */ -class TestDialog extends Dialog { - - TextArea instructionsText; - TextArea messageText; - int maxStringLength = 80; - - //DO NOT call this directly, go through Sysout - public TestDialog(Frame frame, String name) { - super(frame, name); - int scrollBoth = TextArea.SCROLLBARS_BOTH; - instructionsText = new TextArea("", 15, maxStringLength, scrollBoth); - add("North", instructionsText); - - messageText = new TextArea("", 5, maxStringLength, scrollBoth); - add("Center", messageText); - - pack(); - - setVisible(true); - }// TestDialog() - - //DO NOT call this directly, go through Sysout - public void printInstructions(String[] instructions) { - //Clear out any current instructions - instructionsText.setText(""); - - //Go down array of instruction strings - - String printStr, remainingStr; - for (int i = 0; i < instructions.length; i++) { - //chop up each into pieces maxSringLength long - remainingStr = instructions[ i]; - while (remainingStr.length() > 0) { - //if longer than max then chop off first max chars to print - if (remainingStr.length() >= maxStringLength) { - //Try to chop on a word boundary - int posOfSpace = remainingStr.lastIndexOf(' ', maxStringLength - 1); - - if (posOfSpace <= 0) { - posOfSpace = maxStringLength - 1; - } - - printStr = remainingStr.substring(0, posOfSpace + 1); - remainingStr = remainingStr.substring(posOfSpace + 1); - } //else just print - else { - printStr = remainingStr; - remainingStr = ""; - } - - instructionsText.append(printStr + "\n"); - - }// while - - }// for - - }//printInstructions() - - //DO NOT call this directly, go through Sysout - public void displayMessage(String messageIn) { - messageText.append(messageIn + "\n"); - System.out.println(messageIn); - } -}// TestDialog class diff --git a/test/jdk/javax/swing/JOptionPane/TestJOptionHTMLTag.java b/test/jdk/javax/swing/JOptionPane/TestJOptionHTMLTag.java new file mode 100644 index 0000000000000..94318492bd93d --- /dev/null +++ b/test/jdk/javax/swing/JOptionPane/TestJOptionHTMLTag.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* @test + * @bug 5074006 + * @key headful + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Swing JOptionPane shows tag as a string after newline + * @run main/manual TestJOptionHTMLTag +*/ + +import javax.swing.JDialog; +import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; + +public class TestJOptionHTMLTag { + static String instructions + = """ + INSTRUCTIONS: + A dialog will be shown. + If it does not contain string, press Pass else press Fail. + """; + static PassFailJFrame passFailJFrame; + + public static void main(String[] args) throws Exception { + + SwingUtilities.invokeAndWait(() -> { + try { + String message = "" + "This is a test\n" + ""; + JOptionPane optionPane = new JOptionPane(); + optionPane.setMessage(message); + optionPane.setMessageType(JOptionPane.INFORMATION_MESSAGE); + JDialog dialog = new JDialog(); + dialog.setContentPane(optionPane); + dialog.pack(); + dialog.setVisible(true); + + passFailJFrame = new PassFailJFrame(instructions); + PassFailJFrame.addTestWindow(dialog); + PassFailJFrame.positionTestWindow(dialog, PassFailJFrame.Position.HORIZONTAL); + } catch (Exception e) { + e.printStackTrace(); + } + }); + passFailJFrame.awaitAndCheck(); + } +} + diff --git a/test/jdk/javax/swing/JOptionPane/4174551/bug4174551.java b/test/jdk/javax/swing/JOptionPane/TestOptionPaneStackOverflow.java similarity index 51% rename from test/jdk/javax/swing/JOptionPane/4174551/bug4174551.java rename to test/jdk/javax/swing/JOptionPane/TestOptionPaneStackOverflow.java index fd20840867afe..9cfaea13755cc 100644 --- a/test/jdk/javax/swing/JOptionPane/4174551/bug4174551.java +++ b/test/jdk/javax/swing/JOptionPane/TestOptionPaneStackOverflow.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,32 +21,39 @@ * questions. */ -/* - * @test - * @bug 4174551 - * @summary JOptionPane should allow custom buttons - * @author Xhipra Tyagi(xhipra.tyagi@india.sun.com) area=Swing - * @run applet/manual=yesno bug4174551.html +/* @test + @bug 8224267 8290162 + @key headful + @summary Verifies if StackOverflowError is not thrown for multiple newlines + @run main TestOptionPaneStackOverflow */ -import java.awt.Font; -import javax.swing.JApplet; -import javax.swing.UIManager; + +import javax.swing.JDialog; +import javax.swing.JFrame; import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; -public class bug4174551 extends JApplet { +public class TestOptionPaneStackOverflow +{ + static JFrame frame; - public void init() { + public static void main(String[] argv) throws Exception + { try { - java.awt.EventQueue.invokeLater( () -> { - UIManager.getDefaults().put("OptionPane.buttonFont", new Font("Dialog", Font.PLAIN, 10)); - UIManager.getDefaults().put("OptionPane.messageFont", new Font("Dialog", Font.PLAIN, 24)); - JOptionPane.showMessageDialog(null, "HI 24!"); - - System.out.println(UIManager.getDefaults().get("OptionPane.buttonFont")); - System.out.println(UIManager.getDefaults().get("OptionPane.messageFont")); + String message = java.nio.CharBuffer.allocate(5000).toString(). + replace('\0','\n'); + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame(); + JOptionPane optionPane = new JOptionPane(); + optionPane.createDialog(frame, null); + optionPane.setMessage(message); + }); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } }); - }catch(Exception ex) { - ex.printStackTrace(); } } } diff --git a/test/jdk/javax/swing/JOptionPane/bug4174551.java b/test/jdk/javax/swing/JOptionPane/bug4174551.java new file mode 100644 index 0000000000000..f65f95192b7e2 --- /dev/null +++ b/test/jdk/javax/swing/JOptionPane/bug4174551.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4174551 + * @summary JOptionPane should allow custom buttons + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4174551 + */ +import java.awt.FlowLayout; +import java.awt.Font; +import javax.swing.JDialog; +import javax.swing.JOptionPane; +import javax.swing.UIManager; + +public class bug4174551 { + private static final String INSTRUCTIONS = """ + Two Message Dialog should pop up side-by-side + one with a custom message font size 24 with message "HI 24" + and another with default optionpane message font size with message "HI default" + If custom message font size is not more than default message fontsize + AND + custom message buttonfont size is more than default message buttonfont size + press "Fail" else press "Pass". """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("JOptionPane Instructions") + .instructions(INSTRUCTIONS) + .rows(8) + .columns(40) + .testUI(bug4174551::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JDialog createTestUI() { + JOptionPane defaultPane = new JOptionPane(); + defaultPane.setMessage("HI default"); + defaultPane.setMessageType(JOptionPane.INFORMATION_MESSAGE); + UIManager.getDefaults().put("OptionPane.buttonFont", new Font("Dialog", Font.PLAIN, 10)); + UIManager.getDefaults().put("OptionPane.messageFont", new Font("Dialog", Font.PLAIN, 24)); + JOptionPane optionPane = new JOptionPane(); + optionPane.setMessage("HI 24!"); + optionPane.setMessageType(JOptionPane.INFORMATION_MESSAGE); + JDialog dialog = new JDialog(); + dialog.setLayout(new FlowLayout()); + dialog.add(optionPane); + dialog.add(defaultPane); + dialog.pack(); + + System.out.println(UIManager.getDefaults().get("OptionPane.buttonFont")); + System.out.println(UIManager.getDefaults().get("OptionPane.messageFont")); + return dialog; + } +} diff --git a/test/jdk/javax/swing/JOptionPane/bug4194862.java b/test/jdk/javax/swing/JOptionPane/bug4194862.java new file mode 100644 index 0000000000000..2a2822ebf1d38 --- /dev/null +++ b/test/jdk/javax/swing/JOptionPane/bug4194862.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4194862 + * @summary Tests that internal frame-based dialogs are centered relative + to their parents + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4194862 + */ + +import javax.swing.JButton; +import javax.swing.JDesktopPane; +import javax.swing.JFrame; +import javax.swing.JInternalFrame; +import javax.swing.JOptionPane; + +public class bug4194862 { + private static final String INSTRUCTIONS = """ + In the internal frame titled "Main", + click the "Show JOptionPane Dialog" button. + A dialog will appear. It should be centered with + respect to the JInternalFrame - "Main". + + If the above is true then click on JOptionPane's "YES" button + to PASS else click JOptionPane's "NO" button to FAIL the test. + """; + + public static void main(String[] args) throws Exception{ + PassFailJFrame.builder() + .title("Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug4194862::createAndShowUI) + .screenCapture() + .build() + .awaitAndCheck(); + } + + private static JFrame createAndShowUI() { + JFrame frame = new JFrame("bug4194862 - JInternalFrame JOptionPane"); + JDesktopPane desktop = new JDesktopPane(); + frame.add(desktop); + JInternalFrame jInternalFrame = new JInternalFrame("Main", true); + desktop.add(jInternalFrame); + jInternalFrame.setBounds(5, 30, 390, 240); + jInternalFrame.setVisible(true); + + JButton b = new JButton("Show JOptionPane Dialog"); + b.addActionListener(e -> { + int retVal = JOptionPane.showInternalConfirmDialog( + jInternalFrame, "Am I centered?", + "bug4194862 JOptionPane", JOptionPane.YES_NO_OPTION); + switch (retVal) { + case JOptionPane.YES_OPTION -> PassFailJFrame.forcePass(); + case JOptionPane.NO_OPTION -> + PassFailJFrame.forceFail("JOptionPane isn't centered" + + " within JInternalFrame \"Main\""); + } + }); + jInternalFrame.add(b); + + for (int i = 0; i < 4; i++) { + JInternalFrame f = new JInternalFrame("JIF: "+ i); + f.setBounds(i * 50, i * 33, 120, 120); + f.setVisible(true); + desktop.add(f); + } + frame.setSize(450, 400); + return frame; + } +} diff --git a/test/jdk/javax/swing/JOptionPane/bug8024926.java b/test/jdk/javax/swing/JOptionPane/bug8024926.java new file mode 100644 index 0000000000000..667e21f39f5b4 --- /dev/null +++ b/test/jdk/javax/swing/JOptionPane/bug8024926.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +import java.awt.BorderLayout; +import java.awt.Dialog; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.TextArea; +import javax.swing.JDialog; +import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; + +/** + * @test + * @bug 8024926 8040279 + * @requires (os.family == "mac") + * @summary [macosx] AquaIcon HiDPI support + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug8024926 + */ +public class bug8024926 { + + private static final String INSTRUCTIONS = """ + Verify that high resolution system icons are used + in JOptionPane on HiDPI displays. + 1) Run the test on Retina display or enable the Quartz Debug + and select the screen resolution with (HiDPI) label. + "2) Check that the error icon on the JOptionPane is smooth. + "If so, press PASS, else press FAIL."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("AquaIcon HIDPI Instructions") + .instructions(INSTRUCTIONS) + .rows(10) + .columns(35) + .testUI(bug8024926::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JDialog createTestUI() { + JOptionPane optionPane = new JOptionPane("High resolution icon test"); + optionPane.setMessage("Icons should have high resolutions"); + optionPane.setMessageType(JOptionPane.ERROR_MESSAGE); + JDialog dialog = new JDialog(); + dialog.setContentPane(optionPane); + dialog.pack(); + return dialog; + } +} diff --git a/test/jdk/javax/swing/JPasswordField/PasswordFieldInputMapWordTest.java b/test/jdk/javax/swing/JPasswordField/PasswordFieldInputMapWordTest.java new file mode 100644 index 0000000000000..c49e9f7083cfb --- /dev/null +++ b/test/jdk/javax/swing/JPasswordField/PasswordFieldInputMapWordTest.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @key headful + * @bug 8358813 + * @summary Password fields' InputMap should not include any word-related action. + * + * @run main PasswordFieldInputMapWordTest + */ + +import java.util.Collection; +import java.util.Set; + +import javax.swing.InputMap; +import javax.swing.JComponent; +import javax.swing.JPasswordField; +import javax.swing.KeyStroke; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; +import javax.swing.text.DefaultEditorKit; + +public class PasswordFieldInputMapWordTest { + public static void main(String[] args) throws Exception { + for (UIManager.LookAndFeelInfo laf : + UIManager.getInstalledLookAndFeels()) { + System.out.println("Testing LAF: " + laf.getClassName()); + SwingUtilities.invokeAndWait(() -> { + if (setLookAndFeel(laf)) { + runTest(); + } + }); + } + } + + private static boolean setLookAndFeel(UIManager.LookAndFeelInfo laf) { + try { + UIManager.setLookAndFeel(laf.getClassName()); + return true; + } catch (UnsupportedLookAndFeelException e) { + System.err.println("Skipping unsupported look and feel:"); + e.printStackTrace(); + return false; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + static int[] inputMapConditions = new int[] { + JComponent.WHEN_IN_FOCUSED_WINDOW, + JComponent.WHEN_FOCUSED, + JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT + }; + + /** + * These are all the actions with "word" in their field name. + */ + static Collection wordActions = Set.of( + DefaultEditorKit.deleteNextWordAction, + DefaultEditorKit.deletePrevWordAction, + DefaultEditorKit.beginWordAction, + DefaultEditorKit.endWordAction, + DefaultEditorKit.selectionBeginWordAction, + DefaultEditorKit.selectionEndWordAction, + DefaultEditorKit.previousWordAction, + DefaultEditorKit.nextWordAction, + DefaultEditorKit.selectionPreviousWordAction, + DefaultEditorKit.selectionNextWordAction + ); + + private static void runTest() { + JPasswordField field = new JPasswordField(); + + boolean testPassed = true; + for (int condition : inputMapConditions) { + InputMap inputMap = field.getInputMap(condition); + if (inputMap.allKeys() == null) { + continue; + } + for (KeyStroke keyStroke : inputMap.allKeys()) { + Object actionBinding = inputMap.get(keyStroke); + if (wordActions.contains(actionBinding)) { + if (testPassed) { + System.err.println("The following inputs/actions should not be available in a JPasswordField:"); + } + System.err.println(inputMap.get(keyStroke) + " (try typing " + keyStroke + ")"); + testPassed = false; + } + } + } + + if (!testPassed) { + throw new RuntimeException("One or more input/action binding was observed for a JPasswordField."); + } + } +} diff --git a/test/jdk/javax/swing/JPasswordField/bug4382819.java b/test/jdk/javax/swing/JPasswordField/bug4382819.java new file mode 100644 index 0000000000000..3a8799a2d521e --- /dev/null +++ b/test/jdk/javax/swing/JPasswordField/bug4382819.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.FlowLayout; +import java.awt.event.ItemListener; +import javax.swing.JCheckBox; +import javax.swing.JFrame; +import javax.swing.JPasswordField; + +/* + * @test + * @bug 4382819 + * @summary Tests the correctness of color used for the disabled passwordField. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4382819 + */ + +public class bug4382819 { + static JCheckBox enabledCheckBox; + static JPasswordField passwordField; + + private static final String INSTRUCTIONS = """ + Clear the "enabled" checkbox. + If the JPasswordField's foreground color changes to + light gray press Pass. If it stays black press Fail. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(bug4382819::createTestUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createTestUI() { + JFrame mainFrame = new JFrame("bug4382819"); + enabledCheckBox = new javax.swing.JCheckBox(); + enabledCheckBox.setSelected(true); + enabledCheckBox.setText("enabled"); + enabledCheckBox.setActionCommand("enabled"); + mainFrame.add(enabledCheckBox); + + passwordField = new javax.swing.JPasswordField(); + passwordField.setText("blahblahblah"); + mainFrame.add(passwordField); + SymItem lSymItem = new SymItem(); + enabledCheckBox.addItemListener(lSymItem); + + mainFrame.setSize(300, 100); + mainFrame.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5)); + return mainFrame; + } + + static class SymItem implements ItemListener { + public void itemStateChanged(java.awt.event.ItemEvent event) { + Object object = event.getSource(); + if (object == enabledCheckBox) { + passwordField.setEnabled(enabledCheckBox.isSelected()); + } + } + } +} + diff --git a/test/jdk/javax/swing/JPopupMenu/6583251/bug6583251.java b/test/jdk/javax/swing/JPopupMenu/6583251/bug6583251.java index e77b60ad33a65..cba687e9cbaea 100644 --- a/test/jdk/javax/swing/JPopupMenu/6583251/bug6583251.java +++ b/test/jdk/javax/swing/JPopupMenu/6583251/bug6583251.java @@ -4,9 +4,7 @@ * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/test/jdk/javax/swing/JPopupMenu/7160604/bug7160604.html b/test/jdk/javax/swing/JPopupMenu/7160604/bug7160604.html deleted file mode 100644 index d1356c3f6d59c..0000000000000 --- a/test/jdk/javax/swing/JPopupMenu/7160604/bug7160604.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - -Click on the top-bar and combo-box more than once. -Make sure popup menu and drop-down list have a border and their items are drawn properly. - - diff --git a/test/jdk/javax/swing/JPopupMenu/7160604/bug7160604.java b/test/jdk/javax/swing/JPopupMenu/7160604/bug7160604.java deleted file mode 100644 index cf73ba608f7cf..0000000000000 --- a/test/jdk/javax/swing/JPopupMenu/7160604/bug7160604.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* @test - @bug 7160604 - @summary Using non-opaque windows - popups are initially not painted correctly - @author Oleg Pekhovskiy - @run applet/manual=yesno bug7160604.html -*/ - -import javax.swing.AbstractAction; -import javax.swing.BorderFactory; -import javax.swing.JApplet; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JMenuItem; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JWindow; -import javax.swing.SwingUtilities; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import static java.awt.GraphicsDevice.WindowTranslucency.*; - -public class bug7160604 extends JApplet { - - public void init() { - SwingUtilities.invokeLater(() -> { - if (!GraphicsEnvironment - .getLocalGraphicsEnvironment() - .getDefaultScreenDevice() - .isWindowTranslucencySupported(PERPIXEL_TRANSLUCENT)) { - // Tested translucency is not supported. Test passed - return; - } - - final JWindow window = new JWindow(); - window.setLocation(200, 200); - window.setSize(300, 300); - - final JLabel label = new JLabel("...click to invoke JPopupMenu"); - label.setOpaque(true); - final JPanel contentPane = new JPanel(new BorderLayout()); - contentPane.setBorder(BorderFactory.createLineBorder(Color.RED)); - window.setContentPane(contentPane); - contentPane.add(label, BorderLayout.NORTH); - - final JComboBox comboBox = new JComboBox(new Object[]{"1", "2", "3", "4"}); - contentPane.add(comboBox, BorderLayout.SOUTH); - - final JPopupMenu jPopupMenu = new JPopupMenu(); - - jPopupMenu.add("string"); - jPopupMenu.add(new AbstractAction("action") { - @Override - public void actionPerformed(final ActionEvent e) { - } - }); - jPopupMenu.add(new JLabel("label")); - jPopupMenu.add(new JMenuItem("MenuItem")); - label.addMouseListener(new MouseAdapter() { - @Override - public void mouseReleased(final MouseEvent e) { - jPopupMenu.show(label, 0, 0); - } - }); - - window.setBackground(new Color(0, 0, 0, 0)); - - window.setVisible(true); - }); - } -} diff --git a/test/jdk/javax/swing/JPopupMenu/bug4119993.java b/test/jdk/javax/swing/JPopupMenu/bug4119993.java new file mode 100644 index 0000000000000..1fe72ea29d6ba --- /dev/null +++ b/test/jdk/javax/swing/JPopupMenu/bug4119993.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 4119993 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Check that mouse button 3 is reserved for popup invocation not selection. + * @run main/manual bug4119993 + */ + +import java.awt.BorderLayout; +import java.awt.Dimension; +import javax.swing.BoxLayout; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.JTextArea; +import javax.swing.border.BevelBorder; + +/* + * This is a sort of negative test. Mouse Button 3 is not supposed to cause selections. + * If it did, then it would not be useable to invoke popup menus. + * So this popup menu test .. does not popup menus. + */ + +public class bug4119993 { + + static final String INSTRUCTIONS = """ + + The test window contains a text area, a table, and a list. +

    + For each component, try to select text/cells/rows/items as appropriate + using the RIGHT mouse button (Mouse Button 3). +

    + If the selection changes, then press FAIL. +

    + If the selection does not change, press PASS + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(60) + .testUI(bug4119993::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + JFrame frame = new JFrame("bug4119993"); + JPanel p = new JPanel(); + p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); + frame.add(p); + + String text = "This is some text that you should try to select using the right mouse button"; + JTextArea area = new JTextArea(text, 5, 40); + JScrollPane scrollpane0 = new JScrollPane(area); + scrollpane0.setBorder(new BevelBorder(BevelBorder.LOWERED)); + scrollpane0.setPreferredSize(new Dimension(430, 200)); + p.add(scrollpane0); + + String[][] data = new String[5][5]; + String[] cols = new String[5]; + for (int r = 0; r < 5; r ++) { + cols[r] = "col " + r; + for (int c = 0; c < 5; c ++) { + data[r][c] = "(" + r + "," + c + ")"; + } + } + + JTable tableView = new JTable(data, cols); + JScrollPane scrollpane = new JScrollPane(tableView); + scrollpane.setBorder(new BevelBorder(BevelBorder.LOWERED)); + scrollpane.setPreferredSize(new Dimension(430, 200)); + p.add(scrollpane); + + String[] s = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"}; + JList listView = new JList(s); + JScrollPane scrollpane2 = new JScrollPane(listView); + scrollpane2.setBorder(new BevelBorder(BevelBorder.LOWERED)); + scrollpane2.setPreferredSize(new Dimension(430, 200)); + p.add(scrollpane2); + + frame.pack(); + return frame; + } +} diff --git a/test/jdk/javax/swing/JPopupMenu/bug4187004.java b/test/jdk/javax/swing/JPopupMenu/bug4187004.java new file mode 100644 index 0000000000000..40a6633c13006 --- /dev/null +++ b/test/jdk/javax/swing/JPopupMenu/bug4187004.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + @bug 4187004 + @summary test that title/label is show on menu for Motif L&F + @key headful +*/ + +import java.awt.Dimension; +import java.awt.Robot; +import javax.swing.JFrame; +import javax.swing.JPopupMenu; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; + +public class bug4187004 { + + static volatile JPopupMenu m; + static volatile Dimension d1, d2; + + public static void main(String[] args) throws Exception { + try { + SwingUtilities.invokeAndWait(bug4187004::createUI); + + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(1000); + d1 = m.getSize(); + SwingUtilities.invokeAndWait(bug4187004::hideMenu); + robot.waitForIdle(); + robot.delay(1000); + SwingUtilities.invokeAndWait(bug4187004::updateUI); + robot.waitForIdle(); + robot.delay(1000); + SwingUtilities.invokeAndWait(bug4187004::showMenu); + robot.waitForIdle(); + robot.delay(1000); + d2 = m.getSize(); + } finally { + SwingUtilities.invokeAndWait(bug4187004::hideMenu); + } + System.out.println(d1); + System.out.println(d2); + if (d1.width <= d2.width) { + throw new RuntimeException("Menu not updated"); + } + } + + static void createUI() { + try { + UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel"); + } catch (Exception e) { + throw new RuntimeException(e); + } + m = new JPopupMenu("One really long menu title"); + m.add("Item 1"); + m.add("Item 2"); + m.add("Item 3"); + m.add("Item 4"); + m.pack(); + m.setVisible(true); + } + + static void hideMenu() { + m.setVisible(false); + } + + static void showMenu() { + m.setVisible(true); + } + + static void updateUI() { + m.setLabel("short"); + m.pack(); + } +} diff --git a/test/jdk/javax/swing/JPopupMenu/bug4188832.java b/test/jdk/javax/swing/JPopupMenu/bug4188832.java new file mode 100644 index 0000000000000..9c958ebc1efb5 --- /dev/null +++ b/test/jdk/javax/swing/JPopupMenu/bug4188832.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @summary Test that medium weight submenus are not hidden by a heavyweight canvas. + * @bug 4188832 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4188832 + */ + +import java.awt.Color; +import java.awt.Panel; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; + +public class bug4188832 { + + static final String INSTRUCTIONS = """ + Select the File menu, then select the "Save As..." submenu. + If you can see the submenu items displayed, press PASS, else press FAIL + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug4188832::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + // for Medium Weight menus + JPopupMenu.setDefaultLightWeightPopupEnabled(false); + JFrame frame = new JFrame("bug4188832"); + + // Create the menus + JMenuBar menuBar = new JMenuBar(); + JMenu fileMenu = new JMenu("File"); + menuBar.add(fileMenu); + fileMenu.add(new JMenuItem("New")); + fileMenu.add(new JMenuItem("Open")); + fileMenu.add(new JMenuItem("Save")); + JMenu sm = new JMenu("Save As..."); + // these guys don't show up + sm.add(new JMenuItem("This")); + sm.add(new JMenuItem("That")); + fileMenu.add(sm); + fileMenu.add(new JMenuItem("Exit")); + frame.setJMenuBar(menuBar); + + Panel field = new Panel(); // a heavyweight container + field.setBackground(Color.blue); + frame.add(field); + frame.setSize(400, 400); + return frame; + } +} diff --git a/test/jdk/javax/swing/JPopupMenu/bug4212464.java b/test/jdk/javax/swing/JPopupMenu/bug4212464.java new file mode 100644 index 0000000000000..c1c434f6b16f7 --- /dev/null +++ b/test/jdk/javax/swing/JPopupMenu/bug4212464.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 4212464 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Verify popup menu borders are drawn correctly when switching L&Fs + * @run main/manual bug4212464 + */ + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import javax.swing.JApplet; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; + +public class bug4212464 extends JFrame implements ActionListener { + + static String strMotif = "Motif"; + static String motifClassName = "com.sun.java.swing.plaf.motif.MotifLookAndFeel"; + + static String strMetal = "Metal"; + static String metalClassName = "javax.swing.plaf.metal.MetalLookAndFeel"; + + static bug4212464 frame; + static JPopupMenu popup; + + static final String INSTRUCTIONS = """ + This test is to see whether popup menu borders behave properly when switching + back and forth between Motif and Metal L&F. The initial L&F is Metal. + + Pressing the mouse button on the label in the center of the test window brings + up a popup menu. + + In order to test, use the labeled buttons to switch the look and feel. + Clicking a button will cause the menu to be hidden. This is OK. Just click the label again. + Switch back and forth and verify that the popup menu border changes consistently + and there is a title for the menu when using Motif L&F (Metal won't have a title). + + Make sure you switch back and forth several times. + If the change is consistent, press PASS otherwise press FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(bug4212464::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + try { + UIManager.setLookAndFeel(metalClassName); // initialize to Metal. + } catch (Exception e) { + throw new RuntimeException(e); + } + frame = new bug4212464("bug4212464"); + popup = new JPopupMenu("Test"); + popup.add("Item 1"); + popup.add("Item 2"); + popup.add("Item 3"); + popup.add("Item 4"); + + JPanel p = new JPanel(); + p.setLayout(new FlowLayout()); + JButton motif = (JButton)p.add(new JButton(strMotif)); + JButton metal = (JButton)p.add(new JButton(strMetal)); + motif.setActionCommand(motifClassName); + metal.setActionCommand(metalClassName); + motif.addActionListener(frame); + metal.addActionListener(frame); + frame.add(BorderLayout.NORTH, p); + + JLabel l = new JLabel("Click any mouse button on this big label"); + l.setFont(new Font(Font.DIALOG, Font.PLAIN, 20)); + l.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent e) { + popup.show(e.getComponent(), e.getX(), e.getY()); + } + }); + frame.add(BorderLayout.CENTER, l); + frame.setSize(500, 400); + return frame; + } + + public bug4212464(String title) { + super(title); + } + + public void actionPerformed(ActionEvent e) { + String str = e.getActionCommand(); + if (str.equals(metalClassName) || str.equals(motifClassName)) { + changeLNF(str); + } else { + System.out.println("ActionEvent: " + str); + } + } + + public void changeLNF(String str) { + System.out.println("Changing LNF to " + str); + try { + UIManager.setLookAndFeel(str); + SwingUtilities.updateComponentTreeUI(frame); + SwingUtilities.updateComponentTreeUI(popup); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/test/jdk/javax/swing/JPopupMenu/bug4234793.java b/test/jdk/javax/swing/JPopupMenu/bug4234793.java new file mode 100644 index 0000000000000..9ad91e5165f50 --- /dev/null +++ b/test/jdk/javax/swing/JPopupMenu/bug4234793.java @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 4234793 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary PopupMenuListener popupMenuCanceled is never called + * @run main/manual bug4234793 + */ + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import javax.swing.AbstractAction; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JSeparator; +import javax.swing.KeyStroke; +import javax.swing.event.PopupMenuEvent; +import javax.swing.event.PopupMenuListener; + +/** + * For all 3 components (JPopupMenu, JComboBox, JPopup) when the popup is visible, + * the popupMenuCanceled should be invoked in these two circumstances: + * + * 1. The ESCAPE key is pressed while the popup is open. + * + * 2. The mouse is clicked on another component. + * + */ + +public class bug4234793 extends JFrame implements PopupMenuListener { + + static final String INSTRUCTIONS = """ + The test window will contain several kinds of menus. + + * A menu bar with two menus labeled "1 - First Menu" and "2 - Second Menu" + * A drop down combo box - ie a button which pops up a menu when clicked + * Clicking any where on the background of the window will display a popup menu + + That is 4 menus in total. + + For each case, verify that the menu can be cancelled (hidden) in two ways + 1) Click to display the menu, then to hide it, press the ESCAPE key. + 2) Click to display the menu, then to hide it, LEFT click on the window background. + Note : the popup menu must be displayed using RIGHT click, the others use LEFT click. + + Notice each time you perform a hide/cancel action an appropriate message should + appear in the log area + If this is true for all 8 combinations of menus + hide actions the test PASSES + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(60) + .testUI(bug4234793::createUI) + .logArea() + .build() + .awaitAndCheck(); + } + + private static String[] numData = { + "One", "Two", "Three", "Four", "Five", "Six", "Seven" + }; + + private static String[] dayData = { + "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" + }; + + private static char[] mnDayData = { + 'M', 'T', 'W', 'R', 'F', 'S', 'U' + }; + + bug4234793(String title) { + super(title); + } + + static volatile JPopupMenu popupMenu; + static volatile bug4234793 frame; + + static JFrame createUI() { + frame = new bug4234793("bug4234793"); + frame.setJMenuBar(createMenuBar()); + JPanel panel = createContentPane(); + frame.add(panel); + + // CTRL-down will show the popup. + panel.getInputMap().put(KeyStroke.getKeyStroke( + KeyEvent.VK_DOWN, InputEvent.CTRL_MASK), "OPEN_POPUP"); + panel.getActionMap().put("OPEN_POPUP", new PopupHandler()); + panel.addMouseListener(new PopupListener(popupMenu)); + panel.setPreferredSize(new Dimension(400, 300)); + frame.setSize(400, 300); + return frame; + } + + static class PopupListener extends MouseAdapter { + private JPopupMenu popup; + + public PopupListener(JPopupMenu popup) { + this.popup = popup; + } + + public void mousePressed(MouseEvent e) { + maybeShowPopup(e); + } + + public void mouseReleased(MouseEvent e) { + maybeShowPopup(e); + } + + public void mouseClicked(MouseEvent ex) { + } + + private void maybeShowPopup(MouseEvent e) { + if (e.isPopupTrigger()) { + popup.show(e.getComponent(), e.getX(), e.getY()); + } + } + } + + static class PopupHandler extends AbstractAction { + public void actionPerformed(ActionEvent e) { + if (!popupMenu.isVisible()) + popupMenu.show((Component)e.getSource(), 40, 40); + } + } + + static JPanel createContentPane() { + popupMenu = new JPopupMenu(); + JMenuItem item; + for (int i = 0; i < dayData.length; i++) { + item = popupMenu.add(new JMenuItem(dayData[i], mnDayData[i])); + } + popupMenu.addPopupMenuListener(frame); + + JComboBox combo = new JComboBox(numData); + combo.addPopupMenuListener(frame); + JPanel comboPanel = new JPanel(); + comboPanel.add(combo); + + JPanel panel = new JPanel(new BorderLayout()); + + panel.add(new JLabel("Right click on the panel to show the PopupMenu"), BorderLayout.NORTH); + panel.add(comboPanel, BorderLayout.CENTER); + + return panel; + } + + static JMenuBar createMenuBar() { + JMenuBar menubar = new JMenuBar(); + JMenuItem menuitem; + + JMenu menu = new JMenu("1 - First Menu"); + menu.setMnemonic('1'); + menu.getPopupMenu().addPopupMenuListener(frame); + + menubar.add(menu); + for (int i = 0; i < 10; i ++) { + menuitem = new JMenuItem("1 JMenuItem" + i); + menuitem.setMnemonic('0' + i); + menu.add(menuitem); + } + + // second menu + menu = new JMenu("2 - Second Menu"); + menu.getPopupMenu().addPopupMenuListener(frame); + menu.setMnemonic('2'); + + menubar.add(menu); + for (int i = 0; i < 5; i++) { + menuitem = new JMenuItem("2 JMenuItem" + i); + menuitem.setMnemonic('0' + i); + menu.add(menuitem); + } + + JMenu submenu = new JMenu("Sub Menu"); + submenu.setMnemonic('S'); + submenu.getPopupMenu().addPopupMenuListener(frame); + for (int i = 0; i < 5; i++) { + menuitem = new JMenuItem("S JMenuItem" + i); + menuitem.setMnemonic('0' + i); + submenu.add(menuitem); + } + menu.add(new JSeparator()); + menu.add(submenu); + + return menubar; + } + + // PopupMenuListener methods. + + public void popupMenuWillBecomeVisible(PopupMenuEvent e) { + Object source = e.getSource(); + PassFailJFrame.log("popupmenu visible: " + source.getClass().getName()); + } + + public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { + Object source = e.getSource(); + PassFailJFrame.log("popupMenuWillBecomeInvisible: " + source.getClass().getName()); + } + + public void popupMenuCanceled(PopupMenuEvent e) { + Object source = e.getSource(); + PassFailJFrame.log("POPUPMENU CANCELED: " + source.getClass().getName()); + } +} diff --git a/test/jdk/javax/swing/JPopupMenu/bug4236750.java b/test/jdk/javax/swing/JPopupMenu/bug4236750.java new file mode 100644 index 0000000000000..2090c7feb23b9 --- /dev/null +++ b/test/jdk/javax/swing/JPopupMenu/bug4236750.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.Action; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.MenuElement; +import javax.swing.SwingUtilities; +import java.awt.event.ActionEvent; +import java.beans.PropertyChangeListener; + +/* + * @test + * @bug 4236750 + * @summary Tests presence of JPopupMenu.insert(Action, int) + * @run main bug4236750 + */ + +public class bug4236750 { + private static MenuElement[] elements; + private static volatile boolean passed = true; + + /** + * Auxilliary class implementing Action + */ + static class NullAction implements Action { + public void addPropertyChangeListener( + PropertyChangeListener listener) { + } + + public void removePropertyChangeListener( + PropertyChangeListener listener) { + } + + public void setEnabled(boolean b) { + } + + public boolean isEnabled() { + return true; + } + + public void actionPerformed(ActionEvent e) { + } + + private String name; + + public NullAction(String s) { + name = s; + } + + public void putValue(String key, Object value) { + if (key.equals(Action.NAME)) { + name = (String) value; + } + } + + public Object getValue(String key) { + if (key.equals(Action.NAME)) { + return name; + } + return null; + } + } + + public static void main(String[] args) throws Exception { + SwingUtilities.invokeAndWait(() -> { + JPopupMenu popup; + popup = new JPopupMenu("Test Popup"); + JMenuItem item0 = popup.add(new NullAction("0")); + JMenuItem item2 = popup.add(new NullAction("2")); + popup.insert(new NullAction("1"), 1); + elements = popup.getSubElements(); + for (int i = 0; i < 3; i++) { + JMenuItem mi = (JMenuItem) elements[i]; + if (!mi.getText().equals("" + i)) { + passed = false; + } + } + }); + + if (!passed) { + throw new RuntimeException("Failed: wrong order of menuitems"); + } + System.out.println("Test Passed!"); + } +} diff --git a/test/jdk/javax/swing/JPopupMenu/bug4321273.java b/test/jdk/javax/swing/JPopupMenu/bug4321273.java new file mode 100644 index 0000000000000..a19210373fd6f --- /dev/null +++ b/test/jdk/javax/swing/JPopupMenu/bug4321273.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.SwingUtilities; +import java.awt.Robot; +import java.io.ByteArrayOutputStream; +import java.io.ObjectOutputStream; + + +/* + * @test + * @bug 4321273 + * @summary NotSerializableException during the menu serialization + * @key headful + * @run main bug4321273 +*/ + +public class bug4321273 { + public static JFrame frame; + public static JMenu menu; + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + SwingUtilities.invokeAndWait(() -> { + JMenuBar menuBar = new JMenuBar(); + frame = new JFrame(); + frame.setJMenuBar(menuBar); + menu = new JMenu("Menu"); + menuBar.add(menu); + menu.add(new JMenuItem("item 1")); + menu.add(new JMenuItem("item 2")); + menu.add(new JMenuItem("item 3")); + frame.pack(); + frame.setVisible(true); + }); + robot.waitForIdle(); + robot.delay(1000); + SwingUtilities.invokeAndWait(() -> { + menu.doClick(); + try { + ByteArrayOutputStream byteArrayOutputStream = + new ByteArrayOutputStream(); + ObjectOutputStream oos = + new ObjectOutputStream(byteArrayOutputStream); + oos.writeObject(menu); + } catch (Exception se) { + throw new RuntimeException("NotSerializableException " + + "during the menu serialization", se); + } + }); + + robot.waitForIdle(); + robot.delay(100); + System.out.println("Test Passed!"); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } +} diff --git a/test/jdk/javax/swing/JPopupMenu/bug4530303.java b/test/jdk/javax/swing/JPopupMenu/bug4530303.java new file mode 100644 index 0000000000000..c99dacc7cff3c --- /dev/null +++ b/test/jdk/javax/swing/JPopupMenu/bug4530303.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 4530303 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Tests JPopupMenu.pack() + * @run main/manual bug4530303 + */ + +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; + +public class bug4530303 { + + static final String INSTRUCTIONS = """ + The test window has a menu bar. + Open the menu "Menu" and place the mouse pointer over the first menu item, "Point here". + The second menu item, "Ghost", should be replaced with another item, "Fixed!". + If the item just disappears and no new item appears in the empty space, the test FAILS. + """; + + static volatile JMenu menu; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(60) + .testUI(bug4530303::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + JFrame frame = new JFrame("bug4530303"); + menu = new JMenu("Menu"); + JMenuItem item = new JMenuItem("Point here"); + item.addMouseListener(new MenuBuilder()); + menu.add(item); + menu.add(new JMenuItem("Ghost")); + + JMenuBar mbar = new JMenuBar(); + mbar.add(menu); + frame.setJMenuBar(mbar); + frame.setSize(300, 300); + return frame; + } + + static class MenuBuilder extends MouseAdapter { + public void mouseEntered(MouseEvent ev) { + menu.remove(1); + menu.add(new JMenuItem("Fixed!")); + + JPopupMenu pm = menu.getPopupMenu(); + pm.pack(); + pm.paintImmediately(pm.getBounds()); + } + } +} diff --git a/test/jdk/javax/swing/JPopupMenu/bug4711693.java b/test/jdk/javax/swing/JPopupMenu/bug4711693.java new file mode 100644 index 0000000000000..f24b2eb7c4298 --- /dev/null +++ b/test/jdk/javax/swing/JPopupMenu/bug4711693.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.JFrame; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.SwingUtilities; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Insets; +import java.awt.Robot; +import java.awt.Window; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.InputEvent; + +/* + * @test + * @bug 4711693 + * @summary Pop-up doesn't stay up + * @key headful + * @run main bug4711693 + */ + +public class bug4711693 { + static JFrame fr; + static Robot robot; + static volatile boolean passed = true; + static volatile Dimension scr; + + public static void main(String[] args) throws Exception { + try { + robot = new Robot(); + SwingUtilities.invokeAndWait(() -> { + fr = new JFrame("Test 4711693"); + scr = new Dimension(); + fr.setSize(600, 600); + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice[] gs = ge.getScreenDevices(); + GraphicsConfiguration gc = null; + + for (int j = 0; j < gs.length; j++) { + GraphicsDevice gd = gs[j]; + gc = gd.getDefaultConfiguration(); + if (gc.getBounds().contains(100, 100)) break; + } + scr = Toolkit.getDefaultToolkit().getScreenSize(); + Insets ins = Toolkit.getDefaultToolkit().getScreenInsets(gc); + scr.width -= ins.right; + scr.height -= ins.bottom; + fr.setLocation(scr.width - 400, scr.height - 400); + fr.setVisible(true); + }); + robot.waitForIdle(); + robot.delay(1000); + SwingUtilities.invokeAndWait(() -> { + final JPopupMenu popupMenu = new JPopupMenu(); + final Component pane = fr.getContentPane(); + for (int i = 1; i < 10; i++) { + final String itemName = "Item " + i; + JMenuItem it = popupMenu.add(new JMenuItem(itemName)); + it.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent a) { + passed = false; + } + }); + } + + pane.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent e) { + if ((e.isAltDown() || + ((e.getModifiersEx() & + InputEvent.BUTTON3_DOWN_MASK) != 0))) { + Component parent = e.getComponent(); + while (parent != null && !(parent instanceof Window)) { + parent = parent.getParent(); + } + popupMenu.show(pane, e.getX(), e.getY()); + } + } + }); + }); + + robot.mouseMove(scr.width - 55, scr.height - 55); + robot.mousePress(InputEvent.BUTTON3_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON3_DOWN_MASK); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (fr != null) { + fr.dispose(); + } + }); + } + if (!passed) { + throw new RuntimeException("Test failed. Popup disposed on mouse release."); + } else { + System.out.println("Test Passed!"); + } + } +} diff --git a/test/jdk/javax/swing/JPopupMenu/bug4962731.java b/test/jdk/javax/swing/JPopupMenu/bug4962731.java new file mode 100644 index 0000000000000..651678cf3866c --- /dev/null +++ b/test/jdk/javax/swing/JPopupMenu/bug4962731.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2004, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPopupMenu; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.plaf.PopupMenuUI; +import java.awt.BorderLayout; +import java.awt.Robot; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 4962731 + * @summary The PopupMenu is not repainted if the LAF is changed. + * @key headful + * @run main bug4962731 + */ + +public class bug4962731 { + + public static volatile boolean passed = false; + public static boolean isLafOk = true; + public static JFrame mainFrame; + public static JButton button; + public static MyPopup popup; + public static Robot robot; + + public static void main(String[] args) throws Exception { + + try { + UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel"); + } catch (Exception ex) { + System.err.println("Can not initialize Motif L&F. Testing skipped."); + isLafOk = false; + } + + try { + UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); + } catch (Exception ex) { + System.err.println("Can not initialize Metal L&F. Testing skipped."); + isLafOk = false; + } + + if (isLafOk) { + try { + robot = new Robot(); + SwingUtilities.invokeAndWait(() -> { + mainFrame = new JFrame("Bug4962731"); + button = new JButton("Popup!"); + popup = new MyPopup(); + popup.add("one"); + popup.add("two"); + button.setComponentPopupMenu(popup); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + popup.show(button, 300, 300); + popup.engage(); + try { + Thread.sleep(1000); + } catch (InterruptedException e1) { + } + try { + UIManager.setLookAndFeel + ("com.sun.java.swing.plaf.motif.MotifLookAndFeel"); + } catch (Exception ex) { + } + try { + Thread.sleep(1000); + } catch (InterruptedException e1) { + } + SwingUtilities.updateComponentTreeUI(mainFrame); + passed = popup.check(); + } + }); + mainFrame.setLayout(new BorderLayout()); + mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + mainFrame.add(button, BorderLayout.CENTER); + mainFrame.pack(); + mainFrame.setVisible(true); + }); + + robot.delay(1000); + SwingUtilities.invokeAndWait(() -> { + button.doClick(); + }); + + if (!passed) { + throw new RuntimeException("The UI of popup was not changed"); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (mainFrame != null) { + mainFrame.dispose(); + } + }); + } + } + System.out.println("test Passed!"); + } + + public static class MyPopup extends JPopupMenu { + PopupMenuUI thisUI; + + public void engage() { + thisUI = getUI(); + } + + public boolean check() { + return getUI() != thisUI; + } + } +} diff --git a/test/jdk/javax/swing/JPopupMenu/bug4966109.java b/test/jdk/javax/swing/JPopupMenu/bug4966109.java new file mode 100644 index 0000000000000..a643448a7bb4f --- /dev/null +++ b/test/jdk/javax/swing/JPopupMenu/bug4966109.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2004, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.JLabel; +import javax.swing.JFrame; +import javax.swing.JPopupMenu; +import javax.swing.SwingUtilities; +import java.awt.BorderLayout; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +/* + * @test + * @bug 4966109 + * @summary Popup is not populated by mouse actions on lightweight components without mouse + * @key headful + * @run main bug4966109 + */ + +public class bug4966109 { + public static JFrame mainFrame; + public static JPopupMenu popup; + public static JLabel label1; + public static JLabel label2; + public static Robot robot; + public static volatile Point loc; + public static volatile Boolean passed = true; + public static int popupTrigger; + + public static void main(String[] args) throws Exception { + try { + robot = new Robot(); + SwingUtilities.invokeAndWait(() -> { + mainFrame = new JFrame("Bug4966109"); + popup = new JPopupMenu(); + label1 = new JLabel("Label with the listener"); + label2 = new JLabel("Label w/o listener"); + mainFrame.setLayout(new BorderLayout()); + mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + mainFrame.add(label1, BorderLayout.NORTH); + mainFrame.add(label2, BorderLayout.SOUTH); + mainFrame.pack(); + mainFrame.setVisible(true); + popup.add("One"); + popup.add("Two"); + popup.add("Three"); + label1.setComponentPopupMenu(popup); + label1.addMouseListener(new MouseAdapter() { + }); + label2.setComponentPopupMenu(popup); + }); + robot.waitForIdle(); + robot.delay(1000); + + SwingUtilities.invokeAndWait(() -> { + loc = label1.getLocationOnScreen(); + loc.x = loc.x + (int) (label1.getWidth() / 2); + loc.y = loc.y + (int) (label1.getHeight() / 2); + }); + popupTrigger = MouseEvent.BUTTON2_DOWN_MASK; + robot.mouseMove(loc.x, loc.y); + robot.mousePress(popupTrigger); + robot.mouseRelease(popupTrigger); + robot.waitForIdle(); + robot.delay(100); + + SwingUtilities.invokeAndWait(() -> { + if (popup.isVisible()) { + System.out.println("ZAV: Good!!! BUTTON2 is the way to go."); + } else { + System.out.println("ZAV: Bad :( Let's try BUTTON3"); + popupTrigger = MouseEvent.BUTTON3_DOWN_MASK; + } + }); + + robot.mousePress(popupTrigger); + robot.mouseRelease(popupTrigger); + robot.waitForIdle(); + robot.delay(100); + + SwingUtilities.invokeAndWait(() -> { + if (popup.isVisible()) { + System.out.println("ZAV: Good!!! BUTTON3 is working. At last :)"); + popup.setVisible(false); + } else { + System.out.println("ZAV: Bad :( Very bad. Nothing is working..."); + passed = false; + } + }); + if (!passed) { + throw new RuntimeException("No popup trigger mouse events found"); + } + robot.waitForIdle(); + robot.delay(100); + + SwingUtilities.invokeAndWait(() -> { + loc = label2.getLocationOnScreen(); + loc.x = loc.x + (int) (label2.getWidth() / 2); + loc.y = loc.y + (int) (label2.getHeight() / 2); + }); + robot.mouseMove(loc.x, loc.y); + robot.mousePress(popupTrigger); + robot.mouseRelease(popupTrigger); + robot.waitForIdle(); + robot.delay(100); + + SwingUtilities.invokeAndWait(() -> { + if (!popup.isVisible()) { + passed = false; + } + }); + if (!passed) { + throw new RuntimeException("Regression: bug 4966109, popup is not visible"); + } + } finally { + if (mainFrame != null) { + mainFrame.dispose(); + } + } + System.out.println("test Passed!"); + } +} diff --git a/test/jdk/javax/swing/JPopupMenu/bug5091257.java b/test/jdk/javax/swing/JPopupMenu/bug5091257.java new file mode 100644 index 0000000000000..cd4d285f9cb71 --- /dev/null +++ b/test/jdk/javax/swing/JPopupMenu/bug5091257.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2004, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPopupMenu; +import javax.swing.SwingUtilities; +import javax.swing.event.PopupMenuListener; +import javax.swing.event.PopupMenuEvent; +import java.awt.BorderLayout; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseEvent; + +/* + * @test + * @bug 5091257 + * @summary Application key does not display a pop-up menu in users view. + * @key headful + * @run main bug5091257 + */ + +public class bug5091257 { + public static volatile boolean passed = false; + public static volatile boolean isKeyOk = false; + public static JFrame mainFrame; + public static JButton button; + public static Robot robot; + public static JPopupMenu popup; + public static volatile Point loc; + + public static void main(String[] args) throws Exception { + try { + robot = new Robot(); + robot.setAutoDelay(50); + SwingUtilities.invokeAndWait(() -> { + button = new JButton("Popup button"); + button.addKeyListener(new KeyListener() { + public void keyTyped(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_CONTEXT_MENU) { + isKeyOk = true; + } + } + + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_CONTEXT_MENU) { + isKeyOk = true; + } + } + + public void keyReleased(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_CONTEXT_MENU) { + isKeyOk = true; + } + } + }); + mainFrame = new JFrame("Bug5091257"); + mainFrame.setLayout(new BorderLayout()); + mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + mainFrame.add(button, BorderLayout.CENTER); + mainFrame.pack(); + mainFrame.setVisible(true); + }); + robot.waitForIdle(); + robot.delay(1000); + + SwingUtilities.invokeAndWait(() -> { + loc = button.getLocationOnScreen(); + loc.x = loc.x + (int) (button.getWidth() / 2); + loc.y = loc.y + (int) (button.getHeight() / 2); + }); + robot.mouseMove(loc.x, loc.y); + robot.mousePress(MouseEvent.BUTTON3_DOWN_MASK); + robot.mouseRelease(MouseEvent.BUTTON3_DOWN_MASK); + + robot.waitForIdle(); + robot.delay(100); + + try { + robot.keyPress(KeyEvent.VK_CONTEXT_MENU); + robot.keyRelease(KeyEvent.VK_CONTEXT_MENU); + } catch (IllegalArgumentException ex) { + isKeyOk = false; + } + + if (!isKeyOk) { + System.out.println("KeyEvent can't create or deliver " + + "VK_CONTEXT_MENU event to component. Testing skipped."); + passed = true; + } else { + SwingUtilities.invokeAndWait(() -> { + popup = new JPopupMenu(); + popup.add("Item to make popup not empty"); + popup.addPopupMenuListener(new PopupMenuListener() { + public void popupMenuWillBecomeVisible(PopupMenuEvent e) { + System.out.println("Popup menu became visible " + + "on context menu key press. Test passed."); + passed = true; + } + + public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { + } + + public void popupMenuCanceled(PopupMenuEvent e) { + } + }); + button.setComponentPopupMenu(popup); + }); + robot.keyPress(KeyEvent.VK_CONTEXT_MENU); + robot.keyRelease(KeyEvent.VK_CONTEXT_MENU); + + robot.waitForIdle(); + robot.delay(100); + + if (!passed) { + throw new RuntimeException("Popup did not open on " + + "VK_CONTEXT_MENU press. Test failed."); + } + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (mainFrame != null) { + mainFrame.dispose(); + } + }); + } + } +} diff --git a/test/jdk/javax/swing/JPopupMenu/bug7160604.java b/test/jdk/javax/swing/JPopupMenu/bug7160604.java new file mode 100644 index 0000000000000..591817b75c909 --- /dev/null +++ b/test/jdk/javax/swing/JPopupMenu/bug7160604.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 7160604 + * @summary Using non-opaque windows - popups are initially not painted correctly + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug7160604 +*/ +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.GraphicsEnvironment; +import java.awt.event.ActionEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import static java.awt.GraphicsDevice.WindowTranslucency.PERPIXEL_TRANSLUCENT; +import javax.swing.AbstractAction; +import javax.swing.BorderFactory; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JWindow; +import javax.swing.SwingUtilities; + +public class bug7160604 { + + private static final String INSTRUCTIONS = """ + Click on the top-bar and combo-box at the bottom more than once. + Check top-bar popup menu and combo-box drop-down list have a border + and their items are drawn properly. + If yes, Click Pass else click Fail."""; + + public static void main(String[] args) throws Exception { + if (!GraphicsEnvironment + .getLocalGraphicsEnvironment() + .getDefaultScreenDevice() + .isWindowTranslucencySupported(PERPIXEL_TRANSLUCENT)) { + // Tested translucency is not supported. Test passed + return; + } + PassFailJFrame.builder() + .title("PopupMenu Instructions") + .instructions(INSTRUCTIONS) + .rows(5) + .columns(35) + .testUI(bug7160604::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JWindow createTestUI() { + + final JWindow window = new JWindow(); + window.setLocation(200, 200); + window.setSize(300, 300); + + final JLabel label = new JLabel("...click to invoke JPopupMenu"); + label.setOpaque(true); + final JPanel contentPane = new JPanel(new BorderLayout()); + contentPane.setBorder(BorderFactory.createLineBorder(Color.RED)); + window.setContentPane(contentPane); + contentPane.add(label, BorderLayout.NORTH); + + final JComboBox comboBox = new JComboBox(new Object[]{"1", "2", "3", "4"}); + contentPane.add(comboBox, BorderLayout.SOUTH); + + final JPopupMenu jPopupMenu = new JPopupMenu(); + + jPopupMenu.add("string"); + jPopupMenu.add(new AbstractAction("action") { + @Override + public void actionPerformed(final ActionEvent e) { + } + }); + jPopupMenu.add(new JLabel("label")); + jPopupMenu.add(new JMenuItem("MenuItem")); + label.addMouseListener(new MouseAdapter() { + @Override + public void mouseReleased(final MouseEvent e) { + jPopupMenu.show(label, 0, 0); + } + }); + + window.setBackground(new Color(0, 0, 0, 0)); + + return window; + } +} diff --git a/test/jdk/javax/swing/JProgressBar/RightLeftOrientation.java b/test/jdk/javax/swing/JProgressBar/RightLeftOrientation.java new file mode 100644 index 0000000000000..5cc183b1e8e4d --- /dev/null +++ b/test/jdk/javax/swing/JProgressBar/RightLeftOrientation.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4230355 + * @summary + * This test checks if progress bars lay out correctly when their + * ComponentOrientation property is set to RIGHT_TO_LEFT. This test is + * manual. The tester is asked to compare left-to-right and + * right-to-left progress bars and judge whether they are mirror images + * of each other. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual RightLeftOrientation + */ + +import java.awt.ComponentOrientation; +import java.awt.Container; +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.LookAndFeel; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.UIManager; + +public class RightLeftOrientation { + + static final String INSTRUCTIONS = """ + This test checks progress bars for correct Right-To-Left Component Orientation. + The progress bars in the left column should fill up from the left while the bars in + the right column should fill up from the right. + If this is so, the test PASSES, otherwise it FAILS. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Progress Bar Orientation Test Instructions") + .instructions(INSTRUCTIONS) + .columns(60) + .testUI(RightLeftOrientation::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + JFrame frame = new JFrame("Progress Bar Orientation Test"); + Container contentPane = frame.getContentPane(); + contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.X_AXIS)); + contentPane.add(createBarSet(ComponentOrientation.LEFT_TO_RIGHT)); + contentPane.add(createBarSet(ComponentOrientation.RIGHT_TO_LEFT)); + frame.pack(); + return frame; + } + + static JPanel createBarSet(ComponentOrientation o) { + JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); + panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); + JLabel header; + if (o.isLeftToRight()) + header = new JLabel("Left To Right"); + else + header = new JLabel("Right To Left"); + panel.add(header); + + UIManager.LookAndFeelInfo[] lafs = UIManager.getInstalledLookAndFeels(); + for (int i = 0; i < lafs.length; i++) { + if (i > 0) + panel.add(Box.createVerticalStrut(10)); + panel.add(createProgressBars(lafs[i].getName(), + lafs[i].getClassName(), o)); + } + + return panel; + } + + static JPanel createProgressBars(String name, String plaf, + ComponentOrientation o) { + JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); + JLabel label = new JLabel(name); + panel.add(label); + try { + LookAndFeel save = UIManager.getLookAndFeel(); + UIManager.setLookAndFeel(plaf); + + panel.add(createProgressBar(true, 0, o)); + panel.add(Box.createVerticalStrut(5)); + + panel.add(createProgressBar(true, 5, o)); + panel.add(Box.createVerticalStrut(5)); + + panel.add(createProgressBar(true, 10, o)); + panel.add(Box.createVerticalStrut(5)); + + panel.add(createProgressBar(true, 20, o)); + panel.add(Box.createVerticalStrut(5)); + + UIManager.put("ProgressBar.cellSpacing", Integer.valueOf(2)); + UIManager.put("ProgressBar.cellLength", Integer.valueOf(7)); + + panel.add(createProgressBar(false, 5, o)); + panel.add(Box.createVerticalStrut(5)); + + panel.add(createProgressBar(false, 20, o)); + + UIManager.setLookAndFeel(save); + } catch (Exception e) { + System.err.println(e); + } + return panel; + } + + static JProgressBar createProgressBar(boolean paintStr, int value, + ComponentOrientation o) { + JProgressBar p = new JProgressBar(JProgressBar.HORIZONTAL, 0, 20); + p.setStringPainted(paintStr); + p.setValue(value); + p.setComponentOrientation(o); + return p; + } + +} diff --git a/test/jdk/javax/swing/JProgressBar/bug4230391.java b/test/jdk/javax/swing/JProgressBar/bug4230391.java new file mode 100644 index 0000000000000..473862d83a338 --- /dev/null +++ b/test/jdk/javax/swing/JProgressBar/bug4230391.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 4230391 + * @summary Tests that JProgressBar draws correctly when Insets are not zero + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4230391 +*/ + +import java.awt.ComponentOrientation; +import java.awt.Container; +import java.awt.Insets; +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.LookAndFeel; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.UIManager; + +public class bug4230391 { + + static final String INSTRUCTIONS = """ + Tests that progress bars honor insets in different L&Fs. + Different L&Fs render the progress bar differently, and may or may + not have a different colored background around the progress bar, + and may or may not draw a border around the bar+background. + The progress bars should be of equal width and the progress + rendering line/bar should not extend past/overlap any border. + If it is as described, the test PASSES. + """; + + static class InsetProgressBar extends JProgressBar { + private Insets insets = new Insets(12, 12, 12, 12); + + public InsetProgressBar(boolean horiz, int low, int hi) { + super((horiz)?JProgressBar.HORIZONTAL:JProgressBar.VERTICAL, low, hi); + } + + public Insets getInsets() { + return insets; + } + } + + static JPanel createBarSet() { + JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); + panel.setBorder(BorderFactory.createEmptyBorder(20,20,20,20)); + UIManager.LookAndFeelInfo[] lafs = UIManager.getInstalledLookAndFeels(); + for (int i = 0; i < lafs.length; i++) { + if (i > 0) { + panel.add(Box.createVerticalStrut(10)); + } + panel.add(createProgressBars(lafs[i].getName(), lafs[i].getClassName())); + } + return panel; + } + + static JPanel createProgressBars(String name, String plaf) { + JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); + try { + LookAndFeel save = UIManager.getLookAndFeel(); + UIManager.setLookAndFeel(plaf); + + ComponentOrientation ltr = ComponentOrientation.LEFT_TO_RIGHT; + + Box b = Box.createVerticalBox(); + panel.add(b); + panel.add(Box.createHorizontalStrut(5)); + panel.add(createProgressBar(false, true, ltr)); + UIManager.setLookAndFeel(save); + } catch (Exception e) { + System.err.println(e); + } + return panel; + } + + static JProgressBar createProgressBar(boolean solid, boolean horiz, + ComponentOrientation o) { + if (solid) { + UIManager.put("ProgressBar.cellSpacing", Integer.valueOf(0)); + UIManager.put("ProgressBar.cellLength", Integer.valueOf(1)); + } else { + UIManager.put("ProgressBar.cellSpacing", Integer.valueOf(2)); + UIManager.put("ProgressBar.cellLength", Integer.valueOf(7)); + } + + JProgressBar p = new InsetProgressBar(horiz, 0, 20); + p.setStringPainted(solid); + p.setValue(20); + p.setComponentOrientation(o); + + return p; + } + + static JFrame createUI() { + JFrame frame = new JFrame("Progress Bar Insets Test"); + Container contentPane = frame.getContentPane(); + contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.X_AXIS)); + contentPane.add(createBarSet()); + frame.setSize(400, 300); + return frame; + } + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Progress Bar Insets Test Instructions") + .instructions(INSTRUCTIONS) + .columns(60) + .testUI(bug4230391::createUI) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/javax/swing/JProgressBar/bug4393042.java b/test/jdk/javax/swing/JProgressBar/bug4393042.java new file mode 100644 index 0000000000000..f17d70a1309d5 --- /dev/null +++ b/test/jdk/javax/swing/JProgressBar/bug4393042.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 4393042 + * @summary JProgressBar should update painting when maximum value is very large + * @key headful + */ + +import java.awt.Graphics; +import javax.swing.JFrame; +import javax.swing.JProgressBar; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; + +public class bug4393042 extends JProgressBar { + + static final int MAXIMUM = Integer.MAX_VALUE - 100; + static volatile int value = 0; + static volatile bug4393042 progressBar; + static JFrame frame; + static volatile int paintCount = 0; + + public void paintComponent(Graphics g) { + super.paintComponent(g); + System.out.println("paint count=" + (++paintCount)); + } + + public bug4393042(int min, int max) { + super(min, max); + } + + public static void main(String[] args) throws Exception { + + UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); + + try { + SwingUtilities.invokeAndWait(bug4393042::createUI); + + value = 0; + for (int i = 0; i <= 10; i++) { + Thread.sleep(1000); + SwingUtilities.invokeAndWait(() -> { + progressBar.setValue(value); + }); + value += MAXIMUM / 10; + } + + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + if (paintCount < 10 || paintCount > 100) { + throw new RuntimeException("Unexpected paint count : " + paintCount); + } + } + + static void createUI() { + frame = new JFrame("bug4393042"); + progressBar = new bug4393042(0, MAXIMUM); + progressBar.setStringPainted(true); + progressBar.setValue(0); + frame.add(progressBar); + frame.setSize(400, 200); + frame.setVisible(true); + } +} diff --git a/test/jdk/javax/swing/JProgressBar/bug5003022.java b/test/jdk/javax/swing/JProgressBar/bug5003022.java new file mode 100644 index 0000000000000..57f5fdfc45da0 --- /dev/null +++ b/test/jdk/javax/swing/JProgressBar/bug5003022.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 5003022 + * @summary Test that setting zero value on JProgressBar works in GTK L&F + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug5003022 +*/ + +import java.awt.FlowLayout; +import javax.swing.JFrame; +import javax.swing.JProgressBar; +import javax.swing.UIManager; + +public class bug5003022 { + + static final String INSTRUCTIONS = """ + There are two progress bars, they should display progress strings. + The first progress bar should display 0% and the bar should show no progress color fill. + The second progress bar should display 30% and the bar should show 30% progress color fill. + If it is as described, the test PASSES, otherwise it FAILS. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug5003022 Test Instructions") + .instructions(INSTRUCTIONS) + .columns(60) + .testUI(bug5003022::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + try { + /* This will only succeed on Linux, but the test is valid for other platforms and L&Fs */ + UIManager.setLookAndFeel("com.sun.java.swing.plaf.gtk.GTKLookAndFeel"); + } catch (Exception e) { + e.printStackTrace(); + } + + JFrame frame = new JFrame("bug5003022"); + JProgressBar pb1 = new JProgressBar(); + pb1.setValue(0); + pb1.setStringPainted(true); + + JProgressBar pb2 = new JProgressBar(); + pb2.setValue(30); + pb2.setStringPainted(true); + + frame.setLayout(new FlowLayout()); + frame.add(pb1); + frame.add(pb2); + + frame.setSize(300, 300); + return frame; + } + +} diff --git a/test/jdk/javax/swing/JRadioButton/8033699/bug8033699.java b/test/jdk/javax/swing/JRadioButton/8033699/bug8033699.java index 06622f718197d..e8cabb7e18109 100644 --- a/test/jdk/javax/swing/JRadioButton/8033699/bug8033699.java +++ b/test/jdk/javax/swing/JRadioButton/8033699/bug8033699.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,34 +21,30 @@ * questions. */ - /* +/* * @test * @key headful - * @library ../../regtesthelpers - * @build Util * @bug 8033699 8154043 8167160 8208640 8226892 * @summary Incorrect radio button behavior when pressing tab key * @run main bug8033699 */ + import java.awt.KeyboardFocusManager; import java.awt.Robot; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; -import java.util.logging.Level; -import java.util.logging.Logger; + import javax.swing.BorderFactory; -import javax.swing.BoxLayout; +import javax.swing.Box; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JFrame; -import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class bug8033699 { - private static JFrame mainFrame; private static Robot robot; private static JButton btnStart; @@ -58,69 +54,101 @@ public class bug8033699 { private static JRadioButton radioBtn2; private static JRadioButton radioBtn3; private static JRadioButton radioBtnSingle; + private static KeyboardFocusManager focusManager; - public static void main(String args[]) throws Throwable { - SwingUtilities.invokeAndWait(() -> { - changeLAF(); - createAndShowGUI(); - }); - + public static void main(String[] args) throws Throwable { robot = new Robot(); - Thread.sleep(100); - - robot.setAutoDelay(100); - - // tab key test grouped radio button - runTest1(); - - // tab key test non-grouped radio button - runTest2(); - - // shift tab key test grouped and non grouped radio button - runTest3(); - - // left/up key test in grouped radio button - runTest4(); - // down/right key test in grouped radio button - runTest5(); + SwingUtilities.invokeAndWait(() -> + focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager()); - // tab from radio button in group to next component in the middle of button group layout - runTest6(); - - // tab to radio button in group from component in the middle of button group layout - runTest7(); - - // down key circle back to first button in grouped radio button - runTest8(); - - // Verify that ActionListener is called when a RadioButton is selected using arrow key. - runTest9(); + UIManager.LookAndFeelInfo[] lafs = UIManager.getInstalledLookAndFeels(); + for (UIManager.LookAndFeelInfo laf : lafs) { + testLaF(laf); + } + } - SwingUtilities.invokeAndWait(() -> mainFrame.dispose()); + private static void testLaF(UIManager.LookAndFeelInfo laf) throws Exception { + try { + System.out.println("Testing LaF: " + laf.getName()); + SwingUtilities.invokeAndWait(() -> { + setLookAndFeel(laf); + createAndShowGUI(); + }); + + robot.waitForIdle(); + robot.delay(1000); + + // tab key test grouped radio button + runTest1(); + robot.delay(100); + + // tab key test non-grouped radio button + runTest2(); + robot.delay(100); + + // shift tab key test grouped and non-grouped radio button + runTest3(); + robot.delay(100); + + // left/up key test in grouped radio button + runTest4(); + robot.delay(100); + + // down/right key test in grouped radio button + runTest5(); + robot.delay(100); + + // tab from radio button in group to next component in the middle of + // button group layout + runTest6(); + robot.delay(100); + + // tab to radio button in group from component in the middle of + // button group layout + runTest7(); + robot.delay(100); + + // down key circle back to first button in grouped radio button + runTest8(); + robot.delay(100); + + // Verify that ActionListener is called when a RadioButton is + // selected using arrow key + runTest9(); + robot.delay(100); + } catch (Exception e) { + Throwable cause = e.getCause(); + throw new RuntimeException("Error testing LaF: " + laf.getName() + + (cause != null ? " - " + cause.getMessage() : ""), + e); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (mainFrame != null) { + mainFrame.dispose(); + mainFrame = null; + } + }); + } } - private static void changeLAF() { - String currentLAF = UIManager.getLookAndFeel().toString(); - System.out.println(currentLAF); - currentLAF = currentLAF.toLowerCase(); - if (currentLAF.contains("nimbus")) { - try { - UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); - } catch (Exception ex) { - ex.printStackTrace(); - } + private static void setLookAndFeel(UIManager.LookAndFeelInfo laf) { + try { + UIManager.setLookAndFeel(laf.getClassName()); + } catch (ClassNotFoundException | InstantiationException | + IllegalAccessException | UnsupportedLookAndFeelException e) { + System.err.println("Error setting LaF: " + laf.getName()); + throw new RuntimeException("Failed to set look and feel", e); } } private static void createAndShowGUI() { - mainFrame = new JFrame("Bug 8033699 - 8 Tests for Grouped/Non Group Radio Buttons"); + mainFrame = new JFrame("Radio Button Focus Tests"); btnStart = new JButton("Start"); btnEnd = new JButton("End"); btnMiddle = new JButton("Middle"); - JPanel box = new JPanel(); - box.setLayout(new BoxLayout(box, BoxLayout.Y_AXIS)); + Box box = Box.createVerticalBox(); box.setBorder(BorderFactory.createTitledBorder("Grouped Radio Buttons")); radioBtn1 = new JRadioButton("A"); radioBtn2 = new JRadioButton("B"); @@ -140,120 +168,140 @@ private static void createAndShowGUI() { radioBtnSingle = new JRadioButton("Not Grouped"); radioBtnSingle.setSelected(true); - mainFrame.getContentPane().add(btnStart); - mainFrame.getContentPane().add(box); - mainFrame.getContentPane().add(radioBtnSingle); - mainFrame.getContentPane().add(btnEnd); + Box mainBox = Box.createVerticalBox(); + mainBox.add(btnStart); + mainBox.add(box); + mainBox.add(radioBtnSingle); + mainBox.add(btnEnd); + mainFrame.add(mainBox); mainFrame.getRootPane().setDefaultButton(btnStart); btnStart.requestFocus(); mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - mainFrame.setLayout(new BoxLayout(mainFrame.getContentPane(), BoxLayout.Y_AXIS)); mainFrame.setSize(300, 300); - mainFrame.setLocation(200, 200); + mainFrame.setLocationRelativeTo(null); mainFrame.setVisible(true); mainFrame.toFront(); } // Radio button Group as a single component when traversing through tab key private static void runTest1() throws Exception { - hitKey(robot, KeyEvent.VK_TAB); - hitKey(robot, KeyEvent.VK_TAB); - hitKey(robot, KeyEvent.VK_TAB); + hitKey(KeyEvent.VK_TAB); + hitKey(KeyEvent.VK_TAB); + hitKey(KeyEvent.VK_TAB); SwingUtilities.invokeAndWait(() -> { - if (KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() != radioBtnSingle) { - System.out.println("Radio Button Group Go To Next Component through Tab Key failed"); - throw new RuntimeException("Focus is not on Radio Button Single as Expected"); + if (focusManager.getFocusOwner() != radioBtnSingle) { + System.out.println("Radio Button Group Go To " + + "Next Component through Tab Key failed"); + throw new RuntimeException("Focus is not on " + + "Radio Button Single as Expected"); } }); } - // Non-Grouped Radio button as a single component when traversing through tab key + // Non-Grouped Radio button as a single component when traversing through + // tab key private static void runTest2() throws Exception { - hitKey(robot, KeyEvent.VK_TAB); + hitKey(KeyEvent.VK_TAB); SwingUtilities.invokeAndWait(() -> { - if (KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() != btnEnd) { - System.out.println("Non Grouped Radio Button Go To Next Component through Tab Key failed"); - throw new RuntimeException("Focus is not on Button End as Expected"); + if (focusManager.getFocusOwner() != btnEnd) { + System.out.println("Non Grouped Radio Button Go To " + + "Next Component through Tab Key failed"); + throw new RuntimeException("Focus is not on Button End " + + "as Expected"); } }); } - // Non-Grouped Radio button and Group Radio button as a single component when traversing through shift-tab key + // Non-Grouped Radio button and Group Radio button as a single component + // when traversing through shift-tab key private static void runTest3() throws Exception { - hitKey(robot, KeyEvent.VK_SHIFT, KeyEvent.VK_TAB); - hitKey(robot, KeyEvent.VK_SHIFT, KeyEvent.VK_TAB); - hitKey(robot, KeyEvent.VK_SHIFT, KeyEvent.VK_TAB); + hitKey(KeyEvent.VK_SHIFT, KeyEvent.VK_TAB); + hitKey(KeyEvent.VK_SHIFT, KeyEvent.VK_TAB); + hitKey(KeyEvent.VK_SHIFT, KeyEvent.VK_TAB); SwingUtilities.invokeAndWait(() -> { - if (KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() != radioBtn1) { - System.out.println("Radio button Group/Non Grouped Radio Button SHIFT-Tab Key Test failed"); - throw new RuntimeException("Focus is not on Radio Button A as Expected"); + if (focusManager.getFocusOwner() != radioBtn1) { + System.out.println("Radio button Group/Non Grouped " + + "Radio Button SHIFT-Tab Key Test failed"); + throw new RuntimeException("Focus is not on Radio Button A " + + "as Expected"); } }); } // Using arrow key to move focus in radio button group private static void runTest4() throws Exception { - hitKey(robot, KeyEvent.VK_DOWN); - hitKey(robot, KeyEvent.VK_RIGHT); + hitKey(KeyEvent.VK_DOWN); + hitKey(KeyEvent.VK_RIGHT); SwingUtilities.invokeAndWait(() -> { - if (KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() != radioBtn3) { - System.out.println("Radio button Group UP/LEFT Arrow Key Move Focus Failed"); - throw new RuntimeException("Focus is not on Radio Button C as Expected"); + if (focusManager.getFocusOwner() != radioBtn3) { + System.out.println("Radio button Group UP/LEFT Arrow Key " + + "Move Focus Failed"); + throw new RuntimeException("Focus is not on Radio Button C " + + "as Expected"); } }); } private static void runTest5() throws Exception { - hitKey(robot, KeyEvent.VK_UP); - hitKey(robot, KeyEvent.VK_LEFT); + hitKey(KeyEvent.VK_UP); + hitKey(KeyEvent.VK_LEFT); SwingUtilities.invokeAndWait(() -> { - if (KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() != radioBtn1) { - System.out.println("Radio button Group Left/Up Arrow Key Move Focus Failed"); - throw new RuntimeException("Focus is not on Radio Button A as Expected"); + if (focusManager.getFocusOwner() != radioBtn1) { + System.out.println("Radio button Group Left/Up Arrow Key " + + "Move Focus Failed"); + throw new RuntimeException("Focus is not on Radio Button A " + + "as Expected"); } }); } private static void runTest6() throws Exception { - hitKey(robot, KeyEvent.VK_UP); - hitKey(robot, KeyEvent.VK_UP); + hitKey(KeyEvent.VK_UP); + hitKey(KeyEvent.VK_UP); SwingUtilities.invokeAndWait(() -> { - if (KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() != radioBtn2) { - System.out.println("Radio button Group Circle Back To First Button Test"); - throw new RuntimeException("Focus is not on Radio Button B as Expected"); + if (focusManager.getFocusOwner() != radioBtn2) { + System.out.println("Radio button Group Circle Back To " + + "First Button Test"); + throw new RuntimeException("Focus is not on Radio Button B " + + "as Expected"); } }); } private static void runTest7() throws Exception { - hitKey(robot, KeyEvent.VK_TAB); + hitKey(KeyEvent.VK_TAB); SwingUtilities.invokeAndWait(() -> { - if (KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() != btnMiddle) { - System.out.println("Separate Component added in button group layout"); - throw new RuntimeException("Focus is not on Middle Button as Expected"); + if (focusManager.getFocusOwner() != btnMiddle) { + System.out.println("Separate Component added in" + + " button group layout"); + throw new RuntimeException("Focus is not on Middle Button" + + " as Expected"); } }); } private static void runTest8() throws Exception { - hitKey(robot, KeyEvent.VK_TAB); + hitKey(KeyEvent.VK_TAB); SwingUtilities.invokeAndWait(() -> { - if (KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() != radioBtnSingle) { - System.out.println("Separate Component added in button group layout"); - throw new RuntimeException("Focus is not on Radio Button Single as Expected"); + if (focusManager.getFocusOwner() != radioBtnSingle) { + System.out.println("Separate Component added in" + + " button group layout"); + throw new RuntimeException("Focus is not on Radio Button Single" + + " as Expected"); } }); } - private static Boolean actRB1 = false; - private static Boolean actRB2 = false; - private static Boolean actRB3 = false; + private static volatile boolean actRB1 = false; + private static volatile boolean actRB2 = false; + private static volatile boolean actRB3 = false; - // JDK-8226892: Verify that ActionListener is called when a RadioButton is selected using arrow key. + // JDK-8226892: Verify that ActionListener is called when a RadioButton + // is selected using arrow key private static void runTest9() throws Exception { SwingUtilities.invokeAndWait(() -> { radioBtn1.setSelected(true); @@ -264,15 +312,19 @@ private static void runTest9() throws Exception { ActionListener actLrRB2 = e -> actRB2 = true; ActionListener actLrRB3 = e -> actRB3 = true; - radioBtn1.addActionListener(actLrRB1); - radioBtn2.addActionListener(actLrRB2); - radioBtn3.addActionListener(actLrRB3); + // Adding Action Listeners + SwingUtilities.invokeAndWait(() -> { + radioBtn1.addActionListener(actLrRB1); + radioBtn2.addActionListener(actLrRB2); + radioBtn3.addActionListener(actLrRB3); + }); - hitKey(robot, KeyEvent.VK_DOWN); - hitKey(robot, KeyEvent.VK_DOWN); - hitKey(robot, KeyEvent.VK_DOWN); + hitKey(KeyEvent.VK_DOWN); + hitKey(KeyEvent.VK_DOWN); + hitKey(KeyEvent.VK_DOWN); - String failMessage = "ActionListener not invoked when selected using arrow key."; + String failMessage = "ActionListener not invoked when selected using " + + "arrow key."; if (!actRB2) { throw new RuntimeException("RadioButton 2: " + failMessage); } @@ -283,18 +335,21 @@ private static void runTest9() throws Exception { throw new RuntimeException("RadioButton 1: " + failMessage); } - radioBtn1.removeActionListener(actLrRB1); - radioBtn2.removeActionListener(actLrRB2); - radioBtn3.removeActionListener(actLrRB3); + // Removing Action Listeners + SwingUtilities.invokeAndWait(() -> { + radioBtn1.removeActionListener(actLrRB1); + radioBtn2.removeActionListener(actLrRB2); + radioBtn3.removeActionListener(actLrRB3); + }); } - private static void hitKey(Robot robot, int keycode) { + private static void hitKey(int keycode) { robot.keyPress(keycode); robot.keyRelease(keycode); robot.waitForIdle(); } - private static void hitKey(Robot robot, int mode, int keycode) { + private static void hitKey(int mode, int keycode) { robot.keyPress(mode); robot.keyPress(keycode); robot.keyRelease(keycode); diff --git a/test/jdk/javax/swing/JRadioButton/8075609/bug8075609.java b/test/jdk/javax/swing/JRadioButton/8075609/bug8075609.java index 31c99206b7a9b..4c29a33ffa004 100644 --- a/test/jdk/javax/swing/JRadioButton/8075609/bug8075609.java +++ b/test/jdk/javax/swing/JRadioButton/8075609/bug8075609.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,44 +21,49 @@ * questions. */ - /* +/* * @test * @key headful - * @library ../../regtesthelpers - * @build Util * @bug 8075609 * @summary IllegalArgumentException when transferring focus from JRadioButton using tab - * @author Vivi An * @run main bug8075609 */ - -import javax.swing.*; -import javax.swing.event.*; -import java.awt.event.*; -import java.awt.*; +import java.awt.BorderLayout; +import java.awt.Robot; +import java.awt.event.KeyEvent; + +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JTextField; +import javax.swing.LayoutFocusTraversalPolicy; +import javax.swing.SwingUtilities; public class bug8075609 { private static Robot robot; private static JTextField textField; private static JFrame mainFrame; - public static void main(String args[]) throws Throwable { + public static void main(String[] args) throws Throwable { try { - SwingUtilities.invokeAndWait(new Runnable() { - public void run() { - createAndShowGUI(); - } - }); + SwingUtilities.invokeAndWait(bug8075609::createAndShowGUI); robot = new Robot(); Thread.sleep(100); + robot.waitForIdle(); robot.setAutoDelay(100); // Radio button group tab key test runTest1(); } finally { - if (mainFrame != null) SwingUtilities.invokeAndWait(() -> mainFrame.dispose()); + SwingUtilities.invokeAndWait(() -> { + if (mainFrame != null) { + mainFrame.dispose(); + } + }); } } @@ -91,26 +96,25 @@ private static void createAndShowGUI() { mainFrame.add(rootPanel); mainFrame.pack(); + mainFrame.setLocationRelativeTo(null); mainFrame.setVisible(true); mainFrame.toFront(); } // Radio button Group as a single component when traversing through tab key - private static void runTest1() throws Exception{ - hitKey(robot, KeyEvent.VK_TAB); - - robot.delay(1000 ); - SwingUtilities.invokeAndWait(new Runnable() { - public void run() { - if (!textField.hasFocus()) { - System.out.println("Radio Button Group Go To Next Component through Tab Key failed"); - throw new RuntimeException("Focus is not on textField as Expected"); - } + private static void runTest1() throws Exception { + hitKey(KeyEvent.VK_TAB); + + robot.delay(1000); + SwingUtilities.invokeAndWait(() -> { + if (!textField.hasFocus()) { + System.out.println("Radio Button Group Go To Next Component through Tab Key failed"); + throw new RuntimeException("Focus is not on textField as Expected"); } }); } - private static void hitKey(Robot robot, int keycode) { + private static void hitKey(int keycode) { robot.keyPress(keycode); robot.keyRelease(keycode); robot.waitForIdle(); diff --git a/test/jdk/javax/swing/JRadioButton/ButtonGroupFocus/ButtonGroupFocusTest.java b/test/jdk/javax/swing/JRadioButton/ButtonGroupFocus/ButtonGroupFocusTest.java index c696b166dd085..633b0356f85df 100644 --- a/test/jdk/javax/swing/JRadioButton/ButtonGroupFocus/ButtonGroupFocusTest.java +++ b/test/jdk/javax/swing/JRadioButton/ButtonGroupFocus/ButtonGroupFocusTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,26 +28,53 @@ * @run main ButtonGroupFocusTest */ -import javax.swing.*; -import java.awt.*; +import java.awt.AWTEvent; +import java.awt.Container; +import java.awt.FlowLayout; +import java.awt.KeyboardFocusManager; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.Toolkit; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; import java.awt.event.KeyEvent; +import java.awt.image.BufferedImage; +import java.io.File; +import java.util.concurrent.CountDownLatch; -public class ButtonGroupFocusTest { +import javax.imageio.ImageIO; +import javax.swing.ButtonGroup; +import javax.swing.JFrame; +import javax.swing.JRadioButton; +import javax.swing.SwingUtilities; + +import static java.awt.KeyboardFocusManager.getCurrentKeyboardFocusManager; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; + +public final class ButtonGroupFocusTest { private static JRadioButton button1; private static JRadioButton button2; private static JRadioButton button3; private static JRadioButton button4; private static JRadioButton button5; - private static Robot robot; + + private static final CountDownLatch button2FocusLatch = new CountDownLatch(1); + private static final CountDownLatch button3FocusLatch = new CountDownLatch(1); + private static final CountDownLatch button4FocusLatch = new CountDownLatch(1); + + private static final CountDownLatch button2FocusLatch2 = new CountDownLatch(2); + + private static final long FOCUS_TIMEOUT = 4; + private static JFrame frame; public static void main(String[] args) throws Exception { - robot = new Robot(); - robot.setAutoDelay(100); + final Robot robot = new Robot(); SwingUtilities.invokeAndWait(() -> { - frame = new JFrame(); + frame = new JFrame("ButtonGroupFocusTest"); Container contentPane = frame.getContentPane(); contentPane.setLayout(new FlowLayout()); button1 = new JRadioButton("Button 1"); @@ -60,6 +87,7 @@ public static void main(String[] args) throws Exception { contentPane.add(button4); button5 = new JRadioButton("Button 5"); contentPane.add(button5); + ButtonGroup group = new ButtonGroup(); group.add(button1); group.add(button2); @@ -69,52 +97,96 @@ public static void main(String[] args) throws Exception { group.add(button4); group.add(button5); + button2.addFocusListener(new LatchFocusListener(button2FocusLatch)); + button3.addFocusListener(new LatchFocusListener(button3FocusLatch)); + button4.addFocusListener(new LatchFocusListener(button4FocusLatch)); + + button2.addFocusListener(new LatchFocusListener(button2FocusLatch2)); + button2.setSelected(true); + // Debugging aid: log focus owner changes... + KeyboardFocusManager focusManager = getCurrentKeyboardFocusManager(); + focusManager.addPropertyChangeListener("focusOwner", + e -> System.out.println(e.getPropertyName() + + "\n\t" + e.getOldValue() + + "\n\t" + e.getNewValue())); + + // ...and dispatched key events + Toolkit.getDefaultToolkit().addAWTEventListener( + e -> System.out.println("Dispatched " + e), + AWTEvent.KEY_EVENT_MASK); + frame.pack(); + frame.setLocationRelativeTo(null); frame.setVisible(true); }); - robot.waitForIdle(); - robot.delay(200); - - SwingUtilities.invokeAndWait(() -> { - if( !button2.hasFocus() ) { - frame.dispose(); - throw new RuntimeException( - "Button 2 should get focus after activation"); + try { + if (!button2FocusLatch.await(FOCUS_TIMEOUT, SECONDS)) { + throw new RuntimeException("Button 2 should get focus " + + "after activation"); } - }); + robot.waitForIdle(); + robot.delay(200); - robot.keyPress(KeyEvent.VK_TAB); - robot.keyRelease(KeyEvent.VK_TAB); + System.out.println("\n\n*** Tab 1st"); + robot.keyPress(KeyEvent.VK_TAB); + robot.keyRelease(KeyEvent.VK_TAB); - robot.waitForIdle(); - robot.delay(200); + if (!button4FocusLatch.await(FOCUS_TIMEOUT, SECONDS)) { + throw new RuntimeException("Button 4 should get focus"); + } + robot.waitForIdle(); + robot.delay(200); - SwingUtilities.invokeAndWait(() -> { - if( !button4.hasFocus() ) { - frame.dispose(); - throw new RuntimeException( - "Button 4 should get focus"); + if (button2FocusLatch2.await(1, MILLISECONDS)) { + throw new RuntimeException("Focus moved back to Button 2"); } - button3.setSelected(true); - }); - robot.keyPress(KeyEvent.VK_TAB); - robot.keyRelease(KeyEvent.VK_TAB); + SwingUtilities.invokeAndWait(() -> button3.setSelected(true)); + robot.waitForIdle(); + robot.delay(200); - robot.waitForIdle(); - robot.delay(200); + System.out.println("\n\n*** Tab 2nd"); + robot.keyPress(KeyEvent.VK_TAB); + robot.keyRelease(KeyEvent.VK_TAB); - SwingUtilities.invokeAndWait(() -> { - if( !button3.hasFocus() ) { - frame.dispose(); - throw new RuntimeException( - "selected Button 3 should get focus"); + if (!button3FocusLatch.await(FOCUS_TIMEOUT, SECONDS)) { + throw new RuntimeException("Selected Button 3 should get focus"); } - }); + } catch (Exception e) { + BufferedImage image = robot.createScreenCapture(getFrameBounds()); + ImageIO.write(image, "png", + new File("image.png")); + + SwingUtilities.invokeAndWait(() -> + System.err.println("Current focus owner: " + + getCurrentKeyboardFocusManager() + .getFocusOwner())); + + throw e; + } finally { + SwingUtilities.invokeAndWait(frame::dispose); + } + } + + private static Rectangle getFrameBounds() throws Exception { + Rectangle[] bounds = new Rectangle[1]; + SwingUtilities.invokeAndWait(() -> bounds[0] = frame.getBounds()); + return bounds[0]; + } + + private static final class LatchFocusListener extends FocusAdapter { + private final CountDownLatch focusGainedLatch; + + private LatchFocusListener(CountDownLatch focusGainedLatch) { + this.focusGainedLatch = focusGainedLatch; + } - SwingUtilities.invokeLater(frame::dispose); + @Override + public void focusGained(FocusEvent e) { + focusGainedLatch.countDown(); + } } } diff --git a/test/jdk/javax/swing/JRadioButton/bug4673850.java b/test/jdk/javax/swing/JRadioButton/bug4673850.java new file mode 100644 index 0000000000000..4f9058e978eeb --- /dev/null +++ b/test/jdk/javax/swing/JRadioButton/bug4673850.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4673850 + * @summary Tests that JRadioButton and JCheckBox checkmarks are painted entirely + * inside circular/rectangle checkboxes for Motif LaF. + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame jtreg.SkippedException + * @run main/manual bug4673850 + */ + +import java.awt.FlowLayout; +import javax.swing.JCheckBox; +import javax.swing.JFrame; +import javax.swing.JRadioButton; +import javax.swing.SwingConstants; +import javax.swing.UIManager; +import jtreg.SkippedException; + +public class bug4673850 { + private static final String INSTRUCTIONS = """ + + + + + +

    This test is for Motif LaF.

    + +

    + When the test starts, you'll see 2 radio buttons and 2 check boxes + with the checkmarks painted.

    + +

    + Ensure that all the button's checkmarks are painted entirely + within the circular/rectangle checkbox, NOT over them or outside them. +

    + + """; + + public static void main(String[] args) throws Exception { + try { + UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel"); + } catch (Exception e) { + throw new SkippedException("Unsupported LaF", e); + } + + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .rows(10) + .columns(45) + .testUI(createAndShowUI()) + .build() + .awaitAndCheck(); + } + + private static JFrame createAndShowUI() { + JFrame frame = new JFrame("bug4673850"); + frame.setLayout(new FlowLayout()); + + JRadioButton rb = new JRadioButton("RadioButt"); + rb.setSelected(true); + frame.add(rb); + JRadioButton rb2 = new JRadioButton("RadioButt"); + rb2.setHorizontalTextPosition(SwingConstants.LEFT); + rb2.setSelected(true); + frame.add(rb2); + + JCheckBox cb = new JCheckBox("CheckBox"); + cb.setSelected(true); + frame.add(cb); + JCheckBox cb2 = new JCheckBox("CheckBox"); + cb2.setHorizontalTextPosition(SwingConstants.LEFT); + cb2.setSelected(true); + frame.add(cb2); + frame.setSize(200, 150); + return frame; + } +} \ No newline at end of file diff --git a/test/jdk/javax/swing/JRootPane/4670486/bug4670486.java b/test/jdk/javax/swing/JRootPane/4670486/bug4670486.java index 6e1316a1edb84..2f53fa48773aa 100644 --- a/test/jdk/javax/swing/JRootPane/4670486/bug4670486.java +++ b/test/jdk/javax/swing/JRootPane/4670486/bug4670486.java @@ -104,7 +104,7 @@ public static void checkAction() { public static void main(String[] args) throws Throwable { try { Robot robot = new Robot(); - robot.setAutoDelay(250); + robot.setAutoDelay(100); UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); @@ -116,12 +116,14 @@ public void run() { frame.setContentPane(createPanel(frame)); frame.setJMenuBar(createMenuBar()); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setLocationRelativeTo(null); frame.pack(); frame.setVisible(true); } }); robot.waitForIdle(); + robot.delay(1000); // Change the default button to // force a call to BasicRootPaneUI.updateDefaultButtonBindings() diff --git a/test/jdk/javax/swing/JRootPane/bug4207333.java b/test/jdk/javax/swing/JRootPane/bug4207333.java new file mode 100644 index 0000000000000..b93438eae50b9 --- /dev/null +++ b/test/jdk/javax/swing/JRootPane/bug4207333.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.JButton; +import javax.swing.JRootPane; +import java.lang.reflect.Field; + +/* + * @test + * @bug 4207333 + * @summary Inadvertant API regression in JRootPane + * @run main bug4207333 + */ + +public class bug4207333 { + public static void main(String[] argv) { + TestableRootPane rp = new TestableRootPane(); + rp.setDefaultButton(new JButton("Default, eh?")); + + if (!rp.test("defaultPressAction")) { + throw new RuntimeException("Failed test for bug 4207333"); + } + if (!rp.test("defaultReleaseAction")) { + throw new RuntimeException("Failed test for bug 4207333"); + } + System.out.println("Test Passed!"); + } + + private static class TestableRootPane extends JRootPane { + public boolean test(String fieldName) { + boolean result = false; + try { + Class superClass = getClass().getSuperclass(); + Field field = superClass.getDeclaredField(fieldName); + Class fieldClass = field.getType(); + Class actionClass = Class.forName("javax.swing.Action"); + + // Is the Field an Action? + result = actionClass.isAssignableFrom(fieldClass); + } catch (NoSuchFieldException pe) { + // Not a bug if the fields are removed since their + // type was a package private class! + result = true; + } catch (Exception iae) { + System.out.println("Exception " + iae); + } + return result; + } + } +} diff --git a/test/jdk/javax/swing/JRootPane/bug4224113.java b/test/jdk/javax/swing/JRootPane/bug4224113.java new file mode 100644 index 0000000000000..e52b369a88687 --- /dev/null +++ b/test/jdk/javax/swing/JRootPane/bug4224113.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.plaf.RootPaneUI; + +/* + * @test + * @bug 4224113 + * @summary Tests the presence of RootPaneUI + * @run main bug4224113 + */ + +public class bug4224113 { + public static class TestRootPaneUI extends RootPaneUI { + } + + public static void main(String[] args) { + TestRootPaneUI r = new TestRootPaneUI(); + System.out.println("Test Passed!"); + } +} diff --git a/test/jdk/javax/swing/JRootPane/bug4614623.java b/test/jdk/javax/swing/JRootPane/bug4614623.java new file mode 100644 index 0000000000000..9a714d3cdfdee --- /dev/null +++ b/test/jdk/javax/swing/JRootPane/bug4614623.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4614623 + * @requires (os.family == "windows") + * @summary Tests that w2k mnemonic underlining works when there's no + focus owner + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4614623 + */ + +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.UIManager; + +public class bug4614623 { + private static final String INSTRUCTIONS = """ + This test verifies if the short-cut character + (menu mnemonic) is underlined when the ALT key is held down. + + Check if the following is true. + 1) Press Alt key. The letter 'F' (menu mnemonic) of + the "File" menu should now be underlined. + 2) Release the Alt key, the selection background (light grey) + should appear around the "File" menu. Compare "About" menu + with "File" menu to see the light grey selection background. + + If the above is true, press PASS else FAIL. + """; + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); + + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(62) + .rows(12) + .testUI(bug4614623::createAndShowUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createAndShowUI() { + JFrame frame = new JFrame("bug4614623 - File menu test"); + JMenuBar menuBar = new JMenuBar(); + + JMenu fileMenu = new JMenu("File"); + fileMenu.setMnemonic('F'); + menuBar.add(fileMenu); + + JMenu about = new JMenu("About"); + menuBar.add(about); + menuBar.setSize(300, 100); + + frame.setJMenuBar(menuBar); + menuBar.requestFocus(); + frame.setSize(300, 200); + return frame; + } +} diff --git a/test/jdk/javax/swing/JRootPane/bug4627806.java b/test/jdk/javax/swing/JRootPane/bug4627806.java new file mode 100644 index 0000000000000..cd6d64213a2e0 --- /dev/null +++ b/test/jdk/javax/swing/JRootPane/bug4627806.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.JFrame; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JRootPane; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; + +/* + * @test + * @bug 4627806 + * @key headful + * @summary MetalRootPaneUI calls validate() instead of revalidate() + * @run main bug4627806 + */ + +public class bug4627806 { + private static JFrame frame; + + public static void main(String[] args) throws Exception { + try { + UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame("Test"); + JPanel p = new JPanel(); + JMenuItem c = new JMenuItem(); + p.add(c); + frame.getContentPane().add(p); + frame.pack(); + c.getUI().uninstallUI(c); + JRootPane rootPane = frame.getRootPane(); + rootPane.getUI().uninstallUI(rootPane); + System.out.println("Test Passed!"); + }); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } +} diff --git a/test/jdk/javax/swing/JScrollBar/8039464/Test8039464.html b/test/jdk/javax/swing/JScrollBar/8039464/Test8039464.html deleted file mode 100644 index a473b819d1c3a..0000000000000 --- a/test/jdk/javax/swing/JScrollBar/8039464/Test8039464.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - -Choose the variable applet size and try to resize the applet. -The test passes the thumb is painted correctly. - - - - - diff --git a/test/jdk/javax/swing/JScrollBar/8039464/Test8039464.java b/test/jdk/javax/swing/JScrollBar/Test8039464.java similarity index 74% rename from test/jdk/javax/swing/JScrollBar/8039464/Test8039464.java rename to test/jdk/javax/swing/JScrollBar/Test8039464.java index 44bb33abd948c..780d5e9db7740 100644 --- a/test/jdk/javax/swing/JScrollBar/8039464/Test8039464.java +++ b/test/jdk/javax/swing/JScrollBar/Test8039464.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +26,6 @@ import java.awt.GridBagConstraints; import java.awt.GridBagLayout; -import javax.swing.JApplet; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JScrollBar; @@ -37,11 +36,16 @@ * @test * @bug 8039464 * @summary Tests enabling/disabling of titled border's caption - * @author Sergey Malenkov - * @run applet/manual=yesno Test8039464.html + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual Test8039464 */ -public class Test8039464 extends JApplet { +public class Test8039464 { + private static final String INSTRUCTIONS = """ + If the scrollbar thumb is painted correctly in system lookandfeel + click Pass else click Fail. """; + static { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); @@ -50,11 +54,6 @@ public class Test8039464 extends JApplet { } } - @Override - public void init() { - init(this); - } - private static void init(Container container) { container.setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); @@ -77,16 +76,20 @@ private static void init(Container container) { } public static void main(String[] args) throws Exception { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - JFrame frame = new JFrame("8039464"); - init(frame); - frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - frame.pack(); - frame.setLocationRelativeTo(null); - frame.setVisible(true); - } - }); + PassFailJFrame.builder() + .title("JScrollBar Instructions") + .instructions(INSTRUCTIONS) + .rows(5) + .columns(35) + .testUI(Test8039464::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + JFrame frame = new JFrame("8039464"); + init(frame); + frame.pack(); + return frame; } } diff --git a/test/jdk/javax/swing/JScrollBar/bug4495822.java b/test/jdk/javax/swing/JScrollBar/bug4495822.java new file mode 100644 index 0000000000000..01f3024b1a4e1 --- /dev/null +++ b/test/jdk/javax/swing/JScrollBar/bug4495822.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Robot; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; +import javax.swing.JScrollBar; +import javax.swing.SwingUtilities; + +/* + * @test + * @bug 4495822 + * @summary AdjustmentEvent.getValueIsAdjusting() always returns false + * @run main bug4495822 + */ + +public class bug4495822 { + public static volatile boolean isAdjusted = false; + + public static void main(String[] args) throws Exception { + + SwingUtilities.invokeAndWait(() -> { + JScrollBar scrollBar = new JScrollBar(JScrollBar.HORIZONTAL); + scrollBar.addAdjustmentListener(new AdjustmentListener() { + public void adjustmentValueChanged(AdjustmentEvent e) { + if (e.getValueIsAdjusting() != scrollBar.getValueIsAdjusting()) { + throw new RuntimeException("The AdjustmentEvent has incorrect \"valueIsAdjusting\" value"); + } + + isAdjusted = true; + } + }); + + scrollBar.setValueIsAdjusting(true); + }); + Thread.sleep(1000); + if (!isAdjusted) { + throw new RuntimeException("adjustmentValueChanged() not invoked!"); + } + System.out.println("Test Passed!"); + } +} diff --git a/test/jdk/javax/swing/JScrollBar/bug4696826.java b/test/jdk/javax/swing/JScrollBar/bug4696826.java new file mode 100644 index 0000000000000..f82ad6dfa73e1 --- /dev/null +++ b/test/jdk/javax/swing/JScrollBar/bug4696826.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.JComponent; +import javax.swing.JScrollBar; +import javax.swing.SwingUtilities; +import javax.swing.plaf.basic.BasicScrollBarUI; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; + +/* + * @test + * @bug 4696826 + * @summary BasicScrollBarUI should check if it needs to paint the thumb + * @run main bug4696826 + */ + +public class bug4696826 { + public static void main(String[] args) throws Exception { + SwingUtilities.invokeAndWait(() -> { + JScrollBar sb = new JScrollBar(); + sb.setBounds(new Rectangle(0, 0, 20, 20)); + + TestScrollBarUI ui = new TestScrollBarUI(); + sb.setUI(ui); + ui.setThumbBounds(0, 0, 20, 20); + + BufferedImage image = new BufferedImage(100, 100, + BufferedImage.TYPE_3BYTE_BGR); + Graphics g = image.getGraphics(); + g.setClip(200, 200, 100, 100); + sb.paint(g); + }); + System.out.println("Test Passed!"); + } + + static class TestScrollBarUI extends BasicScrollBarUI { + protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) { + throw new RuntimeException("Thumb shouldn't be painted"); + } + public void setThumbBounds(int x, int y, int width, int height) { + super.setThumbBounds(x, y, width, height); + } + } +} diff --git a/test/jdk/javax/swing/JScrollBar/bug4842792.java b/test/jdk/javax/swing/JScrollBar/bug4842792.java new file mode 100644 index 0000000000000..09ada58315a73 --- /dev/null +++ b/test/jdk/javax/swing/JScrollBar/bug4842792.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.JScrollBar; +import javax.swing.SwingUtilities; +import java.awt.Dimension; +import java.awt.event.MouseEvent; +import java.util.Date; + +/* + * @test + * @bug 4842792 + * @summary JScrollBar behaves incorrectly if "Block increment" value is big enough + * @run main bug4842792 + */ + +public class bug4842792 { + public static TestScrollBar scrollBar; + + public static void main(String[] args) throws Exception { + SwingUtilities.invokeAndWait(() -> { + scrollBar = new TestScrollBar(JScrollBar.HORIZONTAL, 10, 10, 0, 100); + scrollBar.setPreferredSize(new Dimension(200, 20)); + scrollBar.setBlockIncrement(Integer.MAX_VALUE); + + if (scrollBar.doTest() == 0) { + throw new RuntimeException("The scrollbar new value should not be 0"); + } + }); + System.out.println("Test Passed!"); + } + + static class TestScrollBar extends JScrollBar { + public TestScrollBar(int orientation, int value, int extent, + int min, int max) { + super(orientation, value, extent, min, max); + } + + public int doTest() { + MouseEvent mouseEvent = new MouseEvent(scrollBar, + MouseEvent.MOUSE_PRESSED, + (new Date()).getTime(), + MouseEvent.BUTTON1_DOWN_MASK, + 150, 10, 1, true); + processMouseEvent(mouseEvent); + return scrollBar.getValue(); + } + } +} diff --git a/test/jdk/javax/swing/JScrollPane/6274267/bug6274267.java b/test/jdk/javax/swing/JScrollPane/6274267/bug6274267.java index a141c43ac2a11..5584cec126c84 100644 --- a/test/jdk/javax/swing/JScrollPane/6274267/bug6274267.java +++ b/test/jdk/javax/swing/JScrollPane/6274267/bug6274267.java @@ -4,9 +4,7 @@ * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/test/jdk/javax/swing/JScrollPane/bug4247092.java b/test/jdk/javax/swing/JScrollPane/bug4247092.java new file mode 100644 index 0000000000000..a1fa369d67ea1 --- /dev/null +++ b/test/jdk/javax/swing/JScrollPane/bug4247092.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.JScrollPane; + +/* + * @test + * @bug 4247092 + * @summary JScrollPane.setCorner(corner,null) causes NPE, but defolt getCorner() rtns null + * @run main bug4247092 + */ + +public class bug4247092 { + public static void main(String[] args) { + JScrollPane sp = new JScrollPane(); + sp.setCorner(JScrollPane.LOWER_RIGHT_CORNER, null); + if (sp.getCorner(JScrollPane.LOWER_RIGHT_CORNER) != null) { + throw new RuntimeException("The corner component should be null"); + } + System.out.println("Test Passed!"); + } +} diff --git a/test/jdk/javax/swing/JScrollPane/bug4264640.java b/test/jdk/javax/swing/JScrollPane/bug4264640.java new file mode 100644 index 0000000000000..ca1bfc146df5b --- /dev/null +++ b/test/jdk/javax/swing/JScrollPane/bug4264640.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Robot; + +/* + * @test + * @bug 4264640 + * @summary Tests that JScrollPane sets correct position of its column header view + * @key headful + * @run main bug4264640 + */ + +public class bug4264640 { + public static JFrame frame; + public static JButton b; + public static Robot robot; + public static volatile int yPos; + + public static void main(String[] args) throws Exception { + try { + robot = new Robot(); + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame("Scroll Pane test"); + JScrollPane scroller = new JScrollPane(); + b = new JButton("This is BUG !"); + b.setBounds(12, 12, 169, 133); + scroller.setColumnHeaderView(b); + + Container pane = frame.getContentPane(); + pane.setLayout(new BorderLayout()); + pane.add(scroller); + frame.setSize(200,200); + frame.setVisible(true); + }); + + robot.waitForIdle(); + robot.delay(1000); + + SwingUtilities.invokeAndWait(() -> { + yPos = b.getY(); + }); + if (yPos != 0) { + throw new RuntimeException("Failed: Y = " + yPos + " (should be 0)"); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + System.out.println("Test Passed!"); + } +} diff --git a/test/jdk/javax/swing/JScrollPane/bug4467063.java b/test/jdk/javax/swing/JScrollPane/bug4467063.java new file mode 100644 index 0000000000000..8bce485e48dfe --- /dev/null +++ b/test/jdk/javax/swing/JScrollPane/bug4467063.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.JButton; +import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; +import java.awt.ComponentOrientation; + +/* + * @test + * @bug 4467063 + * @summary JScrollPane.setCorner() causes IllegalArgumentException. (invalid corner key) + * @run main bug4467063 + */ + +public class bug4467063 { + + public static void main(String[] args) throws Exception { + SwingUtilities.invokeAndWait(() -> { + JScrollPane sp = new JScrollPane(); + + //Test corners for left-to-right orientation + sp.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); + sp.setCorner(JScrollPane.LOWER_LEADING_CORNER, new JButton("0")); + sp.setCorner(JScrollPane.LOWER_TRAILING_CORNER, new JButton("1")); + sp.setCorner(JScrollPane.UPPER_LEADING_CORNER, new JButton("2")); + sp.setCorner(JScrollPane.UPPER_TRAILING_CORNER, new JButton("3")); + + if (!sp.getCorner(JScrollPane.LOWER_LEADING_CORNER).equals( + sp.getCorner(JScrollPane.LOWER_LEFT_CORNER))) { + throw new RuntimeException("Incorrect LOWER_LEADING_CORNER value"); + } + + if (!sp.getCorner(JScrollPane.LOWER_TRAILING_CORNER).equals( + sp.getCorner(JScrollPane.LOWER_RIGHT_CORNER))) { + throw new RuntimeException("Incorrect LOWER_TRAILING_CORNER value"); + } + + if (!sp.getCorner(JScrollPane.UPPER_LEADING_CORNER).equals( + sp.getCorner(JScrollPane.UPPER_LEFT_CORNER))) { + throw new RuntimeException("Incorrect UPPER_LEADING_CORNER value"); + } + + if (!sp.getCorner(JScrollPane.UPPER_TRAILING_CORNER).equals( + sp.getCorner(JScrollPane.UPPER_RIGHT_CORNER))) { + throw new RuntimeException("Incorrect UPPER_TRAILING_CORNER value"); + } + + //Test corners for right-to-left orientation + sp.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); + sp.setCorner(JScrollPane.LOWER_LEADING_CORNER, new JButton("0")); + sp.setCorner(JScrollPane.LOWER_TRAILING_CORNER, new JButton("1")); + sp.setCorner(JScrollPane.UPPER_LEADING_CORNER, new JButton("2")); + sp.setCorner(JScrollPane.UPPER_TRAILING_CORNER, new JButton("3")); + + if (!sp.getCorner(JScrollPane.LOWER_LEADING_CORNER).equals( + sp.getCorner(JScrollPane.LOWER_RIGHT_CORNER))) { + throw new RuntimeException("Incorrect LOWER_LEADING_CORNER value"); + } + + if (!sp.getCorner(JScrollPane.LOWER_TRAILING_CORNER).equals( + sp.getCorner(JScrollPane.LOWER_LEFT_CORNER))) { + throw new RuntimeException("Incorrect LOWER_TRAILING_CORNER value"); + } + + if (!sp.getCorner(JScrollPane.UPPER_LEADING_CORNER).equals( + sp.getCorner(JScrollPane.UPPER_RIGHT_CORNER))) { + throw new RuntimeException("Incorrect UPPER_LEADING_CORNER value"); + } + + if (!sp.getCorner(JScrollPane.UPPER_TRAILING_CORNER).equals( + sp.getCorner(JScrollPane.UPPER_LEFT_CORNER))) { + throw new RuntimeException("Incorrect UPPER_TRAILING_CORNER value"); + } + }); + System.out.println("Test Passed!"); + } +} diff --git a/test/jdk/javax/swing/JSlider/4987336/bug4987336.html b/test/jdk/javax/swing/JSlider/4987336/bug4987336.html deleted file mode 100644 index 606810da8873c..0000000000000 --- a/test/jdk/javax/swing/JSlider/4987336/bug4987336.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - - -There are four Sliders. Each of them has a label with animated gif (a waving duke) -and a label with static image. - -Check that for every LAF animation works for all Sliders. - - diff --git a/test/jdk/javax/swing/JSlider/4987336/bug4987336.java b/test/jdk/javax/swing/JSlider/4987336/bug4987336.java index 3671a9a30eb7c..a5aad958c77d6 100644 --- a/test/jdk/javax/swing/JSlider/4987336/bug4987336.java +++ b/test/jdk/javax/swing/JSlider/4987336/bug4987336.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,25 +21,58 @@ * questions. */ -/* @test - @bug 4987336 - @summary JSlider doesn't show label's animated icon. - @author Pavel Porvatov - @run applet/manual=done bug4987336.html +/* + * @test + * @bug 4987336 + * @summary JSlider doesn't show label's animated icon. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4987336 */ - -import javax.swing.*; -import javax.swing.border.TitledBorder; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import javax.swing.ButtonGroup; +import javax.swing.BoxLayout; +import javax.swing.ImageIcon; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JScrollPane; +import javax.swing.JSlider; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.border.TitledBorder; import java.util.Hashtable; -public class bug4987336 extends JApplet { +public class bug4987336 { + private static final String INSTRUCTIONS = """ + There are four Sliders. + Each of them has a label with animated gif (a waving duke) + and a label with static image. + If it is rendered correctly, click Pass else click Fail."""; + private static final String IMAGE_RES = "box.gif"; private static final String ANIM_IMAGE_RES = "duke.gif"; + private static JFrame frame; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Slider rendering Instructions") + .instructions(INSTRUCTIONS) + .rows(5) + .columns(35) + .testUI(bug4987336::createTestUI) + .position(PassFailJFrame.Position.TOP_LEFT_CORNER) + .build() + .awaitAndCheck(); + } - public void init() { + private static JFrame createTestUI() { + frame = new JFrame("bug4987336"); JPanel pnLafs = new JPanel(); pnLafs.setLayout(new BoxLayout(pnLafs, BoxLayout.Y_AXIS)); @@ -64,7 +97,9 @@ public void init() { pnContent.add(createSlider(true, ANIM_IMAGE_RES, null, IMAGE_RES, IMAGE_RES)); pnContent.add(createSlider(false, ANIM_IMAGE_RES, null, IMAGE_RES, IMAGE_RES)); - getContentPane().add(new JScrollPane(pnContent)); + frame.getContentPane().add(new JScrollPane(pnContent)); + frame.pack(); + return frame; } private static JSlider createSlider(boolean enabled, @@ -99,7 +134,7 @@ private static JLabel createLabel(String enabledImage, String disabledImage) { return result; } - private class LafRadioButton extends JRadioButton { + private static class LafRadioButton extends JRadioButton { public LafRadioButton(final UIManager.LookAndFeelInfo lafInfo) { super(lafInfo.getName(), lafInfo.getName().equals(UIManager.getLookAndFeel().getName())); @@ -108,7 +143,7 @@ public void actionPerformed(ActionEvent e) { try { UIManager.setLookAndFeel(lafInfo.getClassName()); - SwingUtilities.updateComponentTreeUI(bug4987336.this); + SwingUtilities.updateComponentTreeUI(frame); } catch (Exception ex) { // Ignore such errors System.out.println("Cannot set LAF " + lafInfo.getName()); diff --git a/test/jdk/javax/swing/JSlider/6524424/bug6524424.html b/test/jdk/javax/swing/JSlider/6524424/bug6524424.html deleted file mode 100644 index 11a57b6b7fbb7..0000000000000 --- a/test/jdk/javax/swing/JSlider/6524424/bug6524424.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - - -To test fix follow the next steps: -1. Select a slider (do the next steps for every slider) -2. Check that the next keyboard buttons work correctly: - Up, Down, Left, Right, Page Up, Page Down -3. Press left mouse button on a free space of the slider and check - that thumb moves correctly - - diff --git a/test/jdk/javax/swing/JSlider/6587742/bug6587742.html b/test/jdk/javax/swing/JSlider/6587742/bug6587742.html deleted file mode 100644 index b326103a1da33..0000000000000 --- a/test/jdk/javax/swing/JSlider/6587742/bug6587742.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - -Select every theme and check that all sliders looks good. -Note that every slider has a tooltip text with information about -slider configuration. -There is a small difference in sliders with property "filled = null" (it's -default behaviour when property JSlider.isFilled is not setted) -for themes: -1. OceanTheme - sliders look like filled -2. DefaultMetalTheme - sliders look like NOT filled - - diff --git a/test/jdk/javax/swing/JSlider/bug4186062.java b/test/jdk/javax/swing/JSlider/bug4186062.java new file mode 100644 index 0000000000000..1db2f1bba6c29 --- /dev/null +++ b/test/jdk/javax/swing/JSlider/bug4186062.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4382876 + * @summary Tests if JSlider fires ChangeEvents when thumb is clicked and not moved + * @key headful + * @run main bug4186062 + */ + +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.InputEvent; + +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.SwingUtilities; +import javax.swing.event.ChangeListener; + +public class bug4186062 { + private static JFrame f; + private static JSlider slider; + private static volatile Point loc; + private static volatile int labelNum; + + public static void main(String[] args) throws Exception { + try { + SwingUtilities.invokeAndWait(() -> { + f = new JFrame("JSlider Click Value Test"); + f.setSize(400, 200); + f.setLocationRelativeTo(null); + f.setVisible(true); + JPanel panel = new JPanel(); + slider = new JSlider(); + final JLabel label = new JLabel("0"); + labelNum = 0; + + ChangeListener listener = e -> { + labelNum++; + label.setText("" + labelNum); + }; + slider.addChangeListener(listener); + + panel.add(slider); + panel.add(label); + f.add(panel); + }); + + Robot r = new Robot(); + r.setAutoDelay(100); + r.waitForIdle(); + r.delay(1000); + + SwingUtilities.invokeAndWait(() -> { + loc = slider.getLocationOnScreen(); + loc.setLocation(loc.x + (slider.getWidth() / 2), + loc.y + (slider.getHeight() / 2)); + }); + + r.mouseMove(loc.x, loc.y); + r.mousePress(InputEvent.BUTTON1_DOWN_MASK); + r.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + + if (labelNum > 0) { + throw new RuntimeException(labelNum + " ChangeEvents fired. " + + "Test failed"); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + } + } +} diff --git a/test/jdk/javax/swing/JSlider/bug4200901.java b/test/jdk/javax/swing/JSlider/bug4200901.java new file mode 100644 index 0000000000000..cf4810a54cad3 --- /dev/null +++ b/test/jdk/javax/swing/JSlider/bug4200901.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.JSlider; + +/* + * @test + * @bug 4200901 + * @summary Test to check if JSlider.createStandardLabels() throws Exception + * @run main bug4200901 + */ + +public class bug4200901 { + public static void main(String[] args) { + try { + JSlider slider = new JSlider(); + slider.createStandardLabels( -1 ); + } catch ( IllegalArgumentException e ) { + System.out.println("Test Passed!"); + return; + } + throw new RuntimeException( "TEST FAILED!"); + } +} diff --git a/test/jdk/javax/swing/JSlider/bug4203754.java b/test/jdk/javax/swing/JSlider/bug4203754.java new file mode 100644 index 0000000000000..21eb898d27573 --- /dev/null +++ b/test/jdk/javax/swing/JSlider/bug4203754.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2004, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JSlider; +import javax.swing.SwingUtilities; +import java.awt.FlowLayout; +import java.awt.Robot; +import java.util.Dictionary; +import java.util.Hashtable; + +/* + * @test + * @bug 4203754 + * @key headful + * @summary Labels in a JSlider don't disable or enable with the slider + * @run main bug4203754 + */ + +public class bug4203754 { + private static JFrame frame; + private static Robot robot; + private static JLabel label; + + public static void main(String[] argv) throws Exception { + try { + robot = new Robot(); + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame("Test"); + frame.getContentPane().setLayout(new FlowLayout()); + JSlider slider = new JSlider(0, 100, 25); + frame.getContentPane().add(slider); + + label = new JLabel("0", JLabel.CENTER) { + public void setEnabled(boolean b) { + super.setEnabled(b); + } + }; + + Dictionary labels = new Hashtable(); + labels.put(Integer.valueOf(0), label); + slider.setLabelTable(labels); + slider.setPaintLabels(true); + slider.setEnabled(false); + frame.setSize(250, 150); + frame.setVisible(true); + }); + robot.waitForIdle(); + robot.delay(1000); + if (label.isEnabled()) { + throw new RuntimeException("Label should be disabled"); + } + System.out.println("Test Passed!"); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } +} diff --git a/test/jdk/javax/swing/JSlider/bug4275631.java b/test/jdk/javax/swing/JSlider/bug4275631.java new file mode 100644 index 0000000000000..4d0aa55572131 --- /dev/null +++ b/test/jdk/javax/swing/JSlider/bug4275631.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4275631 + * @summary Tests if vertical JSlider is properly aligned in large container + * @key headful + * @run main bug4275631 + */ + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Point; +import java.awt.Robot; + +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.SwingUtilities; + +public class bug4275631 { + private static final int OFFSET = 1; + private static JFrame f; + private static JSlider slider1; + private static JSlider slider2; + private static volatile Point loc1; + private static volatile Point loc2; + + public static void main(String[] args) throws Exception { + try { + SwingUtilities.invokeAndWait(() -> { + f = new JFrame("JSlider Alignment Test"); + f.setSize(400, 200); + f.setLocationRelativeTo(null); + + // Create two sliders, verify the alignment on the slider to be + // used in the border layout + slider1 = new JSlider(JSlider.VERTICAL, 0, 99, 50); + slider1.setInverted(true); + slider1.setMajorTickSpacing(10); + slider1.setMinorTickSpacing(1); + slider1.setPaintTicks(true); + slider1.setPaintLabels(true); + slider2 = new JSlider(JSlider.VERTICAL, 0, 99, 50); + slider2.setInverted(true); + slider2.setMajorTickSpacing(10); + slider2.setMinorTickSpacing(1); + slider2.setPaintTicks(true); + slider2.setPaintLabels(true); + + // Try to center the natural way, using a border layout in the "Center" + JPanel borderPanel = new JPanel(); + borderPanel.setLayout(new BorderLayout()); + borderPanel.setBorder(BorderFactory.createTitledBorder("BorderLayout")); + borderPanel.add(slider1, BorderLayout.CENTER); + borderPanel.setPreferredSize(new Dimension(200, 200)); + + // Try to center using GridBagLayout, with glue on left + // and right to squeeze slider into place + JPanel gridBagPanel = new JPanel(new GridBagLayout()); + gridBagPanel.setBorder(BorderFactory.createTitledBorder("GridBagLayout")); + GridBagConstraints c = new GridBagConstraints(); + c.gridx = 1; + c.fill = GridBagConstraints.VERTICAL; + c.weighty = 1.0; + gridBagPanel.add(slider2, c); + c.gridx = 0; + c.fill = GridBagConstraints.BOTH; + c.weighty = 0.0; + gridBagPanel.add(Box.createHorizontalGlue(), c); + c.gridx = 2; + c.fill = GridBagConstraints.BOTH; + gridBagPanel.add(Box.createHorizontalGlue(), c); + gridBagPanel.setPreferredSize(new Dimension(200, 200)); + + f.add(borderPanel, BorderLayout.WEST); + f.add(gridBagPanel, BorderLayout.EAST); + f.setVisible(true); + }); + + Robot r = new Robot(); + r.setAutoDelay(100); + r.waitForIdle(); + r.delay(1000); + + SwingUtilities.invokeAndWait(() -> { + loc1 = slider1.getLocationOnScreen(); + loc1.setLocation(loc1.x + (slider1.getWidth() / 2), + loc1.y + (slider1.getHeight() / 2)); + + loc2 = slider2.getLocationOnScreen(); + loc2.setLocation(loc2.x + (slider2.getWidth() / 2), + loc2.y + (slider2.getHeight() / 2)); + }); + + if (loc1.y > loc2.y + OFFSET || loc1.y < loc2.y - OFFSET) { + throw new RuntimeException("JSlider position is not aligned!"); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + } + } +} diff --git a/test/jdk/javax/swing/JSlider/bug4382876.java b/test/jdk/javax/swing/JSlider/bug4382876.java new file mode 100644 index 0000000000000..b9ec64aab216c --- /dev/null +++ b/test/jdk/javax/swing/JSlider/bug4382876.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4382876 + * @summary Tests how PgUp and PgDn keys work with JSlider + * @key headful + * @run main bug4382876 + */ + +import java.awt.BorderLayout; +import java.awt.ComponentOrientation; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsEnvironment; +import java.awt.Robot; +import java.awt.event.KeyEvent; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +import javax.imageio.ImageIO; +import javax.swing.JFrame; +import javax.swing.JSlider; +import javax.swing.SwingUtilities; + +public class bug4382876 { + private static Robot r; + private static JFrame f; + private static JSlider slider; + private static boolean upFail; + private static boolean downFail; + + public static void main(String[] args) throws Exception { + try { + SwingUtilities.invokeAndWait(() -> { + f = new JFrame("JSlider PageUp/Down Test"); + f.setSize(300, 200); + f.setLocationRelativeTo(null); + f.setVisible(true); + slider = new JSlider(-1000, -900, -1000); + slider.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); + slider.putClientProperty("JSlider.isFilled", Boolean.TRUE); + f.add(slider, BorderLayout.CENTER); + }); + + r = new Robot(); + r.setAutoDelay(100); + r.waitForIdle(); + r.delay(1000); + + r.keyPress(KeyEvent.VK_PAGE_UP); + SwingUtilities.invokeAndWait(() -> { + if (slider.getValue() < -1000) { + System.out.println("PAGE_UP VAL: " + slider.getValue()); + upFail = true; + } + }); + if (upFail) { + writeFailImage(); + throw new RuntimeException("Slider value did NOT change with PAGE_UP"); + } + r.keyPress(KeyEvent.VK_PAGE_DOWN); + SwingUtilities.invokeAndWait(() -> { + if (slider.getValue() > -1000) { + System.out.println("PAGE_DOWN VAL: " + slider.getValue()); + downFail = true; + } + }); + if (downFail) { + writeFailImage(); + throw new RuntimeException("Slider value did NOT change with PAGE_DOWN"); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + } + } + + private static void writeFailImage() throws IOException { + GraphicsConfiguration ge = GraphicsEnvironment + .getLocalGraphicsEnvironment().getDefaultScreenDevice() + .getDefaultConfiguration(); + BufferedImage failImage = r.createScreenCapture(ge.getBounds()); + ImageIO.write(failImage, "png", new File("failImage.png")); + } +} diff --git a/test/jdk/javax/swing/JSlider/6524424/bug6524424.java b/test/jdk/javax/swing/JSlider/bug6524424.java similarity index 61% rename from test/jdk/javax/swing/JSlider/6524424/bug6524424.java rename to test/jdk/javax/swing/JSlider/bug6524424.java index 3cb8b69d260c3..3ceff68b32b18 100644 --- a/test/jdk/javax/swing/JSlider/6524424/bug6524424.java +++ b/test/jdk/javax/swing/JSlider/bug6524424.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,46 +21,65 @@ * questions. */ -/* @test +/* + * @test * @bug 6524424 * @requires (os.family == "windows") - * @summary JSlider Clicking In Tracks Behavior Inconsistent For Different Tick Spacings - * @author Pavel Porvatov + * @summary JSlider clicking in tracks behavior inconsistent for different tick spacings * @modules java.desktop/com.sun.java.swing.plaf.windows - * @run applet/manual=done bug6524424.html + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug6524424 */ -import java.awt.*; -import javax.swing.*; +import java.awt.Component; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; import com.sun.java.swing.plaf.windows.WindowsLookAndFeel; -public class bug6524424 extends JApplet { - public static void main(String[] args) { +public class bug6524424 { + private static final String INSTRUCTIONS = """ + 1. Select a slider (do the next steps for every slider) + 2. Check that the next keyboard buttons work correctly: + Up, Down, Left, Right, Page Up, Page Down + 3. Press left mouse button on a free space of the slider + check if thumb moves correctly + press Pass else press Fail."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Slider Behavior Instructions") + .instructions(INSTRUCTIONS) + .rows(7) + .columns(30) + .testUI(bug6524424::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { try { UIManager.setLookAndFeel(new WindowsLookAndFeel()); - } catch (UnsupportedLookAndFeelException e) { - e.printStackTrace(); - - return; + } catch (UnsupportedLookAndFeelException ex) { + PassFailJFrame.forceFail(ex.toString()); + return null; } TestPanel panel = new TestPanel(); - JFrame frame = new JFrame(); + JFrame frame = new JFrame("bug6524424"); frame.setContentPane(panel); - frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.pack(); - frame.setLocationRelativeTo(null); - - frame.setVisible(true); - } - - public void init() { - TestPanel panel = new TestPanel(); - - setContentPane(panel); + return frame; } private static class TestPanel extends JPanel { diff --git a/test/jdk/javax/swing/JSlider/6587742/bug6587742.java b/test/jdk/javax/swing/JSlider/bug6587742.java similarity index 66% rename from test/jdk/javax/swing/JSlider/6587742/bug6587742.java rename to test/jdk/javax/swing/JSlider/bug6587742.java index 89e0727994350..9b94ee7ab5029 100644 --- a/test/jdk/javax/swing/JSlider/6587742/bug6587742.java +++ b/test/jdk/javax/swing/JSlider/bug6587742.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,14 +21,18 @@ * questions. */ -/* @test - * @bug 6587742 - * @summary filling half of a JSlider's track is no longer optional - * @author Pavel Porvatov - * @run applet/manual=done bug6587742.html - */ - -import javax.swing.*; +import javax.swing.BoxLayout; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.JTabbedPane; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; import javax.swing.plaf.metal.DefaultMetalTheme; import javax.swing.plaf.metal.MetalLookAndFeel; import javax.swing.plaf.metal.MetalTheme; @@ -36,14 +40,46 @@ import java.awt.event.ItemEvent; import java.awt.event.ItemListener; -public class bug6587742 extends JApplet { - public void init() { - TestPanel panel = new TestPanel(); +/* @test + * @bug 6587742 + * @summary filling half of a JSlider's track is no longer optional + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug6587742 + */ - setContentPane(panel); +public class bug6587742 { + private static final String INSTRUCTIONS = """ + Select every theme and check that all sliders looks good. + Note that every slider has a tooltip text with information about + slider configuration. + There is a small difference in sliders with property "filled = null" (it's + default behaviour when property JSlider.isFilled is not setted) + for themes: + 1. OceanTheme - sliders look like filled + 2. DefaultMetalTheme - sliders look like NOT filled\s"""; + private static JFrame frame; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("JSlider Instructions") + .instructions(INSTRUCTIONS) + .rows(10) + .columns(40) + .testUI(bug6587742::createAndShowUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createAndShowUI() { + frame = new JFrame("Test Sliders"); + TestPanel panel = new TestPanel(); + frame.setSize(800, 600); + frame.getContentPane().add(panel); + return frame; } - private class TestPanel extends JPanel { + private static class TestPanel extends JPanel { private final JComboBox cbThemes = new JComboBox(); private TestPanel() { @@ -68,30 +104,30 @@ public void itemStateChanged(ItemEvent e) { return; } - SwingUtilities.updateComponentTreeUI(bug6587742.this); + SwingUtilities.updateComponentTreeUI(frame); } } }); JPanel pnVertical = new JPanel(); - pnVertical.setLayout(new BoxLayout(pnVertical, BoxLayout.Y_AXIS)); + pnVertical.setLayout(new BoxLayout(pnVertical, BoxLayout.X_AXIS)); for (int i = 0; i < 12; i++) { int filled = i >> 2; - pnVertical.add(createSlider(false, filled > 1 ? null : Boolean.valueOf(filled == 1), (i & 2) == 0, + pnVertical.add(createSlider(true, filled > 1 ? null : Boolean.valueOf(filled == 1), (i & 2) == 0, (i & 1) != 0)); } JPanel pnHorizontal = new JPanel(); - pnHorizontal.setLayout(new BoxLayout(pnHorizontal, BoxLayout.X_AXIS)); + pnHorizontal.setLayout(new BoxLayout(pnHorizontal, BoxLayout.Y_AXIS)); for (int i = 0; i < 12; i++) { int filled = i >> 2; - pnHorizontal.add(createSlider(true, filled > 1 ? null : Boolean.valueOf(filled == 1), (i & 2) == 0, + pnHorizontal.add(createSlider(false, filled > 1 ? null : Boolean.valueOf(filled == 1), (i & 2) == 0, (i & 1) != 0)); } diff --git a/test/jdk/javax/swing/JSlider/6742358/bug6742358.java b/test/jdk/javax/swing/JSlider/bug6742358.java similarity index 59% rename from test/jdk/javax/swing/JSlider/6742358/bug6742358.java rename to test/jdk/javax/swing/JSlider/bug6742358.java index 8ef504a0b2b93..f41ac3365879e 100644 --- a/test/jdk/javax/swing/JSlider/6742358/bug6742358.java +++ b/test/jdk/javax/swing/JSlider/bug6742358.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,37 +21,46 @@ * questions. */ -/* @test +/* + * @test * @bug 6742358 - * @summary MetalSliderUI paint wrong vertical disabled filled JSlider for DefaultMetalTheme - * @author Pavel Porvatov - * @run applet/manual=done bug6742358.html + * @summary Verifies that painting a vertical disabled filled JSlider, the + * track will be painted correctly for DefaultMetalTheme. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug6742358 */ -import javax.swing.*; +import javax.swing.BoxLayout; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.SwingConstants; import javax.swing.plaf.metal.DefaultMetalTheme; import javax.swing.plaf.metal.MetalLookAndFeel; -public class bug6742358 extends JApplet { - public static void main(String[] args) { - MetalLookAndFeel.setCurrentTheme(new DefaultMetalTheme()); - - JFrame frame = new JFrame(); - - frame.setContentPane(new TestPanel()); - frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - frame.pack(); - frame.setLocationRelativeTo(null); +public class bug6742358 { + private static final String INSTRUCTIONS = """ + Check that all sliders look good."""; - frame.setVisible(true); - } - - public void init() { + public static void main(String[] args) throws Exception { MetalLookAndFeel.setCurrentTheme(new DefaultMetalTheme()); + PassFailJFrame.builder() + .title("JSlider Instructions") + .instructions(INSTRUCTIONS) + .rows(5) + .columns(40) + .testUI(bug6742358::createAndShowUI) + .build() + .awaitAndCheck(); + } + public static JFrame createAndShowUI() { + JFrame frame = new JFrame("Test Sliders"); TestPanel panel = new TestPanel(); - - setContentPane(panel); + frame.setSize(400, 300); + frame.getContentPane().add(panel); + return frame; } private static class TestPanel extends JPanel { @@ -62,7 +71,8 @@ private TestPanel() { pnVertical.setLayout(new BoxLayout(pnVertical, BoxLayout.Y_AXIS)); for (int i = 0; i < 8; i++) { - pnVertical.add(createSlider(false, (i & 4) == 0, (i & 2) == 0, (i & 1) == 0)); + pnVertical.add(createSlider(false, (i & 4) == 0, + (i & 2) == 0, (i & 1) == 0)); } JPanel pnHorizontal = new JPanel(); @@ -70,7 +80,8 @@ private TestPanel() { pnHorizontal.setLayout(new BoxLayout(pnHorizontal, BoxLayout.X_AXIS)); for (int i = 0; i < 8; i++) { - pnHorizontal.add(createSlider(true, (i & 4) == 0, (i & 2) == 0, (i & 1) == 0)); + pnHorizontal.add(createSlider(true, (i & 4) == 0, + (i & 2) == 0, (i & 1) == 0)); } add(pnHorizontal); @@ -78,14 +89,17 @@ private TestPanel() { } } - private static JSlider createSlider(boolean vertical, boolean enabled, boolean filled, boolean inverted) { - JSlider result = new JSlider(vertical ? SwingConstants.VERTICAL : SwingConstants.HORIZONTAL, 0, 10, 5); + private static JSlider createSlider(boolean vertical, boolean enabled, + boolean filled, boolean inverted) { + JSlider result = new JSlider(vertical ? SwingConstants.VERTICAL : SwingConstants.HORIZONTAL, + 0, 10, 5); result.setEnabled(enabled); result.putClientProperty("JSlider.isFilled", filled); result.setInverted(inverted); - result.setToolTipText("vertical = " + vertical + "
    enabled = " + enabled + "
    filled = " + filled + - "
    inverted = " + inverted + ""); + result.setToolTipText("vertical = " + vertical + "
    enabled = " + + enabled+ "
    filled = " + filled + + "
    inverted = " + inverted + ""); return result; } diff --git a/test/jdk/javax/swing/JSpinner/4515999/JSpinnerMouseAndKeyPressTest.java b/test/jdk/javax/swing/JSpinner/4515999/JSpinnerMouseAndKeyPressTest.java index 6e5fb3ad97fc6..49a237a1e27f2 100644 --- a/test/jdk/javax/swing/JSpinner/4515999/JSpinnerMouseAndKeyPressTest.java +++ b/test/jdk/javax/swing/JSpinner/4515999/JSpinnerMouseAndKeyPressTest.java @@ -83,6 +83,7 @@ private static void createUI() { panel.add(spinner); frame.add(panel); frame.setUndecorated(true); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.pack(); frame.setAlwaysOnTop(true); frame.setLocationRelativeTo(null); @@ -103,6 +104,7 @@ public static void runTest() throws Exception { setLookAndFeel(laf); createUI(); }); + robot.waitForIdle(); SwingUtilities.invokeAndWait(() -> { Point loc = spinner.getLocationOnScreen(); diff --git a/test/jdk/javax/swing/JSplitPane/4164779/JSplitPaneKeyboardNavigationTest.java b/test/jdk/javax/swing/JSplitPane/4164779/JSplitPaneKeyboardNavigationTest.java index ced7410bf0664..4f0857944aa4a 100644 --- a/test/jdk/javax/swing/JSplitPane/4164779/JSplitPaneKeyboardNavigationTest.java +++ b/test/jdk/javax/swing/JSplitPane/4164779/JSplitPaneKeyboardNavigationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -64,7 +64,7 @@ public class JSplitPaneKeyboardNavigationTest { public static void main(String[] s) throws Exception { robot = new Robot(); robot.setAutoWaitForIdle(true); - robot.setAutoDelay(200); + robot.setAutoDelay(100); List lafs = Arrays.stream(getInstalledLookAndFeels()) .map(LookAndFeelInfo::getClassName) .collect(Collectors.toList()); @@ -81,10 +81,13 @@ public static void main(String[] s) throws Exception { continue; } robot.waitForIdle(); + robot.delay(1000); // Press Right button 1 and move focus to it. pressButton(rightButton1); hitKeys(KeyEvent.VK_F6); + robot.waitForIdle(); + robot.delay(100); // Verifier1 - Verifies that, F6 transfers focus to the right/bottom side of the splitpane if (isFocusOwner(rightButton2)) { @@ -98,9 +101,12 @@ public static void main(String[] s) throws Exception { // Press Right button 2 and move focus to it. pressButton(rightButton2); hitKeys(KeyEvent.VK_F6); + robot.waitForIdle(); + robot.delay(100); // Verifier2 - Verifies that, F6 transfers focus to the left side of the parent splitpane, - // if the right/bottom side of splitpane already has focus, and it is contained within another splitpane + // if the right/bottom side of splitpane already has focus, + // and it is contained within another splitpane if (isFocusOwner(leftButton)) { System.out.println("Verifier 2 passed"); } else { @@ -112,6 +118,9 @@ public static void main(String[] s) throws Exception { // Press Left button and move focus to it. pressButton(leftButton); hitKeys(KeyEvent.VK_CONTROL, KeyEvent.VK_TAB); + robot.waitForIdle(); + robot.delay(100); + // Verifier3 - Verifies that, CTRL-TAB navigates forward outside the JSplitPane if (isFocusOwner(bottomButton)) { System.out.println("Verifier 3 passed"); @@ -124,6 +133,8 @@ public static void main(String[] s) throws Exception { // Press Left button and move focus to it. pressButton(leftButton); hitKeys(KeyEvent.VK_CONTROL, KeyEvent.VK_SHIFT, KeyEvent.VK_TAB); + robot.waitForIdle(); + robot.delay(100); // Verifier4 - Verifies that, CTRL-SHIFT-TAB navigates backward outside the JSplitPane if (isFocusOwner(topButton)) { @@ -137,7 +148,8 @@ public static void main(String[] s) throws Exception { if (failedVerifiers.toString().isEmpty()) { System.out.println("Test passed, All verifiers succeeded for " + laf); } else { - throw new RuntimeException("Test failed, verifiers " + failedVerifiers.toString() + " failed for " + laf); + throw new RuntimeException("Test failed, verifiers " + + failedVerifiers.toString() + " failed for " + laf); } } finally { SwingUtilities.invokeAndWait(JSplitPaneKeyboardNavigationTest::disposeFrame); @@ -159,13 +171,14 @@ private static void pressButton(JButton button) throws Exception { loc.set(button.getLocationOnScreen()); }); final Point buttonLoc = loc.get(); - robot.mouseMove(buttonLoc.x + 8, buttonLoc.y + 8); + robot.mouseMove(buttonLoc.x + button.getWidth() / 2, + buttonLoc.y + button.getHeight() / 2); robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); } public static void createUI() { - frame = new JFrame(); + frame = new JFrame("JSplitPaneKeyboardNavigationTest"); panel = new JPanel(); panel.setLayout(new BorderLayout()); leftButton = new JButton("Left Button"); @@ -175,13 +188,14 @@ public static void createUI() { bottomButton = new JButton("Bottom Button"); panel.add(topButton, BorderLayout.NORTH); panel.add(bottomButton, BorderLayout.SOUTH); - final JSplitPane splitPane2 = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, rightButton1, rightButton2); - final JSplitPane splitPane1 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, leftButton, splitPane2); + final JSplitPane splitPane2 = new JSplitPane(JSplitPane.VERTICAL_SPLIT, + true, rightButton1, rightButton2); + final JSplitPane splitPane1 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, + true, leftButton, splitPane2); panel.add(splitPane1, BorderLayout.CENTER); frame.setContentPane(panel); frame.setSize(200, 200); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - frame.pack(); frame.setAlwaysOnTop(true); frame.setLocationRelativeTo(null); frame.setVisible(true); @@ -213,7 +227,6 @@ private static boolean setLookAndFeel(String lafName) { private static void disposeFrame() { if (frame != null) { frame.dispose(); - frame = null; } } diff --git a/test/jdk/javax/swing/JSplitPane/8132123/bug8132123.html b/test/jdk/javax/swing/JSplitPane/8132123/bug8132123.html deleted file mode 100644 index d65bd7bb4e168..0000000000000 --- a/test/jdk/javax/swing/JSplitPane/8132123/bug8132123.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - -Verify that JSplitPane uses high-resolution system icons for the one-touch expanding -buttons on HiDPI displays. - -If the display does not support HiDPI mode press PASS. - -1. Run the test on HiDPI Display. -2. Check that the one-touch expanding buttons on the JSplitPane are painted -correctly. If so, press PASS, else press FAIL. - - - - - diff --git a/test/jdk/javax/swing/JSplitPane/8132123/bug8132123.java b/test/jdk/javax/swing/JSplitPane/8132123/bug8132123.java deleted file mode 100644 index 75b651de776b7..0000000000000 --- a/test/jdk/javax/swing/JSplitPane/8132123/bug8132123.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -import java.awt.Color; -import javax.swing.JApplet; -import javax.swing.JPanel; -import javax.swing.JSplitPane; -import javax.swing.SwingUtilities; - -/* @test - * @bug 8132123 - * @summary MultiResolutionCachedImage unnecessarily creates base image - * to get its size - * @author Alexander Scherbatiy - * @run applet/manual=yesno bug8132123.html - */ -public class bug8132123 extends JApplet { - - @Override - public void init() { - SwingUtilities.invokeLater(() -> { - JPanel left = new JPanel(); - left.setBackground(Color.GRAY); - JPanel right = new JPanel(); - right.setBackground(Color.GRAY); - JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, - left, right); - splitPane.setOneTouchExpandable(true); - getContentPane().add(splitPane); - }); - } -} diff --git a/test/jdk/javax/swing/JSplitPane/bug4749792.java b/test/jdk/javax/swing/JSplitPane/bug4749792.java new file mode 100644 index 0000000000000..1d9823073b72d --- /dev/null +++ b/test/jdk/javax/swing/JSplitPane/bug4749792.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4749792 + * @requires (os.family == "windows") + * @summary Split pane border is not painted properly + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4749792 + */ + +import java.awt.BorderLayout; +import java.awt.Dimension; + +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JSplitPane; + +public class bug4749792 { + static final String INSTRUCTIONS = """ + If the right/bottom edges of JSplitPane's border is missing then the + test fails. If it is visible, then the test passes. + """; + + static JFrame createUI() { + JFrame frame = new JFrame("JSplitPane Border Test"); + frame.setSize(450, 220); + JPanel left = new JPanel(); + JPanel right = new JPanel(); + left.setPreferredSize(new Dimension(200, 200)); + right.setPreferredSize(new Dimension(200, 200)); + JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, left, right); + frame.add(sp); + + JPanel south = new JPanel(); + south.setPreferredSize(new Dimension(20, 20)); + frame.add(south, BorderLayout.SOUTH); + + JPanel east = new JPanel(); + east.setPreferredSize(new Dimension(20, 20)); + frame.add(east, BorderLayout.EAST); + + return frame; + } + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug4749792 Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug4749792::createUI) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/javax/swing/JSplitPane/bug4820080.java b/test/jdk/javax/swing/JSplitPane/bug4820080.java new file mode 100644 index 0000000000000..0702a3a2f5950 --- /dev/null +++ b/test/jdk/javax/swing/JSplitPane/bug4820080.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Panel; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JSeparator; +import javax.swing.JSplitPane; +import javax.swing.UIManager; + +/* + * @test + * @bug 4820080 7175397 + * @summary RFE: Cannot Change the JSplitPane Divider Color while dragging + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4820080 + */ + +public class bug4820080 { + private static final String INSTRUCTIONS = """ + Drag the dividers of the splitpanes (both top and bottom). If the divider + color is green while dragging then test passes, otherwise test fails. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(bug4820080::createTestUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createTestUI() { + JFrame frame = new JFrame("bug4820080"); + UIManager.put("SplitPaneDivider.draggingColor", Color.GREEN); + + Box box = new Box(BoxLayout.Y_AXIS); + frame.add(box); + + JPanel jleft = new JPanel(); + jleft.setBackground(Color.DARK_GRAY); + jleft.setPreferredSize(new Dimension(100, 100)); + JPanel jright = new JPanel(); + jright.setBackground(Color.DARK_GRAY); + jright.setPreferredSize(new Dimension(100, 100)); + + JSplitPane jsp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, jleft, jright); + jsp.setContinuousLayout(false); + box.add(jsp); + + box.add(Box.createVerticalStrut(5)); + box.add(new JSeparator()); + box.add(Box.createVerticalStrut(5)); + + Panel left = new Panel(); + left.setBackground(Color.DARK_GRAY); + left.setPreferredSize(new Dimension(100, 100)); + Panel right = new Panel(); + right.setBackground(Color.DARK_GRAY); + right.setPreferredSize(new Dimension(100, 100)); + + JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, left, right); + sp.setContinuousLayout(false); + box.add(sp); + frame.pack(); + return frame; + } +} diff --git a/test/jdk/javax/swing/JSplitPane/bug8132123.java b/test/jdk/javax/swing/JSplitPane/bug8132123.java new file mode 100644 index 0000000000000..fd7c73aa4685a --- /dev/null +++ b/test/jdk/javax/swing/JSplitPane/bug8132123.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JSplitPane; + +/* + * @test + * @bug 8132123 + * @summary MultiResolutionCachedImage unnecessarily creates base image + * to get its size + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual/othervm -Dsun.java2d.uiScale=2 bug8132123 + */ + +public class bug8132123 { + + private static final String INSTRUCTIONS = """ + Verify that JSplitPane uses high-resolution system icons for + the one-touch expanding buttons on HiDPI displays. + + If the display does not support HiDPI mode press PASS. + + 1. Run the test on HiDPI Display. + 2. Check that the one-touch expanding buttons on the JSplitPane are painted + correctly. If so, press PASS, else press FAIL. """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("JSplitPane Instructions") + .instructions(INSTRUCTIONS) + .rows(10) + .columns(40) + .testUI(bug8132123::init) + .build() + .awaitAndCheck(); + } + + public static JFrame init() { + JFrame frame = new JFrame("Test SplitPane"); + JPanel left = new JPanel(); + left.setBackground(Color.GRAY); + JPanel right = new JPanel(); + right.setBackground(Color.GRAY); + JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, + left, right); + splitPane.setOneTouchExpandable(true); + frame.setLayout(new BorderLayout()); + frame.setSize(250, 250); + frame.getContentPane().add(splitPane); + return frame; + } +} diff --git a/test/jdk/javax/swing/JTabbedPane/4287208/bug4287208.java b/test/jdk/javax/swing/JTabbedPane/4287208/bug4287208.java new file mode 100644 index 0000000000000..d7dfe01cff0e3 --- /dev/null +++ b/test/jdk/javax/swing/JTabbedPane/4287208/bug4287208.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4287208 + * @summary Tests if JTabbedPane's setEnabledAt properly renders bounds of Tabs + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4287208 +*/ + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; + +public class bug4287208 implements ActionListener { + + static final String INSTRUCTIONS = """ + There are two tabs in the test window. Press the "Test" button 5 times. + If this causes tabs to overlap at any time, the test FAILS, otherwise + the test PASSES. + """; + + static boolean state = true; + static JTabbedPane jtp; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug4287208 Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug4287208::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + JFrame frame = new JFrame("bug4287208"); + + JButton start = new JButton("Test"); + start.addActionListener(new bug4287208()); + JPanel buttonPanel = new JPanel(); + buttonPanel.add(start); + frame.add(buttonPanel,BorderLayout.SOUTH); + + jtp = new JTabbedPane(); + jtp.addTab("Panel One", new JPanel()); + String s = System.getProperty("test.src",".") + + System.getProperty("file.separator") + "duke.gif"; + ImageIcon ii = new ImageIcon(s); + jtp.addTab("Panel Two", ii, new JPanel()); + + frame.add(jtp, BorderLayout.CENTER); + frame.setSize(500, 300); + return frame; + } + + public void actionPerformed(ActionEvent evt) { + jtp.setEnabledAt(0, state); + jtp.setEnabledAt(1, !state); + state = !state; + } + +} diff --git a/test/jdk/javax/swing/JTabbedPane/4287208/duke.gif b/test/jdk/javax/swing/JTabbedPane/4287208/duke.gif new file mode 100644 index 0000000000000..a02a42fd60674 Binary files /dev/null and b/test/jdk/javax/swing/JTabbedPane/4287208/duke.gif differ diff --git a/test/jdk/javax/swing/JTabbedPane/4666224/bug4666224.html b/test/jdk/javax/swing/JTabbedPane/4666224/bug4666224.html deleted file mode 100644 index 9285571bc2b48..0000000000000 --- a/test/jdk/javax/swing/JTabbedPane/4666224/bug4666224.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - bug4666224 - - - -

    bug4666224
    Bug ID: 4666224

    - -

    See the dialog box (usually in upper left corner) for instructions

    - - - - diff --git a/test/jdk/javax/swing/JTabbedPane/4666224/bug4666224.java b/test/jdk/javax/swing/JTabbedPane/4666224/bug4666224.java deleted file mode 100644 index fe2085ac15ff7..0000000000000 --- a/test/jdk/javax/swing/JTabbedPane/4666224/bug4666224.java +++ /dev/null @@ -1,572 +0,0 @@ -/* - * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.awt.*; -import java.awt.event.*; -import javax.swing.*; -/* - * test - * @bug 4666224 -*/ - -public class bug4666224 extends JApplet { - final static int placements[] = { JTabbedPane.LEFT, JTabbedPane.RIGHT, JTabbedPane.TOP, JTabbedPane.BOTTOM }; - private JTabbedPane tabPane; - private JPanel mainPanel; - - public bug4666224() throws Exception { - java.awt.EventQueue.invokeAndWait( () -> { - tabPane = new JTabbedPane(); - tabPane.addMouseListener(new MouseAdapter() { - public void mouseClicked(MouseEvent e) { - Point pt = e.getPoint(); - System.out.println("Index at location: " - + tabPane.indexAtLocation(pt.x, pt.y)); - } - }); - InputMap inputMap = createInputMap(); - SwingUtilities.replaceUIInputMap(getRootPane(), JComponent.WHEN_IN_FOCUSED_WINDOW, inputMap); - ActionMap actionMap = createActionMap(); - SwingUtilities.replaceUIActionMap(getRootPane(), actionMap); - - //setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - tabPane.setTabPlacement(JTabbedPane.TOP); - tabPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); - JPanel panel = new JPanel(); - panel.setPreferredSize(new Dimension(200, 300)); - tabPane.addTab("Number Zero", panel); - panel = new JPanel(); - //panel.requestFocus(); - panel.setPreferredSize(new Dimension(200, 300)); - tabPane.addTab("Number One", panel); - panel = new JPanel(); - panel.setPreferredSize(new Dimension(200, 300)); - tabPane.addTab("Number Two", panel); - panel = new JPanel(); - panel.setPreferredSize(new Dimension(200, 300)); - tabPane.addTab("Number Three", new JColorChooser()); - panel = new JPanel(); - panel.setPreferredSize(new Dimension(200, 300)); - tabPane.addTab("Number Four", panel); - panel = new JPanel(); - panel.setPreferredSize(new Dimension(200, 300)); - tabPane.addTab("Number Five", panel); - panel = new JPanel(); - panel.setPreferredSize(new Dimension(200, 300)); - tabPane.addTab("Number Six", panel); - panel = new JPanel(); - panel.setPreferredSize(new Dimension(200, 300)); - tabPane.addTab("Number Seven", panel); - panel = new JPanel(); - panel.setPreferredSize(new Dimension(200, 300)); - tabPane.addTab("Number Eight", panel); - panel = new JPanel(); - panel.setPreferredSize(new Dimension(200, 300)); - tabPane.addTab("Number Nine", panel); - panel = new JPanel(); - panel.setPreferredSize(new Dimension(200, 300)); - tabPane.addTab("Number Ten", panel); - mainPanel = new JPanel(); - mainPanel.add(tabPane); - - getContentPane().add(mainPanel); - tabPane.requestFocus(); - - - //pack(); - //setVisible(true); - }); - } - - public void init() { - String[][] instructionsSet = - { - { - " Note : Incase of Assertion failure,user can enter", - " remarks by pressing 'Assertion Fail Remarks ' button", - " ", - " You would see an applet with JTabbedPane. Keep the size of applet variable.", - " ", - " ON ALL PLATFORMS", - "1. Click on any of the tabs, focus indicator is visible", - "2. Lose focus on the window by clicking on some other window ", - "3. Focus indicator should disappear", - "4. Regain focus on the window the focus indicator should reappear." , - " If focus doesn't behave as above, ", - " press 'Assertion Fail' else press 'Assertion Pass'", - }, - { - " Note : Incase of Assertion failure,user can enter", - " remarks by pressing 'Assertion Fail Remarks ' button", - " ", - " You would see an applet with JTabbedPane. Keep the size of applet variable.", - " ", - " ON ALL PLATFORMS", - "1. type 'C' to change the tab layout to WRAP_TAB_LAYOUT ", - "2. Lose focus on the window by clicking on some other window ", - "3. Focus indicator should disappear", - "4. Regain focus on the window the focus indicator should reappear." , - " If focus doesn't behave as above, ", - " press 'Assertion Fail' else press 'Assertion Pass'", - }, - - { - " Note : Incase of Assertion failure,user can enter", - " remarks by pressing 'Assertion Fail Remarks ' button", - " ", - " You would see an applet with JTabbedPane. Keep the size of applet variable.", - " ", - " ON ALL PLATFORMS", - "1. type 'R' to align the tabs to the right side ", - "2. Lose focus on the window by clicking on some other window ", - "3. Focus indicator should disappear", - "4. Regain focus on the window the focus indicator should reappear." , - " If focus doesn't behave as above, ", - " press 'Assertion Fail' else press 'Assertion Pass'", - }, - - { - " Note : Incase of Assertion failure,user can enter", - " remarks by pressing 'Assertion Fail Remarks ' button", - " ", - " You would see an applet with JTabbedPane. Keep the size of applet variable.", - " ", - " ON ALL PLATFORMS", - "1. type 'B' to align the tabs to the bottom side ", - "2. Lose focus on the window by clicking on some other window ", - "3. Focus indicator should disappear", - "4. Regain focus on the window the focus indicator should reappear." , - " If focus doesn't behave as above, ", - " press 'Assertion Fail' else press 'Assertion Pass'", - }, - - { - " Note : Incase of Assertion failure,user can enter", - " remarks by pressing 'Assertion Fail Remarks ' button", - " ", - " You would see an applet with JTabbedPane. Keep the size of applet variable.", - " ", - " ON ALL PLATFORMS", - "1. type 'L' to align the tabs to the left side ", - "2. Lose focus on the window by clicking on some other window ", - "3. Focus indicator should disappear", - "4. Regain focus on the window the focus indicator should reappear." , - " If focus doesn't behave as above, ", - " press 'Assertion Fail' else press 'Assertion Pass'", - }, - - - { - " Note : Incase of Assertion failure,user can enter", - " remarks by pressing 'Assertion Fail Remarks ' button", - " ", - " You would see an applet with JTabbedPane. Keep the size of applet variable.", - " ", - " ON ALL PLATFORMS", - "1. type 'T' to align the tabs to the top side ", - "2. Lose focus on the window by clicking on some other window ", - "3. Focus indicator should disappear", - "4. Regain focus on the window the focus indicator should reappear." , - " If focus doesn't behave as above, ", - " press 'Assertion Fail' else press 'Assertion Pass'", - }, - - { - " Note : Incase of Assertion failure,user can enter", - " remarks by pressing 'Assertion Fail Remarks ' button", - " ", - " You would see an applet with JTabbedPane. Keep the size of applet variable.", - " ", - " ON ALL PLATFORMS", - "1. type 'B' to align the tabs to the bottom side ", - "2. Lose focus on the window by clicking on some other window ", - "3. Focus indicator should disappear", - "4. Regain focus on the window the focus indicator should reappear." , - " If focus doesn't behave as above, ", - " press 'Assertion Fail' else press 'Assertion Pass'", - }, - - { - " Note : Incase of Assertion failure,user can enter", - " remarks by pressing 'Assertion Fail Remarks ' button", - " ", - " You would see an applet with JTabbedPane. Keep the size of applet variable.", - " ", - " ON ALL PLATFORMS", - "1. type 'R' to align the tabs to the right side ", - "2. Lose focus on the window by clicking on some other window ", - "3. Focus indicator should disappear", - "4. Regain focus on the window the focus indicator should reappear." , - " If focus doesn't behave as above, ", - " press 'Assertion Fail' else press 'Assertion Pass'", - } - }; - - String[] exceptionsSet = - { - "Focus painted incorrectly in tabbed pane(SCROLL_TAB_LAYOUT) when tabs aligned to the TOP of the window", - "Focus painted incorrectly in tabbed pane(SCROLL_TAB_LAYOUT) when tabs aligned to the RIGHT of the window", - "Focus painted incorrectly in tabbed pane(SCROLL_TAB_LAYOUT) when tabs aligned to the BOTTOM of the window", - "Focus painted incorrectly in tabbed pane(SCROLL_TAB_LAYOUT) when tabs aligned to the LEFT of the window", - "Focus painted incorrectly in tabbed pane(WRAP_TAB_LAYOUT) when tabs aligned to the LEFT of the window", - "Focus painted incorrectly in tabbed pane(WRAP_TAB_LAYOUT) when tabs aligned to the TOP of the window", - "Focus painted incorrectly in tabbed pane(WRAP_TAB_LAYOUT) when tabs aligned to the BOTTOM of the window", - "Focus painted incorrectly in tabbed pane(WRAP_TAB_LAYOUT) when tabs aligned to the RIGHT of the window" - }; - - Sysout.setInstructionsWithExceptions(instructionsSet,exceptionsSet); - - } - - public void start (){} - - public void destroy(){ - if(Sysout.failStatus()) { - String failMsg = Sysout.getFailureMessages(); - failMsg = failMsg.replace('\n',' '); - throw new RuntimeException(failMsg); - }// End destroy - } - - protected InputMap createInputMap() { - return LookAndFeel.makeComponentInputMap(getRootPane(), new Object[] { - "R", "right", - "L", "left", - "T", "top", - "B", "bottom", - "C", "changeLayout", - "D", "dump" - }); - } - - protected ActionMap createActionMap() { - ActionMap map = new ActionMap(); - map.put("right", new RotateAction(JTabbedPane.RIGHT)); - map.put("left", new RotateAction(JTabbedPane.LEFT)); - map.put("top", new RotateAction(JTabbedPane.TOP)); - map.put("bottom", new RotateAction(JTabbedPane.BOTTOM)); - map.put("changeLayout", new ChangeLayoutAction()); - map.put("dump", new DumpAction()); - return map; - } - - private class RotateAction extends AbstractAction { - private int placement; - public RotateAction(int placement) { - this.placement = placement; - } - - public void actionPerformed(ActionEvent e) { - tabPane.setTabPlacement(placement); - } - } - - private class ChangeLayoutAction extends AbstractAction { - private boolean a = true; - public void actionPerformed(ActionEvent e) { - if (a) { - tabPane.setTabLayoutPolicy(JTabbedPane.WRAP_TAB_LAYOUT); - a = false; - } else { - tabPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); - a = true; - } - } - } - - private class DumpAction extends AbstractAction { - public void actionPerformed(ActionEvent e) { - for (int i = 0; i < tabPane.getTabCount(); i++) { - System.out.println("Tab: " + i + " " - + tabPane.getUI().getTabBounds(tabPane, i)); - } - } - } -} - - -/**************************************************** - Standard Test Machinery - DO NOT modify anything below -- it's a standard - chunk of code whose purpose is to make user - interaction uniform, and thereby make it simpler - to read and understand someone else's test. - ****************************************************/ - -/** - This is part of the standard test machinery. - It creates a dialog (with the instructions), and is the interface - for sending text messages to the user. - To print the instructions, send an array of strings to Sysout.createDialog - WithInstructions method. Put one line of instructions per array entry. - To display a message for the tester to see, simply call Sysout.println - with the string to be displayed. - This mimics System.out.println but works within the test harness as well - as standalone. - */ - -class Sysout - { - private static TestDialog dialog; - - public static void createDialogWithInstructions( String[] instructions ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - dialog.printInstructions( instructions ); - dialog.show(); - println( "Any messages for the tester will display here." ); - } - - public static void createDialog( ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - String[] defInstr = { "Instructions will appear here. ", "" } ; - dialog.printInstructions( defInstr ); - dialog.show(); - println( "Any messages for the tester will display here." ); - } - - - public static void printInstructions( String[] instructions ) - { - dialog.printInstructions( instructions ); - } - - - public static void println( String messageIn ) - { - dialog.displayMessage( messageIn ); - } - - public static void setInstructionsWithExceptions(String instructionsSet[][], - String exceptionsSet[]) { - createDialogWithInstructions(instructionsSet[0]); - dialog.setInstructions(instructionsSet); - dialog.setExceptionMessages(exceptionsSet); - } - - public static String getFailureMessages() { - return dialog.failureMessages; - } - - public static boolean failStatus() { - return dialog.failStatus; - } - - }// Sysout class - -/** - This is part of the standard test machinery. It provides a place for the - test instructions to be displayed, and a place for interactive messages - to the user to be displayed. - To have the test instructions displayed, see Sysout. - To have a message to the user be displayed, see Sysout. - Do not call anything in this dialog directly. - */ -class TestDialog extends Dialog - { - - TextArea instructionsText; - TextArea messageText; - int maxStringLength = 70; - - Panel assertPanel; - Button assertPass,assertFail,remarks; - HandleAssert handleAssert; - boolean failStatus=false; - int instructionCounter=0; - String instructions[][]; - int exceptionCounter=0; - String exceptionMessages[]; - String failureMessages="
    "; - String remarksMessage=null; - RemarksDialog remarksDialog; - - //DO NOT call this directly, go through Sysout - public TestDialog( Frame frame, String name ) - { - super( frame, name ); - int scrollBoth = TextArea.SCROLLBARS_BOTH; - instructionsText = new TextArea( "", 14, maxStringLength, scrollBoth ); - add( "North", instructionsText ); - - messageText = new TextArea( "", 3, maxStringLength, scrollBoth ); - add("Center", messageText); - - assertPanel = new Panel(new FlowLayout()); - assertPass=new Button("Assertion Pass"); - assertPass.setName("Assertion Pass"); - assertFail=new Button("Assertion Fail"); - assertFail.setName("Assertion Fail"); - remarks = new Button("Assertion Fail Remarks"); - remarks.setEnabled(false); - remarks.setName("Assertion Remarks"); - assertPanel.add(assertPass); - assertPanel.add(assertFail); - assertPanel.add(remarks); - handleAssert = new HandleAssert(); - assertPass.addActionListener(handleAssert); - assertFail.addActionListener(handleAssert); - remarks.addActionListener(handleAssert); - add("South",assertPanel); - pack(); - - show(); - }// TestDialog() - - //DO NOT call this directly, go through Sysout - public void printInstructions( String[] instructions ) - { - //Clear out any current instructions - instructionsText.setText( "" ); - - //Go down array of instruction strings - - String printStr, remainingStr; - for( int i=0; i < instructions.length; i++ ) - { - //chop up each into pieces maxSringLength long - remainingStr = instructions[ i ]; - while( remainingStr.length() > 0 ) - { - //if longer than max then chop off first max chars to print - if( remainingStr.length() >= maxStringLength ) - { - //Try to chop on a word boundary - int posOfSpace = remainingStr. - lastIndexOf( ' ', maxStringLength - 1 ); - - if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1; - - printStr = remainingStr.substring( 0, posOfSpace + 1 ); - remainingStr = remainingStr.substring( posOfSpace + 1 ); - } - //else just print - else - { - printStr = remainingStr; - remainingStr = ""; - } - - instructionsText.append( printStr + "\n" ); - - }// while - - }// for - - }//printInstructions() - - //DO NOT call this directly, go through Sysout - public void displayMessage( String messageIn ) - { - messageText.append( messageIn + "\n" ); - } - - public void emptyMessage() { - messageText.setText(""); - } - - public void setInstructions(String insStr[][]) { - instructions=insStr; - } - - public void setExceptionMessages(String exceptionMessages[]) { - this.exceptionMessages=exceptionMessages; - } - - class HandleAssert implements ActionListener { - public void actionPerformed(ActionEvent ae) { - if(ae.getSource()==remarks) { - remarksDialog = new RemarksDialog(TestDialog.this, - "Assertion Remarks Dialog",true); - remarks.setEnabled(false); - if(remarksMessage!=null) - failureMessages+=". User Remarks : "+remarksMessage; - } - else { - if(instructionCounter"+ - exceptionMessages[exceptionCounter]; - } - } - exceptionCounter++; - } - } - } - - class RemarksDialog extends Dialog implements ActionListener{ - Panel rootPanel,remarksPanel; - TextArea textarea; - Button addRemarks,cancelRemarks; - public RemarksDialog(Dialog owner,String title,boolean modal) { - super(owner,title,modal); - rootPanel = new Panel(new BorderLayout()); - remarksPanel = new Panel(new FlowLayout()); - textarea = new TextArea(5,30); - addRemarks=new Button("Add Remarks"); - addRemarks.addActionListener(this); - cancelRemarks = new Button("Cancel Remarks"); - cancelRemarks.addActionListener(this); - remarksPanel.add(addRemarks); - remarksPanel.add(cancelRemarks); - rootPanel.add(textarea,"Center"); - rootPanel.add(remarksPanel,"South"); - add(rootPanel); - setBounds(150,150,400,200); - setVisible(true); - } - - public void actionPerformed(ActionEvent ae) { - remarksMessage=null; - if(ae.getSource()==addRemarks) { - String msg = textarea.getText().trim(); - if (msg.length()>0) - remarksMessage=msg; - } - dispose(); - } - - } - -}// TestDialog class diff --git a/test/jdk/javax/swing/JTabbedPane/8007563/Test8007563.java b/test/jdk/javax/swing/JTabbedPane/8007563/Test8007563.java deleted file mode 100644 index 25d3ad41eca59..0000000000000 --- a/test/jdk/javax/swing/JTabbedPane/8007563/Test8007563.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.awt.*; -import java.util.ArrayList; -import java.util.concurrent.CountDownLatch; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JTabbedPane; - -import static javax.swing.UIManager.*; -import static javax.swing.SwingUtilities.*; - -/* - * @test - * @key headful - * @bug 8007563 - * @summary Tests JTabbedPane background - * @author Sergey Malenkov - */ - -public class Test8007563 implements Runnable { - private static final ArrayList LIST = new ArrayList<>(); - private static final LookAndFeelInfo[] INFO = getInstalledLookAndFeels(); - private static final CountDownLatch LATCH = new CountDownLatch(INFO.length); - private static Robot ROBOT; - - public static void main(String[] args) throws Exception { - ROBOT = new Robot(); - invokeLater(new Test8007563()); - LATCH.await(); - if (!LIST.isEmpty()) { - throw new Error(LIST.toString()); - } - } - - private static void addOpaqueError(boolean opaque) { - LIST.add(getLookAndFeel().getName() + " opaque=" + opaque); - } - - private static boolean updateLookAndFeel() { - int index = (int) LATCH.getCount() - 1; - if (index >= 0) { - try { - LookAndFeelInfo info = INFO[index]; - System.err.println("L&F: " + info.getName()); - setLookAndFeel(info.getClassName()); - return true; - } catch (Exception exception) { - exception.printStackTrace(); - } - } - return false; - } - - private JFrame frame; - private JTabbedPane pane; - - public void run() { - if (this.frame == null) { - if (!updateLookAndFeel()) { - return; - } - this.pane = new JTabbedPane(); - this.pane.setOpaque(false); - this.pane.setBackground(Color.RED); - for (int i = 0; i < 3; i++) { - this.pane.addTab("Tab " + i, new JLabel("Content area " + i)); - } - this.frame = new JFrame(getClass().getSimpleName()); - this.frame.getContentPane().setBackground(Color.BLUE); - this.frame.add(this.pane); - this.frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - this.frame.setSize(400, 200); - this.frame.setLocationRelativeTo(null); - this.frame.setVisible(true); - } else { - Point point = new Point(this.pane.getWidth() - 2, 2); - convertPointToScreen(point, this.pane); - Color actual = ROBOT.getPixelColor(point.x, point.y); - - boolean opaque = this.pane.isOpaque(); - Color expected = opaque - ? this.pane.getBackground() - : this.frame.getContentPane().getBackground(); - - if (!expected.equals(actual)){ - addOpaqueError(opaque); - } - if (!opaque) { - this.pane.setOpaque(true); - this.pane.repaint(); - } else { - this.frame.dispose(); - this.frame = null; - this.pane = null; - LATCH.countDown(); - } - - } - SecondaryLoop secondaryLoop = - Toolkit.getDefaultToolkit().getSystemEventQueue() - .createSecondaryLoop(); - new Thread() { - @Override - public void run() { - try { - Thread.sleep(200); - } catch (InterruptedException e) { - } - secondaryLoop.exit(); - invokeLater(Test8007563.this); - } - }.start(); - secondaryLoop.enter(); - } -} diff --git a/test/jdk/javax/swing/JTabbedPane/TestJTabbedPaneBackgroundColor.java b/test/jdk/javax/swing/JTabbedPane/TestJTabbedPaneBackgroundColor.java new file mode 100644 index 0000000000000..cc4ca41802cdf --- /dev/null +++ b/test/jdk/javax/swing/JTabbedPane/TestJTabbedPaneBackgroundColor.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Robot; +import java.util.ArrayList; + +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JTabbedPane; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; + +/* + * @test + * @key headful + * @bug 8007563 + * @summary Tests JTabbedPane background + */ + +public class TestJTabbedPaneBackgroundColor { + private static ArrayList lafList = new ArrayList<>(); + private static JFrame frame; + private static JTabbedPane pane; + private static Robot robot; + private static volatile Dimension dim; + private static volatile Point loc; + private static volatile boolean isOpaque; + private static volatile Color c1 = null; + private static volatile Color c2 = null; + + public static void main(String[] args) throws Exception { + robot = new Robot(); + + for (UIManager.LookAndFeelInfo laf : + UIManager.getInstalledLookAndFeels()) { + System.out.println("Testing: " + laf.getName()); + + try { + SwingUtilities.invokeAndWait(() -> { + setLookAndFeel(laf); + createAndShowUI(); + }); + robot.waitForIdle(); + robot.delay(500); + + SwingUtilities.invokeAndWait(() -> { + loc = pane.getLocationOnScreen(); + dim = pane.getSize(); + }); + + loc = new Point(loc.x + dim.width - 2, loc.y + 2); + doTesting(loc, laf); + + SwingUtilities.invokeAndWait(() -> { + if (!pane.isOpaque()) { + pane.setOpaque(true); + pane.repaint(); + } + }); + robot.waitForIdle(); + robot.delay(500); + + doTesting(loc, laf); + + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + if (!lafList.isEmpty()) { + throw new RuntimeException(lafList.toString()); + } + } + + private static void setLookAndFeel(UIManager.LookAndFeelInfo laf) { + try { + UIManager.setLookAndFeel(laf.getClassName()); + } catch (UnsupportedLookAndFeelException ignored) { + System.out.println("Unsupported LAF: " + laf.getClassName()); + } catch (ClassNotFoundException | InstantiationException + | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + private static void createAndShowUI() { + pane = new JTabbedPane(); + pane.setOpaque(false); + pane.setBackground(Color.RED); + for (int i = 0; i < 3; i++) { + pane.addTab("Tab " + i, new JLabel("Content area " + i)); + } + frame = new JFrame("Test Background Color"); + frame.getContentPane().setBackground(Color.BLUE); + frame.add(pane); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + frame.setSize(400, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private static void doTesting(Point p, UIManager.LookAndFeelInfo laf) throws Exception { + SwingUtilities.invokeAndWait(() -> { + isOpaque = pane.isOpaque(); + c1 = pane.getBackground(); + c2 = frame.getContentPane().getBackground(); + }); + Color actual = robot.getPixelColor(p.x, p.y); + Color expected = isOpaque ? c1 : c2; + + if (!expected.equals(actual)) { + System.out.println("Expected Color : " + expected); + System.out.println("Actual Color : " + actual); + addOpaqueError(laf.getName(), isOpaque); + } + } + + private static void addOpaqueError(String lafName, boolean opaque) { + lafList.add(lafName + " opaque=" + opaque); + } +} diff --git a/test/jdk/javax/swing/JTabbedPane/bug4273320.java b/test/jdk/javax/swing/JTabbedPane/bug4273320.java new file mode 100644 index 0000000000000..d1bcca743b5ec --- /dev/null +++ b/test/jdk/javax/swing/JTabbedPane/bug4273320.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4273320 + * @summary JTabbedPane.setTitleAt() should refresh when using HTML text + * @key headful +*/ + +import java.awt.BorderLayout; +import java.awt.Rectangle; +import java.awt.Robot; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; +import javax.swing.SwingUtilities; +import javax.swing.plaf.TabbedPaneUI; + +public class bug4273320 { + + static JFrame frame; + static volatile JTabbedPane tabs; + + static final String PLAIN = "Plain"; + static final String HTML = "A fairly long HTML text label"; + + public static void main(String[] args) throws Exception { + try { + SwingUtilities.invokeAndWait(bug4273320::createUI); + + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(1000); + + TabbedPaneUI ui = tabs.getUI(); + Rectangle origSize = ui.getTabBounds(tabs, 0); + + SwingUtilities.invokeAndWait(() -> { + tabs.setTitleAt(0, HTML); + }); + robot.waitForIdle(); + robot.delay(1000); + + Rectangle newSize = ui.getTabBounds(tabs, 0); + // The tab should be resized larger if the longer HTML text is added + System.out.println("orig = " + origSize.width + " x " + origSize.height); + System.out.println("new = " + newSize.width + " x " + newSize.height); + if (origSize.width >= newSize.width) { + throw new RuntimeException("Tab text is not updated."); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + static void createUI() { + frame = new JFrame("bug4273320"); + tabs = new JTabbedPane(); + JPanel panel = new JPanel(); + tabs.addTab(PLAIN, panel); + frame.getContentPane().add(tabs, BorderLayout.CENTER); + frame.setSize(500, 300); + frame.setVisible(true); + } +} diff --git a/test/jdk/javax/swing/JTabbedPane/bug4287268.java b/test/jdk/javax/swing/JTabbedPane/bug4287268.java new file mode 100644 index 0000000000000..8f7156b93e056 --- /dev/null +++ b/test/jdk/javax/swing/JTabbedPane/bug4287268.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4287268 + * @summary Tests if setIconAt(index,Icon) does not set Tab's disabled icon + * @key headful +*/ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.image.BufferedImage; +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; +import javax.swing.SwingUtilities; + +public class bug4287268 { + + static JFrame frame; + static volatile JTabbedPane jtp; + + public static void main(String[] args) throws Exception { + try { + SwingUtilities.invokeAndWait(bug4287268::createUI); + + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(1000); + + Point point = jtp.getLocationOnScreen(); + int width = jtp.getWidth(); + int height = jtp.getHeight(); + Rectangle r = new Rectangle(point.x, point.y, width, height); + BufferedImage cap = robot.createScreenCapture(r); + + int red = Color.red.getRGB(); + for (int x = 0; x < cap.getWidth(); x++) { + for (int y = 0; y < cap.getHeight(); y++) { + int rgb = cap.getRGB(x, y); + if (rgb == red) { + try { + javax.imageio.ImageIO.write(cap, "png", new java.io.File("cap.png")); + } catch (Exception ee) { + } + throw new RuntimeException("Test failed : found red"); + } + } + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + static void createUI() { + frame = new JFrame("bug4287268"); + jtp = new JTabbedPane(); + JPanel panel = new JPanel(); + jtp.add("Panel One", panel); + int size = 64; + BufferedImage img = new BufferedImage(size, size, BufferedImage.TYPE_INT_RGB); + Graphics g = img.createGraphics(); + g.setColor(Color.red); + g.fillRect(0, 0, size, size); + ImageIcon ii = new ImageIcon(img); + jtp.setIconAt(0, ii); + jtp.setEnabledAt(0, false); + frame.getContentPane().add(jtp, BorderLayout.CENTER); + frame.pack(); + frame.setVisible(true); + } +} diff --git a/test/jdk/javax/swing/JTabbedPane/bug4362226.java b/test/jdk/javax/swing/JTabbedPane/bug4362226.java new file mode 100644 index 0000000000000..5339de29119ae --- /dev/null +++ b/test/jdk/javax/swing/JTabbedPane/bug4362226.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4362226 + * @summary JTabbedPane's HTML title should have proper offsets + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4362226 +*/ + +import java.awt.BorderLayout; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; +import javax.swing.UIManager; +import javax.swing.plaf.metal.MetalLookAndFeel; + +public class bug4362226 { + + static final String PLAIN = "Label"; + static final String HTML = "Label"; + + static final String INSTRUCTIONS = """ + The test window contains a JTabbedPane with two tabs. + The titles for both tabs should look similar and drawn with the same fonts. + The text of the tabs should start in a position that is offset from the left + boundary of the tab, so there is clear space between them. + If there is no space, then the test FAILS. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug4362226 Test Instructions") + .instructions(INSTRUCTIONS) + .columns(60) + .testUI(bug4362226::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + try { + UIManager.setLookAndFeel(new MetalLookAndFeel()); + } catch (Exception e) { + } + JFrame frame = new JFrame("bug4362226"); + JTabbedPane tabs = new JTabbedPane(); + tabs.addTab(PLAIN, new JPanel()); + tabs.addTab(HTML, new JPanel()); + frame.add(tabs, BorderLayout.CENTER); + frame.setSize(500, 300); + return frame; + } +} diff --git a/test/jdk/javax/swing/JTabbedPane/bug4499556.java b/test/jdk/javax/swing/JTabbedPane/bug4499556.java new file mode 100644 index 0000000000000..f9150b339e2ff --- /dev/null +++ b/test/jdk/javax/swing/JTabbedPane/bug4499556.java @@ -0,0 +1,281 @@ +/* + Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 4499556 + * @summary Use arbitrary (J)Components as JTabbedPane tab labels. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4499556 +*/ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.BorderFactory; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JTabbedPane; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; + +public class bug4499556 { + + static final String INSTRUCTIONS = """ + The test window contains a tabbedPane with 4 tabs. + + Tab #0 without any tabComponent, just a title. + Tab #1 with a JLabel and a little JButton wrapped into JPanel + Tab #2 with a JButton (Delete #1) as a tabComponent + Tab #3 with a JTextField as a tabComponent + + Check that tabbedPane and all tabComponents are shown properly + for different tabLayout and tabPlacement policies, + (you can change them with help of settings in the right panel), + and for different looks and feels (you can change L&F by using the L&F menu) + + Remove tab #1 by clicking the Button labeled Delete #1 and then re-insert it + by clicking "Insert #1". Check that it works - ie Delete #1 is restored. + + If everything displays and behaves as described, the test PASSES, otherwise it FAILS. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(bug4499556::createUI) + .build() + .awaitAndCheck(); + } + + static volatile JTabbedPane pane; + static volatile JFrame frame; + + static JFrame createUI() { + frame = new JFrame("bug4499556"); + pane = getTabbedPane(); + frame.add(pane); + frame.add(getRightPanel(), BorderLayout.EAST); + JMenu menu = new JMenu("L&F Menu"); + JMenuItem platformItem = new JMenuItem("Platform L&F"); + JMenuItem nimbusItem = new JMenuItem("Nimbus L&F"); + JMenuItem metalItem = new JMenuItem("Metal L&F"); + menu.add(platformItem); + menu.add(nimbusItem); + menu.add(metalItem); + JMenuBar menuBar = new JMenuBar(); + menuBar.add(menu); + frame.setJMenuBar(menuBar); + platformItem.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + setPlatformLAF(); + } + }); + metalItem.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + setMetalLAF(); + } + }); + + nimbusItem.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + setNimbusLAF(); + } + }); + frame.setSize(500, 500); + return frame; + } + + static JTabbedPane getTabbedPane() { + final JTabbedPane pane = new JTabbedPane(); + + pane.add("Title", new JLabel("")); + + addCompoundTab(pane); + + pane.add("Title", new JLabel("")); + pane.add("Title", new JLabel("")); + + final JButton button = new JButton("Delete #1"); + pane.setTabComponentAt(2, button); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + if (pane.getTabCount() == 4) { + pane.remove(1); + button.setText("Insert #1"); + } else { + addCompoundTab(pane); + button.setText("Delete #1"); + } + } + }); + + JTextField tf = new JTextField("JTextField", 7); + pane.setTabComponentAt(3, tf); + + return pane; + } + + static JComponent getRightPanel() { + JComponent ret = new Box(BoxLayout.Y_AXIS); + ret.setBorder(BorderFactory.createTitledBorder("Properties")); + ret.setPreferredSize(new Dimension(100, 0)); + final JCheckBox checkBox = new JCheckBox(); + JPanel temp = new JPanel(); + temp.add(new JLabel("Scrollable")); + temp.add(checkBox); + pane.setTabLayoutPolicy(JTabbedPane.WRAP_TAB_LAYOUT); + checkBox.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + if (checkBox.isSelected()) { + pane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); + } else { + pane.setTabLayoutPolicy(JTabbedPane.WRAP_TAB_LAYOUT); + } + } + }); + checkBox.doClick(); + ret.add(temp); + ButtonGroup group = new ButtonGroup(); + temp = new JPanel(); + JRadioButton topRadio = new JRadioButton("Top"); + temp.add(topRadio); + topRadio.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + pane.setTabPlacement(JTabbedPane.TOP); + } + }); + ret.add(temp); + temp = new JPanel(); + JRadioButton bottomRadio = new JRadioButton("Bottom"); + temp.add(bottomRadio); + bottomRadio.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + pane.setTabPlacement(JTabbedPane.BOTTOM); + } + }); + ret.add(temp); + temp = new JPanel(); + JRadioButton leftRadio = new JRadioButton("Left"); + temp.add(leftRadio); + leftRadio.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + pane.setTabPlacement(JTabbedPane.LEFT); + } + }); + ret.add(temp); + temp = new JPanel(); + JRadioButton rightRadio = new JRadioButton("Right"); + temp.add(rightRadio); + rightRadio.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + pane.setTabPlacement(JTabbedPane.RIGHT); + } + }); + ret.add(temp); + group.add(topRadio); + group.add(bottomRadio); + group.add(leftRadio); + group.add(rightRadio); + return ret; + } + + + + static void addCompoundTab(final JTabbedPane pane) { + JLabel label = new JLabel("JLabel"); + label.setOpaque(true); + JPanel testPanel = new JPanel(); + JLabel comp = new JLabel("JLabel"); + testPanel.add(comp); + JButton closeButton = new CloseButton(); + closeButton.setPreferredSize(new Dimension(10, 10)); + testPanel.add(closeButton); + testPanel.setOpaque(false); + pane.insertTab("Test", null, new JLabel(""), "", 1); + pane.setTabComponentAt(1, testPanel); + } + + static class CloseButton extends JButton { + public CloseButton() { + final Object[] options = {"Fine"}; + addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + JOptionPane.showOptionDialog(null, + "How are you ?", "Hello !", JOptionPane.YES_OPTION, + JOptionPane.QUESTION_MESSAGE, null, options, null); + } + }); + } + + protected void paintComponent(Graphics g) { + super.paintComponent(g); + g.setColor(Color.black); + g.drawLine(0, 0, getWidth()-1, getHeight()-1); + g.drawLine(0, getHeight()-1, getWidth()-1, 0); + } + } + + static boolean setLAF(String laf) { + try { + UIManager.setLookAndFeel(laf); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + SwingUtilities.updateComponentTreeUI(frame); + return true; + } + + static final boolean setPlatformLAF() { + return setLAF(UIManager.getSystemLookAndFeelClassName()); + } + + static final boolean setNimbusLAF() { + return setLAF("javax.swing.plaf.nimbus.NimbusLookAndFeel"); + } + + static final boolean setMetalLAF() { + return setLAF("javax.swing.plaf.metal.MetalLookAndFeel"); + } +} diff --git a/test/jdk/javax/swing/JTabbedPane/bug4613811.java b/test/jdk/javax/swing/JTabbedPane/bug4613811.java new file mode 100644 index 0000000000000..ecad64e95f8d1 --- /dev/null +++ b/test/jdk/javax/swing/JTabbedPane/bug4613811.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4613811 + * @summary Scrollable Buttons of JTabbedPane don't + * get enabled or disabled on selecting tab + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4613811 + */ + +import java.awt.BorderLayout; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JTabbedPane; + +public class bug4613811 { + private static final String INSTRUCTIONS = """ + Select different tabs and check that the scrollable + buttons are correctly enabled and disabled. + + When the very first tab (Tab 1) is fully visible + On macOS: + the left arrow button should NOT be visible. + + On other platforms: + the left arrow button should be disabled. + + If the last tab (Tab 5) is fully visible + On macOS: + the right arrow button should NOT be visible. + + On other platforms: + the right arrow button should be disabled. + + If the above is true press PASS else FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(30) + .testUI(bug4613811::createAndShowUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createAndShowUI() { + JFrame frame = new JFrame("bug4613811 - JTabbedPane Test"); + final JTabbedPane tabPane = new JTabbedPane(JTabbedPane.TOP, + JTabbedPane.SCROLL_TAB_LAYOUT); + for (int i = 1; i <= 5; i++) { + tabPane.addTab("TabbedPane: Tab " + i, null, new JLabel("Tab " + i)); + } + frame.add(tabPane, BorderLayout.CENTER); + frame.setResizable(false); + frame.setSize(400, 200); + return frame; + } +} diff --git a/test/jdk/javax/swing/JTabbedPane/bug4666224.java b/test/jdk/javax/swing/JTabbedPane/bug4666224.java new file mode 100644 index 0000000000000..0ade8002d8c85 --- /dev/null +++ b/test/jdk/javax/swing/JTabbedPane/bug4666224.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Dimension; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import javax.swing.AbstractAction; +import javax.swing.ActionMap; +import javax.swing.InputMap; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; +import javax.swing.LookAndFeel; +import javax.swing.SwingUtilities; + +/* @test + * @bug 4666224 + * @summary Tests that focus indicator is painted properly in JTabbedPane + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4666224 + */ + +public class bug4666224 { + private static JTabbedPane tabPane; + private static JFrame frame; + + private static final String INSTRUCTIONS = """ + ON ALL PLATFORMS except macos where pt.6 is not applicable + 1. Click on any of the tabs, focus indicator is visible. + 2. Lose focus on the window by clicking on some other window. + 3. Focus indicator should disappear + 4. Regain focus on the window by pressing the tab, + the focus indicator should reappear. + 5. If focus doesn't behave as above, + press 'Fail' else press 'Pass'. + 6. Type 'C' to change the tab layout to WRAP_TAB_LAYOUT + and repeat from step 1 to 5. + 7. Type 'R' to align the tabs to the right side and repeat + from step 1 to 5. + 8. Type 'B' to align the tabs to the bottom side and repeat + from step 1 to 5. + 9. Type 'L' to align the tabs to the left side and repeat + from step 1 to 5. + 10. Type 'T' to align the tabs to the top side and repeat + from step 1 to 5."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("JTabbedPane Instructions") + .instructions(INSTRUCTIONS) + .rows(20) + .columns(40) + .testUI(bug4666224::createAndShowUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createAndShowUI() { + frame = new JFrame("Test JTabbedPane"); + tabPane = new JTabbedPane(); + tabPane.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + Point pt = e.getPoint(); + System.out.println("Index at location: " + + tabPane.indexAtLocation(pt.x, pt.y)); + } + }); + InputMap inputMap = createInputMap(); + SwingUtilities.replaceUIInputMap(frame.getRootPane(), JComponent.WHEN_IN_FOCUSED_WINDOW, inputMap); + ActionMap actionMap = createActionMap(); + SwingUtilities.replaceUIActionMap(frame.getRootPane(), actionMap); + + tabPane.setTabPlacement(JTabbedPane.TOP); + tabPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); + + for (int i = 0; i <= 10; i++) { + JPanel panel = new JPanel(); + panel.setPreferredSize(new Dimension(500, 200)); + tabPane.addTab("Tab " + i, panel); + } + JPanel mainPanel = new JPanel(); + mainPanel.add(tabPane); + + frame.setSize(600, 300); + frame.getContentPane().add(mainPanel); + tabPane.requestFocus(); + return frame; + } + + protected static InputMap createInputMap() { + return LookAndFeel.makeComponentInputMap(frame.getRootPane(), new Object[] { + "R", "right", + "L", "left", + "T", "top", + "B", "bottom", + "C", "changeLayout", + "D", "dump" + }); + } + + protected static ActionMap createActionMap() { + ActionMap map = new ActionMap(); + map.put("right", new RotateAction(JTabbedPane.RIGHT)); + map.put("left", new RotateAction(JTabbedPane.LEFT)); + map.put("top", new RotateAction(JTabbedPane.TOP)); + map.put("bottom", new RotateAction(JTabbedPane.BOTTOM)); + map.put("changeLayout", new ChangeLayoutAction()); + map.put("dump", new DumpAction()); + return map; + } + + private static class RotateAction extends AbstractAction { + private int placement; + public RotateAction(int placement) { + this.placement = placement; + } + + public void actionPerformed(ActionEvent e) { + tabPane.setTabPlacement(placement); + } + } + + private static class ChangeLayoutAction extends AbstractAction { + private boolean a = true; + public void actionPerformed(ActionEvent e) { + if (a) { + tabPane.setTabLayoutPolicy(JTabbedPane.WRAP_TAB_LAYOUT); + a = false; + } else { + tabPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); + a = true; + } + } + } + + private static class DumpAction extends AbstractAction { + public void actionPerformed(ActionEvent e) { + for (int i = 0; i < tabPane.getTabCount(); i++) { + System.out.println("Tab: " + i + " " + + tabPane.getUI().getTabBounds(tabPane, i)); + } + } + } +} diff --git a/test/jdk/javax/swing/JTabbedPane/bug4668865.java b/test/jdk/javax/swing/JTabbedPane/bug4668865.java new file mode 100644 index 0000000000000..711ef12f68277 --- /dev/null +++ b/test/jdk/javax/swing/JTabbedPane/bug4668865.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4668865 + * @summary Tests if JTabbedPane's setEnabledAt properly renders bounds of Tabs + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4668865 +*/ + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import javax.swing.AbstractAction; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JTabbedPane; +import javax.swing.JTextField; + +public class bug4668865 { + + static final String INSTRUCTIONS = """ + This tests that tooltips are shown for all tabs in all orientations, + when it is necessary to scroll to see all the tabs. + Use the buttons to select each orientation (top/bottom/left/right) in turn. + Scroll through the 8 tabs - using the navigation arrows as needed. + Move the mouse over each tab in turn and verify that the matching tooltip is shown + after sufficient hover time. + The test PASSES if the tooltips are shown for all cases, and FAILS otherwise. + """; + + static JTabbedPane tabPane; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug4668865 Test Instructions") + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(bug4668865::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + JFrame frame = new JFrame("bug4668865"); + + tabPane = new JTabbedPane(JTabbedPane.TOP, JTabbedPane.SCROLL_TAB_LAYOUT); + for(int i = 1; i < 9; i++) { + tabPane.addTab("Tab" + i, null, new JTextField("Tab" + i), "Tab" + i); + } + frame.add(tabPane, BorderLayout.CENTER); + + JButton top = new JButton(new AbstractAction() { + public void actionPerformed(ActionEvent e) { + tabPane.setTabPlacement(JTabbedPane.TOP); + } + }); + top.setText("Top"); + frame.add(top, BorderLayout.NORTH); + + JButton bottom = new JButton(new AbstractAction() { + public void actionPerformed(ActionEvent e) { + tabPane.setTabPlacement(JTabbedPane.BOTTOM); + } + }); + bottom.setText("Bottom"); + frame.add(bottom, BorderLayout.SOUTH); + + JButton left = new JButton(new AbstractAction() { + public void actionPerformed(ActionEvent e) { + tabPane.setTabPlacement(JTabbedPane.LEFT); + } + }); + + left.setText("Left"); + frame.add(left, BorderLayout.WEST); + + JButton right = new JButton(new AbstractAction() { + public void actionPerformed(ActionEvent e) { + tabPane.setTabPlacement(JTabbedPane.RIGHT); + } + }); + + right.setText("Right"); + frame.add(right, BorderLayout.EAST); + + frame.setSize(400, 400); + return frame; + } + +} diff --git a/test/jdk/javax/swing/JTabbedPane/bug6259533.java b/test/jdk/javax/swing/JTabbedPane/bug6259533.java new file mode 100644 index 0000000000000..7aaa3e591ce35 --- /dev/null +++ b/test/jdk/javax/swing/JTabbedPane/bug6259533.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6259533 + * @requires (os.family == "windows") + * @summary Win L&F : JTabbedPane should move upwards the tabComponent of the selected tab. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug6259533 +*/ + +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JTabbedPane; +import javax.swing.UIManager; + +public class bug6259533 { + + static final String INSTRUCTIONS = """ + This test is for the Windows LaF only. + + You should see a JTabbedPane with two tabs. + The first tab uses a string and the second tab has a JLabel as a tabComponent + + Select each tab and notice that on selection the tab title + is moved upwards slightly in comparison with the unselected tab + + If that is the observed behaviour, press PASS, press FAIL otherwise. + + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug6259533::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + try { + UIManager.setLookAndFeel( + "com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel"); + } catch (Exception e) { + throw new RuntimeException(e); + } + + JFrame frame = new JFrame("bug6259533"); + JTabbedPane pane = new JTabbedPane(); + pane.add("String Tab", null); + pane.add("Tab 2", null); + JLabel label = new JLabel("JLabel Tab"); + pane.setTabComponentAt(1, label); + frame.add(pane); + frame.setSize(400, 200); + return frame; + } + +} diff --git a/test/jdk/javax/swing/JTable/4222153/bug4222153.html b/test/jdk/javax/swing/JTable/4222153/bug4222153.html deleted file mode 100644 index efa7e634c53de..0000000000000 --- a/test/jdk/javax/swing/JTable/4222153/bug4222153.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - -Click in the upper-left cell and then press TAB two times. -If table cell selection is not on the left cell of the second row -then test fails. - - - diff --git a/test/jdk/javax/swing/JTable/8236907/LastVisibleRow.java b/test/jdk/javax/swing/JTable/8236907/LastVisibleRow.java new file mode 100644 index 0000000000000..6ca33c9635631 --- /dev/null +++ b/test/jdk/javax/swing/JTable/8236907/LastVisibleRow.java @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* + * @test + * @key headful + * @bug 8236907 + * @summary Verifies if JTable last row is visible. + * @run main LastVisibleRow + */ + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.event.InputEvent; +import java.awt.image.BufferedImage; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.lang.reflect.InvocationTargetException; + +import javax.print.attribute.HashPrintRequestAttributeSet; +import javax.print.attribute.PrintRequestAttributeSet; + +import javax.swing.BorderFactory; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JTable; +import javax.swing.SwingUtilities; +import javax.swing.WindowConstants; + +public class LastVisibleRow { + static JFrame frame; + static JTable table; + static Robot testRobot; + + public static void main(String[] args) throws Exception { + Point clkPoint; + try { + testRobot = new Robot(); + + SwingUtilities.invokeAndWait(new Runnable() { + + public void run() { + createAndShowGUI(); + } + }); + testRobot.delay(1000); + testRobot.waitForIdle(); + BufferedImage bufferedImageBefore = testRobot.createScreenCapture(getCaptureRect()); + testRobot.delay(1000); + testRobot.waitForIdle(); + clkPoint = getMousePosition(); + mouseEvents(clkPoint); + testRobot.waitForIdle(); + clearSelect(); + testRobot.waitForIdle(); + BufferedImage bufferedImageAfter = testRobot.createScreenCapture(getCaptureRect()); + testRobot.delay(1000); + + if (!compare(bufferedImageBefore, bufferedImageAfter)) { + throw new RuntimeException("Test Case Failed!!"); + } + } finally { + if (frame != null) SwingUtilities.invokeAndWait(() -> frame.dispose()); + } + } + + /* + * + * Get clickable screen point for particular row and column of a table + * param row Row Number + * param column Column Number + * return Point + */ + private static Point getCellClickPoint(final int row, final int column) { + Point result; + + Rectangle rect = table.getCellRect(row, column, false); + Point point = new Point(rect.x + rect.width / 2, + rect.y + rect.height / 2); + SwingUtilities.convertPointToScreen(point, table); + result = point; + + return result; + } + + private static void createAndShowGUI() { + final PrintRequestAttributeSet printReqAttr = new HashPrintRequestAttributeSet(); + printReqAttr.add(javax.print.attribute.standard.OrientationRequested.LANDSCAPE); + frame = new JFrame(); + frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + Container contentPane = frame.getContentPane(); + JPanel centerPane = new JPanel(new BorderLayout()); + centerPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + JPanel tablePaneContainer = new JPanel(new BorderLayout()); + JPanel tablePane = new JPanel(new BorderLayout()); + table = new JTable(new Object[][]{{"row_1_col_1", "row_1_col_2", "row_1_col_3"}, {"row_2_col_1", "row_2_col_2", "row_2_col_3"}, {"row_3_col_1", "row_3_col_2", "row_3_col_3"}, {"row_4_col_1", "row_4_col_2", "row_4_col_3"}}, new String[]{"Col1", "Col2", "Col3"}); + table.setPreferredSize(new Dimension(0, (table.getRowHeight() * 3))); + + tablePane.add(table.getTableHeader(), BorderLayout.NORTH); + tablePane.add(table, BorderLayout.CENTER); + tablePaneContainer.add(tablePane, BorderLayout.CENTER); + centerPane.add(tablePaneContainer, BorderLayout.NORTH); + contentPane.add(centerPane, BorderLayout.CENTER); + frame.setSize(400, 120); + frame.setVisible(true); + frame.setLocationRelativeTo(null); + + } + + /* + * + * mouseEvents for last row click + */ + + private static void mouseEvents(Point clkPnt) { + testRobot.mouseMove(clkPnt.x, clkPnt.y); + testRobot.delay(50); + testRobot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + testRobot.delay(50); + testRobot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + testRobot.delay(50); + } + /* + * + * getMousePosition Actions for last row click + * returns Point + * throws Exception + */ + + private static Point getMousePosition() throws Exception { + final Point[] clickPoint = new Point[1]; + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + clickPoint[0] = getCellClickPoint(2, 0); + } + }); + return clickPoint[0]; + } + + /* + * + * Clears the selected table row + * throws Exception + */ + + private static void clearSelect() throws Exception { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + table.getSelectionModel().clearSelection(); + table.setFocusable(false); + } + }); + } + + /* + * getCaptureRect Method - To Compute the Rectangle for + * Screen Capturing the Last Row for comparison + * return Rectangle + */ + + private static Rectangle getCaptureRect() throws InterruptedException, InvocationTargetException { + final Rectangle[] captureRect = new Rectangle[1]; + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + Rectangle cellRect = table.getCellRect(2, 0, true); + Point point = new Point(cellRect.x, cellRect.y); + SwingUtilities.convertPointToScreen(point, table); + + captureRect[0] = new Rectangle(point.x, point.y, table.getColumnCount() * cellRect.width, cellRect.height); + } + }); + return captureRect[0]; + } + + /* + * Compare method - to compare two images. + * param bufferedImage1 Buffered Image Before click + * param bufferedImage2 Buffered Image After click + * return Boolean + */ + + static Boolean compare(BufferedImage bufferedImage1, BufferedImage bufferedImage2) { + if (bufferedImage1.getWidth() == bufferedImage2.getWidth() + && bufferedImage1.getHeight() == bufferedImage2.getHeight()) { + for (int x = 0; x < bufferedImage1.getWidth(); x++) { + for (int y = 0; y < bufferedImage1.getHeight(); y++) { + if (bufferedImage1.getRGB(x, y) != bufferedImage2.getRGB(x, y)) { + return false; + } + } + } + } else { + return false; + } + return true; + } +} diff --git a/test/jdk/javax/swing/JTable/CheckBoxFirstClick.java b/test/jdk/javax/swing/JTable/CheckBoxFirstClick.java new file mode 100644 index 0000000000000..91f8930103eea --- /dev/null +++ b/test/jdk/javax/swing/JTable/CheckBoxFirstClick.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.Dimension; +import javax.swing.DefaultCellEditor; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.border.BevelBorder; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; +import javax.swing.table.TableModel; + +/* + * @test + * @bug 4115930 + * @summary Verify checkboxes in the table respond to first click. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual CheckBoxFirstClick + */ + +public class CheckBoxFirstClick { + private static final String INSTRUCTIONS = """ + Click over the checkbox in the table. It should change state + on the first click. If not - press 'fail'. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(CheckBoxFirstClick::createTestUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createTestUI() { + JFrame frame = new JFrame("ListSizeBug"); + + // Take the dummy data from SwingSet. + final String[] names = {"First Name", "Last Name", "Favorite Color", + "Favorite Number", "Vegetarian"}; + final Object[][] data = { + {"Mark", "Andrews", "Red", 2, true}, + {"Tom", "Ball", "Blue", 99, false}, + {"Alan", "Chung", "Green", 838, false}, + {"Jeff", "Dinkins", "Turquois", 8, true}, + {"Amy", "Fowler", "Yellow", 3, false}, + {"Brian", "Gerhold", "Green", 0, false}, + {"James", "Gosling", "Pink", 21, false}, + {"David", "Karlton", "Red", 1, false}, + {"Dave", "Kloba", "Yellow", 14, false}, + {"Peter", "Korn", "Purple", 12, false}, + {"Phil", "Milne", "Purple", 3, false}, + {"Dave", "Moore", "Green", 88, false}, + {"Hans", "Muller", "Maroon", 5, false}, + {"Rick", "Levenson", "Blue", 2, false}, + {"Tim", "Prinzing", "Blue", 22, false}, + {"Chester", "Rose", "Black", 0, false}, + {"Ray", "Ryan", "Gray", 77, false}, + {"Georges", "Saab", "Red", 4, false}, + {"Willie", "Walker", "Phthalo Blue", 4, false}, + {"Kathy", "Walrath", "Blue", 8, false}, + {"Arnaud", "Weber", "Green", 44, false} + }; + + // Create a model of the data. + TableModel dataModel = new AbstractTableModel() { + // These methods always need to be implemented. + public int getColumnCount() { + return names.length; + } + + public int getRowCount() { + return data.length; + } + + public Object getValueAt(int row, int col) { + return data[row][col]; + } + + // The default implementations of these methods in + // AbstractTableModel would work, but we can refine them. + public String getColumnName(int column) { + return names[column]; + } + + public Class getColumnClass(int c) { + return getValueAt(0, c).getClass(); + } + + public boolean isCellEditable(int row, int col) { + return true; + } + + public void setValueAt(Object aValue, int row, int column) { + System.out.println("Setting value to: " + aValue); + data[row][column] = aValue; + } + }; + + // Create the table + JTable tableView = new JTable(dataModel); + // Turn off auto-resizing so that we can set column sizes programmatically. + // In this mode, all columns will get their preferred widths, as set blow. + tableView.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + + // Create a combo box to show that you can use one in a table. + JComboBox comboBox = new JComboBox(); + comboBox.addItem("Red"); + comboBox.addItem("Orange"); + comboBox.addItem("Yellow"); + comboBox.addItem("Green"); + comboBox.addItem("Blue"); + comboBox.addItem("Indigo"); + comboBox.addItem("Violet"); + + TableColumn colorColumn = tableView.getColumn("Favorite Color"); + // Use the combo box as the editor in the "Favorite Color" column. + colorColumn.setCellEditor(new DefaultCellEditor(comboBox)); + + // Set a pink background and tooltip for the Color column renderer. + DefaultTableCellRenderer colorColumnRenderer = new DefaultTableCellRenderer(); + colorColumnRenderer.setBackground(Color.pink); + colorColumnRenderer.setToolTipText("Click for combo box"); + colorColumn.setCellRenderer(colorColumnRenderer); + + // Set a tooltip for the header of the colors column. + TableCellRenderer headerRenderer = colorColumn.getHeaderRenderer(); + if (headerRenderer instanceof DefaultTableCellRenderer) + ((DefaultTableCellRenderer) headerRenderer).setToolTipText("Hi Mom!"); + + // Set the width of the "Vegetarian" column. + TableColumn vegetarianColumn = tableView.getColumn("Vegetarian"); + vegetarianColumn.setPreferredWidth(100); + + // Show the values in the "Favorite Number" column in different colors. + TableColumn numbersColumn = tableView.getColumn("Favorite Number"); + DefaultTableCellRenderer numberColumnRenderer = new DefaultTableCellRenderer() { + public void setValue(Object value) { + int cellValue = (value instanceof Number) ? ((Number) value).intValue() : 0; + setForeground((cellValue > 30) ? Color.black : Color.red); + setText((value == null) ? "" : value.toString()); + } + }; + numberColumnRenderer.setHorizontalAlignment(JLabel.RIGHT); + numbersColumn.setCellRenderer(numberColumnRenderer); + numbersColumn.setPreferredWidth(110); + + // Finish setting up the table. + JScrollPane scrollpane = new JScrollPane(tableView); + scrollpane.setBorder(new BevelBorder(BevelBorder.LOWERED)); + scrollpane.setPreferredSize(new Dimension(430, 200)); + + frame.add(scrollpane); + frame.setSize(500, 200); + return frame; + } +} diff --git a/test/jdk/javax/swing/JTable/IllegalStateException.java b/test/jdk/javax/swing/JTable/IllegalStateException.java new file mode 100644 index 0000000000000..427b81ab2d90b --- /dev/null +++ b/test/jdk/javax/swing/JTable/IllegalStateException.java @@ -0,0 +1,187 @@ +/* + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.Dimension; +import javax.swing.DefaultCellEditor; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.border.BevelBorder; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; +import javax.swing.table.TableModel; + +/* + * @test + * @bug 4133143 + * @summary Illegal State exception in ComboBox editor in table + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual IllegalStateException + */ + +public class IllegalStateException { + private static final String INSTRUCTIONS = """ + Click on a cell in the first column, delete the contents but leave the editor with focus. + Click on the third column popping up a combo box. + Verify that the text editor loses focus. + If it does, press "pass", otherwise press "fail". + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(IllegalStateException::createTestUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createTestUI() { + JFrame frame = new JFrame("IllegalStateException"); + + // Take the dummy data from SwingSet. + final String[] names = {"First Name", "Last Name", "Favorite Color", + "Favorite Number", "Vegetarian"}; + final Object[][] data = { + {"Mark", "Andrews", "Red", 2, true}, + {"Tom", "Ball", "Blue", 99, false}, + {"Alan", "Chung", "Green", 838, false}, + {"Jeff", "Dinkins", "Turquois", 8, true}, + {"Amy", "Fowler", "Yellow", 3, false}, + {"Brian", "Gerhold", "Green", 0, false}, + {"James", "Gosling", "Pink", 21, false}, + {"David", "Karlton", "Red", 1, false}, + {"Dave", "Kloba", "Yellow", 14, false}, + {"Peter", "Korn", "Purple", 12, false}, + {"Phil", "Milne", "Purple", 3, false}, + {"Dave", "Moore", "Green", 88, false}, + {"Hans", "Muller", "Maroon", 5, false}, + {"Rick", "Levenson", "Blue", 2, false}, + {"Tim", "Prinzing", "Blue", 22, false}, + {"Chester", "Rose", "Black", 0, false}, + {"Ray", "Ryan", "Gray", 77, false}, + {"Georges", "Saab", "Red", 4, false}, + {"Willie", "Walker", "Phthalo Blue", 4, false}, + {"Kathy", "Walrath", "Blue", 8, false}, + {"Arnaud", "Weber", "Green", 44, false} + }; + + // Create a model of the data. + TableModel dataModel = new AbstractTableModel() { + // These methods always need to be implemented. + public int getColumnCount() { + return names.length; + } + + public int getRowCount() { + return data.length; + } + + public Object getValueAt(int row, int col) { + return data[row][col]; + } + + // The default implementations of these methods in + // AbstractTableModel would work, but we can refine them. + public String getColumnName(int column) { + return names[column]; + } + + public Class getColumnClass(int c) { + return getValueAt(0, c).getClass(); + } + + public boolean isCellEditable(int row, int col) { + return true; + } + + public void setValueAt(Object aValue, int row, int column) { + System.out.println("Setting value to: " + aValue); + data[row][column] = aValue; + } + }; + + // Create the table + JTable tableView = new JTable(dataModel); + // Turn off auto-resizing so that we can set column sizes programmatically. + // In this mode, all columns will get their preferred widths, as set blow. + tableView.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + + // Create a combo box to show that you can use one in a table. + JComboBox comboBox = new JComboBox(); + comboBox.addItem("Red"); + comboBox.addItem("Orange"); + comboBox.addItem("Yellow"); + comboBox.addItem("Green"); + comboBox.addItem("Blue"); + comboBox.addItem("Indigo"); + comboBox.addItem("Violet"); + + TableColumn colorColumn = tableView.getColumn("Favorite Color"); + // Use the combo box as the editor in the "Favorite Color" column. + colorColumn.setCellEditor(new DefaultCellEditor(comboBox)); + + // Set a pink background and tooltip for the Color column renderer. + DefaultTableCellRenderer colorColumnRenderer = new DefaultTableCellRenderer(); + colorColumnRenderer.setBackground(Color.pink); + colorColumnRenderer.setToolTipText("Click for combo box"); + colorColumn.setCellRenderer(colorColumnRenderer); + + // Set a tooltip for the header of the colors column. + TableCellRenderer headerRenderer = colorColumn.getHeaderRenderer(); + if (headerRenderer instanceof DefaultTableCellRenderer) + ((DefaultTableCellRenderer) headerRenderer).setToolTipText("Hi Mom!"); + + // Set the width of the "Vegetarian" column. + TableColumn vegetarianColumn = tableView.getColumn("Vegetarian"); + vegetarianColumn.setPreferredWidth(100); + + // Show the values in the "Favorite Number" column in different colors. + TableColumn numbersColumn = tableView.getColumn("Favorite Number"); + DefaultTableCellRenderer numberColumnRenderer = new DefaultTableCellRenderer() { + public void setValue(Object value) { + int cellValue = (value instanceof Number) ? ((Number) value).intValue() : 0; + setForeground((cellValue > 30) ? Color.black : Color.red); + setText((value == null) ? "" : value.toString()); + } + }; + numberColumnRenderer.setHorizontalAlignment(JLabel.RIGHT); + numbersColumn.setCellRenderer(numberColumnRenderer); + numbersColumn.setPreferredWidth(110); + + // Finish setting up the table. + JScrollPane scrollpane = new JScrollPane(tableView); + scrollpane.setBorder(new BevelBorder(BevelBorder.LOWERED)); + scrollpane.setPreferredSize(new Dimension(430, 200)); + + frame.add(scrollpane); + frame.setSize(500, 200); + return frame; + } +} diff --git a/test/jdk/javax/swing/JTable/TAB/TAB.java b/test/jdk/javax/swing/JTable/InternationalCharacters.java similarity index 67% rename from test/jdk/javax/swing/JTable/TAB/TAB.java rename to test/jdk/javax/swing/JTable/InternationalCharacters.java index 8c4f7035772b9..1d8fc2c4576e0 100644 --- a/test/jdk/javax/swing/JTable/TAB/TAB.java +++ b/test/jdk/javax/swing/JTable/InternationalCharacters.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,17 +22,16 @@ */ import java.awt.Color; -import java.awt.Container; import java.awt.Dimension; - +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; import javax.swing.DefaultCellEditor; -import javax.swing.JApplet; import javax.swing.JComboBox; +import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JScrollPane; import javax.swing.JTable; -import javax.swing.SwingUtilities; -import javax.swing.UIManager; import javax.swing.border.BevelBorder; import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; @@ -40,43 +39,59 @@ import javax.swing.table.TableColumn; import javax.swing.table.TableModel; -/** +/* * @test - * @bug 4128521 - * @summary - * Tabbing test - * @author milne - * @run applet/manual=yesno TAB.html + * @bug 4179066 + * @summary Tests that JTable prints AltGr characters (~\@|{}[]²µ³) + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual InternationalCharacters */ -public class TAB extends JApplet -{ - static void initTest(Container contentPane) - { + +public class InternationalCharacters { + private static final String INSTRUCTIONS = """ + Double-click an entry in the JTable. + Press Alt-Gr or Option with any key to type an international character. + Verify that the international character appears in the table. + If it does, press "pass", otherwise press "fail". + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(InternationalCharacters::createTestUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createTestUI() { + JFrame frame = new JFrame("InternationalCharacters test"); // Take the dummy data from SwingSet. final String[] names = {"First Name", "Last Name", "Favorite Color", - "Favorite Number", "Vegetarian"}; + "Favorite Number", "Vegetarian"}; final Object[][] data = { - {"Mark", "Andrews", "Red", new Integer(2), new Boolean(true)}, - {"Tom", "Ball", "Blue", new Integer(99), new Boolean(false)}, - {"Alan", "Chung", "Green", new Integer(838), new Boolean(false)}, - {"Jeff", "Dinkins", "Turquois", new Integer(8), new Boolean(true)}, - {"Amy", "Fowler", "Yellow", new Integer(3), new Boolean(false)}, - {"Brian", "Gerhold", "Green", new Integer(0), new Boolean(false)}, - {"James", "Gosling", "Pink", new Integer(21), new Boolean(false)}, - {"David", "Karlton", "Red", new Integer(1), new Boolean(false)}, - {"Dave", "Kloba", "Yellow", new Integer(14), new Boolean(false)}, - {"Peter", "Korn", "Purple", new Integer(12), new Boolean(false)}, - {"Phil", "Milne", "Purple", new Integer(3), new Boolean(false)}, - {"Dave", "Moore", "Green", new Integer(88), new Boolean(false)}, - {"Hans", "Muller", "Maroon", new Integer(5), new Boolean(false)}, - {"Rick", "Levenson", "Blue", new Integer(2), new Boolean(false)}, - {"Tim", "Prinzing", "Blue", new Integer(22), new Boolean(false)}, - {"Chester", "Rose", "Black", new Integer(0), new Boolean(false)}, - {"Ray", "Ryan", "Gray", new Integer(77), new Boolean(false)}, - {"Georges", "Saab", "Red", new Integer(4), new Boolean(false)}, - {"Willie", "Walker", "Phthalo Blue", new Integer(4), new Boolean(false)}, - {"Kathy", "Walrath", "Blue", new Integer(8), new Boolean(false)}, - {"Arnaud", "Weber", "Green", new Integer(44), new Boolean(false)} + {"Mark", "Andrews", "Red", 2, true}, + {"Tom", "Ball", "Blue", 99, false}, + {"Alan", "Chung", "Green", 838, false}, + {"Jeff", "Dinkins", "Turquois", 8, true}, + {"Amy", "Fowler", "Yellow", 3, false}, + {"Brian", "Gerhold", "Green", 0, false}, + {"James", "Gosling", "Pink", 21, false}, + {"David", "Karlton", "Red", 1, false}, + {"Dave", "Kloba", "Yellow", 14, false}, + {"Peter", "Korn", "Purple", 12, false}, + {"Phil", "Milne", "Purple", 3, false}, + {"Dave", "Moore", "Green", 88, false}, + {"Hans", "Muller", "Maroon", 5, false}, + {"Rick", "Levenson", "Blue", 2, false}, + {"Tim", "Prinzing", "Blue", 22, false}, + {"Chester", "Rose", "Black", 0, false}, + {"Ray", "Ryan", "Gray", 77, false}, + {"Georges", "Saab", "Red", 4, false}, + {"Willie", "Walker", "Phthalo Blue", 4, false}, + {"Kathy", "Walrath", "Blue", 8, false}, + {"Arnaud", "Weber", "Green", 44, false} }; // Create a model of the data. @@ -95,7 +110,7 @@ public void setValueAt(Object aValue, int row, int column) { System.out.println("Setting value to: " + aValue); data[row][column] = aValue; } - }; + }; // Create the table JTable tableView = new JTable(dataModel); @@ -150,20 +165,8 @@ public void setValue(Object value) { scrollpane.setBorder(new BevelBorder(BevelBorder.LOWERED)); scrollpane.setPreferredSize(new Dimension(430, 200)); - contentPane.add(scrollpane); - } - - - public void init() { - SwingUtilities.invokeLater(() -> { - try { - UIManager.setLookAndFeel( - "javax.swing.plaf.metal.MetalLookAndFeel"); - } catch (Exception e) { - throw new RuntimeException(e); - } - - initTest(getContentPane()); - }); + frame.add(scrollpane); + frame.setSize(500, 200); + return frame; } } diff --git a/test/jdk/javax/swing/JTable/JTableOrientationNavTest/JTableOrientationNavTest.java b/test/jdk/javax/swing/JTable/JTableOrientationNavTest/JTableOrientationNavTest.java new file mode 100644 index 0000000000000..653ae205993b9 --- /dev/null +++ b/test/jdk/javax/swing/JTable/JTableOrientationNavTest/JTableOrientationNavTest.java @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* @test + * @bug 8024624 + * @key headful + * @requires (os.family != "mac") + * @summary Tests some of JTable's key navigation + * @run main JTableOrientationNavTest + */ + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.ComponentOrientation; +import java.awt.Rectangle; +import java.awt.Robot; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; + +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.ListSelectionModel; +import javax.swing.SwingUtilities; +import javax.swing.table.DefaultTableModel; +import javax.swing.table.TableModel; + +import static java.awt.event.KeyEvent.VK_CONTROL; +import static java.awt.event.KeyEvent.VK_LEFT; +import static java.awt.event.KeyEvent.VK_PAGE_DOWN; +import static java.awt.event.KeyEvent.VK_PAGE_UP; +import static java.awt.event.KeyEvent.VK_RIGHT; +import static java.awt.event.KeyEvent.VK_SHIFT; + +public class JTableOrientationNavTest { + private static JFrame frame; + private static JTable table; + private static JScrollPane sp; + private static Robot robot; + private static boolean ltr = true; + + public static void main(String[] args) throws InterruptedException, InvocationTargetException { + try { + robot = new Robot(); + robot.setAutoDelay(100); + robot.setAutoWaitForIdle(true); + + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.add(createContentPane()); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + }); + + executeTest(); + setupRTL(); + executeTest(); + + System.out.println("Passed"); + } catch (Exception e) { + e.printStackTrace(); + } finally { + SwingUtilities.invokeAndWait(() -> frame.dispose()); + } + } + + private static TableModel getTableModel1() { + String[] columnNames = {"Column 0", "Column 1", "Column 2", + "Column 3", "Column 4"}; + String[][] data = {{"Table 00, 00", "Table 00, 01", "Table 00, 02", + "Table 00, 03", "Table 00, 04"}, + {"Table 01, 00", "Table 01, 01", "Table 01, 02", + "Table 01, 03", "Table 01, 04"}, + {"Table 02, 00", "Table 02, 01", "Table 02, 02", + "Table 02, 03", "Table 02, 04"}, + {"Table 03, 00", "Table 03, 01", "Table 03, 02", + "Table 03, 03", "Table 03, 04"}, + {"Table 04, 00", "Table 04, 01", "Table 04, 02", + "Table 04, 03", "Table 04, 04"}, + {"Table 05, 00", "Table 05, 01", "Table 05, 02", + "Table 05, 03", "Table 05, 04"}}; + + return new DefaultTableModel(data, columnNames); + } + + private static TableModel getTableModel2() { + String[] columnNames = new String[30]; + String[][] data = new String[1][30]; + + for (int i = 0; i < columnNames.length; i++) { + columnNames[i] = "Column " + i; + data[0][i] = "Data " + 1; + } + + return new DefaultTableModel(data, columnNames); + } + + private static Component createContentPane() { + table = new JTable(getTableModel1()); + table.setCellSelectionEnabled(true); + table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + JPanel panel = new JPanel(new BorderLayout()); + sp = new JScrollPane(table); + panel.add(sp); + return panel; + } + + private static void executeTest() throws InterruptedException, InvocationTargetException { + SwingUtilities.invokeAndWait(() -> { + table.setRowSelectionInterval(0, 0); + table.setColumnSelectionInterval(0, 0); + checkSelection(0, 0); + }); + + robot.keyPress(ltr ? VK_RIGHT : VK_LEFT); + robot.keyRelease(ltr ? VK_RIGHT : VK_LEFT); + + SwingUtilities.invokeAndWait(() -> checkSelection(1, 1)); + + robot.keyPress(VK_CONTROL); + robot.keyPress(ltr ? VK_RIGHT : VK_LEFT); + robot.keyRelease(ltr ? VK_RIGHT : VK_LEFT); + robot.keyRelease(VK_CONTROL); + + SwingUtilities.invokeAndWait(() -> { + checkSelection(2, 1); + table.setRowSelectionInterval(0, 0); + table.setColumnSelectionInterval(4, 4); + checkSelection(4, 4); + }); + + robot.keyPress(ltr ? VK_LEFT : VK_RIGHT); + robot.keyRelease(ltr ? VK_LEFT : VK_RIGHT); + + SwingUtilities.invokeAndWait(() -> checkSelection(3, 3)); + + robot.keyPress(VK_CONTROL); + robot.keyPress(ltr ? VK_LEFT : VK_RIGHT); + robot.keyRelease(ltr ? VK_LEFT : VK_RIGHT); + robot.keyRelease(VK_CONTROL); + + SwingUtilities.invokeAndWait(() -> { + checkSelection(2, 3); + table.setColumnSelectionInterval(2, 2); + checkSelection(2, 2); + }); + + robot.keyPress(VK_CONTROL); + robot.keyPress(VK_PAGE_UP); + robot.keyRelease(VK_PAGE_UP); + robot.keyRelease(VK_CONTROL); + + SwingUtilities.invokeAndWait(() -> { + checkSelection(0, 0); + table.setColumnSelectionInterval(2, 2); + checkSelection(2, 2); + }); + + robot.keyPress(VK_CONTROL); + robot.keyPress(VK_PAGE_DOWN); + robot.keyRelease(VK_PAGE_DOWN); + robot.keyRelease(VK_CONTROL); + + SwingUtilities.invokeAndWait(() -> { + checkSelection(4, 4); + table.setColumnSelectionInterval(2, 2); + checkSelection(2, 2); + }); + + robot.keyPress(VK_CONTROL); + robot.keyPress(VK_SHIFT); + robot.keyPress(VK_PAGE_UP); + robot.keyRelease(VK_PAGE_UP); + robot.keyRelease(VK_SHIFT); + robot.keyRelease(VK_CONTROL); + + SwingUtilities.invokeAndWait(() -> { + checkSelection(0, 0, 1, 2); + table.setColumnSelectionInterval(2, 2); + checkSelection(2, 2); + }); + + robot.keyPress(VK_CONTROL); + robot.keyPress(VK_SHIFT); + robot.keyPress(VK_PAGE_DOWN); + robot.keyRelease(VK_PAGE_DOWN); + robot.keyRelease(VK_SHIFT); + robot.keyRelease(VK_CONTROL); + + SwingUtilities.invokeAndWait(() -> { + checkSelection(4, 2, 3, 4); + table.setModel(getTableModel2()); + table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + table.setRowSelectionInterval(0, 0); + table.setColumnSelectionInterval(0, 0); + table.scrollRectToVisible(new Rectangle(ltr ? 0 : table.getWidth(), 0, 1, 1)); + checkSelection(0, 0); + }); + + robot.keyPress(VK_CONTROL); + robot.keyPress(VK_PAGE_DOWN); + robot.keyRelease(VK_PAGE_DOWN); + robot.keyRelease(VK_CONTROL); + + SwingUtilities.invokeAndWait(() -> { + checkSelection(6, 6); + table.setColumnSelectionInterval(29, 29); + table.scrollRectToVisible(new Rectangle(ltr ? table.getWidth() : 0, 0, 1, 1)); + checkSelection(29, 29); + }); + + robot.keyPress(VK_CONTROL); + robot.keyPress(VK_PAGE_UP); + robot.keyRelease(VK_PAGE_UP); + robot.keyRelease(VK_CONTROL); + + SwingUtilities.invokeAndWait(() -> checkSelection(23, 23)); + + System.out.println("Done with ltr: " + ltr); + } + + private static void setupRTL() throws InterruptedException, InvocationTargetException { + ltr = false; + SwingUtilities.invokeAndWait(() -> { + table.setModel(getTableModel1()); + table.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS); + sp.applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); + }); + } + + private static void checkSelection(int col, int... allCols) { + int trow = table.getSelectionModel().getLeadSelectionIndex(); + int[] trows = table.getSelectedRows(); + int tcol = table.getColumnModel().getSelectionModel().getLeadSelectionIndex(); + int[] tcols = table.getSelectedColumns(); + + if (trow != 0) { + throw new RuntimeException("Wrong lead row"); + } + + if (trows.length != 1 || trows[0] != 0) { + throw new RuntimeException("Bad row selection"); + } + + if (col != tcol) { + throw new RuntimeException("Wrong lead col"); + } + + if (allCols == null || allCols.length == 0) { + if (tcols.length != 0) { + throw new RuntimeException("Should be no cols selected"); + } + } else { + Arrays.sort(tcols); + Arrays.sort(allCols); + + for (int c : allCols) { + if (Arrays.binarySearch(tcols, c) < 0) { + throw new RuntimeException("Wrong column selection"); + } + } + + for (int c : tcols) { + if (Arrays.binarySearch(allCols, c) < 0) { + throw new RuntimeException("Wrong column selection"); + } + } + } + } + +} diff --git a/test/jdk/javax/swing/JTable/JTableScrollPrintTest.java b/test/jdk/javax/swing/JTable/JTableScrollPrintTest.java new file mode 100644 index 0000000000000..a2bd03081a9bc --- /dev/null +++ b/test/jdk/javax/swing/JTable/JTableScrollPrintTest.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.print.PageFormat; +import java.awt.print.Paper; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.awt.print.PrinterJob; + +import javax.swing.BoxLayout; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.JViewport; +import javax.swing.table.DefaultTableModel; + +/* + * @test + * @key headful + * @bug 8210807 8322140 8322135 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Test to check if JTable can be printed when JScrollPane added to it. + * @run main/manual JTableScrollPrintTest + */ + +public class JTableScrollPrintTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Instructions to Test: + 1. Print table onto Paper/PDF, using the Print Dialog. + 2. If entire table is printed, then the Test is PASS. + 3. If table is partially printed without table cells, + then the Test is FAIL. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows(6) + .columns(35) + .testUI(JTableScrollPrintTest::initialize) + .build() + .awaitAndCheck(); + } + + public static JFrame initialize() { + TestTable testTable = new TestTable(true); + JFrame frame = new JFrame("JTable Print Test"); + PassFailJFrame.addTestWindow(frame); + PassFailJFrame.positionTestWindow(frame, PassFailJFrame.Position.VERTICAL); + frame.add(testTable); + frame.pack(); + frame.setVisible(true); + PrintUtilities printerJob = new PrintUtilities(testTable); + printerJob.print("Test BackingStore Image Print"); + return frame; + } + + public static class TestTable extends JPanel { + public TestTable(Boolean useScrollPane) { + + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + + DefaultTableModel model = new DefaultTableModel(); + model.addColumn("Column 1"); + model.addColumn("Column 2"); + model.addColumn("Column 3"); + model.addColumn("Column 4"); + + for (int row = 1; row <= 5; row++) { + model.addRow(new Object[]{ + "R" + row + " C1", "R" + row + " C2", "R" + row + " C3", "R" + row + " C4"}); + } + + JTable table = new JTable(model); + + if (useScrollPane) { + JScrollPane sp = new JScrollPane(table, + JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, + JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + sp.getViewport().setScrollMode(JViewport.BACKINGSTORE_SCROLL_MODE); + add(sp); + } else { + add(table.getTableHeader()); + add(table); + } + } + } + + static class PrintUtilities implements Printable { + private final Component componentToBePrinted; + + public void printComponent(Component c, String jobname) { + new PrintUtilities(c).print(jobname); + } + + public PrintUtilities(Component componentToBePrinted) { + this.componentToBePrinted = componentToBePrinted; + } + + public void print(String jobname) { + PrinterJob printJob = PrinterJob.getPrinterJob(); + PageFormat pf = printJob.defaultPage(); + pf.setOrientation(PageFormat.PORTRAIT); + + // set margins to 1/2" + Paper p = new Paper(); + p.setImageableArea(36, 36, p.getWidth() - 72, p.getHeight() - 72); + pf.setPaper(p); + + printJob.setPrintable(this, pf); + printJob.setJobName(jobname); + + if (printJob.printDialog()) { + try { + printJob.print(); + } catch (PrinterException pe) { + System.out.println("Error printing: " + pe); + } + } + } + + public int print(Graphics g, PageFormat pageFormat, int pageIndex) { + if (pageIndex > 0) { + return NO_SUCH_PAGE; + } else { + Graphics2D g2d = (Graphics2D)g; + g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY()); + Component c = componentToBePrinted; + c.setSize(c.getPreferredSize()); + + double panelX = c.getWidth(); + double panelY = c.getHeight(); + float imageableX = (float) pageFormat.getImageableWidth() - 1; + float imageableY = (float) pageFormat.getImageableHeight() - 1; + + double xscale = imageableX/panelX; + double yscale = imageableY/panelY; + double optimalScale; + if (xscale < yscale) { + optimalScale = xscale; + } else { + optimalScale = yscale; + } + + if (optimalScale > 1) { + optimalScale = 1; + } + + g2d.scale(optimalScale, optimalScale); + c.paint(g2d); + return PAGE_EXISTS; + } + } + } +} diff --git a/test/jdk/javax/swing/JTable/4222153/bug4222153.java b/test/jdk/javax/swing/JTable/NullTableHeader.java similarity index 64% rename from test/jdk/javax/swing/JTable/4222153/bug4222153.java rename to test/jdk/javax/swing/JTable/NullTableHeader.java index 75871c59974e6..dc62b975d045d 100644 --- a/test/jdk/javax/swing/JTable/4222153/bug4222153.java +++ b/test/jdk/javax/swing/JTable/NullTableHeader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,28 +21,24 @@ * questions. */ -import javax.swing.JApplet; +import javax.swing.JFrame; import javax.swing.JTable; import javax.swing.SwingUtilities; -import javax.swing.UIManager; -/** +/* * @test - * @bug 4222153 - * @author Konstantin Eremin - * @run applet/manual=yesno bug4222153.html + * @bug 4129409 + * @summary Tests that JTable.setTableHeader(null) doesn't break AutoResize + * @key headful + * @run main NullTableHeader */ -public class bug4222153 extends JApplet { - public void init() { - SwingUtilities.invokeLater(() -> { - try { - UIManager.setLookAndFeel( - "javax.swing.plaf.metal.MetalLookAndFeel"); - } catch (Exception e) { - throw new RuntimeException(e); - } - getContentPane().add(new JTable(2, 2)); +public class NullTableHeader { + public static void main(String[] args) throws Exception { + SwingUtilities.invokeAndWait(() -> { + JTable tableView = new JTable(); + tableView.setTableHeader(null); + tableView.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); }); } } diff --git a/test/jdk/javax/swing/JTable/PrintAllPagesTest.java b/test/jdk/javax/swing/JTable/PrintAllPagesTest.java new file mode 100644 index 0000000000000..284fbe57097b8 --- /dev/null +++ b/test/jdk/javax/swing/JTable/PrintAllPagesTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* + * @test + * @bug 8257810 8322135 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Verifies if all pages are printed if scrollRectToVisible is set. + * @run main/manual PrintAllPagesTest + */ +import java.awt.print.PrinterJob; +import java.awt.print.PrinterException; +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.SwingUtilities; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.TableModel; +import javax.swing.WindowConstants; + +public class PrintAllPagesTest { + static JFrame f; + static JTable table; + + static final String INSTRUCTIONS = """ + Note: You must have a printer installed for this test. + If printer is not available, the test passes automatically. + + A JTable with 1000 rows and a print dialog will be shown. + If only 1 page is printed, + then press fail else press pass. + """; + + public static void main(String[] args) throws Exception { + + PrinterJob pj = PrinterJob.getPrinterJob(); + if (pj.getPrintService() == null) { + System.out.println("Printer not configured or available." + + " Test cannot continue."); + PassFailJFrame.forcePass(); + } + + PassFailJFrame passFailJFrame = new PassFailJFrame(INSTRUCTIONS); + + SwingUtilities.invokeAndWait(() -> { + printAllPagesTest(); + // add the test frame to dispose + PassFailJFrame.addTestWindow(f); + + // Arrange the test instruction frame and test frame side by side + PassFailJFrame.positionTestWindow(f, PassFailJFrame.Position.HORIZONTAL); + f.setVisible(true); + + boolean ret; + try { + ret = table.print(); + } catch (PrinterException ex) { + ret = false; + } + if (!ret) { + PassFailJFrame.forceFail("Printing cancelled/failed"); + } + }); + passFailJFrame.awaitAndCheck(); + } + + + private static void printAllPagesTest() { + TableModel dataModel = new AbstractTableModel() { + @Override + public int getColumnCount() { + return 10; + } + + @Override + public int getRowCount() { + return 1000; + } + + @Override + public Object getValueAt(int row, int col) { + return Integer.valueOf(0 == col ? row + 1 : row * col); + } + }; + table = new JTable(dataModel); + JScrollPane scrollpane = new JScrollPane(table); + table.scrollRectToVisible(table.getCellRect(table.getRowCount() - 1, + 0, false)); + + f = new JFrame("Table test"); + f.add(scrollpane); + f.setSize(500, 400); + } +} diff --git a/test/jdk/javax/swing/JTable/ShiftClick.java b/test/jdk/javax/swing/JTable/ShiftClick.java new file mode 100644 index 0000000000000..4d617b5eedaf5 --- /dev/null +++ b/test/jdk/javax/swing/JTable/ShiftClick.java @@ -0,0 +1,187 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.Dimension; +import javax.swing.DefaultCellEditor; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.border.BevelBorder; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; +import javax.swing.table.TableModel; + +/* + * @test + * @bug 4201917 + * @summary Shift Click in table before making selection + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ShiftClick + */ + +public class ShiftClick { + private static final String INSTRUCTIONS = """ + Shift click in the table. Check that all cells + from the first through where you clicked are selected. + If the cells are selected, press pass, otherwise fail. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(ShiftClick::createTestUI) + .logArea(6) + .build() + .awaitAndCheck(); + } + + public static JFrame createTestUI() { + JFrame frame = new JFrame("ShiftClick"); + + // Take the dummy data from SwingSet. + final String[] names = {"First Name", "Last Name", "Favorite Color", + "Favorite Number", "Vegetarian"}; + final Object[][] data = { + {"Mark", "Andrews", "Red", 2, true}, + {"Tom", "Ball", "Blue", 99, false}, + {"Alan", "Chung", "Green", 838, false}, + {"Jeff", "Dinkins", "Turquois", 8, true}, + {"Amy", "Fowler", "Yellow", 3, false}, + {"Brian", "Gerhold", "Green", 0, false}, + {"James", "Gosling", "Pink", 21, false}, + {"David", "Karlton", "Red", 1, false}, + {"Dave", "Kloba", "Yellow", 14, false}, + {"Peter", "Korn", "Purple", 12, false}, + {"Phil", "Milne", "Purple", 3, false}, + {"Dave", "Moore", "Green", 88, false}, + {"Hans", "Muller", "Maroon", 5, false}, + {"Rick", "Levenson", "Blue", 2, false}, + {"Tim", "Prinzing", "Blue", 22, false}, + {"Chester", "Rose", "Black", 0, false}, + {"Ray", "Ryan", "Gray", 77, false}, + {"Georges", "Saab", "Red", 4, false}, + {"Willie", "Walker", "Phthalo Blue", 4, false}, + {"Kathy", "Walrath", "Blue", 8, false}, + {"Arnaud", "Weber", "Green", 44, false} + }; + + // Create a model of the data. + TableModel dataModel = new AbstractTableModel() { + // These methods always need to be implemented. + public int getColumnCount() { + return names.length; + } + + public int getRowCount() { + return data.length; + } + + public Object getValueAt(int row, int col) { + return data[row][col]; + } + + // The default implementations of these methods in + // AbstractTableModel would work, but we can refine them. + public String getColumnName(int column) { + return names[column]; + } + + public Class getColumnClass(int c) { + return getValueAt(0, c).getClass(); + } + + public boolean isCellEditable(int row, int col) { + return true; + } + + public void setValueAt(Object aValue, int row, int column) { + System.out.println("Setting value to: " + aValue); + data[row][column] = aValue; + } + }; + + // Create the table + JTable tableView = new JTable(dataModel); + // Turn off auto-resizing so that we can set column sizes programmatically. + // In this mode, all columns will get their preferred widths, as set blow. + tableView.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + + // Create a combo box to show that you can use one in a table. + JComboBox comboBox = new JComboBox(); + comboBox.addItem("Red"); + comboBox.addItem("Orange"); + comboBox.addItem("Yellow"); + comboBox.addItem("Green"); + comboBox.addItem("Blue"); + comboBox.addItem("Indigo"); + comboBox.addItem("Violet"); + + TableColumn colorColumn = tableView.getColumn("Favorite Color"); + // Use the combo box as the editor in the "Favorite Color" column. + colorColumn.setCellEditor(new DefaultCellEditor(comboBox)); + + // Set a pink background and tooltip for the Color column renderer. + DefaultTableCellRenderer colorColumnRenderer = new DefaultTableCellRenderer(); + colorColumnRenderer.setBackground(Color.pink); + colorColumnRenderer.setToolTipText("Click for combo box"); + colorColumn.setCellRenderer(colorColumnRenderer); + + // Set a tooltip for the header of the colors column. + TableCellRenderer headerRenderer = colorColumn.getHeaderRenderer(); + if (headerRenderer instanceof DefaultTableCellRenderer) + ((DefaultTableCellRenderer) headerRenderer).setToolTipText("Hi Mom!"); + + // Set the width of the "Vegetarian" column. + TableColumn vegetarianColumn = tableView.getColumn("Vegetarian"); + vegetarianColumn.setPreferredWidth(100); + + // Show the values in the "Favorite Number" column in different colors. + TableColumn numbersColumn = tableView.getColumn("Favorite Number"); + DefaultTableCellRenderer numberColumnRenderer = new DefaultTableCellRenderer() { + public void setValue(Object value) { + int cellValue = (value instanceof Number) ? ((Number) value).intValue() : 0; + setForeground((cellValue > 30) ? Color.black : Color.red); + setText((value == null) ? "" : value.toString()); + } + }; + numberColumnRenderer.setHorizontalAlignment(JLabel.RIGHT); + numbersColumn.setCellRenderer(numberColumnRenderer); + numbersColumn.setPreferredWidth(110); + + // Finish setting up the table. + JScrollPane scrollpane = new JScrollPane(tableView); + scrollpane.setBorder(new BevelBorder(BevelBorder.LOWERED)); + scrollpane.setPreferredSize(new Dimension(430, 200)); + + frame.add(scrollpane); + frame.setSize(500, 200); + return frame; + } +} diff --git a/test/jdk/javax/swing/JTable/TAB/TAB.html b/test/jdk/javax/swing/JTable/TAB/TAB.html deleted file mode 100644 index 7c37825365997..0000000000000 --- a/test/jdk/javax/swing/JTable/TAB/TAB.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - Tabbing test - - - -

    Tabbing test

    - - - -

    - Select a cell by double clicking it, press tab. Check that the focus moves to the next cell. - If it does, press "pass", otherwise press "fail". -


    -
    Philip Milne
    - - - - diff --git a/test/jdk/javax/swing/JTable/Tab.java b/test/jdk/javax/swing/JTable/Tab.java new file mode 100644 index 0000000000000..ebda63bc88b75 --- /dev/null +++ b/test/jdk/javax/swing/JTable/Tab.java @@ -0,0 +1,225 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.DefaultCellEditor; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.border.BevelBorder; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; +import javax.swing.table.TableModel; + +/* + * @test + * @bug 4128521 + * @key headful + * @summary Verify focus changes correctly when tab is pressed while editing + * a JTextField in a JTable + * @run main Tab + */ + +public class Tab { + private static Robot robot; + private static JFrame frame; + private static JTable tableView; + private static volatile Point tableLoc; + private static volatile Rectangle cellRect; + private static volatile int selectedRowBeforeTabPress; + private static volatile int selectedColumnBeforeTabPress; + private static volatile int selectedRowAfterTabPress; + private static volatile int selectedColumnAfterTabPress; + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); + robot = new Robot(); + robot.setAutoDelay(50); + try { + SwingUtilities.invokeAndWait(Tab::createAndShowUI); + robot.waitForIdle(); + robot.delay(1000); + + SwingUtilities.invokeAndWait(() -> { + tableLoc = tableView.getLocationOnScreen(); + cellRect = tableView.getCellRect(2, 1, true); + }); + + robot.mouseMove(tableLoc.x + cellRect.x + cellRect.width / 2, + tableLoc.y + cellRect.y + cellRect.height / 2); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(20); + + SwingUtilities.invokeAndWait(() -> { + selectedRowBeforeTabPress = tableView.getSelectedRow(); + selectedColumnBeforeTabPress = tableView.getSelectedColumn(); + }); + + robot.keyPress(KeyEvent.VK_TAB); + robot.keyRelease(KeyEvent.VK_TAB); + robot.waitForIdle(); + robot.delay(20); + + SwingUtilities.invokeAndWait(() -> { + selectedRowAfterTabPress = tableView.getSelectedRow(); + selectedColumnAfterTabPress = tableView.getSelectedColumn(); + }); + + if (selectedRowAfterTabPress != selectedRowBeforeTabPress + && selectedColumnAfterTabPress != (selectedColumnBeforeTabPress + 1)) { + throw new RuntimeException("JTable's cell focus didn't move to next" + + " cell on TAB press"); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + static void createAndShowUI() { + frame = new JFrame("Test JTable's Focus Component"); + // Take the dummy data from SwingSet. + final String[] names = {"First Name", "Last Name", "Favorite Color", + "Favorite Number", "Vegetarian"}; + final Object[][] data = { + {"Mark", "Andrews", "Red", 2, true}, + {"Tom", "Ball", "Blue", 99, false}, + {"Alan", "Chung", "Green", 838, false}, + {"Jeff", "Dinkins", "Turquois", 8, true}, + {"Amy", "Fowler", "Yellow", 3, false}, + {"Brian", "Gerhold", "Green", 0, false}, + {"James", "Gosling", "Pink", 21, false}, + {"David", "Karlton", "Red", 1, false}, + {"Dave", "Kloba", "Yellow", 14, false}, + {"Peter", "Korn", "Purple", 12, false}, + {"Phil", "Milne", "Purple", 3, false}, + {"Dave", "Moore", "Green", 88, false}, + {"Hans", "Muller", "Maroon", 5, false}, + {"Rick", "Levenson", "Blue", 2, false}, + {"Tim", "Prinzing", "Blue", 22, false}, + {"Chester", "Rose", "Black", 0, false}, + {"Ray", "Ryan", "Gray", 77, false}, + {"Georges", "Saab", "Red", 4, false}, + {"Willie", "Walker", "Phthalo Blue", 4, false}, + {"Kathy", "Walrath", "Blue", 8, false}, + {"Arnaud", "Weber", "Green", 44, false} + }; + + // Create a model of the data. + TableModel dataModel = new AbstractTableModel() { + // These methods always need to be implemented. + public int getColumnCount() { return names.length; } + public int getRowCount() { return data.length;} + public Object getValueAt(int row, int col) {return data[row][col];} + + // The default implementations of these methods in + // AbstractTableModel would work, but we can refine them. + public String getColumnName(int column) {return names[column];} + public Class getColumnClass(int c) {return getValueAt(0, c).getClass();} + public boolean isCellEditable(int row, int col) {return true;} + public void setValueAt(Object aValue, int row, int column) { + System.out.println("Setting value to: " + aValue); + data[row][column] = aValue; + } + }; + + // Create the table + tableView = new JTable(dataModel); + // Turn off auto-resizing so that we can set column sizes programmatically. + // In this mode, all columns will get their preferred widths, as set blow. + tableView.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + + // Create a combo box to show that you can use one in a table. + JComboBox comboBox = new JComboBox(); + comboBox.addItem("Red"); + comboBox.addItem("Orange"); + comboBox.addItem("Yellow"); + comboBox.addItem("Green"); + comboBox.addItem("Blue"); + comboBox.addItem("Indigo"); + comboBox.addItem("Violet"); + + TableColumn colorColumn = tableView.getColumn("Favorite Color"); + // Use the combo box as the editor in the "Favorite Color" column. + colorColumn.setCellEditor(new DefaultCellEditor(comboBox)); + + // Set a pink background and tooltip for the Color column renderer. + DefaultTableCellRenderer colorColumnRenderer = new DefaultTableCellRenderer(); + colorColumnRenderer.setBackground(Color.pink); + colorColumnRenderer.setToolTipText("Click for combo box"); + colorColumn.setCellRenderer(colorColumnRenderer); + + // Set a tooltip for the header of the colors column. + TableCellRenderer headerRenderer = colorColumn.getHeaderRenderer(); + if (headerRenderer instanceof DefaultTableCellRenderer) + ((DefaultTableCellRenderer)headerRenderer).setToolTipText("Hi Mom!"); + + // Set the width of the "Vegetarian" column. + TableColumn vegetarianColumn = tableView.getColumn("Vegetarian"); + vegetarianColumn.setPreferredWidth(100); + + // Show the values in the "Favorite Number" column in different colors. + TableColumn numbersColumn = tableView.getColumn("Favorite Number"); + DefaultTableCellRenderer numberColumnRenderer = new DefaultTableCellRenderer() { + public void setValue(Object value) { + int cellValue = (value instanceof Number) ? ((Number)value).intValue() : 0; + setForeground((cellValue > 30) ? Color.black : Color.red); + setText((value == null) ? "" : value.toString()); + } + }; + numberColumnRenderer.setHorizontalAlignment(JLabel.RIGHT); + numbersColumn.setCellRenderer(numberColumnRenderer); + numbersColumn.setPreferredWidth(110); + + // Finish setting up the table. + JScrollPane scrollpane = new JScrollPane(tableView); + scrollpane.setBorder(new BevelBorder(BevelBorder.LOWERED)); + scrollpane.setPreferredSize(new Dimension(430, 200)); + + frame.getContentPane().add(scrollpane); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setVisible(true); + } +} diff --git a/test/jdk/javax/swing/JTable/bug4118307.java b/test/jdk/javax/swing/JTable/bug4118307.java new file mode 100644 index 0000000000000..537e92176efa7 --- /dev/null +++ b/test/jdk/javax/swing/JTable/bug4118307.java @@ -0,0 +1,182 @@ +/* + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.InputEvent; +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.SwingUtilities; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableModel; + +/* + * @test + * @bug 4118307 + * @summary Tests that JTable's cell editor for Number and Date work correctly + * @key headful + * @run main bug4118307 + */ + +public class bug4118307 { + static JFrame frame; + static MyTable tbl; + static Point tableLoc; + static Point p; + private static volatile boolean flag; + static final String[] columnNames = {"Integer", "Double"}; + static final Object[][] data = { + {5, 3.14}, + {10, 2.71}, + {70, 3.14}, + {200, 2.71}, + }; + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + robot.setAutoDelay(250); + SwingUtilities.invokeAndWait(() -> createTestUI()); + robot.waitForIdle(); + robot.delay(1000); + + SwingUtilities.invokeAndWait(() -> { + tableLoc = tbl.getLocationOnScreen(); + p = tbl.getCellRect(0, 0, true).getLocation(); + }); + robot.waitForIdle(); + + robot.mouseMove(tableLoc.x + p.x + 10, tableLoc.y + p.y + 10); + robot.waitForIdle(); + + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + + SwingUtilities.invokeAndWait(() -> + p = tbl.getCellRect(1, 1, true).getLocation()); + robot.waitForIdle(); + + robot.mouseMove(tableLoc.x + p.x + 10, tableLoc.y + p.y + 10); + robot.waitForIdle(); + + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + + SwingUtilities.invokeAndWait(() -> + p = tbl.getCellRect(1, 0, true).getLocation()); + robot.waitForIdle(); + + robot.mouseMove(tableLoc.x + p.x + 10, tableLoc.y + p.y + 10); + robot.waitForIdle(); + + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(5000); + + if (!flag) { + throw new RuntimeException("Test Failed."); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + public static void createTestUI() { + frame = new JFrame("bug4118307"); + MyTableModel myModel = new MyTableModel(); + tbl = new MyTable(myModel); + JScrollPane sp = new JScrollPane(tbl); + flag = true; + + frame.add(sp, BorderLayout.CENTER); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + static class MyTable extends JTable { + public MyTable(TableModel tm) { + super(tm); + } + + public Component prepareRenderer(TableCellRenderer rend, int row, int col) { + try { + return super.prepareRenderer(rend, row, col); + } catch (Exception e) { + e.printStackTrace(); + flag = false; + return null; + } + } + } + + static class MyTableModel extends AbstractTableModel { + public int getColumnCount() { + return columnNames.length; + } + + public int getRowCount() { + return data.length; + } + + @Override + public String getColumnName(int col) { + return columnNames[col]; + } + + public Object getValueAt(int row, int col) { + return data[row][col]; + } + + public Class getColumnClass(int c) { + return getValueAt(0, c).getClass(); + } + + @Override + public boolean isCellEditable(int row, int col) { + return true; + } + + @Override + public void setValueAt(Object value, int row, int col) { + data[row][col] = value; + } + } +} diff --git a/test/jdk/javax/swing/JTable/bug4128506.java b/test/jdk/javax/swing/JTable/bug4128506.java new file mode 100644 index 0000000000000..21273c6f318e2 --- /dev/null +++ b/test/jdk/javax/swing/JTable/bug4128506.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.TableModel; + +/* + * @test + * @bug 4128506 + * @summary Tests that JTable with AUTO_RESIZE_ALL_COLUMNS correctly compute width of columns + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4128506 + */ + +public class bug4128506 { + private static final String INSTRUCTIONS = """ + If the columns of JTable have the same width the test passes, else test fails. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(bug4128506::createTestUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createTestUI() { + final Object[][] data = { + {"cell_1_1", "cell_1_2", "cell_1_3"}, + {"cell_2_1", "cell_2_2", "cell_2_3"}, + {"cell_3_1", "cell_3_2", "cell_3_3"}, + {"cell_4_1", "cell_4_2", "cell_4_3"}, + }; + + TableModel dataModel = new AbstractTableModel() { + public int getColumnCount() { + return 3; + } + + public int getRowCount() { + return data.length; + } + + public Object getValueAt(int row, int col) { + return data[row][col]; + } + }; + + JFrame frame = new JFrame("bug4128506"); + JTable tableView = new JTable(dataModel); + tableView.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); + tableView.getColumnModel().getColumn(1).setMinWidth(5); + JScrollPane scrollpane = new JScrollPane(tableView); + frame.add(scrollpane); + frame.pack(); + return frame; + } +} diff --git a/test/jdk/javax/swing/JTable/bug4129401.java b/test/jdk/javax/swing/JTable/bug4129401.java new file mode 100644 index 0000000000000..37765d34698a3 --- /dev/null +++ b/test/jdk/javax/swing/JTable/bug4129401.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.DefaultCellEditor; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.table.TableColumn; + +/* + * @test + * @bug 4129401 + * @summary Tests that keystroking for combobox cell editor in JTable works + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4129401 + */ + +public class bug4129401 { + private static final String INSTRUCTIONS = """ + 1. Move the mouse cursor to the cell "CELL 2 1", + which contains JComboBox and click left mouse button + to drop down combobox list. + 2. Change selected item in the combobox list + using up and down arrows. + 3. Press Esc. JComboBox drop down list should hide. + If all was successful then test passes, else test fails. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(bug4129401::createTestUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createTestUI() { + Object data[][] = new Object[4][2]; + JComboBox cb = new JComboBox(); + cb.addItem("Item1"); + cb.addItem("Item2"); + cb.addItem("Item3"); + cb.addItem("Item4"); + data[0][0] = "CELL 0 0"; + data[0][1] = "CELL 0 1"; + data[1][0] = "CELL 1 0"; + data[1][1] = "CELL 1 1"; + data[2][0] = "CELL 2 0"; + data[2][1] = "CELL 2 1"; + data[3][0] = "CELL 3 0"; + data[3][1] = "CELL 3 1"; + String[] str = {"Column 0", "Column 1"}; + JTable tbl = new JTable(data, str); + JScrollPane sp = new JScrollPane(tbl); + + TableColumn col = tbl.getColumn("Column 1"); + col.setCellEditor(new DefaultCellEditor(cb)); + + JFrame f = new JFrame("4129401 test"); + f.getContentPane().add(sp); + f.setBounds(100, 100, 300, 300); + return f; + } +} diff --git a/test/jdk/javax/swing/JTable/bug4138158.java b/test/jdk/javax/swing/JTable/bug4138158.java new file mode 100644 index 0000000000000..400b8b1952ef0 --- /dev/null +++ b/test/jdk/javax/swing/JTable/bug4138158.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTable; + +/* + * @test + * @bug 4138158 + * @summary Tests that setAutoscrolls(false) locks autoscroll + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4138158 + */ + +public class bug4138158 { + private static final String INSTRUCTIONS = """ + Move mouse to beginning of table, press left mouse button and drag mouse down + below the frame. If the table isn't scrolled down then test passes. + If the table is scrolled then test fails. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(bug4138158::createTestUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createTestUI() { + JFrame frame = new JFrame("bug4138158"); + JTable table = new JTable(20, 3); + table.setAutoscrolls(false); + JScrollPane sp = new JScrollPane(table); + frame.add(sp); + frame.setSize(200, 200); + return frame; + } +} diff --git a/test/jdk/javax/swing/JTable/bug4139910.java b/test/jdk/javax/swing/JTable/bug4139910.java new file mode 100644 index 0000000000000..c9d6d62cf0c99 --- /dev/null +++ b/test/jdk/javax/swing/JTable/bug4139910.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.table.DefaultTableModel; + +/* + * @test + * @bug 4139910 + * @summary Column resize mouse pointer doesn't display in non-resizable JTable. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4139910 + */ + +public class bug4139910 { + private static final String INSTRUCTIONS = """ + Move mouse pointer to the position between "A" and "B" headers. + If mouse pointer does not change its shape then test passes. If + it does then test fails. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(bug4139910::createTestUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createTestUI() { + JFrame frame = new JFrame("bug4139910"); + + String[] colName = {"A", "B"}; + JTable tbl = new JTable(new DefaultTableModel(colName, 6)); + tbl.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + tbl.getTableHeader().setReorderingAllowed(false); + tbl.getTableHeader().setResizingAllowed(false); + JScrollPane sp = new JScrollPane(tbl); + frame.add(sp); + + frame.pack(); + return frame; + } +} diff --git a/test/jdk/javax/swing/JTable/bug4188504.java b/test/jdk/javax/swing/JTable/bug4188504.java new file mode 100644 index 0000000000000..b58ee7def0387 --- /dev/null +++ b/test/jdk/javax/swing/JTable/bug4188504.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4188504 + * @summary setResizable for specified column. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4188504 + */ + +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTable; + +public class bug4188504 { + private static final String INSTRUCTIONS = """ + 1. A table is displayed with 3 columns - A, B, C. + + 2. Try to resize second column of table (Move mouse to the position + between "B" and "C" headers, press left mouse button and move to + right/left). + PLEASE NOTE: You may be able to swap the columns but make sure the + width of column B stays the same. + + 3. If the second column does not change its width then press PASS + otherwise press FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(createAndShowUI()) + .build() + .awaitAndCheck(); + } + + private static JFrame createAndShowUI() { + JFrame jFrame = new JFrame("bug4188504"); + JTable tableView = new JTable(4, 3); + tableView.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + tableView.getColumnModel().getColumn(1).setResizable(false); + + jFrame.add(new JScrollPane(tableView)); + jFrame.setSize(300, 150); + return jFrame; + } +} diff --git a/test/jdk/javax/swing/JTable/bug4190222.java b/test/jdk/javax/swing/JTable/bug4190222.java new file mode 100644 index 0000000000000..5d6fcd2b0ccdc --- /dev/null +++ b/test/jdk/javax/swing/JTable/bug4190222.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Dimension; +import java.awt.Robot; +import java.util.Vector; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.SwingUtilities; +import javax.swing.table.DefaultTableModel; + +/* + * @test + * @bug 4190222 + * @summary Setting data vector on the model correctly repaint table + * @key headful + * @run main bug4190222 + */ + +public class bug4190222 { + static JFrame frame; + static DefaultTableModel dtm; + static JTable tbl; + + static Vector data; + static Vector colNames; + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + robot.setAutoDelay(250); + + SwingUtilities.invokeAndWait(() -> createTestUI()); + robot.waitForIdle(); + robot.delay(1000); + + SwingUtilities.invokeAndWait(() -> { + Dimension preResize = tbl.getSize(); + dtm.setDataVector(data, colNames); + + if (!preResize.equals(tbl.getSize())) { + throw new RuntimeException("Size of table changed after resizing."); + } + }); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + public static void createTestUI() { + frame = new JFrame("bug4190222"); + + data = new Vector(1, 1); + colNames = new Vector(3); + for (int i = 1; i < 4; i++) { + Vector row = new Vector(1, 1); + row.addElement("Row " + i + ", Col 1"); + row.addElement("Row " + i + ", Col 2"); + row.addElement("Row " + i + ", Col 3"); + data.addElement(row); + } + colNames.addElement("Col 1"); + colNames.addElement("Col 2"); + colNames.addElement("Col 3"); + + dtm = new DefaultTableModel(data, colNames); + tbl = new JTable(dtm); + JScrollPane scrollPane = new JScrollPane(tbl); + frame.add("Center", scrollPane); + JPanel panel = new JPanel(); + frame.add("South", panel); + + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } +} diff --git a/test/jdk/javax/swing/JTable/bug4193727.java b/test/jdk/javax/swing/JTable/bug4193727.java new file mode 100644 index 0000000000000..2ef4159e90c0a --- /dev/null +++ b/test/jdk/javax/swing/JTable/bug4193727.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.FontMetrics; +import java.util.Vector; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.SwingUtilities; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; + +/* + * @test + * @bug 4193727 + * @summary Tests that resizing JTable via TableColumn's + * setWidth(int) repaints correctly + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4193727 + */ + +public class bug4193727 { + static EnhancedJTable tblResults; + static JButton bTest = new JButton("Resize"); + + private static final String INSTRUCTIONS = """ + Push button "Resize". + If either of the following happen, test fails: + 1) The size of the columns change + 2) The JTable is not repainted correctly + + Otherwise test passes. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(bug4193727::createTestUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createTestUI() { + JFrame frame = new JFrame("bug4193727"); + Vector v = new Vector(); + Vector data = new Vector(); + Vector cols = new Vector(); + + cols.add("Name"); + cols.add("Address"); + data.add("Steve"); + data.add("100 East Main Street"); + v.add(data); + + data.add("Richard"); + data.add("99 Main Road"); + v.add(data); + + frame.setLayout(new BorderLayout()); + tblResults = new EnhancedJTable(v, cols); + MyTableHeader mth = new MyTableHeader(); + for (int i = 0; i < tblResults.getColumnCount(); i++) + tblResults.getColumnModel().getColumn(i).setHeaderRenderer(mth.getTHR()); + tblResults.setAutoResizeMode(EnhancedJTable.AUTO_RESIZE_OFF); + + JScrollPane pane = new JScrollPane(tblResults); + frame.add(pane, BorderLayout.CENTER); + JPanel panel = new JPanel(); + panel.add(bTest); + frame.add(panel, BorderLayout.EAST); + bTest.addActionListener(e -> tblResults.autoSizeColumns()); + frame.setSize(300, 200); + return frame; + } +} + +class MyTableHeader extends TableColumn { + public TableCellRenderer getTHR() { + return createDefaultHeaderRenderer(); + } +} + +class EnhancedJTable extends JTable { + public EnhancedJTable(Vector data, Vector colNames) { + super(data, colNames); + } + + public synchronized void autoSizeColumns() { + setAutoResizeMode(AUTO_RESIZE_OFF); + int colcnt = getColumnCount(); + int rowcnt = getRowCount(); + + for (int i = 0; i < colcnt; i++) { + // get the max column width needed + Component cell = getColumnModel().getColumn(i).getHeaderRenderer() + .getTableCellRendererComponent(this, null, false, false, -1, i); + FontMetrics fm = cell.getFontMetrics(cell.getFont()); + int max = SwingUtilities.computeStringWidth(fm, getColumnModel().getColumn(i).getHeaderValue() + .toString() + " "); + for (int j = 0; j < rowcnt; j++) { + // add 2 spaces to account for gutter + int width = SwingUtilities.computeStringWidth(fm, getValueAt(j, i).toString() + " "); + if (max < width) max = width; + } + // set the new column width + getColumnModel().getColumn(i).setWidth(max); + } + } +} diff --git a/test/jdk/javax/swing/JTable/bug4222153.java b/test/jdk/javax/swing/JTable/bug4222153.java new file mode 100644 index 0000000000000..7d37c3d083039 --- /dev/null +++ b/test/jdk/javax/swing/JTable/bug4222153.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.JFrame; +import javax.swing.JTable; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; + +/* + * @test + * @bug 4222153 + * @key headful + * @summary Verify that when tab is pressed, the focus shift to next row first cell, + * if the current selected cell is the last cell in that row. + * @run main bug4222153 + */ + +public class bug4222153 { + private static JFrame frame; + private static JTable table; + private static volatile Point tableLoc; + private static volatile Rectangle cellRect; + private static volatile int selectedRowBeforeTabPress; + private static volatile int selectedColumnBeforeTabPress; + private static volatile int selectedRowAfterTabPress; + private static volatile int selectedColumnAfterTabPress; + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); + Robot robot = new Robot(); + robot.setAutoDelay(50); + try { + SwingUtilities.invokeAndWait(bug4222153::createAndShowUI); + robot.waitForIdle(); + robot.delay(1000); + + SwingUtilities.invokeAndWait(() -> { + tableLoc = table.getLocationOnScreen(); + cellRect = table.getCellRect(0, 0, true); + }); + + robot.mouseMove(tableLoc.x + cellRect.x + cellRect.width / 2, + tableLoc.y + cellRect.y + cellRect.height / 2); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(20); + + SwingUtilities.invokeAndWait(() -> { + selectedRowBeforeTabPress = table.getSelectedRow(); + selectedColumnBeforeTabPress = table.getSelectedColumn(); + }); + + robot.keyPress(KeyEvent.VK_TAB); + robot.keyRelease(KeyEvent.VK_TAB); + robot.waitForIdle(); + robot.delay(20); + robot.keyPress(KeyEvent.VK_TAB); + robot.keyRelease(KeyEvent.VK_TAB); + robot.waitForIdle(); + robot.delay(20); + + SwingUtilities.invokeAndWait(() -> { + selectedRowAfterTabPress = table.getSelectedRow(); + selectedColumnAfterTabPress = table.getSelectedColumn(); + }); + + if (selectedRowAfterTabPress != (selectedRowBeforeTabPress + 1) + && selectedColumnAfterTabPress != selectedColumnBeforeTabPress) { + throw new RuntimeException("JTable's cell focus didn't shift to next" + + " row first cell on TAB press"); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void createAndShowUI() { + frame = new JFrame("Test JTable Tab Press"); + table = new JTable(2, 2); + frame.getContentPane().add(table); + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setVisible(true); + } +} diff --git a/test/jdk/javax/swing/JTable/bug4224179.java b/test/jdk/javax/swing/JTable/bug4224179.java new file mode 100644 index 0000000000000..b223f3f4716e7 --- /dev/null +++ b/test/jdk/javax/swing/JTable/bug4224179.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Dimension; +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTable; + +/* + * @test + * @bug 4224179 + * @summary Tests if Table default cell editor doesn't handle % (percent) character correctly + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4224179 + */ + +public class bug4224179 { + private static final String INSTRUCTIONS = """ + 1. Click ONCE on the center cell ("Huml") + 2. Type the following symbols one after another: a%b + + If the center cell doesn't read "Humla%b" the test fails. + + 3. After the above, press the ESCAPE key and note that the cell + reverts back to "Huml" + 4. Do the stuff in part 1 again + 5. Press the ESCAPE key + + If the center cell now reads "Huml" as it initially was, + the test passed and fails otherwise. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(bug4224179::createTestUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createTestUI() { + JFrame frame = new JFrame("bug4224179"); + JTable table = new JTable(3, 3); + table.setValueAt("Huml", 1, 1); + table.setPreferredScrollableViewportSize(new Dimension(500, 70)); + JScrollPane scrollPane = new JScrollPane(table); + frame.add(scrollPane, BorderLayout.CENTER); + frame.pack(); + return frame; + } +} diff --git a/test/jdk/javax/swing/JTable/bug4226181.java b/test/jdk/javax/swing/JTable/bug4226181.java new file mode 100644 index 0000000000000..70749cf5d5599 --- /dev/null +++ b/test/jdk/javax/swing/JTable/bug4226181.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.GridLayout; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTable; +import javax.swing.table.AbstractTableModel; + +/* + * @test + * @bug 4226181 + * @summary Tests that JTable setModel() correctly re-sizes and counts columns + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4226181 + */ + +public class bug4226181 { + private static final String INSTRUCTIONS = """ + Take a look at the table and remember the number of columns you see. + Now press the "setModel" button. If the number of columns has changed, + then test fails, otherwise it passes. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(bug4226181::createTestUI) + .build() + .awaitAndCheck(); + } + + static class TestModel extends AbstractTableModel { + public int getRowCount() { + return 5; + } + + public int getColumnCount() { + return 7; + } + + public Object getValueAt(int row, int column) { + return row + ":" + column; + } + } + + public static JFrame createTestUI() { + JFrame frame = new JFrame("bug4226181"); + TestModel testModel = new TestModel(); + final JTable t = new JTable(testModel); + JButton b = new JButton("setModel"); + b.addActionListener(ae -> t.setModel(new TestModel())); + t.setCellSelectionEnabled(true); + JPanel p1 = new JPanel(new GridLayout(1, 2)); + p1.add(new JLabel("dummy")); + p1.add(t); + frame.add(p1); + frame.add(b, BorderLayout.SOUTH); + frame.pack(); + return frame; + } +} diff --git a/test/jdk/javax/swing/JTable/bug4239157.java b/test/jdk/javax/swing/JTable/bug4239157.java new file mode 100644 index 0000000000000..025af1615e037 --- /dev/null +++ b/test/jdk/javax/swing/JTable/bug4239157.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.DefaultCellEditor; +import javax.swing.JFrame; +import javax.swing.JTable; +import javax.swing.JTextField; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableColumn; + +/* + * @test + * @bug 4239157 + * @summary Tests that JTable performs cell validation properly + * (i.e. does not accept entries for which stopCellEditing() + * returns false) + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4239157 + */ + +public class bug4239157 { + private static final String INSTRUCTIONS = """ + You see a JTable having one row and two columns. + Click in the very first cell (where "click here" is displayed). + Edit its content (e.g. type some letters) and press right arrow key. + The edited cell should stay active, its content shouldn't change. + The right cell (that with text "inactive forever") shouldn't become active. + The same should be true when you press Tab key. + If it is so, test passes, otherwise it fails. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(bug4239157::createTestUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createTestUI() { + JFrame frame = new JFrame("bug4239157"); + JTable table = new JTable(new Object[][]{{"click here", + "inactive forever"}}, + new Object[]{"1", "2"}); + frame.add("Center", table); + TableColumn column = table.getColumn("1"); + TableCellEditor editor = new TestEditor(new JTextField()); + column.setCellEditor(editor); + + frame.pack(); + return frame; + } + + static class TestEditor extends DefaultCellEditor { + public TestEditor(JTextField tf) { + super(tf); + } + + public boolean stopCellEditing() { + return false; + } + } +} diff --git a/test/jdk/javax/swing/JTable/bug4242631.java b/test/jdk/javax/swing/JTable/bug4242631.java new file mode 100644 index 0000000000000..6e9d1eddc32a7 --- /dev/null +++ b/test/jdk/javax/swing/JTable/bug4242631.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.util.ArrayList; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.table.AbstractTableModel; + +/* + * @test + * @bug 4242631 + * @summary Tests that JTable repaints itself correctly after a record + * has been removed and added to the table model. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4242631 + */ + +public class bug4242631 { + private static JButton addButton; + private static JButton removeButton; + private static JButton bothButton; + private static SimpleTableModel tableModel; + + private static final String INSTRUCTIONS = """ + Press Add button to add a record to the table. The record added should + have number 0. Then press Remove/Add button some times. The record number + should increase as you press. If it does not, test fails. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(bug4242631::createTestUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createTestUI() { + JFrame frame = new JFrame("bug4242631"); + GridBagLayout grid = new GridBagLayout(); + + frame.setLayout(grid); + GridBagConstraints c = new GridBagConstraints(); + c.insets = new Insets(2, 2, 2, 2); + + // Add button. + c.gridx = 0; + c.gridy = 0; + grid.setConstraints(addButton = new JButton("Add"), c); + frame.add(addButton); + + // Edit button. + c.gridx = 1; + c.gridy = 0; + grid.setConstraints(removeButton = new JButton("Remove"), c); + frame.add(removeButton); + + // Remove button. + c.gridx = 2; + c.gridy = 0; + grid.setConstraints(bothButton = new JButton("Remove/Add"), c); + frame.add(bothButton); + + // Table. + c.gridx = 0; + c.gridy = 1; + c.gridwidth = 6; + c.gridheight = 0; + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1.0; + c.weighty = 1.0; + JScrollPane scroll = null; + tableModel = new SimpleTableModel(); + grid.setConstraints(scroll = new JScrollPane(new JTable(tableModel)), c); + frame.add(scroll); + + // Create some action listeners. + addButton.addActionListener(event -> tableModel.addRow()); + removeButton.addActionListener(event -> tableModel.removeRow()); + bothButton.addActionListener(event -> tableModel.removeThenAddRow()); + + frame.pack(); + return frame; + } + + static class SimpleTableModel extends AbstractTableModel { + int counter = 0; + ArrayList list = new ArrayList(); + + public SimpleTableModel() {} + public int getColumnCount() { return 1; } + public int getRowCount() { return list.size(); } + + public Object getValueAt(int row, int col) { + String str = (String) list.get(row); + return str;// + "." + col; + } + + public void addRow() { + list.add("" + counter++); + fireTableRowsInserted(list.size() - 1, list.size() - 1); + } + + public void removeRow() { + if (list.size() == 0) return; + list.remove(list.size() - 1); + fireTableRowsDeleted(list.size(), list.size()); + } + + public void removeThenAddRow() { + if (list.size() == 0) return; + removeRow(); + addRow(); + } + } +} diff --git a/test/jdk/javax/swing/JTableHeader/6884066/bug6884066.java b/test/jdk/javax/swing/JTableHeader/6884066/bug6884066.java index 58926f642116f..2ea4ba7f9f354 100644 --- a/test/jdk/javax/swing/JTableHeader/6884066/bug6884066.java +++ b/test/jdk/javax/swing/JTableHeader/6884066/bug6884066.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,56 +23,74 @@ /* @test - @key headful + @key headful @bug 6884066 @summary JTableHeader listens mouse in disabled state and doesn't work when not attached to a table - @author Alexander Potochkin @run main bug6884066 */ -import javax.swing.*; +import java.awt.event.InputEvent; +import java.awt.Point; +import java.awt.Robot; +import javax.swing.JFrame; +import javax.swing.JTable; import javax.swing.table.JTableHeader; import javax.swing.table.TableColumnModel; import javax.swing.table.TableColumn; -import java.awt.*; -import java.awt.event.InputEvent; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; public class bug6884066 { + private static JFrame frame; private static JTableHeader header; + private static volatile Point point; public static void main(String[] args) throws Exception { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - - Robot robot = new Robot(); - robot.setAutoDelay(20); - SwingUtilities.invokeAndWait(new Runnable() { - public void run() { - // just to quickly grab a column model - JTable table = new JTable(10, 5); - header = new JTableHeader(table.getColumnModel()); - checkColumn(0, "A"); - JFrame frame = new JFrame("standalone header"); - frame.add(header); - frame.pack(); - frame.setVisible(true); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + Robot robot = new Robot(); + robot.setAutoDelay(100); + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + // just to quickly grab a column model + JTable table = new JTable(10, 5); + header = new JTableHeader(table.getColumnModel()); + checkColumn(0, "A"); + frame = new JFrame("standalone header"); + frame.add(header); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + } + }); + robot.waitForIdle(); + robot.delay(1000); + SwingUtilities.invokeAndWait(() -> { + point = header.getLocationOnScreen(); + }); + robot.mouseMove(point.x + 3, point.y + 3); + robot.waitForIdle(); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + for (int i = 0; i < header.getWidth() - 3; i++) { + robot.mouseMove(point.x + i, point.y + 3); } - }); - robot.waitForIdle(); - Point point = header.getLocationOnScreen(); - robot.mouseMove(point.x + 3, point.y + 3); - robot.mousePress(InputEvent.BUTTON1_MASK); - for (int i = 0; i < header.getWidth() - 3; i++) { - robot.mouseMove(point.x + i, point.y + 3); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + TableColumnModel model = header.getColumnModel(); + checkColumn(model.getColumnCount() - 1, "A"); + } + }); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); } - robot.mouseRelease(InputEvent.BUTTON1_MASK); - SwingUtilities.invokeAndWait(new Runnable() { - public void run() { - TableColumnModel model = header.getColumnModel(); - checkColumn(model.getColumnCount() - 1, "A"); - } - }); } private static void checkColumn(int index, String str) { diff --git a/test/jdk/javax/swing/JTextArea/bug4265784.java b/test/jdk/javax/swing/JTextArea/bug4265784.java index 3465fced59598..2b1ea06834f65 100644 --- a/test/jdk/javax/swing/JTextArea/bug4265784.java +++ b/test/jdk/javax/swing/JTextArea/bug4265784.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,7 +50,7 @@ public static void main(String[] args) throws Exception { try { SwingUtilities.invokeAndWait(() -> { frame = new JFrame("bug4265784"); - ta = new JTextArea(); + ta = new JTextArea("some text", 50, 50); frame.getContentPane().add(ta); frame.pack(); frame.setLocationRelativeTo(null); diff --git a/test/jdk/javax/swing/JTextField/bug4232716.java b/test/jdk/javax/swing/JTextField/bug4232716.java new file mode 100644 index 0000000000000..b4fc902f62399 --- /dev/null +++ b/test/jdk/javax/swing/JTextField/bug4232716.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4232716 + * @key headful + * @summary Tests if creates a maximized JTextField + * @run main bug4232716 + */ + +import java.awt.Component; +import java.awt.Container; +import java.awt.Robot; +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; + +public class bug4232716 { + + private static JFrame frame; + private static JEditorPane e; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + try { + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame("bug4232716"); + String html =" Test " + + "
    " + + "
    "; + e = new JEditorPane("text/html", html); + e.setEditable(false); + frame.add(e); + frame.setSize(400, 300); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + }); + robot.waitForIdle(); + robot.delay(1000); + + Container c = (Container)e.getComponent(0); + Component swingComponent = c.getComponent(0); + System.out.println(swingComponent); + if (swingComponent instanceof JTextField tf) { + System.out.println(tf.getWidth()); + System.out.println(frame.getWidth()); + if (swingComponent.getWidth() > (frame.getWidth() * 0.75)) { + throw new RuntimeException("textfield width almost same as frame width"); + } + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } +} diff --git a/test/jdk/javax/swing/JTextField/bug5027332.java b/test/jdk/javax/swing/JTextField/bug5027332.java new file mode 100644 index 0000000000000..2d9cd777d9b5c --- /dev/null +++ b/test/jdk/javax/swing/JTextField/bug5027332.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 5027332 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Tests that textfield caret is placed slightly off textfield borders + * @run main/manual bug5027332 + */ + +import java.awt.BorderLayout; + +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.UIManager; + +public class bug5027332 { + + private static final String INSTRUCTIONS = """ + Click into the text field so that caret appears inside. + The caret should be placed slightly off text field borders, + so that it can be easily distinguished from the border. + Test fails if the caret touches the border."""; + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + + PassFailJFrame.builder() + .title("bug5027332 Instructions") + .instructions(INSTRUCTIONS) + .rows(6) + .columns(35) + .testUI(bug5027332::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + JFrame frame = new JFrame("bug5027332"); + JTextField t = new JTextField(10); + JPanel p = new JPanel(); + p.add(t, BorderLayout.CENTER); + frame.setContentPane(p); + frame.pack(); + return frame; + } +} diff --git a/test/jdk/javax/swing/JToggleButton/4128979/bug4128979.html b/test/jdk/javax/swing/JToggleButton/4128979/bug4128979.html deleted file mode 100644 index 699d33fc38d8d..0000000000000 --- a/test/jdk/javax/swing/JToggleButton/4128979/bug4128979.html +++ /dev/null @@ -1,41 +0,0 @@ - - - -This test requires Windows look and feel, just press Pass if you -run it not on Windows. - -When the test starts you'll see toggle buttons in three rows -two of which are toolbars. - -Make these buttons pressed, their background color must change -to halftones between the button background colors and the ToggleButton -highlight color (it is shown in the square below). - -If the background color does not change correctly for at least one button, -the test fails. - - - - - diff --git a/test/jdk/javax/swing/JToggleButton/4128979/bug4128979.java b/test/jdk/javax/swing/JToggleButton/bug4128979.java similarity index 64% rename from test/jdk/javax/swing/JToggleButton/4128979/bug4128979.java rename to test/jdk/javax/swing/JToggleButton/bug4128979.java index 76893c73c8951..36625ef083d21 100644 --- a/test/jdk/javax/swing/JToggleButton/4128979/bug4128979.java +++ b/test/jdk/javax/swing/JToggleButton/bug4128979.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,64 +21,67 @@ * questions. */ -/* @test - @bug 4128979 - @requires (os.family == "windows") - @summary Tests that background changes correctly in WinLF for JToggleButton when pressed - @key headful - @run applet/manual=yesno bug4128979.html -*/ +import java.awt.Color; +import java.awt.Component; +import java.awt.ComponentOrientation; +import java.awt.Container; +import java.awt.FlowLayout; +import java.awt.Graphics; import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.Icon; -import javax.swing.JApplet; import javax.swing.JFrame; import javax.swing.JLabel; -import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JToggleButton; import javax.swing.JToolBar; import javax.swing.UIManager; -import javax.swing.UnsupportedLookAndFeelException; -import javax.swing.WindowConstants; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.ComponentOrientation; -import java.awt.Container; -import java.awt.FlowLayout; -import java.awt.Graphics; -public class bug4128979 extends JApplet { +import jtreg.SkippedException; +import sun.awt.OSInfo; - public static void main(String[] args) { - JApplet applet = new bug4128979(); - applet.init(); - applet.start(); +/* @test + * @bug 4128979 + * @requires (os.family == "windows") + * @modules java.desktop/sun.awt + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame jtreg.SkippedException + * @summary Tests that background changes correctly in WinLF for JToggleButton when pressed + * @run main/manual bug4128979 + */ - JFrame frame = new JFrame("Test window"); - frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); - frame.setLayout(new BorderLayout()); - frame.add(applet, BorderLayout.CENTER); - frame.setSize(600, 240); - frame.setVisible(true); - } +public class bug4128979 { + private static final String INSTRUCTIONS = """ + When the test starts, toggle buttons are visible in three rows + two of which are toolbars. - public void init() { - try { - UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel"); - } catch (UnsupportedLookAndFeelException e) { - JOptionPane.showMessageDialog(this, - "This test requires Windows look and feel, so just press Pass\n as "+ - " this look and feel is unsupported on this platform.", - "Unsupported LF", JOptionPane.ERROR_MESSAGE); - return; - } catch (Exception e) { - throw new RuntimeException("Couldn't set look and feel"); + Press these buttons, their background color must change + to half tones between the button background colors and the ToggleButton + highlight color (it is shown in the square below). + + If the background color does not change correctly for at least one button, + the test fails."""; + + public static void main(String[] args) throws Exception { + if (OSInfo.getOSType() != OSInfo.OSType.WINDOWS) { + throw new SkippedException("This test is for Windows only"); } + UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel"); + PassFailJFrame.builder() + .title("JToggleButton Instructions") + .instructions(INSTRUCTIONS) + .rows(15) + .columns(60) + .testUI(bug4128979::createAndShowUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createAndShowUI() { + JFrame frame = new JFrame("JToggleButton's Background Color Test"); + frame.setLayout(new FlowLayout()); - setLayout(new FlowLayout()); JPanel p = new JPanel(); p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); @@ -113,8 +116,11 @@ public int getIconHeight() { return 50; } }); - add(p); - add(label); + + frame.getContentPane().add(p); + frame.getContentPane().add(label); + frame.setSize(600, 250); + return frame; } static void addButtons(Container c) { diff --git a/test/jdk/javax/swing/JToolBar/4529206/bug4529206.java b/test/jdk/javax/swing/JToolBar/4529206/bug4529206.java index 0b05ded2f3f69..56f4a224e3b10 100644 --- a/test/jdk/javax/swing/JToolBar/4529206/bug4529206.java +++ b/test/jdk/javax/swing/JToolBar/4529206/bug4529206.java @@ -1,12 +1,10 @@ /* - * Copyright (c) 2004, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or @@ -23,71 +21,84 @@ * questions. */ +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Robot; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.JToolBar; +import javax.swing.SwingUtilities; +import javax.swing.plaf.basic.BasicToolBarUI; + /* * @test * @key headful - * @bug 4529206 + * @bug 4529206 * @summary JToolBar - setFloating does not work correctly - * @author Konstantin Eremin - * @run main bug4529206 + * @run main bug4529206 */ -import javax.swing.*; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -public class bug4529206 extends JFrame { +public class bug4529206 { static JFrame frame; static JToolBar jToolBar1; - public bug4529206() { - setDefaultCloseOperation(EXIT_ON_CLOSE); - JPanel jPanFrame = (JPanel) this.getContentPane(); + static JButton jButton1; + + private static void test() { + frame = new JFrame("bug4529206"); + JPanel jPanFrame = (JPanel) frame.getContentPane(); jPanFrame.setLayout(new BorderLayout()); - this.setSize(new Dimension(200, 100)); - this.setLocation(125, 75); - this.setTitle("Test Floating Toolbar"); + frame.setSize(new Dimension(200, 100)); + frame.setTitle("Test Floating Toolbar"); jToolBar1 = new JToolBar(); - JButton jButton1 = new JButton("Float"); + jButton1 = new JButton("Float"); jPanFrame.add(jToolBar1, BorderLayout.NORTH); JTextField tf = new JTextField("click here"); jPanFrame.add(tf); jToolBar1.add(jButton1, null); - jButton1.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - buttonPressed(e); - } - }); - makeToolbarFloat(); - setVisible(true); + jButton1.addActionListener(e -> buttonPressed()); + + frame.setUndecorated(true); + frame.setLocationRelativeTo(null); + frame.setVisible(true); } - private void makeToolbarFloat() { - javax.swing.plaf.basic.BasicToolBarUI ui = (javax.swing.plaf.basic.BasicToolBarUI) jToolBar1.getUI(); + private static void makeToolbarFloat() { + BasicToolBarUI ui = (BasicToolBarUI) jToolBar1.getUI(); if (!ui.isFloating()) { ui.setFloatingLocation(100, 100); ui.setFloating(true, jToolBar1.getLocation()); } } - private void buttonPressed(ActionEvent e) { + private static void buttonPressed() { makeToolbarFloat(); } public static void main(String[] args) throws Exception { - SwingUtilities.invokeAndWait(new Runnable() { - public void run() { - frame = new bug4529206(); - } - }); - Robot robot = new Robot(); - robot.waitForIdle(); - SwingUtilities.invokeAndWait(new Runnable() { - public void run() { + try { + SwingUtilities.invokeAndWait(() -> test()); + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(1000); + + SwingUtilities.invokeAndWait(() -> makeToolbarFloat()); + robot.waitForIdle(); + robot.delay(300); + + SwingUtilities.invokeAndWait(() -> { if (frame.isFocused()) { - throw (new RuntimeException("setFloating does not work correctly")); + throw new RuntimeException( + "setFloating does not work correctly"); } - } - }); + }); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } } } diff --git a/test/jdk/javax/swing/JToolBar/bug4188825.java b/test/jdk/javax/swing/JToolBar/bug4188825.java new file mode 100644 index 0000000000000..e5dd6538de42c --- /dev/null +++ b/test/jdk/javax/swing/JToolBar/bug4188825.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4188825 + * @summary Tests if toolbars return to original location when closed + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4188825 + */ + +import java.awt.BorderLayout; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JToolBar; + +public class bug4188825 { + static final String INSTRUCTIONS = """ + Drag the toolbar out of frame and close it. If it returns to + the original location, then the test succeeded, otherwise it failed. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug4188825 Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug4188825::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + JFrame frame = new JFrame("Toolbar Drag Test"); + frame.setLayout(new BorderLayout()); + JToolBar tb = new JToolBar(); + tb.setOrientation(JToolBar.VERTICAL); + tb.add(new JButton("a")); + tb.add(new JButton("b")); + tb.add(new JButton("c")); + frame.add(tb, BorderLayout.WEST); + JButton l = new JButton("Get me!!!"); + l.setSize(200, 200); + frame.add(l); + frame.setSize(200, 200); + return frame; + } +} diff --git a/test/jdk/javax/swing/JToolBar/bug4251592.java b/test/jdk/javax/swing/JToolBar/bug4251592.java new file mode 100644 index 0000000000000..add4eb8ec923e --- /dev/null +++ b/test/jdk/javax/swing/JToolBar/bug4251592.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4251592 + * @summary JToolBar should have ability to set custom layout. + * @key headful + * @run main bug4251592 + */ + +import java.awt.BorderLayout; +import java.awt.GridLayout; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.InputEvent; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JToolBar; +import javax.swing.SwingUtilities; + +public class bug4251592 { + private static final int OFFSET = 3; + private static volatile Point loc; + private static JFrame frame; + private static JToolBar toolBar; + private static GridLayout customLayout; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + robot.setAutoDelay(100); + try { + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame("Toolbar Layout Save Test"); + toolBar = new JToolBar(); + customLayout = new GridLayout(); + frame.setLayout(new BorderLayout()); + frame.add(toolBar, BorderLayout.NORTH); + + toolBar.setLayout(customLayout); + toolBar.add(new JButton("Button1")); + toolBar.add(new JButton("Button2")); + toolBar.add(new JButton("Button3")); + toolBar.setFloatable(true); + + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + }); + + robot.waitForIdle(); + robot.delay(1000); + + SwingUtilities.invokeAndWait(() -> loc = toolBar.getLocationOnScreen()); + + robot.mouseMove(loc.x + OFFSET, loc.y + OFFSET); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseMove(loc.x + OFFSET, loc.y + 50); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + + if (toolBar.getLayout() != customLayout) { + throw new RuntimeException("Custom layout not saved..."); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } +} diff --git a/test/jdk/javax/swing/JToolBar/bug5035668.java b/test/jdk/javax/swing/JToolBar/bug5035668.java new file mode 100644 index 0000000000000..b0cdef7c7ef70 --- /dev/null +++ b/test/jdk/javax/swing/JToolBar/bug5035668.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 5035668 + * @summary Test that metal ToolBar border correctly sizes the MetalBumps used + * for the grip + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug5035668 + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.ComponentOrientation; +import java.awt.GridLayout; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JToolBar; +import javax.swing.UIManager; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; + +public class bug5035668 { + static final String INSTRUCTIONS = """ + This test is for Metal LaF only. + + All of them have an empty border around their own border. + If you see that in any toolbar the grip (little dotted strip) overlaps + the empty border press Fail. If you see that grips are completely + inside empty borders press Pass. + """; + + public static void main(String[] args) throws Exception { + // Set metal l&f + UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); + PassFailJFrame.builder() + .title("bug4251592 Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug5035668::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + JFrame frame = new JFrame("Metal JToolBar Border Overlap Test"); + frame.setLayout(new BorderLayout()); + frame.setBackground(Color.white); + + // Horizontal toolbar left-to-right + final JToolBar toolBar = new JToolBar(); + toolBar.setBorder(new CompoundBorder(new EmptyBorder(10, 10, 10, 10), + toolBar.getBorder())); + toolBar.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); + toolBar.add(new ToolBarButton(toolBar)); + + // Horizontal toolbar right-to-left + JToolBar toolBar2 = new JToolBar(); + toolBar2.setBorder(new CompoundBorder(new EmptyBorder(10, 10, 10, 10), + toolBar2.getBorder())); + toolBar2.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); + toolBar2.add(new ToolBarButton(toolBar2)); + + JPanel topPanel = new JPanel(new GridLayout(2, 0)); + topPanel.add(toolBar); + topPanel.add(toolBar2); + frame.add(topPanel, BorderLayout.NORTH); + + JToolBar toolBar3 = new JToolBar(); + toolBar3.setBorder(new CompoundBorder(new EmptyBorder(10, 10, 10, 10), + toolBar3.getBorder())); + toolBar3.setOrientation(JToolBar.VERTICAL); + toolBar3.add(new ToolBarButton(toolBar3)); + frame.add(toolBar3, BorderLayout.EAST); + + frame.setSize(200, 200); + return frame; + } + + static class ToolBarButton extends JButton { + final JToolBar toolBar; + + public ToolBarButton(JToolBar p_toolBar) { + super("Change toolbar's orientation"); + this.toolBar = p_toolBar; + addActionListener(e -> toolBar.setOrientation(1 - toolBar.getOrientation())); + } + } +} diff --git a/test/jdk/javax/swing/JToolTip/4644444/bug4644444.html b/test/jdk/javax/swing/JToolTip/4644444/bug4644444.html deleted file mode 100644 index b7e9bcb03fe85..0000000000000 --- a/test/jdk/javax/swing/JToolTip/4644444/bug4644444.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - bug4644444 - - - -

    bug4644444
    Bug ID: 4644444

    - -

    See the dialog box (usually in upper left corner) for instructions

    - - - - diff --git a/test/jdk/javax/swing/JToolTip/4644444/bug4644444.java b/test/jdk/javax/swing/JToolTip/4644444/bug4644444.java deleted file mode 100644 index 124c565a9cbd6..0000000000000 --- a/test/jdk/javax/swing/JToolTip/4644444/bug4644444.java +++ /dev/null @@ -1,354 +0,0 @@ -/* - * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.awt.*; -import javax.swing.*; -import java.awt.event.*; - -/* - * test - * @bug 4644444 8076246 -*/ - -public class bug4644444 extends JApplet { - - JPanel panel; - JButton button; - - public bug4644444() throws Exception { - java.awt.EventQueue.invokeLater( () -> { - panel = new JPanel(); - button = new JButton("whooo"); - button.setToolTipText("Somthing really long 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890"); - panel.add(button); - getContentPane().add(panel); - }); - } - - public void init() { - String[][] instructionsSet = - { - { - " Note : Incase of Assertion failure,user can enter", - " remarks by pressing 'Assertion Fail Remarks ' button", - " ", - " You would see a testframe with a Button", - " ", - " ON ALL PLATFORMS", - "1. Move the mouse on the button, ", - " so that the tooltip attached to it comes up ", - "2. Tool tip should get adjusted it-self to show ", - " its full length of text. ", - "3. If tooltip text gets cut, ", - " press 'Assertion Fail' else press 'Assertion Pass'", - "4. Similarly, move the applet to different locations of the screen, ", - " & see if tooltip works properly everywhere. " - } - }; - - String[] exceptionsSet = - { - "JToolTip is shown partially when placed very close to screen boundaries", - }; - - Sysout.setInstructionsWithExceptions(instructionsSet,exceptionsSet); - - } - - public void start (){} - - public void destroy(){ - if(Sysout.failStatus()) { - String failMsg = Sysout.getFailureMessages(); - failMsg = failMsg.replace('\n',' '); - throw new RuntimeException(failMsg); - }// End destroy - } -} - - -/**************************************************** - Standard Test Machinery - DO NOT modify anything below -- it's a standard - chunk of code whose purpose is to make user - interaction uniform, and thereby make it simpler - to read and understand someone else's test. - ****************************************************/ - -/** - This is part of the standard test machinery. - It creates a dialog (with the instructions), and is the interface - for sending text messages to the user. - To print the instructions, send an array of strings to Sysout.createDialog - WithInstructions method. Put one line of instructions per array entry. - To display a message for the tester to see, simply call Sysout.println - with the string to be displayed. - This mimics System.out.println but works within the test harness as well - as standalone. - */ - -class Sysout - { - private static TestDialog dialog; - - public static void createDialogWithInstructions( String[] instructions ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - dialog.printInstructions( instructions ); - dialog.show(); - println( "Any messages for the tester will display here." ); - } - - public static void createDialog( ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - String[] defInstr = { "Instructions will appear here. ", "" } ; - dialog.printInstructions( defInstr ); - dialog.show(); - println( "Any messages for the tester will display here." ); - } - - - public static void printInstructions( String[] instructions ) - { - dialog.printInstructions( instructions ); - } - - - public static void println( String messageIn ) - { - dialog.displayMessage( messageIn ); - } - - public static void setInstructionsWithExceptions(String instructionsSet[][], - String exceptionsSet[]) { - createDialogWithInstructions(instructionsSet[0]); - dialog.setInstructions(instructionsSet); - dialog.setExceptionMessages(exceptionsSet); - } - - public static String getFailureMessages() { - return dialog.failureMessages; - } - - public static boolean failStatus() { - return dialog.failStatus; - } - - }// Sysout class - -/** - This is part of the standard test machinery. It provides a place for the - test instructions to be displayed, and a place for interactive messages - to the user to be displayed. - To have the test instructions displayed, see Sysout. - To have a message to the user be displayed, see Sysout. - Do not call anything in this dialog directly. - */ -class TestDialog extends Dialog - { - - TextArea instructionsText; - TextArea messageText; - int maxStringLength = 70; - - Panel assertPanel; - Button assertPass,assertFail,remarks; - HandleAssert handleAssert; - boolean failStatus=false; - int instructionCounter=0; - String instructions[][]; - int exceptionCounter=0; - String exceptionMessages[]; - String failureMessages="
    "; - String remarksMessage=null; - RemarksDialog remarksDialog; - - //DO NOT call this directly, go through Sysout - public TestDialog( Frame frame, String name ) - { - super( frame, name ); - int scrollBoth = TextArea.SCROLLBARS_BOTH; - instructionsText = new TextArea( "", 14, maxStringLength, scrollBoth ); - add( "North", instructionsText ); - - messageText = new TextArea( "", 3, maxStringLength, scrollBoth ); - add("Center", messageText); - - assertPanel = new Panel(new FlowLayout()); - assertPass=new Button("Assertion Pass"); - assertPass.setName("Assertion Pass"); - assertFail=new Button("Assertion Fail"); - assertFail.setName("Assertion Fail"); - remarks = new Button("Assertion Fail Remarks"); - remarks.setEnabled(false); - remarks.setName("Assertion Remarks"); - assertPanel.add(assertPass); - assertPanel.add(assertFail); - assertPanel.add(remarks); - handleAssert = new HandleAssert(); - assertPass.addActionListener(handleAssert); - assertFail.addActionListener(handleAssert); - remarks.addActionListener(handleAssert); - add("South",assertPanel); - pack(); - - show(); - }// TestDialog() - - //DO NOT call this directly, go through Sysout - public void printInstructions( String[] instructions ) - { - //Clear out any current instructions - instructionsText.setText( "" ); - - //Go down array of instruction strings - - String printStr, remainingStr; - for( int i=0; i < instructions.length; i++ ) - { - //chop up each into pieces maxSringLength long - remainingStr = instructions[ i ]; - while( remainingStr.length() > 0 ) - { - //if longer than max then chop off first max chars to print - if( remainingStr.length() >= maxStringLength ) - { - //Try to chop on a word boundary - int posOfSpace = remainingStr. - lastIndexOf( ' ', maxStringLength - 1 ); - - if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1; - - printStr = remainingStr.substring( 0, posOfSpace + 1 ); - remainingStr = remainingStr.substring( posOfSpace + 1 ); - } - //else just print - else - { - printStr = remainingStr; - remainingStr = ""; - } - - instructionsText.append( printStr + "\n" ); - - }// while - - }// for - - }//printInstructions() - - //DO NOT call this directly, go through Sysout - public void displayMessage( String messageIn ) - { - messageText.append( messageIn + "\n" ); - } - - public void emptyMessage() { - messageText.setText(""); - } - - public void setInstructions(String insStr[][]) { - instructions=insStr; - } - - public void setExceptionMessages(String exceptionMessages[]) { - this.exceptionMessages=exceptionMessages; - } - - class HandleAssert implements ActionListener { - public void actionPerformed(ActionEvent ae) { - if(ae.getSource()==remarks) { - remarksDialog = new RemarksDialog(TestDialog.this, - "Assertion Remarks Dialog",true); - remarks.setEnabled(false); - if(remarksMessage!=null) - failureMessages+=". User Remarks : "+remarksMessage; - } - else { - if(instructionCounter"+ - exceptionMessages[exceptionCounter]; - } - } - exceptionCounter++; - } - } - } - - class RemarksDialog extends Dialog implements ActionListener{ - Panel rootPanel,remarksPanel; - TextArea textarea; - Button addRemarks,cancelRemarks; - public RemarksDialog(Dialog owner,String title,boolean modal) { - super(owner,title,modal); - rootPanel = new Panel(new BorderLayout()); - remarksPanel = new Panel(new FlowLayout()); - textarea = new TextArea(5,30); - addRemarks=new Button("Add Remarks"); - addRemarks.addActionListener(this); - cancelRemarks = new Button("Cancel Remarks"); - cancelRemarks.addActionListener(this); - remarksPanel.add(addRemarks); - remarksPanel.add(cancelRemarks); - rootPanel.add(textarea,"Center"); - rootPanel.add(remarksPanel,"South"); - add(rootPanel); - setBounds(150,150,400,200); - setVisible(true); - } - - public void actionPerformed(ActionEvent ae) { - remarksMessage=null; - if(ae.getSource()==addRemarks) { - String msg = textarea.getText().trim(); - if (msg.length()>0) - remarksMessage=msg; - } - dispose(); - } - - } - -}// TestDialog class diff --git a/test/jdk/javax/swing/JToolTip/TooltipTest.java b/test/jdk/javax/swing/JToolTip/TooltipTest.java new file mode 100644 index 0000000000000..c081730a8d7b5 --- /dev/null +++ b/test/jdk/javax/swing/JToolTip/TooltipTest.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4207474 4218495 4375928 + * @summary Tests various tooltip issues: HTML tooltips, long tooltip text + * and mnemonic keys displayed in tooltips + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual TooltipTest + */ + +import java.awt.FlowLayout; +import java.awt.event.KeyEvent; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.UIManager; + +public class TooltipTest { + private static final String INSTRUCTIONS = """ + 1. Move the mouse over the button labeled "Red tip" and let it stay + still in order to test HTML in JToolTip. If the tooltip has some + text which is red then test passes, otherwise it fails (bug 4207474). + + 2. Move the mouse over the button labeled "Long tip". + If the last letter of the tooltip appears clipped, + then the test fails. If you can see the entire last character, + then the test passes (bug 4218495). + + 3. Verify that "M" is underlined on the button labeled "Mnemonic" + Move the mouse pointer over the button labeled "Mnemonic" and look + at tooltip when it appears. It should read "hint". + If the above is true test passes else test fails (bug 4375928). + """; + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); + + PassFailJFrame.builder() + .title("TooltipTest Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(TooltipTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JComponent createTestUI() { + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + + JButton b = new JButton("Red tip"); + b.setToolTipText("
    Here is some " + + "red text.
    "); + panel.add(b); + + b = new JButton("Long tip"); + b.setToolTipText("Is the last letter clipped?"); + panel.add(b); + + b = new JButton("Mnemonic"); + b.setMnemonic(KeyEvent.VK_M); + b.setToolTipText("hint"); + panel.add(b); + + return panel; + } +} diff --git a/test/jdk/javax/swing/JToolTip/bug4225314.java b/test/jdk/javax/swing/JToolTip/bug4225314.java new file mode 100644 index 0000000000000..d36bd461272a4 --- /dev/null +++ b/test/jdk/javax/swing/JToolTip/bug4225314.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4225314 + * @summary Tests that tooltip is painted properly when it has thick border + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4225314 + */ + +import java.awt.Color; +import java.awt.FlowLayout; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.JToolTip; +import javax.swing.border.LineBorder; + +public class bug4225314 { + private static final String INSTRUCTIONS = """ + The word "Tooltip" in both tooltips should not be clipped by the + black border and be fully visible for this test to pass. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug4225314 Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug4225314::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JComponent createTestUI() { + JToolTip tt1 = new JToolTip(); + tt1.setTipText("Tooltip"); + tt1.setBorder(new LineBorder(Color.BLACK, 10)); + + JToolTip tt2 = new JToolTip(); + tt2.setTipText("Tooltip"); + tt2.setBorder(new LineBorder(Color.BLACK, 10)); + + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + panel.add(tt1); + panel.add(tt2); + + return panel; + } +} diff --git a/test/jdk/javax/swing/JToolTip/bug4255441.java b/test/jdk/javax/swing/JToolTip/bug4255441.java new file mode 100644 index 0000000000000..4a90c97550256 --- /dev/null +++ b/test/jdk/javax/swing/JToolTip/bug4255441.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4255441 + * @summary Tests that tooltip appears inside AWT Frame + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4255441 + */ + +import java.awt.FlowLayout; +import java.awt.Frame; +import javax.swing.JButton; + +public class bug4255441 { + private static final String INSTRUCTIONS = """ + Move mouse pointer inside the button. + If a tooltip with "Tooltip text" appears, the test passes. + """; + + private static Frame createTestUI() { + Frame fr = new Frame("bug4255441"); + fr.setLayout(new FlowLayout()); + + JButton bt = new JButton("Button"); + bt.setToolTipText("Tooltip text"); + fr.add(bt); + + fr.setSize(200, 200); + return fr; + } + + public static void main(String[] argv) throws Exception { + PassFailJFrame.builder() + .title("bug4255441 Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug4255441::createTestUI) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/javax/swing/JToolTip/bug4644444.java b/test/jdk/javax/swing/JToolTip/bug4644444.java new file mode 100644 index 0000000000000..2921ecf0da5cc --- /dev/null +++ b/test/jdk/javax/swing/JToolTip/bug4644444.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.JButton; +import javax.swing.JFrame; + +/* + * @test + * @bug 4644444 8076246 + * @summary JToolTip is shown improperly when placed very close to screen boundaries + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4644444 + */ + +public class bug4644444 { + + private static final String INSTRUCTIONS = """ + 1. Move the mouse on the button, so that the tooltip is visible. + 2. Tooltip should get adjusted itself to show its full length of text. + 3. Similarly, move the frame to different locations of the screen + & see if tooltip works properly everywhere. + 4. Press 'Pass' if tooltip text is fully visible else press 'Fail'. """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("JToolTip Instructions") + .instructions(INSTRUCTIONS) + .testUI(bug4644444::createUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createUI() { + JFrame frame = new JFrame("bug4644444"); + JButton button = new JButton("Button"); + button.setToolTipText("Something really long 1234567890 1234567890 " + + "1234567890 1234567890 1234567890 1234567890 1234567890 1234567890"); + frame.getContentPane().add(button); + frame.setSize(200, 80); + return frame; + } +} diff --git a/test/jdk/javax/swing/JTree/4314199/bug4314199.html b/test/jdk/javax/swing/JTree/4314199/bug4314199.html deleted file mode 100644 index 1ce22cbeac303..0000000000000 --- a/test/jdk/javax/swing/JTree/4314199/bug4314199.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - -Select the last tree node (marked "Here") and click on the menu. -Look at the vertical line connecting nodes "Bug" and "Here". If -this line disappears when the menu drops down, test fails. - - - diff --git a/test/jdk/javax/swing/JTree/NodeChangedTest.java b/test/jdk/javax/swing/JTree/NodeChangedTest.java new file mode 100644 index 0000000000000..5461136c57be7 --- /dev/null +++ b/test/jdk/javax/swing/JTree/NodeChangedTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4199472 + * @summary Tests that node changed for the root of the tree update the + * structure. + * @run main NodeChangedTest + */ + +import javax.swing.JTree; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; + +public class NodeChangedTest { + public static void main(String[] args) { + // Create 3 nodes + final DefaultMutableTreeNode root = new DefaultMutableTreeNode("root", + true); + final DefaultMutableTreeNode child = new DefaultMutableTreeNode("child", + true); + final DefaultMutableTreeNode leaf = new DefaultMutableTreeNode("leaf", + false); + root.add(child); + child.add(leaf); + + final JTree tree = new JTree(root); + + // Change the root node + root.setUserObject("New root"); + ((DefaultTreeModel) tree.getModel()).nodeChanged(root); + + // Check + if (!root.getUserObject().toString().equals("New root")) { + throw new RuntimeException("Failed changing root node for default model."); + } + + // Change to large model + tree.setLargeModel(true); + tree.setRowHeight(20); + root.setUserObject("root"); + tree.setModel(new DefaultTreeModel(root)); + root.setUserObject("New root"); + ((DefaultTreeModel) tree.getModel()).nodeChanged(root); + + // Check again + if (!root.getUserObject().toString().equals("New root")) { + throw new RuntimeException("Failed changing root node for large model."); + } + } +} diff --git a/test/jdk/javax/swing/JTree/bug4118860.java b/test/jdk/javax/swing/JTree/bug4118860.java new file mode 100644 index 0000000000000..eb266eb9324ab --- /dev/null +++ b/test/jdk/javax/swing/JTree/bug4118860.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4118860 + * @summary setToggleClickCount/getToggleClickCount have been added. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4118860 + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.GridLayout; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JTree; + +public class bug4118860 { + static final String INSTRUCTIONS = """ + Push the "Single Click" button and try expanding/contracting + branch nodes of the tree with one left mouse button click + on the label part of the node (not the icon or handles). + + Then push the "Double Click" button and try doing the same using + left mouse button double click. Single click shouldn't cause + expanding/contracting. A double click should now be required + to expand/contract nodes. + + If it works then the test PASSES, else the test FAILS. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug4118860 Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug4118860::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + JFrame f = new JFrame("ToggleClickCount Test"); + JTree tr = new JTree(); + JPanel p = new JPanel(); + p.setBackground(Color.red); + p.setLayout(new GridLayout(1, 1)); + tr.setOpaque(false); + p.add(tr); + f.add(p, BorderLayout.CENTER); + JPanel bp = new JPanel(); + JButton bt1 = new JButton("Single Click"); + bt1.addActionListener(e -> { + tr.setToggleClickCount(1); + if (tr.getToggleClickCount() != 1) { + throw new RuntimeException("ToggleClickCount doesn't set..."); + } + }); + JButton bt2 = new JButton("Double Click"); + bt2.addActionListener(e -> { + tr.setToggleClickCount(2); + if (tr.getToggleClickCount() != 2) { + throw new RuntimeException("ToggleClickCount doesn't set..."); + } + }); + bp.setLayout(new GridLayout(1, 2)); + bp.add(bt1); + bp.add(bt2); + f.add(bp, BorderLayout.SOUTH); + f.setSize(300, 200); + return f; + } +} diff --git a/test/jdk/javax/swing/JTree/bug4169215.java b/test/jdk/javax/swing/JTree/bug4169215.java new file mode 100644 index 0000000000000..0b8780b3939d7 --- /dev/null +++ b/test/jdk/javax/swing/JTree/bug4169215.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4169215 + * @summary Accessibility hierarchy JTree node test. + * @run main bug4169215 + */ + +import javax.accessibility.AccessibleContext; +import javax.swing.JTree; +import javax.swing.tree.DefaultMutableTreeNode; + +public class bug4169215 { + public static void main(String[] args) { + // create the tree + DefaultMutableTreeNode root = new DefaultMutableTreeNode("top"); + DefaultMutableTreeNode nodeA = new DefaultMutableTreeNode("A"); + DefaultMutableTreeNode nodeB = new DefaultMutableTreeNode("B"); + root.add(nodeA); + root.add(nodeB); + JTree tree = new JTree(root); + + // find the AccessibleContext of the tree + AccessibleContext actree = tree.getAccessibleContext(); + + // find the AccessibleContext of top node of the tree + AccessibleContext act = actree.getAccessibleChild(0).getAccessibleContext(); + + // find the AccessibleContext of the first child of the table -> + // the AccessibleContext of nodeA + AccessibleContext accA = act.getAccessibleChild(0).getAccessibleContext(); + + // find the AccessibleContext of the next sibling of nodeA, by getting + // child+1 of the parent (the table) + AccessibleContext accB = act.getAccessibleChild( + accA.getAccessibleIndexInParent()+1).getAccessibleContext(); + + // look to see who the sibling is. + if (accB.getAccessibleName().compareTo("B") != 0) { + throw new RuntimeException("Parent node is a sibling instead!"); + } + } +} diff --git a/test/jdk/javax/swing/JTree/bug4196987.java b/test/jdk/javax/swing/JTree/bug4196987.java new file mode 100644 index 0000000000000..1387b669b0560 --- /dev/null +++ b/test/jdk/javax/swing/JTree/bug4196987.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4196987 + * @summary Test Metal L&F JTree expander icons transparency. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4196987 + */ + +import java.awt.Color; +import java.awt.GridLayout; + +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JTree; +import javax.swing.UIManager; + +public class bug4196987 { + static final String INSTRUCTIONS = """ + If the background of tree icons are red, the test PASSES. + Otherwise the test FAILS. + """; + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); + PassFailJFrame.builder() + .title("bug4196987 Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug4196987::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + JFrame f = new JFrame("JTree Icon Transparency Test"); + JPanel p = new JPanel(); + p.setBackground(Color.red); + p.setLayout(new GridLayout(1, 1)); + JTree t = new JTree(); + t.setOpaque(false); + p.add(t); + f.add(p); + f.setSize(200, 200); + return f; + } +} diff --git a/test/jdk/javax/swing/JTree/bug4270654.java b/test/jdk/javax/swing/JTree/bug4270654.java new file mode 100644 index 0000000000000..413626d788158 --- /dev/null +++ b/test/jdk/javax/swing/JTree/bug4270654.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4270654 + * @summary Tests that selection change in JTree does not cause unnecessary + scrolling. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4270654 + */ + +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTree; +import javax.swing.tree.DefaultMutableTreeNode; + +public class bug4270654 { + static final String INSTRUCTIONS = """ + Select the "dan" node and scroll to the right a little using the + scrollbar. Then press down arrow key. If the tree unscrolls back + to the left, the test FAILS. Otherwise, the test PASSES. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug4270654 Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug4270654::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + JFrame f = new JFrame("JTree Scroll Back Test"); + DefaultMutableTreeNode root = new DefaultMutableTreeNode("root"); + String[] lev1 = {"foo", "bar", "dan"}; + String[][] lev2 = { + {}, + {"small", "a nice big node for testing"}, + {"xyz", "pqd", "a really really big node for testing"}}; + for (int i = 0; i < lev1.length; i++) { + DefaultMutableTreeNode child = new DefaultMutableTreeNode(lev1[i]); + root.add(child); + for (int j = 0; j < lev2[i].length; j++) + child.add(new DefaultMutableTreeNode(lev2[i][j])); + } + final JTree tree = new JTree(root); + tree.expandRow(3); + f.add(new JScrollPane(tree)); + f.setSize(200, 200); + return f; + } +} diff --git a/test/jdk/javax/swing/JTree/4314199/bug4314199.java b/test/jdk/javax/swing/JTree/bug4314199.java similarity index 58% rename from test/jdk/javax/swing/JTree/4314199/bug4314199.java rename to test/jdk/javax/swing/JTree/bug4314199.java index ecf0dabb3429a..8dd38830d0f68 100644 --- a/test/jdk/javax/swing/JTree/4314199/bug4314199.java +++ b/test/jdk/javax/swing/JTree/bug4314199.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,42 +26,44 @@ * @test * @bug 4314199 * @summary Tests that JTree repaints correctly in a container with a JMenu - * @author Peter Zhelezniakov - * @run applet/manual=yesno bug4314199.html + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4314199 */ -import javax.swing.*; -import javax.swing.tree.*; +import java.awt.BorderLayout; +import javax.swing.Box; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JTree; +import javax.swing.UIManager; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreePath; -public class bug4314199 extends JApplet { +public class bug4314199 { - public void init() { + private static final String INSTRUCTIONS = """ + Select the last tree node (marked "Here") and click on the "Menu". + Look at the vertical line connecting nodes "Bug" and "Here". + If the connecting line does not disappear when the "Menu" drops down, + press 'Pass' else 'Fail'. """; - try { - UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); - SwingUtilities.invokeAndWait(new Runnable() { - - public void run() { - createAndShowGUI(); - } - }); - } catch (final Exception e) { - SwingUtilities.invokeLater(new Runnable() { - - public void run() { - createAndShowMessage("Test fails because of exception: " - + e.getMessage()); - } - }); - } + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); + PassFailJFrame.builder() + .title("JTree Instructions") + .instructions(INSTRUCTIONS) + .rows(6) + .splitUI(bug4314199::createAndShowGUI) + .build() + .awaitAndCheck(); } - private void createAndShowMessage(String message) { - getContentPane().add(new JLabel(message)); - } - - private void createAndShowGUI() { + private static JPanel createAndShowGUI() { JMenuBar mb = new JMenuBar(); // needed to exactly align left edge of menu and angled line of tree @@ -71,7 +73,6 @@ private void createAndShowGUI() { JMenuItem mi = new JMenuItem("MenuItem"); mn.add(mi); mb.add(mn); - setJMenuBar(mb); DefaultMutableTreeNode n1 = new DefaultMutableTreeNode("Root"); DefaultMutableTreeNode n2 = new DefaultMutableTreeNode("Duke"); @@ -87,6 +88,12 @@ private void createAndShowGUI() { JTree tree = new JTree(new DefaultTreeModel(n1)); tree.putClientProperty("JTree.lineStyle", "Angled"); tree.expandPath(new TreePath(new Object[]{n1, n2, n3})); - setContentPane(tree); + + JPanel p = new JPanel(); + p.setLayout(new BorderLayout()); + p.setSize(200, 200); + p.add(mb, BorderLayout.NORTH); + p.add(tree); + return p; } } diff --git a/test/jdk/javax/swing/JTree/bug4618767.java b/test/jdk/javax/swing/JTree/bug4618767.java new file mode 100644 index 0000000000000..d8aa52c3952f2 --- /dev/null +++ b/test/jdk/javax/swing/JTree/bug4618767.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4618767 + * @summary First letter navigation in JTree interferes with mnemonics + * @key headful + * @run main bug4618767 + */ + +import java.awt.Robot; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.KeyEvent; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JTree; +import javax.swing.SwingUtilities; +import javax.swing.event.MenuEvent; +import javax.swing.event.MenuListener; + +public class bug4618767 { + private static JFrame f; + private static final JTree tree = new + JTree(new String[] {"one", "two", "three", "four"}); + private static boolean menuSelected; + private static CountDownLatch listGainedFocusLatch = new CountDownLatch(1); + + public static void main(String[] args) throws Exception { + try { + SwingUtilities.invokeAndWait(() -> { + f = new JFrame("bug4618767 Test"); + JMenu menu = new JMenu("File"); + menu.setMnemonic('F'); + JMenuItem menuItem = new JMenuItem("item"); + menu.add(menuItem); + JMenuBar menuBar = new JMenuBar(); + menuBar.add(menu); + f.setJMenuBar(menuBar); + + menu.addMenuListener(new MenuListener() { + public void menuCanceled(MenuEvent e) {} + public void menuDeselected(MenuEvent e) {} + public void menuSelected(MenuEvent e) { + menuSelected = true; + } + }); + + tree.addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + listGainedFocusLatch.countDown(); + } + }); + f.add(tree); + f.pack(); + f.setLocationRelativeTo(null); + f.setAlwaysOnTop(true); + f.setVisible(true); + }); + runTest(); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + } + } + + private static void runTest() throws Exception { + if (!listGainedFocusLatch.await(3, TimeUnit.SECONDS)) { + throw new RuntimeException("Waited too long, but can't gain" + + " focus for list"); + } + Robot robot = new Robot(); + robot.setAutoDelay(200); + robot.waitForIdle(); + robot.keyPress(KeyEvent.VK_O); + robot.keyRelease(KeyEvent.VK_O); + robot.waitForIdle(); + robot.keyPress(KeyEvent.VK_ALT); + robot.keyPress(KeyEvent.VK_F); + robot.keyRelease(KeyEvent.VK_F); + robot.keyRelease(KeyEvent.VK_ALT); + + SwingUtilities.invokeAndWait(() -> { + if (menuSelected && !tree.getSelectionPath() + .getLastPathComponent().toString().equals("one")) { + throw new RuntimeException("Mnemonics interferes with JTree" + + " item selection using KeyEvent"); + } + }); + } +} diff --git a/test/jdk/javax/swing/JViewport/ScrollRectToVisibleTest3.java b/test/jdk/javax/swing/JViewport/ScrollRectToVisibleTest3.java new file mode 100644 index 0000000000000..bf96ab2fc46c2 --- /dev/null +++ b/test/jdk/javax/swing/JViewport/ScrollRectToVisibleTest3.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @key headful + * @bug 4217252 + * @summary Verify that scrolling beyond the visible region and scrolling + * a component smaller than the viewport is not allowed. + * @library /javax/swing/regtesthelpers + * @build Util + * @run main/othervm -Dsun.java2d.uiScale=1 ScrollRectToVisibleTest3 + */ + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.SwingUtilities; +import javax.swing.table.AbstractTableModel; + +public class ScrollRectToVisibleTest3 { + private static JFrame frame; + private static JTable table; + private static JButton scrollButton; + private static volatile int clickCount = 0; + private static final String[] EXPECTED_TEXT = {"99 x 0", "98 x 0", + "97 x 0", "96 x 0"}; + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + robot.setAutoWaitForIdle(true); + + SwingUtilities.invokeAndWait(ScrollRectToVisibleTest3::createTestUI); + robot.waitForIdle(); + robot.delay(1000); + + Rectangle frameBounds = Util.invokeOnEDT(() -> getComponentBounds(frame)); + robot.delay(100); + Point scrollBtnLoc = Util.getCenterPoint(scrollButton); + + robot.mouseMove(scrollBtnLoc.x, scrollBtnLoc.y); + robot.mousePress(MouseEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(MouseEvent.BUTTON1_DOWN_MASK); + robot.mousePress(MouseEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(MouseEvent.BUTTON1_DOWN_MASK); + robot.delay(50); + + int rowHeight = Util.invokeOnEDT(() -> table.getRowHeight()); + for (int i = 1; i <= 4; i++) { + robot.mouseMove(frameBounds.x + 50, + frameBounds.y + frameBounds.height - (rowHeight * i + 2)); + robot.delay(300); + robot.mousePress(MouseEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(MouseEvent.BUTTON1_DOWN_MASK); + // 500 ms delay added so that current mouseClicked event + // is processed successfully before proceeding to the next + robot.delay(500); + } + if (clickCount != 4) { + throw new RuntimeException("Test Failed! Expected 4 mouse clicks" + + " but got " + clickCount); + } + } + + private static void createTestUI() { + frame = new JFrame("ScrollRectToVisibleTest3"); + table = new JTable(new TestModel()); + table.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + JTable testTable = (JTable) e.getComponent(); + int row = testTable.getSelectedRow(); + int column = testTable.getSelectedColumn(); + String cellContent = testTable.getValueAt(row, column).toString(); + if (!EXPECTED_TEXT[clickCount].equals(cellContent)) { + throw new RuntimeException(("Test failed! Table Cell Content" + + " at (row %d , col %d)\n Expected: %s vs Actual: %s") + .formatted(row, column, + EXPECTED_TEXT[clickCount], cellContent)); + } + clickCount++; + } + }); + + scrollButton = new JButton("Scroll"); + scrollButton.addActionListener(ae -> { + Rectangle bounds = table.getBounds(); + bounds.y = bounds.height + table.getRowHeight(); + bounds.height = table.getRowHeight(); + System.out.println("scrolling: " + bounds); + table.scrollRectToVisible(bounds); + System.out.println("bounds: " + table.getVisibleRect()); + }); + + frame.add(scrollButton, BorderLayout.NORTH); + frame.add(new JScrollPane(table), BorderLayout.CENTER); + frame.setSize(400, 300); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + + private static class TestModel extends AbstractTableModel { + @Override + public String getColumnName(int column) { + return Integer.toString(column); + } + + @Override + public int getRowCount() { + return 100; + } + + @Override + public int getColumnCount() { + return 5; + } + + @Override + public Object getValueAt(int row, int col) { + return row + " x " + col; + } + + @Override + public boolean isCellEditable(int row, int column) { return false; } + + @Override + public void setValueAt(Object value, int row, int col) { + } + } + + private static Rectangle getComponentBounds(Component c) { + Point locationOnScreen = c.getLocationOnScreen(); + Dimension size = c.getSize(); + return new Rectangle(locationOnScreen, size); + } +} diff --git a/test/jdk/javax/swing/JViewport/SetViewRepaint.java b/test/jdk/javax/swing/JViewport/SetViewRepaint.java new file mode 100644 index 0000000000000..379a3e4e33b63 --- /dev/null +++ b/test/jdk/javax/swing/JViewport/SetViewRepaint.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4128110 + * @summary Verify that JViewport.setViewportView() and JScrollPane.setViewport() + * force a re-layout and a repaint. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual SetViewRepaint + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.GridLayout; +import java.awt.event.ActionListener; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JViewport; + +public class SetViewRepaint { + private static final String INSTRUCTIONS = """ + Verify the following two cases: + + 1) Press "JViewport.setViewportView()" button and verify that + the blue label is replaced by a scrolling list. + + 2) Press "JScrollPane.setViewport()" button and verify that + the red label is replaced by a scrolling list as well. + + In either case the display should update automatically after + pressing the button. + + If the above is true, press PASS else press FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(30) + .testUI(SetViewRepaint::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + JFrame frame = new JFrame("SetViewRepaint"); + JPanel p1 = new JPanel(new BorderLayout()); + JPanel p2 = new JPanel(new BorderLayout()); + + JLabel label1 = new ColorLabel(Color.BLUE, "Blue Label"); + final JList list1 = new JList(new String[]{"one", "two", "three", "four"}); + final JScrollPane sp1 = new JScrollPane(label1); + ActionListener doSetViewportView = e -> sp1.setViewportView(list1); + JButton b1 = new JButton("JViewport.setViewportView()"); + b1.addActionListener(doSetViewportView); + p1.add(sp1, BorderLayout.CENTER); + p1.add(b1, BorderLayout.SOUTH); + + JLabel label2 = new ColorLabel(Color.RED, "Red Label"); + final JList list2 = new JList(new String[]{"five", "six", "seven", "eight"}); + final JScrollPane sp2 = new JScrollPane(label2); + ActionListener doSetViewport = e -> { + JViewport vp = new JViewport(); + vp.setView(list2); + sp2.setViewport(vp); + }; + JButton b2 = new JButton("JScrollPane.setViewport()"); + b2.addActionListener(doSetViewport); + p2.add(sp2, BorderLayout.CENTER); + p2.add(b2, BorderLayout.SOUTH); + frame.setLayout(new GridLayout(1, 2)); + frame.add(p1); + frame.add(p2); + frame.setResizable(false); + frame.setSize(500, 120); + return frame; + } + + private static class ColorLabel extends JLabel { + ColorLabel(Color color, String text) { + super(text); + setForeground(Color.WHITE); + setBackground(color); + setOpaque(true); + setHorizontalAlignment(CENTER); + } + } +} diff --git a/test/jdk/javax/swing/JViewport/bug4137282.java b/test/jdk/javax/swing/JViewport/bug4137282.java new file mode 100644 index 0000000000000..c0cec64dd4f8e --- /dev/null +++ b/test/jdk/javax/swing/JViewport/bug4137282.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4137282 + * @summary Tests that scrollbars appear automatically when the enclosed + * component is enlarged + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4137282 + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Container; +import java.awt.Dimension; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class bug4137282 { + + private static final String INSTRUCTIONS = """ + Press the "resize" button. Two scrollbars should appear. + Press Pass if they appear and Fail otherwise."""; + + static volatile JPanel pane; + + public static void main(String[] args) throws Exception { + + PassFailJFrame.builder() + .title("JViewport Instructions") + .instructions(INSTRUCTIONS) + .rows(5) + .columns(30) + .testUI(bug4137282::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + pane = new JPanel(); + + pane.setBackground(Color.blue); + setPaneSize(100, 100); + JFrame frame = new JFrame("bug4137282"); + JScrollPane sp = new JScrollPane(pane); + + JButton b = new JButton("resize"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent ev) { + setPaneSize(300, 300); + } + }); + + frame.add(b, BorderLayout.NORTH); + frame.add(sp, BorderLayout.CENTER); + frame.pack(); + return frame; + } + + static void setPaneSize(int w, int h) { + pane.setPreferredSize(new Dimension(w, h)); + pane.setSize(w, h); + } +} diff --git a/test/jdk/javax/swing/JViewport/bug4217252.java b/test/jdk/javax/swing/JViewport/bug4217252.java new file mode 100644 index 0000000000000..3eb38125de3cf --- /dev/null +++ b/test/jdk/javax/swing/JViewport/bug4217252.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4217252 + * @summary Tests + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4217252 + */ + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.Rectangle; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.table.AbstractTableModel; + +public class bug4217252 { + + private static final String INSTRUCTIONS = """ + Click on the 'Scroll' button and then click it again. + If you see row 98 and 99 twice, then test failed, otherwise it passed."""; + + static class TestModel extends AbstractTableModel { + + public String getColumnName(int column) { + return Integer.toString(column); + } + + public int getRowCount() { + return 100; + } + + public int getColumnCount() { + return 5; + } + + public Object getValueAt(int row, int col) { + return row + " x " + col; + } + + public boolean isCellEditable(int row, int column) { return false; } + + public void setValueAt(Object value, int row, int col) { } + } + + public static void main(String[] args) throws Exception { + + PassFailJFrame.builder() + .title("JViewport Instructions") + .instructions(INSTRUCTIONS) + .rows(5) + .columns(30) + .testUI(bug4217252::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI(){ + JFrame frame = new JFrame("bug4217252"); + final JTable table = new JTable(new TestModel()); + JButton scrollButton = new JButton("Scroll"); + scrollButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent ae) { + Rectangle bounds = table.getBounds(); + bounds.y = bounds.height + table.getRowHeight(); + bounds.height = table.getRowHeight(); + table.scrollRectToVisible(bounds); + } + }); + frame.getContentPane().add(new JScrollPane(table), + BorderLayout.CENTER); + frame.getContentPane().add(scrollButton, BorderLayout.SOUTH); + frame.pack(); + return frame; + } + +} diff --git a/test/jdk/javax/swing/JViewport/bug4237176.java b/test/jdk/javax/swing/JViewport/bug4237176.java new file mode 100644 index 0000000000000..7129dd5897fd3 --- /dev/null +++ b/test/jdk/javax/swing/JViewport/bug4237176.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4237176 + * @summary Tests that background color is set properly for JViewport + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4237176 + */ + +import java.awt.Color; +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.JViewport; + +public class bug4237176 { + + private static final String INSTRUCTIONS = """ + Look at the empty space below the table. It should be blue. + If it is, test passes, otherwise it fails."""; + + public static void main(String[] args) throws Exception { + + PassFailJFrame.builder() + .title("JViewport Instructions") + .instructions(INSTRUCTIONS) + .rows(5) + .columns(30) + .testUI(bug4237176::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + JFrame frame = new JFrame("Color Demo"); + JTable table = new JTable(new Object[][]{{"one", "two"}}, new Object[]{"A", "B"}); + JScrollPane sp = new JScrollPane(table); + JViewport vp = sp.getViewport(); + vp.setBackground(Color.blue); + vp.setOpaque(true); + + frame.getContentPane().add(sp); + frame.pack(); + return frame; + } + +} diff --git a/test/jdk/javax/swing/JViewport/bug4243479.java b/test/jdk/javax/swing/JViewport/bug4243479.java new file mode 100644 index 0000000000000..7521312368105 --- /dev/null +++ b/test/jdk/javax/swing/JViewport/bug4243479.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4243479 + * @summary Tests that JViewport do not blit when not visible + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4243479 + */ + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.FlowLayout; +import java.awt.Point; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTabbedPane; +import javax.swing.JTextArea; +import javax.swing.JViewport; + +public class bug4243479 { + private static final String INSTRUCTIONS = """ + The tabbed pane contains two tabs: "button" and "scrollpane". + The second contains some text inserted into a scrollpane. + Press the "Press here" button. + If a piece of scrollpane appears, press Fail else press Pass."""; + + private static JFrame createTestUI() { + char[] text = new char[20000]; + for (int counter = 0; counter < text.length; counter++) { + if (counter % 80 == 0) { + text[counter] = '\n'; + } + else { + text[counter] = 'a'; + } + } + JScrollPane sp = new JScrollPane(new JTextArea + (new String(text))); + final JViewport vp = sp.getViewport(); + vp.putClientProperty("EnableWindowBlit", Boolean.TRUE); + JTabbedPane tp = new JTabbedPane(); + JPanel panel = new JPanel(new FlowLayout()); + JButton button = new JButton("Press here"); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent ae) { + Point location = vp.getViewPosition(); + location.y += 100; + vp.setViewPosition(location); + } + }); + panel.add(button); + tp.add("button", panel); + tp.add("scrollpane", sp); + JFrame frame = new JFrame("4243479 Test"); + frame.getContentPane().add(tp); + frame.pack(); + return frame; + } + + public static void main(String[] argv) throws Exception { + PassFailJFrame.builder() + .title("JViewport Instructions") + .instructions(INSTRUCTIONS) + .rows(5) + .columns(30) + .position(PassFailJFrame.Position.TOP_LEFT_CORNER) + .testUI(bug4243479::createTestUI) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/javax/swing/JViewport/bug4750421.java b/test/jdk/javax/swing/JViewport/bug4750421.java new file mode 100644 index 0000000000000..950cc4f1d69e3 --- /dev/null +++ b/test/jdk/javax/swing/JViewport/bug4750421.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4750421 + * @summary 4143833 - regression in 1.4.x + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4750421 + */ + +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.JViewport; + +public class bug4750421 { + + private static final String INSTRUCTIONS = """ + A table will be shown. + Select the third row of the table. + Then press down arrow button of vertical scrollbar to scroll down. + (in macos drag the vertical scrollbar down via mouse just enough + to scroll by 1 unit as there is no arrow button in scrollbar) + If the selection disappears press Fail else press Pass."""; + + private static JFrame createTestUI() { + JFrame frame = new JFrame("bug4750421"); + JTable table = new JTable(30, 10); + table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + JScrollPane pane = new JScrollPane(table); + frame.getContentPane().add(pane); + pane.getViewport().setScrollMode(JViewport.BACKINGSTORE_SCROLL_MODE); + frame.pack(); + return frame; + } + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("JViewport Instructions") + .instructions(INSTRUCTIONS) + .rows(7) + .columns(35) + .testUI(bug4750421::createTestUI) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/javax/swing/JWindow/bug4251781.java b/test/jdk/javax/swing/JWindow/bug4251781.java new file mode 100644 index 0000000000000..0152db71f2497 --- /dev/null +++ b/test/jdk/javax/swing/JWindow/bug4251781.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4251781 + * @summary Tests that JWindow repaint is optimized (background is not + cleared). + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4251781 + */ + +import java.awt.Color; +import java.awt.Container; +import javax.swing.JButton; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.JWindow; + +public class bug4251781 { + private static final String INSTRUCTIONS = """ + Press the button at the bottom-right corner of the gray + window with the mouse. + If the window DOES NOT flicker when you press and/or release + the mouse button press PASS else FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug4251781::createAndShowUI) + .build() + .awaitAndCheck(); + } + + private static JWindow createAndShowUI() { + JWindow w = new JWindow(); + final Container pane = w.getContentPane(); + pane.setLayout(null); + pane.setBackground(Color.GRAY.darker()); + + final JPopupMenu popup = new JPopupMenu(); + popup.add(new JMenuItem("item 1")); + popup.add(new JMenuItem("exit")); + + JButton b = new JButton("menu"); + b.setBounds(350, 250, 50, 50); + b.addActionListener(ev -> popup.show(pane, 0, 0)); + pane.add(b); + + w.setSize(400, 300); + return w; + } +} diff --git a/test/jdk/javax/swing/Popup/6514582/bug6514582.java b/test/jdk/javax/swing/Popup/6514582/bug6514582.java index b2d5d57984fdf..a54bfe8d1f331 100644 --- a/test/jdk/javax/swing/Popup/6514582/bug6514582.java +++ b/test/jdk/javax/swing/Popup/6514582/bug6514582.java @@ -4,9 +4,7 @@ * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/test/jdk/javax/swing/Popup/TaskbarPositionTest.java b/test/jdk/javax/swing/Popup/TaskbarPositionTest.java index 5378f61c96f77..a649956748360 100644 --- a/test/jdk/javax/swing/Popup/TaskbarPositionTest.java +++ b/test/jdk/javax/swing/Popup/TaskbarPositionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,7 +37,11 @@ import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import javax.imageio.ImageIO; import javax.swing.AbstractAction; import javax.swing.JComboBox; import javax.swing.JFrame; @@ -337,11 +341,11 @@ public static void main(String[] args) throws Throwable { } } - try { - // Use Robot to automate the test - Robot robot = new Robot(); - robot.setAutoDelay(50); + // Use Robot to automate the test + Robot robot = new Robot(); + robot.setAutoDelay(50); + try { SwingUtilities.invokeAndWait(TaskbarPositionTest::new); robot.waitForIdle(); @@ -442,6 +446,9 @@ public static void main(String[] args) throws Throwable { hidePopup(robot); robot.waitForIdle(); + } catch (Throwable t) { + saveScreenCapture(robot, screens); + throw t; } finally { SwingUtilities.invokeAndWait(() -> { if (frame != null) { @@ -450,4 +457,17 @@ public static void main(String[] args) throws Throwable { }); } } + + private static void saveScreenCapture(Robot robot, GraphicsDevice[] screens) { + for (int i = 0; i < screens.length; i++) { + Rectangle bounds = screens[i].getDefaultConfiguration() + .getBounds(); + BufferedImage image = robot.createScreenCapture(bounds); + try { + ImageIO.write(image, "png", new File("Screenshot.png")); + } catch (IOException e) { + e.printStackTrace(); + } + } + } } diff --git a/test/jdk/javax/swing/ProgressMonitor/bug4401480.java b/test/jdk/javax/swing/ProgressMonitor/bug4401480.java new file mode 100644 index 0000000000000..74b47107e77c6 --- /dev/null +++ b/test/jdk/javax/swing/ProgressMonitor/bug4401480.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4401480 + * @summary Tests that closing ProgressMonitor dialog cancels it + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4401480 + */ + +import java.lang.reflect.InvocationTargetException; +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.ProgressMonitor; +import javax.swing.SwingUtilities; + +public class bug4401480 { + private static ProgressMonitor monitor; + private static volatile boolean cancelled = false; + + private static final String INSTRUCTIONS = """ + This is a semi-automated test which automatically + passes if closing the JProgressBar dialog cancels it. + Read the following test instructions and when ready + click on the Start button below. + + After clicking on Start button wait for few seconds for + progress monitor (a dialog with progress bar) to appear. + Close it by clicking on the window close button. + DO NOT click on Cancel button. + + NOTE: + Ensure to click on the window close button before + progress bar reaches its max limit. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("JProgress Monitor Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .splitUIBottom(bug4401480::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JPanel createTestUI() { + JPanel panel = new JPanel(); + JButton startButton = new JButton("Start"); + startButton.addActionListener(e -> { + monitor = new ProgressMonitor(null, "Progress", "Running ...", 0, 10); + monitor.setProgress(0); + + new Thread(() -> { + for (int i = 0; i < 10; i++) { + int count = i; + try { + SwingUtilities.invokeAndWait(() -> + monitor.setProgress(count)); + Thread.sleep(2000); + SwingUtilities.invokeAndWait(() -> + cancelled = monitor.isCanceled()); + } catch (InterruptedException + | InvocationTargetException ex) { + throw new RuntimeException(ex); + } + if (cancelled) { + break; + } + } + + if (cancelled) { + PassFailJFrame.forcePass(); + } else { + PassFailJFrame.forceFail("Test Failed! JProgress Monitor" + + " was not cancelled"); + } + }).start(); + }); + panel.add(startButton); + return panel; + } +} diff --git a/test/jdk/javax/swing/SwingUtilities/bug4369355.java b/test/jdk/javax/swing/SwingUtilities/bug4369355.java new file mode 100644 index 0000000000000..f55d888ef3179 --- /dev/null +++ b/test/jdk/javax/swing/SwingUtilities/bug4369355.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @key headful + * @bug 4369355 + * @summary To verify if SwingUtilities.convertPointToScreen() (for invisible frame) + * and SwingUtilities.convertPointFromScreen() return correct values + * @run main bug4369355 + */ + +import java.awt.Point; +import java.awt.Robot; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +public class bug4369355 { + private static JFrame frame; + + private static volatile Point frameToScreenLoc; + private static volatile Point frameFromScreenLoc; + + private static final Point EXPECTED_FROM_SCREEN_LOC = new Point(0, 0); + private static final Point EXPECTED_TO_SCREEN_LOC = new Point(100, 100); + + public static void main (String[] args) throws Exception { + try { + Robot robot = new Robot(); + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame("bug4369355"); + frame.setBounds(100, 100, 100, 100); + }); + robot.waitForIdle(); + robot.delay(1000); + + SwingUtilities.invokeAndWait(() -> { + frameToScreenLoc = new Point(0, 0); + SwingUtilities.convertPointToScreen(frameToScreenLoc, frame); + }); + robot.delay(100); + + if (!frameToScreenLoc.equals(EXPECTED_TO_SCREEN_LOC)) { + throw new RuntimeException("SwingUtilities.convertPointToScreen()" + + " returns incorrect point " + frameToScreenLoc + "\n" + + "Should be " + EXPECTED_TO_SCREEN_LOC); + } + + SwingUtilities.invokeAndWait(() -> frame.setVisible(true)); + robot.delay(500); + + SwingUtilities.invokeAndWait(() -> { + frameFromScreenLoc = frame.getLocationOnScreen(); + SwingUtilities.convertPointFromScreen(frameFromScreenLoc, frame); + }); + robot.delay(100); + + if (!frameFromScreenLoc.equals(EXPECTED_FROM_SCREEN_LOC)) { + throw new RuntimeException("SwingUtilities.convertPointFromScreen()" + + " returns incorrect point " + frameFromScreenLoc + "\n" + + "Should be " + EXPECTED_FROM_SCREEN_LOC); + } + } finally { + SwingUtilities.invokeAndWait(frame::dispose); + } + } +} diff --git a/test/jdk/javax/swing/SwingUtilities/bug4967768.java b/test/jdk/javax/swing/SwingUtilities/bug4967768.java new file mode 100644 index 0000000000000..6ce1f5c787bd3 --- /dev/null +++ b/test/jdk/javax/swing/SwingUtilities/bug4967768.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4967768 + * @requires (os.family != "mac") + * @summary Tests that underline is painted correctly in mnemonics + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4967768 + */ + +import java.awt.Font; +import javax.swing.JButton; +import javax.swing.JPanel; + +public class bug4967768 { + private static final String INSTRUCTIONS = """ + When the test starts you'll see a button "Oops". + + For Windows and GTK Look and Feel, you will need to + press the ALT key to make the mnemonic visible. + Once the ALT key is pressed, the letter "p" will be + underlined at the bottom of the instruction frame. + + Ensure the underline cuts through the descender + of letter "p", i.e. the underline is painted + not below the letter but below the baseline. + + Press Pass if you see the expected behaviour else + press Fail. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(35) + .splitUIBottom(bug4967768::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JPanel createTestUI() { + JPanel panel = new JPanel(); + JButton but = new JButton("Oops"); + but.setFont(new Font("Dialog", Font.BOLD, 24)); + but.setMnemonic('p'); + panel.add(but); + return panel; + } +} diff --git a/test/jdk/javax/swing/ToolTipManager/bug4250178.java b/test/jdk/javax/swing/ToolTipManager/bug4250178.java new file mode 100644 index 0000000000000..18a9c4d93e2cf --- /dev/null +++ b/test/jdk/javax/swing/ToolTipManager/bug4250178.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4250178 + * @summary Tooltip in incorrect location on ToolBar + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4250178 + */ + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.ToolTipManager; + +public class bug4250178 { + private static final String INSTRUCTIONS = """ + Click somewhere in the test UI frame to make it focused. + Move mouse to the bottom right corner of the button in this frame + and wait until tooltip appears. + + If the tooltip fits into the frame OR partially covered by the mouse + cursor then test fails. Otherwise test passes. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(bug4250178::createAndShowUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createAndShowUI() { + JFrame fr = new JFrame("bug4250178"); + JButton button = new JButton("Button"); + button.setToolTipText("ToolTip"); + ToolTipManager.sharedInstance().setLightWeightPopupEnabled(true); + fr.add(button); + fr.setSize(250, 100); + return fr; + } +} diff --git a/test/jdk/javax/swing/ToolTipManager/bug4294808.java b/test/jdk/javax/swing/ToolTipManager/bug4294808.java new file mode 100644 index 0000000000000..d4ec109020a29 --- /dev/null +++ b/test/jdk/javax/swing/ToolTipManager/bug4294808.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4294808 + * @summary Tooltip blinking. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4294808 + */ + +import java.awt.Dimension; +import javax.swing.JButton; +import javax.swing.JComponent; + +public class bug4294808 { + private static final String INSTRUCTIONS = """ + Move mouse cursor to the button named "Tooltip Button" + at the bottom of the instruction window and wait until + tooltip has appeared. + + If tooltip appears and eventually disappears without + rapid blinking then press PASS else FAIL. + """; + + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug4294808 Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .splitUIBottom(bug4294808::createAndShowUI) + .build() + .awaitAndCheck(); + } + + private static JComponent createAndShowUI() { + JButton bt = new JButton("Tooltip Button"); + bt.setToolTipText("Long tooltip text here"); + bt.setPreferredSize(new Dimension(200, 60)); + return bt; + } +} diff --git a/test/jdk/javax/swing/ToolTipManager/bug6178004.java b/test/jdk/javax/swing/ToolTipManager/bug6178004.java new file mode 100644 index 0000000000000..e33a6391e8959 --- /dev/null +++ b/test/jdk/javax/swing/ToolTipManager/bug6178004.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4148057 6178004 + * @summary REGRESSION: setToolTipText does not work if the + * component is not focused + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug6178004 + */ + +import java.awt.Point; +import java.awt.Window; +import java.awt.event.MouseEvent; +import java.util.List; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.LookAndFeel; +import javax.swing.SwingUtilities; +import javax.swing.ToolTipManager; +import javax.swing.UIManager; + +public class bug6178004 { + private static JFrame frame1; + private static JFrame frame2; + private static final int SIZE = 300; + + private static final String INSTRUCTIONS = """ + You can change Look And Feel using the menu "Change LaF". + + Make sure that Frame2 or instruction window is active. + Move mouse over the button inside "Frame 1". + If tooltip is NOT shown or Frame 1 jumped on top of + the Frame2, press FAIL. + + For Metal/Windows LaF: + Tooltips are shown only if one of the frames (or the instruction + window) is active. To test it click on any other application to + make frames and instruction window inactive and then verify that + tooltips are not shown any more. + + For Motif/GTK/Nimbus/Aqua LaF: + Tooltips should be shown for all frames irrespective of whether + the application is active or inactive. + + Note: Tooltip for Frame1 is always shown at the top-left corner. + Tooltips could be shown partly covered by another frame. + + If above is true press PASS else FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug6178004 Test Instructions") + .instructions(INSTRUCTIONS) + .testTimeOut(10) + .columns(40) + .testUI(createAndShowUI()) + .positionTestUI(bug6178004::positionTestWindows) + .build() + .awaitAndCheck(); + } + + private static List createAndShowUI() { + ToolTipManager.sharedInstance().setInitialDelay(0); + + frame1 = new JFrame("bug6178004 Frame1"); + frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + JButton button = new JButton("Test") { + public Point getToolTipLocation(MouseEvent event) { + return new Point(10, 10); + } + }; + button.setToolTipText("Tooltip-1"); + frame1.add(button); + frame1.setSize(SIZE, SIZE); + + frame2 = new JFrame("bug6178004 Frame2"); + frame2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + JButton button2 = new JButton("Click me") ; + button2.setToolTipText("Tooltip-2"); + frame2.add(button2); + frame2.setSize(SIZE, SIZE); + + JMenuBar bar = new JMenuBar(); + JMenu lafMenu = new JMenu("Change LaF"); + ButtonGroup lafGroup = new ButtonGroup(); + + LookAndFeel currentLaf = UIManager.getLookAndFeel(); + UIManager.LookAndFeelInfo[] lafs = UIManager.getInstalledLookAndFeels(); + for (final UIManager.LookAndFeelInfo lafInfo : lafs) { + JCheckBoxMenuItem lafItem = new JCheckBoxMenuItem(lafInfo.getName()); + lafItem.addActionListener(e -> setLaF(lafInfo.getClassName())); + if (lafInfo.getClassName().equals(currentLaf.getClass().getName())) { + lafItem.setSelected(true); + } + + lafGroup.add(lafItem); + lafMenu.add(lafItem); + } + + bar.add(lafMenu); + frame2.setJMenuBar(bar); + return List.of(frame1, frame2); + } + + private static void setLaF(String laf) { + try { + UIManager.setLookAndFeel(laf); + SwingUtilities.updateComponentTreeUI(frame1); + SwingUtilities.updateComponentTreeUI(frame2); + } catch (Exception e) { + e.printStackTrace(); + } + } + + // custom window layout required for this test + private static void positionTestWindows(List testWindows, + PassFailJFrame.InstructionUI instructionUI) { + int gap = 5; + int x = instructionUI.getLocation().x + instructionUI.getSize().width + gap; + // the two test frames need to overlap for this test + testWindows.get(0).setLocation(x, instructionUI.getLocation().y); + testWindows.get(1).setLocation((x + SIZE / 2), instructionUI.getLocation().y); + } +} diff --git a/test/jdk/javax/swing/border/EtchedBorder/ScaledEtchedBorderTest.java b/test/jdk/javax/swing/border/EtchedBorder/ScaledEtchedBorderTest.java index 4db602f0e4dd0..283e80d7ea663 100644 --- a/test/jdk/javax/swing/border/EtchedBorder/ScaledEtchedBorderTest.java +++ b/test/jdk/javax/swing/border/EtchedBorder/ScaledEtchedBorderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,6 @@ import java.awt.BorderLayout; import java.awt.Color; -import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Point; @@ -31,30 +30,36 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import javax.imageio.ImageIO; -import javax.swing.BorderFactory; import javax.swing.Box; -import javax.swing.BoxLayout; +import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities; +import static javax.swing.BorderFactory.createEmptyBorder; +import static javax.swing.BorderFactory.createEtchedBorder; + /* * @test - * @bug 8279614 + * @bug 8279614 8294921 * @summary The left line of the TitledBorder is not painted on 150 scale factor * @requires (os.family == "windows") * @run main ScaledEtchedBorderTest */ - public class ScaledEtchedBorderTest { - public static final Dimension SIZE = new Dimension(120, 20); + private static final Dimension SIZE = new Dimension(125, 25); - public static Color highlight = Color.RED; - public static Color shadow = Color.BLUE; + private static final Color OUTER_COLOR = Color.BLACK; + private static final Color INSIDE_COLOR = Color.WHITE; + private static final Color HIGHLIGHT = Color.RED; + private static final Color SHADOW = Color.BLUE; + private static final Color TRANSPARENT_COLOR = new Color(0x00000000, true); private static final double[] scales = {1.00, 1.25, 1.50, 1.75, 2.00, 2.50, 3.00}; @@ -66,165 +71,256 @@ public class ScaledEtchedBorderTest { new ArrayList<>(4); public static void main(String[] args) throws Exception { - boolean showFrame = args.length > 0 && "-show".equals(args[0]); - SwingUtilities.invokeAndWait(() -> testScaling(showFrame)); + Collection params = Arrays.asList(args); + final boolean showFrame = params.contains("-show"); + final boolean saveImages = params.contains("-save"); + SwingUtilities.invokeAndWait(() -> testScaling(showFrame, saveImages)); } - private static void testScaling(boolean show) { - createGUI(show); + private static void testScaling(boolean showFrame, boolean saveImages) { + JComponent content = createUI(); + if (showFrame) { + showFrame(content); + } + + paintToImages(content, saveImages); + verifyBorderRendering(saveImages); + } - for (int i = 0; i < scales.length; i++) { + private static void verifyBorderRendering(final boolean saveImages) { + String errorMessage = null; + int errorCount = 0; + for (int i = 0; i < images.size(); i++) { BufferedImage img = images.get(i); double scaling = scales[i]; - System.out.println("Testing scaling: " + scaling); - + try { + int thickness = (int) Math.floor(scaling); - // checking vertical border - int x = SIZE.width / 2; - checkVerticalBorder(x, img, scaling); + checkVerticalBorders(SIZE.width / 2, thickness, img); - for (Point p : panelLocations) { - int y = (int) (p.y * scaling) + SIZE.height / 2; - checkHorizontalBorder(y, img, scaling); - } - } - } - - private static void checkHorizontalBorder(int y, BufferedImage img, double scaling) { - int thickness = 0; - boolean checkShadow = false; - boolean checkHighlight = false; - for (int x = 0; x < img.getWidth(); x++) { - int color = img.getRGB(x, y); - if (!checkHighlight && !checkShadow) { - if (color == shadow.getRGB()) { - checkHighlight = true; - thickness++; - } else if (color == highlight.getRGB()) { - throw new RuntimeException("Horizontal Border was clipped or overdrawn."); + for (Point p : panelLocations) { + int y = (int) (p.y * scaling) + SIZE.height / 2; + checkHorizontalBorder(y, thickness, img); } - } else if (checkHighlight) { - if (color == shadow.getRGB()) { - thickness++; - } else if (color == highlight.getRGB()) { - verifyThickness(x, y, thickness, scaling, "Horizontal"); - checkHighlight = false; - checkShadow = true; - thickness = 1; - } else { - throw new RuntimeException("Horizontal Border has empty space between highlight and shadow."); + } catch (Error e) { + if (errorMessage == null) { + errorMessage = e.getMessage(); } - } else { - if (color == shadow.getRGB()) { - throw new RuntimeException("Border colors reversed."); - } else if (color == highlight.getRGB()) { - thickness++; - } else { - verifyThickness(x, y, thickness, scaling, "Horizontal"); - checkShadow = false; - thickness = 0; + errorCount++; + + System.err.printf("Scaling: %.2f\n", scaling); + e.printStackTrace(); + + // Save the image if it wasn't already saved + if (!saveImages) { + saveImage(img, getImageFileName(scaling)); } } } - } - private static void verifyThickness(int x, int y, int thickness, double scaling, String orientation) { - int expected = (int) Math.floor(scaling); - if (thickness != expected) { - throw new RuntimeException("Unexpected " + orientation + " Border thickness at x:" - + x + " y: " + y + ". Expected: " + expected + " Actual: " + thickness); + if (errorCount > 0) { + throw new Error("Test failed: " + + errorCount + " error(s) detected - " + + errorMessage); } } - private static void checkVerticalBorder(int x, BufferedImage img, double scaling) { - int thickness = 0; - boolean checkShadow = false; - boolean checkHighlight = false; - for (int y = 0; y < img.getHeight(); y++) { - int color = img.getRGB(x, y); - if (!checkHighlight && !checkShadow) { - if (color == shadow.getRGB()) { - checkHighlight = true; - thickness++; - } else if (color == highlight.getRGB()) { - throw new RuntimeException("Vertical Border was clipped or overdrawn."); - } - } else if (checkHighlight) { - if (color == shadow.getRGB()) { - thickness++; - } else if (color == highlight.getRGB()) { - verifyThickness(x, y, thickness, scaling, "Vertical"); - checkHighlight = false; - checkShadow = true; - thickness = 1; - } else { - throw new RuntimeException("Vertical Border has empty space between highlight and shadow."); - } - } else { - if (color == shadow.getRGB()) { - throw new RuntimeException("Border colors reversed."); - } else if (color == highlight.getRGB()) { - thickness++; - } else { - verifyThickness(x, y, thickness, scaling, "Vertical"); - checkShadow = false; - thickness = 0; + private static void checkVerticalBorders(final int x, + final int thickness, + final BufferedImage img) { + checkBorder(x, 0, + 0, 1, + thickness, img); + } + + private static void checkHorizontalBorder(final int y, + final int thickness, + final BufferedImage img) { + checkBorder(0, y, + 1, 0, + thickness, img); + } + + private enum State { + BACKGROUND, + LEFT_SHADOW, LEFT_HIGHLIGHT, + INSIDE, + RIGHT_SHADOW, RIGHT_HIGHLIGHT + } + + private static void checkBorder(final int xStart, final int yStart, + final int xStep, final int yStep, + final int thickness, + final BufferedImage img) { + final int width = img.getWidth(); + final int height = img.getHeight(); + + State state = State.BACKGROUND; + int borderThickness = 0; + + int x = xStart; + int y = yStart; + do { + do { + final int color = img.getRGB(x, y); + switch (state) { + case BACKGROUND: + if (color == SHADOW.getRGB()) { + state = State.LEFT_SHADOW; + borderThickness = 1; + } else if (color != OUTER_COLOR.getRGB() + && color != TRANSPARENT_COLOR.getRGB()) { + throwUnexpectedColor(x, y, color); + } + break; + + case LEFT_SHADOW: + if (color == SHADOW.getRGB()) { + borderThickness++; + } else if (color == HIGHLIGHT.getRGB()) { + if (borderThickness != thickness) { + throwWrongThickness(thickness, borderThickness, x, y); + } + borderThickness = 1; + state = State.LEFT_HIGHLIGHT; + } else { + throwUnexpectedColor(x, y, color); + } + break; + + case LEFT_HIGHLIGHT: + if (color == HIGHLIGHT.getRGB()) { + borderThickness++; + } else if (color == INSIDE_COLOR.getRGB()) { + if (borderThickness != thickness) { + throwWrongThickness(thickness, borderThickness, x, y); + } + borderThickness = 0; + state = State.INSIDE; + } else { + throwUnexpectedColor(x, y, color); + } + break; + + case INSIDE: + if (color == SHADOW.getRGB()) { + state = State.RIGHT_SHADOW; + borderThickness = 1; + } else if (color != INSIDE_COLOR.getRGB()) { + throwUnexpectedColor(x, y, color); + } + break; + + case RIGHT_SHADOW: + if (color == SHADOW.getRGB()) { + borderThickness++; + } else if (color == HIGHLIGHT.getRGB()) { + if (borderThickness != thickness) { + throwWrongThickness(thickness, borderThickness, x, y); + } + borderThickness = 1; + state = State.RIGHT_HIGHLIGHT; + } else { + throwUnexpectedColor(x, y, color); + } + break; + + case RIGHT_HIGHLIGHT: + if (color == HIGHLIGHT.getRGB()) { + borderThickness++; + } else if (color == OUTER_COLOR.getRGB()) { + if (borderThickness != thickness) { + throwWrongThickness(thickness, borderThickness, x, y); + } + borderThickness = 0; + state = State.BACKGROUND; + } else { + throwUnexpectedColor(x, y, color); + } + break; } - } - } + } while (yStep > 0 && ((y += yStep) < height)); + } while (xStep > 0 && ((x += xStep) < width)); + } + + private static void throwWrongThickness(int thickness, int borderThickness, + int x, int y) { + throw new Error( + String.format("Wrong border thickness at %d, %d: %d vs %d", + x, y, borderThickness, thickness)); } - private static void createGUI(boolean show) { - // Render content panel - JPanel contentPanel = new JPanel(); - contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS)); + private static void throwUnexpectedColor(int x, int y, int color) { + throw new Error( + String.format("Unexpected color at %d, %d: %08x", + x, y, color)); + } + + private static JComponent createUI() { + Box contentPanel = Box.createVerticalBox(); + contentPanel.setBackground(OUTER_COLOR); Dimension childSize = null; for (int i = 0; i < 4; i++) { + JComponent filler = new JPanel(null); + filler.setBackground(INSIDE_COLOR); + filler.setPreferredSize(SIZE); + filler.setBounds(i, 0, SIZE.width, SIZE.height); + filler.setBorder(createEtchedBorder(HIGHLIGHT, SHADOW)); + JPanel childPanel = new JPanel(new BorderLayout()); - childPanel.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createEmptyBorder(0, i, 4, 4), - BorderFactory.createEtchedBorder(highlight, shadow))); - childPanel.add(Box.createRigidArea(SIZE), BorderLayout.CENTER); + childPanel.setBorder(createEmptyBorder(0, i, 4, 4)); + childPanel.add(filler, BorderLayout.CENTER); + childPanel.setBackground(OUTER_COLOR); contentPanel.add(childPanel); if (childSize == null) { childSize = childPanel.getPreferredSize(); } - childPanel.setBounds(0, childSize.height * i, childSize.width, childSize.height); + childPanel.setBounds(0, childSize.height * i, + childSize.width, childSize.height); + + panelLocations.add(childPanel.getLocation()); } contentPanel.setSize(childSize.width, childSize.height * 4); + return contentPanel; + } + + private static void showFrame(final JComponent content) { + JFrame frame = new JFrame("Scaled Etched Border Test"); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + frame.getContentPane().add(content, BorderLayout.CENTER); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private static void paintToImages(final JComponent content, + final boolean saveImages) { for (double scaling : scales) { - // Create BufferedImage - BufferedImage buff = new BufferedImage((int) Math.ceil(contentPanel.getWidth() * scaling), - (int) Math.ceil(contentPanel.getHeight() * scaling), - BufferedImage.TYPE_INT_ARGB); - Graphics2D graph = buff.createGraphics(); - graph.scale(scaling, scaling); - // Painting panel onto BufferedImage - contentPanel.paint(graph); - graph.dispose(); - // Save each image ? -- Here it's useful for debugging - saveImage(buff, String.format("test%.2f.png", scaling)); - images.add(buff); - } - // Save coordinates of the panels - for (Component comp : contentPanel.getComponents()) { - panelLocations.add(comp.getLocation()); - } + BufferedImage image = + new BufferedImage((int) Math.ceil(content.getWidth() * scaling), + (int) Math.ceil(content.getHeight() * scaling), + BufferedImage.TYPE_INT_ARGB); + + Graphics2D g2d = image.createGraphics(); + g2d.scale(scaling, scaling); + content.paint(g2d); + g2d.dispose(); - if (show) { - JFrame frame = new JFrame("Swing Test"); - frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - frame.getContentPane().add(contentPanel, BorderLayout.CENTER); - frame.pack(); - frame.setLocationRelativeTo(null); - frame.setVisible(true); + if (saveImages) { + saveImage(image, getImageFileName(scaling)); + } + images.add(image); } } + private static String getImageFileName(final double scaling) { + return String.format("test%.2f.png", scaling); + } + private static void saveImage(BufferedImage image, String filename) { try { ImageIO.write(image, "png", new File(filename)); diff --git a/test/jdk/javax/swing/border/LineBorder/ScaledLineBorderTest.java b/test/jdk/javax/swing/border/LineBorder/ScaledLineBorderTest.java index 2d858a8039b4e..80faeed460ebb 100644 --- a/test/jdk/javax/swing/border/LineBorder/ScaledLineBorderTest.java +++ b/test/jdk/javax/swing/border/LineBorder/ScaledLineBorderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,6 @@ import java.awt.BorderLayout; import java.awt.Color; -import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Point; @@ -43,15 +42,18 @@ import javax.swing.JPanel; import javax.swing.SwingUtilities; +import static sun.java2d.pipe.Region.clipRound; + /* * @test - * @bug 8282958 + * @bug 8282958 8349188 * @summary Verify LineBorder edges have the same width * @requires (os.family == "windows") + * @modules java.desktop/sun.java2d.pipe * @run main ScaledLineBorderTest */ public class ScaledLineBorderTest { - private static final Dimension SIZE = new Dimension(120, 25); + private static final Dimension SIZE = new Dimension(250, 50); private static final Color OUTER_COLOR = Color.BLACK; private static final Color BORDER_COLOR = Color.RED; @@ -60,12 +62,19 @@ public class ScaledLineBorderTest { private static final double[] scales = {1.00, 1.25, 1.50, 1.75, 2.00, 2.50, 3.00}; + private static final int[] thickness = {1, 4, 10, 15}; - private static final List images = - new ArrayList<>(scales.length); + private record TestImage(BufferedImage image, + List panelLocations, + double scale, + int thickness) { + } + + private record TestUI(JComponent content, + List panelLocations, + int thickness) { + } - private static final List panelLocations = - new ArrayList<>(4); public static void main(String[] args) throws Exception { Collection params = Arrays.asList(args); @@ -75,29 +84,38 @@ public static void main(String[] args) throws Exception { } private static void testScaling(boolean showFrame, boolean saveImages) { - JComponent content = createUI(); - if (showFrame) { - showFrame(content); + for (int thickness : thickness) { + TestUI testUI = createUI(thickness); + if (showFrame) { + showFrame(testUI.content); + } + + List images = paintToImages(testUI, saveImages); + verifyBorderRendering(images, saveImages); + } + + if (errorCount > 0) { + throw new Error("Test failed: " + + errorCount + " error(s) detected - " + + errorMessage); } - paintToImages(content, saveImages); - verifyBorderRendering(saveImages); } - private static void verifyBorderRendering(final boolean saveImages) { - String errorMessage = null; - int errorCount = 0; - for (int i = 0; i < images.size(); i++) { - BufferedImage img = images.get(i); - double scaling = scales[i]; - try { - int thickness = (int) Math.floor(scaling); + private static String errorMessage = null; + private static int errorCount = 0; - checkVerticalBorders(SIZE.width / 2, thickness, img); + private static void verifyBorderRendering(final List images, + final boolean saveImages) { + for (TestImage test : images) { + final BufferedImage img = test.image; + final int effectiveThickness = clipRound(test.thickness * test.scale); + try { + checkVerticalBorders((int) (SIZE.width * test.scale / 2), effectiveThickness, img); - for (Point p : panelLocations) { - int y = (int) (p.y * scaling) + SIZE.height / 2; - checkHorizontalBorder(y, thickness, img); + for (Point p : test.panelLocations) { + int y = (int) ((p.y + (SIZE.height / 2)) * test.scale); + checkHorizontalBorder(y, effectiveThickness, img); } } catch (Error e) { if (errorMessage == null) { @@ -105,21 +123,13 @@ private static void verifyBorderRendering(final boolean saveImages) { } errorCount++; - System.err.printf("Scaling: %.2f\n", scaling); + System.err.printf("Scale: %.2f; thickness: %d, effective: %d\n", + test.scale, test.thickness, effectiveThickness); e.printStackTrace(); - // Save the image if it wasn't already saved - if (!saveImages) { - saveImage(img, getImageFileName(scaling)); - } + saveImage(img, getImageFileName(test.scale, test.thickness)); } } - - if (errorCount > 0) { - throw new Error("Test failed: " - + errorCount + " error(s) detected - " - + errorMessage); - } } private static void checkVerticalBorders(final int x, @@ -138,6 +148,10 @@ private static void checkHorizontalBorder(final int y, thickness, img); } + private enum State { + BACKGROUND, LEFT, INSIDE, RIGHT + } + private static void checkBorder(final int xStart, final int yStart, final int xStep, final int yStep, final int thickness, @@ -204,10 +218,6 @@ private static void checkBorder(final int xStart, final int yStart, } while (xStep > 0 && ((x += xStep) < width)); } - private enum State { - BACKGROUND, LEFT, INSIDE, RIGHT - } - private static void throwWrongThickness(int thickness, int borderThickness, int x, int y) { throw new Error( @@ -221,17 +231,19 @@ private static void throwUnexpectedColor(int x, int y, int color) { x, y, color)); } - private static JComponent createUI() { + private static TestUI createUI(int thickness) { Box contentPanel = Box.createVerticalBox(); contentPanel.setBackground(OUTER_COLOR); + List panelLocations = new ArrayList<>(4); + Dimension childSize = null; for (int i = 0; i < 4; i++) { JComponent filler = new JPanel(null); filler.setBackground(INSIDE_COLOR); filler.setPreferredSize(SIZE); filler.setBounds(i, 0, SIZE.width, SIZE.height); - filler.setBorder(BorderFactory.createLineBorder(BORDER_COLOR)); + filler.setBorder(BorderFactory.createLineBorder(BORDER_COLOR, thickness)); JPanel childPanel = new JPanel(new BorderLayout()); childPanel.setBorder(BorderFactory.createEmptyBorder(0, i, 4, 4)); @@ -243,16 +255,13 @@ private static JComponent createUI() { childSize = childPanel.getPreferredSize(); } childPanel.setBounds(0, childSize.height * i, childSize.width, childSize.height); + + panelLocations.add(childPanel.getLocation()); } contentPanel.setSize(childSize.width, childSize.height * 4); - // Save coordinates of the panels - for (Component comp : contentPanel.getComponents()) { - panelLocations.add(comp.getLocation()); - } - - return contentPanel; + return new TestUI(contentPanel, panelLocations, thickness); } private static void showFrame(JComponent content) { @@ -264,28 +273,33 @@ private static void showFrame(JComponent content) { frame.setVisible(true); } - private static void paintToImages(final JComponent content, - final boolean saveImages) { - for (double scaling : scales) { + private static List paintToImages(final TestUI testUI, + final boolean saveImages) { + final List images = new ArrayList<>(scales.length); + final JComponent content = testUI.content; + for (double scale : scales) { BufferedImage image = - new BufferedImage((int) Math.ceil(content.getWidth() * scaling), - (int) Math.ceil(content.getHeight() * scaling), + new BufferedImage((int) Math.ceil(content.getWidth() * scale), + (int) Math.ceil(content.getHeight() * scale), BufferedImage.TYPE_INT_ARGB); Graphics2D g2d = image.createGraphics(); - g2d.scale(scaling, scaling); + g2d.scale(scale, scale); content.paint(g2d); g2d.dispose(); if (saveImages) { - saveImage(image, getImageFileName(scaling)); + saveImage(image, getImageFileName(scale, testUI.thickness)); } - images.add(image); + images.add(new TestImage(image, testUI.panelLocations, + scale, testUI.thickness)); } + return images; } - private static String getImageFileName(final double scaling) { - return String.format("test%.2f.png", scaling); + private static String getImageFileName(final double scaling, + final int thickness) { + return String.format("test%02d@%.2f.png", thickness, scaling); } private static void saveImage(BufferedImage image, String filename) { diff --git a/test/jdk/javax/swing/border/LineBorder/ScaledTextFieldBorderTest.java b/test/jdk/javax/swing/border/LineBorder/ScaledTextFieldBorderTest.java index ea8254c401a6e..2a732812e38de 100644 --- a/test/jdk/javax/swing/border/LineBorder/ScaledTextFieldBorderTest.java +++ b/test/jdk/javax/swing/border/LineBorder/ScaledTextFieldBorderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,6 @@ import java.awt.BorderLayout; import java.awt.Color; -import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Point; @@ -45,12 +44,15 @@ import javax.swing.SwingUtilities; import javax.swing.border.LineBorder; +import static sun.java2d.pipe.Region.clipRound; + /* * @test - * @bug 8282958 + * @bug 8282958 8349188 * @summary Verify all the borders are rendered consistently for a JTextField * in Windows LaF which uses LineBorder * @requires (os.family == "windows") + * @modules java.desktop/sun.java2d.pipe * @run main ScaledTextFieldBorderTest */ public class ScaledTextFieldBorderTest { @@ -93,7 +95,7 @@ private static void verifyBorderRendering(final boolean saveImages) { BufferedImage img = images.get(i); double scaling = scales[i]; try { - int thickness = (int) Math.floor(scaling); + int thickness = clipRound(scaling); checkVerticalBorders(textFieldSize.width / 2, thickness, img); diff --git a/test/jdk/javax/swing/border/Test4243289.html b/test/jdk/javax/swing/border/Test4243289.html deleted file mode 100644 index 5616bd835ac12..0000000000000 --- a/test/jdk/javax/swing/border/Test4243289.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - -When applet starts, you'll see a panel with a TitledBorder with title "Panel Title". -If this title is overstriken with the border line, test fails, otherwise it passes. - - - - - diff --git a/test/jdk/javax/swing/border/Test4243289.java b/test/jdk/javax/swing/border/Test4243289.java index ae535a1e517e0..4b89239bc19bb 100644 --- a/test/jdk/javax/swing/border/Test4243289.java +++ b/test/jdk/javax/swing/border/Test4243289.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,32 +21,55 @@ * questions. */ +import java.awt.Dimension; +import java.awt.Font; +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.border.TitledBorder; + /* * @test * @bug 4243289 * @summary Tests that TitledBorder do not draw line through its caption - * @author Peter Zhelezniakov - * @run applet/manual=yesno Test4243289.html + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual Test4243289 */ -import java.awt.Font; -import javax.swing.BorderFactory; -import javax.swing.JApplet; -import javax.swing.JPanel; -import javax.swing.border.TitledBorder; +public class Test4243289 { + public static void main(String[] args) throws Exception { + String testInstructions = """ + If TitledBorder with title "Panel Title" is overstruck with + the border line, test fails, otherwise it passes. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(testInstructions) + .rows(3) + .columns(35) + .splitUI(Test4243289::init) + .build() + .awaitAndCheck(); + } -public class Test4243289 extends JApplet { - public void init() { - Font font = new Font("Dialog", Font.PLAIN, 12); // NON-NLS: the font name + public static JComponent init() { + Font font = new Font(Font.DIALOG, Font.PLAIN, 12); TitledBorder border = BorderFactory.createTitledBorder( BorderFactory.createEtchedBorder(), - "Panel Title", // NON-NLS: the title of the border + "Panel Title", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, font); JPanel panel = new JPanel(); panel.setBorder(border); - getContentPane().add(panel); + panel.setPreferredSize(new Dimension(100, 100)); + Box main = Box.createVerticalBox(); + main.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); + main.add(panel); + return main; } } diff --git a/test/jdk/javax/swing/border/Test4247606.html b/test/jdk/javax/swing/border/Test4247606.html deleted file mode 100644 index 5a4553382e01f..0000000000000 --- a/test/jdk/javax/swing/border/Test4247606.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - -If the button do not fit into the titled border bounds -and cover the bottom border's line then test fails. -Otherwise test passes. - - - - - diff --git a/test/jdk/javax/swing/border/Test4247606.java b/test/jdk/javax/swing/border/Test4247606.java index 03806508567b7..71ebef2e2b05e 100644 --- a/test/jdk/javax/swing/border/Test4247606.java +++ b/test/jdk/javax/swing/border/Test4247606.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,36 +21,55 @@ * questions. */ -/* - * @test - * @bug 4247606 - * @summary BorderedPane appears wrong with Title Position Below Bottom - * @author Andrey Pikalev - * @run applet/manual=yesno Test4247606.html - */ - import java.awt.BorderLayout; import java.awt.Color; +import java.awt.Dimension; import javax.swing.BorderFactory; -import javax.swing.JApplet; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.border.Border; import javax.swing.border.TitledBorder; -public class Test4247606 extends JApplet { - public void init() { - JButton button = new JButton("Button"); // NON-NLS: the button text +/* + * @test + * @bug 4247606 + * @summary BorderedPane appears wrong with title position below bottom + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual Test4247606 + */ + +public class Test4247606 { + public static void main(String[] args) throws Exception { + String testInstructions = """ + If the button does not fit into the titled border bounds + and cover the bottom border's line then test fails. + Otherwise test passes + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(testInstructions) + .rows(4) + .columns(35) + .splitUI(Test4247606::initializeTest) + .build() + .awaitAndCheck(); + } + + public static JComponent initializeTest() { + JButton button = new JButton("Button"); button.setBorder(BorderFactory.createLineBorder(Color.red, 1)); - TitledBorder border = new TitledBorder("Bordered Pane"); // NON-NLS: the panel title + TitledBorder border = new TitledBorder("Bordered Pane"); border.setTitlePosition(TitledBorder.BELOW_BOTTOM); JPanel panel = create(button, border); panel.setBackground(Color.green); + panel.setPreferredSize(new Dimension(200, 150)); - getContentPane().add(create(panel, BorderFactory.createEmptyBorder(10, 10, 10, 10))); + return create(panel, BorderFactory.createEmptyBorder(10, 10, 10, 10)); } private static JPanel create(JComponent component, Border border) { diff --git a/test/jdk/javax/swing/border/Test4252164.html b/test/jdk/javax/swing/border/Test4252164.html deleted file mode 100644 index 442be90fcc8d4..0000000000000 --- a/test/jdk/javax/swing/border/Test4252164.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - -Please, ensure that rounded border is filled completely. -It should not contain white points inside. -Use Mouse Wheel to change thickness of the border. - - - - - diff --git a/test/jdk/javax/swing/border/Test4252164.java b/test/jdk/javax/swing/border/Test4252164.java index 923df7150546d..8d7eace7deacc 100644 --- a/test/jdk/javax/swing/border/Test4252164.java +++ b/test/jdk/javax/swing/border/Test4252164.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,53 +21,56 @@ * questions. */ +import java.awt.Color; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.LineBorder; + /* * @test * @bug 4252164 8041917 * @summary Tests rounded LineBorder for components - * @author Sergey Malenkov - * @run applet/manual=yesno Test4252164.html + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual Test4252164 */ -import java.awt.Color; -import java.awt.event.MouseWheelEvent; -import java.awt.event.MouseWheelListener; -import javax.swing.JApplet; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.border.LineBorder; - -public class Test4252164 extends JApplet implements MouseWheelListener { - private int thickness; - private JLabel rounded; - private JLabel straight; +public class Test4252164 { + private static int thickness; + private static JLabel rounded; + private static JLabel straight; - public void mouseWheelMoved(MouseWheelEvent event) { - update(event.getWheelRotation()); - } + public static void main(String[] args) throws Exception { + String testInstructions = """ + Please, ensure that rounded border is filled completely. + It should not contain white points inside. + Use Mouse Wheel to change thickness of the border. + """; - public void init() { - add(createUI()); - addMouseWheelListener(this); + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(testInstructions) + .rows(4) + .columns(35) + .splitUI(Test4252164::createUI) + .build() + .awaitAndCheck(); } - private JPanel createUI() { - this.rounded = new JLabel("ROUNDED"); // NON-NLS: the label for rounded border - this.straight = new JLabel("STRAIGHT"); // NON-NLS: the label for straight border - + private static JPanel createUI() { + rounded = new JLabel("ROUNDED"); // NON-NLS: the label for rounded border + straight = new JLabel("STRAIGHT"); // NON-NLS: the label for straight border JPanel panel = new JPanel(); - panel.add(this.rounded); - panel.add(this.straight); - + panel.add(rounded); + panel.add(straight); update(10); - + panel.addMouseWheelListener(e -> update(e.getWheelRotation())); return panel; } - private void update(int thickness) { - this.thickness += thickness; - - this.rounded.setBorder(new LineBorder(Color.RED, this.thickness, true)); - this.straight.setBorder(new LineBorder(Color.RED, this.thickness, false)); + private static void update(int thicknessValue) { + thickness += thicknessValue; + rounded.setBorder(new LineBorder(Color.RED, thickness, true)); + straight.setBorder(new LineBorder(Color.RED, thickness, false)); } } diff --git a/test/jdk/javax/swing/border/Test4760089.html b/test/jdk/javax/swing/border/Test4760089.html deleted file mode 100644 index 63c1ff87e0654..0000000000000 --- a/test/jdk/javax/swing/border/Test4760089.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - -When applet starts, you'll see a panel with a compound titled border. -If one of its titles is overstriken with the border's line then test fails. -Otherwise test passes. - - - - - diff --git a/test/jdk/javax/swing/border/Test4760089.java b/test/jdk/javax/swing/border/Test4760089.java index 81762c729f267..da5e7f3053007 100644 --- a/test/jdk/javax/swing/border/Test4760089.java +++ b/test/jdk/javax/swing/border/Test4760089.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,29 +21,52 @@ * questions. */ +import java.awt.Dimension; +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.border.Border; +import javax.swing.border.EtchedBorder; +import javax.swing.border.TitledBorder; + /* * @test * @bug 4760089 * @summary Tests that titled border do not paint inner titled border over its caption - * @author Sergey Malenkov - * @run applet/manual=yesno Test4760089.html + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual Test4760089 */ -import javax.swing.JApplet; -import javax.swing.JPanel; -import javax.swing.border.Border; -import javax.swing.border.EtchedBorder; -import javax.swing.border.TitledBorder; +public class Test4760089 { + public static void main(String[] args) throws Exception { + String testInstructions = """ + When test starts, a panel with a compound titled border is seen. + If one of its titles is overstruck with the border's + line then test fails. Otherwise test passes."""; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(testInstructions) + .rows(4) + .columns(35) + .splitUI(Test4760089::init) + .build() + .awaitAndCheck(); + } -public class Test4760089 extends JApplet { - @Override - public void init() { + public static JComponent init() { Border border = new EtchedBorder(); - border = new TitledBorder(border, "LEFT", TitledBorder.LEFT, TitledBorder.TOP); + border = new TitledBorder(border, "LEFT", TitledBorder.LEFT, TitledBorder.TOP); border = new TitledBorder(border, "RIGHT", TitledBorder.RIGHT, TitledBorder.TOP); JPanel panel = new JPanel(); panel.setBorder(border); - getContentPane().add(panel); + panel.setPreferredSize(new Dimension(200, 100)); + Box main = Box.createVerticalBox(); + main.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); + main.add(panel); + return main; } } diff --git a/test/jdk/javax/swing/border/Test6910490.html b/test/jdk/javax/swing/border/Test6910490.html deleted file mode 100644 index 5cc40cefc397c..0000000000000 --- a/test/jdk/javax/swing/border/Test6910490.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - -If the border is painted over scroll bars then test fails. -Otherwise test passes. - - - - - diff --git a/test/jdk/javax/swing/border/Test6910490.java b/test/jdk/javax/swing/border/Test6910490.java index 78dd3dfb872f3..f42e4fe0dd23a 100644 --- a/test/jdk/javax/swing/border/Test6910490.java +++ b/test/jdk/javax/swing/border/Test6910490.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,8 +27,8 @@ import java.awt.Graphics; import java.awt.Insets; import javax.swing.Icon; -import javax.swing.JApplet; import javax.swing.JButton; +import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.border.MatteBorder; @@ -37,22 +37,40 @@ * @test * @bug 6910490 * @summary Tests a matte border around a component inside a scroll pane. - * @author Sergey Malenkov - * @run applet/manual=yesno Test6910490.html + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual Test6910490 */ -public class Test6910490 extends JApplet implements Icon { +public class Test6910490 implements Icon { + public static void main(String[] args) throws Exception { + String testInstructions = """ + If the border is painted over scroll bars then test fails. + Otherwise test passes."""; + Test6910490 obj = new Test6910490(); + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(testInstructions) + .rows(3) + .columns(35) + .testUI(obj.initializeTest()) + .build() + .awaitAndCheck(); + } - @Override - public void init() { + public JFrame initializeTest() { Insets insets = new Insets(10, 10, 10, 10); - Dimension size = new Dimension(getWidth() / 2, getHeight()); + JFrame frame = new JFrame("Matte Border Test"); + frame.setSize(600, 300); + Dimension size = new Dimension(frame.getWidth() / 2, frame.getHeight()); JSplitPane pane = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, create("Color", size, new MatteBorder(insets, RED)), create("Icon", size, new MatteBorder(insets, this))); + pane.setDividerLocation(size.width - pane.getDividerSize() / 2); - add(pane); + frame.add(pane); + return frame; } private JScrollPane create(String name, Dimension size, MatteBorder border) { diff --git a/test/jdk/javax/swing/border/TransparentTitleTest.java b/test/jdk/javax/swing/border/TransparentTitleTest.java new file mode 100644 index 0000000000000..49a32a5890c2b --- /dev/null +++ b/test/jdk/javax/swing/border/TransparentTitleTest.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4154572 + * @summary Tests that the area behind a TitledBorder's title string is transparent, + * allowing the component's background to show through + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual TransparentTitleTest + */ + +import java.awt.GridLayout; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Color; +import java.awt.image.BufferedImage; +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.border.TitledBorder; +import javax.swing.border.LineBorder; + +public class TransparentTitleTest { + private static final String INSTRUCTIONS = """ + If all panels are correctly painted such that the title of the + border allows the underlying panel image (green rectangle) + to show through the background of the text, + then this test passes; else it fails. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("TransparentTitleTest Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(TransparentTitleTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + JFrame frame = new JFrame("TransparentTitleTest"); + + frame.setLayout(new GridLayout(3, 6, 5, 5)); + + frame.add(new ImagePanel(TitledBorder.TOP, TitledBorder.LEFT)); + frame.add(new ImagePanel(TitledBorder.TOP, TitledBorder.CENTER)); + frame.add(new ImagePanel(TitledBorder.TOP, TitledBorder.RIGHT)); + frame.add(new ImagePanel(TitledBorder.ABOVE_TOP, TitledBorder.LEFT)); + frame.add(new ImagePanel(TitledBorder.ABOVE_TOP, TitledBorder.CENTER)); + frame.add(new ImagePanel(TitledBorder.ABOVE_TOP, TitledBorder.RIGHT)); + frame.add(new ImagePanel(TitledBorder.BELOW_TOP, TitledBorder.LEFT)); + frame.add(new ImagePanel(TitledBorder.BELOW_TOP, TitledBorder.CENTER)); + frame.add(new ImagePanel(TitledBorder.BELOW_TOP, TitledBorder.RIGHT)); + frame.add(new ImagePanel(TitledBorder.BOTTOM, TitledBorder.LEFT)); + frame.add(new ImagePanel(TitledBorder.BOTTOM, TitledBorder.CENTER)); + frame.add(new ImagePanel(TitledBorder.BOTTOM, TitledBorder.RIGHT)); + frame.add(new ImagePanel(TitledBorder.ABOVE_BOTTOM, TitledBorder.LEFT)); + frame.add(new ImagePanel(TitledBorder.ABOVE_BOTTOM, TitledBorder.CENTER)); + frame.add(new ImagePanel(TitledBorder.ABOVE_BOTTOM, TitledBorder.RIGHT)); + frame.add(new ImagePanel(TitledBorder.BELOW_BOTTOM, TitledBorder.LEFT)); + frame.add(new ImagePanel(TitledBorder.BELOW_BOTTOM, TitledBorder.CENTER)); + frame.add(new ImagePanel(TitledBorder.BELOW_BOTTOM, TitledBorder.RIGHT)); + + frame.pack(); + return frame; + } +} + +class ImagePanel extends JPanel { + + private final ImageIcon imageIcon; + + private static final BufferedImage bufferedImage = + new BufferedImage(128, 128, BufferedImage.TYPE_INT_ARGB); + + static { + Graphics g = bufferedImage.getGraphics(); + g.setColor(Color.GREEN); + g.fillRect(0, 0, 128, 128); + } + + public ImagePanel(int titlePos, int titleJust) { + imageIcon = new ImageIcon(bufferedImage); + + TitledBorder b = new TitledBorder(new LineBorder(Color.black,3), "title text"); + b.setTitlePosition(titlePos); + b.setTitleJustification(titleJust); + b.setTitleColor(Color.black); + setBorder(b); + } + + public Dimension getPreferredSize() { + return new Dimension(imageIcon.getIconWidth(), imageIcon.getIconHeight()); + } + + public void paintComponent(Graphics g) { + imageIcon.paintIcon(this, g, 0, 0); + } +} diff --git a/test/jdk/javax/swing/plaf/basic/BasicGraphicsUtils/DrawEtchedRectTest.java b/test/jdk/javax/swing/plaf/basic/BasicGraphicsUtils/DrawEtchedRectTest.java new file mode 100644 index 0000000000000..0c5c501596ce0 --- /dev/null +++ b/test/jdk/javax/swing/plaf/basic/BasicGraphicsUtils/DrawEtchedRectTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4198822 + * @summary Tests that the bottom line drawn by + * BasicGraphicsUtils.drawEtchedRect extends to the end. + * @run main DrawEtchedRectTest + */ + +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.io.File; + +import javax.imageio.ImageIO; +import javax.swing.plaf.basic.BasicGraphicsUtils; + +import static java.awt.image.BufferedImage.TYPE_INT_ARGB; + +public class DrawEtchedRectTest { + private static final int WIDTH = 200; + private static final int HEIGHT = 200; + private static final int RANGE = 10; + + public static void main(String[] args) throws Exception { + // Draw etched rectangle to a BufferedImage + BufferedImage image = new BufferedImage(WIDTH, HEIGHT, TYPE_INT_ARGB); + Graphics2D g2d = image.createGraphics(); + Component sq = new Component() { + public void paint(Graphics g) { + g.setColor(Color.WHITE); + g.fillRect(0, 0, WIDTH, HEIGHT); + BasicGraphicsUtils.drawEtchedRect(g, 0, 0, WIDTH, HEIGHT, + Color.black, Color.black, + Color.black, Color.black); + } + }; + sq.paint(g2d); + g2d.dispose(); + + // Check if connected at bottom-right corner + int c1; + int c2; + for (int i = 1; i < RANGE; i++) { + c1 = image.getRGB(WIDTH - i, HEIGHT - 1); + c2 = image.getRGB(WIDTH - 1, HEIGHT - i); + if (c1 == Color.WHITE.getRGB() || c2 == Color.WHITE.getRGB()) { + ImageIO.write(image, "png", new File("failImage.png")); + throw new RuntimeException("Bottom line is not connected!"); + } + } + } +} diff --git a/test/jdk/javax/swing/plaf/basic/BasicHTML/4228104/bug4228104.java b/test/jdk/javax/swing/plaf/basic/BasicHTML/4228104/bug4228104.java new file mode 100644 index 0000000000000..451590c66abad --- /dev/null +++ b/test/jdk/javax/swing/plaf/basic/BasicHTML/4228104/bug4228104.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4228104 + * @summary Tests work of BODY BACKGROUND tag in HTML renderer + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4228104 + */ + +import java.awt.BorderLayout; + +import javax.swing.JFrame; +import javax.swing.JLabel; + +public class bug4228104 { + static final String INSTRUCTIONS = """ + There should be an image displaying dukes under the rows of digits. + If you can see it, the test PASSES. Otherwise, the test FAILS. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug4228104 Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug4228104::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + JFrame f = new JFrame("Background HTML Text Test"); + String dir = System.getProperty("test.src", + System.getProperty("user.dir")); + String htmlText1 = + "\n" + + "
    111111111111111111" + + "
    111111111111111111" + + "
    111111111111111111" + + "
    111111111111111111" + + "
    111111111111111111" + + "
    111111111111111111" + + "
    111111111111111111" + + "
    111111111111111111" + + "
    111111111111111111"; + JLabel button1 = new JLabel(htmlText1); + f.add(button1, BorderLayout.NORTH); + f.setSize(200, 200); + return f; + } +} diff --git a/test/jdk/javax/swing/plaf/basic/BasicHTML/4228104/duke.gif b/test/jdk/javax/swing/plaf/basic/BasicHTML/4228104/duke.gif new file mode 100644 index 0000000000000..ed32e0ff79b05 Binary files /dev/null and b/test/jdk/javax/swing/plaf/basic/BasicHTML/4228104/duke.gif differ diff --git a/test/jdk/javax/swing/plaf/basic/BasicInternalFrameTitlePane/bug4331515.java b/test/jdk/javax/swing/plaf/basic/BasicInternalFrameTitlePane/bug4331515.java new file mode 100644 index 0000000000000..3129148b362d1 --- /dev/null +++ b/test/jdk/javax/swing/plaf/basic/BasicInternalFrameTitlePane/bug4331515.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4331515 + * @requires (os.family == "windows") + * @summary System menu of an internal frame shouldn't have duplicated items in Win L&F + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4331515 + */ + +import javax.swing.JDesktopPane; +import javax.swing.JFrame; +import javax.swing.JInternalFrame; +import javax.swing.UIManager; + +public class bug4331515 { + static final String INSTRUCTIONS = """ + Open the system menu of internal frame "JIF" placed in the frame "Test". + If this menu contains duplicates of some items then test FAILS, else + test PASSES. + """; + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); + PassFailJFrame.builder() + .title("bug4331515 Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug4331515::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + JFrame fr = new JFrame("System Menu in JIF Test"); + JDesktopPane dp = new JDesktopPane(); + fr.setContentPane(dp); + JInternalFrame jif = new JInternalFrame("JIF", true, true, true, true); + dp.add(jif); + jif.setBounds(20, 20, 120, 100); + jif.setVisible(true); + fr.setSize(200, 200); + return fr; + } +} diff --git a/test/jdk/javax/swing/plaf/basic/BasicSliderUI/bug4220108.java b/test/jdk/javax/swing/plaf/basic/BasicSliderUI/bug4220108.java new file mode 100644 index 0000000000000..94cc3d65e5d17 --- /dev/null +++ b/test/jdk/javax/swing/plaf/basic/BasicSliderUI/bug4220108.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4220108 + * @summary JSlider in JInternalFrame should be painted correctly + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4220108 + */ + +import java.awt.BorderLayout; +import java.awt.FlowLayout; + +import javax.swing.JDesktopPane; +import javax.swing.JFrame; +import javax.swing.JInternalFrame; +import javax.swing.JLabel; +import javax.swing.JSlider; + +public class bug4220108 { + static final String INSTRUCTIONS = """ + If you see a slider in the internal frame, then the test PASSES. + Otherwise the test FAILS. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug4220108 Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug4220108::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + JFrame f = new JFrame("Internal Frame Slider Test"); + f.setLayout(new FlowLayout()); + JDesktopPane desktop = new JDesktopPane(); + f.setContentPane(desktop); + + JInternalFrame iFrame = + new JInternalFrame("Slider Frame", true, true, true, true); + JSlider sl = new JSlider(); + iFrame.add(sl); + iFrame.add(new JLabel("Label"), BorderLayout.SOUTH); + desktop.add(iFrame); + iFrame.pack(); + iFrame.setVisible(true); + f.setSize(300, 200); + return f; + } +} diff --git a/test/jdk/javax/swing/plaf/basic/BasicSplitPaneDivider/AddMouseListenerTest.java b/test/jdk/javax/swing/plaf/basic/BasicSplitPaneDivider/AddMouseListenerTest.java new file mode 100644 index 0000000000000..79ae9bd5ec8e3 --- /dev/null +++ b/test/jdk/javax/swing/plaf/basic/BasicSplitPaneDivider/AddMouseListenerTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4165874 + * @summary Adds a MouseListener to the splitpane divider. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual AddMouseListenerTest + */ + +import java.awt.Component; +import java.awt.event.MouseAdapter; + +import javax.swing.JFrame; +import javax.swing.JSplitPane; + +public class AddMouseListenerTest { + static final String INSTRUCTIONS = """ + Try dragging the split pane divider, if you can, click PASS, + else click FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("AddMouseListenerTest Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(AddMouseListenerTest::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + JFrame f = new JFrame("JSplitPane With ActionListener Test"); + JSplitPane sp = new JSplitPane(); + + sp.setContinuousLayout(true); + Component[] children = sp.getComponents(); + for (int counter = children.length - 1; counter >= 0; counter--) { + children[counter].addMouseListener(new MouseAdapter() {}); + } + f.getContentPane().add(sp); + f.setSize(400, 400); + return f; + } +} diff --git a/test/jdk/javax/swing/plaf/basic/BasicSplitPaneUI/NegativeSizeTest.java b/test/jdk/javax/swing/plaf/basic/BasicSplitPaneUI/NegativeSizeTest.java new file mode 100644 index 0000000000000..3a461410e4b5e --- /dev/null +++ b/test/jdk/javax/swing/plaf/basic/BasicSplitPaneUI/NegativeSizeTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4199666 + * @summary Makes sure initial negative size of a component does not confuse + * JSplitPane. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual NegativeSizeTest + */ + +import java.awt.BorderLayout; +import java.awt.CardLayout; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JSplitPane; + +public class NegativeSizeTest { + static final String INSTRUCTIONS = """ + Click on the 'Show JSplitPane' button. If two buttons appear, + click PASS, otherwise click FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("NegativeSizeTest Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(NegativeSizeTest::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + JFrame f = new JFrame("Negative Size Test"); + CardLayout cardLayout = new CardLayout(); + JPanel mainPanel = new JPanel(cardLayout); + + JSplitPane splitPane = new JSplitPane(); + splitPane.setContinuousLayout(true); + JPanel splitContainer = new JPanel(new BorderLayout()); + splitContainer.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + splitContainer.add(splitPane, BorderLayout.CENTER); + + if (false) { + mainPanel.add(splitContainer, "split"); + mainPanel.add(new JPanel(), "blank"); + } + else { + mainPanel.add(new JPanel(), "blank"); + mainPanel.add(splitContainer, "split"); + } + + f.add(mainPanel, BorderLayout.CENTER); + + JButton button = new JButton("Show JSplitPane"); + button.addActionListener(e -> cardLayout.show(mainPanel, "split")); + f.add(button, BorderLayout.SOUTH); + f.setSize(400, 300); + return f; + } +} diff --git a/test/jdk/javax/swing/plaf/basic/BasicSplitPaneUI/PreferredSizeLayoutTest.java b/test/jdk/javax/swing/plaf/basic/BasicSplitPaneUI/PreferredSizeLayoutTest.java new file mode 100644 index 0000000000000..76d0e45b8eb57 --- /dev/null +++ b/test/jdk/javax/swing/plaf/basic/BasicSplitPaneUI/PreferredSizeLayoutTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4208549 + * @summary Makes sure preferred size returned by layout managers used by + * JSplitPane is correct. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual PreferredSizeLayoutTest + */ + +import java.awt.Container; +import java.awt.FlowLayout; + +import javax.swing.JFrame; +import javax.swing.JSplitPane; + +public class PreferredSizeLayoutTest { + static final String INSTRUCTIONS = """ + If the buttons in the JSplitpanes do not have '...' in them, + click PASS, otherwise click FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("PreferredSizeLayoutTest Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(PreferredSizeLayoutTest::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + JFrame f = new JFrame("Preferred Size Layout Test"); + Container parent = f.getContentPane(); + JSplitPane sp = new JSplitPane(); + + parent.setLayout(new FlowLayout()); + + sp.setOrientation(JSplitPane.HORIZONTAL_SPLIT); + parent.add(sp); + sp = new JSplitPane(); + sp.setOrientation(JSplitPane.VERTICAL_SPLIT); + parent.add(sp); + f.setSize(400, 300); + return f; + } +} diff --git a/test/jdk/javax/swing/plaf/basic/BasicTableHeaderUI/6394566/bug6394566.java b/test/jdk/javax/swing/plaf/basic/BasicTableHeaderUI/6394566/bug6394566.java new file mode 100644 index 0000000000000..b17243def33cc --- /dev/null +++ b/test/jdk/javax/swing/plaf/basic/BasicTableHeaderUI/6394566/bug6394566.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2006, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* + * @test + * @bug 6394566 + * @key headful + * @summary Tests that ESC moves the focus from the header to the table + * @library ../../../../regtesthelpers + * @build SwingTestHelper JRobot + * @run main bug6394566 + */ + +import java.awt.Component; +import java.awt.event.KeyEvent; +import java.awt.event.FocusEvent; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.table.JTableHeader; +import javax.swing.table.DefaultTableModel; + +public class bug6394566 extends SwingTestHelper { + private JTable table; + private JTableHeader header; + + public static void main(String[] args) throws Throwable { + new bug6394566().run(args); + } + + protected Component createContentPane() { + table = new JTable(new DefaultTableModel(2, 2)); + header = table.getTableHeader(); + return new JScrollPane(table); + } + + public void onEDT10() { + // Give the table the focus. + requestAndWaitForFocus(table); + } + + //First, give the header focus using F8 on the JTable. + //This will fail prior to mustang b72. + public void onEDT20() { + waitForEvent(header, FocusEvent.FOCUS_GAINED); //set up focus listener + robot.hitKey(KeyEvent.VK_F8); + } + + //Next, give the table back the focus using ESC. + //This will fail prior to the build with this bugfix. + public void onEDT30() { + waitForEvent(table, FocusEvent.FOCUS_GAINED); //set up focus listener + robot.hitKey(KeyEvent.VK_ESCAPE); + } +} diff --git a/test/jdk/javax/swing/plaf/basic/BasicTextUI/PasswordSelectionWordTest.java b/test/jdk/javax/swing/plaf/basic/BasicTextUI/PasswordSelectionWordTest.java new file mode 100644 index 0000000000000..695d8a83c70d0 --- /dev/null +++ b/test/jdk/javax/swing/plaf/basic/BasicTextUI/PasswordSelectionWordTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @key headful + * @bug 4231444 8354646 + * @summary Password fields' ActionMap needs to replace + * DefaultEditorKit.selectWordAction with + * DefaultEditorKit.selectLineAction. + * + * @run main PasswordSelectionWordTest + */ + +import javax.swing.Action; +import javax.swing.JPasswordField; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; +import javax.swing.plaf.basic.BasicTextUI; +import javax.swing.text.DefaultEditorKit; +import java.awt.event.ActionEvent; + +public class PasswordSelectionWordTest { + public static void main(String[] args) throws Exception { + for (UIManager.LookAndFeelInfo laf : + UIManager.getInstalledLookAndFeels()) { + System.out.println("Testing LAF: " + laf.getClassName()); + SwingUtilities.invokeAndWait(() -> { + if (setLookAndFeel(laf)) { + runTest(); + } + }); + } + } + + private static boolean setLookAndFeel(UIManager.LookAndFeelInfo laf) { + try { + UIManager.setLookAndFeel(laf.getClassName()); + return true; + } catch (UnsupportedLookAndFeelException e) { + System.err.println("Skipping unsupported look and feel:"); + e.printStackTrace(); + return false; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static void runTest() { + String str = "one two three"; + JPasswordField field = new JPasswordField(str); + if (!(field.getUI() instanceof BasicTextUI)) { + throw new RuntimeException("Unexpected condition: JPasswordField UI was " + field.getUI()); + } + System.out.println("Testing " + field.getUI()); + + // do something (anything) to initialize the Views: + field.setSize(100, 100); + field.addNotify(); + + Action action = field.getActionMap().get( + DefaultEditorKit.selectWordAction); + action.actionPerformed(new ActionEvent(field, 0, "")); + int selectionStart = field.getSelectionStart(); + int selectionEnd = field.getSelectionEnd(); + System.out.println("selectionStart = " + selectionStart); + System.out.println("selectionEnd = " + selectionEnd); + if (selectionStart != 0 || selectionEnd != str.length()) { + throw new RuntimeException("selectionStart = " + selectionStart + + " and selectionEnd = " + selectionEnd); + } + } +} diff --git a/test/jdk/javax/swing/plaf/basic/BasicToolBarUI/bug4305622.java b/test/jdk/javax/swing/plaf/basic/BasicToolBarUI/bug4305622.java new file mode 100644 index 0000000000000..5fdae14e74747 --- /dev/null +++ b/test/jdk/javax/swing/plaf/basic/BasicToolBarUI/bug4305622.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4305622 + * @summary MetalToolBarUI.installUI invokeLater causes flickering + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4305622 + */ + +import java.awt.BorderLayout; +import java.awt.Color; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JToolBar; +import javax.swing.UIManager; +import javax.swing.border.LineBorder; + + +public class bug4305622 { + private static JFrame fr; + static final String INSTRUCTIONS = """ + Press button "Create ToolBar" at frame "Create ToolBar Test". + If you see any flickering during creating of toolbar + then the test FAILS, otherwise the test PASSES. + """; + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); + PassFailJFrame.builder() + .title("bug4305622 Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug4305622::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + fr = new JFrame("Create ToolBar Test"); + JButton button = new JButton("Create ToolBar"); + button.addActionListener(ae -> addToolBar()); + fr.add(button, BorderLayout.SOUTH); + fr.setSize(400, 400); + return fr; + } + + static void addToolBar() { + fr.repaint(); + fr.revalidate(); + JToolBar toolbar = new JToolBar(); + + JButton btn = new JButton("Button 1"); + btn.setBorder(new LineBorder(Color.red, 30)); + toolbar.add(btn); + + btn = new JButton("Button 2"); + btn.setBorder(new LineBorder(Color.red, 30)); + toolbar.add(btn); + + toolbar.updateUI(); + fr.add(toolbar, BorderLayout.NORTH); + } +} diff --git a/test/jdk/javax/swing/plaf/basic/BasicToolBarUI/bug4331392.java b/test/jdk/javax/swing/plaf/basic/BasicToolBarUI/bug4331392.java new file mode 100644 index 0000000000000..eaca65bfa6d3c --- /dev/null +++ b/test/jdk/javax/swing/plaf/basic/BasicToolBarUI/bug4331392.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4331392 + * @summary Tests if BasicToolBarUI has bogus logic that prevents vertical + * toolbars from docking + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4331392 + */ + +import java.awt.BorderLayout; +import java.awt.Container; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JToolBar; + +public class bug4331392 { + static final String INSTRUCTIONS = """ + Try to dock the toolbar across all the edges of frame. If you succeed, + then the test PASSES. Otherwise, it FAILS. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug4331392 Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug4331392::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + JFrame frame = new JFrame("JToolBar Docking Test"); + Container c = frame.getContentPane(); + + JToolBar tbar = new JToolBar(JToolBar.VERTICAL); + + tbar.add(new JButton("A")); + tbar.add(new JButton("B")); + tbar.add(new JButton("C")); + + JButton b = new JButton("Hello"); + c.add(b, BorderLayout.CENTER); + c.add(tbar, BorderLayout.EAST); + frame.setSize(300, 300); + return frame; + } +} diff --git a/test/jdk/javax/swing/plaf/metal/MenuItemUI/JavaLAFMenuAcceleratorDelimiter.java b/test/jdk/javax/swing/plaf/metal/MenuItemUI/JavaLAFMenuAcceleratorDelimiter.java new file mode 100644 index 0000000000000..f9885c985705d --- /dev/null +++ b/test/jdk/javax/swing/plaf/metal/MenuItemUI/JavaLAFMenuAcceleratorDelimiter.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4210461 + * @summary Confirm Metal Look & Feel's MenuItem Accelerator Delimiter + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual JavaLAFMenuAcceleratorDelimiter + */ + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; + +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.KeyStroke; +import javax.swing.UIManager; + +public class JavaLAFMenuAcceleratorDelimiter { + static final String INSTRUCTIONS = """ + A simple check. The visual design specification for JLF/Metal asks for + a "-" to delimit the other two entities in a menu item's accelerator. + The test passes if the delimiter for the accelerator is correct when + opening the example menu. Otherwise, the test fails. + """; + + public static void main(String[] args) throws Exception { + // Set Metal L&F + UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); + PassFailJFrame.builder() + .title("JavaLAFMenuAcceleratorDelimiter Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(JavaLAFMenuAcceleratorDelimiter::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + JFrame frame = new JFrame("Metal L&F Accelerator Delimiter Test"); + JPanel menuPanel = new JPanel(); + JMenuBar menuBar = new JMenuBar(); + menuBar.setOpaque(true); + JMenu exampleMenu = new JMenu("Example"); + JMenuItem hiMenuItem = new JMenuItem("Hi There!"); + hiMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_H, + ActionEvent.CTRL_MASK)); + exampleMenu.add(hiMenuItem); + menuBar.add(exampleMenu); + menuPanel.add(menuBar); + + frame.getContentPane().setLayout(new BorderLayout()); + frame.getContentPane().add(menuPanel, BorderLayout.CENTER); + frame.setSize(250, 150); + return frame; + } +} diff --git a/test/jdk/javax/swing/plaf/metal/MetalIconFactory/bug4952462.java b/test/jdk/javax/swing/plaf/metal/MetalIconFactory/bug4952462.java new file mode 100644 index 0000000000000..65c5c510fc3f7 --- /dev/null +++ b/test/jdk/javax/swing/plaf/metal/MetalIconFactory/bug4952462.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4952462 + * @summary Ocean: Tests that disabled selected JRadioButton dot is NOT + * painted with the foreground color + * @modules java.desktop/sun.awt + * @library /test/lib + * @key headful + * @run main/othervm -Dsun.java2d.uiScale=1 bug4952462 + */ + +import java.awt.Color; +import java.awt.FlowLayout; +import java.awt.Point; +import java.awt.Robot; + +import javax.swing.JFrame; +import javax.swing.JRadioButton; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.plaf.metal.MetalLookAndFeel; +import javax.swing.plaf.metal.OceanTheme; + +public class bug4952462 { + private static JFrame frame; + private static JRadioButton rb; + + public static void main(String[] args) throws Exception { + try { + MetalLookAndFeel.setCurrentTheme(new OceanTheme()); + UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); + Robot r = new Robot(); + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame("Metal JRadioButton Foreground Color Test"); + frame.getContentPane().setLayout(new FlowLayout()); + rb = new JRadioButton("RadioButton", true); + rb.setEnabled(false); + rb.setForeground(Color.RED); + frame.getContentPane().add(rb); + frame.setSize(250, 100); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + }); + + r.waitForIdle(); + r.delay(500); + + SwingUtilities.invokeAndWait(() -> { + Point p = rb.getLocationOnScreen(); + for (int i = 0; i < 50; i++) { + Color c = r.getPixelColor(p.x + 10 + i, p.y + (rb.getHeight() / 2)); + System.out.println(c); + if (c.getRed() > 200 && c.getBlue() < 80 && c.getGreen() < 80) { + throw new RuntimeException("Test failed. Radiobutton is red " + + "and not grey."); + } + } + }); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } +} diff --git a/test/jdk/javax/swing/plaf/metal/MetalSliderUI/4186347/bug4186347.java b/test/jdk/javax/swing/plaf/metal/MetalSliderUI/4186347/bug4186347.java new file mode 100644 index 0000000000000..9de9d851805ef --- /dev/null +++ b/test/jdk/javax/swing/plaf/metal/MetalSliderUI/4186347/bug4186347.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4186347 + * @summary Tests changing Slider.horizontalThumbIcon UIResource + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4186347 + */ + +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import javax.swing.JSlider; +import javax.swing.UIDefaults; +import javax.swing.UIManager; +import javax.swing.plaf.IconUIResource; + +public class bug4186347 { + static final String INSTRUCTIONS = """ + If the slider's thumb icon is painted correctly + (that is centered vertically relative to slider + channel) then test passed, otherwise it failed. + """; + + public static void main(String[] args) throws Exception { + // Set Metal L&F + UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); + PassFailJFrame.builder() + .title("bug4186347 Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug4186347::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + JFrame frame = new JFrame("Metal JSlider Icon Test"); + String a = System.getProperty("test.src", ".") + + System.getProperty("file.separator") + + "duke.gif"; + Icon icon = new ImageIcon(a); + IconUIResource iconResource = new IconUIResource(icon); + UIDefaults defaults = UIManager.getDefaults(); + defaults.put("Slider.horizontalThumbIcon", iconResource); + JSlider s = new JSlider(); + frame.getContentPane().add(s); + frame.setSize(250, 150); + return frame; + } +} diff --git a/test/jdk/javax/swing/plaf/metal/MetalSliderUI/4186347/duke.gif b/test/jdk/javax/swing/plaf/metal/MetalSliderUI/4186347/duke.gif new file mode 100644 index 0000000000000..a02a42fd60674 Binary files /dev/null and b/test/jdk/javax/swing/plaf/metal/MetalSliderUI/4186347/duke.gif differ diff --git a/test/jdk/javax/swing/plaf/metal/OceanTheme/4969419/bug4969419.java b/test/jdk/javax/swing/plaf/metal/OceanTheme/4969419/bug4969419.java new file mode 100644 index 0000000000000..023f17f4c43c2 --- /dev/null +++ b/test/jdk/javax/swing/plaf/metal/OceanTheme/4969419/bug4969419.java @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4969419 + * @summary Tests that generated disabled icons have same look with Ocean + * and are updated when theme is switched + * @modules java.desktop/sun.awt + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @key headful + * @run main/manual bug4969419 + */ + +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.FlowLayout; + +import javax.swing.BoxLayout; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; +import javax.swing.JToggleButton; +import javax.swing.LookAndFeel; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; +import javax.swing.plaf.metal.DefaultMetalTheme; +import javax.swing.plaf.metal.MetalLookAndFeel; +import javax.swing.plaf.metal.MetalTheme; + +import sun.awt.AppContext; + +public class bug4969419 { + static final String INSTRUCTIONS = """ + When the test starts you'll see several components with icons. + Use the bottom combo box and the "Set" button to switch between + the Ocean theme and DefaultMetalTheme. + + 1. Set the Ocean theme. Ensure all the icons are the same + Note that they all are of the same brightness: none of them + can be brighter or darker than the others. + + 2. Switch to DefaultMetalTheme. Ensure all the icons changed + (became slightly darker). + + 3. Switch back to Ocean. Ensure all the icons changed + (became brighter). + """; + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); + PassFailJFrame.builder() + .title("bug4969419 Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug4969419::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + JFrame frame = new JFrame("Metal Themes Icon Test"); + Container pane = frame.getContentPane(); + + LFSwitch lfswitch = new LFSwitch(pane); + if (!lfswitch.obtainOceanTheme()) { + throw new RuntimeException("No Ocean theme available"); + } + + pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS)); + + String prefix = System.getProperty("test.src", + System.getProperty("user.dir")) + System.getProperty("file.separator"); + ImageIcon icon = new ImageIcon(prefix + "duke.gif"); + + JPanel panel = new JPanel(); + JButton b = new JButton(icon); + b.setEnabled(false); + + JLabel label = new JLabel(icon, SwingConstants.LEFT); + label.setEnabled(false); + + JTabbedPane tp = new JTabbedPane(); + tp.addTab("", icon, new JPanel()); + tp.addTab("", icon, new JPanel()); + tp.setEnabledAt(0, false); + tp.setEnabledAt(1, false); + + JButton sb = new JButton(icon); + sb.setSelectedIcon(icon); + sb.setSelected(true); + sb.setEnabled(false); + + JToggleButton tb = new JToggleButton(icon); + tb.setEnabled(false); + + JToggleButton stb = new JToggleButton(icon); + stb.setSelectedIcon(icon); + stb.setSelected(true); + stb.setEnabled(false); + + pane.setBackground(Color.white); + panel.setBackground(Color.white); + b.setBackground(Color.white); + label.setBackground(Color.white); + tp.setBackground(Color.white); + sb.setBackground(Color.white); + tb.setBackground(Color.white); + stb.setBackground(Color.white); + + panel.add(b); + panel.add(label); + panel.add(tp); + panel.add(sb); + panel.add(tb); + panel.add(stb); + + pane.add(panel); + pane.add(lfswitch); + frame.setSize(400, 400); + return frame; + } + + static class LFSwitch extends JPanel { + private Component target; + static MetalTheme oceanTheme; + JComboBox lfcombo; + + public LFSwitch(Component target) { + this.target = target; + setLayout(new FlowLayout()); + lfcombo = new JComboBox(lookAndFeels); + add(lfcombo); + JButton setLfBut = new JButton("Set"); + add(setLfBut); + setLfBut.addActionListener(e -> setLf(lfcombo.getSelectedIndex(), + LFSwitch.this.target)); + } + + boolean obtainOceanTheme() { + if (oceanTheme != null) { + return true; + } + try { + UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); + SwingUtilities.updateComponentTreeUI(this); + } catch (Exception e) { + JOptionPane.showMessageDialog(this, + "Unexpected error: couldn't set Metal", "Error", + JOptionPane.ERROR_MESSAGE); + return false; + } + MetalTheme theme = (MetalTheme) AppContext.getAppContext(). + get("currentMetalTheme"); + if (theme == null || theme.getName() != "Ocean") { + JOptionPane.showMessageDialog(this, + "The Ocean theme is not the default Metal theme,\n" + + "but this test requires it to be default.\n" + + "Therefore simply click PASS"); + return false; + } + oceanTheme = theme; + return true; + } + + void setLf(int idx, final Component root) { + try { + UIManager.setLookAndFeel((LookAndFeel) lfs[idx]); + if (root != null) { + SwingUtilities.updateComponentTreeUI(root); + } + } catch (UnsupportedLookAndFeelException e) { + JOptionPane.showMessageDialog(root, + "The selected look and feel is unsupported on this platform", + "Error", JOptionPane.ERROR_MESSAGE); + } catch (Exception exc) { + JOptionPane.showMessageDialog(root, + "Error setting the selected look and feel", "Error", + JOptionPane.ERROR_MESSAGE); + } + } + + static Object[] lookAndFeels = { + "Metal (Ocean)", "Metal (DefaultMetalTheme)", + }; + static Object[] lfs = { + new MetalLookAndFeel() { + protected void createDefaultTheme() { + setCurrentTheme(oceanTheme); + } + }, + new MetalLookAndFeel() { + protected void createDefaultTheme() { + MetalTheme dt = new DefaultMetalTheme(); + setCurrentTheme(dt); + } + }, + }; + } +} diff --git a/test/jdk/javax/swing/plaf/metal/OceanTheme/4969419/duke.gif b/test/jdk/javax/swing/plaf/metal/OceanTheme/4969419/duke.gif new file mode 100644 index 0000000000000..ed32e0ff79b05 Binary files /dev/null and b/test/jdk/javax/swing/plaf/metal/OceanTheme/4969419/duke.gif differ diff --git a/test/jdk/javax/swing/plaf/motif/bug4150591.java b/test/jdk/javax/swing/plaf/motif/bug4150591.java new file mode 100644 index 0000000000000..66c668a441c38 --- /dev/null +++ b/test/jdk/javax/swing/plaf/motif/bug4150591.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import com.sun.java.swing.plaf.motif.MotifInternalFrameTitlePane; +import javax.swing.JInternalFrame; + +/* + * @test + * @bug 4150591 + * @summary MotifInternalFrameTitlePane is public now and can be + * instantiated by other classes within the desktop module without using Reflection. + * This does not mean that this class will ever become part + * of the official public Java API. + * @modules java.desktop/com.sun.java.swing.plaf.motif + * @run main bug4150591 + */ + +public class bug4150591 { + public static void main(String[] args) { + MotifInternalFrameTitlePane mtp = new MotifInternalFrameTitlePane(new JInternalFrame()); + } +} diff --git a/test/jdk/javax/swing/plaf/windows/bug4991587.java b/test/jdk/javax/swing/plaf/windows/bug4991587.java new file mode 100644 index 0000000000000..e4e4fde2b8620 --- /dev/null +++ b/test/jdk/javax/swing/plaf/windows/bug4991587.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4991587 + * @requires (os.family == "windows") + * @summary Tests that disabled JButton text is positioned properly in Windows L&F + * @modules java.desktop/com.sun.java.swing.plaf.windows + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4991587 + */ + +import java.awt.Color; +import java.awt.FlowLayout; +import java.awt.Graphics; +import java.awt.Rectangle; + +import javax.swing.AbstractButton; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.UIManager; + +import com.sun.java.swing.plaf.windows.WindowsButtonUI; + +public class bug4991587 { + static final String INSTRUCTIONS = """ + There are two buttons: enabled (left) and disabled (right). + Ensure that the disabled button text is painted entirely + inside the blue bounding rectangle, just like the enabled + button (use it as an example of how this should look like). + """; + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); + PassFailJFrame.builder() + .title("bug4991587 Test Instructions") + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(bug4991587::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + JFrame f = new JFrame("Disabled JButton Text Test"); + f.setLayout(new FlowLayout()); + f.setSize(400, 100); + + JButton button1 = new JButton("\u0114 Enabled JButton"); + button1.setUI(new MyButtonUI()); + f.add(button1); + + JButton button2 = new JButton("\u0114 Disabled JButton"); + button2.setEnabled(false); + button2.setUI(new MyButtonUI()); + f.add(button2); + + return f; + } + + static class MyButtonUI extends WindowsButtonUI { + protected void paintText(Graphics g, AbstractButton b, + Rectangle textRect, String text) { + g.setColor(Color.blue); + g.drawRect(textRect.x, + textRect.y, + textRect.width + 1, // add 1 for the shadow, otherwise it + // will be painted over the textRect + textRect.height); + super.paintText(g, b, textRect, text); + } + } +} diff --git a/test/jdk/javax/swing/regtesthelpers/JRobot.java b/test/jdk/javax/swing/regtesthelpers/JRobot.java index aba18c3535b6f..5c4c4cbcf4064 100644 --- a/test/jdk/javax/swing/regtesthelpers/JRobot.java +++ b/test/jdk/javax/swing/regtesthelpers/JRobot.java @@ -4,9 +4,7 @@ * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/test/jdk/javax/swing/regtesthelpers/SwingTestHelper.java b/test/jdk/javax/swing/regtesthelpers/SwingTestHelper.java index 5b6d35fa49a0a..8c10397c1fcd6 100644 --- a/test/jdk/javax/swing/regtesthelpers/SwingTestHelper.java +++ b/test/jdk/javax/swing/regtesthelpers/SwingTestHelper.java @@ -4,9 +4,7 @@ * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/test/jdk/javax/swing/text/AbstractDocument/DocumentInsert/DocumentInsertAtWrongPositionTest.java b/test/jdk/javax/swing/text/AbstractDocument/DocumentInsert/DocumentInsertAtWrongPositionTest.java index a6da38699c611..54b1de1a03a68 100644 --- a/test/jdk/javax/swing/text/AbstractDocument/DocumentInsert/DocumentInsertAtWrongPositionTest.java +++ b/test/jdk/javax/swing/text/AbstractDocument/DocumentInsert/DocumentInsertAtWrongPositionTest.java @@ -4,9 +4,7 @@ * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/test/jdk/javax/swing/text/BoxView/BaselineTest.java b/test/jdk/javax/swing/text/BoxView/BaselineTest.java new file mode 100644 index 0000000000000..1af8d6cb28626 --- /dev/null +++ b/test/jdk/javax/swing/text/BoxView/BaselineTest.java @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 4519537 4522866 + * @summary Tests that text and components in paragraph views line up at their baselines. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual BaselineTest + */ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.GridLayout; + +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JScrollPane; +import javax.swing.JTextPane; + +import javax.swing.text.ComponentView; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.text.Element; +import javax.swing.text.SimpleAttributeSet; +import javax.swing.text.StyleConstants; +import javax.swing.text.StyledEditorKit; +import javax.swing.text.View; +import javax.swing.text.ViewFactory; + +public class BaselineTest { + + static final String INSTRUCTIONS = """ + Test that components displayed in a JTextPane properly respect their vertical alignment. + There are two text panes, stacked vertically with similar content except the bottom components are taller. + The content consists of a leading and trailing text string, with pink coloured components between. + The text string content means the strings 'Default Size Text' and 'Large Size Text'. + Text content baseline is at the bottom of CAPITAL letters in the text. + Each pink component has a string displaying its alignment setting in the range 0.0 to 1.0 + NB: The position of the strings "align = 0.0" etc is not important, it is the component position that matters. + 0.0 means it should be aligned with its top at the text content baseline, + 1.0 means it should be aligned with its bottom at the text content baseline. + A value in between will be a proportional alignment, eg 0.5 is centered on the text content baseline + If the content displays as described, the test PASSES. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("BaselineTest Test Instructions") + .instructions(INSTRUCTIONS) + .columns(60) + .rows(12) + .testUI(BaselineTest::createUI) + .positionTestUIBottomRowCentered() + .build() + .awaitAndCheck(); + } + + public static JFrame createUI() { + JFrame frame = new JFrame("BaselineTest"); + frame.setLayout(new GridLayout(2, 1)); + + JTextPane prefPane = new JTextPane(); + initJTextPane(prefPane); + frame.add(new JScrollPane(prefPane)); + + JTextPane variablePane = new JTextPane(); + variablePane.setEditorKit(new CustomEditorKit()); + initJTextPane(variablePane); + frame.add(new JScrollPane(variablePane)); + frame.setSize(800, 400); + return frame; + } + + static void initJTextPane(JTextPane tp) { + + try { + Document doc = tp.getDocument(); + + doc.insertString(0, " Default Size Text ", null); + tp.setCaretPosition(doc.getLength()); + tp.insertComponent(new PaintLabel(0.0f)); + tp.insertComponent(new PaintLabel(0.2f)); + tp.insertComponent(new PaintLabel(0.5f)); + tp.insertComponent(new PaintLabel(0.7f)); + tp.insertComponent(new PaintLabel(1.0f)); + SimpleAttributeSet set = new SimpleAttributeSet(); + StyleConstants.setFontSize(set, 20); + tp.setCaretPosition(doc.getLength()); + doc.insertString(doc.getLength(), " Large Size Text ", set); + } catch (BadLocationException ble) { + throw new RuntimeException(ble); + } + } + + static class PaintLabel extends JLabel { + + private int pref = 40; + private int min = pref - 30; + private int max = pref + 30; + + public PaintLabel(float align) { + + setAlignmentY(align); + String alignStr = String.valueOf(align); + + setText("align = " + alignStr); + setOpaque(true); + setBackground(Color.PINK); + } + + public Dimension getMinimumSize() { + return new Dimension(super.getMinimumSize().width, min); + } + + public Dimension getPreferredSize() { + return new Dimension(super.getPreferredSize().width, pref); + } + + public Dimension getMaximumSize() { + return new Dimension(super.getMaximumSize().width, max); + } + + public void paintComponent(Graphics g) { + g.setColor(Color.PINK); + g.fillRect(0, 0, getWidth(), getHeight()); + int y = (int)(getAlignmentY() * getHeight()); + g.setColor(Color.BLACK); + g.drawLine(0, y, getWidth(), y); + g.drawString(getText(), 0, 10); + } + } + + static class CustomComponentView extends ComponentView { + + public CustomComponentView(Element elem) { + super(elem); + } + + public int getResizeWeight(int axis) { + return 1; + } +} + + static class CustomEditorKit extends StyledEditorKit implements ViewFactory { + + public View create(Element elem) { + if (StyleConstants.ComponentElementName.equals(elem.getName())) { + return new CustomComponentView(elem); + } else { + return super.getViewFactory().create(elem); + } + } + + public ViewFactory getViewFactory() { + return this; + } +} + +} diff --git a/test/jdk/javax/swing/text/BoxView/bug6494356.java b/test/jdk/javax/swing/text/BoxView/bug6494356.java new file mode 100644 index 0000000000000..fe899d5798461 --- /dev/null +++ b/test/jdk/javax/swing/text/BoxView/bug6494356.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 6494356 + * @key headful + * @summary Test that BoxView.layout() is not called with negative arguments + * @run main bug6494356 + */ + +import java.awt.Dimension; +import java.awt.Toolkit; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.CountDownLatch; +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; +import javax.swing.text.Element; +import javax.swing.text.StyleConstants; +import javax.swing.text.View; +import javax.swing.text.ViewFactory; +import javax.swing.text.html.HTML; +import javax.swing.text.html.HTMLEditorKit; +import javax.swing.text.html.ParagraphView; + +public class bug6494356 { + static JEditorPane ep; + private static final CountDownLatch latch = new CountDownLatch(1); + + public static void main(final String[] args) throws Exception { + final Path file = Path.of("bug6494356.html"); + try (Writer writer = Files.newBufferedWriter(file)) { + writer.write("

    Paragraph

    "); + } + try { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + ep = new JEditorPane(); + ep.setEditorKitForContentType("text/html", new MyEditorKit()); + ep.addPropertyChangeListener("page", new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent pce) { + if (pce.getPropertyName().equals("page")) { + latch.countDown(); + } + } + }); + JFrame f = new JFrame(); + f.setTitle("6494356"); + f.setSize(new Dimension( + Toolkit.getDefaultToolkit().getScreenSize().width, 600)); + f.setContentPane(ep); + f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + f.setVisible(true); + try { + ep.setPage("file:" + file); + } catch (Exception ex) { + testPassed = false; + throw new RuntimeException(ex); + } + } + }); + + latch.await(); + if (!testPassed) { + throw new RuntimeException("test failed."); + } + } finally { + Files.delete(file); + } + System.out.println("6494356 OK"); + } + + static volatile boolean testPassed = true; + + static class MyEditorKit extends HTMLEditorKit { + static class MyViewFactory extends HTMLFactory { + public View create(Element elem) { + HTML.Tag tag = (HTML.Tag) elem.getAttributes().getAttribute( + StyleConstants.NameAttribute); + if ((tag != null) && (tag == HTML.Tag.P)) { + return new MyParagraphView(elem); + } else { + return super.create(elem); + } + } + + static class MyParagraphView extends ParagraphView { + MyParagraphView(Element elem) { + super(elem); + } + + protected void layout(int width, int height) { + if ((width < 0) || (height < 0)) { + testPassed = false; + throw new RuntimeException("w=" + width + " h=" + height); + } + super.layout(width, height); + } + } + + } + + final ViewFactory viewFactory = new MyViewFactory(); + + public ViewFactory getViewFactory() { + return viewFactory; + } + } + +} + diff --git a/test/jdk/javax/swing/text/DefaultCaret/PaintTest.java b/test/jdk/javax/swing/text/DefaultCaret/PaintTest.java new file mode 100644 index 0000000000000..c554764527eaa --- /dev/null +++ b/test/jdk/javax/swing/text/DefaultCaret/PaintTest.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4193062 + * @summary Tests that when a TextField first gets focus, if modelToView fails + * (null is returned) that the caret will start to blink again. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual PaintTest +*/ + +import java.awt.FlowLayout; +import javax.swing.JFrame; +import javax.swing.JTextField; + +public class PaintTest { + + static final String INSTRUCTIONS = """ + If the test window displays with the text caret flashing (do wait at + least several second for it to start) the test PASSES, otherwise it FAILS. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("PaintTest Test Instructions") + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(PaintTest::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + JFrame frame = new JFrame("PaintTest"); + JTextField tf = new JTextField(20); + frame.setLayout(new FlowLayout()); + frame.add(tf); + frame.setSize(300, 300); + return frame; + } +} diff --git a/test/jdk/javax/swing/text/DefaultCaret/bug4785160.java b/test/jdk/javax/swing/text/DefaultCaret/bug4785160.java new file mode 100644 index 0000000000000..644becf8ae6ac --- /dev/null +++ b/test/jdk/javax/swing/text/DefaultCaret/bug4785160.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4785160 + * @summary Test that the cursor is always visible when typing in JTextArea with JScrollBar + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4785160 +*/ + +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; + +public class bug4785160 { + + static final String INSTRUCTIONS = """ + Ensure that the horizontal scrollbar is visible in the JTextArea. + If necessary, reduce the width of the window so that the scrollbar becomes visible. + Scroll all the way to the right so the end of the line is visible. + If necessary, move the text caret in the text area to the end of line. + The test PASSES if the caret is visible at the end of the line. + The test FAILS if the caret disappears when moved to the end of the line. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug4785160 Test Instructions") + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(bug4785160::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + JFrame frame = new JFrame("bug4785160"); + JTextArea area = new JTextArea(); + String s = ""; + for (int i = 0; i < 80; i++) { + s += "m"; + } + area.setText(s); + area.getCaret().setDot(area.getText().length() + 1); + frame.add(new JScrollPane(area)); + frame.setSize(300, 300); + return frame; + } +} diff --git a/test/jdk/javax/swing/text/DefaultEditorKit/4278839/bug4278839.java b/test/jdk/javax/swing/text/DefaultEditorKit/4278839/bug4278839.java index e67d2ddb5884a..819ba52d270a7 100644 --- a/test/jdk/javax/swing/text/DefaultEditorKit/4278839/bug4278839.java +++ b/test/jdk/javax/swing/text/DefaultEditorKit/4278839/bug4278839.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,17 +26,22 @@ * @key headful * @bug 4278839 8233634 * @summary Incorrect cursor movement between words at the end of line - * @author Anton Nashatyrev * @library ../../../regtesthelpers * @build Util * @run main bug4278839 */ -import java.awt.*; -import java.awt.event.*; -import javax.swing.*; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.event.KeyEvent; +import java.awt.event.InputEvent; +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; -public class bug4278839 extends JFrame { +public class bug4278839 { private static boolean passed = true; private static JTextArea area; @@ -44,40 +49,80 @@ public class bug4278839 extends JFrame { private static JFrame frame; public static void main(String[] args) throws Exception { + int caret; try { robo = new Robot(); - robo.setAutoDelay(200); + robo.setAutoDelay(100); - SwingUtilities.invokeAndWait(new Runnable() { - @Override - public void run() { - createAndShowGUI(); - } - }); + SwingUtilities.invokeAndWait(() -> createAndShowGUI()); robo.waitForIdle(); + robo.delay(1000); clickMouse(); robo.waitForIdle(); + robo.delay(250); area.setCaretPosition(0); robo.waitForIdle(); + robo.delay(250); - passed &= moveCaret(true) == 1; - passed &= moveCaret(true) == 5; - passed &= moveCaret(true) == 8; - passed &= moveCaret(true) == 9; - passed &= moveCaret(true) == 13; - passed &= moveCaret(true) == 16; - passed &= moveCaret(true) == 17; - passed &= moveCaret(false) == 16; - passed &= moveCaret(false) == 13; - passed &= moveCaret(false) == 9; - passed &= moveCaret(false) == 8; - passed &= moveCaret(false) == 5; - passed &= moveCaret(false) == 1; - passed &= moveCaret(false) == 0; + passed &= (caret = moveCaret(true)) == 1; + System.out.println(" passed " + passed + + " Expected position 1 actual position " + caret); + + passed &= (caret = moveCaret(true)) == 5; + System.out.println(" passed " + passed + + " Expected position 5 actual position " + caret); + + passed &= (caret = moveCaret(true)) == 8; + System.out.println(" passed " + passed + + " Expected position 8 actual position " + caret); + + passed &= (caret = moveCaret(true)) == 9; + System.out.println(" passed " + passed + + " Expected position 9 actual position " + caret); + + passed &= (caret = moveCaret(true)) == 13; + System.out.println(" passed " + passed + + " Expected position 13 actual position " + caret); + + passed &= (caret = moveCaret(true)) == 16; + System.out.println(" passed " + passed + + " Expected position 16 actual position " + caret); + + passed &= (caret = moveCaret(true)) == 17; + System.out.println(" passed " + passed + + " Expected position 17 actual position " + caret); + + passed &= (caret = moveCaret(false)) == 16; + System.out.println(" passed " + passed + + " Expected position 16 actual position " + caret); + + passed &= (caret = moveCaret(false)) == 13; + System.out.println(" passed " + passed + + " Expected position 13 actual position " + caret); + + passed &= (caret = moveCaret(false)) == 9; + System.out.println(" passed " + passed + + " Expected position 9 actual position " + caret); + + passed &= (caret = moveCaret(false)) == 8; + System.out.println(" passed " + passed + + " Expected position 8 actual position " + caret); + + passed &= (caret = moveCaret(false)) == 5; + System.out.println(" passed " + passed + + " Expected position 5 actual position " + caret); + + passed &= (caret = moveCaret(false)) == 1; + System.out.println(" passed " + passed + + " Expected position 1 actual position " + caret); + + passed &= (caret = moveCaret(false)) == 0; + System.out.println(" passed " + passed + + " Expected position 0 actual position " + caret); } catch (Exception e) { throw new RuntimeException("Test failed because of an exception:", @@ -97,36 +142,31 @@ private static int moveCaret(boolean right) throws Exception { Util.hitKeys(robo, getCtrlKey(), right ? KeyEvent.VK_RIGHT : KeyEvent.VK_LEFT); robo.waitForIdle(); + robo.delay(250); final int[] result = new int[1]; - SwingUtilities.invokeAndWait(new Runnable() { - - @Override - public void run() { - result[0] = area.getCaretPosition(); - } + SwingUtilities.invokeAndWait(() -> { + result[0] = area.getCaretPosition(); }); - int pos = result[0]; - return pos; + return result[0]; } private static void clickMouse() throws Exception { final Rectangle result[] = new Rectangle[1]; - SwingUtilities.invokeAndWait(new Runnable() { - @Override - public void run() { - result[0] = new Rectangle(area.getLocationOnScreen(), area.getSize()); - } + SwingUtilities.invokeAndWait(() -> { + result[0] = new Rectangle(area.getLocationOnScreen(), area.getSize()); }); Rectangle rect = result[0]; robo.mouseMove(rect.x + rect.width / 2, rect.y + rect.width / 2); - robo.mousePress(InputEvent.BUTTON1_MASK); - robo.mouseRelease(InputEvent.BUTTON1_MASK); + robo.waitForIdle(); + robo.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robo.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robo.waitForIdle(); } /** diff --git a/test/jdk/javax/swing/text/GapContent/4496801/bug4496801.java b/test/jdk/javax/swing/text/GapContent/4496801/bug4496801.java index aa258a8dc8650..d0d425668a113 100644 --- a/test/jdk/javax/swing/text/GapContent/4496801/bug4496801.java +++ b/test/jdk/javax/swing/text/GapContent/4496801/bug4496801.java @@ -4,9 +4,7 @@ * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/test/jdk/javax/swing/text/GlyphView/4984669/bug4984669.html b/test/jdk/javax/swing/text/GlyphView/4984669/bug4984669.html deleted file mode 100644 index f9991a231c1a5..0000000000000 --- a/test/jdk/javax/swing/text/GlyphView/4984669/bug4984669.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - -The four lines printed above in a bold typeface should all be underlined. -It is a bug if any of these lines is underlined only partially. -The very first line should not be underlined at all. - - diff --git a/test/jdk/javax/swing/text/GlyphView/bug4188841.java b/test/jdk/javax/swing/text/GlyphView/bug4188841.java new file mode 100644 index 0000000000000..9935712bd49c3 --- /dev/null +++ b/test/jdk/javax/swing/text/GlyphView/bug4188841.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 4188841 + * @summary Tests a JTextPane wrapping issue + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4188841 +*/ + +import java.awt.Dimension; +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTextPane; + +public class bug4188841 { + + static final String INSTRUCTIONS = """ + The text pane contains the phrase "the quick brown fox jumps over the lazy dog", + all the words are separated by tabs. When the test starts, the whole phrase should + appear on one line. If it is wrapped along two or more lines, the test FAILS. + + Otherwise, place the text caret in the very end of the line (e.g. by clicking + in the line and hitting End). Press Enter twice. The text should appear as one + line at all times. If the text wraps when you press Enter, the test FAILS. + """; + + static class NoWrapTextPane extends JTextPane { + + public boolean getScrollableTracksViewportWidth() { + //should not allow text to be wrapped + return false; + } + + public void setSize(Dimension d) { + // don't let the Textpane get sized smaller than its parent + if (d.width < getParent().getSize().width) { + super.setSize(getParent().getSize()); + } + else { + super.setSize(d); + } + } + } + + static JFrame createUI() { + + JFrame frame = new JFrame("bug4188841"); + + NoWrapTextPane nwp = new NoWrapTextPane(); + nwp.setText("the\tquick\tbrown\tfox\tjumps\tover\tthe\tlazy\tdog!"); + nwp.setCaretPosition(nwp.getText().length()); + + JScrollPane scrollPane = new JScrollPane(nwp, + JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, + JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); + + frame.add(scrollPane); + frame.setSize(400, 300); + return frame; + } + + public static void main(String args[]) throws Exception { + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(60) + .testUI(bug4188841::createUI) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/javax/swing/text/GlyphView/4984669/bug4984669.java b/test/jdk/javax/swing/text/GlyphView/htmlUnderliningTest.java similarity index 54% rename from test/jdk/javax/swing/text/GlyphView/4984669/bug4984669.java rename to test/jdk/javax/swing/text/GlyphView/htmlUnderliningTest.java index ba590f9472a5f..b12a129dfa291 100644 --- a/test/jdk/javax/swing/text/GlyphView/4984669/bug4984669.java +++ b/test/jdk/javax/swing/text/GlyphView/htmlUnderliningTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,24 +21,48 @@ * questions. */ +import javax.swing.JEditorPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.text.MutableAttributeSet; +import javax.swing.text.SimpleAttributeSet; +import javax.swing.text.StyleConstants; +import javax.swing.text.StyledEditorKit; + /* @test - @bug 4984669 8002148 - @summary Tests HTML underlining - @author Peter Zhelezniakov - @run applet/manual=yesno bug4984669.html -*/ -import javax.swing.*; -import javax.swing.text.*; + * @bug 4984669 8002148 + * @summary Tests HTML underlining + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual htmlUnderliningTest + */ + +public class htmlUnderliningTest { + public static void main(String[] args) throws Exception { + String testInstructions = """ + The four lines printed in a bold typeface should all be underlined. + It is a bug if any of these lines is underlined only partially. + The very first line should not be underlined at all. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(testInstructions) + .rows(4) + .columns(35) + .splitUI(htmlUnderliningTest::initializeTest) + .build() + .awaitAndCheck(); + } -public class bug4984669 extends JApplet -{ - public void init() { + public static JPanel initializeTest() { + JPanel panel = new JPanel(); JEditorPane pane = new JEditorPane(); - this.getContentPane().add(new JScrollPane(pane)); + panel.add(new JScrollPane(pane)); pane.setEditorKit(new StyledEditorKit()); try { - pane.getDocument().insertString(0,"12 \n",null); + pane.getDocument().insertString(0, "12 \n", null); MutableAttributeSet attrs = new SimpleAttributeSet(); StyleConstants.setFontSize(attrs, 36); @@ -51,5 +75,6 @@ public void init() { } catch (Exception e) { throw new Error("Failed: Unexpected Exception", e); } + return panel; } } diff --git a/test/jdk/javax/swing/text/html/CSS/ColorValue/RGBColorValueTest.java b/test/jdk/javax/swing/text/html/CSS/ColorValue/RGBColorValueTest.java index fce9408717dca..c3a563d42b54e 100644 --- a/test/jdk/javax/swing/text/html/CSS/ColorValue/RGBColorValueTest.java +++ b/test/jdk/javax/swing/text/html/CSS/ColorValue/RGBColorValueTest.java @@ -4,9 +4,7 @@ * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/test/jdk/javax/swing/text/html/CSS/bug4174871.java b/test/jdk/javax/swing/text/html/CSS/bug4174871.java new file mode 100644 index 0000000000000..7ae27150be5fa --- /dev/null +++ b/test/jdk/javax/swing/text/html/CSS/bug4174871.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Robot; +import java.awt.Shape; +import javax.swing.JFrame; +import javax.swing.JTextPane; +import javax.swing.SwingUtilities; +import javax.swing.text.View; + +/* + * @test + * @bug 4174871 + * @key headful + * @summary Tests if CELLSPACING attribute in HTML table is rendered. + */ + +public class bug4174871 { + private static JFrame frame; + private static JTextPane pane; + private static volatile boolean passed = false; + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + + SwingUtilities.invokeAndWait(bug4174871::createAndShowUI); + robot.waitForIdle(); + robot.delay(500); + + SwingUtilities.invokeAndWait(bug4174871::testUI); + + if (!passed) { + throw new RuntimeException("Test failed!!" + + " CELLSPACING attribute in HTML table is NOT rendered"); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + public static void createAndShowUI() { + pane = new JTextPane(); + pane.setContentType("text/html"); + pane.setText("" + + "" + + "" + + "
    onetwothree
    "); + + frame = new JFrame("Table CellSpacing Test"); + frame.getContentPane().add(pane); + frame.setSize(600, 200); + frame.setVisible(true); + } + + private static void testUI() { + int tableWidth = 0; + Shape r = pane.getBounds(); + View v = pane.getUI().getRootView(pane); + + while (!(v instanceof javax.swing.text.html.ParagraphView)) { + int n = v.getViewCount(); + Shape sh = v.getChildAllocation(n - 1, r); + String viewName = v.getClass().getName(); + if (viewName.endsWith("TableView")) { + tableWidth = r.getBounds().width; + } + v = v.getView(n - 1); + if (sh != null) { + r = sh; + } + } + // tableWidth should be the sum of TD's widths (300) + // and cellspacings (80) + passed = (tableWidth >= 380); + } +} diff --git a/test/jdk/javax/swing/text/html/CSS/bug4174874.java b/test/jdk/javax/swing/text/html/CSS/bug4174874.java new file mode 100644 index 0000000000000..b6ba85014dafc --- /dev/null +++ b/test/jdk/javax/swing/text/html/CSS/bug4174874.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.JFrame; +import javax.swing.JTextPane; +import javax.swing.SwingUtilities; +import javax.swing.text.View; +import java.awt.Robot; +import java.awt.Shape; + +/* + * @test + * @bug 4174874 + * @key headful + * @summary Tests if borders in HTML table are rendered + */ + +public class bug4174874 { + private static JFrame frame; + private static JTextPane pane; + private static volatile boolean passed = false; + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + + SwingUtilities.invokeAndWait(bug4174874::createAndShowUI); + robot.waitForIdle(); + robot.delay(500); + + SwingUtilities.invokeAndWait(bug4174874::testUI); + + if (!passed) { + throw new RuntimeException("Test failed!!" + + " Borders of HTML table not rendered correctly"); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + public static void createAndShowUI() { + pane = new JTextPane(); + pane.setContentType("text/html"); + pane.setText("" + + "" + + "" + + "
    onetwothree
    "); + + frame = new JFrame("Table Border Test"); + frame.getContentPane().add(pane); + frame.setSize(600, 200); + frame.setVisible(true); + } + + private static void testUI() { + Shape r = pane.getBounds(); + View v = pane.getUI().getRootView(pane); + int tableWidth = 0; + while (!(v instanceof javax.swing.text.html.ParagraphView)) { + int n = v.getViewCount(); + Shape sh = v.getChildAllocation(n - 1, r); + String viewName = v.getClass().getName(); + if (viewName.endsWith("TableView")) { + tableWidth = r.getBounds().width; + } + v = v.getView(n - 1); + if (sh != null) { + r = sh; + } + } + // tableWidth should be the sum of TD's widths (300) + // and border width * 2 (40) + passed = tableWidth >= 340; + } +} diff --git a/test/jdk/javax/swing/text/html/CSS/bug4271058.java b/test/jdk/javax/swing/text/html/CSS/bug4271058.java new file mode 100644 index 0000000000000..83aad377b1def --- /dev/null +++ b/test/jdk/javax/swing/text/html/CSS/bug4271058.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4270889 4271058 4285098 + * @summary Tests that ,
    and
    tags work + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4271058 +*/ + +import javax.swing.JEditorPane; +import javax.swing.JFrame; + +public class bug4271058 { + + private static String INSTRUCTIONS = """ + What should be seen is three 2x2 tables. + + The first table should have borders and lines distinguishing + table cells. If they are not shown, test fails (bug 4271058). + + In the second table, the first (left) column should be about + four times as wide as the second (right) column. + If this is not so, test fails (bug 4270889). + + The third table should be right aligned, i.e. its right edge + should be close to the right edge of the viewable area. + Otherwise test fails (bug 4285098). + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("CSS html tag verification Instructions") + .instructions(INSTRUCTIONS) + .rows(15) + .columns(30) + .testUI(bug4271058::createTestUI) + .screenCapture() + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + String htmlText = + "" + + "" + + "" + + "" + + "" + + "
    col Acol B
    aaaaaabbbbbbb
    " + + "" + + "" + + "" + + "" + + "" + + "
    AB
    ab
    " + + "" + + "" + + "" + + "" + + "" + + "
    col Acol B
    aaaaaabbbbbbb
    "; + + JEditorPane lbl = new JEditorPane("text/html", htmlText); + JFrame frame = new JFrame("bug4271058"); + frame.add(lbl); + frame.pack(); + return frame; + } +} diff --git a/test/jdk/javax/swing/text/html/CSS/bug4284162.java b/test/jdk/javax/swing/text/html/CSS/bug4284162.java new file mode 100644 index 0000000000000..e1f7116c0a82a --- /dev/null +++ b/test/jdk/javax/swing/text/html/CSS/bug4284162.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Robot; +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; +import javax.swing.text.AttributeSet; +import javax.swing.text.StyleConstants; +import javax.swing.text.View; +import javax.swing.text.html.HTMLEditorKit; + + +/* + * @test + * @bug 4284162 + * @key headful + * @summary Tests if css text-indent attribute is supported with negative values. + */ + +public class bug4284162 { + private static JEditorPane jep; + private static JFrame frame; + private static volatile boolean passed = false; + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + + SwingUtilities.invokeAndWait(bug4284162::createAndShowUI); + robot.waitForIdle(); + robot.delay(500); + + SwingUtilities.invokeAndWait(bug4284162::testUI); + + if (!passed) { + throw new RuntimeException("Test failed!!" + + " CSS Text-indent attribute doesn't support negative values"); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + public static void createAndShowUI() { + String text ="" + + "

    paragraph"; + + frame = new JFrame("CSS Text-Indent Test"); + jep = new JEditorPane(); + jep.setEditorKit(new HTMLEditorKit()); + jep.setEditable(false); + + jep.setText(text); + + frame.getContentPane().add(jep); + frame.setSize(200, 200); + frame.setVisible(true); + } + + private static void testUI() { + View v = jep.getUI().getRootView(jep); + while (!(v instanceof javax.swing.text.html.ParagraphView)) { + int n = v.getViewCount(); + v = v.getView(n - 1); + } + + AttributeSet attrs = v.getAttributes(); + Object attr = attrs.getAttribute(StyleConstants.FirstLineIndent); + passed = (attr.toString().startsWith("-")); + } +} diff --git a/test/jdk/javax/swing/text/html/CSS/bug4286458.java b/test/jdk/javax/swing/text/html/CSS/bug4286458.java new file mode 100644 index 0000000000000..110967d9e1dd7 --- /dev/null +++ b/test/jdk/javax/swing/text/html/CSS/bug4286458.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4286458 + * @summary Tests if cellpadding in tables is non-negative number + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4286458 +*/ + +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.text.html.HTMLEditorKit; + +public class bug4286458 { + + private static String INSTRUCTIONS = """ + If you can clearly read the line of text in the appeared frame + press PASS. Otherwise test fails."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("CSS tag Instructions") + .instructions(INSTRUCTIONS) + .rows(5) + .columns(30) + .testUI(bug4286458::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + + String text = + "" + + "" + + "
    This line should be clearly readable
    "; + + JFrame f = new JFrame("bug4286458"); + JEditorPane jep = new JEditorPane("text/html", text); + jep.setEditable(false); + + f.add(jep); + f.pack(); + return f; + } +} diff --git a/test/jdk/javax/swing/text/html/CSS/bug4764897.java b/test/jdk/javax/swing/text/html/CSS/bug4764897.java new file mode 100644 index 0000000000000..68de9b1bb847f --- /dev/null +++ b/test/jdk/javax/swing/text/html/CSS/bug4764897.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Robot; +import java.awt.Shape; +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.JTextPane; +import javax.swing.SwingUtilities; +import javax.swing.text.View; + +/* + * @test + * @bug 4764897 + * @key headful + * @summary Tests if text and borders in HTML table doesn't run over right edge + */ + +public class bug4764897 { + private static JEditorPane pane; + private static JFrame frame; + private static volatile boolean passed = false; + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + + SwingUtilities.invokeAndWait(bug4764897::createAndShowUI); + robot.waitForIdle(); + robot.delay(500); + + SwingUtilities.invokeAndWait(bug4764897::testUI); + + if (!passed) { + throw new RuntimeException("Test failed!!" + + " Text and Borders of HTML table run over the right edge"); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + public static void createAndShowUI() { + pane = new JTextPane(); + pane.setContentType("text/html"); + pane.setText("" + + "" + + "" + + "" + + "
    one ThisIsAnExtraWideWord" + + "This is text that won't get displayed correctly.
    "); + + frame = new JFrame("Table Border & Text Test"); + frame.getContentPane().add(pane); + frame.setSize(600, 200); + frame.setVisible(true); + } + + private static void testUI() { + Shape r = pane.getBounds(); + View v = pane.getUI().getRootView(pane); + int tableWidth = 0; + int cellsWidth = 0; + while (!(v instanceof javax.swing.text.html.ParagraphView)) { + int n = v.getViewCount(); + Shape sh = v.getChildAllocation(n - 1, r); + String viewName = v.getClass().getName(); + if (viewName.endsWith("TableView")) { + tableWidth = r.getBounds().width; + } + + if (viewName.endsWith("CellView")) { + cellsWidth = r.getBounds().x + r.getBounds().width; + } + + v = v.getView(n - 1); + if (sh != null) { + r = sh; + } + } + passed = cellsWidth <= tableWidth; + } +} diff --git a/test/jdk/javax/swing/text/html/FormView/4473401/bug4473401.java b/test/jdk/javax/swing/text/html/FormView/4473401/bug4473401.java new file mode 100644 index 0000000000000..29dbba7ca0458 --- /dev/null +++ b/test/jdk/javax/swing/text/html/FormView/4473401/bug4473401.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 4473401 + * @summary Tests if FormSubmitEvent is submitted correctly. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4473401 +*/ + +import java.io.File; +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; +import javax.swing.text.html.FormSubmitEvent; +import javax.swing.text.html.HTMLEditorKit; + +public class bug4473401 implements HyperlinkListener { + + static final String INSTRUCTIONS = """ + The test window displays an HTML frameset with a frame + on the left and another to the right. + Push the 'Submit Query' button in the left frame to perform testing. + The window should be updated to have only one frame with the + message 'If you see this page the test PASSED'. + If it appears, PASS the test, otherwise FAIL the test. + """; + + static JEditorPane jep; + static JFrame createUI() { + + JFrame frame = new JFrame("bug4473401"); + jep = new JEditorPane(); + jep.addHyperlinkListener(new bug4473401()); + HTMLEditorKit kit = new HTMLEditorKit(); + kit.setAutoFormSubmission(false); + jep.setEditorKit(kit); + jep.setEditable(false); + + try { + File file = new File(System.getProperty("test.src", "."), "frameset.html"); + System.out.println(file.toURI().toURL()); + jep.setPage(file.toURL()); + } catch (Exception e) { + throw new RuntimeException(e); + } + + frame.add(jep); + frame.setSize(500, 500); + return frame; + } + + public void hyperlinkUpdate(HyperlinkEvent e) { + if (e instanceof FormSubmitEvent) { + jep.setText("If you see this page the test PASSED"); + } + } + + public static void main(String args[]) throws Exception { + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug4473401::createUI) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/javax/swing/text/html/FormView/4473401/frame1.html b/test/jdk/javax/swing/text/html/FormView/4473401/frame1.html new file mode 100644 index 0000000000000..833bc1f635297 --- /dev/null +++ b/test/jdk/javax/swing/text/html/FormView/4473401/frame1.html @@ -0,0 +1,9 @@ + + +Push the Submit query button +

    +
    + +
    + + diff --git a/test/jdk/javax/swing/text/html/FormView/4473401/frame2.html b/test/jdk/javax/swing/text/html/FormView/4473401/frame2.html new file mode 100644 index 0000000000000..5bc6dba57fc4c --- /dev/null +++ b/test/jdk/javax/swing/text/html/FormView/4473401/frame2.html @@ -0,0 +1,4 @@ + + + + diff --git a/test/jdk/javax/swing/text/html/FormView/4473401/frameresult.html b/test/jdk/javax/swing/text/html/FormView/4473401/frameresult.html new file mode 100644 index 0000000000000..6ef5c8198cc35 --- /dev/null +++ b/test/jdk/javax/swing/text/html/FormView/4473401/frameresult.html @@ -0,0 +1,5 @@ + + +If you see this page the test FAILED. + + diff --git a/test/jdk/javax/swing/text/html/FormView/4473401/frameset.html b/test/jdk/javax/swing/text/html/FormView/4473401/frameset.html new file mode 100644 index 0000000000000..1e7f8298d628b --- /dev/null +++ b/test/jdk/javax/swing/text/html/FormView/4473401/frameset.html @@ -0,0 +1,11 @@ + + + + Manual test for bug 4463014 + + + + + + + \ No newline at end of file diff --git a/test/jdk/javax/swing/text/html/FormView/bug4529702.java b/test/jdk/javax/swing/text/html/FormView/bug4529702.java new file mode 100644 index 0000000000000..4962a0a666304 --- /dev/null +++ b/test/jdk/javax/swing/text/html/FormView/bug4529702.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 4529702 + * @summary Test that radio buttons with different names should be selectable at the same time + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4529702 +*/ + +import javax.swing.JFrame; +import javax.swing.JTextPane; + +public class bug4529702 { + + static final String INSTRUCTIONS = """ + There are two rows of radio buttons, each row having two buttons. + If you can select radio buttons from the first and the second rows + at the same time the test PASSES otherwise the test FAILS. + """; + + static JFrame createUI() { + JFrame frame = new JFrame("bug4529702"); + JTextPane jtp = new JTextPane(); + jtp.setContentType("text/html"); + jtp.setText("

    " + + "
    "); + frame.add(jtp); + frame.setSize(200, 200); + return frame; + } + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug4529702::createUI) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/javax/swing/text/html/FrameSetView/4890934/bug4890934.java b/test/jdk/javax/swing/text/html/FrameSetView/4890934/bug4890934.java new file mode 100644 index 0000000000000..988db296f7b6f --- /dev/null +++ b/test/jdk/javax/swing/text/html/FrameSetView/4890934/bug4890934.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 4890934 + * @summary Tests if JEditor Pane updates the correct frame when using + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4890934 +*/ + +import java.io.File; +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; +import javax.swing.text.html.HTMLDocument; +import javax.swing.text.html.HTMLEditorKit; +import javax.swing.text.html.HTMLFrameHyperlinkEvent; + +public class bug4890934 implements HyperlinkListener { + + static final String INSTRUCTIONS = """ + The test window displays an HTML frameset with a frame + on the left and another to the right. + Click the link in the left frame which should change the view. + The resulting page will tell you if the test PASSED or FAILED. + """; + + static JFrame createUI() { + + JFrame frame = new JFrame("bug4890934"); + JEditorPane jep = new JEditorPane(); + jep.setEditorKit(new HTMLEditorKit()); + jep.setEditable(false); + jep.addHyperlinkListener(new bug4890934()); + + try { + File file = new File(System.getProperty("test.src", "."), "frameset.html"); + System.out.println(file.toURI().toURL()); + jep.setPage(file.toURL()); + } catch (Exception e) { + throw new RuntimeException(e); + } + + frame.add(jep); + frame.setSize(600, 300); + return frame; + } + + public void hyperlinkUpdate(HyperlinkEvent e) { + if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + JEditorPane pane = (JEditorPane) e.getSource(); + if (e instanceof HTMLFrameHyperlinkEvent) { + HTMLFrameHyperlinkEvent evt = (HTMLFrameHyperlinkEvent)e; + HTMLDocument doc = (HTMLDocument)pane.getDocument(); + doc.processHTMLFrameHyperlinkEvent(evt); + } + } + } + + public static void main(String args[]) throws Exception { + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug4890934::createUI) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/javax/swing/text/html/FrameSetView/4890934/frame1.html b/test/jdk/javax/swing/text/html/FrameSetView/4890934/frame1.html new file mode 100644 index 0000000000000..31f95568aaeec --- /dev/null +++ b/test/jdk/javax/swing/text/html/FrameSetView/4890934/frame1.html @@ -0,0 +1,8 @@ + + + + + + a link + + diff --git a/test/jdk/javax/swing/text/html/FrameSetView/4890934/frame2.html b/test/jdk/javax/swing/text/html/FrameSetView/4890934/frame2.html new file mode 100644 index 0000000000000..5bc6dba57fc4c --- /dev/null +++ b/test/jdk/javax/swing/text/html/FrameSetView/4890934/frame2.html @@ -0,0 +1,4 @@ + + + + diff --git a/test/jdk/javax/swing/text/html/FrameSetView/4890934/frameresult.html b/test/jdk/javax/swing/text/html/FrameSetView/4890934/frameresult.html new file mode 100644 index 0000000000000..a31045833b5c6 --- /dev/null +++ b/test/jdk/javax/swing/text/html/FrameSetView/4890934/frameresult.html @@ -0,0 +1,7 @@ + + +If you see this text in the RIGHT frame the test PASSED. +

    If you see this text in the LEFT frame the test FAILED. + + + diff --git a/test/jdk/javax/swing/text/html/FrameSetView/4890934/frameset.html b/test/jdk/javax/swing/text/html/FrameSetView/4890934/frameset.html new file mode 100644 index 0000000000000..9462e1894a590 --- /dev/null +++ b/test/jdk/javax/swing/text/html/FrameSetView/4890934/frameset.html @@ -0,0 +1,11 @@ + + + + Manual test for bug 4463014 + + + + + + + diff --git a/test/jdk/javax/swing/text/html/FrameView/4463014/bug4463014.java b/test/jdk/javax/swing/text/html/FrameView/4463014/bug4463014.java new file mode 100644 index 0000000000000..4b7199c901754 --- /dev/null +++ b/test/jdk/javax/swing/text/html/FrameView/4463014/bug4463014.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 4463014 + * @summary Tests if JEditorPane updates the correct frame when using

    + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4463014 + */ + +import java.io.File; +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.text.html.HTMLEditorKit; + +public class bug4463014 { + + static final String INSTRUCTIONS = """ + The test window displays an HTML frameset with a frame + on the left and another to the right. + Follow the instructions displayed in the left frame to perform testing. + The test PASSES only if the test behaves as per instructions. + """; + + static JFrame createUI() { + JFrame frame = new JFrame("bug4463014"); + JEditorPane jep = new JEditorPane(); + jep.setEditorKit(new HTMLEditorKit()); + jep.setEditable(false); + + try { + File file = new File(System.getProperty("test.src", "."), "frameset.html"); + System.out.println(file.toURL()); + jep.setPage(file.toURL()); + } catch (Exception e) { + } + + frame.add(jep); + frame.setSize(500,500); + return frame; + } + + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug4463014::createUI) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/javax/swing/text/html/FrameView/4463014/frame1.html b/test/jdk/javax/swing/text/html/FrameView/4463014/frame1.html new file mode 100644 index 0000000000000..833bc1f635297 --- /dev/null +++ b/test/jdk/javax/swing/text/html/FrameView/4463014/frame1.html @@ -0,0 +1,9 @@ + + +Push the Submit query button + +
    + +
    + + diff --git a/test/jdk/javax/swing/text/html/FrameView/4463014/frame2.html b/test/jdk/javax/swing/text/html/FrameView/4463014/frame2.html new file mode 100644 index 0000000000000..5bc6dba57fc4c --- /dev/null +++ b/test/jdk/javax/swing/text/html/FrameView/4463014/frame2.html @@ -0,0 +1,4 @@ + + + + diff --git a/test/jdk/javax/swing/text/html/FrameView/4463014/frameresult.html b/test/jdk/javax/swing/text/html/FrameView/4463014/frameresult.html new file mode 100644 index 0000000000000..a31045833b5c6 --- /dev/null +++ b/test/jdk/javax/swing/text/html/FrameView/4463014/frameresult.html @@ -0,0 +1,7 @@ + + +If you see this text in the RIGHT frame the test PASSED. +

    If you see this text in the LEFT frame the test FAILED. + + + diff --git a/test/jdk/javax/swing/text/html/FrameView/4463014/frameset.html b/test/jdk/javax/swing/text/html/FrameView/4463014/frameset.html new file mode 100644 index 0000000000000..1e7f8298d628b --- /dev/null +++ b/test/jdk/javax/swing/text/html/FrameView/4463014/frameset.html @@ -0,0 +1,11 @@ + + + + Manual test for bug 4463014 + + + + + + + \ No newline at end of file diff --git a/test/hotspot/jtreg/vmTestbase/vm/share/UnsafeAccess.java b/test/jdk/javax/swing/text/html/HTMLDocument/bug4209280.java similarity index 70% rename from test/hotspot/jtreg/vmTestbase/vm/share/UnsafeAccess.java rename to test/jdk/javax/swing/text/html/HTMLDocument/bug4209280.java index 79a0a7392deff..e9170d8cf2dad 100644 --- a/test/hotspot/jtreg/vmTestbase/vm/share/UnsafeAccess.java +++ b/test/jdk/javax/swing/text/html/HTMLDocument/bug4209280.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -20,23 +20,20 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package vm.share; -import java.lang.reflect.Field; +import javax.swing.JLabel; -import jdk.internal.misc.Unsafe; +/* + * @test + * @bug 4209280 + * @summary Tests that no exception is thrown on unknown HTML tag + */ -@SuppressWarnings("restriction") -public class UnsafeAccess { - public static Unsafe unsafe; +public class bug4209280 { - static { - try { - unsafe = Unsafe.getUnsafe(); - } catch ( Exception e ) { - e.printStackTrace(); - } + public static void main(String[] args) throws Exception { + String html = "Foo"; + // The following line should throw no exceptions + new JLabel(html); } - - } diff --git a/test/jdk/javax/swing/text/html/HTMLEditorKit/bug4102068.java b/test/jdk/javax/swing/text/html/HTMLEditorKit/bug4102068.java new file mode 100644 index 0000000000000..39dd1d539255a --- /dev/null +++ b/test/jdk/javax/swing/text/html/HTMLEditorKit/bug4102068.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 4102068 + * @summary Tests HTML editor JEditorPane change mouse icon over the link + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4102068 + */ + +import java.awt.Cursor; +import javax.swing.JFrame; +import javax.swing.JTextPane; +import javax.swing.text.html.HTMLEditorKit; + +public class bug4102068 { + + static final String INSTRUCTIONS = """ + The test window contains an HTML frame containing a string with one hyperlink. + Move the mouse pointer over this hyperlink. + If the pointer over the hyperlink became a HAND cursor then the test PASSES, + otherwise the test FAILS. + """; + + static JFrame createUI() { + + JFrame frame = new JFrame("bug4102068"); + JTextPane ep = new JTextPane(); + ep.setContentType("text/html"); + HTMLEditorKit ek = new HTMLEditorKit(); + ep.setEditorKit(ek); + ep.setText("Here is a HyperLink Cursor Test"); + ek.setDefaultCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + Cursor ct = ek.getDefaultCursor(); + ek.setLinkCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + Cursor cl = ek.getLinkCursor(); + if (ct.getType() != Cursor.DEFAULT_CURSOR || cl.getType() != Cursor.HAND_CURSOR) { + throw new RuntimeException("Error with cursor settings..."); + } + ep.setEditable(false); + + frame.add(ep); + frame.setSize(300, 300); + return frame; + } + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(bug4102068::createUI) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/javax/swing/text/html/HTMLEditorKit/bug4198022.java b/test/jdk/javax/swing/text/html/HTMLEditorKit/bug4198022.java new file mode 100644 index 0000000000000..2d26ace21318c --- /dev/null +++ b/test/jdk/javax/swing/text/html/HTMLEditorKit/bug4198022.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 4198022 + * @summary Tests if HTML tags , and are supported in JEditorPane + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4198022 + */ + +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.text.html.HTMLEditorKit; + +public class bug4198022 { + + static final String INSTRUCTIONS = """ + There are two "lines" of text in the displayed HTML window + The first line/string contains and HTML elements. + The word "subscript" should be subscripted (placed lower than the word "Testing"), + and the word "superscript" should be superscripted (placed higher than "Testing"). + If instead these words are placed on the same level then the test FAILS. + + The second line/string contains a sentence marked with tag. + It should be presented as one long line without breaks. + It is OK for the line to extend past the end of the window and not be all visible. + If the line is broken up so you see multiple lines the test FAILS. + + If all behaves as expected, the test PASSES. + """; + + static JFrame createUI() { + + JFrame frame = new JFrame("bug4198022"); + JEditorPane ep = new JEditorPane(); + HTMLEditorKit ek = new HTMLEditorKit(); + ep.setEditorKit(ek); + ep.setText( + "Testing subscript and superscript.
    " + + "
    This text is intended to be presented as a single line without " + + "any word wrapping, regardless of whether it fits the viewable area of " + + "the editor pane or not."); + + ep.setEditable(false); + + frame.add(ep); + frame.setSize(500, 300); + return frame; + } + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(60) + .testUI(bug4198022::createUI) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/javax/swing/text/html/HTMLEditorKit/bug4214848.java b/test/jdk/javax/swing/text/html/HTMLEditorKit/bug4214848.java new file mode 100644 index 0000000000000..9a5353598660c --- /dev/null +++ b/test/jdk/javax/swing/text/html/HTMLEditorKit/bug4214848.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.StringReader; +import java.io.StringWriter; +import javax.swing.text.Document; +import javax.swing.text.html.HTMLEditorKit; + +/* + * @test + * @bug 4214848 + * @summary Tests whether HTMLEditorKit.read(...) + * creates Document for html with empty BODY + */ + +public class bug4214848 { + public static void main (String[] args) throws Exception { + StringWriter sw = new StringWriter(); + String test = ""; + HTMLEditorKit kit = new HTMLEditorKit(); + Document doc = kit.createDefaultDocument(); + kit.read(new StringReader(test), doc, 0); // prepare test document + kit.write(sw, doc, 0, 10); + String out = sw.toString().toLowerCase(); + if (out.indexOf("") != out.lastIndexOf("")) { + throw new RuntimeException("Test failed: extra section generated"); + } + } +} diff --git a/test/jdk/javax/swing/text/html/HTMLEditorKit/bug4230197.java b/test/jdk/javax/swing/text/html/HTMLEditorKit/bug4230197.java new file mode 100644 index 0000000000000..59d4952c3230d --- /dev/null +++ b/test/jdk/javax/swing/text/html/HTMLEditorKit/bug4230197.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.StringWriter; +import javax.swing.text.html.HTML; +import javax.swing.text.html.HTMLDocument; +import javax.swing.text.html.HTMLEditorKit; + +/* + * @test + * @bug 4230197 + * @summary Tests if HTMLEditorKit.insertHTML() works for font/phrase tags + */ + +public class bug4230197 { + + public static void main(String[] args) throws Exception { + HTMLEditorKit kit = new HTMLEditorKit(); + StringWriter sw = new StringWriter(); + HTMLDocument doc = (HTMLDocument) kit.createDefaultDocument(); + kit.insertHTML(doc, doc.getLength(), "0", 0, 0, HTML.Tag.SUB); + kit.insertHTML(doc, doc.getLength(), "0", 0, 0, HTML.Tag.SUP); + kit.insertHTML(doc, doc.getLength(), "0", 0, 0, HTML.Tag.B); + kit.insertHTML(doc, doc.getLength(), "0", 0, 0, HTML.Tag.I); + kit.insertHTML(doc, doc.getLength(), "0", 0, 0, HTML.Tag.CODE); + kit.write(sw, doc, 0, doc.getLength()); + + String out = sw.toString().toLowerCase(); + if ((!out.contains("0")) + || (!out.contains("0")) + || (!out.contains("0")) + || (!out.contains("0")) + || (!out.contains("0"))) { + throw new RuntimeException("Test failed: HTMLEditorKit.insertHTML()" + + " doesn't work for font/phrase tags"); + } + } +} diff --git a/test/jdk/javax/swing/text/html/HTMLEditorKit/bug4238223.java b/test/jdk/javax/swing/text/html/HTMLEditorKit/bug4238223.java new file mode 100644 index 0000000000000..86479ea55709c --- /dev/null +++ b/test/jdk/javax/swing/text/html/HTMLEditorKit/bug4238223.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.Reader; +import java.io.StringReader; +import javax.swing.text.MutableAttributeSet; +import javax.swing.text.html.HTML; +import javax.swing.text.html.HTMLEditorKit; +import javax.swing.text.html.parser.ParserDelegator; + +/* + * @test + * @bug 4238223 + * @summary Tests that HTMLEditorKit.ParserCallback methods receive + * correct 'pos' argument. + */ + +public class bug4238223 { + + public static void main(String[] argv) throws Exception { + TestParser parser = new TestParser(); + String testHTML = "Text" + + "Simple text"; + parser.parse(testHTML); + } + + static class TestCallback extends HTMLEditorKit.ParserCallback { + String commentData = "comment"; + int commentIndex = 65; + + public void handleComment(char[] data, int pos) { + if (!(new String(data)).equals(commentData) + || pos != commentIndex) { + + throw new RuntimeException("handleComment failed"); + } + } + + HTML.Tag[] endTags = {HTML.Tag.TITLE, HTML.Tag.HEAD, + HTML.Tag.BODY, HTML.Tag.HTML}; + int[] endTagPositions = {23, 31, 79, 86}; + int endTagIndex = 0; + public void handleEndTag(HTML.Tag tag, int pos) { + if (!tag.equals(endTags[endTagIndex]) + || pos != endTagPositions[endTagIndex]) { + + throw new RuntimeException("handleEndTag failed"); + } else { + endTagIndex++; + } + } + + int errorIndex = 54; + public void handleError(String errorMsg, int pos) { + if (pos != errorIndex) { + throw new RuntimeException("handleError failed"); + } + } + + int[] simpleTagPositions = {44, 93}; + int simpleTagIndex = 0; + public void handleSimpleTag(HTML.Tag tag, MutableAttributeSet attr, + int pos) { + if (pos != simpleTagPositions[simpleTagIndex++]) { + throw new RuntimeException("handleSimpleTag failed"); + } + } + + HTML.Tag[] startTags = {HTML.Tag.HTML, HTML.Tag.HEAD, + HTML.Tag.TITLE, HTML.Tag.BODY}; + int[] startTagPositions = {0, 6, 12, 38}; + int startTagIndex = 0; + public void handleStartTag(HTML.Tag tag, MutableAttributeSet attr, + int pos) { + if (!tag.equals(startTags[startTagIndex]) + || pos != startTagPositions[startTagIndex]) { + + throw new RuntimeException("handleStartTag failed"); + } else { + startTagIndex++; + } + } + + String[] textData = {"Text", "Simple text"}; + int[] textPositions = {19, 54}; + int textIndex = 0; + public void handleText(char[] data, int pos) { + if (!textData[textIndex].equals(new String(data)) + || pos != textPositions[textIndex]) { + + throw new RuntimeException("handleText failed"); + } else { + textIndex++; + } + } + } + + static class TestParser extends ParserDelegator { + public void parse(String html) throws Exception { + Reader r = new StringReader(html); + super.parse(r, new TestCallback(), false); + r.close(); + } + } +} diff --git a/test/jdk/javax/swing/text/html/HTMLEditorKit/bug4245401.java b/test/jdk/javax/swing/text/html/HTMLEditorKit/bug4245401.java new file mode 100644 index 0000000000000..1f9bc7da914e7 --- /dev/null +++ b/test/jdk/javax/swing/text/html/HTMLEditorKit/bug4245401.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 4245401 + * @summary Tests that JTextPane with HTMLEditorKit handles the HEAD tag properly + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4245401 + */ + +import java.io.StringReader; +import javax.swing.JFrame; +import javax.swing.JTextPane; +import javax.swing.text.html.HTMLDocument; +import javax.swing.text.html.HTMLEditorKit; + +public class bug4245401 { + + static final String INSTRUCTIONS = """ + Place the cursor before the "H" in the word "HTML" + Then press and . + If an extra HEAD tag appear, the test FAILS, otherwise it PASSES. + """; + + static JFrame createUI() { + + JFrame frame = new JFrame("bug4245401"); + JTextPane ep = new JTextPane(); + ep.setEditable(true); + ep.setContentType("text/html"); + HTMLEditorKit kit = (HTMLEditorKit) ep.getEditorKit(); + ep.setEditorKit(kit); + HTMLDocument doc = (HTMLDocument) kit.createDefaultDocument(); + ep.setDocument(doc); + + try { + String text = "HTML Test... Test is a test..."; + kit.read(new StringReader(text), doc, 0); + } catch (Exception e) { + throw new RuntimeException(e); + } + + frame.add(ep); + frame.setSize(300, 300); + return frame; + } + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug4245401::createUI) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/javax/swing/text/html/StyleSheet/bug4619595.java b/test/jdk/javax/swing/text/html/StyleSheet/bug4619595.java new file mode 100644 index 0000000000000..2d99d867cddd3 --- /dev/null +++ b/test/jdk/javax/swing/text/html/StyleSheet/bug4619595.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 4619595 + * @summary Tests that embedded list items do not inherit the 'value' + * property from their parent list item + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4619595 + */ + +import javax.swing.JEditorPane; +import javax.swing.JFrame; + +public class bug4619595 { + + static final String INSTRUCTIONS = """ + The test window contains numbered lists. + Look at the three indented/embedded list items (the ones that are bold). + If they are not numbered 1, 2 and 3, then press FAIL. + + Below the lists there should also be a line saying: "The quick brown fox". + If you don't see this, press FAIL. + + If all is as expected, PASS the test. + """; + + final static String HTML = "" + + "

    1. Let's start
    2. Look @ inner list" + + "
      1. Inner list starts
      2. Second inner item" + + "
      3. Inner list ends
      " + + "
    3. That's all
    " + + "

    The quick brown fox

    " + + " "; + + static JFrame createUI() { + + JFrame frame = new JFrame("bug4619595"); + JEditorPane pane = new JEditorPane(); + pane.setContentType("text/html"); + pane.setText(HTML); + frame.add(pane); + frame.setSize(400, 400); + return frame; + } + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(bug4619595::createUI) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/javax/swing/text/html/TableView/7030332/bug7030332.html b/test/jdk/javax/swing/text/html/TableView/7030332/bug7030332.html deleted file mode 100644 index 22dc03d4fe09e..0000000000000 --- a/test/jdk/javax/swing/text/html/TableView/7030332/bug7030332.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - -Compare Golden Images with rendered JEditorPane. -They should looks simalar in each line. Pay attention to: - -1. Border width around tables -2. Border width around cells - - - diff --git a/test/jdk/javax/swing/text/html/TableView/7030332/bug7030332.java b/test/jdk/javax/swing/text/html/TableView/7030332/bug7030332.java index 7038edb24652e..db2a1a16f1ebe 100644 --- a/test/jdk/javax/swing/text/html/TableView/7030332/bug7030332.java +++ b/test/jdk/javax/swing/text/html/TableView/7030332/bug7030332.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,18 +21,49 @@ * questions. */ -/* @test - @bug 7030332 - @summary Default borders in tables looks incorrect JEditorPane - @author Pavel Porvatov - * @run applet/manual=yesno bug7030332.html -*/ +import java.awt.GridLayout; +import javax.swing.JComponent; +import javax.swing.JEditorPane; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextArea; +import javax.swing.ImageIcon; +import javax.swing.SwingConstants; -import javax.swing.*; -import java.awt.*; import java.net.URL; -public class bug7030332 extends JApplet { +/* @test + * @bug 7030332 + * @summary Default borders in tables looks incorrect + * when rendered using JEditorPane + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual/othervm -Dsun.java2d.uiScale=1 bug7030332 + */ + +public class bug7030332 { + public static void main(String[] args) throws Exception { + String testInstructions = """ + Compare Golden Images with rendered JEditorPane. + They should look similar in each line. + Pay attention to: + 1. Border width around tables + 2. Border width around cells + Note: The test was written before there was hidpi. + Hence we are considering the border width being + "similar enough" with 1.0 scaling. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(testInstructions) + .rows(9) + .columns(35) + .splitUI(bug7030332::createContentPane) + .build() + .awaitAndCheck(); + } + public static final String[] HTML_SAMPLES = new String[]{ "
    Column1Column2
    ", "
    Column1Column2
    ", @@ -40,35 +71,7 @@ public class bug7030332 extends JApplet { "
    Column1Column2
    ", }; - public static void main(String[] args) throws Exception { - SwingUtilities.invokeLater(new Runnable() { - public void run() { - JFrame frame = new JFrame(); - - frame.setContentPane(createContentPane()); - frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - frame.setSize(600, 400); - frame.setLocationRelativeTo(null); - - frame.setVisible(true); - - } - }); - } - - public void init() { - try { - SwingUtilities.invokeAndWait(new Runnable() { - public void run() { - setContentPane(createContentPane()); - } - }); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private static Container createContentPane() { + private static JComponent createContentPane() { JPanel result = new JPanel(new GridLayout(HTML_SAMPLES.length + 1, 3, 10, 10)); result.add(new JLabel("Html code")); @@ -77,22 +80,16 @@ private static Container createContentPane() { for (int i = 0; i < HTML_SAMPLES.length; i++) { String htmlSample = HTML_SAMPLES[i]; - JTextArea textArea = new JTextArea(htmlSample); - textArea.setLineWrap(true); - result.add(textArea); String imageName = "sample" + i + ".png"; URL resource = bug7030332.class.getResource(imageName); - result.add(resource == null ? new JLabel(imageName + " not found") : new JLabel(new ImageIcon(resource), SwingConstants.LEFT)); - result.add(new JEditorPane("text/html", htmlSample)); } - return result; } } diff --git a/test/jdk/javax/xml/crypto/dsig/GenerationTests.java b/test/jdk/javax/xml/crypto/dsig/GenerationTests.java index 45b7223433c0f..29e14165ece7c 100644 --- a/test/jdk/javax/xml/crypto/dsig/GenerationTests.java +++ b/test/jdk/javax/xml/crypto/dsig/GenerationTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,7 @@ /** * @test * @bug 4635230 6283345 6303830 6824440 6867348 7094155 8038184 8038349 8046949 - * 8046724 8079693 8177334 8205507 8210736 8217878 8241306 8305972 + * 8046724 8079693 8177334 8205507 8210736 8217878 8241306 8305972 8344137 * @summary Basic unit tests for generating XML Signatures with JSR 105 * @modules java.base/sun.security.util * java.base/sun.security.x509 @@ -99,6 +99,7 @@ public class GenerationTests { private static SignatureMethod dsaSha1, dsaSha256, rsaSha1, rsaSha224, rsaSha256, rsaSha384, rsaSha512, ecdsaSha1, ecdsaSha224, ecdsaSha256, ecdsaSha384, ecdsaSha512, + ecdsaSha3_224, ecdsaSha3_256, ecdsaSha3_384, ecdsaSha3_512, hmacSha1, hmacSha224, hmacSha256, hmacSha384, hmacSha512, rsaSha1mgf1, rsaSha224mgf1, rsaSha256mgf1, rsaSha384mgf1, rsaSha512mgf1, rsaSha3_224mgf1, rsaSha3_256mgf1, rsaSha3_384mgf1, rsaSha3_512mgf1, @@ -305,6 +306,10 @@ public static void main(String args[]) throws Exception { test_create_signature_enveloping_p256_sha256(); test_create_signature_enveloping_p256_sha384(); test_create_signature_enveloping_p256_sha512(); + test_create_signature_enveloping_p256_sha3_224(); + test_create_signature_enveloping_p256_sha3_256(); + test_create_signature_enveloping_p256_sha3_384(); + test_create_signature_enveloping_p256_sha3_512(); test_create_signature_enveloping_p384_sha1(); test_create_signature_enveloping_p521_sha1(); test_create_signature_enveloping_ed25519(); @@ -559,6 +564,10 @@ private static void setup() throws Exception { ecdsaSha256 = fac.newSignatureMethod(SignatureMethod.ECDSA_SHA256, null); ecdsaSha384 = fac.newSignatureMethod(SignatureMethod.ECDSA_SHA384, null); ecdsaSha512 = fac.newSignatureMethod(SignatureMethod.ECDSA_SHA512, null); + ecdsaSha3_224 = fac.newSignatureMethod("http://www.w3.org/2021/04/xmldsig-more#ecdsa-sha3-224", null); + ecdsaSha3_256 = fac.newSignatureMethod("http://www.w3.org/2021/04/xmldsig-more#ecdsa-sha3-256", null); + ecdsaSha3_384 = fac.newSignatureMethod("http://www.w3.org/2021/04/xmldsig-more#ecdsa-sha3-384", null); + ecdsaSha3_512 = fac.newSignatureMethod("http://www.w3.org/2021/04/xmldsig-more#ecdsa-sha3-512", null); ed25519 = fac.newSignatureMethod("http://www.w3.org/2021/04/xmldsig-more#eddsa-ed25519", null); ed448 = fac.newSignatureMethod("http://www.w3.org/2021/04/xmldsig-more#eddsa-ed448", null); @@ -892,6 +901,34 @@ static void test_create_signature_enveloping_p256_sha512() throws Exception { System.out.println(); } + static void test_create_signature_enveloping_p256_sha3_224() throws Exception { + System.out.println("* Generating signature-enveloping-p256-sha3_224.xml"); + test_create_signature_enveloping(sha1, ecdsaSha3_224, p256ki, + getECPrivateKey("P256"), kvks, false, true); + System.out.println(); + } + + static void test_create_signature_enveloping_p256_sha3_256() throws Exception { + System.out.println("* Generating signature-enveloping-p256-sha3_256.xml"); + test_create_signature_enveloping(sha1, ecdsaSha3_256, p256ki, + getECPrivateKey("P256"), kvks, false, true); + System.out.println(); + } + + static void test_create_signature_enveloping_p256_sha3_384() throws Exception { + System.out.println("* Generating signature-enveloping-p256-sha3_384.xml"); + test_create_signature_enveloping(sha1, ecdsaSha3_384, p256ki, + getECPrivateKey("P256"), kvks, false, true); + System.out.println(); + } + + static void test_create_signature_enveloping_p256_sha3_512() throws Exception { + System.out.println("* Generating signature-enveloping-p256-sha3_512.xml"); + test_create_signature_enveloping(sha1, ecdsaSha3_512, p256ki, + getECPrivateKey("P256"), kvks, false, true); + System.out.println(); + } + static void test_create_signature_enveloping_p384_sha1() throws Exception { System.out.println("* Generating signature-enveloping-p384-sha1.xml"); test_create_signature_enveloping(sha1, ecdsaSha1, p384ki, diff --git a/test/jdk/javax/xml/crypto/dsig/PSS.java b/test/jdk/javax/xml/crypto/dsig/PSS.java new file mode 100644 index 0000000000000..3b5730577fb98 --- /dev/null +++ b/test/jdk/javax/xml/crypto/dsig/PSS.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.test.lib.Asserts; +import jdk.test.lib.security.XMLUtils; + +import javax.xml.crypto.dsig.DigestMethod; +import javax.xml.crypto.dsig.SignatureMethod; +import javax.xml.crypto.dsig.spec.RSAPSSParameterSpec; +import java.security.KeyPairGenerator; +import java.security.spec.MGF1ParameterSpec; +import java.security.spec.PSSParameterSpec; + +/** + * @test + * @bug 8344137 + * @summary check RSASSA-PSS key + * @library /test/lib + * @modules java.xml.crypto + */ +public class PSS { + + public static void main(String[] args) throws Exception { + + var doc = XMLUtils.string2doc("TextRaw"); + var kpg = KeyPairGenerator.getInstance("RSASSA-PSS"); + kpg.initialize(2048); + var keyPair = kpg.generateKeyPair(); + + var pspec = new PSSParameterSpec("SHA-384", "MGF1", + MGF1ParameterSpec.SHA512, 48, + PSSParameterSpec.TRAILER_FIELD_BC); + + var signed = XMLUtils.signer(keyPair.getPrivate(), keyPair.getPublic()) + .dm(DigestMethod.SHA384) + .sm(SignatureMethod.RSA_PSS, new RSAPSSParameterSpec(pspec)) + .sign(doc); + + Asserts.assertTrue(XMLUtils.validator().validate(signed)); + } +} diff --git a/test/jdk/javax/xml/jaxp/datatype/8033980/GregorianCalAndDurSerDataTemplate.java b/test/jdk/javax/xml/jaxp/datatype/8033980/GregorianCalAndDurSerDataTemplate.java new file mode 100644 index 0000000000000..dfb12c4c0e960 --- /dev/null +++ b/test/jdk/javax/xml/jaxp/datatype/8033980/GregorianCalAndDurSerDataTemplate.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * This class provide the template for JDK version specific GregorianCalendarAndDurationSerData.java src file. + */ +public class GregorianCalAndDurSerDataTemplate { + public static final String ORACLE_COPY_RIGHT = """ + /* + * Copyright (c) %s, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + \s + /** + * Mechanically generated %s specific serialization bytes for XMLGregorianCalendar and Duration data type. + * Do not edit this file. + */"""; + public static final String GREGO_CAL_DUR_SER_CLASS = """ + public class %sGregorianCalendarAndDurationSerData extends GregorianCalendarAndDurationSerData { + %s + %s + @Override + public byte[] getGregorianCalendarByteArray() { + return gregorianCalendarBytes; + } + \s + @Override + public byte[] getDurationBytes() { + return durationBytes; + } + };"""; +} diff --git a/test/jdk/javax/xml/jaxp/datatype/8033980/GregorianCalAndDurSerDataUtil.java b/test/jdk/javax/xml/jaxp/datatype/8033980/GregorianCalAndDurSerDataUtil.java new file mode 100644 index 0000000000000..323ba4a802bf6 --- /dev/null +++ b/test/jdk/javax/xml/jaxp/datatype/8033980/GregorianCalAndDurSerDataUtil.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @summary utility to generate Gregorian Calendar and Duration serialized data java classes. + * @run junit/manual GregorianCalAndDurSerDataUtil + */ + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.LocalDate; +import java.util.Formatter; + +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeFactory; +import javax.xml.datatype.Duration; +import javax.xml.datatype.XMLGregorianCalendar; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +/** + * Utility to generate the java source file for Gregorian Calendar and Duration serialized data + * for specific version of JDK to be added in SerializationTest. Execute this test with desired version + * of JDK to generate the java source file. + */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class GregorianCalAndDurSerDataUtil { + static String JDK = "JDK" + System.getProperty("java.version"); + static String testsrc = System.getProperty("test.src"); + final static String EXPECTED_CAL = "0001-01-01T00:00:00.0000000-05:00"; + final static String EXPECTED_DURATION = "P1Y1M1DT1H1M1S"; + String srcFilePrefix = JDK.toUpperCase().replace("-", "_"); + + + /** + * Create the serialized Bytes array and serialized bytes base64 string for GregorianCalender and Duration + * with jdk under test and generate the java source file. + * @throws DatatypeConfigurationException Unexpected. + * @throws IOException Unexpected. + */ + @BeforeAll + public void setup() throws DatatypeConfigurationException, IOException { + DatatypeFactory dtf = DatatypeFactory.newInstance(); + XMLGregorianCalendar xmlGregorianCalendar = dtf.newXMLGregorianCalendar(EXPECTED_CAL); + Duration duration = dtf.newDuration(EXPECTED_DURATION); + String copyRightStr = GregorianCalAndDurSerDataTemplate.ORACLE_COPY_RIGHT; + String classStr = GregorianCalAndDurSerDataTemplate.GREGO_CAL_DUR_SER_CLASS; + try(ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); + ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); ObjectOutputStream oos2 = new ObjectOutputStream(baos2)) { + //Serialize the given xmlGregorianCalendar + oos.writeObject(xmlGregorianCalendar); + //Serialize the given xml Duration + oos2.writeObject(duration); + Files.deleteIfExists(Path.of(testsrc,srcFilePrefix+"GregorianCalendarAndDurationSerData.java")); + + copyRightStr = String.format(copyRightStr, LocalDate.now().getYear(), JDK); + classStr = String.format(classStr, srcFilePrefix, generatePseudoCodeForGregCalSerBytes(baos), + generatePseudoCodeForDurationSerBytes(baos2)); + String srcStr = copyRightStr + "\n" + classStr; + Files.writeString(Path.of(testsrc,srcFilePrefix+"GregorianCalendarAndDurationSerData.java"), srcStr); + } + } + + /** + * Verify that Java source file is created. + */ + @Test + void testFileCreated() { + assertTrue(Files.exists(Path.of(testsrc,srcFilePrefix+"GregorianCalendarAndDurationSerData.java"))); + } + + /** + * Generates the Java Pseudo code for serialized Gregorian Calendar byte array. + * @param baos Serialized GregorianCalendar ByteArrayOutputStream. + * @return pseudocode String for serialized Gregorian Calendar byte array. + */ + public static String generatePseudoCodeForGregCalSerBytes(ByteArrayOutputStream baos) { + byte [] bytes = baos.toByteArray(); + StringBuilder sb = new StringBuilder(bytes.length * 5); + sb.append("private final byte[] gregorianCalendarBytes = {"); + return generatePseudoCode(sb, bytes); + } + + /** + * Generates the Java Pseudo code for serialized Duration byte array. + * @param baos Serialized Duration ByteArrayOutputStream. + * @return pseudocode String for serialized Duration byte array. + */ + public static String generatePseudoCodeForDurationSerBytes(ByteArrayOutputStream baos) { + byte [] bytesdur = baos.toByteArray(); + StringBuilder sb = new StringBuilder(bytesdur.length * 5); + sb.append("private final byte[] durationBytes = {"); + return generatePseudoCode(sb, bytesdur); + } + + private static String generatePseudoCode(StringBuilder sb, byte [] bytes) { + final int linelen = 8; +// HexFormat hex = HexFormat.of().withPrefix(" (byte) 0x").withSuffix(","); +// for (int i = 0; i < bytes.length; i += linelen) { +// sb.append("\n"); +// sb.append(hex.formatHex(bytes, i, Math.min(i + linelen, bytes.length))); +// } +// sb.append("};"); + Formatter fmt = new Formatter(sb); + for (int i = 0; i */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class SerializationTest { - final String FILENAME_CAL = "_XMLGregorianCalendar.ser"; - final String FILENAME_DURATION = "_Duration.ser"; - String filePath; - - { - filePath = System.getProperty("test.src"); - if (filePath == null) { - //current directory - filePath = System.getProperty("user.dir"); - } - filePath += File.separator; - } final String EXPECTED_CAL = "0001-01-01T00:00:00.0000000-05:00"; final String EXPECTED_DURATION = "P1Y1M1DT1H1M1S"; - static String[] JDK = {"JDK6", "JDK7", "JDK8", "JDK9"}; - - public static void main(String[] args) { - SerializationTest test = new SerializationTest(); - - if (args[0].equalsIgnoreCase("read")) { - test.testReadCal(); - test.testReadDuration(); - test.report(); - } else { - int ver = Integer.valueOf(args[1]).intValue(); - test.createTestFile(JDK[ver - 6]); - } + static String[] JDK = {System.getProperty("java.version"), "JDK6", "JDK7", "JDK8", "JDK9"}; - } + // If needed to add serialized data of more JDK versions, serialized data source file can be generated using + // GregorianCalAndDurSerDataUtil class. + private GregorianCalendarAndDurationSerData[] gregorianCalendarAndDurationSerData = {null, new JDK6GregorianCalendarAndDurationSerData(), + new JDK7GregorianCalendarAndDurationSerData(), new JDK8GregorianCalendarAndDurationSerData(), new JDK9GregorianCalendarAndDurationSerData()}; - public void testReadCal() { - try { - for (String javaVersion : JDK) { - XMLGregorianCalendar d1 = (XMLGregorianCalendar) fromFile( - javaVersion + FILENAME_CAL); - if (!d1.toString().equalsIgnoreCase(EXPECTED_CAL)) { - fail("Java version: " + javaVersion - + "\nExpected: " + EXPECTED_CAL - + "\nActual: " + d1.toString()); - } else { - success("testReadCal: read " + javaVersion + " serialized file, passed."); + /** + * Create the serialized Bytes array and serialized bytes base64 string for GregorianCalender and Duration + * with jdk under test. + * @throws DatatypeConfigurationException Unexpected. + * @throws IOException Unexpected. + */ + @BeforeAll + public void setup() throws DatatypeConfigurationException, IOException { + DatatypeFactory dtf = DatatypeFactory.newInstance(); + XMLGregorianCalendar xmlGregorianCalendar = dtf.newXMLGregorianCalendar(EXPECTED_CAL); + Duration duration = dtf.newDuration(EXPECTED_DURATION); + try(ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); + ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); ObjectOutputStream oos2 = new ObjectOutputStream(baos2)) { + //Serialize the given xmlGregorianCalendar + oos.writeObject(xmlGregorianCalendar); + //Serialize the given xml Duration + oos2.writeObject(duration); + // Create the Data for JDK under test. + gregorianCalendarAndDurationSerData[0] = new GregorianCalendarAndDurationSerData() { + @Override + public byte[] getGregorianCalendarByteArray() { + return baos.toByteArray(); } - } - } catch (ClassNotFoundException ex) { - fail("testReadCal: " + ex.getMessage()); - } catch (IOException ex) { - fail("testReadCal: " + ex.getMessage()); - } - } - public void testReadDuration() { - try { - for (String javaVersion : JDK) { - Duration d1 = (Duration) fromFile( - javaVersion + FILENAME_DURATION); - if (!d1.toString().equalsIgnoreCase(EXPECTED_DURATION)) { - fail("Java version: " + javaVersion - + "\nExpected: " + EXPECTED_DURATION - + "\nActual: " + d1.toString()); - } else { - success("testReadDuration: read " + javaVersion + " serialized file, passed."); + @Override + public byte[] getDurationBytes() { + return baos2.toByteArray(); } - } - } catch (ClassNotFoundException ex) { - fail("testReadDuration: " + ex.getMessage()); - } catch (IOException ex) { - fail("testReadDuration: " + ex.getMessage()); + }; } } /** - * Create test files - * - * @param javaVersion JDK version + * Provide data for JDK version and Gregorian Calendar serialized bytes. + * @return A Stream of arguments where each element is an array of size three. First element contain JDK version, + * second element contain object reference to GregorianCalendarAndDurationSerData specific to JDK version + * and third element contain expected Gregorian Calendar as string. */ - public void createTestFile(String javaVersion) { - try { - DatatypeFactory dtf = DatatypeFactory.newInstance(); - XMLGregorianCalendar c = dtf.newXMLGregorianCalendar(EXPECTED_CAL); - Duration d = dtf.newDuration(EXPECTED_DURATION); - toFile((Serializable) c, filePath + javaVersion + FILENAME_CAL); - toFile((Serializable) d, filePath + javaVersion + FILENAME_DURATION); - } catch (Exception e) { - fail(e.getMessage()); - } + + public Stream gregorianCalendarDataBytes() { + return Stream.of( + Arguments.of(JDK[0], gregorianCalendarAndDurationSerData[0], EXPECTED_CAL), + Arguments.of(JDK[1], gregorianCalendarAndDurationSerData[1], EXPECTED_CAL), + Arguments.of(JDK[2], gregorianCalendarAndDurationSerData[2], EXPECTED_CAL), + Arguments.of(JDK[3], gregorianCalendarAndDurationSerData[3], EXPECTED_CAL), + Arguments.of(JDK[4], gregorianCalendarAndDurationSerData[4], EXPECTED_CAL) + ); } /** - * Read the object from a file. + * Provide data for JDK version and Duration serialized bytes. + * @return A Stream of arguments where each element is an array of size three. First element contain JDK version, + * second element contain object reference to GregorianCalendarAndDurationSerData specific to JDK version + * and third element contain expected Duration as string. */ - private static Object fromFile(String filePath) throws IOException, - ClassNotFoundException { - InputStream streamIn = SerializationTest.class.getResourceAsStream( - filePath); - ObjectInputStream objectinputstream = new ObjectInputStream(streamIn); - Object o = objectinputstream.readObject(); - return o; + + public Stream durationData() { + return Stream.of(Arguments.of(JDK[0], gregorianCalendarAndDurationSerData[0], EXPECTED_DURATION), + Arguments.of(JDK[1], gregorianCalendarAndDurationSerData[1], EXPECTED_DURATION), + Arguments.of(JDK[2], gregorianCalendarAndDurationSerData[2], EXPECTED_DURATION), + Arguments.of(JDK[3], gregorianCalendarAndDurationSerData[3], EXPECTED_DURATION), + Arguments.of(JDK[4], gregorianCalendarAndDurationSerData[4], EXPECTED_DURATION)); } /** - * Write the object to a file. + * Verify that GregorianCalendar serialized with different old JDK versions can be deserialized correctly with + * JDK under test. + * @param javaVersion JDK version used to GregorianCalendar serialization. + * @param gcsd JDK version specific GregorianCalendarAndDurationSerData. + * @param gregorianDate String representation of GregorianCalendar Date. + * @throws IOException Unexpected. + * @throws ClassNotFoundException Unexpected. */ - private static void toFile(Serializable o, String filePath) throws IOException { - FileOutputStream fout = new FileOutputStream(filePath, true); - ObjectOutputStream oos = new ObjectOutputStream(fout); - oos.writeObject(o); - oos.close(); - } - - static String errMessage; - int passed = 0, failed = 0; - - void fail(String errMsg) { - if (errMessage == null) { - errMessage = errMsg; - } else { - errMessage = errMessage + "\n" + errMsg; - } - failed++; - } - void success(String msg) { - passed++; - System.out.println(msg); + @ParameterizedTest + @MethodSource("gregorianCalendarDataBytes") + public void testReadCalBytes(String javaVersion, GregorianCalendarAndDurationSerData gcsd, String gregorianDate) throws IOException, + ClassNotFoundException { + final ByteArrayInputStream bais = new ByteArrayInputStream(gcsd.getGregorianCalendarByteArray()); + final ObjectInputStream ois = new ObjectInputStream(bais); + final XMLGregorianCalendar xgc = (XMLGregorianCalendar) ois.readObject(); + assertEquals(gregorianDate, xgc.toString()); } - public void report() { - - System.out.println("\nNumber of tests passed: " + passed); - System.out.println("Number of tests failed: " + failed + "\n"); + /** + * Verify that Duration serialized with different old JDK versions can be deserialized correctly with + * JDK under test. + * @param javaVersion JDK version used to GregorianCalendar serialization. + * @param gcsd JDK version specific GregorianCalendarAndDurationSerData. + * @param duration String representation of Duration. + * @throws IOException Unexpected. + * @throws ClassNotFoundException Unexpected. + */ - if (errMessage != null) { - throw new RuntimeException(errMessage); - } + @ParameterizedTest + @MethodSource("durationData") + public void testReadDurationBytes(String javaVersion, GregorianCalendarAndDurationSerData gcsd, String duration) throws IOException, + ClassNotFoundException { + final ByteArrayInputStream bais = new ByteArrayInputStream(gcsd.getDurationBytes()); + final ObjectInputStream ois = new ObjectInputStream(bais); + final Duration d1 = (Duration) ois.readObject(); + assertEquals(duration, d1.toString().toUpperCase()); } } diff --git a/test/jdk/javax/xml/jaxp/testng/parse/EntityCharacterEventOrder.java b/test/jdk/javax/xml/jaxp/testng/parse/EntityCharacterEventOrder.java index 5a5f57baa9b8b..313fa29a97434 100644 --- a/test/jdk/javax/xml/jaxp/testng/parse/EntityCharacterEventOrder.java +++ b/test/jdk/javax/xml/jaxp/testng/parse/EntityCharacterEventOrder.java @@ -16,7 +16,7 @@ /** * JDK-6770436: Entity callback order differs between Java1.5 and Java1.6 - * https://bugs.openjdk.java.net/browse/JDK-6770436 + * https://bugs.openjdk.org/browse/JDK-6770436 * */ diff --git a/test/jdk/javax/xml/jaxp/testng/parse/XMLEntityScannerLoad.java b/test/jdk/javax/xml/jaxp/testng/parse/XMLEntityScannerLoad.java index 36abd0c1458d8..308f17382fcf8 100644 --- a/test/jdk/javax/xml/jaxp/testng/parse/XMLEntityScannerLoad.java +++ b/test/jdk/javax/xml/jaxp/testng/parse/XMLEntityScannerLoad.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + package parse; import java.io.ByteArrayInputStream; @@ -13,11 +36,11 @@ /** * JDK-8059327: XML parser returns corrupt attribute value - * https://bugs.openjdk.java.net/browse/JDK-8059327 + * https://bugs.openjdk.org/browse/JDK-8059327 * * Also: * JDK-8061550: XMLEntityScanner can corrupt corrupt content during parsing - * https://bugs.openjdk.java.net/browse/JDK-8061550 + * https://bugs.openjdk.org/browse/JDK-8061550 * * @Summary: verify that the character cache in XMLEntityScanner is reset properly */ diff --git a/test/jdk/javax/xml/jaxp/testng/parse/jdk7156085/UTF8ReaderBug.java b/test/jdk/javax/xml/jaxp/testng/parse/jdk7156085/UTF8ReaderBug.java index 993fc61b87992..587d36e921f9b 100644 --- a/test/jdk/javax/xml/jaxp/testng/parse/jdk7156085/UTF8ReaderBug.java +++ b/test/jdk/javax/xml/jaxp/testng/parse/jdk7156085/UTF8ReaderBug.java @@ -33,7 +33,7 @@ /** * JDK-7156085: ArrayIndexOutOfBoundsException throws in UTF8Reader of SAXParser - * https://bugs.openjdk.java.net/browse/JDK-7156085 + * https://bugs.openjdk.org/browse/JDK-7156085 * * XERCESJ-1257: buffer overflow in UTF8Reader for characters out of BMP * https://issues.apache.org/jira/browse/XERCESJ-1257 diff --git a/test/jdk/jdk/incubator/vector/LoadJsvmlTest.java b/test/jdk/jdk/incubator/vector/LoadJsvmlTest.java index e6e613bbc90b7..f654b179e616d 100644 --- a/test/jdk/jdk/incubator/vector/LoadJsvmlTest.java +++ b/test/jdk/jdk/incubator/vector/LoadJsvmlTest.java @@ -29,6 +29,7 @@ * @requires vm.compiler2.enabled * @requires os.arch == "x86_64" | os.arch == "amd64" * @requires os.family == "linux" | os.family == "windows" + * @requires vm.flagless * @library /test/lib * @run main LoadJsvmlTest */ diff --git a/test/jdk/jdk/internal/platform/docker/GetFreeSwapSpaceSize.java b/test/jdk/jdk/internal/platform/docker/GetFreeSwapSpaceSize.java index 92b8cf282b784..b7721899ea3b2 100644 --- a/test/jdk/jdk/internal/platform/docker/GetFreeSwapSpaceSize.java +++ b/test/jdk/jdk/internal/platform/docker/GetFreeSwapSpaceSize.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020, 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2020, 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/jdk/jdk/internal/platform/docker/MetricsMemoryTester.java b/test/jdk/jdk/internal/platform/docker/MetricsMemoryTester.java index 6307a93fa5699..c27a1c7480fb4 100644 --- a/test/jdk/jdk/internal/platform/docker/MetricsMemoryTester.java +++ b/test/jdk/jdk/internal/platform/docker/MetricsMemoryTester.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -48,6 +48,8 @@ public static void main(String[] args) { case "softlimit": testMemorySoftLimit(args[1]); break; + default: + throw new RuntimeException("unknown args: " + args[0] + " for MetricsMemoryTester"); } } @@ -68,7 +70,7 @@ private static void testMemoryFailCount() { // We need swap to execute this test or will SEGV if (memAndSwapLimit <= memLimit) { - System.out.println("No swap memory limits, test case skipped"); + System.out.println("No swap memory limits. Ignoring test!"); } else { long count = Metrics.systemMetrics().getMemoryFailCount(); diff --git a/test/jdk/jdk/internal/platform/docker/TestDockerBasic.java b/test/jdk/jdk/internal/platform/docker/TestDockerBasic.java index 5518943a6e666..e236292de984a 100644 --- a/test/jdk/jdk/internal/platform/docker/TestDockerBasic.java +++ b/test/jdk/jdk/internal/platform/docker/TestDockerBasic.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2022, Red Hat, Inc. + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +27,7 @@ * @bug 8293540 * @summary Verify that -XshowSettings:system works * @key cgroups - * @requires docker.support + * @requires container.support * @library /test/lib * @run main/timeout=360 TestDockerBasic */ diff --git a/test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java b/test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java index f08e80c76f422..4d452f20eefe1 100644 --- a/test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java +++ b/test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,7 +34,7 @@ * @test * @key cgroups * @summary Test JDK Metrics class when running inside docker container - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.platform * @build MetricsCpuTester diff --git a/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java b/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java index 09cd60baabe2a..d877f38cab2c6 100644 --- a/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java +++ b/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,12 +27,13 @@ import jdk.test.lib.containers.docker.DockerRunOptions; import jdk.test.lib.containers.docker.DockerTestUtils; import jdk.test.lib.process.OutputAnalyzer; +import jtreg.SkippedException; /* * @test * @key cgroups * @summary Test JDK Metrics class when running inside docker container - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.platform * @build MetricsMemoryTester @@ -75,7 +76,7 @@ public static void main(String[] args) throws Exception { } testOomKillFlag("100m", true); - testMemoryFailCount("64m"); + testMemoryFailCount("128m"); testMemorySoftLimit("500m","200m"); @@ -111,18 +112,22 @@ private static void testMemoryFailCount(String value) throws Exception { // Check whether swapping really works for this test // On some systems there is no swap space enabled. And running - // 'java -Xms{mem-limit} -Xmx{mem-limit} -version' would fail due to swap space size being 0. + // 'java -Xms{mem-limit} -Xmx{mem-limit} -XX:+AlwaysPreTouch -version' + // would fail due to swap space size being 0. Note that when swap is + // properly enabled on the system the container gets the same amount + // of swap as is configured for memory. Thus, 2x{mem-limit} is the actual + // memory and swap bound for this pre-test. DockerRunOptions preOpts = new DockerRunOptions(imageName, "/jdk/bin/java", "-version"); preOpts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/") .addDockerOpts("--memory=" + value) + .addJavaOpts("-XX:+AlwaysPreTouch") .addJavaOpts("-Xms" + value) .addJavaOpts("-Xmx" + value); OutputAnalyzer oa = DockerTestUtils.dockerRunJava(preOpts); String output = oa.getOutput(); if (!output.contains("version")) { - System.out.println("Swapping doesn't work for this test."); - return; + throw new SkippedException("Swapping doesn't work for this test."); } DockerRunOptions opts = @@ -136,8 +141,7 @@ private static void testMemoryFailCount(String value) throws Exception { oa = DockerTestUtils.dockerRunJava(opts); output = oa.getOutput(); if (output.contains("Ignoring test")) { - System.out.println("Ignored by the tester"); - return; + throw new SkippedException("Ignored by the tester"); } oa.shouldHaveExitValue(0).shouldContain("TEST PASSED!!!"); } diff --git a/test/jdk/jdk/internal/platform/docker/TestGetFreeSwapSpaceSize.java b/test/jdk/jdk/internal/platform/docker/TestGetFreeSwapSpaceSize.java index 92f3364da10b0..acc82c0ab144d 100644 --- a/test/jdk/jdk/internal/platform/docker/TestGetFreeSwapSpaceSize.java +++ b/test/jdk/jdk/internal/platform/docker/TestGetFreeSwapSpaceSize.java @@ -1,5 +1,6 @@ /* - * Copyright (C) 2020, 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2020, 2022, Tencent. All rights reserved. + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +26,7 @@ * @test * @key cgroups * @bug 8242480 - * @requires docker.support + * @requires container.support * @library /test/lib * @build GetFreeSwapSpaceSize * @run driver TestGetFreeSwapSpaceSize diff --git a/test/jdk/jdk/internal/platform/docker/TestLimitsUpdating.java b/test/jdk/jdk/internal/platform/docker/TestLimitsUpdating.java index 22e03293c486e..1544088f68858 100644 --- a/test/jdk/jdk/internal/platform/docker/TestLimitsUpdating.java +++ b/test/jdk/jdk/internal/platform/docker/TestLimitsUpdating.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2023, Red Hat, Inc. + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -28,7 +29,7 @@ * @bug 8308090 * @key cgroups * @summary Test container limits updating as they get updated at runtime without restart - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.platform * @build LimitUpdateChecker diff --git a/test/jdk/jdk/internal/platform/docker/TestPidsLimit.java b/test/jdk/jdk/internal/platform/docker/TestPidsLimit.java index 6c6ff76fa9998..9fedeb55234ca 100644 --- a/test/jdk/jdk/internal/platform/docker/TestPidsLimit.java +++ b/test/jdk/jdk/internal/platform/docker/TestPidsLimit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2021 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -27,7 +27,7 @@ * @key cgroups * @summary Test JDK Metrics class when running inside a docker container with limited pids * @bug 8266490 - * @requires docker.support + * @requires container.support * @library /test/lib * @build TestPidsLimit * @run driver TestPidsLimit diff --git a/test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java b/test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java index 854d24b2279f8..93efff64cc428 100644 --- a/test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java +++ b/test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,7 @@ * @test * @key cgroups * @summary Test JDK Metrics class when running inside docker container - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.platform * @run main TestSystemMetrics diff --git a/test/jdk/jdk/internal/platform/docker/TestUseContainerSupport.java b/test/jdk/jdk/internal/platform/docker/TestUseContainerSupport.java index 7a2f77b3ce41a..6a96514771ce4 100644 --- a/test/jdk/jdk/internal/platform/docker/TestUseContainerSupport.java +++ b/test/jdk/jdk/internal/platform/docker/TestUseContainerSupport.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Red Hat, Inc. + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +25,7 @@ /* * @test * @summary UseContainerSupport flag should reflect Metrics being available - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.platform * @build CheckUseContainerSupport diff --git a/test/jdk/jdk/internal/ref/Cleaner/ExitOnThrow.java b/test/jdk/jdk/internal/ref/Cleaner/ExitOnThrow.java index 02346414cd3f4..7651c92bf77b2 100644 --- a/test/jdk/jdk/internal/ref/Cleaner/ExitOnThrow.java +++ b/test/jdk/jdk/internal/ref/Cleaner/ExitOnThrow.java @@ -44,9 +44,9 @@ public class ExitOnThrow { public static void main(String[] args) throws Exception { if (args.length == 0) { - ProcessTools.executeTestJvm("--add-exports", "java.base/jdk.internal.ref=ALL-UNNAMED", - "ExitOnThrow", - "-executeCleaner") + ProcessTools.executeTestJava("--add-exports", "java.base/jdk.internal.ref=ALL-UNNAMED", + "ExitOnThrow", + "-executeCleaner") .outputTo(System.out) .errorTo(System.out) .shouldHaveExitValue(1) diff --git a/test/jdk/jdk/jfr/api/consumer/security/TestStreamingRemote.java b/test/jdk/jdk/jfr/api/consumer/security/TestStreamingRemote.java index 6b97613f4ab36..f5a32e0b45a6d 100644 --- a/test/jdk/jdk/jfr/api/consumer/security/TestStreamingRemote.java +++ b/test/jdk/jdk/jfr/api/consumer/security/TestStreamingRemote.java @@ -81,7 +81,7 @@ public static void main(String... args) throws Exception { c[1] = "-Djava.security.policy=" + escapeBackslashes(policy.toString()); c[2] = Test.class.getName(); c[3] = repository; - OutputAnalyzer oa = ProcessTools.executeTestJvm(c); + OutputAnalyzer oa = ProcessTools.executeTestJava(c); oa.shouldContain(SUCCESS); } } diff --git a/test/jdk/jdk/jfr/api/metadata/annotations/TestPeriod.java b/test/jdk/jdk/jfr/api/metadata/annotations/TestPeriod.java index 893b1ab4720d9..a993675547074 100644 --- a/test/jdk/jdk/jfr/api/metadata/annotations/TestPeriod.java +++ b/test/jdk/jdk/jfr/api/metadata/annotations/TestPeriod.java @@ -26,6 +26,7 @@ import jdk.jfr.Event; import jdk.jfr.EventType; import jdk.jfr.Period; +import jdk.jfr.FlightRecorder; import jdk.test.lib.Asserts; import jdk.test.lib.jfr.Events; @@ -34,7 +35,7 @@ * @key jfr * @requires vm.hasJFR * @library /test/lib - * @run main/othervm jdk.jfr.api.metadata.annotations.TestLabel + * @run main/othervm jdk.jfr.api.metadata.annotations.TestPeriod */ public class TestPeriod { @@ -44,6 +45,7 @@ static class PeriodicEvent extends Event { public static void main(String[] args) throws Exception { EventType periodicEvent = EventType.getEventType(PeriodicEvent.class); + FlightRecorder.addPeriodicEvent(PeriodicEvent.class, () -> {}); String defaultValue = Events.getSetting(periodicEvent, Period.NAME).getDefaultValue(); Asserts.assertEQ(defaultValue, "47 s", "Incorrect default value for period"); } diff --git a/test/jdk/jdk/jfr/api/metadata/settingdescriptor/TestGetContentType.java b/test/jdk/jdk/jfr/api/metadata/settingdescriptor/TestGetContentType.java index 2af0bb3af11b8..e21b30b650ac7 100644 --- a/test/jdk/jdk/jfr/api/metadata/settingdescriptor/TestGetContentType.java +++ b/test/jdk/jdk/jfr/api/metadata/settingdescriptor/TestGetContentType.java @@ -38,7 +38,7 @@ * @key jfr * @requires vm.hasJFR * @library /test/lib /test/jdk - * @run main/othervm jdk.jfr.api.metadata.settingdescriptor.TestGetDescription + * @run main/othervm jdk.jfr.api.metadata.settingdescriptor.TestGetContentType */ public class TestGetContentType { @@ -49,7 +49,7 @@ public static void main(String[] args) throws Exception { Asserts.assertNull(plain.getContentType()); SettingDescriptor annotatedType = Events.getSetting(type, "annotatedType"); - Asserts.assertNull(annotatedType.getContentType(), Timestamp.class.getName()); + Asserts.assertEquals(annotatedType.getContentType(), Timestamp.class.getName()); SettingDescriptor newName = Events.getSetting(type, "newName"); Asserts.assertEquals(newName.getContentType(), Timespan.class.getName()); @@ -58,7 +58,7 @@ public static void main(String[] args) throws Exception { Asserts.assertNull(overridden.getContentType()); SettingDescriptor protectedBase = Events.getSetting(type, "protectedBase"); - Asserts.assertEquals(protectedBase.getContentType(), Frequency.class); + Asserts.assertEquals(protectedBase.getContentType(), Frequency.class.getName()); SettingDescriptor publicBase = Events.getSetting(type, "publicBase"); Asserts.assertEquals(publicBase.getContentType(), Timestamp.class.getName()); diff --git a/test/jdk/jdk/jfr/event/compiler/TestCodeCacheFull.java b/test/jdk/jdk/jfr/event/compiler/TestCodeCacheFull.java index 70cdb7c31da05..13a473100c864 100644 --- a/test/jdk/jdk/jfr/event/compiler/TestCodeCacheFull.java +++ b/test/jdk/jdk/jfr/event/compiler/TestCodeCacheFull.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,6 +36,7 @@ /** * @test TestCodeCacheFull * @requires vm.hasJFR + * @requires vm.opt.UseCodeCacheFlushing == null | vm.opt.UseCodeCacheFlushing == true * * @library /test/lib * @modules jdk.jfr diff --git a/test/jdk/jdk/jfr/event/compiler/TestCompilerCompile.java b/test/jdk/jdk/jfr/event/compiler/TestCompilerCompile.java index 775d7789bee23..8e23ee140e27b 100644 --- a/test/jdk/jdk/jfr/event/compiler/TestCompilerCompile.java +++ b/test/jdk/jdk/jfr/event/compiler/TestCompilerCompile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,7 +41,7 @@ * @test * @key jfr * @requires vm.hasJFR - * @requires vm.compMode!="Xint" + * @requires vm.compMode == "Xmixed" * @library /test/lib * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox diff --git a/test/jdk/jdk/jfr/event/compiler/TestCompilerInlining.java b/test/jdk/jdk/jfr/event/compiler/TestCompilerInlining.java index cea86e6e99701..ffe0bc16ac567 100644 --- a/test/jdk/jdk/jfr/event/compiler/TestCompilerInlining.java +++ b/test/jdk/jdk/jfr/event/compiler/TestCompilerInlining.java @@ -47,7 +47,7 @@ * @key jfr * @summary Verifies that corresponding JFR events are emitted in case of inlining. * @requires vm.hasJFR - * + * @requires vm.compMode == "Xmixed" * @requires vm.opt.Inline == true | vm.opt.Inline == null * @library /test/lib * @modules java.base/jdk.internal.org.objectweb.asm diff --git a/test/jdk/jdk/jfr/event/compiler/TestDeoptimization.java b/test/jdk/jdk/jfr/event/compiler/TestDeoptimization.java index 32c634fc59a90..47d0a09de9cf8 100644 --- a/test/jdk/jdk/jfr/event/compiler/TestDeoptimization.java +++ b/test/jdk/jdk/jfr/event/compiler/TestDeoptimization.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,7 +51,7 @@ public static void dummyMethod(boolean b) { * @key jfr * @summary sanity test for Deoptimization event, depends on Compilation event * @requires vm.hasJFR - * @requires vm.compMode != "Xint" + * @requires vm.compMode == "Xmixed" * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == 4 | vm.opt.TieredStopAtLevel == null) * @library /test/lib * @build jdk.test.whitebox.WhiteBox diff --git a/test/jdk/jdk/jfr/event/gc/collection/TestSystemGC.java b/test/jdk/jdk/jfr/event/gc/collection/TestSystemGC.java index 05b6aa45b8c38..baba7175860a6 100644 --- a/test/jdk/jdk/jfr/event/gc/collection/TestSystemGC.java +++ b/test/jdk/jdk/jfr/event/gc/collection/TestSystemGC.java @@ -68,12 +68,12 @@ public static void main(String[] args) throws Exception { RecordedEvent event2 = events.get(1); Events.assertFrame(event2, Runtime.class, "gc"); Events.assertEventThread(event2, Thread.currentThread()); - Events.assertField(event1, "invokedConcurrent").isEqual(concurrent); + Events.assertField(event2, "invokedConcurrent").isEqual(concurrent); RecordedEvent event3 = events.get(2); // MemoryMXBean.class is an interface so can't assertFrame on it Events.assertEventThread(event3, Thread.currentThread()); - Events.assertField(event1, "invokedConcurrent").isEqual(concurrent); + Events.assertField(event3, "invokedConcurrent").isEqual(concurrent); } } } diff --git a/test/jdk/jdk/jfr/event/io/TestInstrumentation.java b/test/jdk/jdk/jfr/event/io/TestInstrumentation.java index 5e08f45cb5550..ddbc120649762 100644 --- a/test/jdk/jdk/jfr/event/io/TestInstrumentation.java +++ b/test/jdk/jdk/jfr/event/io/TestInstrumentation.java @@ -283,7 +283,7 @@ private static void launchTest() throws Throwable { "-classpath", classpath, "-javaagent:" + testClassDir + "TestInstrumentation.jar", "jdk.jfr.event.io.TestInstrumentation$TestMain" }; - OutputAnalyzer output = ProcessTools.executeTestJvm(args); + OutputAnalyzer output = ProcessTools.executeTestJava(args); output.shouldHaveExitValue(0); } diff --git a/test/jdk/jdk/jfr/event/os/TestCPULoad.java b/test/jdk/jdk/jfr/event/os/TestCPULoad.java index efc9b39adf50e..1edb163969955 100644 --- a/test/jdk/jdk/jfr/event/os/TestCPULoad.java +++ b/test/jdk/jdk/jfr/event/os/TestCPULoad.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,6 @@ import jdk.test.lib.jfr.EventNames; import jdk.test.lib.jfr.Events; - /** * @test * @key jfr @@ -41,13 +40,32 @@ public class TestCPULoad { private final static String EVENT_NAME = EventNames.CPULoad; + public static boolean isPrime(int num) { + if (num <= 1) return false; + for (int i = 2; i <= Math.sqrt(num); i++) { + if (num % i == 0) return false; + } + return true; + } + + public static int burnCpuCycles(int limit) { + int primeCount = 0; + for (int i = 2; i < limit; i++) { + if (isPrime(i)) { + primeCount++; + } + } + return primeCount; + } + public static void main(String[] args) throws Throwable { Recording recording = new Recording(); recording.enable(EVENT_NAME); recording.start(); - // Need to sleep so a time delta can be calculated - Thread.sleep(100); + // burn some cycles to check increase of CPU related counters + int pn = burnCpuCycles(2500000); recording.stop(); + System.out.println("Found " + pn + " primes while burning cycles"); List events = Events.fromRecording(recording); if (events.isEmpty()) { diff --git a/test/jdk/jdk/jfr/startupargs/TestMemoryOptions.java b/test/jdk/jdk/jfr/startupargs/TestMemoryOptions.java index 9c90d04691ab1..00424760c942d 100644 --- a/test/jdk/jdk/jfr/startupargs/TestMemoryOptions.java +++ b/test/jdk/jdk/jfr/startupargs/TestMemoryOptions.java @@ -485,6 +485,7 @@ private static void launchTestVM(TestCase tc) throws Exception { if (flightRecorderOptions != null) { pb = ProcessTools.createTestJavaProcessBuilder("--add-exports=jdk.jfr/jdk.jfr.internal=ALL-UNNAMED", "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED", + "-Xmx256m", flightRecorderOptions, "-XX:StartFlightRecording", SUT.class.getName(), @@ -493,6 +494,7 @@ private static void launchTestVM(TestCase tc) throws Exception { // default, no FlightRecorderOptions passed pb = ProcessTools.createTestJavaProcessBuilder("--add-exports=jdk.jfr/jdk.jfr.internal=ALL-UNNAMED", "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED", + "-Xmx256m", "-XX:StartFlightRecording", SUT.class.getName(), tc.getTestName()); diff --git a/test/jdk/jdk/jfr/tool/TestPrintJSON.java b/test/jdk/jdk/jfr/tool/TestPrintJSON.java index bfa22aae8793a..6a8977a672e66 100644 --- a/test/jdk/jdk/jfr/tool/TestPrintJSON.java +++ b/test/jdk/jdk/jfr/tool/TestPrintJSON.java @@ -35,7 +35,8 @@ import jdk.jfr.consumer.RecordedEvent; import jdk.jfr.consumer.RecordedObject; import jdk.jfr.consumer.RecordingFile; -import jdk.jfr.tool.JSONValue.JSONArray; +import jdk.test.lib.json.JSONValue; +import jdk.test.lib.json.JSONValue.JSONArray; import jdk.test.lib.Asserts; import jdk.test.lib.process.OutputAnalyzer; diff --git a/test/jdk/jdk/modules/incubator/ServiceBinding.java b/test/jdk/jdk/modules/incubator/ServiceBinding.java index 3cb4a903e3e3c..c184acaddf458 100644 --- a/test/jdk/jdk/modules/incubator/ServiceBinding.java +++ b/test/jdk/jdk/modules/incubator/ServiceBinding.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,9 +24,11 @@ /** * @test * @bug 8233922 - * @modules java.base/jdk.internal.module + * @modules java.base/jdk.internal.org.objectweb.asm + * java.base/jdk.internal.org.objectweb.asm.commons + * java.base/jdk.internal.module * @library /test/lib - * @build ServiceBinding TestBootLayer + * @build ServiceBinding TestBootLayer jdk.test.lib.util.ModuleInfoWriter * @run testng ServiceBinding * @summary Test service binding with incubator modules */ @@ -43,17 +45,16 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; -import java.util.stream.Stream; import static java.lang.module.ModuleDescriptor.newModule; -import jdk.internal.module.ModuleInfoWriter; import jdk.internal.module.ModuleResolution; import org.testng.annotations.Test; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.util.ModuleInfoWriter; @Test public class ServiceBinding { diff --git a/test/jdk/sanity/client/SwingSet/src/EditorPaneDemoTest.java b/test/jdk/sanity/client/SwingSet/src/EditorPaneDemoTest.java index b769c6d826878..4cc16e53e71a2 100644 --- a/test/jdk/sanity/client/SwingSet/src/EditorPaneDemoTest.java +++ b/test/jdk/sanity/client/SwingSet/src/EditorPaneDemoTest.java @@ -52,7 +52,7 @@ /* * @test * @key headful screenshots - * @summary Verifies SwingSet3 EditorPaneDemo by navigating and and validating + * @summary Verifies SwingSet3 EditorPaneDemo by navigating and validating * the page contents in all pages * * @library /sanity/client/lib/jemmy/src diff --git a/test/jdk/sanity/client/SwingSet/src/TabbedPaneDemoTest.java b/test/jdk/sanity/client/SwingSet/src/TabbedPaneDemoTest.java index 6b7b270173f37..0e80df72a6346 100644 --- a/test/jdk/sanity/client/SwingSet/src/TabbedPaneDemoTest.java +++ b/test/jdk/sanity/client/SwingSet/src/TabbedPaneDemoTest.java @@ -39,7 +39,7 @@ * @test * @key headful * @summary Verifies SwingSet3 TabbedPaneDemo by iterating through tab placement - * positions, opening each tab and verifying the the tab gets selected. + * positions, opening each tab and verifying the tab gets selected. * * @library /sanity/client/lib/jemmy/src * @library /sanity/client/lib/Extensions/src diff --git a/test/jdk/sanity/client/lib/Extensions/src/org/jtregext/GuiTestListener.java b/test/jdk/sanity/client/lib/Extensions/src/org/jtregext/GuiTestListener.java index 407fd52d292c6..751551fc25b23 100644 --- a/test/jdk/sanity/client/lib/Extensions/src/org/jtregext/GuiTestListener.java +++ b/test/jdk/sanity/client/lib/Extensions/src/org/jtregext/GuiTestListener.java @@ -1,7 +1,7 @@ package org.jtregext; /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,7 @@ import org.testng.ITestListener; import org.testng.ITestResult; -// TODO: Remove this once https://bugs.openjdk.java.net/browse/JDK-8151671 is fixed +// TODO: Remove this once https://bugs.openjdk.org/browse/JDK-8151671 is fixed public class GuiTestListener implements ITestListener { private void afterTest() { diff --git a/test/jdk/sanity/client/lib/SwingSet2/README b/test/jdk/sanity/client/lib/SwingSet2/README index 2016753dd7d19..87d7ec5e58830 100644 --- a/test/jdk/sanity/client/lib/SwingSet2/README +++ b/test/jdk/sanity/client/lib/SwingSet2/README @@ -1,4 +1,4 @@ -The content of this src folder was originally taken from openjdk SwingSet2 demo: http://hg.openjdk.java.net/jdk/jdk/file/2d5d75263e77/src/demo/share/jfc/SwingSet2. +The content of this src folder was originally taken from openjdk SwingSet2 demo: https://git.openjdk.org/jdk/tree/92ec4c5/src/demo/share/jfc/SwingSet2. Then it was modified to increase testability and removed extra content and extra dependencies. This is NOT the official location of the SwingSet2 demo. \ No newline at end of file diff --git a/test/jdk/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tree/resources/TreeDemo.properties b/test/jdk/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tree/resources/TreeDemo.properties index 77c6f82fc1e62..3d584efb05af2 100644 --- a/test/jdk/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tree/resources/TreeDemo.properties +++ b/test/jdk/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tree/resources/TreeDemo.properties @@ -1,6 +1,6 @@ ### Tree Demo ### -TreeDemo.accessible_description=This demo shows shows a sample usage of a JTree component. +TreeDemo.accessible_description=This demo shows a sample usage of a JTree component. TreeDemo.tooltip=JTree demo TreeDemo.name=Tree Demo TreeDemo.music=Music diff --git a/test/jdk/sanity/client/lib/jemmy/README b/test/jdk/sanity/client/lib/jemmy/README index 629e529220075..98fdcf650f2a4 100644 --- a/test/jdk/sanity/client/lib/jemmy/README +++ b/test/jdk/sanity/client/lib/jemmy/README @@ -1,3 +1,3 @@ -This src folder contains a copy of Jemmy 2 library sources from http://hg.openjdk.java.net/code-tools/jemmy/v2 +This src folder contains a copy of Jemmy 2 library sources from https://git.openjdk.org/jemmy-v2 If a change to Jemmy library is needed, please first suggest it to that repository. diff --git a/test/jdk/sanity/client/lib/jemmy/src/org/netbeans/jemmy/WindowWaiter.java b/test/jdk/sanity/client/lib/jemmy/src/org/netbeans/jemmy/WindowWaiter.java index c7b27be0895c9..0e2c6866fd01e 100644 --- a/test/jdk/sanity/client/lib/jemmy/src/org/netbeans/jemmy/WindowWaiter.java +++ b/test/jdk/sanity/client/lib/jemmy/src/org/netbeans/jemmy/WindowWaiter.java @@ -286,7 +286,7 @@ public Window waitWindow(Window o, ComponentChooser ch) } /** - * Wait till the count of windows which meet the the search criteria becomes + * Wait till the count of windows which meet the search criteria becomes * equal to count. * * @param ch a component chooser used to define and apply the search @@ -300,7 +300,7 @@ public static void waitWindowCount(ComponentChooser ch, int count) } /** - * Wait till the count of windows which meet the the search criteria becomes + * Wait till the count of windows which meet the search criteria becomes * equal to count. * * @param owner The owner window of all the windows to be checked diff --git a/test/jdk/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/ComponentOperator.java b/test/jdk/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/ComponentOperator.java index 99a076b7b4ddd..4575c4dbe981a 100644 --- a/test/jdk/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/ComponentOperator.java +++ b/test/jdk/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/ComponentOperator.java @@ -252,7 +252,7 @@ public ComponentOperator(ContainerOperator cont, int index) { /** * Constructor. Waits for a component in a container to show. The component - * is is the first {@code java.awt.Component} that shows and that lies + * is the first {@code java.awt.Component} that shows and that lies * below the container in the display containment hierarchy. Uses cont's * timeout and output for waiting and to init operator. * diff --git a/test/jdk/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTreeOperator.java b/test/jdk/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTreeOperator.java index e97862832e279..29d7c238b7948 100644 --- a/test/jdk/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTreeOperator.java +++ b/test/jdk/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTreeOperator.java @@ -66,7 +66,7 @@ *

    Timeouts used:
    * JTreeOperator.WaitNodeExpandedTimeout - time to wait node expanded
    * JTreeOperator.WaitNodeCollapsedTimeout - time to wait node collapsed
    - * JTreeOperator.WaitAfterNodeExpandedTimeout - time to to sleep after node + * JTreeOperator.WaitAfterNodeExpandedTimeout - time to sleep after node * expanded
    * JTreeOperator.WaitNextNodeTimeout - time to wait next node displayed
    * JTreeOperator.WaitNodeVisibleTimeout - time to wait node visible
    diff --git a/test/jdk/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/Operator.java b/test/jdk/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/Operator.java index 840362b73bdf5..e29bf9b116b02 100644 --- a/test/jdk/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/Operator.java +++ b/test/jdk/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/Operator.java @@ -366,7 +366,7 @@ public Timeouts getTimeouts() { } /** - * Returns component visualizer. Visualizer is used from from + * Returns component visualizer. Visualizer is used from * makeComponentVisible() method. * * @return a visualizer assigned to this operator. @@ -378,7 +378,7 @@ public ComponentVisualizer getVisualizer() { } /** - * Changes component visualizer. Visualizer is used from from + * Changes component visualizer. Visualizer is used from * makeComponentVisible() method. * * @param vo a visualizer to assign to this operator. @@ -1080,7 +1080,7 @@ public String map() { } /** - * Interface used to make component visible & ready to to make operations + * Interface used to make component visible & ready to make operations * with. */ public interface ComponentVisualizer { diff --git a/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/CAInterop.java b/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/CAInterop.java index 3f16717153bda..bdc294aeb2292 100644 --- a/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/CAInterop.java +++ b/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/CAInterop.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -416,50 +416,6 @@ * @run main/othervm/manual -Djava.security.debug=certpath CAInterop certignarootca CRL */ -/* - * @test id=affirmtrustcommercialca - * @bug 8040012 - * @summary Interoperability tests with AffirmTrust Commercial CA - * @library /test/lib - * @build jtreg.SkippedException ValidatePathWithURL CAInterop - * @run main/othervm/manual -Djava.security.debug=certpath,ocsp CAInterop affirmtrustcommercialca OCSP - * @run main/othervm/manual -Djava.security.debug=certpath,ocsp -Dcom.sun.security.ocsp.useget=false CAInterop affirmtrustcommercialca OCSP - * @run main/othervm/manual -Djava.security.debug=certpath CAInterop affirmtrustcommercialca CRL - */ - -/* - * @test id=affirmtrustnetworkingca - * @bug 8040012 - * @summary Interoperability tests with AffirmTrust Networking CA - * @library /test/lib - * @build jtreg.SkippedException ValidatePathWithURL CAInterop - * @run main/othervm/manual -Djava.security.debug=certpath,ocsp CAInterop affirmtrustnetworkingca OCSP - * @run main/othervm/manual -Djava.security.debug=certpath,ocsp -Dcom.sun.security.ocsp.useget=false CAInterop affirmtrustnetworkingca OCSP - * @run main/othervm/manual -Djava.security.debug=certpath CAInterop affirmtrustnetworkingca CRL - */ - -/* - * @test id=affirmtrustpremiumca - * @bug 8040012 - * @summary Interoperability tests with AffirmTrust Premium CA - * @library /test/lib - * @build jtreg.SkippedException ValidatePathWithURL CAInterop - * @run main/othervm/manual -Djava.security.debug=certpath,ocsp CAInterop affirmtrustpremiumca OCSP - * @run main/othervm/manual -Djava.security.debug=certpath,ocsp -Dcom.sun.security.ocsp.useget=false CAInterop affirmtrustpremiumca OCSP - * @run main/othervm/manual -Djava.security.debug=certpath CAInterop affirmtrustpremiumca CRL - */ - -/* - * @test id=affirmtrustpremiumeccca - * @bug 8040012 - * @summary Interoperability tests with AffirmTrust Premium ECC CA - * @library /test/lib - * @build jtreg.SkippedException ValidatePathWithURL CAInterop - * @run main/othervm/manual -Djava.security.debug=certpath,ocsp CAInterop affirmtrustpremiumeccca OCSP - * @run main/othervm/manual -Djava.security.debug=certpath,ocsp -Dcom.sun.security.ocsp.useget=false CAInterop affirmtrustpremiumeccca OCSP - * @run main/othervm/manual -Djava.security.debug=certpath CAInterop affirmtrustpremiumeccca CRL - */ - /* * @test id=teliarootcav2 * @bug 8317373 @@ -557,6 +513,36 @@ * @run main/othervm/manual -Djava.security.debug=certpath CAInterop ssltlsrootrsa2022 CRL */ +/* + * @test id=sectigotlsrootr46 + * @bug 8359170 + * @summary Interoperability tests with Sectigo Public Server Authentication + * Root R46 + * @library /test/lib + * @build jtreg.SkippedException ValidatePathWithURL CAInterop + * @run main/othervm/manual -Djava.security.debug=certpath,ocsp CAInterop + * sectigotlsrootr46 OCSP + * @run main/othervm/manual -Djava.security.debug=certpath,ocsp + * -Dcom.sun.security.ocsp.useget=false CAInterop sectigotlsrootr46 OCSP + * @run main/othervm/manual -Djava.security.debug=certpath CAInterop + * sectigotlsrootr46 CRL + */ + +/* + * @test id=sectigotlsroote46 + * @bug 8359170 + * @summary Interoperability tests with Sectigo Public Server Authentication + * Root E46 + * @library /test/lib + * @build jtreg.SkippedException ValidatePathWithURL CAInterop + * @run main/othervm/manual -Djava.security.debug=certpath,ocsp CAInterop + * sectigotlsroote46 OCSP + * @run main/othervm/manual -Djava.security.debug=certpath,ocsp + * -Dcom.sun.security.ocsp.useget=false CAInterop sectigotlsroote46 OCSP + * @run main/othervm/manual -Djava.security.debug=certpath CAInterop + * sectigotlsroote46 CRL + */ + /** * Collection of certificate validation tests for interoperability with external CAs. * These tests are marked as manual as they depend on external infrastructure and may fail @@ -696,20 +682,6 @@ private CATestURLs getTestURLs(String alias) { new CATestURLs("https://valid.servicesca.dhimyotis.com", "https://revoked.servicesca.dhimyotis.com"); - // These are listed at https://www.affirmtrust.com/resources/ - case "affirmtrustcommercialca" -> - new CATestURLs("https://validcommercial.affirmtrust.com", - "https://revokedcommercial.affirmtrust.com"); - case "affirmtrustnetworkingca" -> - new CATestURLs("https://validnetworking.affirmtrust.com", - "https://revokednetworking.affirmtrust.com"); - case "affirmtrustpremiumca" -> - new CATestURLs("https://validpremium.affirmtrust.com", - "https://revokedpremium.affirmtrust.com"); - case "affirmtrustpremiumeccca" -> - new CATestURLs("https://validpremiumecc.affirmtrust.com", - "https://revokedpremiumecc.affirmtrust.com"); - case "teliarootcav2" -> new CATestURLs("https://juolukka.cover.telia.fi:10600", "https://juolukka.cover.telia.fi:10601"); @@ -742,6 +714,13 @@ private CATestURLs getTestURLs(String alias) { new CATestURLs("https://test-root-2022-rsa.ssl.com", "https://revoked-root-2022-rsa.ssl.com"); + case "sectigotlsrootr46" -> + new CATestURLs("https://sectigopublicserverauthenticationrootr46-ev.sectigo.com", + "https://sectigopublicserverauthenticationrootr46-ev.sectigo.com:444"); + case "sectigotlsroote46" -> + new CATestURLs("https://sectigopublicserverauthenticationroote46-ev.sectigo.com", + "https://sectigopublicserverauthenticationroote46-ev.sectigo.com:444"); + default -> throw new RuntimeException("No test setup found for: " + alias); }; } diff --git a/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/SectigoCSRootCAs.java b/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/SectigoCSRootCAs.java new file mode 100644 index 0000000000000..80cd1d41cbc6b --- /dev/null +++ b/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/SectigoCSRootCAs.java @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8359170 + * @summary Interoperability tests with Sectigo Public Code Signing Root CAs + * @build ValidatePathWithParams + * @run main/othervm/manual -Djava.security.debug=ocsp,certpath SectigoCSRootCAs OCSP + * @run main/othervm/manual -Djava.security.debug=certpath SectigoCSRootCAs CRL + */ + +public class SectigoCSRootCAs { + + public static void main(String[] args) throws Exception { + + ValidatePathWithParams pathValidator = new ValidatePathWithParams(null); + + if (args.length >= 1 && "CRL".equalsIgnoreCase(args[0])) { + pathValidator.enableCRLCheck(); + } else { + // OCSP check by default + pathValidator.enableOCSPCheck(); + } + + new SectigoCSRootCA_R46().runTest(pathValidator); + new SectigoCSRootCA_E46().runTest(pathValidator); + } +} + +class SectigoCSRootCA_R46 { + + // Owner: CN=Sectigo Public Code Signing CA R36, O=Sectigo Limited, C=GB + // Issuer: CN=Sectigo Public Code Signing Root R46, O=Sectigo Limited, C=GB + // Serial number: 621d6d0c52019e3b9079152089211c0a + // Valid from: Sun Mar 21 17:00:00 PDT 2021 until: Fri Mar 21 16:59:59 + // PDT 2036 + private static final String INT = "-----BEGIN CERTIFICATE-----\n" + + "MIIGGjCCBAKgAwIBAgIQYh1tDFIBnjuQeRUgiSEcCjANBgkqhkiG9w0BAQwFADBW\n" + + "MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMS0wKwYDVQQD\n" + + "EyRTZWN0aWdvIFB1YmxpYyBDb2RlIFNpZ25pbmcgUm9vdCBSNDYwHhcNMjEwMzIy\n" + + "MDAwMDAwWhcNMzYwMzIxMjM1OTU5WjBUMQswCQYDVQQGEwJHQjEYMBYGA1UEChMP\n" + + "U2VjdGlnbyBMaW1pdGVkMSswKQYDVQQDEyJTZWN0aWdvIFB1YmxpYyBDb2RlIFNp\n" + + "Z25pbmcgQ0EgUjM2MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAmyud\n" + + "U/o1P45gBkNqwM/1f/bIU1MYyM7TbH78WAeVF3llMwsRHgBGRmxDeEDIArCS2VCo\n" + + "Vk4Y/8j6stIkmYV5Gej4NgNjVQ4BYoDjGMwdjioXan1hlaGFt4Wk9vT0k2oWJMJj\n" + + "L9G//N523hAm4jF4UjrW2pvv9+hdPX8tbbAfI3v0VdJiJPFy/7XwiunD7mBxNtec\n" + + "M6ytIdUlh08T2z7mJEXZD9OWcJkZk5wDuf2q52PN43jc4T9OkoXZ0arWZVeffvMr\n" + + "/iiIROSCzKoDmWABDRzV/UiQ5vqsaeFaqQdzFf4ed8peNWh1OaZXnYvZQgWx/SXi\n" + + "JDRSAolRzZEZquE6cbcH747FHncs/Kzcn0Ccv2jrOW+LPmnOyB+tAfiWu01TPhCr\n" + + "9VrkxsHC5qFNxaThTG5j4/Kc+ODD2dX/fmBECELcvzUHf9shoFvrn35XGf2RPaNT\n" + + "O2uSZ6n9otv7jElspkfK9qEATHZcodp+R4q2OIypxR//YEb3fkDn3UayWW9bAgMB\n" + + "AAGjggFkMIIBYDAfBgNVHSMEGDAWgBQy65Ka/zWWSC8oQEJwIDaRXBeF5jAdBgNV\n" + + "HQ4EFgQUDyrLIIcouOxvSK4rVKYpqhekzQwwDgYDVR0PAQH/BAQDAgGGMBIGA1Ud\n" + + "EwEB/wQIMAYBAf8CAQAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwGwYDVR0gBBQwEjAG\n" + + "BgRVHSAAMAgGBmeBDAEEATBLBgNVHR8ERDBCMECgPqA8hjpodHRwOi8vY3JsLnNl\n" + + "Y3RpZ28uY29tL1NlY3RpZ29QdWJsaWNDb2RlU2lnbmluZ1Jvb3RSNDYuY3JsMHsG\n" + + "CCsGAQUFBwEBBG8wbTBGBggrBgEFBQcwAoY6aHR0cDovL2NydC5zZWN0aWdvLmNv\n" + + "bS9TZWN0aWdvUHVibGljQ29kZVNpZ25pbmdSb290UjQ2LnA3YzAjBggrBgEFBQcw\n" + + "AYYXaHR0cDovL29jc3Auc2VjdGlnby5jb20wDQYJKoZIhvcNAQEMBQADggIBAAb/\n" + + "guF3YzZue6EVIJsT/wT+mHVEYcNWlXHRkT+FoetAQLHI1uBy/YXKZDk8+Y1LoNqH\n" + + "rp22AKMGxQtgCivnDHFyAQ9GXTmlk7MjcgQbDCx6mn7yIawsppWkvfPkKaAQsiqa\n" + + "T9DnMWBHVNIabGqgQSGTrQWo43MOfsPynhbz2Hyxf5XWKZpRvr3dMapandPfYgoZ\n" + + "8iDL2OR3sYztgJrbG6VZ9DoTXFm1g0Rf97Aaen1l4c+w3DC+IkwFkvjFV3jS49ZS\n" + + "c4lShKK6BrPTJYs4NG1DGzmpToTnwoqZ8fAmi2XlZnuchC4NPSZaPATHvNIzt+z1\n" + + "PHo35D/f7j2pO1S8BCysQDHCbM5Mnomnq5aYcKCsdbh0czchOm8bkinLrYrKpii+\n" + + "Tk7pwL7TjRKLXkomm5D1Umds++pip8wH2cQpf93at3VDcOK4N7EwoIJB0kak6pSz\n" + + "Eu4I64U6gZs7tS/dGNSljf2OSSnRr7KWzq03zl8l75jy+hOds9TWSenLbjBQUGR9\n" + + "6cFr6lEUfAIEHVC1L68Y1GGxx4/eRI82ut83axHMViw1+sVpbPxg51Tbnio1lB93\n" + + "079WPFnYaOvfGAA0e0zcfF/M9gXr+korwQTh2Prqooq2bYNMvUoUKD85gnJ+t0sm\n" + + "rWrb8dee2CvYZXD5laGtaAxOfy/VKNmwuWuAh9kc\n" + + "-----END CERTIFICATE-----"; + + // Owner: CN=Sectigo Limited, O=Sectigo Limited, ST=West Yorkshire, C=GB + // Issuer: CN=Sectigo Public Code Signing CA R36, O=Sectigo Limited, C=GB + // Serial number: c1de046377578f1605414f3fa91bf5f6 + // Valid from: Wed Jun 04 17:00:00 PDT 2025 until: Fri Jun 05 16:59:59 + // PDT 2026 + private static final String VALID = "-----BEGIN CERTIFICATE-----\n" + + "MIIGRDCCBKygAwIBAgIRAMHeBGN3V48WBUFPP6kb9fYwDQYJKoZIhvcNAQEMBQAw\n" + + "VDELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDErMCkGA1UE\n" + + "AxMiU2VjdGlnbyBQdWJsaWMgQ29kZSBTaWduaW5nIENBIFIzNjAeFw0yNTA2MDUw\n" + + "MDAwMDBaFw0yNjA2MDUyMzU5NTlaMFoxCzAJBgNVBAYTAkdCMRcwFQYDVQQIDA5X\n" + + "ZXN0IFlvcmtzaGlyZTEYMBYGA1UECgwPU2VjdGlnbyBMaW1pdGVkMRgwFgYDVQQD\n" + + "DA9TZWN0aWdvIExpbWl0ZWQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC\n" + + "AQDCYjakgsoYkqVpENW0MuN5hBZDdIM60WJgBXU7zTAXORntSu/Grn/SQywwTg4o\n" + + "ltRcKuCp0Cd5zLAIjtgpVDDACWHgxKUtxerjjBZeGp0+viR+biLL0mVNPXgZZ5bQ\n" + + "AnDYVKJaGnPsXQD8l+Bn/R2c4cw7mXjBYp2KrTuqOBkPzk4LmdgpKXjxiw1yYb+n\n" + + "WKZ+3BMLIU6/k+LB9+WB6Odrl4Lff1jB4C6XhQELGjZAbpkFB2+Qr0ajIA3ZFXqU\n" + + "IMh0j5oD5juuXxryOvCgSBkEwxPHnlXxZBNd3DmrZ9NGClBIGE2f9FOjzo5Rl7lV\n" + + "KlzFdFmcH8LaLtWjniF+iT+YZw3Ld1O9VMK7RaHePsS4JYfbjeapoCEgudecmIz4\n" + + "5Q2tTjCdR5s/SxiVbynfEw+cAGeiv4sRXNSg0uhZ2eGMHh6mPley2pUoRMR8Qx1U\n" + + "0Uzg2NtPsHAo0DIH3jKEWU2zP5UPwCfqKYGaZLNLeGh07NZHBCp3TGp9kPVen5Ib\n" + + "tnJssu+pab7fixvbpDM4/r9MkKU6C1IsE6lffyON0PA6LaywwecYTJGpieXqoz03\n" + + "5UmQXvAzkb9omIAcQ6yWMZNrqwwG9XRKQEhvG3v7sbFkck1KZOz4r/KHkLx9sIxm\n" + + "vngdD/qLFebxPIvPT0GKnvSzuGcdQXVTdkZBBBrunv+XpQIDAQABo4IBiTCCAYUw\n" + + "HwYDVR0jBBgwFoAUDyrLIIcouOxvSK4rVKYpqhekzQwwHQYDVR0OBBYEFGMpbbJO\n" + + "xiuD6t+HEyA3hjp4devXMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBMG\n" + + "A1UdJQQMMAoGCCsGAQUFBwMDMEoGA1UdIARDMEEwNQYMKwYBBAGyMQECAQMCMCUw\n" + + "IwYIKwYBBQUHAgEWF2h0dHBzOi8vc2VjdGlnby5jb20vQ1BTMAgGBmeBDAEEATBJ\n" + + "BgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLnNlY3RpZ28uY29tL1NlY3RpZ29Q\n" + + "dWJsaWNDb2RlU2lnbmluZ0NBUjM2LmNybDB5BggrBgEFBQcBAQRtMGswRAYIKwYB\n" + + "BQUHMAKGOGh0dHA6Ly9jcnQuc2VjdGlnby5jb20vU2VjdGlnb1B1YmxpY0NvZGVT\n" + + "aWduaW5nQ0FSMzYuY3J0MCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5zZWN0aWdv\n" + + "LmNvbTANBgkqhkiG9w0BAQwFAAOCAYEAP2OCzJ+0hrg4XK3w+Ypoe0G5hKfzZ9RH\n" + + "nDcgY8BjgWYVOlN9ad2bU70RfgkZzylkaSt02KHOpkXmYpgfjZsovmyUchvlZ4fU\n" + + "RmivZleuO3G/ZvDFX373S40QFDd+lC1LYYUolRVz7/ZU2Vzql4FxsM1psRaW17xj\n" + + "jf9qaAvDlOH45eEEkfRUbIDdn1UYqDxdCN+90jtD1vRWkYINvE1T6mq3rHpEVoTS\n" + + "dIOgmcSL3MAKMB0LxWUPfoVdhnoUuoIxIAcV1SuR6zej4wHjClEaR8ReT/C23Jr3\n" + + "hQ4VDbfGu3gvlZG8/+lNmT+t4WaNPECxbFP0BgbD70FP594mSVH3fgptYiiRN7ez\n" + + "iUfOSBeCZpSMm7Z5P0KkxkagyFIR3vzgvqbJS/iaomvd9ZIkd9AwMEhugJpITeZj\n" + + "lKSXs+2q2UHQdLTPGVoOjmqyPhDGKAeVVF+jLIUWwtAaAoJm6ooXSp8sAeLA+e+K\n" + + "6RUFETVxhCefCjkbWq64OYLXriDb0l+W\n" + + "-----END CERTIFICATE-----"; + + // Owner: CN=Sectigo Limited, O=Sectigo Limited, ST=West Yorkshire, C=GB + // Issuer: CN=Sectigo Public Code Signing CA R36, O=Sectigo Limited, C=GB + // Serial number: 5ca6fb60da04db99dedbbc0c37131ec + // Valid from: Wed Jun 04 17:00:00 PDT 2025 until: Sun Jun 04 16:59:59 + // PDT 2028 + private static final String REVOKED = "-----BEGIN CERTIFICATE-----\n" + + "MIIGQzCCBKugAwIBAgIQBcpvtg2gTbmd7bvAw3Ex7DANBgkqhkiG9w0BAQwFADBU\n" + + "MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSswKQYDVQQD\n" + + "EyJTZWN0aWdvIFB1YmxpYyBDb2RlIFNpZ25pbmcgQ0EgUjM2MB4XDTI1MDYwNTAw\n" + + "MDAwMFoXDTI4MDYwNDIzNTk1OVowWjELMAkGA1UEBhMCR0IxFzAVBgNVBAgMDldl\n" + + "c3QgWW9ya3NoaXJlMRgwFgYDVQQKDA9TZWN0aWdvIExpbWl0ZWQxGDAWBgNVBAMM\n" + + "D1NlY3RpZ28gTGltaXRlZDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB\n" + + "ALDjoFCMN16zlrcv/saieSYT7FXEhTVDPLNzGAbNdBEX09FoL5EIkiN3biVIbDki\n" + + "bIocCFpo/yTrjARG/zz82AjdWHyomdtDjL35CQmgiUX7V8tu64xUfUAgadqm+0PL\n" + + "by1LRddKE7chpdJu+EDEmeYDPqcRGM+u8suPgosFf6XfVNFy/FZJiD1c7q6JNZ8i\n" + + "5NrvTs0zA9HckKE3uvPO9rw56EyF3SfUz9+zHKHwSElv8nCYpREudUf4yCzPNisK\n" + + "MVovzeCo36nzJFEdWTnDOr4mtvfCEGvJOU3mgzpECK7QF+yFifr90SG4lvrwzkig\n" + + "wYQymukXmB2gxN1tGOvgLig3Q/b4vljBiEeRPEba/L8YQnaXpR/BnPze8yb2t39l\n" + + "bzmnghkWkGA0PAB2vrzpi7pq12fGOD0+ErtAzAl/TAD/UFWwXDQLWX9LXRRKi5E+\n" + + "ScTlqLl9U1q9HsWYfM6CvLbc32TByaQ8yBytvsSRB0C0blp7CtP5MAc8j9xJdwAs\n" + + "Mj2bvSOfA+NJ0Kdg/tqdHHU6hex2HnGzDiEhovm6u/oAfDp/i2bBKLgARglMfGaC\n" + + "hFWeHLL6GAyBezMv+AQNCDCTYDMlqAihVMRUAfYgoHcVCfvTSETTTGdRUDFzIdCA\n" + + "wNwSVfykpadsev43I2IF+F3aNgJYuXnpxSCLPngemcgxAgMBAAGjggGJMIIBhTAf\n" + + "BgNVHSMEGDAWgBQPKssghyi47G9IritUpimqF6TNDDAdBgNVHQ4EFgQUlff/C/GC\n" + + "faJ+Y7ua3hKsCsrW9y4wDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwEwYD\n" + + "VR0lBAwwCgYIKwYBBQUHAwMwSgYDVR0gBEMwQTA1BgwrBgEEAbIxAQIBAwIwJTAj\n" + + "BggrBgEFBQcCARYXaHR0cHM6Ly9zZWN0aWdvLmNvbS9DUFMwCAYGZ4EMAQQBMEkG\n" + + "A1UdHwRCMEAwPqA8oDqGOGh0dHA6Ly9jcmwuc2VjdGlnby5jb20vU2VjdGlnb1B1\n" + + "YmxpY0NvZGVTaWduaW5nQ0FSMzYuY3JsMHkGCCsGAQUFBwEBBG0wazBEBggrBgEF\n" + + "BQcwAoY4aHR0cDovL2NydC5zZWN0aWdvLmNvbS9TZWN0aWdvUHVibGljQ29kZVNp\n" + + "Z25pbmdDQVIzNi5jcnQwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLnNlY3RpZ28u\n" + + "Y29tMA0GCSqGSIb3DQEBDAUAA4IBgQAfVq3mY7ggMcTJeWKKrGxs9RUiuAY0p4Xv\n" + + "CHNQViM/tHAn0t/nkPp+d2Ji3Zr6PefN+1F6zmsxsbZHse52JNHWYUwCb/Dx4Vw6\n" + + "3Wnc1zhXtZnvUTUfgrivuIsMjUG8yzTdEt/taMKEO0KqlKPsBPgFKveDVVaq9UZa\n" + + "FfxTWqgrnvkvP/Lag/YeKKj4cJG+a/MJZJm7kvyaBNKXVAamr/bumoxKDzpD67ds\n" + + "n9qwBi2Mv0rRXvZ2SHQXzsJ/zjNKWUhpPVrpypaER7EUxjNuSgC4L8AmfvHiO67v\n" + + "9EVIEud+beP3FtCXl/cSHhVeDxiC0KBXXBl9zLBaYvCH+8iABnZLStLgBDtfdkfk\n" + + "TZEAGbrNOXJDMnKRxr8y377Zq+KHwfiTnyizACHyMMTi+CCwg1ZFGcLOHa5shByc\n" + + "Ln9lYysM1/5vrEjt3ZUw11+pDqbPCGS++xgAwcftKfJ0TZrW/g6NZ9URg+11H9ad\n" + + "WalBv2VkhJAFJam9P2Y+pi9luk85sGo=\n" + + "-----END CERTIFICATE-----"; + + public void runTest(ValidatePathWithParams pathValidator) throws Exception { + // Validate valid + pathValidator.validate(new String[]{VALID, INT}, + ValidatePathWithParams.Status.GOOD, null, System.out); + + // Validate Revoked + pathValidator.validate(new String[]{REVOKED, INT}, + ValidatePathWithParams.Status.REVOKED, + "Thu Jun 05 10:27:45 PDT 2025", System.out); + } +} + +class SectigoCSRootCA_E46 { + + // Owner: CN=Sectigo Public Code Signing CA EV E36, O=Sectigo Limited, C=GB + // Issuer: CN=Sectigo Public Code Signing Root E46, O=Sectigo Limited, C=GB + // Serial number: 3774434f9eb40e221f9236ca1f2f2717 + // Valid from: Sun Mar 21 17:00:00 PDT 2021 until: Fri Mar 21 16:59:59 + // PDT 2036 + private static final String INT_VALID = "-----BEGIN CERTIFICATE-----\n" + + "MIIDMDCCAragAwIBAgIQN3RDT560DiIfkjbKHy8nFzAKBggqhkjOPQQDAzBWMQsw\n" + + "CQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMS0wKwYDVQQDEyRT\n" + + "ZWN0aWdvIFB1YmxpYyBDb2RlIFNpZ25pbmcgUm9vdCBFNDYwHhcNMjEwMzIyMDAw\n" + + "MDAwWhcNMzYwMzIxMjM1OTU5WjBXMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2Vj\n" + + "dGlnbyBMaW1pdGVkMS4wLAYDVQQDEyVTZWN0aWdvIFB1YmxpYyBDb2RlIFNpZ25p\n" + + "bmcgQ0EgRVYgRTM2MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE3mMV9nNViYoH\n" + + "4aSrPwFjpbbeXHw2pMbqezwDGb45uEZQr3qI9Hgt0k4R26o5upfXzJt03F8efu0r\n" + + "RNEs4yDDz6OCAWMwggFfMB8GA1UdIwQYMBaAFM99LKCQepgd3bZehcLg2hVx0uVe\n" + + "MB0GA1UdDgQWBBQadKQ417m2DrNb+txerj+28HM9iDAOBgNVHQ8BAf8EBAMCAYYw\n" + + "EgYDVR0TAQH/BAgwBgEB/wIBADATBgNVHSUEDDAKBggrBgEFBQcDAzAaBgNVHSAE\n" + + "EzARMAYGBFUdIAAwBwYFZ4EMAQMwSwYDVR0fBEQwQjBAoD6gPIY6aHR0cDovL2Ny\n" + + "bC5zZWN0aWdvLmNvbS9TZWN0aWdvUHVibGljQ29kZVNpZ25pbmdSb290RTQ2LmNy\n" + + "bDB7BggrBgEFBQcBAQRvMG0wRgYIKwYBBQUHMAKGOmh0dHA6Ly9jcnQuc2VjdGln\n" + + "by5jb20vU2VjdGlnb1B1YmxpY0NvZGVTaWduaW5nUm9vdEU0Ni5wN2MwIwYIKwYB\n" + + "BQUHMAGGF2h0dHA6Ly9vY3NwLnNlY3RpZ28uY29tMAoGCCqGSM49BAMDA2gAMGUC\n" + + "MQCger3L4CYx2W7HyHzvLaAnNee9QVqOwOrBYZyyqXERLtZg1DscsdoYZ2gszEW3\n" + + "zaUCMAaLtcwdoV35ADpru29wChS7kFgXt599Ex27wmL++uJCJth6xYr3nyF2b2YJ\n" + + "DAatOw==\n" + + "-----END CERTIFICATE-----"; + + // Owner: CN=Sectigo Public Code Signing CA E36, O=Sectigo Limited, C=GB + // Issuer: CN=Sectigo Public Code Signing Root E46, O=Sectigo Limited, C=GB + // Serial number: 3602617636e7034b9cc1fc5ffeac2d54 + // Valid from: Sun Mar 21 17:00:00 PDT 2021 until: Fri Mar 21 16:59:59 + // PDT 2036 + private static final String INT_REVOKED = "-----BEGIN CERTIFICATE-----\n" + + "MIIDLjCCArSgAwIBAgIQNgJhdjbnA0ucwfxf/qwtVDAKBggqhkjOPQQDAzBWMQsw\n" + + "CQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMS0wKwYDVQQDEyRT\n" + + "ZWN0aWdvIFB1YmxpYyBDb2RlIFNpZ25pbmcgUm9vdCBFNDYwHhcNMjEwMzIyMDAw\n" + + "MDAwWhcNMzYwMzIxMjM1OTU5WjBUMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2Vj\n" + + "dGlnbyBMaW1pdGVkMSswKQYDVQQDEyJTZWN0aWdvIFB1YmxpYyBDb2RlIFNpZ25p\n" + + "bmcgQ0EgRTM2MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAElDe1m6jawDhrwMxJ\n" + + "yFPhKYf8EGu+lBw3bF5CzmfaH1I7Zi+WAmkeEwS3tiNxzPh8GbBBLtdaRuqGuyWc\n" + + "W6ERmaOCAWQwggFgMB8GA1UdIwQYMBaAFM99LKCQepgd3bZehcLg2hVx0uVeMB0G\n" + + "A1UdDgQWBBQlDZtt2Bh3t4rDOFFW5cfytf+DajAOBgNVHQ8BAf8EBAMCAYYwEgYD\n" + + "VR0TAQH/BAgwBgEB/wIBADATBgNVHSUEDDAKBggrBgEFBQcDAzAbBgNVHSAEFDAS\n" + + "MAYGBFUdIAAwCAYGZ4EMAQQBMEsGA1UdHwREMEIwQKA+oDyGOmh0dHA6Ly9jcmwu\n" + + "c2VjdGlnby5jb20vU2VjdGlnb1B1YmxpY0NvZGVTaWduaW5nUm9vdEU0Ni5jcmww\n" + + "ewYIKwYBBQUHAQEEbzBtMEYGCCsGAQUFBzAChjpodHRwOi8vY3J0LnNlY3RpZ28u\n" + + "Y29tL1NlY3RpZ29QdWJsaWNDb2RlU2lnbmluZ1Jvb3RFNDYucDdjMCMGCCsGAQUF\n" + + "BzABhhdodHRwOi8vb2NzcC5zZWN0aWdvLmNvbTAKBggqhkjOPQQDAwNoADBlAjBM\n" + + "ykNTSVvegC1m17yIi87qgx6QIGbw1Mw2bQ4gtOWBVb/C8ALByC1YK7yQJNLJFTkC\n" + + "MQCNBv3fe1eLrGELS5KQD0cEFbXGlzQ5r1mnOHePMqlK5d+rmMxff58/t6bo3QEb\n" + + "8SQ=\n" + + "-----END CERTIFICATE-----"; + + // Owner: CN=Sectigo Limited, O=Sectigo Limited, ST=West Yorkshire, C=GB, + // OID.2.5.4.15=Private Organization, OID.1.3.6.1.4.1.311.60.2.1.3=GB, + // SERIALNUMBER=04058690 + // Issuer: CN=Sectigo Public Code Signing CA EV E36, O=Sectigo Limited, C=GB + // Serial number: fa2aa131f36b337717ac73f606a6ec49 + // Valid from: Tue Feb 13 16:00:00 PST 2024 until: Sat Feb 13 15:59:59 + // PST 2027 + private static final String VALID = "-----BEGIN CERTIFICATE-----\n" + + "MIIDrjCCA1SgAwIBAgIRAPoqoTHzazN3F6xz9gam7EkwCgYIKoZIzj0EAwIwVzEL\n" + + "MAkGA1UEBhMCR0IxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDEuMCwGA1UEAxMl\n" + + "U2VjdGlnbyBQdWJsaWMgQ29kZSBTaWduaW5nIENBIEVWIEUzNjAeFw0yNDAyMTQw\n" + + "MDAwMDBaFw0yNzAyMTMyMzU5NTlaMIGhMREwDwYDVQQFEwgwNDA1ODY5MDETMBEG\n" + + "CysGAQQBgjc8AgEDEwJHQjEdMBsGA1UEDxMUUHJpdmF0ZSBPcmdhbml6YXRpb24x\n" + + "CzAJBgNVBAYTAkdCMRcwFQYDVQQIDA5XZXN0IFlvcmtzaGlyZTEYMBYGA1UECgwP\n" + + "U2VjdGlnbyBMaW1pdGVkMRgwFgYDVQQDDA9TZWN0aWdvIExpbWl0ZWQwWTATBgcq\n" + + "hkjOPQIBBggqhkjOPQMBBwNCAASwXGEU01WkW/hWNYza08ZT7i0ZeZ9M1s93JYEB\n" + + "rZ/f1Ho1YzxtToqgIK2o+32afablPFYWlE6wGyuL/TYggBpKo4IBtDCCAbAwHwYD\n" + + "VR0jBBgwFoAUGnSkONe5tg6zW/rcXq4/tvBzPYgwHQYDVR0OBBYEFHEcsJgcYuDO\n" + + "dv1raL6h83a6j9C/MA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBMGA1Ud\n" + + "JQQMMAoGCCsGAQUFBwMDMEkGA1UdIARCMEAwNQYMKwYBBAGyMQECAQYBMCUwIwYI\n" + + "KwYBBQUHAgEWF2h0dHBzOi8vc2VjdGlnby5jb20vQ1BTMAcGBWeBDAEDMEsGA1Ud\n" + + "HwREMEIwQKA+oDyGOmh0dHA6Ly9jcmwuc2VjdGlnby5jb20vU2VjdGlnb1B1Ymxp\n" + + "Y0NvZGVTaWduaW5nQ0FFVkUzNi5jcmwwewYIKwYBBQUHAQEEbzBtMEYGCCsGAQUF\n" + + "BzAChjpodHRwOi8vY3J0LnNlY3RpZ28uY29tL1NlY3RpZ29QdWJsaWNDb2RlU2ln\n" + + "bmluZ0NBRVZFMzYuY3J0MCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5zZWN0aWdv\n" + + "LmNvbTAmBgNVHREEHzAdoBsGCCsGAQUFBwgDoA8wDQwLR0ItMDQwNTg2OTAwCgYI\n" + + "KoZIzj0EAwIDSAAwRQIgQVp7IIkEZNmC7GfmT1MSEhDebIjjzd75ZVEEbPP/4ocC\n" + + "IQDSyfPDKNMbKNOKrweDLwSE2GZV6nDWbiLz6ZmSZHob8w==\n" + + "-----END CERTIFICATE-----"; + + // Owner: CN=Sectigo Limited, O=Sectigo Limited, ST=West Yorkshire, C=GB + // Issuer: CN=Sectigo Public Code Signing CA E36, O=Sectigo Limited, C=GB + // Serial number: a1f601514271f24ca0a31c0d7b856705 + // Valid from: Wed Jun 04 17:00:00 PDT 2025 until: Sun Jun 04 16:59:59 + // PDT 2028 + private static final String REVOKED = "-----BEGIN CERTIFICATE-----\n" + + "MIIDOTCCAt6gAwIBAgIRAKH2AVFCcfJMoKMcDXuFZwUwCgYIKoZIzj0EAwIwVDEL\n" + + "MAkGA1UEBhMCR0IxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDErMCkGA1UEAxMi\n" + + "U2VjdGlnbyBQdWJsaWMgQ29kZSBTaWduaW5nIENBIEUzNjAeFw0yNTA2MDUwMDAw\n" + + "MDBaFw0yODA2MDQyMzU5NTlaMFoxCzAJBgNVBAYTAkdCMRcwFQYDVQQIDA5XZXN0\n" + + "IFlvcmtzaGlyZTEYMBYGA1UECgwPU2VjdGlnbyBMaW1pdGVkMRgwFgYDVQQDDA9T\n" + + "ZWN0aWdvIExpbWl0ZWQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASwXGEU01Wk\n" + + "W/hWNYza08ZT7i0ZeZ9M1s93JYEBrZ/f1Ho1YzxtToqgIK2o+32afablPFYWlE6w\n" + + "GyuL/TYggBpKo4IBiTCCAYUwHwYDVR0jBBgwFoAUJQ2bbdgYd7eKwzhRVuXH8rX/\n" + + "g2owHQYDVR0OBBYEFHEcsJgcYuDOdv1raL6h83a6j9C/MA4GA1UdDwEB/wQEAwIH\n" + + "gDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMEoGA1UdIARDMEEw\n" + + "NQYMKwYBBAGyMQECAQMCMCUwIwYIKwYBBQUHAgEWF2h0dHBzOi8vc2VjdGlnby5j\n" + + "b20vQ1BTMAgGBmeBDAEEATBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLnNl\n" + + "Y3RpZ28uY29tL1NlY3RpZ29QdWJsaWNDb2RlU2lnbmluZ0NBRTM2LmNybDB5Bggr\n" + + "BgEFBQcBAQRtMGswRAYIKwYBBQUHMAKGOGh0dHA6Ly9jcnQuc2VjdGlnby5jb20v\n" + + "U2VjdGlnb1B1YmxpY0NvZGVTaWduaW5nQ0FFMzYuY3J0MCMGCCsGAQUFBzABhhdo\n" + + "dHRwOi8vb2NzcC5zZWN0aWdvLmNvbTAKBggqhkjOPQQDAgNJADBGAiEAlEkiISLz\n" + + "PdJsFmVzJ2VZ8hnnVsOBXKbqISFQvIdguJoCIQCH4T0vwxn6uVkJpMvtxiMG/rYg\n" + + "jRFhfbxDcVee6likOw==\n" + + "-----END CERTIFICATE-----"; + + public void runTest(ValidatePathWithParams pathValidator) throws Exception { + // Validate valid + pathValidator.validate(new String[]{VALID, INT_VALID}, + ValidatePathWithParams.Status.GOOD, null, System.out); + + // Validate Revoked + pathValidator.validate(new String[]{REVOKED, INT_REVOKED}, + ValidatePathWithParams.Status.REVOKED, + "Thu Jun 05 10:27:19 PDT 2025", System.out); + } +} diff --git a/test/jdk/sun/awt/font/CacheFlushTest.java b/test/jdk/sun/awt/font/CacheFlushTest.java new file mode 100644 index 0000000000000..f3b3293127530 --- /dev/null +++ b/test/jdk/sun/awt/font/CacheFlushTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4286726 + * @summary Java2D raster printing: large text may overflow glyph cache. + * Draw a large glyphvector, the 'A' glyph should appear and not get flushed. +*/ + +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.geom.Point2D; +import java.awt.image.BufferedImage; +import java.util.HashMap; + +/** + * Draw a very large glyphvector on a surface. + * If the cache was flushed the first glyph is not rendered. + * Note: the implementation no longer uses glyphs for rendering large text, + * but in principle the test is still useful. + */ +public class CacheFlushTest { + + static final int WIDTH = 400, HEIGHT = 600; + static final int FONTSIZE = 250; + static final String TEST = "ABCDEFGHIJKLMNOP"; + static final HashMap HINTS = new HashMap<>(); + + static { + HINTS.put(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + HINTS.put(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + HINTS.put(RenderingHints.KEY_FRACTIONALMETRICS, + RenderingHints.VALUE_FRACTIONALMETRICS_ON); + } + + public static void main(String args[]) { + BufferedImage bi = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB); + + Graphics2D g2d = bi.createGraphics(); + g2d.addRenderingHints(HINTS); + g2d.setColor(Color.white); + g2d.fillRect(0, 0, WIDTH, HEIGHT); + g2d.setColor(Color.black); + + FontRenderContext frc = g2d.getFontRenderContext(); + Font font = new Font(Font.DIALOG, Font.PLAIN, 250); + GlyphVector gv = font.createGlyphVector(frc, TEST); + + /* Set the positions of all but the first glyph to be offset vertically but + * FONTSIZE pixels. So if the first glyph "A" is not flushed we can tell this + * by checking for non-white pixels in the range for the default y offset of 0 + * from the specified y location. + */ + Point2D.Float pt = new Point2D.Float(20f, FONTSIZE); + for (int i = 1; i < gv.getNumGlyphs(); ++i) { + gv.setGlyphPosition(i, pt); + pt.x += 25f; + pt.y = FONTSIZE; + } + g2d.drawGlyphVector(gv, 20, FONTSIZE); + /* Now expect to find at least one black pixel in the rect (0,0) -> (WIDTH, FONTSIZE) */ + boolean found = false; + int blackPixel = Color.black.getRGB(); + for (int y = 0; y < FONTSIZE; y++) { + for (int x = 0; x < WIDTH; x++) { + if (bi.getRGB(x, y) == blackPixel) { + found = true; + break; + } + } + if (found == true) { + break; + } + } + if (!found) { + throw new RuntimeException("NO BLACK PIXELS"); + } + } +} diff --git a/test/jdk/sun/awt/font/TestArabicHebrew.java b/test/jdk/sun/awt/font/TestArabicHebrew.java new file mode 100644 index 0000000000000..61cbe931c32c4 --- /dev/null +++ b/test/jdk/sun/awt/font/TestArabicHebrew.java @@ -0,0 +1,215 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4198081 + * @key headful + * @summary Arabic characters should appear instead of boxes and be correctly shaped. + * Hebrew characters should appear instead of boxes. + * Test is made headful so there's no excuse for test systems not having the fonts. + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GridLayout; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.geom.Rectangle2D; + +public class TestArabicHebrew extends Panel { + + static volatile Frame frame; + static volatile Font font = new Font(Font.DIALOG, Font.PLAIN, 36); + + static void createUI() { + frame = new Frame("Test Arabic/Hebrew"); + frame.setLayout(new BorderLayout()); + TestArabicHebrew panel = new TestArabicHebrew(); + frame.add(panel, BorderLayout.CENTER); + frame.pack(); + frame.setVisible(true); + } + + public static void main(String args[]) throws Exception { + EventQueue.invokeAndWait(TestArabicHebrew::createUI); + try { + checkStrings(); + } finally { + if (frame != null && args.length == 0) { + EventQueue.invokeAndWait(frame::dispose); + } + } + } + + static void checkString(String script, String str) { + int index = font.canDisplayUpTo(str); + if (index != -1) { + throw new RuntimeException("Cannot display char " + index + " for " + script); + } + } + + static void checkStrings() { + checkString("Arabic", arabic); + checkString("Hebrew", hebrew); + checkString("Latin-1 Supplement", latin1sup); + } + + // Table of arabic unicode characters - minimal support level + // Includes arabic chars from basic block up to 0652 and + // corresponding shaped characters from the arabic + // extended-B block from fe80 to fefc (does include lam-alef + // ligatures). + // Does not include arabic-indic digits nor "arabic extended" + // range. + + static final String arabic = + "\u060c\u061b\u061f\u0621\u0622\u0623\u0624\u0625\u0626\u0627" + + "\u0628\u0629\u062a\u062b\u062c\u062d\u062e\u062f\u0630\u0631" + + "\u0632\u0633\u0634\u0635\u0636\u0637\u0638\u0639\u063a\u0640" + + "\u0641\u0642\u0643\u0644\u0645\u0646\u0647\u0648\u0649\u064a" + + "\u064b\u064c\u064d\u064e\u064f\u0650\u0651\u0652\ufe80\ufe81" + + "\ufe82\ufe83\ufe84\ufe85\ufe86\ufe87\ufe88\ufe89\ufe8a\ufe8b" + + "\ufe8c\ufe8d\ufe8e\ufe8f\ufe90\ufe91\ufe92\ufe93\ufe94\ufe95" + + "\ufe96\ufe97\ufe98\ufe99\ufe9a\ufe9b\ufe9c\ufe9d\ufe9e\ufe9f" + + "\ufea0\ufea1\ufea2\ufea3\ufea4\ufea5\ufea6\ufea7\ufea8\ufea9" + + "\ufeaa\ufeab\ufeac\ufead\ufeae\ufeaf\ufeb0\ufeb1\ufeb2\ufeb3" + + "\ufeb4\ufeb5\ufeb6\ufeb7\ufeb8\ufeb9\ufeba\ufebb\ufebc\ufebd" + + "\ufebe\ufebf\ufec0\ufec1\ufec2\ufec3\ufec4\ufec5\ufec6\ufec7" + + "\ufec8\ufec9\ufeca\ufecb\ufecc\ufecd\ufece\ufecf\ufed0\ufed1" + + "\ufed2\ufed3\ufed4\ufed5\ufed6\ufed7\ufed8\ufed9\ufeda\ufedb" + + "\ufedc\ufedd\ufede\ufedf\ufee0\ufee1\ufee2\ufee3\ufee4\ufee5" + + "\ufee6\ufee7\ufee8\ufee9\ufeea\ufeeb\ufeec\ufeed\ufeee\ufeef" + + "\ufef0\ufef1\ufef2\ufef3\ufef4\ufef5\ufef6\ufef7\ufef8\ufef9" + + "\ufefa\ufefb\ufefc"; + + // hebrew table includes all characters in hebrew block + + static final String hebrew = + "\u0591\u0592\u0593\u0594\u0595\u0596\u0597\u0598\u0599\u059a" + + "\u059b\u059c\u059d\u059e\u059f\u05a0\u05a1\u05a3\u05a4\u05a5" + + "\u05a6\u05a7\u05a8\u05a9\u05aa\u05ab\u05ac\u05ad\u05ae\u05af" + + "\u05b0\u05b1\u05b2\u05b3\u05b4\u05b5\u05b6\u05b7\u05b8\u05b9" + + "\u05bb\u05bc\u05bd\u05be\u05bf\u05c0\u05c1\u05c2\u05c3\u05c4" + + "\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9" + + "\u05da\u05db\u05dc\u05dd\u05de\u05df\u05e0\u05e1\u05e2\u05e3" + + "\u05e4\u05e5\u05e6\u05e7\u05e8\u05e9\u05ea\u05f0\u05f1\u05f2" + + "\u05f3\u05f4"; + + // latin 1 supplement table includes all non-control characters + // in this range. Included because of comment in code that claims + // some problems displaying this range with some SJIS fonts. + + static final String latin1sup = + "\u00a0\u00a1\u00a2\u00a3\u00a4\u00a5\u00a6\u00a7" + + "\u00a8\u00a9\u00aa\u00ab\u00ac\u00ad\u00ae\u00af\u00b0\u00b1" + + "\u00b2\u00b3\u00b4\u00b5\u00b6\u00b7\u00b8\u00b9\u00ba\u00bb" + + "\u00bc\u00bd\u00be\u00bf\u00c0\u00c1\u00c2\u00c3\u00c4\u00c5" + + "\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf" + + "\u00d0\u00d1\u00d2\u00d3\u00d4\u00d5\u00d6\u00d7\u00d8\u00d9" + + "\u00da\u00db\u00dc\u00dd\u00de\u00df\u00e0\u00e1\u00e2\u00e3" + + "\u00e4\u00e5\u00e6\u00e7\u00e8\u00e9\u00ea\u00eb\u00ec\u00ed" + + "\u00ee\u00ef\u00f0\u00f1\u00f2\u00f3\u00f4\u00f5\u00f6\u00f7" + + "\u00f8\u00f9\u00fa\u00fb\u00fc\u00fd\u00fe\u00ff"; + + public TestArabicHebrew() { + setLayout(new GridLayout(3, 1)); + + FontRenderContext frc = new FontRenderContext(null, false, false); + add(new SubGlyphPanel("Arabic", arabic, font, frc)); + add(new SubGlyphPanel("Hebrew", hebrew, font, frc)); + add(new SubGlyphPanel("Latin-1 Supplement", latin1sup, font, frc)); + } + + static class SubGlyphPanel extends Panel { + String title; + Dimension extent; + GlyphVector[] vectors; + + static final int kGlyphsPerLine = 20; + + SubGlyphPanel(String title, String chars, Font font, FontRenderContext frc) { + + this.title = title; + setBackground(Color.white); + + double width = 0; + double height = 0; + + int max = chars.length(); + vectors = new GlyphVector[(max + kGlyphsPerLine - 1) / kGlyphsPerLine]; + for (int i = 0; i < vectors.length; i++) { + int start = i * 20; + int limit = Math.min(max, (i + 1) * kGlyphsPerLine); + String substr = ""; + for (int j = start; j < limit; ++j) { + substr = substr.concat(chars.charAt(j) + " "); + } + GlyphVector gv = font.createGlyphVector(frc, substr); + vectors[i] = gv; + Rectangle2D bounds = gv.getLogicalBounds(); + + width = Math.max(width, bounds.getWidth()); + height += bounds.getHeight(); + } + + extent = new Dimension((int)(width + 1), (int)(height + 1 + 30)); // room for title + + setSize(getPreferredSize()); + } + + public Dimension getPreferredSize() { + return new Dimension(extent); + } + + public Dimension getMinimumSize() { + return getPreferredSize(); + } + + public Dimension getMaximumSize() { + return getPreferredSize(); + } + + public void paint(Graphics g) { + Graphics2D g2d = (Graphics2D)g; + + g.drawString(title, 10, 20); + + float x = 10; + float y = 30; + for (int i = 0; i < vectors.length; ++i) { + GlyphVector gv = vectors[i]; + Rectangle2D bounds = gv.getLogicalBounds(); + g2d.drawGlyphVector(gv, x, (float)(y - bounds.getY())); + y += bounds.getHeight(); + } + } + } +} diff --git a/test/jdk/sun/awt/font/TestDevTransform.java b/test/jdk/sun/awt/font/TestDevTransform.java new file mode 100644 index 0000000000000..39d4255f15459 --- /dev/null +++ b/test/jdk/sun/awt/font/TestDevTransform.java @@ -0,0 +1,214 @@ +/* + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test id=dialog_double + * @bug 4269775 8341535 + * @summary Check that different text rendering APIs agree + * @run main/othervm TestDevTransform DIALOG DOUBLE + */ + +/* + * @test id=dialog_float + * @bug 4269775 8341535 + * @summary Check that different text rendering APIs agree + * @run main/othervm TestDevTransform DIALOG FLOAT + */ + +/* + * @test id=monospaced_double + * @bug 4269775 8341535 + * @summary Check that different text rendering APIs agree + * @run main/othervm TestDevTransform MONOSPACED DOUBLE + */ + +/* + * @test id=monospaced_float + * @bug 4269775 8341535 + * @summary Check that different text rendering APIs agree + * @run main/othervm TestDevTransform MONOSPACED FLOAT + */ + +/** + * Draw into an image rendering the same text string nine different + * ways: as a TextLayout, a simple String, and a GlyphVector, each + * with three different x scale factors. The expectation is that each + * set of three strings would appear the same although offset in y to + * avoid overlap. The bug was that the y positions of the individual characters + * of the TextLayout and GlyphVector were wrong, so the strings appeared + * to be rendered at different angles. + */ + +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import javax.imageio.ImageIO; +import java.io.File; +import java.util.HashMap; + +public class TestDevTransform { + + static HashMap hints = new HashMap<>(); + + static { + hints.put(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + hints.put(RenderingHints.KEY_FRACTIONALMETRICS, + RenderingHints.VALUE_FRACTIONALMETRICS_ON); + } + + static String test = "This is only a test"; + static double angle = Math.PI / 6.0; // Rotate 30 degrees + static final int W = 400, H = 400; + static boolean useDialog; + static boolean useDouble; + + static void draw(Graphics2D g2d, TextLayout layout, + float x, float y, float scalex) { + AffineTransform saveTransform = g2d.getTransform(); + g2d.translate(x, y); + g2d.rotate(angle); + g2d.scale(scalex, 1f); + layout.draw(g2d, 0f, 0f); + g2d.setTransform(saveTransform); + } + + static void draw(Graphics2D g2d, String string, + float x, float y, float scalex) { + AffineTransform saveTransform = g2d.getTransform(); + g2d.translate(x, y); + g2d.rotate(angle); + g2d.scale(scalex, 1f); + g2d.drawString(string, 0f, 0f); + g2d.setTransform(saveTransform); + } + + static void draw(Graphics2D g2d, GlyphVector gv, + float x, float y, float scalex) { + AffineTransform saveTransform = g2d.getTransform(); + g2d.translate(x, y); + g2d.rotate(angle); + g2d.scale(scalex, 1f); + g2d.drawGlyphVector(gv, 0f, 0f); + g2d.setTransform(saveTransform); + } + + static void init(Graphics2D g2d) { + g2d.setColor(Color.white); + g2d.fillRect(0, 0, W, H); + g2d.setColor(Color.black); + if (useDouble) { + g2d.scale(1.481, 1.481); // Convert to 108 dpi + } else { + g2d.scale(1.481f, 1.481f); // Convert to 108 dpi + } + g2d.addRenderingHints(hints); + String name; + if (useDialog) { + name = Font.DIALOG; + } else { + name = Font.MONOSPACED; + } + Font font = new Font(name, Font.PLAIN, 12); + g2d.setFont(font); + } + + static void compare(BufferedImage bi1, String name1, BufferedImage bi2, String name2) throws Exception { + int nonWhite1 = 0; + int nonWhite2 = 0; + int differences = 0; + int whitePixel = Color.white.getRGB(); + for (int x = 0; x < bi1.getWidth(); x++) { + for (int y = 0; y < bi1.getHeight(); y++) { + int pix1 = bi1.getRGB(x, y); + int pix2 = bi2.getRGB(x, y); + if (pix1 != whitePixel) { nonWhite1++; } + if (pix2 != whitePixel) { nonWhite2++; } + if (bi1.getRGB(x, y) != bi2.getRGB(x, y)) { + differences++; + } + } + } + int nonWhite = (nonWhite1 < nonWhite2) ? nonWhite1 : nonWhite2; + if (differences > 0 && ((nonWhite / differences) < 20)) { + ImageIO.write(bi1, "png", new File(name1 + ".png")); + ImageIO.write(bi2, "png", new File(name2 + ".png")); + System.err.println("nonWhite image 1 = " + nonWhite1); + System.err.println("nonWhite image 2 = " + nonWhite2); + System.err.println("Number of non-white differing pixels=" + differences); + throw new RuntimeException("Different rendering: " + differences + " pixels differ."); + } + } + + public static void main(String args[]) throws Exception { + if (args[0].equals("DIALOG")) { + useDialog = true; + } + if (args[1].equals("DOUBLE")) { + useDouble = true; + } + + BufferedImage tl_Image = new BufferedImage(W, H, BufferedImage.TYPE_INT_RGB); + { + Graphics2D tl_g2d = tl_Image.createGraphics(); + init(tl_g2d); + FontRenderContext frc = tl_g2d.getFontRenderContext(); + // Specify font from graphics to be sure it is the same as the other cases. + TextLayout tl = new TextLayout(test, tl_g2d.getFont(), frc); + draw(tl_g2d, tl, 10f, 12f, 3.0f); + draw(tl_g2d, tl, 10f, 24f, 1.0f); + draw(tl_g2d, tl, 10f, 36f, 0.33f); + } + + BufferedImage st_Image = new BufferedImage(400, 400, BufferedImage.TYPE_INT_RGB); + { + Graphics2D st_g2d = st_Image.createGraphics(); + init(st_g2d); + draw(st_g2d, test, 10f, 12f, 3.0f); + draw(st_g2d, test, 10f, 24f, 1.0f); + draw(st_g2d, test, 10f, 36f, .33f); + } + + BufferedImage gv_Image = new BufferedImage(400, 400, BufferedImage.TYPE_INT_RGB); + { + Graphics2D gv_g2d = gv_Image.createGraphics(); + init(gv_g2d); + FontRenderContext frc = gv_g2d.getFontRenderContext(); + GlyphVector gv = gv_g2d.getFont().createGlyphVector(frc, test); + draw(gv_g2d, gv, 10f, 12f, 3.0f); + draw(gv_g2d, gv, 10f, 24f, 1.0f); + draw(gv_g2d, gv, 10f, 36f, .33f); + } + + compare(tl_Image, "textlayout", st_Image, "string"); + compare(gv_Image, "glyphvector", st_Image, "string"); + } +} diff --git a/test/jdk/sun/awt/windows/TestPen.java b/test/jdk/sun/awt/windows/TestPen.java new file mode 100644 index 0000000000000..25b7304bdc862 --- /dev/null +++ b/test/jdk/sun/awt/windows/TestPen.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4277201 + * @summary verifies that invoking a fill on a brand new Graphics object + * does not stroke the shape in addition to filling it + * @key headful + */ + +/* + * This test case tests for a problem with initializing GDI graphics + * contexts (HDCs) where a pen is left installed in the graphics object + * even though the AWT believes that there is no Pen installed. The + * result is that when you try to fill a shape, GDI will both fill and + * stroke it. +*/ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.image.BufferedImage; + +public class TestPen extends Panel { + + static volatile TestPen pen; + static volatile Frame frame; + + public TestPen() { + setForeground(Color.black); + setBackground(Color.white); + } + + public Dimension getPreferredSize() { + return new Dimension(200, 200); + } + + public void paint(Graphics g) { + g.setColor(Color.green); + g.fillOval(50, 50, 100, 100); + } + + static void createUI() { + frame = new Frame(); + pen = new TestPen(); + frame.add(pen); + frame.pack(); + frame.setVisible(true); + } + + public static void main(String argv[]) throws Exception { + try { + EventQueue.invokeAndWait(TestPen::createUI); + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(2000); + Point p = pen.getLocationOnScreen(); + Dimension d = pen.getSize(); + Rectangle r = new Rectangle(p.x + 1, p.y + 1, d.width - 2, d.height - 2); + BufferedImage bi = robot.createScreenCapture(r); + int blackPixel = Color.black.getRGB(); + for (int y = 0; y < bi.getHeight(); y++ ) { + for (int x = 0; x < bi.getWidth(); x++ ) { + if (bi.getRGB(x, y) == blackPixel) { + throw new RuntimeException("Black pixel !"); + } + } + } + } finally { + if (frame != null) { + EventQueue.invokeAndWait(frame::dispose); + } + } + } +} diff --git a/test/jdk/sun/java2d/cmm/ColorConvertOp/CompatibleColorSpace.java b/test/jdk/sun/java2d/cmm/ColorConvertOp/CompatibleColorSpace.java new file mode 100644 index 0000000000000..edf5c92628c81 --- /dev/null +++ b/test/jdk/sun/java2d/cmm/ColorConvertOp/CompatibleColorSpace.java @@ -0,0 +1,61 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.color.ColorSpace; +import java.awt.image.BufferedImage; +import java.awt.image.ColorConvertOp; + +/** + * @test + * @bug 8312191 + * @summary Standard color spaces should be reused for CompatibleDestImage + */ +public final class CompatibleColorSpace { + + private static final int[] spaces = { + ColorSpace.CS_CIEXYZ, ColorSpace.CS_GRAY, ColorSpace.CS_LINEAR_RGB, + ColorSpace.CS_PYCC, ColorSpace.CS_sRGB + }; + + public static void main(String[] args) { + for (int from : spaces) { + for (int to : spaces) { + test(from, to); + } + } + } + + private static void test(int from, int to) { + ColorSpace srcCS = ColorSpace.getInstance(from); + ColorSpace dstCS = ColorSpace.getInstance(to); + ColorConvertOp op = new ColorConvertOp(srcCS, dstCS, null); + BufferedImage src = new BufferedImage(10, 10, + BufferedImage.TYPE_INT_ARGB); + BufferedImage dst = op.filter(src, null); + // dst image is not set and will be created automatically, the dstCS + // should be reused + if (dst.getColorModel().getColorSpace() != dstCS) { + throw new RuntimeException("Wrong color space"); + } + } +} diff --git a/test/jdk/sun/java2d/loops/ARGBBgToRGB.java b/test/jdk/sun/java2d/loops/ARGBBgToRGB.java new file mode 100644 index 0000000000000..55764af2c1354 --- /dev/null +++ b/test/jdk/sun/java2d/loops/ARGBBgToRGB.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4238978 + * @summary This test verifies that the correct blitting loop is being used. + * The correct output should have a yellow border on the top and + * left sides of a red box. The incorrect output would have only + * a red box -- no yellow border." + */ + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; + +public class ARGBBgToRGB { + + public static void main(String[] argv) { + BufferedImage bi = new BufferedImage(256, 256, BufferedImage.TYPE_INT_ARGB); + Graphics2D big = bi.createGraphics(); + big.setColor(Color.red); + big.fillRect(30, 30, 150, 150); + + BufferedImage bi2 = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB); + Graphics2D big2 = bi2.createGraphics(); + big2.drawImage(bi, 0, 0, Color.yellow, null); + + int expectYellowPix = bi2.getRGB(0, 0); + int expectRedPix = bi2.getRGB(50, 50); + if ((expectYellowPix != Color.yellow.getRGB()) || + (expectRedPix != Color.red.getRGB())) + { + throw new RuntimeException("Unexpected colors " + expectYellowPix + " " + expectRedPix); + } + } +} diff --git a/test/jdk/sun/java2d/loops/CopyAreaSpeed.html b/test/jdk/sun/java2d/loops/CopyAreaSpeed.html deleted file mode 100644 index a60ef8caa3cf3..0000000000000 --- a/test/jdk/sun/java2d/loops/CopyAreaSpeed.html +++ /dev/null @@ -1,39 +0,0 @@ - - - - - CopyAreaSpeed - - - -

    CopyAreaSpeed

    -
    -
    Thanh Nguyen
    - - -Last modified: Tue Jan 19 16:18:37 PST 1999 - - - - diff --git a/test/jdk/sun/java2d/loops/CopyAreaSpeed.java b/test/jdk/sun/java2d/loops/CopyAreaSpeed.java index 00d1cff5c386d..16fbbfe77ce4a 100644 --- a/test/jdk/sun/java2d/loops/CopyAreaSpeed.java +++ b/test/jdk/sun/java2d/loops/CopyAreaSpeed.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -20,6 +20,17 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + +import java.awt.Color; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Rectangle; +import javax.swing.JFrame; +import javax.swing.JOptionPane; + +import java.util.Date; + /* * @test * @bug 4189070 @@ -27,46 +38,54 @@ * copyArea calls to be completed. Because the performance measurement is * relative, this code only provides a benchmark to run with different releases * to compare the outcomes. - * @run applet/manual=done CopyAreaSpeed.html + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual CopyAreaSpeed */ -import java.applet.Applet; -import java.awt.*; -import java.awt.event.*; -import java.util.*; - -public class CopyAreaSpeed extends Applet implements Runnable { - int top = 0; +public class CopyAreaSpeed { + public static void main(String args[]) throws Exception { + String instructions = """ + This test prints out the time it takes for a certain amount + of copyArea calls to be completed. Because the performance + measurement is relative, this code only provides a benchmark + to run with different releases to compare the outcomes. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(instructions) + .rows(5) + .columns(35) + .testUI(CopyAreaSpeed::initialize) + .build() + .awaitAndCheck(); + } - public void init() { + public static JFrame initialize() { + JFrame frame = new JFrame("Copy Area Test"); + frame.add(new CopyAreaSpeedTest()); + frame.setSize(300, 320); + frame.setVisible(true); + return frame; } +} + +class CopyAreaSpeedTest extends Container implements Runnable { + int top = 0; + public static String result; - public CopyAreaSpeed() - { + public CopyAreaSpeedTest() { super(); - String[] instructions = - { - "This test prints out the time it takes for a certain amount ", - "of copyArea calls to be completed. Because the performance ", - "measurement is relative, this code only provides a benchmark ", - "to run with different releases to compare the outcomes." - }; - Sysout.createDialogWithInstructions( instructions ); (new Thread(this)).start(); - Button bt = new Button("Hello"); - bt.setBounds(50, 10, 50, 22); - bt.setVisible(false); - add(bt); } - public void update(Graphics g) - { + public void update(Graphics g) { paint(g); } - public void paint(Graphics g) - { - synchronized(this) { + public void paint(Graphics g) { + synchronized (this) { Rectangle rct = g.getClipBounds(); g.setColor(Color.white); g.fillRect(rct.x, rct.y, rct.width, rct.height); @@ -78,19 +97,18 @@ public void paint(Graphics g) if (y > rct.y) { int z = y / 20 + top; g.drawString("" + z, 10, y); - } /* endif */ - } // endfor + } + } } } static long millsec(Date s, Date e) { long ts = s.getTime(); long te = e.getTime(); - return te-ts; + return te - ts; } - public void run() - { + public void run() { int count = 1000; int loops = count; Date start; @@ -100,7 +118,7 @@ public void run() while (count-- > 0) { Dimension dm = getSize(); if (dm != null && dm.width != 0 && dm.height != 0) { - synchronized(this) { + synchronized (this) { top++; Graphics g = getGraphics(); g.copyArea(0, 20, dm.width, dm.height - 20, 0, -20); @@ -111,158 +129,15 @@ public void run() } try { Thread.sleep(1); - } catch(Exception ex) { + } catch (Exception ex) { ex.printStackTrace(); } } end = new Date(); - Sysout.println("copyArea X "+loops+" = "+ millsec(start, end) + " msec"); - } - - public static void main(String args[]) { - Frame frm = new Frame("CopyAreaSpeed"); - frm.add(new CopyAreaSpeed()); - frm.addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent ev) { - System.exit(0); - } - }); - frm.setSize(500, 500); - frm.show(); + Graphics g = getGraphics(); + g.setFont(getFont()); + g.setColor(Color.black); + result = "copyArea X " + loops + " = " + millsec(start, end) + " msec"; + JOptionPane.showMessageDialog(null, result); } } -/**************************************************** - Standard Test Machinery - DO NOT modify anything below -- it's a standard - chunk of code whose purpose is to make user - interaction uniform, and thereby make it simpler - to read and understand someone else's test. - ****************************************************/ - -/** - This is part of the standard test machinery. - It creates a dialog (with the instructions), and is the interface - for sending text messages to the user. - To print the instructions, send an array of strings to Sysout.createDialog - WithInstructions method. Put one line of instructions per array entry. - To display a message for the tester to see, simply call Sysout.println - with the string to be displayed. - This mimics System.out.println but works within the test harness as well - as standalone. - */ -class Sysout -{ - private static TestDialog dialog; - - public static void createDialogWithInstructions( String[] instructions ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - dialog.printInstructions( instructions ); - dialog.show(); - println( "Any messages for the tester will display here." ); - } - - public static void createDialog( ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - String[] defInstr = { "Instructions will appear here. ", "" } ; - dialog.printInstructions( defInstr ); - dialog.show(); - println( "Any messages for the tester will display here." ); - } - - - public static void printInstructions( String[] instructions ) - { - dialog.printInstructions( instructions ); - } - - - public static void println( String messageIn ) - { - dialog.displayMessage( messageIn ); - } - -}// Sysout class - -/** - This is part of the standard test machinery. It provides a place for the - test instructions to be displayed, and a place for interactive messages - to the user to be displayed. - To have the test instructions displayed, see Sysout. - To have a message to the user be displayed, see Sysout. - Do not call anything in this dialog directly. - */ -class TestDialog extends Dialog -{ - - TextArea instructionsText; - TextArea messageText; - int maxStringLength = 80; - - //DO NOT call this directly, go through Sysout - public TestDialog( Frame frame, String name ) - { - super( frame, name ); - int scrollBoth = TextArea.SCROLLBARS_BOTH; - instructionsText = new TextArea( "", 15, maxStringLength, scrollBoth ); - add( "North", instructionsText ); - - messageText = new TextArea( "", 5, maxStringLength, scrollBoth ); - add("South", messageText); - - pack(); - - show(); - }// TestDialog() - - //DO NOT call this directly, go through Sysout - public void printInstructions( String[] instructions ) - { - //Clear out any current instructions - instructionsText.setText( "" ); - - //Go down array of instruction strings - - String printStr, remainingStr; - for( int i=0; i < instructions.length; i++ ) - { - //chop up each into pieces maxSringLength long - remainingStr = instructions[ i ]; - while( remainingStr.length() > 0 ) - { - //if longer than max then chop off first max chars to print - if( remainingStr.length() >= maxStringLength ) - { - //Try to chop on a word boundary - int posOfSpace = remainingStr. - lastIndexOf( ' ', maxStringLength - 1 ); - - if( posOfSpace <= 0 ) { - posOfSpace = maxStringLength - 1; - } - - printStr = remainingStr.substring( 0, posOfSpace + 1 ); - remainingStr = remainingStr.substring( posOfSpace + 1 ); - } - else //else just print - { - printStr = remainingStr; - remainingStr = ""; - } - - instructionsText.append( printStr + "\n" ); - - }// while - - }// for - - }//printInstructions() - - //DO NOT call this directly, go through Sysout - public void displayMessage( String messageIn ) - { - messageText.append( messageIn + "\n" ); - } - -}// TestDialog class diff --git a/test/jdk/sun/java2d/loops/CopyNegative.java b/test/jdk/sun/java2d/loops/CopyNegative.java new file mode 100644 index 0000000000000..0b8296918ac63 --- /dev/null +++ b/test/jdk/sun/java2d/loops/CopyNegative.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4188744 + * @summary This test verifies that copyArea performs correctly for negative offset values. + * The correct output shows that the text area is moved to the left and down, + * leaving some garbage on the right and the top. + * The incorrect copy would show the text area garbled and no text is legible. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual CopyNegative + */ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.Panel; + +public class CopyNegative extends Panel { + + private static final String INSTRUCTIONS = """ + This test verifies that copyArea performs correctly for negative offset values. + The test draws text in an image, then copies the contents repeatedly. + The correct output shows that the text is moved to the left and down, + leaving some garbage on the top / right and some legible text at the bottom left. + The incorrect copy would show the whole text area garbled and no text is legible. + """; + + public static void main(String[] argv) throws Exception { + PassFailJFrame.builder() + .title("CopyNegativeTest") + .instructions(INSTRUCTIONS) + .testUI(CopyNegative::createUI) + .testTimeOut(5) + .rows(10) + .columns(50) + .build() + .awaitAndCheck(); + } + + Image img; + + static final int W = 200, H = 200; + + static Frame createUI() { + Frame f = new Frame("CopyNegative"); + f.add(new CopyNegative()); + f.pack(); + return f; + } + + public Dimension getPreferredSize() { + return new Dimension(W, H); + } + + private void doCopy() { + Graphics g = img.getGraphics(); + g.setColor(Color.white); + g.fillRect(0, 0, W, H); + g.setColor(Color.black); + String text = "Some Text To Display, it is long enough to fill the entire display line."; + StringBuffer sb = new StringBuffer(text); + + for (int i = 1; i < 50; i++) { + g.drawString(sb.toString(), 5,20 * i - 10); + sb.insert(0, Integer.toString(i)); + } + for (int i = 0 ; i < 20 ; i++ ) { + g.copyArea(0, 0, W, H, -3, 3); + } + } + + public void paint(Graphics g) { + img = createImage(W, H); + doCopy(); + g.drawImage(img, 0, 0, this); + } + +} diff --git a/test/jdk/sun/java2d/loops/DitheredSolidFill.java b/test/jdk/sun/java2d/loops/DitheredSolidFill.java new file mode 100644 index 0000000000000..509b0bbe3b234 --- /dev/null +++ b/test/jdk/sun/java2d/loops/DitheredSolidFill.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 4181172 + * @summary Confirm that solid white fill is not dithered on an 8-bit indexed surface. + * The test draws two areas filled with white solid color. + * The upper left square is filled in aliasing mode and + * the lower right square is filled in anti-aliasing mode. + */ + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; + +public class DitheredSolidFill { + + public static void main(String args[]) { + BufferedImage bi = new BufferedImage(120, 120, BufferedImage.TYPE_BYTE_INDEXED); + Graphics2D g2D = bi.createGraphics(); + + g2D.setColor(Color.black); + g2D.fillRect(0, 0, 100, 100); + + g2D.setColor(Color.white); + g2D.fillRect(5, 5, 40, 40); + checkPixels(bi, 5, 5, 40, 40); + + g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2D.fillRect(55, 55, 40, 40); + checkPixels(bi, 55, 55, 40, 40); + } + + static void checkPixels(BufferedImage bi, int x, int y, int w, int h) { + // pixel can be off white, but must be the same in all cases. + int expectedPix = bi.getRGB(x, y); + for (int x0 = x; x0 < x + w; x0++) { + for (int y0 = y; y0 < y + h; y0++) { + if (bi.getRGB(x0, y0) != expectedPix) { + try { + javax.imageio.ImageIO.write(bi, "png", new java.io.File("failed.png")); + } catch (Exception e) { + } + throw new RuntimeException("Not expected pix : " + + Integer.toHexString(bi.getRGB(x0, y0)) + + " at " + x0 + "," + y0); + } + } + } + } +} diff --git a/test/jdk/sun/java2d/loops/OffsetCalculationTest.java b/test/jdk/sun/java2d/loops/OffsetCalculationTest.java new file mode 100644 index 0000000000000..fe30107f22a9f --- /dev/null +++ b/test/jdk/sun/java2d/loops/OffsetCalculationTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + @test + @bug 4236576 + @summary tests that a BufferedImage in TYPE_3BYTE_BGR format is correctly + drawn when there is an offset between the Graphics clip bounds + and the clip box of the underlying device context. + @run main OffsetCalculationTest +*/ + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import java.awt.image.DataBuffer; + +public class OffsetCalculationTest { + + public static void main(String[] args) { + BufferedImage srcImage = new BufferedImage(500, 500, BufferedImage.TYPE_3BYTE_BGR); + + DataBuffer buffer = srcImage.getRaster().getDataBuffer(); + for (int i = 2; i < buffer.getSize(); i+=3) { + // setting each pixel to blue via the data buffer elements. + buffer.setElem(i - 2, 0xff); + buffer.setElem(i - 1, 0); + buffer.setElem(i, 0); + } + + int w = 200, h = 200; + BufferedImage destImage = new BufferedImage(w, h, BufferedImage.TYPE_3BYTE_BGR); + Graphics2D g = destImage.createGraphics(); + Rectangle r = new Rectangle(0, 0, w, h); + g.setClip(r.x - 1, r.y, r.width + 1, r.height); + g.drawImage(srcImage, 0, 0, null); + + int bluepix = Color.blue.getRGB(); + for (int y = 0; y < w; y++) { + for (int x = 0; x < h; x++) { + if (destImage.getRGB(x, y) != bluepix) { + throw new RuntimeException("Not Blue"); + } + } + } + } +} diff --git a/test/jdk/sun/java2d/loops/XORClearRect.java b/test/jdk/sun/java2d/loops/XORClearRect.java new file mode 100644 index 0000000000000..f5c30581cf1f3 --- /dev/null +++ b/test/jdk/sun/java2d/loops/XORClearRect.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4088173 + * @summary This interactive test verifies that the XOR mode is not affecting + * the clearRect() call. The correct output looks like: + * + * \ / + * \ / + * The backgound is blue. + * The lines outside the central rectangle are green. + * The central rectangle is also blue (the result of clearRect()) + * / \ + * / \ + * + * @key headful + * @run main XORClearRect + */ + +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.Graphics; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Robot; + +public class XORClearRect extends Panel { + + public static void main(String args[]) throws Exception { + EventQueue.invokeAndWait(XORClearRect::createUI); + try { + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(2000); + Point p = frame.getLocationOnScreen(); + int pix = robot.getPixelColor(p.x + 100, p.y + 100).getRGB(); + if (pix != Color.blue.getRGB()) { + throw new RuntimeException("Not blue"); + } + } finally { + if (frame != null) { + EventQueue.invokeAndWait(frame::dispose); + } + } + } + + static volatile Frame frame; + + static void createUI() { + frame = new Frame("XORClearRect"); + frame.setBackground(Color.blue); + XORClearRect xor = new XORClearRect(); + frame.add(xor); + frame.setSize(200,200); + frame.setVisible(true); + } + + public XORClearRect() { + setBackground(Color.blue); + } + + public void paint(Graphics g) { + g.setColor(Color.green); + g.drawLine(0,0,200,200); + g.drawLine(0,200,200,0); + g.setXORMode(Color.blue); + g.clearRect(50,50,100,100); //expecting the rectangle to be filled + // with the background color (blue) + } +} diff --git a/test/jdk/sun/java2d/pipe/hw/RSLAPITest/RSLAPITest.java b/test/jdk/sun/java2d/pipe/hw/RSLAPITest/RSLAPITest.java index 9e0194b498530..9c5cf3de2e37c 100644 --- a/test/jdk/sun/java2d/pipe/hw/RSLAPITest/RSLAPITest.java +++ b/test/jdk/sun/java2d/pipe/hw/RSLAPITest/RSLAPITest.java @@ -75,7 +75,7 @@ private static void testInvalidType(AccelSurface surface, int type) { if (ret != 0l) { System.err.printf( "FAILED: surface.getNativeResource(%d) returned" + - " 0x%s. It should have have returned 0L\n", + " 0x%s. It should have returned 0L\n", type, ret); failed = true; } diff --git a/test/jdk/sun/jvmstat/monitor/MonitoredVm/ConcurrentGetMonitoredHost.java b/test/jdk/sun/jvmstat/monitor/MonitoredVm/ConcurrentGetMonitoredHost.java new file mode 100644 index 0000000000000..65294f45e646c --- /dev/null +++ b/test/jdk/sun/jvmstat/monitor/MonitoredVm/ConcurrentGetMonitoredHost.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import sun.jvmstat.monitor.MonitoredHost; +import sun.jvmstat.monitor.VmIdentifier; + +/* + * @test + * @bug 8320687 + * @summary verify that sun.jvmstat.monitor.MonitoredHost.getMonitoredHost() doesn't + * unexpectedly throw an exception when invoked by concurrent threads + * + * @run main/othervm ConcurrentGetMonitoredHost + */ +public class ConcurrentGetMonitoredHost { + + /* + * Launches multiple concurrent threads and invokes MonitoredHost.getMonitoredHost() + * in each of these threads and expects the call to return successfully without any + * exceptions. + */ + public static void main(final String[] args) throws Exception { + final String pidStr = "12345"; + final VmIdentifier vmid = new VmIdentifier(pidStr); + final int numTasks = 100; + final List tasks = new ArrayList<>(); + for (int i = 0; i < numTasks; i++) { + tasks.add(new Task(vmid)); + } + System.out.println("Submitting " + numTasks + " concurrent tasks to" + + " get MonitoredHost for " + vmid); + try { + final ExecutorService executor = Executors.newCachedThreadPool(); + // wait for all tasks to complete + final List> results = executor.invokeAll(tasks); + // verify each one successfully completed and each of + // the returned MonitoredHost is not null + for (final Future result : results) { + final MonitoredHost mh = result.get(); + if (mh == null) { + throw new AssertionError("MonitoredHost.getMonitoredHost() returned" + + " null for vmid " + vmid); + } + } + } catch (Exception e) { + } + System.out.println("All " + numTasks + " completed successfully"); + } + + // a task which just calls MonitoredHost.getMonitoredHost(VmIdentifier) and + // returns the resultant MonitoredHost + private static final class Task implements Callable { + private final VmIdentifier vmid; + + private Task(final VmIdentifier vmid) { + this.vmid = Objects.requireNonNull(vmid); + } + + @Override + public MonitoredHost call() throws Exception { + return MonitoredHost.getMonitoredHost(this.vmid); + } + } +} diff --git a/test/jdk/sun/jvmstat/monitor/MonitoredVm/MonitorVmStartTerminate.java b/test/jdk/sun/jvmstat/monitor/MonitoredVm/MonitorVmStartTerminate.java index 1a59b0df11877..b4291645007d6 100644 --- a/test/jdk/sun/jvmstat/monitor/MonitoredVm/MonitorVmStartTerminate.java +++ b/test/jdk/sun/jvmstat/monitor/MonitoredVm/MonitorVmStartTerminate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -74,6 +74,7 @@ public final class MonitorVmStartTerminate { private static final int PROCESS_COUNT = 10; + private static final int ARGS_ATTEMPTS = 10; public static void main(String... args) throws Exception { @@ -175,8 +176,6 @@ private void releaseTerminated(Integer id) { } } - private static final int ARGS_ATTEMPTS = 3; - private boolean hasMainArgs(Integer id, String args) { VmIdentifier vmid = null; try { @@ -204,7 +203,10 @@ private boolean hasMainArgs(Integer id, String args) { } catch (MonitorException e) { // Process probably not running or not ours, e.g. // sun.jvmstat.monitor.MonitorException: Could not attach to PID - System.out.println("hasMainArgs(" + id + "): " + e); + // Only log if something else, to avoid filling log: + if (!e.getMessage().contains("Could not attach")) { + System.out.println("hasMainArgs(" + id + "): " + e); + } } } return false; @@ -249,23 +251,15 @@ private static void createFile(Path path) throws IOException { } private static void waitForRemoval(Path path) { - String timeoutFactorText = System.getProperty("test.timeout.factor", "1.0"); - double timeoutFactor = Double.parseDouble(timeoutFactorText); - long timeoutNanos = 1000_000_000L*(long)(1000*timeoutFactor); long start = System.nanoTime(); + System.out.println("Waiting for " + path + " to be removed"); while (true) { long now = System.nanoTime(); long waited = now - start; - System.out.println("Waiting for " + path + " to be removed, " + waited + " ns"); if (!Files.exists(path)) { + System.out.println("waitForRemoval: " + path + " has been removed in " + waited + " ns"); return; } - if (waited > timeoutNanos) { - System.out.println("Start: " + start); - System.out.println("Now: " + now); - System.out.println("Process timed out after " + waited + " ns. Abort."); - System.exit(1); - } takeNap(); } } diff --git a/test/jdk/sun/management/jdp/DynamicLauncher.java b/test/jdk/sun/management/jdp/DynamicLauncher.java index 9102fe32a94de..b79672211b1ef 100644 --- a/test/jdk/sun/management/jdp/DynamicLauncher.java +++ b/test/jdk/sun/management/jdp/DynamicLauncher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,6 +37,8 @@ */ public abstract class DynamicLauncher { + private static final int MAX_RETRY_ATTEMPTS = 10; + final String jdpName = UUID.randomUUID().toString(); OutputAnalyzer output; int jmxPort; @@ -52,7 +54,7 @@ protected void run() throws Exception { try { output.shouldNotContain("Port already in use"); } catch (RuntimeException e) { - if (retries < 3) { + if (retries < MAX_RETRY_ATTEMPTS) { retries++; tryAgain = true; } diff --git a/test/jdk/sun/management/jmxremote/bootstrap/AbstractFilePermissionTest.java b/test/jdk/sun/management/jmxremote/bootstrap/AbstractFilePermissionTest.java index ddbca2c444ded..7d1756be06255 100644 --- a/test/jdk/sun/management/jmxremote/bootstrap/AbstractFilePermissionTest.java +++ b/test/jdk/sun/management/jmxremote/bootstrap/AbstractFilePermissionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,7 +35,6 @@ import java.nio.file.attribute.PosixFilePermission; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -47,7 +46,6 @@ * @author Taras Ledkov */ public abstract class AbstractFilePermissionTest { - private final String TEST_CLASS_PATH = System.getProperty("test.class.path"); protected final String TEST_CLASSES = System.getProperty("test.classes"); protected final FileSystem FS = FileSystems.getDefault(); private int MAX_GET_FREE_PORT_TRIES = 10; @@ -169,11 +167,8 @@ private int doTest() throws Exception { final String pp = "-Dcom.sun.management.jmxremote.port=" + jdk.test.lib.Utils.getFreePort(); List command = new ArrayList<>(); - Collections.addAll(command, jdk.test.lib.Utils.getTestJavaOpts()); command.add(mp); command.add(pp); - command.add("-cp"); - command.add(TEST_CLASSES); command.add(className); ProcessBuilder processBuilder = ProcessTools.createTestJavaProcessBuilder(command); diff --git a/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java b/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java index cb0ba50e5972f..945824832cca6 100644 --- a/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java +++ b/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java @@ -52,7 +52,7 @@ public class JMXInterfaceBindingTest { public static final int JMX_PORT_RANGE_UPPER = 9200; public static final int JMX_PORT_RANGE_LOWER_SSL = 9201; // 9200 might be RMI Port public static final int JMX_PORT_RANGE_UPPER_SSL = 9300; - private static final int MAX_RETRY_ATTEMTS = 10; + private static final int MAX_RETRY_ATTEMPTS = 10; public static final String READY_MSG = "MainThread: Ready for connections"; public static final String TEST_CLASS = JMXAgentInterfaceBinding.class.getSimpleName(); public static final String KEYSTORE_LOC = System.getProperty("test.src", ".") + @@ -159,7 +159,7 @@ public void run() { System.err.println("Retrying the test for " + name); } needRetry = runTest(); - } while (needRetry && (attempts++ < MAX_RETRY_ATTEMTS)); + } while (needRetry && (attempts++ < MAX_RETRY_ATTEMPTS)); if (testFailed) { int exitValue = output.getExitValue(); diff --git a/test/jdk/sun/management/jmxremote/bootstrap/LocalManagementTest.java b/test/jdk/sun/management/jmxremote/bootstrap/LocalManagementTest.java index 0a7c735e2e0b0..b6e478528d970 100644 --- a/test/jdk/sun/management/jmxremote/bootstrap/LocalManagementTest.java +++ b/test/jdk/sun/management/jmxremote/bootstrap/LocalManagementTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,12 +24,10 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import jdk.test.lib.process.ProcessTools; -import jdk.test.lib.Utils; /** * @test @@ -48,7 +46,6 @@ * @run main/othervm/timeout=300 LocalManagementTest */ public class LocalManagementTest { - private static final String TEST_CLASSPATH = System.getProperty("test.class.path"); public static void main(String[] args) throws Exception { int failures = 0; @@ -99,16 +96,13 @@ private static boolean test5() throws Exception { private static boolean doTest(String testId, String arg) throws Exception { List args = new ArrayList<>(); args.add("-XX:+UsePerfData"); - Collections.addAll(args, Utils.getTestJavaOpts()); - args.add("-cp"); - args.add(TEST_CLASSPATH); if (arg != null) { args.add(arg); } args.add("TestApplication"); ProcessBuilder server = ProcessTools.createTestJavaProcessBuilder( - args.toArray(new String[args.size()]) + args.toArray(new String[0]) ); Process serverPrc = null, clientPrc = null; @@ -134,8 +128,6 @@ private static boolean doTest(String testId, String arg) throws Exception { System.out.println(" shutdown port : " + port.get()); ProcessBuilder client = ProcessTools.createTestJavaProcessBuilder( - "-cp", - TEST_CLASSPATH, "--add-exports", "jdk.management.agent/jdk.internal.agent=ALL-UNNAMED", "TestManager", String.valueOf(serverPrc.pid()), diff --git a/test/jdk/sun/management/jmxremote/bootstrap/RmiRegistrySslTest.java b/test/jdk/sun/management/jmxremote/bootstrap/RmiRegistrySslTest.java index 6cc5708b385bd..1aa20937962dc 100644 --- a/test/jdk/sun/management/jmxremote/bootstrap/RmiRegistrySslTest.java +++ b/test/jdk/sun/management/jmxremote/bootstrap/RmiRegistrySslTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,6 @@ import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; -import java.net.BindException; import java.nio.charset.Charset; import java.nio.file.FileSystem; import java.nio.file.FileSystems; @@ -180,12 +179,9 @@ private int doTest(String... args) throws Exception { initTestEnvironment(); List command = new ArrayList<>(); - Collections.addAll(command, Utils.getTestJavaOpts()); command.add("-Dtest.src=" + TEST_SRC); command.add("-Dtest.rmi.port=" + port); command.addAll(Arrays.asList(args)); - command.add("-cp"); - command.add(TEST_CLASS_PATH); command.add(className); ProcessBuilder processBuilder = ProcessTools.createTestJavaProcessBuilder(command); diff --git a/test/jdk/sun/net/www/protocol/file/FileURLTest.java b/test/jdk/sun/net/www/protocol/file/FileURLTest.java index 135274a9504d0..e111a538aca09 100644 --- a/test/jdk/sun/net/www/protocol/file/FileURLTest.java +++ b/test/jdk/sun/net/www/protocol/file/FileURLTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,7 @@ * @test * @bug 4474391 * @summary url: file:///D|/Projects/tmp/test.html: urlConnection.getInputStream() broken. + * @requires os.family == "windows" */ import java.io.*; import java.net.*; @@ -33,10 +34,7 @@ public class FileURLTest { public static void main(String [] args) { - String name = System.getProperty("os.name"); - if (name.startsWith("Windows")) { String urlStr = "file:///C|/nonexisted.txt"; - try { URL url = new URL(urlStr); URLConnection urlConnection = url.openConnection(); @@ -49,6 +47,5 @@ public static void main(String [] args) throw new RuntimeException("Can't handle '|' in place of ':' in file urls"); } } - } } } diff --git a/test/jdk/sun/net/www/protocol/file/NonLocalFtpFallback.java b/test/jdk/sun/net/www/protocol/file/NonLocalFtpFallback.java new file mode 100644 index 0000000000000..49d09cfb507a2 --- /dev/null +++ b/test/jdk/sun/net/www/protocol/file/NonLocalFtpFallback.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @test + * @bug 8353662 + * @summary Verify long-standing behavior of resolving non-local file URLs using FTP. + * @run junit NonLocalFtpFallback + */ + +public class NonLocalFtpFallback { + + // Port 21 may not be available, use an HTTP proxy with an ephemeral port + private HttpServer proxyServer; + + // The file requested in this test + private Path file; + + // FTP URIs requested by the proxy client + private Set uris = new HashSet<>(); + + /** + * Set up the HTTP proxy used for serving FTP in this test + * + * @throws IOException if an unexpected IO error occurs + */ + @BeforeEach + public void setup() throws IOException { + // Create a file with some random data + byte[] data = new byte[512]; + new Random().nextBytes(data); + file = Files.write(Path.of("ftp-file.txt"), data); + + // Set up an HTTP proxy server + proxyServer = HttpServer.create(); + // Bind to the loopback address with an ephemeral port + InetAddress loopbackAddress = InetAddress.getLoopbackAddress(); + proxyServer.bind(new InetSocketAddress(loopbackAddress, 0), 0); + // Handler for the FTP proxy request + proxyServer.createContext("/", new HttpHandler() { + @Override + public void handle(HttpExchange exchange) throws IOException { + // Record the URI requested + uris.add(exchange.getRequestURI()); + // Send the data + exchange.sendResponseHeaders(200, Files.size(file)); + try (OutputStream out = exchange.getResponseBody()) { + Files.copy(file, out); + } + // Complete the exchange + exchange.close(); + } + }); + // Start the proxy server + proxyServer.start(); + } + + /** + * Shut down proxy server and clean up files created + * + * @throws IOException if an unexpected IO error occurs + */ + @AfterEach + public void destroy() throws IOException { + proxyServer.stop(2); + Files.delete(file); + } + + /** + * Verifies the long-standing and unspecified FTP fallback feature where the file + * URL scheme handler attempts an FTP connection for non-local files. + * + * The non-local file URL used here is of the form file://127.0.0.1/path. Since the + * host component here is not equal to "localhost", this is considered a non-local + * URL. + * + * @throws Exception + */ + @Test + public void verifyNonLocalFtpFallback() throws Exception { + URL localURL = file.toUri().toURL(); + // We can use a fake host name here, no actual FTP request will be made + String hostname = "remotehost"; + URL nonLocalURL = new URL("file", hostname, localURL.getFile()); + + // Open the non-local file: URL connection using a proxy + Proxy proxy = new Proxy(Proxy.Type.HTTP, + new InetSocketAddress(proxyServer.getAddress().getAddress(), + proxyServer.getAddress().getPort())); + URLConnection con = nonLocalURL.openConnection(proxy); + + // Assert that the expected file content is retrieved + try (InputStream in = con.getInputStream()) { + byte[] retrived = in.readAllBytes(); + assertArrayEquals(Files.readAllBytes(file), retrived); + } + + // Assert that the expected FTP URI was requested in the HTTP proxy + assertEquals(1, uris.size()); + URL ftpURL = new URL("ftp", hostname, localURL.getFile()); + assertEquals(ftpURL.toURI(), uris.iterator().next()); + } +} diff --git a/test/jdk/sun/net/www/protocol/http/UserCookie.java b/test/jdk/sun/net/www/protocol/http/UserCookie.java index 29261112ece88..7867523719b13 100644 --- a/test/jdk/sun/net/www/protocol/http/UserCookie.java +++ b/test/jdk/sun/net/www/protocol/http/UserCookie.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,9 +24,9 @@ /* * @test * @bug 6439651 - * @modules jdk.httpserver - * @run main/othervm UserAuth * @summary Sending "Cookie" header with JRE 1.5.0_07 doesn't work anymore + * @modules jdk.httpserver + * @run main/othervm UserCookie */ import java.net.*; diff --git a/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/DNSIdentities.java b/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/DNSIdentities.java index 0d479d60fd271..4b2dece0c75a7 100644 --- a/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/DNSIdentities.java +++ b/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/DNSIdentities.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -893,7 +893,7 @@ private static SSLContext getSSLContext(String trusedCertStr, TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); tmf.init(ks); - SSLContext ctx = SSLContext.getInstance("TLS"); + SSLContext ctx = SSLContext.getInstance("TLSv1.2"); if (keyCertStr != null) { KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); diff --git a/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/IPAddressIPIdentities.java b/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/IPAddressIPIdentities.java index bd6c62c603ca1..a15093c821ece 100644 --- a/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/IPAddressIPIdentities.java +++ b/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/IPAddressIPIdentities.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -901,7 +901,7 @@ private static SSLContext getSSLContext(String trusedCertStr, TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); tmf.init(ks); - SSLContext ctx = SSLContext.getInstance("TLS"); + SSLContext ctx = SSLContext.getInstance("TLSv1.2"); if (keyCertStr != null) { KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); diff --git a/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/IPIdentities.java b/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/IPIdentities.java index 91f0020f7d124..9ce71064b5418 100644 --- a/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/IPIdentities.java +++ b/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/IPIdentities.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,379 +34,41 @@ * @author Xuelei Fan */ -import java.net.*; -import java.util.*; -import java.io.*; -import javax.net.ssl.*; -import java.security.Security; + +import java.io.PrintStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.URL; +import java.security.KeyPair; +import java.security.KeyPairGenerator; import java.security.KeyStore; -import java.security.KeyFactory; +import java.security.SecureRandom; +import java.security.Security; +import java.security.cert.X509Certificate; + import java.security.cert.Certificate; -import java.security.cert.CertificateFactory; -import java.security.spec.*; -import java.security.interfaces.*; import java.math.BigInteger; import jdk.test.lib.net.URIBuilder; - -/* - * Certificates and key used in the test. - * - * TLS server certificate: - * server private key: - * -----BEGIN RSA PRIVATE KEY----- - * Proc-Type: 4,ENCRYPTED - * DEK-Info: DES-EDE3-CBC,D9AE407F6D0E389A - * - * WPrA7TFol/cQCcp9oHnXWNpYlvRbbIcQj0m+RKT2Iuzfus+DHt3Zadf8nJpKfX2e - * h2rnhlzCN9M7djRDooZKDOPCsdBn51Au7HlZF3S3Opgo7D8XFM1a8t1Je4ke14oI - * nw6QKYsBblRziPnP2PZ0zvX24nOv7bbY8beynlJHGs00VWSFdoH2DS0aE1p6D+3n - * ptJuJ75dVfZFK4X7162APlNXevX8D6PEQpSiRw1rjjGGcnvQ4HdWk3BxDVDcCNJb - * Y1aGNRxsjTDvPi3R9Qx2M+W03QzEPx4SR3ZHVskeSJHaetM0TM/w/45Paq4GokXP - * ZeTnbEx1xmjkA7h+t4doLL4watx5F6yLsJzu8xB3lt/1EtmkYtLz1t7X4BetPAXz - * zS69X/VwhKfsOI3qXBWuL2oHPyhDmT1gcaUQwEPSV6ogHEEQEDXdiUS8heNK13KF - * TCQYFkETvV2BLxUhV1hypPzRQ6tUpJiAbD5KmoK2lD9slshG2QtvKQq0/bgkDY5J - * LhDHV2dtcZ3kDPkkZXpbcJQvoeH3d09C5sIsuTFo2zgNR6oETHUc5TzP6FY2YYRa - * QcK5HcmtsRRiXFm01ac+aMejJUIujjFt84SiKWT/73vC8AmY4tYcJBLjCg4XIxSH - * fdDFLL1YZENNO5ivlp8mdiHqcawx+36L7DrEZQ8RZt6cqST5t/+XTdM74s6k81GT - * pNsa82P2K2zmIUZ/DL2mKjW1vfRByw1NQFEBkN3vdyZxYfM/JyUzX4hbjXBEkh9Q - * QYrcwLKLjis2QzSvK04B3bvRzRb+4ocWiso8ZPAXAIxZFBWDpTMM2A== - * -----END RSA PRIVATE KEY----- - * - * -----BEGIN RSA PRIVATE KEY----- - * MIICXAIBAAKBgQClrFscN6LdmYktsnm4j9VIpecchBeNaZzGrG358h0fORna03Ie - * buxEzHCk3LoAMPagTz1UemFqzFfQCn+VKBg/mtmU8hvIJIh+/p0PPftXUwizIDPU - * PxdHFNHN6gjYDnVOr77M0uyvqXpJ38LZrLgkQJCmA1Yq0DAFQCxPq9l0iQIDAQAB - * AoGAbqcbg1E1mkR99uOJoNeQYKFOJyGiiXTMnXV1TseC4+PDfQBU7Dax35GcesBi - * CtapIpFKKS5D+ozY6b7ZT8ojxuQ/uHLPAvz0WDR3ds4iRF8tyu71Q1ZHcQsJa17y - * yO7UbkSSKn/Mp9Rb+/dKqftUGNXVFLqgHBOzN2s3We3bbbECQQDYBPKOg3hkaGHo - * OhpHKqtQ6EVkldihG/3i4WejRonelXN+HRh1KrB2HBx0M8D/qAzP1i3rNSlSHer4 - * 59YRTJnHAkEAxFX/sVYSn07BHv9Zhn6XXct/Cj43z/tKNbzlNbcxqQwQerw3IH51 - * 8UH2YOA+GD3lXbKp+MytoFLWv8zg4YT/LwJAfqan75Z1R6lLffRS49bIiq8jwE16 - * rTrUJ+kv8jKxMqc9B3vXkxpsS1M/+4E8bqgAmvpgAb8xcsvHsBd9ErdukQJBAKs2 - * j67W75BrPjBI34pQ1LEfp56IGWXOrq1kF8IbCjxv3+MYRT6Z6UJFkpRymNPNDjsC - * dgUYgITiGJHUGXuw3lMCQHEHqo9ZtXz92yFT+VhsNc29B8m/sqUJdtCcMd/jGpAF - * u6GHufjqIZBpQsk63wbwESAPZZ+kk1O1kS5GIRLX608= - * -----END RSA PRIVATE KEY----- - * - * Private-Key: (1024 bit) - * modulus: - * 00:a5:ac:5b:1c:37:a2:dd:99:89:2d:b2:79:b8:8f: - * d5:48:a5:e7:1c:84:17:8d:69:9c:c6:ac:6d:f9:f2: - * 1d:1f:39:19:da:d3:72:1e:6e:ec:44:cc:70:a4:dc: - * ba:00:30:f6:a0:4f:3d:54:7a:61:6a:cc:57:d0:0a: - * 7f:95:28:18:3f:9a:d9:94:f2:1b:c8:24:88:7e:fe: - * 9d:0f:3d:fb:57:53:08:b3:20:33:d4:3f:17:47:14: - * d1:cd:ea:08:d8:0e:75:4e:af:be:cc:d2:ec:af:a9: - * 7a:49:df:c2:d9:ac:b8:24:40:90:a6:03:56:2a:d0: - * 30:05:40:2c:4f:ab:d9:74:89 - * publicExponent: 65537 (0x10001) - * privateExponent: - * 6e:a7:1b:83:51:35:9a:44:7d:f6:e3:89:a0:d7:90: - * 60:a1:4e:27:21:a2:89:74:cc:9d:75:75:4e:c7:82: - * e3:e3:c3:7d:00:54:ec:36:b1:df:91:9c:7a:c0:62: - * 0a:d6:a9:22:91:4a:29:2e:43:fa:8c:d8:e9:be:d9: - * 4f:ca:23:c6:e4:3f:b8:72:cf:02:fc:f4:58:34:77: - * 76:ce:22:44:5f:2d:ca:ee:f5:43:56:47:71:0b:09: - * 6b:5e:f2:c8:ee:d4:6e:44:92:2a:7f:cc:a7:d4:5b: - * fb:f7:4a:a9:fb:54:18:d5:d5:14:ba:a0:1c:13:b3: - * 37:6b:37:59:ed:db:6d:b1 - * prime1: - * 00:d8:04:f2:8e:83:78:64:68:61:e8:3a:1a:47:2a: - * ab:50:e8:45:64:95:d8:a1:1b:fd:e2:e1:67:a3:46: - * 89:de:95:73:7e:1d:18:75:2a:b0:76:1c:1c:74:33: - * c0:ff:a8:0c:cf:d6:2d:eb:35:29:52:1d:ea:f8:e7: - * d6:11:4c:99:c7 - * prime2: - * 00:c4:55:ff:b1:56:12:9f:4e:c1:1e:ff:59:86:7e: - * 97:5d:cb:7f:0a:3e:37:cf:fb:4a:35:bc:e5:35:b7: - * 31:a9:0c:10:7a:bc:37:20:7e:75:f1:41:f6:60:e0: - * 3e:18:3d:e5:5d:b2:a9:f8:cc:ad:a0:52:d6:bf:cc: - * e0:e1:84:ff:2f - * exponent1: - * 7e:a6:a7:ef:96:75:47:a9:4b:7d:f4:52:e3:d6:c8: - * 8a:af:23:c0:4d:7a:ad:3a:d4:27:e9:2f:f2:32:b1: - * 32:a7:3d:07:7b:d7:93:1a:6c:4b:53:3f:fb:81:3c: - * 6e:a8:00:9a:fa:60:01:bf:31:72:cb:c7:b0:17:7d: - * 12:b7:6e:91 - * exponent2: - * 00:ab:36:8f:ae:d6:ef:90:6b:3e:30:48:df:8a:50: - * d4:b1:1f:a7:9e:88:19:65:ce:ae:ad:64:17:c2:1b: - * 0a:3c:6f:df:e3:18:45:3e:99:e9:42:45:92:94:72: - * 98:d3:cd:0e:3b:02:76:05:18:80:84:e2:18:91:d4: - * 19:7b:b0:de:53 - * coefficient: - * 71:07:aa:8f:59:b5:7c:fd:db:21:53:f9:58:6c:35: - * cd:bd:07:c9:bf:b2:a5:09:76:d0:9c:31:df:e3:1a: - * 90:05:bb:a1:87:b9:f8:ea:21:90:69:42:c9:3a:df: - * 06:f0:11:20:0f:65:9f:a4:93:53:b5:91:2e:46:21: - * 12:d7:eb:4f - * - * - * server certificate: - * Data: - * Version: 3 (0x2) - * Serial Number: 7 (0x7) - * Signature Algorithm: md5WithRSAEncryption - * Issuer: C=US, ST=Some-State, L=Some-City, O=Some-Org - * Validity - * Not Before: Dec 8 03:27:57 2008 GMT - * Not After : Aug 25 03:27:57 2028 GMT - * Subject: C=US, ST=Some-State, L=Some-City, O=Some-Org, OU=SSL-Server, CN=localhost - * Subject Public Key Info: - * Public Key Algorithm: rsaEncryption - * RSA Public Key: (1024 bit) - * Modulus (1024 bit): - * 00:a5:ac:5b:1c:37:a2:dd:99:89:2d:b2:79:b8:8f: - * d5:48:a5:e7:1c:84:17:8d:69:9c:c6:ac:6d:f9:f2: - * 1d:1f:39:19:da:d3:72:1e:6e:ec:44:cc:70:a4:dc: - * ba:00:30:f6:a0:4f:3d:54:7a:61:6a:cc:57:d0:0a: - * 7f:95:28:18:3f:9a:d9:94:f2:1b:c8:24:88:7e:fe: - * 9d:0f:3d:fb:57:53:08:b3:20:33:d4:3f:17:47:14: - * d1:cd:ea:08:d8:0e:75:4e:af:be:cc:d2:ec:af:a9: - * 7a:49:df:c2:d9:ac:b8:24:40:90:a6:03:56:2a:d0: - * 30:05:40:2c:4f:ab:d9:74:89 - * Exponent: 65537 (0x10001) - * X509v3 extensions: - * X509v3 Basic Constraints: - * CA:FALSE - * X509v3 Key Usage: - * Digital Signature, Non Repudiation, Key Encipherment - * X509v3 Subject Key Identifier: - * ED:6E:DB:F4:B5:56:C8:FB:1A:06:61:3F:0F:08:BB:A6:04:D8:16:54 - * X509v3 Authority Key Identifier: - * keyid:FA:B9:51:BF:4C:E7:D9:86:98:33:F9:E7:CB:1E:F1:33:49:F7:A8:14 - * - * X509v3 Subject Alternative Name: critical - * IP Address:127.0.0.1 - * Signature Algorithm: md5WithRSAEncryption - * - * -----BEGIN CERTIFICATE----- - * MIICnzCCAgigAwIBAgIBBzANBgkqhkiG9w0BAQQFADBJMQswCQYDVQQGEwJVUzET - * MBEGA1UECBMKU29tZS1TdGF0ZTESMBAGA1UEBxMJU29tZS1DaXR5MREwDwYDVQQK - * EwhTb21lLU9yZzAeFw0wODEyMDgwMzI3NTdaFw0yODA4MjUwMzI3NTdaMHIxCzAJ - * BgNVBAYTAlVTMRMwEQYDVQQIEwpTb21lLVN0YXRlMRIwEAYDVQQHEwlTb21lLUNp - * dHkxETAPBgNVBAoTCFNvbWUtT3JnMRMwEQYDVQQLEwpTU0wtU2VydmVyMRIwEAYD - * VQQDEwlsb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKWsWxw3 - * ot2ZiS2yebiP1Uil5xyEF41pnMasbfnyHR85GdrTch5u7ETMcKTcugAw9qBPPVR6 - * YWrMV9AKf5UoGD+a2ZTyG8gkiH7+nQ89+1dTCLMgM9Q/F0cU0c3qCNgOdU6vvszS - * 7K+peknfwtmsuCRAkKYDVirQMAVALE+r2XSJAgMBAAGjbjBsMAkGA1UdEwQCMAAw - * CwYDVR0PBAQDAgXgMB0GA1UdDgQWBBTtbtv0tVbI+xoGYT8PCLumBNgWVDAfBgNV - * HSMEGDAWgBT6uVG/TOfZhpgz+efLHvEzSfeoFDASBgNVHREBAf8ECDAGhwR/AAAB - * MA0GCSqGSIb3DQEBBAUAA4GBAFJjItCtCBZcjD69wdqfIbKmRFa6eJAjR6LcoDva - * cKC/sDOLelpspiZ66Zb0Xdv5qQ7QrfOXt3K8QqJKRMdZLF9WfUfy0gJDM32ub91h - * pu+TmcGPs+6RdrAQcuvU1ZDV9X8SMj7BtKaim4d5sqFw1npncKiA5xFn8vOYwdun - * nZif - * -----END CERTIFICATE----- - * - * - * TLS client certificate: - * client private key: - * ----BEGIN RSA PRIVATE KEY----- - * Proc-Type: 4,ENCRYPTED - * DEK-Info: DES-EDE3-CBC,FA2A435CD35A9390 - * - * Z+Y2uaETbsUWIyJUyVu1UV2G4rgFYJyACZT6Tp1KjRtxflSh2kXkJ9MpuXMXA0V4 - * Yy3fDzPqCL9NJmQAYRlAx/W/+j4F5EyMWDIx8fUxzONRZyoiwF7jLm+KscAfv6Pf - * q7ItWOdj3z7IYrwlB8YIGd3F2cDKT3S+lYRk7rKb/qT7itbuHnY4Ardh3yl+MZak - * jBp+ELUlRsUqSr1V0LoM+0rCCykarpyfhpxEcqsrl0v9Cyi5uhU50/oKv5zql3SH - * l2ImgDjp3batAs8+Bd4NF2aqi0a7Hy44JUHxRm4caZryU/i/D9N1MbuM6882HLat - * 5N0G+NaIUfywa8mjwq2D5aiit18HqKA6XeRRYeJ5Dvu9DCO4GeFSwcUFIBMI0L46 - * 7s114+oDodg57pMgITi+04vmUxvqlN9aiyd7f5Fgd7PeHGeOdbMz1NaJLJaPI9++ - * NakK8eK9iwT/Gdq0Uap5/CHW7vCT5PO+h3HY0STH0lWStXhdWnFO04zTdywsbSp+ - * DLpHeFT66shfeUlxR0PsCbG9vPRt/QmGLeYQZITppWo/ylSq4j+pRIuXvuWHdBRN - * rTZ8QF4Y7AxQUXVz1j1++s6ZMHTzaK2i9HrhmDs1MbJl+QwWre3Xpv3LvTVz3k5U - * wX8kuY1m3STt71QCaRWENq5sRaMImLxZbxc/ivFl9RAzUqo4NCxLod/QgA4iLqtO - * ztnlpzwlC/F8HbQ1oqYWwnZAPhzU/cULtstl+Yrws2c2atO323LbPXZqbASySgig - * sNpFXQMObdfP6LN23bY+1SvtK7V4NUTNhpdIc6INQAQ= - * -----END RSA PRIVATE KEY----- - * - * -----BEGIN RSA PRIVATE KEY----- - * MIICWwIBAAKBgQC78EA2rCZUTvSjWgAvaSFvuXo6k+yi9uGOx2PYLxIwmS6w8o/4 - * Jy0keCiE9wG/jUR53TvSVfPOPLJbIX3v/TNKsaP/xsibuQ98QTWX+ds6BWAFFa9Z - * F5KjEK0WHOQHU6+odqJWKpLT+SjgeM9eH0irXBnd4WdDunWN9YKsQ5JEGwIDAQAB - * AoGAEbdqNj0wN85hnWyEi/ObJU8UyKTdL9eaF72QGfcF/fLSxfd3vurihIeXOkGW - * tpn4lIxYcVGM9CognhqgJpl11jFTQzn1KqZ+NEJRKkCHA4hDabKJbSC9fXHvRwrf - * BsFpZqgiNxp3HseUTiwnaUVeyPgMt/jAj5nB5Sib+UyUxrECQQDnNQBiF2aifEg6 - * zbJOOC7he5CHAdkFxSxWVFVHL6EfXfqdLVkUohMbgZv+XxyIeU2biOExSg49Kds3 - * FOKgTau1AkEA0Bd1haj6QuCo8I0AXm2WO+MMTZMTvtHD/bGjKNM+fT4I8rKYnQRX - * 1acHdqS9Xx2rNJqZgkMmpESIdPR2fc4yjwJALFeM6EMmqvj8/VIf5UJ/Mz14fXwM - * PEARfckUxd9LnnFutCBTWlKvKXJVEZb6KO5ixPaegc57Jp3Vbh3yTN44lQJADD/1 - * SSMDaIB1MYP7a5Oj7m6VQNPRq8AJe5vDcRnOae0G9dKRrVyeFxO4GsHj6/+BHp2j - * P8nYMn9eURQ7DXjf/QJAAQzMlWnKGSO8pyTDtnQx3hRMoUkOEhmNq4bQhLkYqtnY - * FcqpUQ2qMjW+NiNWk5HnTrMS3L9EdJobMUzaNZLy4w== - * -----END RSA PRIVATE KEY----- - * - * Private-Key: (1024 bit) - * modulus: - * 00:bb:f0:40:36:ac:26:54:4e:f4:a3:5a:00:2f:69: - * 21:6f:b9:7a:3a:93:ec:a2:f6:e1:8e:c7:63:d8:2f: - * 12:30:99:2e:b0:f2:8f:f8:27:2d:24:78:28:84:f7: - * 01:bf:8d:44:79:dd:3b:d2:55:f3:ce:3c:b2:5b:21: - * 7d:ef:fd:33:4a:b1:a3:ff:c6:c8:9b:b9:0f:7c:41: - * 35:97:f9:db:3a:05:60:05:15:af:59:17:92:a3:10: - * ad:16:1c:e4:07:53:af:a8:76:a2:56:2a:92:d3:f9: - * 28:e0:78:cf:5e:1f:48:ab:5c:19:dd:e1:67:43:ba: - * 75:8d:f5:82:ac:43:92:44:1b - * publicExponent: 65537 (0x10001) - * privateExponent: - * 11:b7:6a:36:3d:30:37:ce:61:9d:6c:84:8b:f3:9b: - * 25:4f:14:c8:a4:dd:2f:d7:9a:17:bd:90:19:f7:05: - * fd:f2:d2:c5:f7:77:be:ea:e2:84:87:97:3a:41:96: - * b6:99:f8:94:8c:58:71:51:8c:f4:2a:20:9e:1a:a0: - * 26:99:75:d6:31:53:43:39:f5:2a:a6:7e:34:42:51: - * 2a:40:87:03:88:43:69:b2:89:6d:20:bd:7d:71:ef: - * 47:0a:df:06:c1:69:66:a8:22:37:1a:77:1e:c7:94: - * 4e:2c:27:69:45:5e:c8:f8:0c:b7:f8:c0:8f:99:c1: - * e5:28:9b:f9:4c:94:c6:b1 - * prime1: - * 00:e7:35:00:62:17:66:a2:7c:48:3a:cd:b2:4e:38: - * 2e:e1:7b:90:87:01:d9:05:c5:2c:56:54:55:47:2f: - * a1:1f:5d:fa:9d:2d:59:14:a2:13:1b:81:9b:fe:5f: - * 1c:88:79:4d:9b:88:e1:31:4a:0e:3d:29:db:37:14: - * e2:a0:4d:ab:b5 - * prime2: - * 00:d0:17:75:85:a8:fa:42:e0:a8:f0:8d:00:5e:6d: - * 96:3b:e3:0c:4d:93:13:be:d1:c3:fd:b1:a3:28:d3: - * 3e:7d:3e:08:f2:b2:98:9d:04:57:d5:a7:07:76:a4: - * bd:5f:1d:ab:34:9a:99:82:43:26:a4:44:88:74:f4: - * 76:7d:ce:32:8f - * exponent1: - * 2c:57:8c:e8:43:26:aa:f8:fc:fd:52:1f:e5:42:7f: - * 33:3d:78:7d:7c:0c:3c:40:11:7d:c9:14:c5:df:4b: - * 9e:71:6e:b4:20:53:5a:52:af:29:72:55:11:96:fa: - * 28:ee:62:c4:f6:9e:81:ce:7b:26:9d:d5:6e:1d:f2: - * 4c:de:38:95 - * exponent2: - * 0c:3f:f5:49:23:03:68:80:75:31:83:fb:6b:93:a3: - * ee:6e:95:40:d3:d1:ab:c0:09:7b:9b:c3:71:19:ce: - * 69:ed:06:f5:d2:91:ad:5c:9e:17:13:b8:1a:c1:e3: - * eb:ff:81:1e:9d:a3:3f:c9:d8:32:7f:5e:51:14:3b: - * 0d:78:df:fd - * coefficient: - * 01:0c:cc:95:69:ca:19:23:bc:a7:24:c3:b6:74:31: - * de:14:4c:a1:49:0e:12:19:8d:ab:86:d0:84:b9:18: - * aa:d9:d8:15:ca:a9:51:0d:aa:32:35:be:36:23:56: - * 93:91:e7:4e:b3:12:dc:bf:44:74:9a:1b:31:4c:da: - * 35:92:f2:e3 - * - * client certificate: - * Data: - * Version: 3 (0x2) - * Serial Number: 6 (0x6) - * Signature Algorithm: md5WithRSAEncryption - * Issuer: C=US, ST=Some-State, L=Some-City, O=Some-Org - * Validity - * Not Before: Dec 8 03:27:34 2008 GMT - * Not After : Aug 25 03:27:34 2028 GMT - * Subject: C=US, ST=Some-State, L=Some-City, O=Some-Org, OU=SSL-Client, CN=localhost - * Subject Public Key Info: - * Public Key Algorithm: rsaEncryption - * RSA Public Key: (1024 bit) - * Modulus (1024 bit): - * 00:bb:f0:40:36:ac:26:54:4e:f4:a3:5a:00:2f:69: - * 21:6f:b9:7a:3a:93:ec:a2:f6:e1:8e:c7:63:d8:2f: - * 12:30:99:2e:b0:f2:8f:f8:27:2d:24:78:28:84:f7: - * 01:bf:8d:44:79:dd:3b:d2:55:f3:ce:3c:b2:5b:21: - * 7d:ef:fd:33:4a:b1:a3:ff:c6:c8:9b:b9:0f:7c:41: - * 35:97:f9:db:3a:05:60:05:15:af:59:17:92:a3:10: - * ad:16:1c:e4:07:53:af:a8:76:a2:56:2a:92:d3:f9: - * 28:e0:78:cf:5e:1f:48:ab:5c:19:dd:e1:67:43:ba: - * 75:8d:f5:82:ac:43:92:44:1b - * Exponent: 65537 (0x10001) - * X509v3 extensions: - * X509v3 Basic Constraints: - * CA:FALSE - * X509v3 Key Usage: - * Digital Signature, Non Repudiation, Key Encipherment - * X509v3 Subject Key Identifier: - * CD:BB:C8:85:AA:91:BD:FD:1D:BE:CD:67:7C:FF:B3:E9:4C:A8:22:E6 - * X509v3 Authority Key Identifier: - * keyid:FA:B9:51:BF:4C:E7:D9:86:98:33:F9:E7:CB:1E:F1:33:49:F7:A8:14 - * - * X509v3 Subject Alternative Name: critical - * IP Address:127.0.0.1 - * Signature Algorithm: md5WithRSAEncryption - * - * -----BEGIN CERTIFICATE----- - * MIICnzCCAgigAwIBAgIBBjANBgkqhkiG9w0BAQQFADBJMQswCQYDVQQGEwJVUzET - * MBEGA1UECBMKU29tZS1TdGF0ZTESMBAGA1UEBxMJU29tZS1DaXR5MREwDwYDVQQK - * EwhTb21lLU9yZzAeFw0wODEyMDgwMzI3MzRaFw0yODA4MjUwMzI3MzRaMHIxCzAJ - * BgNVBAYTAlVTMRMwEQYDVQQIEwpTb21lLVN0YXRlMRIwEAYDVQQHEwlTb21lLUNp - * dHkxETAPBgNVBAoTCFNvbWUtT3JnMRMwEQYDVQQLEwpTU0wtQ2xpZW50MRIwEAYD - * VQQDEwlsb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALvwQDas - * JlRO9KNaAC9pIW+5ejqT7KL24Y7HY9gvEjCZLrDyj/gnLSR4KIT3Ab+NRHndO9JV - * 8848slshfe/9M0qxo//GyJu5D3xBNZf52zoFYAUVr1kXkqMQrRYc5AdTr6h2olYq - * ktP5KOB4z14fSKtcGd3hZ0O6dY31gqxDkkQbAgMBAAGjbjBsMAkGA1UdEwQCMAAw - * CwYDVR0PBAQDAgXgMB0GA1UdDgQWBBTNu8iFqpG9/R2+zWd8/7PpTKgi5jAfBgNV - * HSMEGDAWgBT6uVG/TOfZhpgz+efLHvEzSfeoFDASBgNVHREBAf8ECDAGhwR/AAAB - * MA0GCSqGSIb3DQEBBAUAA4GBACjj9PS+W6XOF7toFMwMOv/AemZeBOpcEF1Ei1Hx - * HjvB6EOHkMY8tFm5OPzkiWiK3+s3awpSW0jWdzMYwrQJ3/klMsPDpI7PEuirqwHP - * i5Wyl/vk7jmfWVcBO9MVhPUo4BYl4vS9aj6JA5QbkbkB95LOgT/BowY0WmHeVsXC - * I9aw - * -----END CERTIFICATE----- - * - * - * - * Trusted CA certificate: - * Certificate: - * Data: - * Version: 3 (0x2) - * Serial Number: 0 (0x0) - * Signature Algorithm: md5WithRSAEncryption - * Issuer: C=US, ST=Some-State, L=Some-City, O=Some-Org - * Validity - * Not Before: Dec 8 02:43:36 2008 GMT - * Not After : Aug 25 02:43:36 2028 GMT - * Subject: C=US, ST=Some-State, L=Some-City, O=Some-Org - * Subject Public Key Info: - * Public Key Algorithm: rsaEncryption - * RSA Public Key: (1024 bit) - * Modulus (1024 bit): - * 00:cb:c4:38:20:07:be:88:a7:93:b0:a1:43:51:2d: - * d7:8e:85:af:54:dd:ad:a2:7b:23:5b:cf:99:13:53: - * 99:45:7d:ee:6d:ba:2d:bf:e3:ad:6e:3d:9f:1a:f9: - * 03:97:e0:17:55:ae:11:26:57:de:01:29:8e:05:3f: - * 21:f7:e7:36:e8:2e:37:d7:48:ac:53:d6:60:0e:c7: - * 50:6d:f6:c5:85:f7:8b:a6:c5:91:35:72:3c:94:ee: - * f1:17:f0:71:e3:ec:1b:ce:ca:4e:40:42:b0:6d:ee: - * 6a:0e:d6:e5:ad:3c:0f:c9:ba:82:4f:78:f8:89:97: - * 89:2a:95:12:4c:d8:09:2a:e9 - * Exponent: 65537 (0x10001) - * X509v3 extensions: - * X509v3 Subject Key Identifier: - * FA:B9:51:BF:4C:E7:D9:86:98:33:F9:E7:CB:1E:F1:33:49:F7:A8:14 - * X509v3 Authority Key Identifier: - * keyid:FA:B9:51:BF:4C:E7:D9:86:98:33:F9:E7:CB:1E:F1:33:49:F7:A8:14 - * DirName:/C=US/ST=Some-State/L=Some-City/O=Some-Org - * serial:00 - * - * X509v3 Basic Constraints: - * CA:TRUE - * Signature Algorithm: md5WithRSAEncryption - * - * -----BEGIN CERTIFICATE----- - * MIICrDCCAhWgAwIBAgIBADANBgkqhkiG9w0BAQQFADBJMQswCQYDVQQGEwJVUzET - * MBEGA1UECBMKU29tZS1TdGF0ZTESMBAGA1UEBxMJU29tZS1DaXR5MREwDwYDVQQK - * EwhTb21lLU9yZzAeFw0wODEyMDgwMjQzMzZaFw0yODA4MjUwMjQzMzZaMEkxCzAJ - * BgNVBAYTAlVTMRMwEQYDVQQIEwpTb21lLVN0YXRlMRIwEAYDVQQHEwlTb21lLUNp - * dHkxETAPBgNVBAoTCFNvbWUtT3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB - * gQDLxDggB76Ip5OwoUNRLdeOha9U3a2ieyNbz5kTU5lFfe5tui2/461uPZ8a+QOX - * 4BdVrhEmV94BKY4FPyH35zboLjfXSKxT1mAOx1Bt9sWF94umxZE1cjyU7vEX8HHj - * 7BvOyk5AQrBt7moO1uWtPA/JuoJPePiJl4kqlRJM2Akq6QIDAQABo4GjMIGgMB0G - * A1UdDgQWBBT6uVG/TOfZhpgz+efLHvEzSfeoFDBxBgNVHSMEajBogBT6uVG/TOfZ - * hpgz+efLHvEzSfeoFKFNpEswSTELMAkGA1UEBhMCVVMxEzARBgNVBAgTClNvbWUt - * U3RhdGUxEjAQBgNVBAcTCVNvbWUtQ2l0eTERMA8GA1UEChMIU29tZS1PcmeCAQAw - * DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQBcIm534U123Hz+rtyYO5uA - * ofd81G6FnTfEAV8Kw9fGyyEbQZclBv34A9JsFKeMvU4OFIaixD7nLZ/NZ+IWbhmZ - * LovmJXyCkOufea73pNiZ+f/4/ScZaIlM/PRycQSqbFNd4j9Wott+08qxHPLpsf3P - * 6Mvf0r1PNTY2hwTJLJmKtg== - * -----END CERTIFICATE--- - */ +import jdk.test.lib.security.CertificateBuilder; +import jdk.test.lib.security.CertificateBuilder.KeyUsage; +import sun.security.x509.AuthorityKeyIdentifierExtension; +import sun.security.x509.GeneralName; +import sun.security.x509.GeneralNames; +import sun.security.x509.KeyIdentifier; +import sun.security.x509.SerialNumber; +import sun.security.x509.X500Name; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManagerFactory; public class IPIdentities { - static Map cookies; - ServerSocket ss; /* * ============================================================= @@ -421,208 +83,14 @@ public class IPIdentities { */ static boolean separateServerThread = true; - /* - * Where do we find the keystores? - */ - static String trusedCertStr = - "-----BEGIN CERTIFICATE-----\n" + - "MIICrDCCAhWgAwIBAgIBADANBgkqhkiG9w0BAQQFADBJMQswCQYDVQQGEwJVUzET\n" + - "MBEGA1UECBMKU29tZS1TdGF0ZTESMBAGA1UEBxMJU29tZS1DaXR5MREwDwYDVQQK\n" + - "EwhTb21lLU9yZzAeFw0wODEyMDgwMjQzMzZaFw0yODA4MjUwMjQzMzZaMEkxCzAJ\n" + - "BgNVBAYTAlVTMRMwEQYDVQQIEwpTb21lLVN0YXRlMRIwEAYDVQQHEwlTb21lLUNp\n" + - "dHkxETAPBgNVBAoTCFNvbWUtT3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB\n" + - "gQDLxDggB76Ip5OwoUNRLdeOha9U3a2ieyNbz5kTU5lFfe5tui2/461uPZ8a+QOX\n" + - "4BdVrhEmV94BKY4FPyH35zboLjfXSKxT1mAOx1Bt9sWF94umxZE1cjyU7vEX8HHj\n" + - "7BvOyk5AQrBt7moO1uWtPA/JuoJPePiJl4kqlRJM2Akq6QIDAQABo4GjMIGgMB0G\n" + - "A1UdDgQWBBT6uVG/TOfZhpgz+efLHvEzSfeoFDBxBgNVHSMEajBogBT6uVG/TOfZ\n" + - "hpgz+efLHvEzSfeoFKFNpEswSTELMAkGA1UEBhMCVVMxEzARBgNVBAgTClNvbWUt\n" + - "U3RhdGUxEjAQBgNVBAcTCVNvbWUtQ2l0eTERMA8GA1UEChMIU29tZS1PcmeCAQAw\n" + - "DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQBcIm534U123Hz+rtyYO5uA\n" + - "ofd81G6FnTfEAV8Kw9fGyyEbQZclBv34A9JsFKeMvU4OFIaixD7nLZ/NZ+IWbhmZ\n" + - "LovmJXyCkOufea73pNiZ+f/4/ScZaIlM/PRycQSqbFNd4j9Wott+08qxHPLpsf3P\n" + - "6Mvf0r1PNTY2hwTJLJmKtg==\n" + - "-----END CERTIFICATE-----"; - - static String serverCertStr = - "-----BEGIN CERTIFICATE-----\n" + - "MIICnzCCAgigAwIBAgIBBzANBgkqhkiG9w0BAQQFADBJMQswCQYDVQQGEwJVUzET\n" + - "MBEGA1UECBMKU29tZS1TdGF0ZTESMBAGA1UEBxMJU29tZS1DaXR5MREwDwYDVQQK\n" + - "EwhTb21lLU9yZzAeFw0wODEyMDgwMzI3NTdaFw0yODA4MjUwMzI3NTdaMHIxCzAJ\n" + - "BgNVBAYTAlVTMRMwEQYDVQQIEwpTb21lLVN0YXRlMRIwEAYDVQQHEwlTb21lLUNp\n" + - "dHkxETAPBgNVBAoTCFNvbWUtT3JnMRMwEQYDVQQLEwpTU0wtU2VydmVyMRIwEAYD\n" + - "VQQDEwlsb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKWsWxw3\n" + - "ot2ZiS2yebiP1Uil5xyEF41pnMasbfnyHR85GdrTch5u7ETMcKTcugAw9qBPPVR6\n" + - "YWrMV9AKf5UoGD+a2ZTyG8gkiH7+nQ89+1dTCLMgM9Q/F0cU0c3qCNgOdU6vvszS\n" + - "7K+peknfwtmsuCRAkKYDVirQMAVALE+r2XSJAgMBAAGjbjBsMAkGA1UdEwQCMAAw\n" + - "CwYDVR0PBAQDAgXgMB0GA1UdDgQWBBTtbtv0tVbI+xoGYT8PCLumBNgWVDAfBgNV\n" + - "HSMEGDAWgBT6uVG/TOfZhpgz+efLHvEzSfeoFDASBgNVHREBAf8ECDAGhwR/AAAB\n" + - "MA0GCSqGSIb3DQEBBAUAA4GBAFJjItCtCBZcjD69wdqfIbKmRFa6eJAjR6LcoDva\n" + - "cKC/sDOLelpspiZ66Zb0Xdv5qQ7QrfOXt3K8QqJKRMdZLF9WfUfy0gJDM32ub91h\n" + - "pu+TmcGPs+6RdrAQcuvU1ZDV9X8SMj7BtKaim4d5sqFw1npncKiA5xFn8vOYwdun\n" + - "nZif\n" + - "-----END CERTIFICATE-----"; - - static String clientCertStr = - "-----BEGIN CERTIFICATE-----\n" + - "MIICnzCCAgigAwIBAgIBBjANBgkqhkiG9w0BAQQFADBJMQswCQYDVQQGEwJVUzET\n" + - "MBEGA1UECBMKU29tZS1TdGF0ZTESMBAGA1UEBxMJU29tZS1DaXR5MREwDwYDVQQK\n" + - "EwhTb21lLU9yZzAeFw0wODEyMDgwMzI3MzRaFw0yODA4MjUwMzI3MzRaMHIxCzAJ\n" + - "BgNVBAYTAlVTMRMwEQYDVQQIEwpTb21lLVN0YXRlMRIwEAYDVQQHEwlTb21lLUNp\n" + - "dHkxETAPBgNVBAoTCFNvbWUtT3JnMRMwEQYDVQQLEwpTU0wtQ2xpZW50MRIwEAYD\n" + - "VQQDEwlsb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALvwQDas\n" + - "JlRO9KNaAC9pIW+5ejqT7KL24Y7HY9gvEjCZLrDyj/gnLSR4KIT3Ab+NRHndO9JV\n" + - "8848slshfe/9M0qxo//GyJu5D3xBNZf52zoFYAUVr1kXkqMQrRYc5AdTr6h2olYq\n" + - "ktP5KOB4z14fSKtcGd3hZ0O6dY31gqxDkkQbAgMBAAGjbjBsMAkGA1UdEwQCMAAw\n" + - "CwYDVR0PBAQDAgXgMB0GA1UdDgQWBBTNu8iFqpG9/R2+zWd8/7PpTKgi5jAfBgNV\n" + - "HSMEGDAWgBT6uVG/TOfZhpgz+efLHvEzSfeoFDASBgNVHREBAf8ECDAGhwR/AAAB\n" + - "MA0GCSqGSIb3DQEBBAUAA4GBACjj9PS+W6XOF7toFMwMOv/AemZeBOpcEF1Ei1Hx\n" + - "HjvB6EOHkMY8tFm5OPzkiWiK3+s3awpSW0jWdzMYwrQJ3/klMsPDpI7PEuirqwHP\n" + - "i5Wyl/vk7jmfWVcBO9MVhPUo4BYl4vS9aj6JA5QbkbkB95LOgT/BowY0WmHeVsXC\n" + - "I9aw\n" + - "-----END CERTIFICATE-----"; - - - static byte serverPrivateExponent[] = { - (byte)0x6e, (byte)0xa7, (byte)0x1b, (byte)0x83, - (byte)0x51, (byte)0x35, (byte)0x9a, (byte)0x44, - (byte)0x7d, (byte)0xf6, (byte)0xe3, (byte)0x89, - (byte)0xa0, (byte)0xd7, (byte)0x90, (byte)0x60, - (byte)0xa1, (byte)0x4e, (byte)0x27, (byte)0x21, - (byte)0xa2, (byte)0x89, (byte)0x74, (byte)0xcc, - (byte)0x9d, (byte)0x75, (byte)0x75, (byte)0x4e, - (byte)0xc7, (byte)0x82, (byte)0xe3, (byte)0xe3, - (byte)0xc3, (byte)0x7d, (byte)0x00, (byte)0x54, - (byte)0xec, (byte)0x36, (byte)0xb1, (byte)0xdf, - (byte)0x91, (byte)0x9c, (byte)0x7a, (byte)0xc0, - (byte)0x62, (byte)0x0a, (byte)0xd6, (byte)0xa9, - (byte)0x22, (byte)0x91, (byte)0x4a, (byte)0x29, - (byte)0x2e, (byte)0x43, (byte)0xfa, (byte)0x8c, - (byte)0xd8, (byte)0xe9, (byte)0xbe, (byte)0xd9, - (byte)0x4f, (byte)0xca, (byte)0x23, (byte)0xc6, - (byte)0xe4, (byte)0x3f, (byte)0xb8, (byte)0x72, - (byte)0xcf, (byte)0x02, (byte)0xfc, (byte)0xf4, - (byte)0x58, (byte)0x34, (byte)0x77, (byte)0x76, - (byte)0xce, (byte)0x22, (byte)0x44, (byte)0x5f, - (byte)0x2d, (byte)0xca, (byte)0xee, (byte)0xf5, - (byte)0x43, (byte)0x56, (byte)0x47, (byte)0x71, - (byte)0x0b, (byte)0x09, (byte)0x6b, (byte)0x5e, - (byte)0xf2, (byte)0xc8, (byte)0xee, (byte)0xd4, - (byte)0x6e, (byte)0x44, (byte)0x92, (byte)0x2a, - (byte)0x7f, (byte)0xcc, (byte)0xa7, (byte)0xd4, - (byte)0x5b, (byte)0xfb, (byte)0xf7, (byte)0x4a, - (byte)0xa9, (byte)0xfb, (byte)0x54, (byte)0x18, - (byte)0xd5, (byte)0xd5, (byte)0x14, (byte)0xba, - (byte)0xa0, (byte)0x1c, (byte)0x13, (byte)0xb3, - (byte)0x37, (byte)0x6b, (byte)0x37, (byte)0x59, - (byte)0xed, (byte)0xdb, (byte)0x6d, (byte)0xb1 - }; - - static byte serverModulus[] = { - (byte)0x00, - (byte)0xa5, (byte)0xac, (byte)0x5b, (byte)0x1c, - (byte)0x37, (byte)0xa2, (byte)0xdd, (byte)0x99, - (byte)0x89, (byte)0x2d, (byte)0xb2, (byte)0x79, - (byte)0xb8, (byte)0x8f, (byte)0xd5, (byte)0x48, - (byte)0xa5, (byte)0xe7, (byte)0x1c, (byte)0x84, - (byte)0x17, (byte)0x8d, (byte)0x69, (byte)0x9c, - (byte)0xc6, (byte)0xac, (byte)0x6d, (byte)0xf9, - (byte)0xf2, (byte)0x1d, (byte)0x1f, (byte)0x39, - (byte)0x19, (byte)0xda, (byte)0xd3, (byte)0x72, - (byte)0x1e, (byte)0x6e, (byte)0xec, (byte)0x44, - (byte)0xcc, (byte)0x70, (byte)0xa4, (byte)0xdc, - (byte)0xba, (byte)0x00, (byte)0x30, (byte)0xf6, - (byte)0xa0, (byte)0x4f, (byte)0x3d, (byte)0x54, - (byte)0x7a, (byte)0x61, (byte)0x6a, (byte)0xcc, - (byte)0x57, (byte)0xd0, (byte)0x0a, (byte)0x7f, - (byte)0x95, (byte)0x28, (byte)0x18, (byte)0x3f, - (byte)0x9a, (byte)0xd9, (byte)0x94, (byte)0xf2, - (byte)0x1b, (byte)0xc8, (byte)0x24, (byte)0x88, - (byte)0x7e, (byte)0xfe, (byte)0x9d, (byte)0x0f, - (byte)0x3d, (byte)0xfb, (byte)0x57, (byte)0x53, - (byte)0x08, (byte)0xb3, (byte)0x20, (byte)0x33, - (byte)0xd4, (byte)0x3f, (byte)0x17, (byte)0x47, - (byte)0x14, (byte)0xd1, (byte)0xcd, (byte)0xea, - (byte)0x08, (byte)0xd8, (byte)0x0e, (byte)0x75, - (byte)0x4e, (byte)0xaf, (byte)0xbe, (byte)0xcc, - (byte)0xd2, (byte)0xec, (byte)0xaf, (byte)0xa9, - (byte)0x7a, (byte)0x49, (byte)0xdf, (byte)0xc2, - (byte)0xd9, (byte)0xac, (byte)0xb8, (byte)0x24, - (byte)0x40, (byte)0x90, (byte)0xa6, (byte)0x03, - (byte)0x56, (byte)0x2a, (byte)0xd0, (byte)0x30, - (byte)0x05, (byte)0x40, (byte)0x2c, (byte)0x4f, - (byte)0xab, (byte)0xd9, (byte)0x74, (byte)0x89 - }; - - static byte clientPrivateExponent[] = { - (byte)0x11, (byte)0xb7, (byte)0x6a, (byte)0x36, - (byte)0x3d, (byte)0x30, (byte)0x37, (byte)0xce, - (byte)0x61, (byte)0x9d, (byte)0x6c, (byte)0x84, - (byte)0x8b, (byte)0xf3, (byte)0x9b, (byte)0x25, - (byte)0x4f, (byte)0x14, (byte)0xc8, (byte)0xa4, - (byte)0xdd, (byte)0x2f, (byte)0xd7, (byte)0x9a, - (byte)0x17, (byte)0xbd, (byte)0x90, (byte)0x19, - (byte)0xf7, (byte)0x05, (byte)0xfd, (byte)0xf2, - (byte)0xd2, (byte)0xc5, (byte)0xf7, (byte)0x77, - (byte)0xbe, (byte)0xea, (byte)0xe2, (byte)0x84, - (byte)0x87, (byte)0x97, (byte)0x3a, (byte)0x41, - (byte)0x96, (byte)0xb6, (byte)0x99, (byte)0xf8, - (byte)0x94, (byte)0x8c, (byte)0x58, (byte)0x71, - (byte)0x51, (byte)0x8c, (byte)0xf4, (byte)0x2a, - (byte)0x20, (byte)0x9e, (byte)0x1a, (byte)0xa0, - (byte)0x26, (byte)0x99, (byte)0x75, (byte)0xd6, - (byte)0x31, (byte)0x53, (byte)0x43, (byte)0x39, - (byte)0xf5, (byte)0x2a, (byte)0xa6, (byte)0x7e, - (byte)0x34, (byte)0x42, (byte)0x51, (byte)0x2a, - (byte)0x40, (byte)0x87, (byte)0x03, (byte)0x88, - (byte)0x43, (byte)0x69, (byte)0xb2, (byte)0x89, - (byte)0x6d, (byte)0x20, (byte)0xbd, (byte)0x7d, - (byte)0x71, (byte)0xef, (byte)0x47, (byte)0x0a, - (byte)0xdf, (byte)0x06, (byte)0xc1, (byte)0x69, - (byte)0x66, (byte)0xa8, (byte)0x22, (byte)0x37, - (byte)0x1a, (byte)0x77, (byte)0x1e, (byte)0xc7, - (byte)0x94, (byte)0x4e, (byte)0x2c, (byte)0x27, - (byte)0x69, (byte)0x45, (byte)0x5e, (byte)0xc8, - (byte)0xf8, (byte)0x0c, (byte)0xb7, (byte)0xf8, - (byte)0xc0, (byte)0x8f, (byte)0x99, (byte)0xc1, - (byte)0xe5, (byte)0x28, (byte)0x9b, (byte)0xf9, - (byte)0x4c, (byte)0x94, (byte)0xc6, (byte)0xb1 - }; - - static byte clientModulus[] = { - (byte)0x00, - (byte)0xbb, (byte)0xf0, (byte)0x40, (byte)0x36, - (byte)0xac, (byte)0x26, (byte)0x54, (byte)0x4e, - (byte)0xf4, (byte)0xa3, (byte)0x5a, (byte)0x00, - (byte)0x2f, (byte)0x69, (byte)0x21, (byte)0x6f, - (byte)0xb9, (byte)0x7a, (byte)0x3a, (byte)0x93, - (byte)0xec, (byte)0xa2, (byte)0xf6, (byte)0xe1, - (byte)0x8e, (byte)0xc7, (byte)0x63, (byte)0xd8, - (byte)0x2f, (byte)0x12, (byte)0x30, (byte)0x99, - (byte)0x2e, (byte)0xb0, (byte)0xf2, (byte)0x8f, - (byte)0xf8, (byte)0x27, (byte)0x2d, (byte)0x24, - (byte)0x78, (byte)0x28, (byte)0x84, (byte)0xf7, - (byte)0x01, (byte)0xbf, (byte)0x8d, (byte)0x44, - (byte)0x79, (byte)0xdd, (byte)0x3b, (byte)0xd2, - (byte)0x55, (byte)0xf3, (byte)0xce, (byte)0x3c, - (byte)0xb2, (byte)0x5b, (byte)0x21, (byte)0x7d, - (byte)0xef, (byte)0xfd, (byte)0x33, (byte)0x4a, - (byte)0xb1, (byte)0xa3, (byte)0xff, (byte)0xc6, - (byte)0xc8, (byte)0x9b, (byte)0xb9, (byte)0x0f, - (byte)0x7c, (byte)0x41, (byte)0x35, (byte)0x97, - (byte)0xf9, (byte)0xdb, (byte)0x3a, (byte)0x05, - (byte)0x60, (byte)0x05, (byte)0x15, (byte)0xaf, - (byte)0x59, (byte)0x17, (byte)0x92, (byte)0xa3, - (byte)0x10, (byte)0xad, (byte)0x16, (byte)0x1c, - (byte)0xe4, (byte)0x07, (byte)0x53, (byte)0xaf, - (byte)0xa8, (byte)0x76, (byte)0xa2, (byte)0x56, - (byte)0x2a, (byte)0x92, (byte)0xd3, (byte)0xf9, - (byte)0x28, (byte)0xe0, (byte)0x78, (byte)0xcf, - (byte)0x5e, (byte)0x1f, (byte)0x48, (byte)0xab, - (byte)0x5c, (byte)0x19, (byte)0xdd, (byte)0xe1, - (byte)0x67, (byte)0x43, (byte)0xba, (byte)0x75, - (byte)0x8d, (byte)0xf5, (byte)0x82, (byte)0xac, - (byte)0x43, (byte)0x92, (byte)0x44, (byte)0x1b - }; + static X509Certificate trustedCert; + + static X509Certificate serverCert; + + static X509Certificate clientCert; + + static KeyPair serverKeys; + static KeyPair clientKeys; static char passphrase[] = "passphrase".toCharArray(); @@ -639,7 +107,7 @@ public class IPIdentities { /* * Turn on SSL debugging? */ - static boolean debug = false; + static boolean debug = Boolean.getBoolean("test.debug"); private SSLServerSocket sslServerSocket = null; @@ -650,8 +118,8 @@ public class IPIdentities { * to avoid infinite hangs. */ void doServerSide() throws Exception { - SSLContext context = getSSLContext(trusedCertStr, serverCertStr, - serverModulus, serverPrivateExponent, passphrase); + SSLContext context = getSSLContext(trustedCert, serverCert, + serverKeys, passphrase); SSLServerSocketFactory sslssf = context.getServerSocketFactory(); // doClientSide() connects to the loopback address @@ -706,8 +174,8 @@ void doServerSide() throws Exception { void doClientSide() throws Exception { SSLContext reservedSSLContext = SSLContext.getDefault(); try { - SSLContext context = getSSLContext(trusedCertStr, clientCertStr, - clientModulus, clientPrivateExponent, passphrase); + SSLContext context = getSSLContext(trustedCert, clientCert, + clientKeys, passphrase); SSLContext.setDefault(context); /* @@ -755,6 +223,65 @@ void doClientSide() throws Exception { volatile Exception serverException = null; volatile Exception clientException = null; + private static X509Certificate createTrustedCert(KeyPair caKeys) throws Exception { + SecureRandom random = new SecureRandom(); + + KeyIdentifier kid = new KeyIdentifier(caKeys.getPublic()); + GeneralNames gns = new GeneralNames(); + GeneralName name = new GeneralName(new X500Name( + "O=Some-Org, L=Some-City, ST=Some-State, C=US")); + gns.add(name); + BigInteger serialNumber = BigInteger.valueOf(random.nextLong(1000000)+1); + return CertificateBuilder.newCertificateBuilder( + "O=Some-Org, L=Some-City, ST=Some-State, C=US", + caKeys.getPublic(), caKeys.getPublic()) + .setSerialNumber(serialNumber) + .addExtension(new AuthorityKeyIdentifierExtension(kid, gns, + new SerialNumber(serialNumber))) + .addBasicConstraintsExt(true, true, -1) + .setOneHourValidity() + .build(null, caKeys.getPrivate(), "MD5WithRSA"); + } + + private static void setupCertificates() throws Exception { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); + KeyPair caKeys = kpg.generateKeyPair(); + serverKeys = kpg.generateKeyPair(); + clientKeys = kpg.generateKeyPair(); + + trustedCert = createTrustedCert(caKeys); + if (debug) { + System.out.println("----------- Trusted Cert -----------"); + CertificateBuilder.printCertificate(trustedCert, System.out); + } + + serverCert = CertificateBuilder.newCertificateBuilder( + "O=Some-Org, L=Some-City, ST=Some-State, C=US", + serverKeys.getPublic(), caKeys.getPublic(), + KeyUsage.DIGITAL_SIGNATURE, KeyUsage.NONREPUDIATION, KeyUsage.KEY_ENCIPHERMENT) + .addBasicConstraintsExt(false, false, -1) + .addExtension(CertificateBuilder.createIPSubjectAltNameExt(true, "127.0.0.1")) + .setOneHourValidity() + .build(trustedCert, caKeys.getPrivate(), "MD5WithRSA"); + if (debug) { + System.out.println("----------- Server Cert -----------"); + CertificateBuilder.printCertificate(serverCert, System.out); + } + + clientCert = CertificateBuilder.newCertificateBuilder( + "CN=localhost, OU=SSL-Client, O=Some-Org, L=Some-City, ST=Some-State, C=US", + clientKeys.getPublic(), caKeys.getPublic(), + KeyUsage.DIGITAL_SIGNATURE, KeyUsage.NONREPUDIATION, KeyUsage.KEY_ENCIPHERMENT) + .addExtension(CertificateBuilder.createIPSubjectAltNameExt(true, "127.0.0.1")) + .addBasicConstraintsExt(false, false, -1) + .setOneHourValidity() + .build(trustedCert, caKeys.getPrivate(), "MD5WithRSA"); + if (debug) { + System.out.println("----------- Client Cert -----------"); + CertificateBuilder.printCertificate(clientCert, System.out); + } + } + public static void main(String args[]) throws Exception { // MD5 is used in this test case, don't disable MD5 algorithm. Security.setProperty("jdk.certpath.disabledAlgorithms", @@ -762,8 +289,11 @@ public static void main(String args[]) throws Exception { Security.setProperty("jdk.tls.disabledAlgorithms", "SSLv3, RC4, DH keySize < 768"); - if (debug) + if (debug) { System.setProperty("javax.net.debug", "all"); + } + + setupCertificates(); /* * Start the tests. @@ -855,54 +385,32 @@ public void run() { } // get the ssl context - private static SSLContext getSSLContext(String trusedCertStr, - String keyCertStr, byte[] modulus, - byte[] privateExponent, char[] passphrase) throws Exception { - - // generate certificate from cert string - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - - ByteArrayInputStream is = - new ByteArrayInputStream(trusedCertStr.getBytes()); - Certificate trusedCert = cf.generateCertificate(is); - is.close(); + private static SSLContext getSSLContext(X509Certificate trustedCert, + X509Certificate keyCert, KeyPair key, char[] passphrase) throws Exception { // create a key store - KeyStore ks = KeyStore.getInstance("JKS"); + KeyStore ks = KeyStore.getInstance("PKCS12"); ks.load(null, null); // import the trused cert - ks.setCertificateEntry("RSA Export Signer", trusedCert); - - if (keyCertStr != null) { - // generate the private key. - RSAPrivateKeySpec priKeySpec = new RSAPrivateKeySpec( - new BigInteger(modulus), - new BigInteger(privateExponent)); - KeyFactory kf = KeyFactory.getInstance("RSA"); - RSAPrivateKey priKey = - (RSAPrivateKey)kf.generatePrivate(priKeySpec); - - // generate certificate chain - is = new ByteArrayInputStream(keyCertStr.getBytes()); - Certificate keyCert = cf.generateCertificate(is); - is.close(); + ks.setCertificateEntry("RSA Export Signer", trustedCert); + if (keyCert != null) { Certificate[] chain = new Certificate[2]; chain[0] = keyCert; - chain[1] = trusedCert; + chain[1] = trustedCert; // import the key entry. - ks.setKeyEntry("Whatever", priKey, passphrase, chain); + ks.setKeyEntry("Whatever", key.getPrivate(), passphrase, chain); } // create SSL context TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); tmf.init(ks); - SSLContext ctx = SSLContext.getInstance("TLS"); + SSLContext ctx = SSLContext.getInstance("TLSv1.2"); - if (keyCertStr != null) { + if (keyCert != null) { KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); kmf.init(ks, passphrase); diff --git a/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/Identities.java b/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/Identities.java index 3764b3de68819..53960c058959c 100644 --- a/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/Identities.java +++ b/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/Identities.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -893,7 +893,7 @@ private static SSLContext getSSLContext(String trusedCertStr, TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); tmf.init(ks); - SSLContext ctx = SSLContext.getInstance("TLS"); + SSLContext ctx = SSLContext.getInstance("TLSv1.2"); if (keyCertStr != null) { KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); diff --git a/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/TEST.properties b/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/TEST.properties new file mode 100644 index 0000000000000..e1ed216f21db8 --- /dev/null +++ b/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/TEST.properties @@ -0,0 +1,3 @@ +modules = \ + java.base/sun.security.util \ + java.base/sun.security.x509 diff --git a/test/jdk/sun/net/www/protocol/jar/FileURLConnectionLeak.java b/test/jdk/sun/net/www/protocol/jar/FileURLConnectionLeak.java new file mode 100644 index 0000000000000..5ff687ca42df8 --- /dev/null +++ b/test/jdk/sun/net/www/protocol/jar/FileURLConnectionLeak.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6956385 + * @summary JarURLConnection may fail to close its underlying FileURLConnection + * @run main/othervm FileURLConnectionLeak + */ + +import java.net.URI; +import java.net.URLConnection; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.jar.Attributes; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +public class FileURLConnectionLeak { + public static void main(String[] args) throws Exception { + URLConnection.setDefaultUseCaches("file", false); + URLConnection.setDefaultUseCaches("jar", false); + var jar = Path.of("x.jar").toAbsolutePath(); + var mani = new Manifest(); + mani.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); + try (var os = Files.newOutputStream(jar); var jos = new JarOutputStream(os, mani)) {} + var u = URI.create("jar:" + jar.toUri() + "!/META-INF/MANIFEST.MF").toURL(); + // FileURLConnection.is not used, so was always fine: + try (var is = u.openStream()) { + is.transferTo(System.out); + } + // FileURLConnection.is opened implicitly: + var conn = u.openConnection(); + conn.getLastModified(); + // Idiom to close URLConnection (cf. JDK-8224095), which must also close the other stream: + conn.getInputStream().close(); + var fds = Path.of("/proc/" + ProcessHandle.current().pid() + "/fd"); + if (Files.isDirectory(fds)) { + // Linux: verify that x.jar is not open + for (var fd : (Iterable) Files.list(fds)::iterator) { + if (Files.isSymbolicLink(fd)) { + var file = Files.readSymbolicLink(fd); + if (file.equals(jar)) { + throw new IllegalStateException("Still held open " + jar + " from " + fd); + } + } + } + } + // Windows: verify that mandatory file locks do not prevent deletion + Files.delete(jar); + } +} diff --git a/test/jdk/sun/nio/cs/StreamEncoderClose.java b/test/jdk/sun/nio/cs/StreamEncoderClose.java index 68b4111fb4763..8e0d259536cfd 100644 --- a/test/jdk/sun/nio/cs/StreamEncoderClose.java +++ b/test/jdk/sun/nio/cs/StreamEncoderClose.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,15 @@ import java.io.*; public class StreamEncoderClose { + private static void ck(String s, int actual, int expected) + throws IOException { + if (actual != expected) { + String msg = String.format("%s: actual (%d) != expected (%d)%n", + s, actual, expected); + throw new IOException(msg); + } + } + public static void main( String arg[] ) throws Exception { byte[] expected = {(byte)0x1b,(byte)0x24,(byte)0x42, (byte)0x30,(byte)0x6c, @@ -46,13 +55,10 @@ public static void main( String arg[] ) throws Exception { //double check, probably not necessary byte[] out = baos.toByteArray(); - if (out.length != expected.length) { - throw new IOException("Failed"); - } + ck("Lengths are unequal", out.length, expected.length); for (int i = 0; i < out.length; i++) { //System.out.printf("(byte)0x%x,", out[i] & 0xff); - if (out[i] != expected[i]) - throw new IOException("Failed"); + ck("Values are unequal", out[i], expected[i]); } } diff --git a/test/jdk/sun/security/ec/TestEC.java b/test/jdk/sun/security/ec/TestEC.java index dacb67ce892fc..3720df03e1fdf 100644 --- a/test/jdk/sun/security/ec/TestEC.java +++ b/test/jdk/sun/security/ec/TestEC.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,7 +34,6 @@ * @library ../pkcs11 * @library ../pkcs11/ec * @library ../pkcs11/sslecc - * @library ../../../java/security/testlibrary * @library ../../../javax/net/ssl/TLSCommon * @modules jdk.crypto.cryptoki/sun.security.pkcs11.wrapper * @run main/othervm -Djdk.tls.namedGroups="secp256r1" TestEC @@ -45,6 +44,7 @@ import java.security.NoSuchProviderException; import java.security.Provider; import java.security.Security; +import jdk.test.lib.security.ProvidersSnapshot; /* * Leverage the collection of EC tests used by PKCS11 diff --git a/test/jdk/sun/security/krb5/Krb5NameEquals.java b/test/jdk/sun/security/krb5/Krb5NameEquals.java index 0851423a7fded..421c290453cff 100644 --- a/test/jdk/sun/security/krb5/Krb5NameEquals.java +++ b/test/jdk/sun/security/krb5/Krb5NameEquals.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,57 +22,65 @@ */ /* + * @test * @bug 4634392 - * @summary JDK code doesn't respect contract for equals and hashCode + * @summary Ensure the GSSName has the correct impl which respects + * the contract for equals and hashCode across different configurations. + * @library /test/lib * @author Andrew Fan + * + * @run main/othervm -Djava.security.krb5.realm=R -Djava.security.krb5.kdc=127.0.0.1 Krb5NameEquals + * @run main/othervm -Dsun.security.jgss.native=true Krb5NameEquals */ -import org.ietf.jgss.*; +import jtreg.SkippedException; +import org.ietf.jgss.GSSManager; +import org.ietf.jgss.GSSName; +import org.ietf.jgss.Oid; public class Krb5NameEquals { - private static String NAME_STR1 = "service@localhost"; - private static String NAME_STR2 = "service2@localhost"; + private static final String NAME_STR1 = "service@localhost"; + private static final String NAME_STR2 = "service2@localhost"; private static final Oid MECH; static { - Oid temp = null; try { - temp = new Oid("1.2.840.113554.1.2.2"); // KRB5 + MECH = new Oid("1.2.840.113554.1.2.2"); // KRB5 } catch (Exception e) { // should never happen + throw new RuntimeException("Exception initialising Oid", e); } - MECH = temp; } public static void main(String[] argv) throws Exception { - GSSManager mgr = GSSManager.getInstance(); + final GSSManager mgr = GSSManager.getInstance(); + + // Checking if native GSS is installed, throwing skip exception if it's not. + if (Boolean.getBoolean("sun.security.jgss.native")) { + final var mechs = mgr.getMechs(); + if (mechs == null || mechs.length == 0) { + throw new SkippedException("NativeGSS not supported"); + } + } - boolean result = true; // Create GSSName and check their equals(), hashCode() impl - GSSName name1 = mgr.createName(NAME_STR1, - GSSName.NT_HOSTBASED_SERVICE, MECH); - GSSName name2 = mgr.createName(NAME_STR2, - GSSName.NT_HOSTBASED_SERVICE, MECH); - GSSName name3 = mgr.createName(NAME_STR1, - GSSName.NT_HOSTBASED_SERVICE, MECH); + final GSSName name1 = mgr.createName(NAME_STR1, + GSSName.NT_HOSTBASED_SERVICE, MECH); + final GSSName name2 = mgr.createName(NAME_STR2, + GSSName.NT_HOSTBASED_SERVICE, MECH); + final GSSName name3 = mgr.createName(NAME_STR1, + GSSName.NT_HOSTBASED_SERVICE, MECH); - if (!name1.equals(name1) || !name1.equals(name3) || - !name1.equals((Object) name1) || - !name1.equals((Object) name3)) { - System.out.println("Error: should be the same name"); - result = false; + if (!name1.equals(name3) || !name1.equals((Object) name3)) { + throw new RuntimeException("Error: should be the same name"); } else if (name1.hashCode() != name3.hashCode()) { - System.out.println("Error: should have same hash"); - result = false; + throw new RuntimeException("Error: should have same hash"); } if (name1.equals(name2) || name1.equals((Object) name2)) { - System.out.println("Error: should be different names"); - result = false; + throw new RuntimeException("Error: should be different names"); } - if (result) { - System.out.println("Done"); - } else System.exit(1); + System.out.println("Done"); } } diff --git a/test/jdk/sun/security/krb5/MicroTime.java b/test/jdk/sun/security/krb5/MicroTime.java index 8b6b92324156c..84a88cedf83d5 100644 --- a/test/jdk/sun/security/krb5/MicroTime.java +++ b/test/jdk/sun/security/krb5/MicroTime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,9 +44,10 @@ public static void main(String[] args) throws Exception { count++; } } - // We believe a nice KerberosTime can at least tell the - // difference of 100 musec. - if (count < 10000) { + // Before JDK-6882687, KerberosTime was measured in milliseconds. + // Now it's in microseconds. We should be able to record more than + // 1000 distinct KerberosTime values within one second. + if (count < 1001) { throw new Exception("What? only " + (1000000/count) + " musec precision?"); } diff --git a/test/jdk/sun/security/krb5/auto/SaslBasic.java b/test/jdk/sun/security/krb5/auto/SaslBasic.java index 0aebdefbe04af..89eb22383f272 100644 --- a/test/jdk/sun/security/krb5/auto/SaslBasic.java +++ b/test/jdk/sun/security/krb5/auto/SaslBasic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,12 +32,11 @@ * @run main/othervm -Djdk.net.hosts.file=TestHosts SaslBasic unbound auth-conf * @run main/othervm -Djdk.net.hosts.file=TestHosts SaslBasic bound auth */ -import java.io.IOException; +import static jdk.test.lib.Asserts.assertEquals; + import java.util.Arrays; import java.util.HashMap; import javax.security.auth.callback.Callback; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.sasl.*; // The basic krb5 test skeleton you can copy from @@ -61,15 +60,12 @@ public static void main(String[] args) throws Exception { srvprops.put(Sasl.QOP, "auth,auth-int,auth-conf"); SaslServer ss = Sasl.createSaslServer("GSSAPI", "server", bound? name: null, srvprops, - new CallbackHandler() { - public void handle(Callback[] callbacks) - throws IOException, UnsupportedCallbackException { - for (Callback cb : callbacks) { - if (cb instanceof RealmCallback) { - ((RealmCallback) cb).setText(OneKDC.REALM); - } else if (cb instanceof AuthorizeCallback) { - ((AuthorizeCallback) cb).setAuthorized(true); - } + callbacks -> { + for (Callback cb : callbacks) { + if (cb instanceof RealmCallback) { + ((RealmCallback) cb).setText(OneKDC.REALM); + } else if (cb instanceof AuthorizeCallback) { + ((AuthorizeCallback) cb).setAuthorized(true); } } }); @@ -89,28 +85,85 @@ public void handle(Callback[] callbacks) String boundName = (String)ss.getNegotiatedProperty( Sasl.BOUND_SERVER_NAME); if (!boundName.equals(name)) { - throw new Exception("Wrong bound server name"); + throw new RuntimeException("Wrong bound server name"); } } Object key = ss.getNegotiatedProperty( "com.sun.security.jgss.inquiretype.krb5_get_session_key"); if (key == null) { - throw new Exception("Extended negotiated property not read"); + throw new RuntimeException("Extended negotiated property not read"); } if (args[1].equals("auth")) { // 8170732. These are the maximum size bytes after jgss/krb5 wrap. if (lastClientToken[17] != 0 || lastClientToken[18] != 0 || lastClientToken[19] != 0) { - throw new Exception("maximum size for auth must be 0"); + throw new RuntimeException("maximum size for auth must be 0"); } + testWrapUnwrapNoSecLayer(sc, ss); } else { - byte[] hello = "hello".getBytes(); - token = sc.wrap(hello, 0, hello.length); - token = ss.unwrap(token, 0, token.length); - if (!Arrays.equals(hello, token)) { - throw new Exception("Message altered"); - } + testWrapUnwrapWithSecLayer(sc, ss); + } + } + + private static void testWrapUnwrapWithSecLayer(SaslClient sc, SaslServer ss) + throws SaslException { + byte[] token; + byte[] hello = "hello".getBytes(); + + // test client wrap and server unwrap + token = sc.wrap(hello, 0, hello.length); + token = ss.unwrap(token, 0, token.length); + + if (!Arrays.equals(hello, token)) { + throw new RuntimeException("Client message altered"); + } + + // test server wrap and client unwrap + token = ss.wrap(hello, 0, hello.length); + token = sc.unwrap(token, 0, token.length); + + if (!Arrays.equals(hello, token)) { + throw new RuntimeException("Server message altered"); + } + } + + private static void testWrapUnwrapNoSecLayer(SaslClient sc, SaslServer ss) + throws SaslException { + byte[] clntBuf = new byte[]{0, 1, 2, 3}; + byte[] srvBuf = new byte[]{10, 11, 12, 13}; + String expectedError = "No security layer negotiated"; + + try { + sc.wrap(clntBuf, 0, clntBuf.length); + throw new RuntimeException( + "client wrap should not be allowed w/no security layer"); + } catch (IllegalStateException e) { + assertEquals(expectedError, e.getMessage()); + } + + try { + ss.wrap(srvBuf, 0, srvBuf.length); + throw new RuntimeException( + "server wrap should not be allowed w/no security layer"); + } catch (IllegalStateException e) { + assertEquals(expectedError, e.getMessage()); + } + + try { + sc.unwrap(clntBuf, 0, clntBuf.length); + throw new RuntimeException( + "client unwrap should not be allowed w/no security layer"); + } catch (IllegalStateException e) { + assertEquals(expectedError, e.getMessage()); + } + + try { + ss.unwrap(srvBuf, 0, srvBuf.length); + throw new RuntimeException( + "server unwrap should not be allowed w/no security layer"); + } catch (IllegalStateException e) { + assertEquals(expectedError, e.getMessage()); } } } diff --git a/test/jdk/sun/security/krb5/auto/TEST.properties b/test/jdk/sun/security/krb5/auto/TEST.properties index a3941f6dc04bb..402754f2f3c07 100644 --- a/test/jdk/sun/security/krb5/auto/TEST.properties +++ b/test/jdk/sun/security/krb5/auto/TEST.properties @@ -1,12 +1,12 @@ -modules java.base/jdk.internal.misc \ - java.base/sun.security.util \ - java.security.jgss/sun.security.jgss \ - java.security.jgss/sun.security.jgss.krb5 \ - java.security.jgss/sun.security.krb5:+open \ - java.security.jgss/sun.security.krb5.internal:+open \ - java.security.jgss/sun.security.krb5.internal.ccache \ - java.security.jgss/sun.security.krb5.internal.rcache \ - java.security.jgss/sun.security.krb5.internal.crypto \ - java.security.jgss/sun.security.krb5.internal.ktab \ - jdk.security.auth \ - jdk.security.jgss +modules = java.base/jdk.internal.misc \ + java.base/sun.security.util \ + java.security.jgss/sun.security.jgss \ + java.security.jgss/sun.security.jgss.krb5 \ + java.security.jgss/sun.security.krb5:+open \ + java.security.jgss/sun.security.krb5.internal:+open \ + java.security.jgss/sun.security.krb5.internal.ccache \ + java.security.jgss/sun.security.krb5.internal.rcache \ + java.security.jgss/sun.security.krb5.internal.crypto \ + java.security.jgss/sun.security.krb5.internal.ktab \ + jdk.security.auth \ + jdk.security.jgss diff --git a/test/jdk/sun/security/krb5/runNameEquals.sh b/test/jdk/sun/security/krb5/runNameEquals.sh deleted file mode 100644 index ecfd65a2fe4a9..0000000000000 --- a/test/jdk/sun/security/krb5/runNameEquals.sh +++ /dev/null @@ -1,127 +0,0 @@ -# -# Copyright (c) 2009, 2024, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -# @test -# @bug 6317711 6944847 8024046 -# @summary Ensure the GSSName has the correct impl which respects -# the contract for equals and hashCode across different configurations. - -# set a few environment variables so that the shell-script can run stand-alone -# in the source directory - -if [ "${TESTSRC}" = "" ] ; then - TESTSRC="." -fi - -if [ "${TESTCLASSES}" = "" ] ; then - TESTCLASSES="." -fi - -if [ "${TESTJAVA}" = "" ] ; then - echo "TESTJAVA not set. Test cannot execute." - echo "FAILED!!!" - exit 1 -fi - -if [ "${COMPILEJAVA}" = "" ]; then - COMPILEJAVA="${TESTJAVA}" -fi - -NATIVE=false - -# set platform-dependent variables -OS=`uname -s` -case "$OS" in - Linux | Darwin ) - PATHSEP=":" - FILESEP="/" - NATIVE=true - # Not all *nix has native GSS libs installed - krb5-config --libs 2> /dev/null - if [ $? != 0 ]; then - # Fedora has a different path - /usr/kerberos/bin/krb5-config --libs 2> /dev/null - if [ $? != 0 ]; then - NATIVE=false - fi - fi - ;; - AIX ) - PATHSEP=":" - FILESEP="/" - ;; - CYGWIN* ) - PATHSEP=";" - FILESEP="/" - ;; - Windows* ) - PATHSEP=";" - FILESEP="\\" - ;; - * ) - echo "Unrecognized system!" - exit 1; - ;; -esac - -TEST=Krb5NameEquals - -${COMPILEJAVA}${FILESEP}bin${FILESEP}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \ - -d ${TESTCLASSES}${FILESEP} \ - ${TESTSRC}${FILESEP}${TEST}.java - -EXIT_STATUS=0 - -if [ "${NATIVE}" = "true" ] ; then - echo "Testing native provider" - ${TESTJAVA}${FILESEP}bin${FILESEP}java ${TESTVMOPTS} ${TESTJAVAOPTS} \ - -classpath ${TESTCLASSES} \ - -Dsun.security.jgss.native=true \ - ${TEST} - if [ $? != 0 ] ; then - echo "Native provider fails" - EXIT_STATUS=1 - if [ "$OS" = "Linux" -a `arch` = "x86_64" ]; then - ${TESTJAVA}${FILESEP}bin${FILESEP}java -XshowSettings:properties -version 2> allprop - cat allprop | grep sun.arch.data.model | grep 32 - if [ "$?" = "0" ]; then - echo "Running 32-bit JDK on 64-bit Linux. Maybe only 64-bit library is installed." - echo "Please manually check if this is the case. Treated as PASSED now." - EXIT_STATUS=0 - fi - fi - fi -fi - -echo "Testing java provider" -${TESTJAVA}${FILESEP}bin${FILESEP}java ${TESTVMOPTS} ${TESTJAVAOPTS} \ - -classpath ${TESTCLASSES} \ - -Djava.security.krb5.realm=R \ - -Djava.security.krb5.kdc=127.0.0.1 \ - ${TEST} -if [ $? != 0 ] ; then - echo "Java provider fails" - EXIT_STATUS=1 -fi - -exit ${EXIT_STATUS} diff --git a/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java b/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java index 1c99897f53bc5..1f636dea78ab0 100644 --- a/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java +++ b/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,7 @@ * 8223499 8225392 8232019 8234245 8233223 8225068 8225069 8243321 8243320 * 8243559 8225072 8258630 8259312 8256421 8225081 8225082 8225083 8245654 * 8305975 8304760 8307134 8295894 8314960 8317373 8317374 8318759 8319187 - * 8321408 8316138 8341057 + * 8321408 8316138 8341057 8303770 8350498 8359170 8361212 * @summary Check root CA entries in cacerts file */ import java.io.ByteArrayInputStream; @@ -47,12 +47,12 @@ public class VerifyCACerts { + File.separator + "security" + File.separator + "cacerts"; // The numbers of certs now. - private static final int COUNT = 112; + private static final int COUNT = 109; // SHA-256 of cacerts, can be generated with // shasum -a 256 cacerts | sed -e 's/../&:/g' | tr '[:lower:]' '[:upper:]' | cut -c1-95 private static final String CHECKSUM - = "8F:E0:6F:7F:21:59:33:A6:43:F3:48:FD:A3:4A:8E:28:35:AA:DD:6E:A5:43:56:F1:28:34:48:DF:5C:D2:7C:72"; + = "F2:0C:60:47:49:FA:13:2A:03:A4:52:20:AD:46:7C:D0:3F:3D:A7:59:D6:27:E9:9B:CC:D4:5A:04:8D:2A:DE:9F"; // Hex formatter to upper case with ":" delimiter private static final HexFormat HEX = HexFormat.ofDelimiter(":").withUpperCase(); @@ -69,10 +69,6 @@ public class VerifyCACerts { "ED:F7:EB:BC:A2:7A:2A:38:4D:38:7B:7D:40:10:C6:66:E2:ED:B4:84:3E:4C:29:B4:AE:1D:5B:93:32:E6:B2:4D"); put("camerfirmachambersca [jdk]", "06:3E:4A:FA:C4:91:DF:D3:32:F3:08:9B:85:42:E9:46:17:D8:93:D7:FE:94:4E:10:A7:93:7E:E2:9D:96:93:C0"); - put("camerfirmachambersignca [jdk]", - "13:63:35:43:93:34:A7:69:80:16:A0:D3:24:DE:72:28:4E:07:9D:7B:52:20:BB:8F:BD:74:78:16:EE:BE:BA:CA"); - put("camerfirmachamberscommerceca [jdk]", - "0C:25:8A:12:A5:67:4A:EF:25:F2:8B:A7:DC:FA:EC:EE:A3:48:E5:41:E6:F5:CC:4E:E6:3B:71:B3:61:60:6A:C3"); put("certumca [jdk]", "D8:E0:FE:BC:1D:B2:E3:8D:00:94:0F:37:D2:7D:41:34:4D:99:3E:73:4B:99:D5:65:6D:97:78:D4:D8:14:36:24"); put("certumtrustednetworkca [jdk]", @@ -95,8 +91,6 @@ public class VerifyCACerts { "68:7F:A4:51:38:22:78:FF:F0:C8:B1:1F:8D:43:D5:76:67:1C:6E:B2:BC:EA:B4:13:FB:83:D9:65:D0:6D:2F:F2"); put("addtrustqualifiedca [jdk]", "80:95:21:08:05:DB:4B:BC:35:5E:44:28:D8:FD:6E:C2:CD:E3:AB:5F:B9:7A:99:42:98:8E:B8:F4:DC:D0:60:16"); - put("baltimorecybertrustca [jdk]", - "16:AF:57:A9:F6:76:B0:AB:12:60:95:AA:5E:BA:DE:F2:2A:B3:11:19:D6:44:AC:95:CD:4B:93:DB:F3:F2:6A:EB"); put("digicertglobalrootca [jdk]", "43:48:A0:E9:44:4C:78:CB:26:5E:05:8D:5E:89:44:B4:D8:4F:96:62:BD:26:DB:25:7F:89:34:A4:43:C7:01:61"); put("digicertglobalrootg2 [jdk]", @@ -199,14 +193,6 @@ public class VerifyCACerts { "43:DF:57:74:B0:3E:7F:EF:5F:E4:0D:93:1A:7B:ED:F1:BB:2E:6B:42:73:8C:4E:6D:38:41:10:3D:3A:A7:F3:39"); put("entrustevca [jdk]", "73:C1:76:43:4F:1B:C6:D5:AD:F4:5B:0E:76:E7:27:28:7C:8D:E5:76:16:C1:E6:E6:14:1A:2B:2C:BC:7D:8E:4C"); - put("affirmtrustnetworkingca [jdk]", - "0A:81:EC:5A:92:97:77:F1:45:90:4A:F3:8D:5D:50:9F:66:B5:E2:C5:8F:CD:B5:31:05:8B:0E:17:F3:F0:B4:1B"); - put("affirmtrustpremiumca [jdk]", - "70:A7:3F:7F:37:6B:60:07:42:48:90:45:34:B1:14:82:D5:BF:0E:69:8E:CC:49:8D:F5:25:77:EB:F2:E9:3B:9A"); - put("affirmtrustcommercialca [jdk]", - "03:76:AB:1D:54:C5:F9:80:3C:E4:B2:E2:01:A0:EE:7E:EF:7B:57:B6:36:E8:A9:3C:9B:8D:48:60:C9:6F:5F:A7"); - put("affirmtrustpremiumeccca [jdk]", - "BD:71:FD:F6:DA:97:E4:CF:62:D1:64:7A:DD:25:81:B0:7D:79:AD:F8:39:7E:B4:EC:BA:9C:5E:84:88:82:14:23"); put("ttelesecglobalrootclass3ca [jdk]", "FD:73:DA:D3:1C:64:4F:F1:B4:3B:EF:0C:CD:DA:96:71:0B:9C:D9:87:5E:CA:7E:31:70:7A:F3:E9:6D:52:2B:BD"); put("ttelesecglobalrootclass2ca [jdk]", @@ -285,6 +271,14 @@ public class VerifyCACerts { "C3:2F:FD:9F:46:F9:36:D1:6C:36:73:99:09:59:43:4B:9A:D6:0A:AF:BB:9E:7C:F3:36:54:F1:44:CC:1B:A1:43"); put("ssltlsrootrsa2022 [jdk]", "8F:AF:7D:2E:2C:B4:70:9B:B8:E0:B3:36:66:BF:75:A5:DD:45:B5:DE:48:0F:8E:A8:D4:BF:E6:BE:BC:17:F2:ED"); + put("sectigotlsrootr46 [jdk]", + "7B:B6:47:A6:2A:EE:AC:88:BF:25:7A:A5:22:D0:1F:FE:A3:95:E0:AB:45:C7:3F:93:F6:56:54:EC:38:F2:5A:06"); + put("sectigotlsroote46 [jdk]", + "C9:0F:26:F0:FB:1B:40:18:B2:22:27:51:9B:5C:A2:B5:3E:2C:A5:B3:BE:5C:F1:8E:FE:1B:EF:47:38:0C:53:83"); + put("sectigocodesignrootr46 [jdk]", + "7E:76:26:0A:E6:9A:55:D3:F0:60:B0:FD:18:B2:A8:C0:14:43:C8:7B:60:79:10:30:C9:FA:0B:05:85:10:1A:38"); + put("sectigocodesignroote46 [jdk]", + "8F:63:71:D8:CC:5A:A7:CA:14:96:67:A9:8B:54:96:39:89:51:E4:31:9F:7A:FB:CC:6A:66:0D:67:3E:43:8D:0B"); } }; diff --git a/test/jdk/sun/security/mscapi/AllTypes.java b/test/jdk/sun/security/mscapi/AllTypes.java index f9c9886070258..9f5fb2f13d69d 100644 --- a/test/jdk/sun/security/mscapi/AllTypes.java +++ b/test/jdk/sun/security/mscapi/AllTypes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -45,33 +45,12 @@ public static void main(String[] args) throws Exception { var nr = test("windows-root"); var nmu = test("windows-my-currentuser"); var nru = test("windows-root-currentuser"); - var hasAdminPrivileges = detectIfRunningWithAdminPrivileges(); - var nmm = adminTest("windows-my-localmachine", hasAdminPrivileges); - var nrm = adminTest("windows-root-localmachine", hasAdminPrivileges); + var nmm = test("windows-my-localmachine"); + var nrm = test("windows-root-localmachine"); Asserts.assertEQ(nm, nmu); Asserts.assertEQ(nr, nru); } - private static boolean detectIfRunningWithAdminPrivileges() { - try { - Process p = Runtime.getRuntime().exec("reg query \"HKU\\S-1-5-19\""); - p.waitFor(); - return (p.exitValue() == 0); - } - catch (Exception ex) { - System.out.println("Warning: unable to detect admin privileges, assuming none"); - return false; - } - } - - private static List adminTest(String type, boolean hasAdminPrivileges) throws Exception { - if (hasAdminPrivileges) { - return test(type); - } - System.out.println("Ignoring: " + type + " as it requires admin privileges"); - return null; - } - private static List test(String type) throws Exception { var stdType = "Windows-" + type.substring(8).toUpperCase(Locale.ROOT); SecurityTools.keytool("-storetype " + type + " -list") diff --git a/test/jdk/sun/security/mscapi/nonUniqueAliases/NonUniqueAliases.java b/test/jdk/sun/security/mscapi/nonUniqueAliases/NonUniqueAliases.java index b78ade34eae3d..9384ac4b1311b 100644 --- a/test/jdk/sun/security/mscapi/nonUniqueAliases/NonUniqueAliases.java +++ b/test/jdk/sun/security/mscapi/nonUniqueAliases/NonUniqueAliases.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,20 +27,32 @@ * @requires os.family == "windows" * @library /test/lib * @summary Test "keytool -list" displays correctly same named certificates - * @ignore Uses certutil.exe that isn't guaranteed to be installed */ import jdk.test.lib.process.ProcessTools; +import java.io.IOException; import java.security.KeyStore; import java.util.Collections; +import jtreg.SkippedException; public class NonUniqueAliases { public static void main(String[] args) throws Throwable { - try { - String testSrc = System.getProperty("test.src", "."); + runTest(); + } catch (IOException ex) { + // It uses certutil.exe that isn't guaranteed to be installed + String certutilMsg = "Cannot run program \"certutil\""; + if (ex.getMessage().contains(certutilMsg)) { + throw new SkippedException("certutil is not installed"); + } + throw ex; + } + } + private static void runTest() throws Exception { + String testSrc = System.getProperty("test.src", "."); + try { // removing the alias NonUniqueName if it already exists ProcessTools.executeCommand("certutil", "-user", "-delstore", "MY", "NonUniqueName"); diff --git a/test/jdk/sun/security/pkcs/pkcs7/SignerOrder.java b/test/jdk/sun/security/pkcs/pkcs7/SignerOrder.java index 37db7fe4e8f76..7ce01c0813ec7 100644 --- a/test/jdk/sun/security/pkcs/pkcs7/SignerOrder.java +++ b/test/jdk/sun/security/pkcs/pkcs7/SignerOrder.java @@ -32,7 +32,6 @@ * @run main SignerOrder default 1024 * @run main SignerOrder Sha256 2048 */ -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigInteger; import java.security.KeyPair; @@ -117,7 +116,7 @@ public static void main(String[] args) throws Exception { } static void printSignerInfos(SignerInfo signerInfo) throws IOException { - ByteArrayOutputStream strm = new ByteArrayOutputStream(); + DerOutputStream strm = new DerOutputStream(); signerInfo.derEncode(strm); System.out.println("SignerInfo, length: " + strm.toByteArray().length); @@ -127,7 +126,7 @@ static void printSignerInfos(SignerInfo signerInfo) throws IOException { } static void printSignerInfos(SignerInfo[] signerInfos) throws IOException { - ByteArrayOutputStream strm = new ByteArrayOutputStream(); + DerOutputStream strm = new DerOutputStream(); for (int i = 0; i < signerInfos.length; i++) { signerInfos[i].derEncode(strm); System.out.println("SignerInfo[" + i + "], length: " diff --git a/test/jdk/sun/security/pkcs/pkcs9/UnknownAttribute.java b/test/jdk/sun/security/pkcs/pkcs9/UnknownAttribute.java index 333550d2ace9d..3a3ba5e908297 100644 --- a/test/jdk/sun/security/pkcs/pkcs9/UnknownAttribute.java +++ b/test/jdk/sun/security/pkcs/pkcs9/UnknownAttribute.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,10 +30,10 @@ * java.base/sun.security.util */ -import java.io.*; import java.util.Arrays; import sun.security.pkcs.PKCS9Attribute; +import sun.security.util.DerOutputStream; import sun.security.util.DerValue; import sun.security.util.ObjectIdentifier; import jdk.test.lib.hexdump.HexPrinter; @@ -57,10 +57,10 @@ public static void main(String[] args) throws Exception { if (p2.isKnown()) { throw new Exception(); } - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - p2.derEncode(bout); - HexPrinter.simple().dest(System.err).format(bout.toByteArray()); - if (!Arrays.equals(data, bout.toByteArray())) { + DerOutputStream dout = new DerOutputStream(); + p2.derEncode(dout); + HexPrinter.simple().dest(System.err).format(dout.toByteArray()); + if (!Arrays.equals(data, dout.toByteArray())) { throw new Exception(); } // Unknown attr from value @@ -75,9 +75,9 @@ public static void main(String[] args) throws Exception { if (p3.isKnown()) { throw new Exception(); } - bout = new ByteArrayOutputStream(); - p3.derEncode(bout); - if (!Arrays.equals(data, bout.toByteArray())) { + dout = new DerOutputStream(); + p3.derEncode(dout); + if (!Arrays.equals(data, dout.toByteArray())) { throw new Exception(); } } diff --git a/test/jdk/sun/security/pkcs11/Config/ReadConfInUTF16Env.java b/test/jdk/sun/security/pkcs11/Config/ReadConfInUTF16Env.java index b5ca601c011d6..11a6a781e01d3 100644 --- a/test/jdk/sun/security/pkcs11/Config/ReadConfInUTF16Env.java +++ b/test/jdk/sun/security/pkcs11/Config/ReadConfInUTF16Env.java @@ -40,7 +40,7 @@ public class ReadConfInUTF16Env { public void testReadConfInUTF16Env() throws Exception { String[] testCommand = new String[] { "-Dfile.encoding=UTF-16", TestSunPKCS11Provider.class.getName()}; - ProcessTools.executeTestJvm(testCommand).shouldHaveExitValue(0); + ProcessTools.executeTestJava(testCommand).shouldHaveExitValue(0); } static class TestSunPKCS11Provider { diff --git a/test/jdk/sun/security/pkcs11/KeyAgreement/SupportedDHKeys.java b/test/jdk/sun/security/pkcs11/KeyAgreement/SupportedDHKeys.java index 1246e13fb2344..11160a83800d3 100644 --- a/test/jdk/sun/security/pkcs11/KeyAgreement/SupportedDHKeys.java +++ b/test/jdk/sun/security/pkcs11/KeyAgreement/SupportedDHKeys.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,8 @@ * @run main/othervm -Djava.security.manager=allow SupportedDHKeys sm */ +import jtreg.SkippedException; + import java.math.BigInteger; import java.security.KeyPair; import java.security.KeyPairGenerator; @@ -63,8 +65,7 @@ private enum SupportedKeySize { @Override public void main(Provider provider) throws Exception { if (provider.getService("KeyPairGenerator", "DiffieHellman") == null) { - System.out.println("No support of DH KeyPairGenerator, skipping"); - return; + throw new SkippedException("No support of DH KeyPairGenerator, skipping"); } for (SupportedKeySize keySize : SupportedKeySize.values()) { diff --git a/test/jdk/sun/security/pkcs11/KeyAgreement/TestDH.java b/test/jdk/sun/security/pkcs11/KeyAgreement/TestDH.java index 13b09d16dcf62..d2394b1f65009 100644 --- a/test/jdk/sun/security/pkcs11/KeyAgreement/TestDH.java +++ b/test/jdk/sun/security/pkcs11/KeyAgreement/TestDH.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,14 +39,14 @@ import javax.crypto.KeyAgreement; import javax.crypto.SecretKey; import jdk.test.lib.security.SecurityUtils; +import jtreg.SkippedException; public class TestDH extends PKCS11Test { @Override public void main(Provider p) throws Exception { if (p.getService("KeyAgreement", "DH") == null) { - System.out.println("DH not supported, skipping"); - return; + throw new SkippedException("DH not supported, skipping"); } String kpgAlgorithm = "DH"; KeyPairGenerator kpg = KeyPairGenerator.getInstance(kpgAlgorithm, p); diff --git a/test/jdk/sun/security/pkcs11/KeyAgreement/TestInterop.java b/test/jdk/sun/security/pkcs11/KeyAgreement/TestInterop.java index 7f6f79e767b55..65dcd1a1bea5b 100644 --- a/test/jdk/sun/security/pkcs11/KeyAgreement/TestInterop.java +++ b/test/jdk/sun/security/pkcs11/KeyAgreement/TestInterop.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,6 +41,7 @@ import javax.crypto.spec.DHPublicKeySpec; import jdk.test.lib.security.DiffieHellmanGroup; import jdk.test.lib.security.SecurityUtils; +import jtreg.SkippedException; public class TestInterop extends PKCS11Test { @@ -77,8 +78,7 @@ public class TestInterop extends PKCS11Test { @Override public void main(Provider prov) throws Exception { if (prov.getService("KeyAgreement", "DH") == null) { - System.out.println("DH not supported, skipping"); - return; + throw new SkippedException("DH not supported, skipping"); } try { System.out.println("testing generateSecret()"); diff --git a/test/jdk/sun/security/pkcs11/KeyAgreement/TestShort.java b/test/jdk/sun/security/pkcs11/KeyAgreement/TestShort.java index 0639c06b7d8d3..1cec208e96559 100644 --- a/test/jdk/sun/security/pkcs11/KeyAgreement/TestShort.java +++ b/test/jdk/sun/security/pkcs11/KeyAgreement/TestShort.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,6 +32,8 @@ * @run main/othervm -Djava.security.manager=allow TestShort sm */ +import jtreg.SkippedException; + import java.math.BigInteger; import java.security.KeyFactory; import java.security.PrivateKey; @@ -91,12 +93,10 @@ public class TestShort extends PKCS11Test { @Override public void main(Provider provider) throws Exception { if (provider.getService("KeyAgreement", "DH") == null) { - System.out.println("DH not supported, skipping"); - return; + throw new SkippedException("DH not supported, skipping"); } + try { - DHPublicKeySpec publicSpec; - DHPrivateKeySpec privateSpec; KeyFactory kf = KeyFactory.getInstance("DH", provider); KeyAgreement ka = KeyAgreement.getInstance("DH", provider); @@ -107,7 +107,7 @@ public void main(Provider provider) throws Exception { ka.init(pr1); ka.doPhase(pu2, true); byte[] n2 = ka.generateSecret(); - if (Arrays.equals(s2, n2) == false) { + if (!Arrays.equals(s2, n2)) { throw new Exception("mismatch 2"); } System.out.println("short ok"); @@ -115,7 +115,7 @@ public void main(Provider provider) throws Exception { ka.init(pr1); ka.doPhase(pu3, true); byte[] n3 = ka.generateSecret(); - if (Arrays.equals(s3, n3) == false) { + if (!Arrays.equals(s3, n3)) { throw new Exception("mismatch 3"); } System.out.println("normal ok"); @@ -124,27 +124,6 @@ public void main(Provider provider) throws Exception { ex.printStackTrace(); throw ex; } - -/* - KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH", provider); - kpg.initialize(512); -// KeyPair kp1 = kpg.generateKeyPair(); -// System.out.println(kp1.getPublic()); -// System.out.println(kp1.getPrivate()); - while (true) { - KeyAgreement ka = KeyAgreement.getInstance("DH", provider); - ka.init(pr1); - KeyPair kp2 = kpg.generateKeyPair(); - ka.doPhase(kp2.getPublic(), true); - byte[] sec = ka.generateSecret(); - if (sec.length == 64) { - System.out.println(kp2.getPrivate()); - System.out.println(kp2.getPublic()); - System.out.println(toString(sec)); - break; - } - } -/**/ } public static void main(String[] args) throws Exception { diff --git a/test/jdk/sun/security/pkcs11/KeyAgreement/UnsupportedDHKeys.java b/test/jdk/sun/security/pkcs11/KeyAgreement/UnsupportedDHKeys.java index af1734dcd8033..56052c017af19 100644 --- a/test/jdk/sun/security/pkcs11/KeyAgreement/UnsupportedDHKeys.java +++ b/test/jdk/sun/security/pkcs11/KeyAgreement/UnsupportedDHKeys.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,8 @@ * @run main/othervm -Djava.security.manager=allow UnsupportedDHKeys sm */ +import jtreg.SkippedException; + import java.security.InvalidParameterException; import java.security.KeyPairGenerator; import java.security.Provider; @@ -60,8 +62,7 @@ private enum UnsupportedKeySize { @Override public void main(Provider provider) throws Exception { if (provider.getService("KeyPairGenerator", "DiffieHellman") == null) { - System.out.println("No supported of DH KeyPairGenerator, skipping"); - return; + throw new SkippedException("DH (DiffieHellman) is not supported in KeyPairGenerator, skipping"); } for (UnsupportedKeySize keySize : UnsupportedKeySize.values()) { diff --git a/test/jdk/sun/security/pkcs11/KeyGenerator/DESParity.java b/test/jdk/sun/security/pkcs11/KeyGenerator/DESParity.java index c08c224c62001..0221156926fe7 100644 --- a/test/jdk/sun/security/pkcs11/KeyGenerator/DESParity.java +++ b/test/jdk/sun/security/pkcs11/KeyGenerator/DESParity.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,6 +33,8 @@ * @run main/othervm -Djava.security.manager=allow DESParity sm */ +import jtreg.SkippedException; + import java.security.Provider; import java.util.Random; import javax.crypto.SecretKey; @@ -46,8 +48,7 @@ public class DESParity extends PKCS11Test { @Override public void main(Provider p) throws Exception { if (p.getService("SecretKeyFactory", "DES") == null) { - System.out.println("Not supported by provider, skipping"); - return; + throw new SkippedException("Not supported by provider, skipping"); } Random random = new Random(); SecretKeyFactory kf; @@ -58,7 +59,7 @@ public void main(Provider p) throws Exception { random.nextBytes(b); SecretKeySpec spec = new SecretKeySpec(b, "DES"); SecretKey key = kf.generateSecret(spec); - if (DESKeySpec.isParityAdjusted(key.getEncoded(), 0) == false) { + if (!DESKeySpec.isParityAdjusted(key.getEncoded(), 0)) { throw new Exception("DES key not parity adjusted"); } } @@ -69,7 +70,7 @@ public void main(Provider p) throws Exception { random.nextBytes(b); SecretKeySpec spec = new SecretKeySpec(b, "DESede"); SecretKey key = kf.generateSecret(spec); - if (DESedeKeySpec.isParityAdjusted(key.getEncoded(), 0) == false) { + if (!DESedeKeySpec.isParityAdjusted(key.getEncoded(), 0)) { throw new Exception("DESede key not parity adjusted"); } } diff --git a/test/jdk/sun/security/pkcs11/KeyGenerator/TestChaCha20.java b/test/jdk/sun/security/pkcs11/KeyGenerator/TestChaCha20.java index a21571cd95790..0eaab53865512 100644 --- a/test/jdk/sun/security/pkcs11/KeyGenerator/TestChaCha20.java +++ b/test/jdk/sun/security/pkcs11/KeyGenerator/TestChaCha20.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,8 @@ * @library /test/lib .. * @run main/othervm TestChaCha20 */ +import jtreg.SkippedException; + import java.security.Provider; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidParameterException; @@ -54,8 +56,7 @@ public void main(Provider p) throws Exception { try { kg = KeyGenerator.getInstance(ALGO, p); } catch (NoSuchAlgorithmException nsae) { - System.out.println("Skip; no support for " + ALGO); - return; + throw new SkippedException("Skip; no support for " + ALGO, nsae); } try { diff --git a/test/jdk/sun/security/pkcs11/KeyGenerator/TestKeyGenerator.java b/test/jdk/sun/security/pkcs11/KeyGenerator/TestKeyGenerator.java index ccf86444564ad..d055630e91cbb 100644 --- a/test/jdk/sun/security/pkcs11/KeyGenerator/TestKeyGenerator.java +++ b/test/jdk/sun/security/pkcs11/KeyGenerator/TestKeyGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/jdk/sun/security/pkcs11/KeyPairGenerator/TestDH2048.java b/test/jdk/sun/security/pkcs11/KeyPairGenerator/TestDH2048.java index f251c443c18fd..59b2465ce7bf3 100644 --- a/test/jdk/sun/security/pkcs11/KeyPairGenerator/TestDH2048.java +++ b/test/jdk/sun/security/pkcs11/KeyPairGenerator/TestDH2048.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,6 +32,8 @@ * @run main/othervm -Djava.security.manager=allow TestDH2048 sm */ +import jtreg.SkippedException; + import java.security.InvalidParameterException; import java.security.KeyPair; import java.security.KeyPairGenerator; @@ -51,8 +53,7 @@ private static void checkUnsupportedKeySize(KeyPairGenerator kpg, int ks) @Override public void main(Provider p) throws Exception { if (p.getService("KeyPairGenerator", "DH") == null) { - System.out.println("KPG for DH not supported, skipping"); - return; + throw new SkippedException("KPG for DH not supported, skipping"); } KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH", p); kpg.initialize(512); diff --git a/test/jdk/sun/security/pkcs11/KeyStore/ClientAuth.java b/test/jdk/sun/security/pkcs11/KeyStore/ClientAuth.java index 1af1258d173fc..5d50015880d31 100644 --- a/test/jdk/sun/security/pkcs11/KeyStore/ClientAuth.java +++ b/test/jdk/sun/security/pkcs11/KeyStore/ClientAuth.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -260,8 +260,7 @@ public void main(Provider p) throws Exception { try { javax.crypto.Cipher.getInstance("RSA/ECB/PKCS1Padding", p); } catch (GeneralSecurityException e) { - System.out.println("Not supported by provider, skipping"); - return; + throw new SkippedException("Not supported by provider, skipping"); } this.provider = p; diff --git a/test/jdk/sun/security/pkcs11/PKCS11Test.java b/test/jdk/sun/security/pkcs11/PKCS11Test.java index c969e566e1210..b98ebf5161529 100644 --- a/test/jdk/sun/security/pkcs11/PKCS11Test.java +++ b/test/jdk/sun/security/pkcs11/PKCS11Test.java @@ -31,7 +31,6 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.security.AlgorithmParameters; import java.security.InvalidAlgorithmParameterException; @@ -60,7 +59,6 @@ import jdk.test.lib.Utils; import jdk.test.lib.artifacts.Artifact; import jdk.test.lib.artifacts.ArtifactResolver; -import jdk.test.lib.artifacts.ArtifactResolverException; import jtreg.SkippedException; public abstract class PKCS11Test { @@ -79,7 +77,7 @@ public abstract class PKCS11Test { // Version of the NSS artifact. This coincides with the version of // the NSS version - private static final String NSS_BUNDLE_VERSION = "3.101"; + private static final String NSS_BUNDLE_VERSION = "3.111"; private static final String NSSLIB = "jpg.tests.jdk.nsslib"; static double nss_version = -1; @@ -240,10 +238,6 @@ public static String getNSSLibDir() throws Exception { static String getNSSLibDir(String library) throws Exception { Path libPath = getNSSLibPath(library); - if (libPath == null) { - return null; - } - String libDir = String.valueOf(libPath.getParent()) + File.separatorChar; System.out.println("nssLibDir: " + libDir); System.setProperty("pkcs11test.nss.libdir", libDir); @@ -257,12 +251,7 @@ private static Path getNSSLibPath() throws Exception { static Path getNSSLibPath(String library) throws Exception { String osid = getOsId(); Path libraryName = Path.of(System.mapLibraryName(library)); - Path nssLibPath = fetchNssLib(osid, libraryName); - if (nssLibPath == null) { - throw new SkippedException("Warning: unsupported OS: " + osid - + ", please initialize NSS library location, skipping test"); - } - return nssLibPath; + return fetchNssLib(osid, libraryName); } private static String getOsId() { @@ -667,7 +656,7 @@ static byte[] generateData(int length) { return data; } - private static Path fetchNssLib(String osId, Path libraryName) { + private static Path fetchNssLib(String osId, Path libraryName) throws IOException { switch (osId) { case "Windows-amd64-64": return fetchNssLib(WINDOWS_X64.class, libraryName); @@ -692,28 +681,13 @@ private static Path fetchNssLib(String osId, Path libraryName) { return fetchNssLib(LINUX_AARCH64.class, libraryName); } default: - return null; + throw new SkippedException("Unsupported OS: " + osId); } } - private static Path fetchNssLib(Class clazz, Path libraryName) { - Path path = null; - try { - Path p = ArtifactResolver.resolve(clazz).entrySet().stream() - .findAny().get().getValue(); - path = findNSSLibrary(p, libraryName); - } catch (ArtifactResolverException | IOException e) { - Throwable cause = e.getCause(); - if (cause == null) { - System.out.println("Cannot resolve artifact, " - + "please check if JIB jar is present in classpath."); - } else { - throw new RuntimeException("Fetch artifact failed: " + clazz - + "\nPlease make sure the artifact is available.", e); - } - } - Policy.setPolicy(null); // Clear the policy created by JIB if any - return path; + private static Path fetchNssLib(Class clazz, Path libraryName) throws IOException { + Path p = ArtifactResolver.fetchOne(clazz); + return findNSSLibrary(p, libraryName); } private static Path findNSSLibrary(Path path, Path libraryName) throws IOException { diff --git a/test/jdk/sun/security/pkcs11/SecmodTest.java b/test/jdk/sun/security/pkcs11/SecmodTest.java index c1b5e4471ba95..8201548bf9daf 100644 --- a/test/jdk/sun/security/pkcs11/SecmodTest.java +++ b/test/jdk/sun/security/pkcs11/SecmodTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -46,7 +46,7 @@ static boolean initSecmod() throws Exception { useNSS(); LIBPATH = getNSSLibDir(); // load all the libraries except libnss3 into memory - if ((LIBPATH == null) || (!loadNSPR(LIBPATH))) { + if (!loadNSPR(LIBPATH)) { throw new SkippedException("Failed to load NSS libraries"); } diff --git a/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestGeneral.java b/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestGeneral.java index bca594c66bd6d..e59d0b4a1ca6c 100644 --- a/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestGeneral.java +++ b/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestGeneral.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,10 +30,14 @@ * @run main/othervm TestGeneral */ +import jtreg.SkippedException; + import java.security.NoSuchAlgorithmException; import java.security.Provider; import java.security.SecureRandom; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import javax.crypto.SecretKeyFactory; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; @@ -57,8 +61,11 @@ private void test(String algorithm, SecretKey key, Provider p, try { skf = SecretKeyFactory.getInstance(algorithm, p); } catch (NoSuchAlgorithmException e) { - System.out.println("Not supported, skipping: " + e); - return; + throw new SkippedException("[algorithm: " + algorithm + + ", key: " + key.getAlgorithm() + "]" + + ", provider: " + p.getName() + "]" + + ", expectedTestResult: " + expected + "]" + + "Not supported, skipping: " + e); } try { SecretKey key2 = skf.translateKey(key); @@ -99,21 +106,31 @@ public void main(Provider p) throws Exception { SecretKey bf_128Key = new SecretKeySpec(rawBytes, 0, 16, "Blowfish"); SecretKey cc20Key = new SecretKeySpec(rawBytes, 0, 32, "ChaCha20"); - // fixed key length - test("AES", aes_128Key, p, TestResult.PASS); - test("AES", aes_256Key, p, TestResult.PASS); - test("AES", cc20Key, p, TestResult.FAIL); + List skippedList = new ArrayList<>(); + try { + // fixed key length + test("AES", aes_128Key, p, TestResult.PASS); + test("AES", aes_256Key, p, TestResult.PASS); + test("AES", cc20Key, p, TestResult.FAIL); - test("ChaCha20", aes_128Key, p, TestResult.FAIL); - test("ChaCha20", aes_256Key, p, TestResult.FAIL); - test("ChaCha20", cc20Key, p, TestResult.PASS); + test("ChaCha20", aes_128Key, p, TestResult.FAIL); + test("ChaCha20", aes_256Key, p, TestResult.FAIL); + test("ChaCha20", cc20Key, p, TestResult.PASS); - // variable key length - // Different PKCS11 impls may have different ranges - // of supported key sizes for variable-key-length - // algorithms. - test("Blowfish", aes_128Key, p, TestResult.FAIL); - test("Blowfish", cc20Key, p, TestResult.FAIL); - test("Blowfish", bf_128Key, p, TestResult.PASS); + // variable key length + // Different PKCS11 impls may have different ranges + // of supported key sizes for variable-key-length + // algorithms. + test("Blowfish", aes_128Key, p, TestResult.FAIL); + test("Blowfish", cc20Key, p, TestResult.FAIL); + test("Blowfish", bf_128Key, p, TestResult.PASS); + } catch (SkippedException skippedException){ + skippedException.printStackTrace(); + skippedList.add(skippedException.getMessage()); + } + + if (!skippedList.isEmpty()) { + throw new SkippedException("One or more tests skipped " + skippedList); + } } } diff --git a/test/jdk/sun/security/pkcs11/SecureRandom/Basic.java b/test/jdk/sun/security/pkcs11/SecureRandom/Basic.java index a014ae13a478e..c7e7f2b8f02a3 100644 --- a/test/jdk/sun/security/pkcs11/SecureRandom/Basic.java +++ b/test/jdk/sun/security/pkcs11/SecureRandom/Basic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,6 +33,8 @@ * @run main/othervm -Djava.security.manager=allow Basic sm */ +import jtreg.SkippedException; + import java.security.NoSuchAlgorithmException; import java.security.Provider; import java.security.SecureRandom; @@ -45,9 +47,8 @@ public void main(Provider p) throws Exception { try { random = SecureRandom.getInstance("PKCS11"); } catch (NoSuchAlgorithmException e) { - System.out.println("Provider " + p + " does not support SecureRandom, skipping"); e.printStackTrace(); - return; + throw new SkippedException("Provider " + p + " does not support SecureRandom, skipping", e); } byte[] b = new byte[32]; random.nextBytes(b); diff --git a/test/jdk/sun/security/pkcs11/SecureRandom/TestDeserialization.java b/test/jdk/sun/security/pkcs11/SecureRandom/TestDeserialization.java index f54ae95f36d39..b8d37e2a00e48 100644 --- a/test/jdk/sun/security/pkcs11/SecureRandom/TestDeserialization.java +++ b/test/jdk/sun/security/pkcs11/SecureRandom/TestDeserialization.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,8 @@ * @modules jdk.crypto.cryptoki */ +import jtreg.SkippedException; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; @@ -43,18 +45,16 @@ public class TestDeserialization extends PKCS11Test { public void main(Provider p) throws Exception { // Skip this test for providers not found by java.security.Security if (Security.getProvider(p.getName()) != p) { - System.out.println("Skip test for provider " + p.getName()); - return; + throw new SkippedException("Skip test for provider " + p.getName()); } SecureRandom r; try { r = SecureRandom.getInstance("PKCS11", p); System.out.println("SecureRandom instance " + r); } catch (NoSuchAlgorithmException e) { - System.out.println("Provider " + p + - " does not support SecureRandom, skipping"); e.printStackTrace(); - return; + throw new SkippedException("Provider " + p + + " does not support SecureRandom, skipping"); } r.setSeed(System.currentTimeMillis()); byte[] buf = new byte[16]; diff --git a/test/jdk/sun/security/pkcs11/Serialize/SerializeProvider.java b/test/jdk/sun/security/pkcs11/Serialize/SerializeProvider.java index 495da8e6f38da..2b268fe8feaa9 100644 --- a/test/jdk/sun/security/pkcs11/Serialize/SerializeProvider.java +++ b/test/jdk/sun/security/pkcs11/Serialize/SerializeProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,9 +41,9 @@ public class SerializeProvider extends PKCS11Test { public void main(Provider p) throws Exception { + if (Security.getProvider(p.getName()) != p) { - System.out.println("Provider not installed in Security, skipping"); - return; + Security.addProvider(p); } ByteArrayOutputStream out = new ByteArrayOutputStream(); @@ -57,7 +57,7 @@ public void main(Provider p) throws Exception { InputStream in = new ByteArrayInputStream(data); ObjectInputStream oin = new ObjectInputStream(in); - Provider p2 = (Provider)oin.readObject(); + Provider p2 = (Provider) oin.readObject(); System.out.println("Reconstituted: " + p2); diff --git a/test/jdk/sun/security/pkcs11/ec/ReadCertificates.java b/test/jdk/sun/security/pkcs11/ec/ReadCertificates.java index 4b4a6a20a4a38..e0700a45553a6 100644 --- a/test/jdk/sun/security/pkcs11/ec/ReadCertificates.java +++ b/test/jdk/sun/security/pkcs11/ec/ReadCertificates.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,6 @@ * and verify their signatures * @author Andreas Sterbenz * @library /test/lib .. - * @library ../../../../java/security/testlibrary * @modules jdk.crypto.cryptoki * @run main/othervm ReadCertificates * @run main/othervm -Djava.security.manager=allow ReadCertificates sm policy @@ -56,6 +55,7 @@ import java.util.List; import java.util.Map; import javax.security.auth.x500.X500Principal; +import jdk.test.lib.security.Providers; public class ReadCertificates extends PKCS11Test { diff --git a/test/jdk/sun/security/pkcs11/ec/ReadPKCS12.java b/test/jdk/sun/security/pkcs11/ec/ReadPKCS12.java index 6005d88aec4db..7acd4c2dc691c 100644 --- a/test/jdk/sun/security/pkcs11/ec/ReadPKCS12.java +++ b/test/jdk/sun/security/pkcs11/ec/ReadPKCS12.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,6 @@ * @summary Verify that we can parse ECPrivateKeys from PKCS#12 and use them * @author Andreas Sterbenz * @library /test/lib .. - * @library ../../../../java/security/testlibrary * @key randomness * @modules jdk.crypto.cryptoki jdk.crypto.ec/sun.security.ec * @run main/othervm ReadPKCS12 @@ -55,6 +54,7 @@ import java.util.List; import java.util.Map; import java.util.Random; +import jdk.test.lib.security.Providers; public class ReadPKCS12 extends PKCS11Test { diff --git a/test/jdk/sun/security/pkcs11/ec/TestECDH.java b/test/jdk/sun/security/pkcs11/ec/TestECDH.java index 49061f1288dcc..d2a45f3842db0 100644 --- a/test/jdk/sun/security/pkcs11/ec/TestECDH.java +++ b/test/jdk/sun/security/pkcs11/ec/TestECDH.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,6 @@ * @summary Basic known answer test for ECDH * @author Andreas Sterbenz * @library /test/lib .. - * @library ../../../../java/security/testlibrary * @modules jdk.crypto.cryptoki * @run main/othervm TestECDH * @run main/othervm -Djava.security.manager=allow TestECDH sm policy @@ -44,6 +43,8 @@ import java.security.spec.X509EncodedKeySpec; import java.util.Arrays; import javax.crypto.KeyAgreement; +import jdk.test.lib.security.Providers; + public class TestECDH extends PKCS11Test { diff --git a/test/jdk/sun/security/pkcs11/ec/TestECDH2.java b/test/jdk/sun/security/pkcs11/ec/TestECDH2.java index 69ea7275193a2..1e4de55da0283 100644 --- a/test/jdk/sun/security/pkcs11/ec/TestECDH2.java +++ b/test/jdk/sun/security/pkcs11/ec/TestECDH2.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,6 @@ * @summary basic test of ECDSA signatures for P-256 and P-384 from the * example data in "Suite B Implementer's Guide to FIPS 186-3". * @library /test/lib .. - * @library ../../../../java/security/testlibrary * @modules java.base/sun.security.util * jdk.crypto.cryptoki * @compile -XDignore.symbol.file TestECDH2.java diff --git a/test/jdk/sun/security/pkcs11/ec/TestECDSA.java b/test/jdk/sun/security/pkcs11/ec/TestECDSA.java index 59db0a3ed0138..8ce69c61667ef 100644 --- a/test/jdk/sun/security/pkcs11/ec/TestECDSA.java +++ b/test/jdk/sun/security/pkcs11/ec/TestECDSA.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,6 @@ * @summary basic test of SHA1withECDSA and NONEwithECDSA signing/verifying * @author Andreas Sterbenz * @library /test/lib .. - * @library ../../../../java/security/testlibrary * @key randomness * @modules jdk.crypto.cryptoki * @run main/othervm TestECDSA @@ -46,6 +45,7 @@ import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Random; +import jdk.test.lib.security.Providers; public class TestECDSA extends PKCS11Test { diff --git a/test/jdk/sun/security/pkcs11/ec/TestECDSA2.java b/test/jdk/sun/security/pkcs11/ec/TestECDSA2.java index 4b6cb6f14ed09..88edb93668e6d 100644 --- a/test/jdk/sun/security/pkcs11/ec/TestECDSA2.java +++ b/test/jdk/sun/security/pkcs11/ec/TestECDSA2.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,6 @@ * @summary basic test of ECDSA signatures for P-256 and P-384 from the * example data in "Suite B Implementer's Guide to FIPS 186-3". * @library /test/lib .. - * @library ../../../../java/security/testlibrary * @modules java.base/sun.security.util * jdk.crypto.cryptoki * @compile -XDignore.symbol.file TestECDSA2.java diff --git a/test/jdk/sun/security/pkcs11/rsa/TestCACerts.java b/test/jdk/sun/security/pkcs11/rsa/TestCACerts.java index c95d27bc3d8cf..a6844d28f782e 100644 --- a/test/jdk/sun/security/pkcs11/rsa/TestCACerts.java +++ b/test/jdk/sun/security/pkcs11/rsa/TestCACerts.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,6 @@ * @summary Test the new RSA provider can verify all the RSA certs in the cacerts file * @author Andreas Sterbenz * @library /test/lib .. - * @library ../../../../java/security/testlibrary * @modules jdk.crypto.cryptoki * @run main/othervm TestCACerts * @run main/othervm -Djava.security.manager=allow TestCACerts sm TestCACerts.policy @@ -43,6 +42,7 @@ import java.security.Security; import java.security.cert.X509Certificate; import java.util.Enumeration; +import jdk.test.lib.security.Providers; public class TestCACerts extends PKCS11Test { diff --git a/test/jdk/sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java b/test/jdk/sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java index 08b29fe6df189..e57ba07741c90 100644 --- a/test/jdk/sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java +++ b/test/jdk/sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,7 +32,6 @@ * @summary Verify that all ciphersuites work (incl. ECC using NSS crypto) * @author Andreas Sterbenz * @library /test/lib .. ../../../../javax/net/ssl/TLSCommon - * @library ../../../../java/security/testlibrary * @modules jdk.crypto.cryptoki * @run main/othervm -Djdk.tls.namedGroups="secp256r1,sect193r1" * ClientJSSEServerJSSE @@ -42,6 +41,7 @@ import java.security.Provider; import java.security.Security; +import jdk.test.lib.security.Providers; public class ClientJSSEServerJSSE extends PKCS11Test { diff --git a/test/jdk/sun/security/pkcs11/tls/TestKeyMaterialMisuse.java b/test/jdk/sun/security/pkcs11/tls/TestKeyMaterialMisuse.java new file mode 100644 index 0000000000000..aa02913cf7ac6 --- /dev/null +++ b/test/jdk/sun/security/pkcs11/tls/TestKeyMaterialMisuse.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8349849 + * @summary Verify that SunTlsKeyMaterial doesn't crash on TLS 1.2 parameters + * @library /test/lib .. + * @modules java.base/sun.security.internal.spec + * @run main/othervm TestKeyMaterialMisuse + */ + +import sun.security.internal.spec.TlsKeyMaterialParameterSpec; +import sun.security.internal.spec.TlsKeyMaterialSpec; + +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.security.Provider; +import java.security.ProviderException; +import java.util.Arrays; +import java.util.List; + +public class TestKeyMaterialMisuse extends PKCS11Test { + + public static void main(String[] args) throws Exception { + System.out.println("NSS Version: " + getNSSVersion()); + main(new TestKeyMaterialMisuse(), args); + } + + @Override + public void main(Provider provider) throws Exception { + byte[] keyBytes = new byte[48]; + Arrays.fill(keyBytes, (byte)1); + SecretKey master = new SecretKeySpec(keyBytes, "TlsMasterSecret"); + byte[] cr = "clientRandom".getBytes(); + byte[] sr = "serverRandom".getBytes(); + for (int minor : List.of(1, 3)) { + try { + // the algorithms below are deliberately reversed: + // - SunTls12KeyMaterial is used with TLS 1.0, + // - SunTlsKeyMaterial is used with TLS 1.2 + String algorithm = minor != 3 ? + "SunTls12KeyMaterial" : + "SunTlsKeyMaterial"; + System.out.println("Generating key material for version: " + + minor + " using algorithm: " + algorithm); + + KeyGenerator g = KeyGenerator.getInstance(algorithm, provider); + TlsKeyMaterialParameterSpec spec = + new TlsKeyMaterialParameterSpec( + master, 3, minor, cr, sr, + "AES", 32, 0, + 12, 32, + "SHA-256", 32, 128); + g.init(spec); + // generateKey crashed the JVM: + TlsKeyMaterialSpec km = (TlsKeyMaterialSpec) g.generateKey(); + System.out.println("Success!"); + } catch (ProviderException e) { + System.out.println("Got exception, not crash:"); + e.printStackTrace(); + } + } + } + +} diff --git a/test/jdk/sun/security/pkcs11/tls/tls12/FipsModeTLS12.java b/test/jdk/sun/security/pkcs11/tls/tls12/FipsModeTLS12.java index 1f6886f2d692b..a91bbac46519a 100644 --- a/test/jdk/sun/security/pkcs11/tls/tls12/FipsModeTLS12.java +++ b/test/jdk/sun/security/pkcs11/tls/tls12/FipsModeTLS12.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2019, Red Hat, Inc. + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -63,6 +64,7 @@ import javax.net.ssl.SSLSession; import javax.net.ssl.TrustManagerFactory; +import jdk.test.lib.security.SecurityUtils; import sun.security.internal.spec.TlsMasterSecretParameterSpec; import sun.security.internal.spec.TlsPrfParameterSpec; import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec; @@ -80,6 +82,9 @@ public final class FipsModeTLS12 extends SecmodTest { private static PublicKey publicKey; public static void main(String[] args) throws Exception { + // Re-enable TLS_RSA_* since test depends on it. + SecurityUtils.removeFromDisabledTlsAlgs("TLS_RSA_*"); + try { initialize(); } catch (Exception e) { diff --git a/test/jdk/sun/security/pkcs12/KeytoolOpensslInteropTest.java b/test/jdk/sun/security/pkcs12/KeytoolOpensslInteropTest.java index c35bf47d56db1..facdbc8a18762 100644 --- a/test/jdk/sun/security/pkcs12/KeytoolOpensslInteropTest.java +++ b/test/jdk/sun/security/pkcs12/KeytoolOpensslInteropTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,7 +22,7 @@ */ /* - * @test + * @test id=GenerateOpensslPKCS12 * @bug 8076190 8242151 8153005 8266182 * @summary This is java keytool <-> openssl interop test. This test generates * some openssl keystores on the fly, java operates on it and @@ -31,13 +31,24 @@ * Note: This test executes some openssl command, so need to set * openssl path using system property "test.openssl.path" or it should * be available in /usr/bin or /usr/local/bin - * Required OpenSSL version : OpenSSL 1.1.* + * Required OpenSSL version : OpensslArtifactFetcher.OPENSSL_BUNDLE_VERSION * * @modules java.base/sun.security.pkcs * java.base/sun.security.util - * @library /test/lib - * @library /sun/security/pkcs11/ - * @run main/othervm/timeout=600 KeytoolOpensslInteropTest + * @library /test/lib /sun/security/pkcs11/ + * @run main/othervm KeytoolOpensslInteropTest true + */ + +/* + * @test id=UseExistingPKCS12 + * @bug 8076190 8242151 8153005 8266182 + * @summary This is java keytool <-> openssl interop test. This test uses + * the existing PKCS12 files located in ./params dir and java operates on it + * + * @modules java.base/sun.security.pkcs + * java.base/sun.security.util + * @library /test/lib /sun/security/pkcs11/ + * @run main/othervm KeytoolOpensslInteropTest false */ import jdk.test.lib.Asserts; @@ -67,22 +78,15 @@ public class KeytoolOpensslInteropTest { public static void main(String[] args) throws Throwable { - String opensslPath = OpensslArtifactFetcher.getOpenssl1dot1dotStar(); - if (opensslPath != null) { - // if preferred version of openssl is available perform all - // keytool <-> openssl interop tests + boolean generatePKCS12 = Boolean.parseBoolean(args[0]); + if (generatePKCS12) { + String opensslPath = OpensslArtifactFetcher.getOpensslPath(); generateInitialKeystores(opensslPath); testWithJavaCommands(); testWithOpensslCommands(opensslPath); } else { - // since preferred version of openssl is not available skip all - // openssl command dependent tests with a warning - System.out.println("\n\u001B[31mWarning: Can't find openssl " - + "(version 1.1.*) binary on this machine, please install" - + " and set openssl path with property " - + "'test.openssl.path'. Now running only half portion of " - + "the test, skipping all tests which depends on openssl " - + "commands.\u001B[0m\n"); + // since this scenario is using preexisting PKCS12, skip all + // openssl command dependent tests // De-BASE64 textual files in ./params to `pwd` try (DirectoryStream stream = Files.newDirectoryStream( Path.of(System.getProperty("test.src"), "params"), @@ -103,6 +107,8 @@ public static void main(String[] args) throws Throwable { private static void generateInitialKeystores(String opensslPath) throws Throwable { + Path providerPath = OpensslArtifactFetcher.getProviderPath(opensslPath); + keytool("-keystore ks -keyalg ec -genkeypair -storepass" + " changeit -alias a -dname CN=A").shouldHaveExitValue(0); @@ -123,7 +129,8 @@ private static void generateInitialKeystores(String opensslPath) ProcessTools.executeCommand(opensslPath, "pkcs12", "-export", "-in", "kandc", "-out", "os4", "-name", "a", "-passout", "pass:changeit", "-certpbe", "PBE-SHA1-RC4-128", "-keypbe", - "PBE-SHA1-RC4-128", "-macalg", "SHA224") + "PBE-SHA1-RC4-128", "-macalg", "SHA224", + "-legacy", "-provider-path", providerPath.toString()) .shouldHaveExitValue(0); ProcessTools.executeCommand(opensslPath, "pkcs12", "-export", "-in", @@ -480,12 +487,14 @@ private static void testWithOpensslCommands(String opensslPath) output1 = ProcessTools.executeCommand(opensslPath, "pkcs12", "-in", "ksnopass", "-passin", "pass:changeit", "-info", "-nokeys", "-nocerts"); - output1.shouldNotHaveExitValue(0); + output1.shouldHaveExitValue(0) + .shouldContain("Warning: MAC is absent!"); output1 = ProcessTools.executeCommand(opensslPath, "pkcs12", "-in", "ksnopass", "-passin", "pass:changeit", "-info", "-nokeys", "-nocerts", "-nomacver"); output1.shouldHaveExitValue(0) + .shouldNotContain("Warning: MAC is absent!") .shouldNotContain("PKCS7 Encrypted data:") .shouldContain("Shrouded Keybag: PBES2, PBKDF2, AES-256-CBC," + " Iteration 10000, PRF hmacWithSHA256") diff --git a/test/jdk/sun/security/pkcs12/P12SecretKey.java b/test/jdk/sun/security/pkcs12/P12SecretKey.java index ed599a5596201..80f037138bf8e 100644 --- a/test/jdk/sun/security/pkcs12/P12SecretKey.java +++ b/test/jdk/sun/security/pkcs12/P12SecretKey.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,7 @@ * @test * @bug 8149411 8007632 * @summary Get AES key from keystore (uses SecretKeySpec not SecretKeyFactory) + * @library /test/lib * @run main P12SecretKey pkcs12 AES 128 * @run main P12SecretKey pkcs12 DES 56 * @run main P12SecretKey pkcs12 DESede 168 @@ -33,14 +34,14 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; -import java.nio.file.Files; import java.security.KeyStore; -import java.security.cert.CertificateException; import java.util.Arrays; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; +import static jdk.test.lib.Utils.createTempFile; + public class P12SecretKey { private static final String ALIAS = "alias"; @@ -66,30 +67,32 @@ private void run(String keystoreType, String algName, int keySize) throws Except KeyStore.ProtectionParameter kspp = new KeyStore.PasswordProtection(pw); ks.setEntry(ALIAS, ske, kspp); - File ksFile = File.createTempFile("test", ".test"); + // temporary files are created in scratch directory + final File ksFile = createTempFile( + String.format("%s-%s-%d-", + keystoreType, + algName, + keySize), + ".ks").toFile(); - try { - try (FileOutputStream fos = new FileOutputStream(ksFile)) { - ks.store(fos, pw); - fos.flush(); - } + try (FileOutputStream fos = new FileOutputStream(ksFile)) { + ks.store(fos, pw); + fos.flush(); + } - // now see if we can get it back - try (FileInputStream fis = new FileInputStream(ksFile)) { - KeyStore ks2 = KeyStore.getInstance(keystoreType); - ks2.load(fis, pw); - KeyStore.Entry entry = ks2.getEntry(ALIAS, kspp); - SecretKey keyIn = ((KeyStore.SecretKeyEntry) entry).getSecretKey(); - if (Arrays.equals(key.getEncoded(), keyIn.getEncoded())) { - System.err.println("OK: worked just fine with " + keystoreType + - " keystore"); - } else { - System.err.println("ERROR: keys are NOT equal after storing in " - + keystoreType + " keystore"); - } + // now see if we can get it back + try (FileInputStream fis = new FileInputStream(ksFile)) { + KeyStore ks2 = KeyStore.getInstance(keystoreType); + ks2.load(fis, pw); + KeyStore.Entry entry = ks2.getEntry(ALIAS, kspp); + SecretKey keyIn = ((KeyStore.SecretKeyEntry) entry).getSecretKey(); + if (Arrays.equals(key.getEncoded(), keyIn.getEncoded())) { + System.err.println("OK: worked just fine with " + keystoreType + + " keystore"); + } else { + throw new RuntimeException("ERROR: keys are NOT equal after storing in " + + keystoreType + " keystore"); } - } finally { - Files.deleteIfExists(ksFile.toPath()); } } } diff --git a/test/jdk/sun/security/provider/certpath/OCSP/OCSPNoContentLength.java b/test/jdk/sun/security/provider/certpath/OCSP/OCSPNoContentLength.java index 8be1b05cd85af..979d270d8a5a0 100644 --- a/test/jdk/sun/security/provider/certpath/OCSP/OCSPNoContentLength.java +++ b/test/jdk/sun/security/provider/certpath/OCSP/OCSPNoContentLength.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,8 +28,7 @@ * @modules java.base/sun.security.x509 * java.base/sun.security.provider.certpath * java.base/sun.security.util - * @library ../../../../../java/security/testlibrary - * @build CertificateBuilder SimpleOCSPServer + * @library /test/lib * @run main/othervm OCSPNoContentLength */ @@ -46,8 +45,8 @@ import java.util.concurrent.TimeUnit; -import sun.security.testlibrary.SimpleOCSPServer; -import sun.security.testlibrary.CertificateBuilder; +import jdk.test.lib.security.SimpleOCSPServer; +import jdk.test.lib.security.CertificateBuilder; public class OCSPNoContentLength { diff --git a/test/jdk/sun/security/ssl/CertPathRestrictions/TLSRestrictions.java b/test/jdk/sun/security/ssl/CertPathRestrictions/TLSRestrictions.java index b9fb72b8faf0e..476dc95488495 100644 --- a/test/jdk/sun/security/ssl/CertPathRestrictions/TLSRestrictions.java +++ b/test/jdk/sun/security/ssl/CertPathRestrictions/TLSRestrictions.java @@ -231,7 +231,7 @@ static void testConstraint(String[] trustNames, String[] certNames, // Run client on another JVM so that its properties cannot be in conflict // with server's. - OutputAnalyzer outputAnalyzer = ProcessTools.executeTestJvm( + OutputAnalyzer outputAnalyzer = ProcessTools.executeTestJava( "-Dcert.dir=" + CERT_DIR, "-Djava.security.debug=certpath", "-classpath", diff --git a/test/jdk/sun/security/ssl/CipherSuite/AbstractDisableCipherSuites.java b/test/jdk/sun/security/ssl/CipherSuite/AbstractDisableCipherSuites.java new file mode 100644 index 0000000000000..3f428e458ca55 --- /dev/null +++ b/test/jdk/sun/security/ssl/CipherSuite/AbstractDisableCipherSuites.java @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.security.GeneralSecurityException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.SSLSession; + +/** + * This is not a test. Actual tests are implemented by concrete subclasses. + * The abstract class AbstractDisableCipherSuites provides a base framework + * for testing cipher suite disablement. + */ +public abstract class AbstractDisableCipherSuites { + + private static final byte RECTYPE_HS = 0x16; + private static final byte HSMSG_CLIHELLO = 0x01; + private static final ByteBuffer CLIOUTBUF = + ByteBuffer.wrap("Client Side".getBytes()); + + /** + * Create an engine with the default set of cipher suites enabled and make + * sure none of the disabled suites are present in the client hello. + * + * @param disabledSuiteIds the {@code List} of disabled cipher suite IDs + * to be checked for. + * + * @return true if the test passed (No disabled suites), false otherwise + */ + protected boolean testDefaultCase(List disabledSuiteIds) + throws Exception { + System.err.println("\nTest: Default SSLEngine suite set"); + SSLEngine ssle = makeEngine(); + if (getDebug()) { + listCiphers("Suite set upon creation", ssle); + } + SSLEngineResult clientResult; + ByteBuffer cTOs = makeClientBuf(ssle); + clientResult = ssle.wrap(CLIOUTBUF, cTOs); + if (getDebug()) { + dumpResult("ClientHello: ", clientResult); + } + cTOs.flip(); + boolean foundSuite = areSuitesPresentCH(cTOs, disabledSuiteIds); + if (foundSuite) { + System.err.println("FAIL: Found disabled suites!"); + return false; + } else { + System.err.println("PASS: No disabled suites found."); + return true; + } + } + + /** + * Create an engine and set only disabled cipher suites. + * The engine should not create the client hello message since the only + * available suites to assert in the client hello are disabled ones. + * + * @param disabledSuiteNames an array of cipher suite names that + * should be disabled cipher suites. + * + * @return true if the engine throws SSLHandshakeException during client + * hello creation, false otherwise. + */ + protected boolean testEngOnlyDisabled(String[] disabledSuiteNames) + throws Exception { + System.err.println( + "\nTest: SSLEngine configured with only disabled suites"); + try { + SSLEngine ssle = makeEngine(); + ssle.setEnabledCipherSuites(disabledSuiteNames); + if (getDebug()) { + listCiphers("Suite set upon creation", ssle); + } + SSLEngineResult clientResult; + ByteBuffer cTOs = makeClientBuf(ssle); + clientResult = ssle.wrap(CLIOUTBUF, cTOs); + if (getDebug()) { + dumpResult("ClientHello: ", clientResult); + } + cTOs.flip(); + } catch (SSLHandshakeException shse) { + System.err.println("PASS: Caught expected exception: " + shse); + return true; + } + System.err.println("FAIL: Expected SSLHandshakeException not thrown"); + return false; + } + + /** + * Create an engine and add some disabled suites to the default + * set of cipher suites. Make sure none of the disabled suites show up + * in the client hello even though they were explicitly added. + * + * @param disabledNames an array of cipher suite names that + * should be disabled cipher suites. + * @param disabledIds the {@code List} of disabled cipher suite IDs + * to be checked for. + * + * @return true if the test passed (No disabled suites), false otherwise + */ + protected boolean testEngAddDisabled(String[] disabledNames, + List disabledIds) throws Exception { + System.err.println("\nTest: SSLEngine with disabled suites added"); + SSLEngine ssle = makeEngine(); + + // Add disabled suites to the existing engine's set of enabled suites + String[] initialSuites = ssle.getEnabledCipherSuites(); + String[] plusDisSuites = Arrays.copyOf(initialSuites, + initialSuites.length + disabledNames.length); + System.arraycopy(disabledNames, 0, plusDisSuites, + initialSuites.length, disabledNames.length); + ssle.setEnabledCipherSuites(plusDisSuites); + + if (getDebug()) { + listCiphers("Suite set upon creation", ssle); + } + SSLEngineResult clientResult; + ByteBuffer cTOs = makeClientBuf(ssle); + clientResult = ssle.wrap(CLIOUTBUF, cTOs); + if (getDebug()) { + dumpResult("ClientHello: ", clientResult); + } + cTOs.flip(); + boolean foundDisabled = areSuitesPresentCH(cTOs, disabledIds); + if (foundDisabled) { + System.err.println("FAIL: Found disabled suites!"); + return false; + } else { + System.err.println("PASS: No disabled suites found."); + return true; + } + } + + protected String getProtocol() { + return "TLSv1.2"; + } + + private SSLEngine makeEngine() throws GeneralSecurityException { + SSLContext ctx = SSLContext.getInstance(getProtocol()); + ctx.init(null, null, null); + return ctx.createSSLEngine(); + } + + private static ByteBuffer makeClientBuf(SSLEngine ssle) { + ssle.setUseClientMode(true); + ssle.setNeedClientAuth(false); + SSLSession sess = ssle.getSession(); + ByteBuffer cTOs = ByteBuffer.allocateDirect(sess.getPacketBufferSize()); + return cTOs; + } + + private static void listCiphers(String prefix, SSLEngine ssle) { + System.err.println(prefix + "\n---------------"); + String[] suites = ssle.getEnabledCipherSuites(); + for (String suite : suites) { + System.err.println(suite); + } + System.err.println("---------------"); + } + + /** + * Walk a TLS 1.2 or earlier ClientHello looking for any of the suites + * in the suiteIdList. + * + * @param clientHello a ByteBuffer containing the ClientHello message as + * a complete TLS record. The position of the buffer should be + * at the first byte of the TLS record header. + * @param suiteIdList a List of integer values corresponding to + * TLS cipher suite identifiers. + * + * @return true if at least one of the suites in {@code suiteIdList} + * is found in the ClientHello's cipher suite list + * + * @throws IOException if the data in the {@code clientHello} + * buffer is not a TLS handshake message or is not a client hello. + */ + private boolean areSuitesPresentCH(ByteBuffer clientHello, + List suiteIdList) throws IOException { + byte val; + + // Process the TLS Record + val = clientHello.get(); + if (val != RECTYPE_HS) { + throw new IOException( + "Not a handshake record, type = " + val); + } + + // Just skip over the version and length + clientHello.position(clientHello.position() + 4); + + // Check the handshake message type + val = clientHello.get(); + if (val != HSMSG_CLIHELLO) { + throw new IOException( + "Not a ClientHello handshake message, type = " + val); + } + + // Skip over the length + clientHello.position(clientHello.position() + 3); + + // Skip over the protocol version (2) and random (32); + clientHello.position(clientHello.position() + 34); + + // Skip past the session ID (variable length <= 32) + int len = Byte.toUnsignedInt(clientHello.get()); + if (len > 32) { + throw new IOException("Session ID is too large, len = " + len); + } + clientHello.position(clientHello.position() + len); + + // Finally, we are at the cipher suites. Walk the list and place them + // into a List. + int csLen = Short.toUnsignedInt(clientHello.getShort()); + if (csLen % 2 != 0) { + throw new IOException("CipherSuite length is invalid, len = " + + csLen); + } + int csCount = csLen / 2; + List csSuiteList = new ArrayList<>(csCount); + log("Found following suite IDs in hello:"); + for (int i = 0; i < csCount; i++) { + int curSuite = Short.toUnsignedInt(clientHello.getShort()); + log(String.format("Suite ID: 0x%04x", curSuite)); + csSuiteList.add(curSuite); + } + + // Now check to see if any of the suites passed in match what is in + // the suite list. + boolean foundMatch = false; + for (Integer cs : suiteIdList) { + if (csSuiteList.contains(cs)) { + System.err.format("Found match for suite ID 0x%04x\n", cs); + foundMatch = true; + break; + } + } + + // We don't care about the rest of the ClientHello message. + // Rewind and return whether we found a match or not. + clientHello.rewind(); + return foundMatch; + } + + private static void dumpResult(String str, SSLEngineResult result) { + System.err.println("The format of the SSLEngineResult is: \n" + + "\t\"getStatus() / getHandshakeStatus()\" +\n" + + "\t\"bytesConsumed() / bytesProduced()\"\n"); + HandshakeStatus hsStatus = result.getHandshakeStatus(); + System.err.println(str + result.getStatus() + "/" + hsStatus + ", " + + result.bytesConsumed() + "/" + result.bytesProduced() + " bytes"); + if (hsStatus == HandshakeStatus.FINISHED) { + System.err.println("\t...ready for application data"); + } + } + + private void log(String str) { + if (getDebug()) { + System.err.println(str); + } + } + + protected boolean getDebug() { + return false; + } +} diff --git a/test/jdk/sun/security/ssl/CipherSuite/NoDesRC4CiphSuite.java b/test/jdk/sun/security/ssl/CipherSuite/NoDesRC4CiphSuite.java index 22238d38e1d5f..4e20cad021bcd 100644 --- a/test/jdk/sun/security/ssl/CipherSuite/NoDesRC4CiphSuite.java +++ b/test/jdk/sun/security/ssl/CipherSuite/NoDesRC4CiphSuite.java @@ -34,21 +34,11 @@ */ import java.security.Security; -import javax.net.ssl.*; -import javax.net.ssl.SSLEngineResult.HandshakeStatus; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.security.GeneralSecurityException; -import java.util.List; -import java.util.ArrayList; import java.util.Arrays; +import java.util.List; -public class NoDesRC4CiphSuite { - - private static final boolean DEBUG = false; +public class NoDesRC4CiphSuite extends AbstractDisableCipherSuites { - private static final byte RECTYPE_HS = 0x16; - private static final byte HSMSG_CLIHELLO = 0x01; // These are some groups of Cipher Suites by names and IDs private static final List DES_CS_LIST = Arrays.asList( @@ -81,23 +71,22 @@ public class NoDesRC4CiphSuite { "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5" }; - private static final ByteBuffer CLIOUTBUF = - ByteBuffer.wrap("Client Side".getBytes()); public static void main(String[] args) throws Exception { boolean allGood = true; String disAlg = Security.getProperty("jdk.tls.disabledAlgorithms"); System.err.println("Disabled Algs: " + disAlg); + NoDesRC4CiphSuite test = new NoDesRC4CiphSuite(); // Disabled DES tests - allGood &= testDefaultCase(DES_CS_LIST); - allGood &= testEngAddDisabled(DES_CS_LIST_NAMES, DES_CS_LIST); - allGood &= testEngOnlyDisabled(DES_CS_LIST_NAMES); + allGood &= test.testDefaultCase(DES_CS_LIST); + allGood &= test.testEngAddDisabled(DES_CS_LIST_NAMES, DES_CS_LIST); + allGood &= test.testEngOnlyDisabled(DES_CS_LIST_NAMES); // Disabled RC4 tests - allGood &= testDefaultCase(RC4_CS_LIST); - allGood &= testEngAddDisabled(RC4_CS_LIST_NAMES, RC4_CS_LIST); - allGood &= testEngOnlyDisabled(RC4_CS_LIST_NAMES); + allGood &= test.testDefaultCase(RC4_CS_LIST); + allGood &= test.testEngAddDisabled(RC4_CS_LIST_NAMES, RC4_CS_LIST); + allGood &= test.testEngOnlyDisabled(RC4_CS_LIST_NAMES); if (allGood) { System.err.println("All tests passed"); @@ -105,242 +94,4 @@ public static void main(String[] args) throws Exception { throw new RuntimeException("One or more tests failed"); } } - - /** - * Create an engine with the default set of cipher suites enabled and make - * sure none of the disabled suites are present in the client hello. - * - * @param disabledSuiteIds the {@code List} of disabled cipher suite IDs - * to be checked for. - * - * @return true if the test passed (No disabled suites), false otherwise - */ - private static boolean testDefaultCase(List disabledSuiteIds) - throws Exception { - System.err.println("\nTest: Default SSLEngine suite set"); - SSLEngine ssle = makeEngine(); - if (DEBUG) { - listCiphers("Suite set upon creation", ssle); - } - SSLEngineResult clientResult; - ByteBuffer cTOs = makeClientBuf(ssle); - clientResult = ssle.wrap(CLIOUTBUF, cTOs); - if (DEBUG) { - dumpResult("ClientHello: ", clientResult); - } - cTOs.flip(); - boolean foundSuite = areSuitesPresentCH(cTOs, disabledSuiteIds); - if (foundSuite) { - System.err.println("FAIL: Found disabled suites!"); - return false; - } else { - System.err.println("PASS: No disabled suites found."); - return true; - } - } - - /** - * Create an engine and set only disabled cipher suites. - * The engine should not create the client hello message since the only - * available suites to assert in the client hello are disabled ones. - * - * @param disabledSuiteNames an array of cipher suite names that - * should be disabled cipher suites. - * - * @return true if the engine throws SSLHandshakeException during client - * hello creation, false otherwise. - */ - private static boolean testEngOnlyDisabled(String[] disabledSuiteNames) - throws Exception { - System.err.println( - "\nTest: SSLEngine configured with only disabled suites"); - try { - SSLEngine ssle = makeEngine(); - ssle.setEnabledCipherSuites(disabledSuiteNames); - if (DEBUG) { - listCiphers("Suite set upon creation", ssle); - } - SSLEngineResult clientResult; - ByteBuffer cTOs = makeClientBuf(ssle); - clientResult = ssle.wrap(CLIOUTBUF, cTOs); - if (DEBUG) { - dumpResult("ClientHello: ", clientResult); - } - cTOs.flip(); - } catch (SSLHandshakeException shse) { - System.err.println("PASS: Caught expected exception: " + shse); - return true; - } - System.err.println("FAIL: Expected SSLHandshakeException not thrown"); - return false; - } - - /** - * Create an engine and add some disabled suites to the default - * set of cipher suites. Make sure none of the disabled suites show up - * in the client hello even though they were explicitly added. - * - * @param disabledSuiteNames an array of cipher suite names that - * should be disabled cipher suites. - * @param disabledIds the {@code List} of disabled cipher suite IDs - * to be checked for. - * - * @return true if the test passed (No disabled suites), false otherwise - */ - private static boolean testEngAddDisabled(String[] disabledNames, - List disabledIds) throws Exception { - System.err.println("\nTest: SSLEngine with disabled suites added"); - SSLEngine ssle = makeEngine(); - - // Add disabled suites to the existing engine's set of enabled suites - String[] initialSuites = ssle.getEnabledCipherSuites(); - String[] plusDisSuites = Arrays.copyOf(initialSuites, - initialSuites.length + disabledNames.length); - System.arraycopy(disabledNames, 0, plusDisSuites, - initialSuites.length, disabledNames.length); - ssle.setEnabledCipherSuites(plusDisSuites); - - if (DEBUG) { - listCiphers("Suite set upon creation", ssle); - } - SSLEngineResult clientResult; - ByteBuffer cTOs = makeClientBuf(ssle); - clientResult = ssle.wrap(CLIOUTBUF, cTOs); - if (DEBUG) { - dumpResult("ClientHello: ", clientResult); - } - cTOs.flip(); - boolean foundDisabled = areSuitesPresentCH(cTOs, disabledIds); - if (foundDisabled) { - System.err.println("FAIL: Found disabled suites!"); - return false; - } else { - System.err.println("PASS: No disabled suites found."); - return true; - } - } - - private static SSLEngine makeEngine() throws GeneralSecurityException { - SSLContext ctx = SSLContext.getInstance("TLSv1.2"); - ctx.init(null, null, null); - return ctx.createSSLEngine(); - } - - private static ByteBuffer makeClientBuf(SSLEngine ssle) { - ssle.setUseClientMode(true); - ssle.setNeedClientAuth(false); - SSLSession sess = ssle.getSession(); - ByteBuffer cTOs = ByteBuffer.allocateDirect(sess.getPacketBufferSize()); - return cTOs; - } - - private static void listCiphers(String prefix, SSLEngine ssle) { - System.err.println(prefix + "\n---------------"); - String[] suites = ssle.getEnabledCipherSuites(); - for (String suite : suites) { - System.err.println(suite); - } - System.err.println("---------------"); - } - - /** - * Walk a TLS 1.2 or earlier ClientHello looking for any of the suites - * in the suiteIdList. - * - * @param clientHello a ByteBuffer containing the ClientHello message as - * a complete TLS record. The position of the buffer should be - * at the first byte of the TLS record header. - * @param suiteIdList a List of integer values corresponding to - * TLS cipher suite identifiers. - * - * @return true if at least one of the suites in {@code suiteIdList} - * is found in the ClientHello's cipher suite list - * - * @throws IOException if the data in the {@code clientHello} - * buffer is not a TLS handshake message or is not a client hello. - */ - private static boolean areSuitesPresentCH(ByteBuffer clientHello, - List suiteIdList) throws IOException { - byte val; - - // Process the TLS Record - val = clientHello.get(); - if (val != RECTYPE_HS) { - throw new IOException( - "Not a handshake record, type = " + val); - } - - // Just skip over the version and length - clientHello.position(clientHello.position() + 4); - - // Check the handshake message type - val = clientHello.get(); - if (val != HSMSG_CLIHELLO) { - throw new IOException( - "Not a ClientHello handshake message, type = " + val); - } - - // Skip over the length - clientHello.position(clientHello.position() + 3); - - // Skip over the protocol version (2) and random (32); - clientHello.position(clientHello.position() + 34); - - // Skip past the session ID (variable length <= 32) - int len = Byte.toUnsignedInt(clientHello.get()); - if (len > 32) { - throw new IOException("Session ID is too large, len = " + len); - } - clientHello.position(clientHello.position() + len); - - // Finally, we are at the cipher suites. Walk the list and place them - // into a List. - int csLen = Short.toUnsignedInt(clientHello.getShort()); - if (csLen % 2 != 0) { - throw new IOException("CipherSuite length is invalid, len = " + - csLen); - } - int csCount = csLen / 2; - List csSuiteList = new ArrayList<>(csCount); - log("Found following suite IDs in hello:"); - for (int i = 0; i < csCount; i++) { - int curSuite = Short.toUnsignedInt(clientHello.getShort()); - log(String.format("Suite ID: 0x%04x", curSuite)); - csSuiteList.add(curSuite); - } - - // Now check to see if any of the suites passed in match what is in - // the suite list. - boolean foundMatch = false; - for (Integer cs : suiteIdList) { - if (csSuiteList.contains(cs)) { - System.err.format("Found match for suite ID 0x%04x\n", cs); - foundMatch = true; - break; - } - } - - // We don't care about the rest of the ClientHello message. - // Rewind and return whether we found a match or not. - clientHello.rewind(); - return foundMatch; - } - - private static void dumpResult(String str, SSLEngineResult result) { - System.err.println("The format of the SSLEngineResult is: \n" + - "\t\"getStatus() / getHandshakeStatus()\" +\n" + - "\t\"bytesConsumed() / bytesProduced()\"\n"); - HandshakeStatus hsStatus = result.getHandshakeStatus(); - System.err.println(str + result.getStatus() + "/" + hsStatus + ", " + - result.bytesConsumed() + "/" + result.bytesProduced() + " bytes"); - if (hsStatus == HandshakeStatus.FINISHED) { - System.err.println("\t...ready for application data"); - } - } - - private static void log(String str) { - if (DEBUG) { - System.err.println(str); - } - } } diff --git a/test/jdk/sun/security/ssl/CipherSuite/TLSCipherSuiteWildCardMatchingDisablePartsOfCipherSuite.java b/test/jdk/sun/security/ssl/CipherSuite/TLSCipherSuiteWildCardMatchingDisablePartsOfCipherSuite.java new file mode 100644 index 0000000000000..57919da9a5ab8 --- /dev/null +++ b/test/jdk/sun/security/ssl/CipherSuite/TLSCipherSuiteWildCardMatchingDisablePartsOfCipherSuite.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8341964 + * @summary Add mechanism to disable different parts of TLS cipher suite + * @run testng/othervm TLSCipherSuiteWildCardMatchingDisablePartsOfCipherSuite + */ + +import static org.testng.AssertJUnit.assertTrue; + +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import java.security.Security; +import java.util.List; + +public class TLSCipherSuiteWildCardMatchingDisablePartsOfCipherSuite extends + AbstractDisableCipherSuites { + + private static final String SECURITY_PROPERTY = "jdk.tls.disabledAlgorithms"; + private static final String TEST_ALGORITHMS = + "TLS_RSA_*," + + " TLS_ECDH*WITH_AES_256_GCM_*," + + " TLS_*_anon_WITH_AES_*_SHA," + + " TLS_.*"; // This pattern should not disable anything + private static final String[] CIPHER_SUITES = new String[] { + "TLS_RSA_WITH_AES_256_GCM_SHA384", + "TLS_RSA_WITH_AES_128_GCM_SHA256", + "TLS_RSA_WITH_AES_256_CBC_SHA256", + "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_DH_anon_WITH_AES_128_CBC_SHA", + "TLS_ECDH_anon_WITH_AES_256_CBC_SHA" + }; + static final List CIPHER_SUITES_IDS = List.of( + 0x009D, + 0x009C, + 0x003D, + 0xC02E, + 0xC02C, + 0x0034, + 0xC018 + ); + + @BeforeTest + void setUp() throws Exception { + Security.setProperty(SECURITY_PROPERTY, TEST_ALGORITHMS); + } + + @Test + public void testDefault() throws Exception { + assertTrue(testDefaultCase(CIPHER_SUITES_IDS)); + } + + @Test + public void testAddDisabled() throws Exception { + assertTrue(testEngAddDisabled(CIPHER_SUITES, CIPHER_SUITES_IDS)); + } + + @Test + public void testOnlyDisabled() throws Exception { + assertTrue(testEngOnlyDisabled(CIPHER_SUITES)); + } +} diff --git a/test/jdk/sun/security/ssl/CipherSuite/TLSCipherSuiteWildCardMatchingIllegalArgument.java b/test/jdk/sun/security/ssl/CipherSuite/TLSCipherSuiteWildCardMatchingIllegalArgument.java new file mode 100644 index 0000000000000..d9894868337c6 --- /dev/null +++ b/test/jdk/sun/security/ssl/CipherSuite/TLSCipherSuiteWildCardMatchingIllegalArgument.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8341964 + * @summary Add mechanism to disable different parts of TLS cipher suite + * @run testng/othervm TLSCipherSuiteWildCardMatchingIllegalArgument + */ + +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.fail; + +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import java.security.Security; + +import javax.net.ssl.SSLContext; + +/** + * SSLContext loads "jdk.tls.disabledAlgorithms" system property statically + * when it's being loaded into memory, so we can't call + * Security.setProperty("jdk.tls.disabledAlgorithms") more than once per test + * class. Thus, we need a separate test class each time we need to modify + * "jdk.tls.disabledAlgorithms" config value for testing. + */ +public class TLSCipherSuiteWildCardMatchingIllegalArgument { + + private static final String SECURITY_PROPERTY = + "jdk.tls.disabledAlgorithms"; + private static final String TEST_ALGORITHMS = "ECDHE_*_WITH_AES_256_GCM_*"; + + @BeforeTest + void setUp() throws Exception { + Security.setProperty(SECURITY_PROPERTY, TEST_ALGORITHMS); + } + + @Test + public void testChainedBefore() throws Exception { + try { + SSLContext.getInstance("TLS"); + fail("No IllegalArgumentException was thrown"); + } catch (ExceptionInInitializerError e) { + assertEquals(IllegalArgumentException.class, + e.getCause().getClass()); + assertEquals("Wildcard pattern must start with \"TLS_\"", + e.getCause().getMessage()); + } + } +} diff --git a/test/jdk/sun/security/ssl/ClientHandshaker/LengthCheckTest.java b/test/jdk/sun/security/ssl/ClientHandshaker/LengthCheckTest.java index 6c9dd847ee910..3aecb84bd9408 100644 --- a/test/jdk/sun/security/ssl/ClientHandshaker/LengthCheckTest.java +++ b/test/jdk/sun/security/ssl/ClientHandshaker/LengthCheckTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -270,8 +270,8 @@ public void execTest() throws Exception { * Main entry point for this test. */ public static void main(String args[]) throws Exception { - // Re-enable TLSv1 since test depends on it. - SecurityUtils.removeFromDisabledTlsAlgs("TLSv1"); + // Re-enable TLSv1 and TLS_RSA_* since test depends on it. + SecurityUtils.removeFromDisabledTlsAlgs("TLSv1", "TLS_RSA_*"); List ccsTests = new ArrayList<>(); diff --git a/test/jdk/sun/security/ssl/EngineArgs/DebugReportsOneExtraByte.java b/test/jdk/sun/security/ssl/EngineArgs/DebugReportsOneExtraByte.java index 1ce50662f4647..1446ad786e4dc 100644 --- a/test/jdk/sun/security/ssl/EngineArgs/DebugReportsOneExtraByte.java +++ b/test/jdk/sun/security/ssl/EngineArgs/DebugReportsOneExtraByte.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -93,15 +93,15 @@ public class DebugReportsOneExtraByte extends SSLEngineTemplate { public static void main(String args[]) throws Exception { if (args.length == 0) { - OutputAnalyzer output = ProcessTools.executeTestJvm( + OutputAnalyzer output = ProcessTools.executeTestJava( "-Dtest.src=" + System.getProperty("test.src"), "-Djavax.net.debug=all", "DebugReportsOneExtraByte", "p"); output.shouldContain("WRITE: TLSv1 application_data, length = 8"); System.out.println("Test Passed."); } else { - // Re-enable TLSv1 since test depends on it - SecurityUtils.removeFromDisabledTlsAlgs("TLSv1"); + // Re-enable TLSv1 and TLS_RSA_* since test depends on it + SecurityUtils.removeFromDisabledTlsAlgs("TLSv1", "TLS_RSA_*"); DebugReportsOneExtraByte test = new DebugReportsOneExtraByte(); test.runTest(); diff --git a/test/jdk/sun/security/ssl/SSLEngineImpl/TestBadDNForPeerCA.java b/test/jdk/sun/security/ssl/SSLEngineImpl/TestBadDNForPeerCA.java new file mode 100644 index 0000000000000..06e2aa94b35e9 --- /dev/null +++ b/test/jdk/sun/security/ssl/SSLEngineImpl/TestBadDNForPeerCA.java @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8294985 + * @library /test/lib + * @summary SSLEngine throws IAE during parsing of X500Principal + * @run main/othervm TestBadDNForPeerCA + * @run main/othervm -Djavax.net.debug=all TestBadDNForPeerCA + */ + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.TrustManagerFactory; +import java.io.FileInputStream; +import java.nio.ByteBuffer; +import java.security.KeyStore; +import java.util.Base64; + + +public class TestBadDNForPeerCA { + + private static final String proto = "TLSv1.3"; + + private final SSLContext sslc; + + private SSLEngine serverEngine; // server Engine + private ByteBuffer serverIn; // read side of serverEngine + + private ByteBuffer cTOs; // "reliable" transport client->server + + private static final String keyStoreFile = + System.getProperty("test.src", "./") + + "/../../../../javax/net/ssl/etc/keystore"; + + // the following ClientHello contains a certificate with an + // invalid/unparseable distinguished name + private static final byte[] payload = Base64.getDecoder().decode( + "FgMDAcsBAAHHAwPbDfeUCIStPzVIfXuGgCu56dSJOJ6xeus1W44frG5tciDEcBfYt" + + "/PN/6MFCGojEVcmPw21mVyjYInMo0UozIn4NwBiEwITARMDwCzAK8ypwDDMqMAvA" + + "J/MqgCjAJ4AosAkwCjAI8AnAGsAagBnAEDALsAywC3AMcAmCgAFKsApJcDAFMAJw" + + "BMAOQA4ADMAMsAFwA/ABMAOAJ0AnAA9ADwANgAvAP8BAAEcAAUABQEAAAAAAAoAF" + + "gAUAB0AFwAYABkAHgEAAQEBAgEDAQQACwACAQAAEQAJAAcCAAQAAAAAABcAAAAjA" + + "AAADQAsACoEAwUDBgMIBwgICAQIBQgGCAkICggLBAEFAQYBBAIDAwMBAwICAwIBA" + + "gIAKwAFBAMEAwMALQACAQEAMgAsACoEAwUDBgMIBwgICAQIBQgGCAkICggLBAEFA" + + "QYBBAIDAwMBAwICAwIBAgIALwBrAGkAHQAAAAARACAAZMUAADkwsiaOwcsWAwAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAtAAAAAAAAAAEAADAAAAA="); + + /* + * The following is to set up the keystores. + */ + private static final String passwd = "passphrase"; + + /* + * Main entry point for this demo. + */ + public static void main(String[] args) throws Exception { + + TestBadDNForPeerCA test = new TestBadDNForPeerCA(); + + try { + test.runTest(); + throw new Exception( + "TEST FAILED: Didn't generate any exception"); + } catch (SSLHandshakeException she) { + System.out.println("TEST PASSED: Caught expected exception"); + } + } + + /* + * Create an initialized SSLContext to use for this demo. + */ + + public TestBadDNForPeerCA() throws Exception { + + KeyStore ks = KeyStore.getInstance("JKS"); + KeyStore ts = KeyStore.getInstance("JKS"); + + char[] passphrase = passwd.toCharArray(); + + ks.load(new FileInputStream(keyStoreFile), passphrase); + ts.load(new FileInputStream(keyStoreFile), passphrase); + + KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + kmf.init(ks, passphrase); + + TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); + tmf.init(ts); + + SSLContext sslCtx = SSLContext.getInstance(proto); + + sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + + sslc = sslCtx; + } + + + private void runTest() throws Exception { + + createSSLEngines(); + createBuffers(); + + cTOs = ByteBuffer.wrap(payload); + + System.out.println("injecting client hello"); + + for (int i = 0; i < 10; i++) { //retry if survived + SSLEngineResult serverResult = serverEngine.unwrap(cTOs, serverIn); + System.out.println("server unwrap: " + serverResult); + runDelegatedTasks(serverResult, serverEngine); + } + } + + private void createSSLEngines() throws Exception { + + serverEngine = sslc.createSSLEngine(); + serverEngine.setUseClientMode(false); + serverEngine.setNeedClientAuth(true); + + } + + + private void createBuffers() { + + serverIn = ByteBuffer.allocateDirect(65536); + + cTOs = ByteBuffer.allocateDirect(65536); + + } + + private static void runDelegatedTasks(SSLEngineResult result, + SSLEngine engine) throws Exception { + + if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { + Runnable runnable; + while ((runnable = engine.getDelegatedTask()) != null) { + System.out.println("\trunning delegated task..."); + runnable.run(); + } + + HandshakeStatus hsStatus = engine.getHandshakeStatus(); + if (hsStatus == HandshakeStatus.NEED_TASK) { + throw new Exception("handshake shouldn't need additional " + + "tasks"); + } + System.out.println("\tnew HandshakeStatus: " + hsStatus); + } + } + + +} diff --git a/test/jdk/sun/security/ssl/SSLEngineImpl/TestBadDNForPeerCA12.java b/test/jdk/sun/security/ssl/SSLEngineImpl/TestBadDNForPeerCA12.java new file mode 100644 index 0000000000000..701620a750286 --- /dev/null +++ b/test/jdk/sun/security/ssl/SSLEngineImpl/TestBadDNForPeerCA12.java @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8294985 + * @library /test/lib + * @summary SSLEngine throws IAE during parsing of X500Principal + * @run main/othervm TestBadDNForPeerCA12 + * @run main/othervm -Djavax.net.debug=all TestBadDNForPeerCA12 + */ + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.TrustManagerFactory; +import java.io.FileInputStream; +import java.nio.ByteBuffer; +import java.security.KeyStore; +import java.util.Base64; + +public class TestBadDNForPeerCA12 { + + // Test was originally written for TLSv1.2 + private static final String proto = "TLSv1.2"; + + private final SSLContext sslc; + + protected SSLEngine clientEngine; // client Engine + protected SSLEngine serverEngine; // server Engine + protected ByteBuffer clientOut; // write side of clientEngine + protected ByteBuffer serverOut; // write side of serverEngine + protected ByteBuffer clientIn; // read side of clientEngine + protected ByteBuffer serverIn; // read side of serverEngine + private ByteBuffer cTOs; // "reliable" transport client->server + protected ByteBuffer sTOc; // "reliable" transport server->client + + private static final String keyStoreFile = + System.getProperty("test.src", "./") + + "/../../../../javax/net/ssl/etc/keystore"; + + // this contains a server response with invalid DNs + private static final byte[] serverPayload = Base64.getDecoder().decode( + "FgMDBhICAABVAwPORrwPxSL0DOnCC+cCvQcXxeU1ugjN5XyT0r9qOrlT0iD4I0BgFq" + + "2Hbt7a9cGreNkhniEEhgQIuxa2Ur21VJr9/AA1AAANABcAAAAjAAD/AQABAAsAA1UAA1" + + "IAA08wggNLMIICMwIEVzmbhzANBgkqhkiG9w0BAQsFADBqMQswCQYDVQQGEwJVUzELMA" + + "kGA1UECAwCQ0ExEjAQBgNVBAcMCUN1cGVydGlubzEOMAwGA1UECgwFRHVtbXkxDjAMBg" + + "NVBAsMBUR1bW15MRowGAYDVQQDDBFkdW1teS5leGFtcGxlLmNvbTAeFw0xNjA1MTYxMD" + + "A2MzhaFw0yNjA1MTYxMDA2MzhaMGoxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMB" + + "AGA1UEBwwJQ3VwZXJ0aW5vMQ4wDAYDVQQKDAVEdW1teTEOMAwGA1UECwwFRHVtbXkxGj" + + "AYBgNVBAMMEWR1bW15LmV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMI" + + "IBCgKCAQEAyRtAPlvIbvGfI5ZXN4jBu0dU96b8smVcAdxYnDPylnvmsYGdmYC2C6ddT7" + + "7I9Nlk6BhNmkz6pCGsXLZnUOL+9XOGVWlw5kHDVEGUjeza5BhpZW0G0q00QthZcRuF/F" + + "UkUGzmUuaxgm59VqwxP7dfMERG4gRRXjclMpLm23CShWBhFfooOsiPSFgDtmY4H/LkTU" + + "EbaYuxKRfRKhMKm6GBjCVY7iS9iga728dJ+6BTNAGpKITXI35B+Xf7vpTbc+Zub9vL2f" + + "czcChQvGTZedCaAFi3NWJXR/UTeuv/vte8jJ1YscHSSi2k0P5k3gi9PCmve/sjLrBuh+" + + "D466e/B/swowIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBZFaKJtN/1RkCVev7ZmYEwww" + + "42kE5RpJt7Es2zoxqEGaNx0TA5D6XnEB1XjFUQOgOG7SbUl4NfLpJejuZiQzaX27+7Pu" + + "1FK24SIz61sINpyVtb8flA52mIjH26HzpwSAGmTjFQ7m9Josj/25IqAaRM0AWuPLcwTf" + + "B9zRx3me1LxxrzGhtyZDn1Jhlv0aLS79g33Kuj1HAYMvw7UGan372ufmGiv+g5UYeVvP" + + "Yw3jeahJkSIh96Bb05aJpaogaoE5e+gQanR7E36WGGaicjfN1gIHSOyzZBibcTUhaplS" + + "Q06DfK6UjGmHcVi8X5wD+9NWWiGrlUHcOwKueQOaptTaaXDQACWANAAQIAKgQDBQMGAw" + + "gHCAgIBAgFCAYICQgKCAsEAQUBBgEEAgMDAwEDAgIDAgECAgImAGwwajELMAkRA1UEBh" + + "MCVVMxCzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlDdXBlcnRpbm8xDjAMBgNVBAoTBUR1bW" + + "15MQ4wDAYDVQQLEwVEdW1teTEaMBgGA1UEAxMRZHVtbXkuZXhhbXBsZS5jb20AbDBqMQ" + + "swCREDVQQGEwJVUzELMAkGA1UECAwCQ0ExEjAQBgNVBAcMCUN1cGVydGlubzEOMAwGA1" + + "UECgwFRHVtbXkxDjAMBgNVBAsMBUR1bW15MRowGAYDVQQDDBFkdW1teS5leGFtcGxlLm" + + "NvbQBsMGoxCzAJEQNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJQ3VwZXJ0aW" + + "5vMQ4wDAYDVQQKDAVEdW1teTEOMAwGA1UECwwFRHVtbXkxGjAYBgNVBAMMEWR1bW15Lm" + + "V4YW1wbGUuY29tAGwwajELMAkRA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRIwEAYDVQQHEw" + + "lDdXBlcnRpbm8xDjAMBgNVBAoTBUR1bW15MQ4wDAYDVQQLEwVEdW1teTEaMBgGA1UEAx" + + "MRZHVtbXkuZXhhbXBsZS5jb20AbDBqMQswCREDVQQGEwJVUzELMAkGA1UECBMCQ0ExEj" + + "AQBgNVBAcTCUN1cGVydGlubzEOMAwGA1UEChMFRHVtbXkxDjAMBgNVBAsTBUR1bW15MR" + + "owGAYDVQQDExFkdW1teS5leGFtcGxlLmNvbQ4AAAA=" + ); + + /* + * The following is to set up the keystores. + */ + private static final String passwd = "passphrase"; + + /* + * Main entry point for this demo. + */ + public static void main(String[] args) throws Exception { + + TestBadDNForPeerCA12 test = new TestBadDNForPeerCA12(); + + try { + test.runTest(); + throw new Exception( + "TEST FAILED: Didn't generate any exception"); + } catch (SSLHandshakeException she) { + System.out.println("TEST PASSED: Caught expected exception"); + } + } + + /* + * Create an initialized SSLContext to use for this demo. + */ + + public TestBadDNForPeerCA12() throws Exception { + + KeyStore ks = KeyStore.getInstance("JKS"); + KeyStore ts = KeyStore.getInstance("JKS"); + + char[] passphrase = passwd.toCharArray(); + + ks.load(new FileInputStream(keyStoreFile), passphrase); + ts.load(new FileInputStream(keyStoreFile), passphrase); + + KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + kmf.init(ks, passphrase); + + TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); + tmf.init(ts); + + SSLContext sslCtx = SSLContext.getInstance(proto); + + sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + + sslc = sslCtx; + } + + + private void runTest() throws Exception { + + createSSLEngines(); + createBuffers(); + + /* + * the following was used to generate the serverPayload value + */ + // ignore output + /*SSLEngineResult clientResult = clientEngine.wrap(clientOut, cTOs); + runDelegatedTasks(clientResult, clientEngine); + cTOs.flip(); + + // ignore output + SSLEngineResult serverResult = serverEngine.unwrap(cTOs, serverIn); + runDelegatedTasks(serverResult, serverEngine); + // server hello, cert material, etc + SSLEngineResult serverWrapResult = serverEngine.wrap(serverOut, sTOc); + runDelegatedTasks(serverWrapResult, serverEngine); + sTOc.flip(); + ByteBuffer sTOcBuff = sTOc.asReadOnlyBuffer(); + byte[] serverContents = new byte[sTOcBuff.remaining()]; + sTOcBuff.get(serverContents); + System.out.println("sw: " + Base64.getEncoder().encodeToString + (serverContents));*/ + + System.out.println("sending client hello"); + SSLEngineResult clientResult = clientEngine.wrap(clientOut, cTOs); + runDelegatedTasks(clientResult, clientEngine); + + cTOs.flip(); + + sTOc = ByteBuffer.wrap(serverPayload); + + SSLEngineResult clientHelloResult = clientEngine.unwrap(sTOc, clientIn); + System.out.println("client unwrap: " + clientHelloResult); + runDelegatedTasks(clientHelloResult, clientEngine); + + SSLEngineResult clientExGen = clientEngine.wrap(clientIn, cTOs); + runDelegatedTasks(clientExGen, clientEngine); + + } + + private void createSSLEngines() { + clientEngine = sslc.createSSLEngine(); + clientEngine.setEnabledProtocols(new String[] {proto}); + clientEngine.setUseClientMode(true); + clientEngine.setEnabledCipherSuites(new String[] + {"TLS_RSA_WITH_AES_256_CBC_SHA"}); + + serverEngine = sslc.createSSLEngine(); + serverEngine.setEnabledProtocols(new String[] {proto}); + serverEngine.setUseClientMode(false); + serverEngine.setNeedClientAuth(true); + serverEngine.setEnabledCipherSuites(new String[] + {"TLS_RSA_WITH_AES_256_CBC_SHA"}); + } + + private void createBuffers() { + cTOs = ByteBuffer.allocateDirect(65536); + + clientIn = ByteBuffer.allocateDirect(65536); + + clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes()); + + sTOc = ByteBuffer.allocateDirect(65536); + + serverOut = ByteBuffer.wrap("Hi Client, I'm Server".getBytes()); + + serverIn = ByteBuffer.allocateDirect(65536); + } + + private static void runDelegatedTasks(SSLEngineResult result, + SSLEngine engine) throws Exception { + + if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { + Runnable runnable; + while ((runnable = engine.getDelegatedTask()) != null) { + System.out.println("\trunning delegated task..."); + runnable.run(); + } + + HandshakeStatus hsStatus = engine.getHandshakeStatus(); + if (hsStatus == HandshakeStatus.NEED_TASK) { + throw new Exception("handshake shouldn't need additional " + + "tasks"); + } + System.out.println("\tnew HandshakeStatus: " + hsStatus); + } + } +} diff --git a/test/jdk/sun/security/ssl/SSLLogger/DebugPropertyValuesTest.java b/test/jdk/sun/security/ssl/SSLLogger/DebugPropertyValuesTest.java new file mode 100644 index 0000000000000..c9ad335a45ede --- /dev/null +++ b/test/jdk/sun/security/ssl/SSLLogger/DebugPropertyValuesTest.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8350582 + * @library /test/lib /javax/net/ssl/templates + * @summary Correct the parsing of the ssl value in javax.net.debug + * @run junit DebugPropertyValuesTest + */ + +// A test to verify debug output for different javax.net.debug scenarios + +import jdk.test.lib.process.ProcessTools; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.stream.Stream; + +import jdk.test.lib.process.OutputAnalyzer; + +public class DebugPropertyValuesTest extends SSLSocketTemplate { + + private static final Path LOG_FILE = Path.of("logging.conf"); + private static final HashMap> debugMessages = new HashMap<>(); + + static { + debugMessages.put("handshake", + List.of("Produced ClientHello handshake message", + "supported_versions")); + debugMessages.put("keymanager", List.of("choosing key:")); + debugMessages.put("packet", List.of("Raw write")); + debugMessages.put("plaintext", List.of("Plaintext before ENCRYPTION")); + debugMessages.put("record", List.of("handshake, length =", "WRITE:")); + debugMessages.put("session", List.of("Session initialized:")); + debugMessages.put("sslctx", List.of("trigger seeding of SecureRandom")); + debugMessages.put("ssl", List.of("jdk.tls.keyLimits:")); + debugMessages.put("trustmanager", List.of("adding as trusted certificates")); + debugMessages.put("verbose", List.of("Ignore unsupported cipher suite:")); + debugMessages.put("handshake-expand", + List.of("\"logger\".*: \"javax.net.ssl\",", + "\"message\".*: \"Produced ClientHello handshake message")); + debugMessages.put("record-expand", + List.of("\"logger\".*: \"javax.net.ssl\",", + "\"message\".*: \"READ: TLSv1.2 application_data")); + debugMessages.put("help", + List.of("print the help messages", + "debugging can be widened with:")); + debugMessages.put("javax.net.debug", + List.of("properties: Initial security property:", + "certpath: Cert path validation succeeded")); + debugMessages.put("logger", + List.of("FINE: adding as trusted certificates", + "FINE: WRITE: TLSv1.3 application_data")); + } + + @BeforeAll + static void setup() throws Exception { + Files.writeString(LOG_FILE, ".level = ALL\n" + + "handlers= java.util.logging.ConsoleHandler\n" + + "java.util.logging.ConsoleHandler.level = ALL\n"); + } + + private static Stream patternMatches() { + return Stream.of( + // all should print everything + Arguments.of(List.of("-Djavax.net.debug=all"), + List.of("handshake", "keymanager", "packet", + "plaintext", "record", "session", "ssl", + "sslctx", "trustmanager", "verbose")), + // ssl should print most details except verbose details + Arguments.of(List.of("-Djavax.net.debug=ssl"), + List.of("handshake", "keymanager", + "record", "session", "ssl", + "sslctx", "trustmanager", "verbose")), + // allow expand option for more verbose output + Arguments.of(List.of("-Djavax.net.debug=ssl,handshake,expand"), + List.of("handshake", "handshake-expand", "keymanager", + "record", "session", "record-expand", "ssl", + "sslctx", "trustmanager", "verbose")), + // filtering on record option, with expand + Arguments.of(List.of("-Djavax.net.debug=ssl:record,expand"), + List.of("handshake", "handshake-expand", "keymanager", + "record", "record-expand", "session", "ssl", + "sslctx", "trustmanager", "verbose")), + // this test is equivalent to ssl:record mode + Arguments.of(List.of("-Djavax.net.debug=ssl,record"), + List.of("handshake", "keymanager", "record", + "session", "ssl", "sslctx", + "trustmanager", "verbose")), + // example of test where no "ssl" value is passed + // handshake debugging with verbose mode + // only verbose gets printed. Needs fixing (JDK-8044609) + Arguments.of(List.of("-Djavax.net.debug=handshake:verbose"), + List.of("verbose")), + // another example of test where no "ssl" value is passed + Arguments.of(List.of("-Djavax.net.debug=record"), + List.of("record")), + // ignore bad sub-option. treat like "ssl" + Arguments.of(List.of("-Djavax.net.debug=ssl,typo"), + List.of("handshake", "keymanager", + "record", "session", "ssl", + "sslctx", "trustmanager", "verbose")), + // ssltypo contains "ssl". Treat like "ssl" + Arguments.of(List.of("-Djavax.net.debug=ssltypo"), + List.of("handshake", "keymanager", + "record", "session", "ssl", + "sslctx", "trustmanager", "verbose")), + // plaintext is valid for record option + Arguments.of(List.of("-Djavax.net.debug=ssl:record:plaintext"), + List.of("handshake", "keymanager", "plaintext", + "record", "session", "ssl", + "sslctx", "trustmanager", "verbose")), + Arguments.of(List.of("-Djavax.net.debug=ssl:trustmanager"), + List.of("handshake", "keymanager", "record", "session", + "ssl", "sslctx", "trustmanager", "verbose")), + Arguments.of(List.of("-Djavax.net.debug=ssl:sslctx"), + List.of("handshake", "keymanager", "record", "session", + "ssl", "sslctx", "trustmanager", "verbose")), + // help message test. Should exit without running test + Arguments.of(List.of("-Djavax.net.debug=help"), + List.of("help")), + // add in javax.net.debug sanity test + Arguments.of(List.of("-Djavax.net.debug=ssl:trustmanager", + "-Djava.security.debug=all"), + List.of("handshake", "javax.net.debug", "keymanager", + "record", "session", "ssl", "sslctx", + "trustmanager", "verbose")), + // empty invokes System.Logger use + Arguments.of(List.of("-Djavax.net.debug", + "-Djava.util.logging.config.file=" + LOG_FILE), + List.of("handshake", "keymanager", "logger", "packet", + "plaintext", "record", "session", "ssl", + "sslctx", "trustmanager", "verbose")) + ); + } + + @ParameterizedTest + @MethodSource("patternMatches") + public void checkDebugOutput(List params, + List expected) throws Exception { + + List args = new ArrayList<>(params); + args.add("DebugPropertyValuesTest"); + OutputAnalyzer outputAnalyzer = ProcessTools.executeTestJava(args); + outputAnalyzer.shouldHaveExitValue(0); + for (String s : debugMessages.keySet()) { + for (String output : debugMessages.get(s)) { + if (expected.contains(s)) { + outputAnalyzer.shouldMatch(output); + } else { + outputAnalyzer.shouldNotMatch(output); + } + } + } + } +} diff --git a/test/jdk/sun/security/ssl/SSLLogger/LoggingFormatConsistency.java b/test/jdk/sun/security/ssl/SSLLogger/LoggingFormatConsistency.java index c614ca62f29a9..5cfa9c9a68008 100644 --- a/test/jdk/sun/security/ssl/SSLLogger/LoggingFormatConsistency.java +++ b/test/jdk/sun/security/ssl/SSLLogger/LoggingFormatConsistency.java @@ -50,7 +50,7 @@ public class LoggingFormatConsistency extends SSLSocketTemplate { public static void main(String[] args) throws Exception { if (args.length != 0) { // A non-empty set of arguments occurs when the "runTest" argument - // is passed to the test via ProcessTools::executeTestJvm. + // is passed to the test via ProcessTools::executeTestJava. // // This is done because an OutputAnalyzer is unable to read // the output of the current running JVM, and must therefore create @@ -71,7 +71,7 @@ public static void main(String[] args) throws Exception { System.out.println("TESTING " + expectedTLSVersion); var activeTLSProtocol = "-Djdk.tls.client.protocols=" + expectedTLSVersion; - var output = ProcessTools.executeTestJvm( + var output = ProcessTools.executeTestJava( testSrc, activeTLSProtocol, javaxNetDebug, diff --git a/test/jdk/sun/security/ssl/SSLSessionImpl/NoInvalidateSocketException.java b/test/jdk/sun/security/ssl/SSLSessionImpl/NoInvalidateSocketException.java index ed488b946c081..d3baef18a2667 100644 --- a/test/jdk/sun/security/ssl/SSLSessionImpl/NoInvalidateSocketException.java +++ b/test/jdk/sun/security/ssl/SSLSessionImpl/NoInvalidateSocketException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,7 @@ /* * @test - * @bug 8274736 8277970 + * @bug 8274736 8277970 8355262 * @summary Concurrent read/close of SSLSockets causes SSLSessions to be * invalidated unnecessarily * @library /javax/net/ssl/templates @@ -287,7 +287,7 @@ public void doServerSide() throws Exception { // Signal the client, the server is ready to accept connection. serverCondition.countDown(); - // Try to accept a connection in 5 seconds. + // Try to accept a connection in 10 seconds. // We will do this in a loop until the client flips the // finished variable to true SSLSocket sslSocket; @@ -353,7 +353,7 @@ public void doServerSide() throws Exception { public void configureServerSocket(SSLServerSocket socket) { try { socket.setReuseAddress(true); - socket.setSoTimeout(5000); + socket.setSoTimeout(10000); } catch (SocketException se) { // Rethrow as unchecked to satisfy the override signature throw new RuntimeException(se); diff --git a/test/jdk/sun/security/ssl/SSLSessionImpl/ResumeClientTLS12withSNI.java b/test/jdk/sun/security/ssl/SSLSessionImpl/ResumeClientTLS12withSNI.java new file mode 100644 index 0000000000000..c9768c1756c61 --- /dev/null +++ b/test/jdk/sun/security/ssl/SSLSessionImpl/ResumeClientTLS12withSNI.java @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8350830 + * @summary TLS 1.2 Client session resumption having ServerNameIndication + * @modules java.base/sun.security.tools.keytool + * @run main/othervm -Djavax.net.debug=all ResumeClientTLS12withSNI + */ + +import javax.net.ssl.*; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import java.io.*; +import java.nio.ByteBuffer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.util.*; + +public class ResumeClientTLS12withSNI { + + /* + * Enables logging of the SSLEngine operations. + */ + private static final boolean logging = true; + + private static SSLContext sslc; + + private SSLEngine clientEngine; // client Engine + private ByteBuffer clientOut; // write side of clientEngine + private ByteBuffer clientIn; // read side of clientEngine + + private SSLEngine serverEngine; // server Engine + private ByteBuffer serverOut; // write side of serverEngine + private ByteBuffer serverIn; // read side of serverEngine + + /* + * For data transport, this example uses local ByteBuffers. + */ + private ByteBuffer cTOs; // "reliable" transport client->server + private ByteBuffer sTOc; // "reliable" transport server->client + + /* + * The following is to set up the keystores. + */ + private static final String keyFilename = "ks_san.p12"; + private static final String trustFilename = "ks_san.p12"; + private static final char[] passphrase = "123456".toCharArray(); + + private static final String HOST_NAME = "arf.yak.foo.localhost123456.localhost123456.localhost123456.localhost123456.localhost123456.localhost123456." + + "localhost123456.localhost123456.localhost123456.localhost123456.localhost123456.localhost123456"; + private static final SNIMatcher SNI_MATCHER = SNIHostName.createSNIMatcher("arf\\.yak\\.foo.*"); + + /* + * Main entry point for this test. + */ + public static void main(String args[]) throws Exception { + Files.deleteIfExists(Path.of(keyFilename)); + + sun.security.tools.keytool.Main.main( + ("-keystore " + keyFilename + " -storepass 123456 -keypass 123456 -dname" + + " CN=test" + " -alias ks_san -genkeypair -keyalg rsa -ext " + + "san=dns:localhost123.localhost123.localhost123.localhost123." + + "localhost123.localhost123.localhost123.localhost123.localhost123." + + "localhost123.localhost123.localhost123.localhost123.localhost123." + + "localhost123.localhost123.localhost123.localhost123.localhost123.com," + + "dns:localhost456").split(" ")); + final ResumeClientTLS12withSNI clientSession = new ResumeClientTLS12withSNI("TLSv1.2"); + for (int i = 0; i < 2; i++) { + clientSession.runTest(); + } + + Files.deleteIfExists(Path.of(keyFilename)); + } + + public ResumeClientTLS12withSNI(final String sslProtocol) throws Exception { + + KeyManagerFactory kmf = makeKeyManagerFactory(keyFilename, + passphrase); + TrustManagerFactory tmf = makeTrustManagerFactory(trustFilename, + passphrase); + + SSLContext sslCtx = SSLContext.getInstance(sslProtocol); + + sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + + sslc = sslCtx; + } + + private static KeyManagerFactory makeKeyManagerFactory(String ksPath, + char[] pass) throws GeneralSecurityException, IOException { + KeyManagerFactory kmf; + KeyStore ks = KeyStore.getInstance("PKCS12"); + + try (FileInputStream fsIn = new FileInputStream(ksPath)) { + ks.load(fsIn, pass); + kmf = KeyManagerFactory.getInstance("SunX509"); + kmf.init(ks, pass); + } + return kmf; + } + + private static TrustManagerFactory makeTrustManagerFactory(String tsPath, + char[] pass) throws GeneralSecurityException, IOException { + TrustManagerFactory tmf; + KeyStore ts = KeyStore.getInstance("JKS"); + + try (FileInputStream fsIn = new FileInputStream(tsPath)) { + ts.load(fsIn, pass); + tmf = TrustManagerFactory.getInstance("SunX509"); + tmf.init(ts); + } + return tmf; + } + + /* + * Run the test. + * + * Sit in a tight loop, both engines calling wrap/unwrap regardless + * of whether data is available or not. We do this until both engines + * report back they are closed. + * + * The main loop handles all of the I/O phases of the SSLEngine's + * lifetime: + * + * initial handshaking + * application data transfer + * engine closing + * + */ + private void runTest() throws Exception { + boolean dataDone = false; + + createSSLEngines(); + createBuffers(); + + SSLEngineResult clientResult; // results from client's last operation + SSLEngineResult serverResult; // results from server's last operation + + /* + * Examining the SSLEngineResults could be much more involved, + * and may alter the overall flow of the application. + * + * For example, if we received a BUFFER_OVERFLOW when trying + * to write to the output pipe, we could reallocate a larger + * pipe, but instead we wait for the peer to drain it. + */ + while (!isEngineClosed(clientEngine) || + !isEngineClosed(serverEngine)) { + + log("================"); + + clientResult = clientEngine.wrap(clientOut, cTOs); + log("client wrap: ", clientResult); + runDelegatedTasks(clientResult, clientEngine); + + serverResult = serverEngine.wrap(serverOut, sTOc); + log("server wrap: ", serverResult); + runDelegatedTasks(serverResult, serverEngine); + + cTOs.flip(); + sTOc.flip(); + + log("-------"); + + clientResult = clientEngine.unwrap(sTOc, clientIn); + log("client unwrap: ", clientResult); + runDelegatedTasks(clientResult, clientEngine); + + serverResult = serverEngine.unwrap(cTOs, serverIn); + log("server unwrap: ", serverResult); + runDelegatedTasks(serverResult, serverEngine); + + cTOs.compact(); + sTOc.compact(); + + /* + * After we've transfered all application data between the client + * and server, we close the clientEngine's outbound stream. + * This generates a close_notify handshake message, which the + * server engine receives and responds by closing itself. + * + * In normal operation, each SSLEngine should call + * closeOutbound(). To protect against truncation attacks, + * SSLEngine.closeInbound() should be called whenever it has + * determined that no more input data will ever be + * available (say a closed input stream). + */ + if (!dataDone && (clientOut.limit() == serverIn.position()) && + (serverOut.limit() == clientIn.position())) { + + /* + * A sanity check to ensure we got what was sent. + */ + checkTransfer(serverOut, clientIn); + checkTransfer(clientOut, serverIn); + + log("\tClosing clientEngine's *OUTBOUND*..."); + clientEngine.closeOutbound(); + // serverEngine.closeOutbound(); + dataDone = true; + } + } + } + + /* + * Using the SSLContext created during object creation, + * create/configure the SSLEngines we'll use for this test. + */ + private void createSSLEngines() throws Exception { + /* + * Configure the serverEngine to act as a server in the SSL/TLS + * handshake. + */ + serverEngine = sslc.createSSLEngine(); + serverEngine.setUseClientMode(false); + serverEngine.setNeedClientAuth(false); + SSLParameters servSSLParams = serverEngine.getSSLParameters(); + servSSLParams.setSNIMatchers(List.of(SNI_MATCHER)); + serverEngine.setSSLParameters(servSSLParams); + + /* + * Similar to above, but using client mode instead. + */ + clientEngine = sslc.createSSLEngine(HOST_NAME, 80); + clientEngine.setUseClientMode(true); + SSLParameters cliSSLParams = clientEngine.getSSLParameters(); + clientEngine.setSSLParameters(cliSSLParams); + } + + /* + * Create and size the buffers appropriately. + */ + private void createBuffers() { + + /* + * We'll assume the buffer sizes are the same + * between client and server. + */ + SSLSession session = clientEngine.getSession(); + int appBufferMax = session.getApplicationBufferSize(); + int netBufferMax = session.getPacketBufferSize(); + + /* + * We'll make the input buffers a bit bigger than the max needed + * size, so that unwrap()s following a successful data transfer + * won't generate BUFFER_OVERFLOWS. + * + * We'll use a mix of direct and indirect ByteBuffers for + * tutorial purposes only. In reality, only use direct + * ByteBuffers when they give a clear performance enhancement. + */ + clientIn = ByteBuffer.allocate(appBufferMax + 50); + serverIn = ByteBuffer.allocate(appBufferMax + 50); + + cTOs = ByteBuffer.allocateDirect(netBufferMax); + sTOc = ByteBuffer.allocateDirect(netBufferMax); + + clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes()); + serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes()); + } + + /* + * If the result indicates that we have outstanding tasks to do, + * go ahead and run them in this thread. + */ + private static void runDelegatedTasks(SSLEngineResult result, + SSLEngine engine) throws Exception { + + if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { + Runnable runnable; + while ((runnable = engine.getDelegatedTask()) != null) { + log("\trunning delegated task..."); + runnable.run(); + } + HandshakeStatus hsStatus = engine.getHandshakeStatus(); + if (hsStatus == HandshakeStatus.NEED_TASK) { + throw new Exception( + "handshake shouldn't need additional tasks"); + } + log("\tnew HandshakeStatus: " + hsStatus); + } + } + + private static boolean isEngineClosed(SSLEngine engine) { + return (engine.isOutboundDone() && engine.isInboundDone()); + } + + /* + * Simple check to make sure everything came across as expected. + */ + private static void checkTransfer(ByteBuffer a, ByteBuffer b) + throws Exception { + a.flip(); + b.flip(); + + if (!a.equals(b)) { + throw new Exception("Data didn't transfer cleanly"); + } else { + log("\tData transferred cleanly"); + } + + a.position(a.limit()); + b.position(b.limit()); + a.limit(a.capacity()); + b.limit(b.capacity()); + } + + /* + * Logging code + */ + private static boolean resultOnce = true; + + private static void log(String str, SSLEngineResult result) { + if (!logging) { + return; + } + if (resultOnce) { + resultOnce = false; + System.out.println("The format of the SSLEngineResult is: \n" + + "\t\"getStatus() / getHandshakeStatus()\" +\n" + + "\t\"bytesConsumed() / bytesProduced()\"\n"); + } + HandshakeStatus hsStatus = result.getHandshakeStatus(); + log(str + + result.getStatus() + "/" + hsStatus + ", " + + result.bytesConsumed() + "/" + result.bytesProduced() + + " bytes"); + if (hsStatus == HandshakeStatus.FINISHED) { + log("\t...ready for application data"); + } + } + + private static void log(String str) { + if (logging) { + System.out.println(str); + } + } +} diff --git a/test/jdk/sun/security/ssl/SSLSocketImpl/IgnorableExceptionMessages.java b/test/jdk/sun/security/ssl/SSLSocketImpl/IgnorableExceptionMessages.java index 708b8f0d11bb8..a1c9cad22711a 100644 --- a/test/jdk/sun/security/ssl/SSLSocketImpl/IgnorableExceptionMessages.java +++ b/test/jdk/sun/security/ssl/SSLSocketImpl/IgnorableExceptionMessages.java @@ -49,7 +49,7 @@ public class IgnorableExceptionMessages extends SSLSocketTemplate { public static void main(String[] args) throws Exception { if (args.length > 0) { // A non-empty set of arguments occurs when the "runTest" argument - // is passed to the test via ProcessTools::executeTestJvm. + // is passed to the test via ProcessTools::executeTestJava. // // This is done because an OutputAnalyzer is unable to read // the output of the current running JVM, and must therefore create @@ -67,7 +67,7 @@ public static void main(String[] args) throws Exception { className, extraArgument); - OutputAnalyzer output = ProcessTools.executeTestJvm(jvmArgs); + OutputAnalyzer output = ProcessTools.executeTestJava(jvmArgs); if (output.getExitValue() != 0) { output.asLines().forEach(System.out::println); // No need to dump the output unless the test fails diff --git a/test/jdk/sun/security/ssl/SSLSocketImpl/NonAutoClose.java b/test/jdk/sun/security/ssl/SSLSocketImpl/NonAutoClose.java index 2209bd3a64857..82f3b7d153777 100644 --- a/test/jdk/sun/security/ssl/SSLSocketImpl/NonAutoClose.java +++ b/test/jdk/sun/security/ssl/SSLSocketImpl/NonAutoClose.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,229 +29,144 @@ /* * @test * @bug 4404399 - * @ignore this test does not work any more as the TLS spec changes the - * behaviors of close_notify. + * @comment this test does not work in TLSv1.3 as the spec changes the + * behaviors of close_notify. * @summary When a layered SSL socket is closed, it should wait for close_notify - * @run main/othervm NonAutoClose + * @library /test/lib /javax/net/ssl/templates + * @run main/othervm NonAutoClose TLSv1 + * @run main/othervm NonAutoClose TLSv1.1 + * @run main/othervm NonAutoClose TLSv1.2 * @author Brad Wetmore */ import java.io.*; +import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; +import java.util.concurrent.CountDownLatch; import javax.net.ssl.*; -import java.security.cert.X509Certificate; -import java.security.cert.CertificateException; +import jdk.test.lib.security.SecurityUtils; -public class NonAutoClose { - /* - * ============================================================= - * Set the various variables needed for the tests, then - * specify what tests to run on each side. - */ +import static jdk.test.lib.Asserts.assertEquals; - /* - * Should we run the client or server in a separate thread? - * Both sides can throw exceptions, but do you have a preference - * as to which side should be the main thread. - */ - private static boolean separateServerThread = true; - - /* - * Where do we find the keystores? - */ - private final static String pathToStores = "../../../../javax/net/ssl/etc"; - private final static String keyStoreFile = "keystore"; - private final static String trustStoreFile = "truststore"; - private final static String passwd = "passphrase"; - private final static char[] cpasswd = "passphrase".toCharArray(); +public class NonAutoClose extends SSLContextTemplate { /* * Is the server ready to serve? */ - volatile static boolean serverReady = false; + private static final CountDownLatch SERVER_READY = new CountDownLatch(1); /* * Turn on SSL debugging? */ - private final static boolean DEBUG = false; - private final static boolean VERBOSE = true; - private final static int NUM_ITERATIONS = 10; + private final static boolean DEBUG = Boolean.getBoolean("test.debug"); + private final static int NUM_ITERATIONS = 10; private final static int PLAIN_SERVER_VAL = 1; private final static int PLAIN_CLIENT_VAL = 2; private final static int TLS_SERVER_VAL = 3; private final static int TLS_CLIENT_VAL = 4; - /* - * If the client or server is doing some kind of object creation - * that the other side depends on, and that thread prematurely - * exits, you may experience a hang. The test harness will - * terminate all hung threads after its timeout has expired, - * currently 3 minutes by default, but you might try to be - * smart about it.... - */ - - void expectValue(int got, int expected, String msg) throws IOException { - if (VERBOSE) { - System.err.println(msg + ": read (" + got + ")"); - } - if (got != expected) { - throw new IOException(msg + ": read (" + got - + ") but expecting(" + expected + ")"); - } - } - - /* * Define the server side of the test. - * - * If the server prematurely exits, serverReady will be set to true - * to avoid infinite hangs. */ + private void doServerSide() throws Exception { + System.out.println("Starting server"); + SSLSocketFactory sslsf = createServerSSLContext().getSocketFactory(); - void doServerSide() throws Exception { - if (VERBOSE) { - System.err.println("Starting server"); - } + try (ServerSocket serverSocket = new ServerSocket(SERVER_PORT)) { + SERVER_PORT = serverSocket.getLocalPort(); - /* - * Setup the SSL stuff - */ - SSLSocketFactory sslsf = - (SSLSocketFactory) SSLSocketFactory.getDefault(); + /* + * Signal Client, we're ready for his connect. + */ + System.out.println("Signal server ready"); + SERVER_READY.countDown(); - ServerSocket serverSocket = new ServerSocket(SERVER_PORT); + try (Socket plainSocket = serverSocket.accept(); + InputStream is = plainSocket.getInputStream(); + OutputStream os = plainSocket.getOutputStream()) { - SERVER_PORT = serverSocket.getLocalPort(); + assertEquals(PLAIN_CLIENT_VAL, is.read()); - /* - * Signal Client, we're ready for his connect. - */ - serverReady = true; + os.write(PLAIN_SERVER_VAL); + os.flush(); - Socket plainSocket = serverSocket.accept(); - InputStream is = plainSocket.getInputStream(); - OutputStream os = plainSocket.getOutputStream(); - - expectValue(is.read(), PLAIN_CLIENT_VAL, "Server"); - - os.write(PLAIN_SERVER_VAL); - os.flush(); - - for (int i = 1; i <= NUM_ITERATIONS; i++) { - if (VERBOSE) { - System.err.println("================================="); - System.err.println("Server Iteration #" + i); - } + for (int i = 1; i <= NUM_ITERATIONS; i++) { + if (DEBUG) { + System.out.println("================================="); + System.out.println("Server Iteration #" + i); + } - SSLSocket ssls = (SSLSocket) sslsf.createSocket(plainSocket, - SERVER_NAME, plainSocket.getPort(), false); + try (SSLSocket ssls = (SSLSocket) sslsf.createSocket(plainSocket, + plainSocket.getInetAddress().getHostName(), + plainSocket.getPort(), false)) { - ssls.setUseClientMode(false); - InputStream sslis = ssls.getInputStream(); - OutputStream sslos = ssls.getOutputStream(); + ssls.setEnabledProtocols(new String[]{protocol}); + ssls.setUseClientMode(false); + try (InputStream sslis = ssls.getInputStream(); + OutputStream sslos = ssls.getOutputStream()) { - expectValue(sslis.read(), TLS_CLIENT_VAL, "Server"); + assertEquals(TLS_CLIENT_VAL, sslis.read()); - sslos.write(TLS_SERVER_VAL); - sslos.flush(); + sslos.write(TLS_SERVER_VAL); + sslos.flush(); + } + } + } - sslis.close(); - sslos.close(); - ssls.close(); + assertEquals(PLAIN_CLIENT_VAL, is.read()); - if (VERBOSE) { - System.err.println("TLS socket is closed"); + os.write(PLAIN_SERVER_VAL); + os.flush(); } } - - expectValue(is.read(), PLAIN_CLIENT_VAL, "Server"); - - os.write(PLAIN_SERVER_VAL); - os.flush(); - - is.close(); - os.close(); - plainSocket.close(); - - if (VERBOSE) { - System.err.println("Server plain socket is closed"); - } } /* * Define the client side of the test. - * - * If the server prematurely exits, serverReady will be set to true - * to avoid infinite hangs. */ private void doClientSide() throws Exception { /* * Wait for server to get started. */ - while (!serverReady) { - Thread.sleep(50); - } - - if (VERBOSE) { - System.err.println("Starting client"); - } - - /* - * Setup the SSL stuff - */ - SSLSocketFactory sslsf = - (SSLSocketFactory) SSLSocketFactory.getDefault(); - - Socket plainSocket = new Socket(SERVER_NAME, SERVER_PORT); - InputStream is = plainSocket.getInputStream(); - OutputStream os = plainSocket.getOutputStream(); - - os.write(PLAIN_CLIENT_VAL); - os.flush(); + System.out.println("Waiting for server ready"); + SERVER_READY.await(); - expectValue(is.read(), PLAIN_SERVER_VAL, "Client"); + SSLSocketFactory sslsf = createClientSSLContext().getSocketFactory(); - for (int i = 1; i <= NUM_ITERATIONS; i++) { - if (VERBOSE) { - System.err.println("==================================="); - System.err.println("Client Iteration #" + i); - } + try (Socket plainSocket = new Socket(InetAddress.getLocalHost(), SERVER_PORT); + InputStream is = plainSocket.getInputStream(); + OutputStream os = plainSocket.getOutputStream()) { - SSLSocket ssls = (SSLSocket) sslsf.createSocket(plainSocket, - SERVER_NAME, plainSocket.getPort(), false); + os.write(PLAIN_CLIENT_VAL); + os.flush(); - ssls.setUseClientMode(true); + assertEquals(PLAIN_SERVER_VAL, is.read()); - InputStream sslis = ssls.getInputStream(); - OutputStream sslos = ssls.getOutputStream(); - - sslos.write(TLS_CLIENT_VAL); - sslos.flush(); + for (int i = 1; i <= NUM_ITERATIONS; i++) { + if (DEBUG) { + System.out.println("==================================="); + System.out.println("Client Iteration #" + i); + } + try (SSLSocket ssls = (SSLSocket) sslsf.createSocket(plainSocket, + plainSocket.getInetAddress().getHostName(), + plainSocket.getPort(), false); + InputStream sslis = ssls.getInputStream(); + OutputStream sslos = ssls.getOutputStream()) { - expectValue(sslis.read(), TLS_SERVER_VAL, "Client"); + ssls.setUseClientMode(true); - sslis.close(); - sslos.close(); - ssls.close(); + sslos.write(TLS_CLIENT_VAL); + sslos.flush(); - if (VERBOSE) { - System.err.println("Client TLS socket is closed"); + assertEquals(TLS_SERVER_VAL, sslis.read()); + } } - } - os.write(PLAIN_CLIENT_VAL); - os.flush(); - - expectValue(is.read(), PLAIN_SERVER_VAL, "Client"); - - is.close(); - os.close(); - plainSocket.close(); - - if (VERBOSE) { - System.err.println("Client plain socket is closed"); + os.write(PLAIN_CLIENT_VAL); + os.flush(); + assertEquals(PLAIN_SERVER_VAL, is.read()); } } @@ -261,25 +176,13 @@ private void doClientSide() throws Exception { */ private volatile int SERVER_PORT = 0; - private final static String SERVER_NAME = "localhost"; - - private volatile Exception serverException = null; private volatile Exception clientException = null; - private final static String keyFilename = - System.getProperty("test.src", ".") + "/" + pathToStores + - "/" + keyStoreFile; - private final static String trustFilename = - System.getProperty("test.src", ".") + "/" + pathToStores + - "/" + trustStoreFile; - - - // Used for running test standalone public static void main(String[] args) throws Exception { - System.setProperty("javax.net.ssl.keyStore", keyFilename); - System.setProperty("javax.net.ssl.keyStorePassword", passwd); - System.setProperty("javax.net.ssl.trustStore", trustFilename); - System.setProperty("javax.net.ssl.trustStorePassword", passwd); + String protocol = args[0]; + if ("TLSv1".equals(protocol) || "TLSv1.1".equals(protocol)) { + SecurityUtils.removeFromDisabledTlsAlgs(protocol); + } if (DEBUG) System.setProperty("javax.net.debug", "all"); @@ -287,94 +190,45 @@ public static void main(String[] args) throws Exception { /* * Start the tests. */ - new NonAutoClose(); + new NonAutoClose(protocol); } private Thread clientThread = null; - private Thread serverThread = null; + private final String protocol; /* * Primary constructor, used to drive remainder of the test. * * Fork off the other side, then do your work. */ - NonAutoClose() throws Exception { - if (separateServerThread) { - startServer(true); - startClient(false); - } else { - startClient(true); - startServer(false); - } + NonAutoClose(String protocol) throws Exception { + this.protocol = protocol; + startClient(); + doServerSide(); /* * Wait for other side to close down. */ - if (separateServerThread) { - serverThread.join(); - } else { - clientThread.join(); - } + clientThread.join(); - /* - * When we get here, the test is pretty much over. - * - * If the main thread excepted, that propagates back - * immediately. If the other thread threw an exception, we - * should report back. - */ - if (serverException != null) { - System.err.print("Server Exception:"); - throw serverException; - } if (clientException != null) { System.err.print("Client Exception:"); throw clientException; } } - private void startServer(boolean newThread) throws Exception { - if (newThread) { - serverThread = new Thread() { - public void run() { - try { - doServerSide(); - } catch (Exception e) { - /* - * Our server thread just died. - * - * Release the client, if not active already... - */ - System.err.println("Server died..."); - serverReady = true; - serverException = e; - } - } - }; - serverThread.start(); - } else { - doServerSide(); - } - } - - private void startClient(boolean newThread) throws Exception { - if (newThread) { - clientThread = new Thread() { - public void run() { - try { - doClientSide(); - } catch (Exception e) { - /* - * Our client thread just died. - */ - System.err.println("Client died..."); - clientException = e; - } - } - }; - clientThread.start(); - } else { - doClientSide(); - } + private void startClient() { + clientThread = new Thread(() -> { + try { + doClientSide(); + } catch (Exception e) { + /* + * Our client thread just died. + */ + System.err.println("Client died..."); + clientException = e; + } + }); + clientThread.start(); } } diff --git a/test/jdk/sun/security/ssl/SSLSocketImpl/SetClientMode.java b/test/jdk/sun/security/ssl/SSLSocketImpl/SetClientMode.java index 8eed2344bbc19..4b55ac6ea9048 100644 --- a/test/jdk/sun/security/ssl/SSLSocketImpl/SetClientMode.java +++ b/test/jdk/sun/security/ssl/SSLSocketImpl/SetClientMode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,11 +27,13 @@ /* * @test * @bug 6223624 - * @ignore this test does not grant to work. The handshake may have completed - * when getSession() return. Please update or remove this test case. + * @library /test/lib /javax/net/ssl/templates * @summary SSLSocket.setUseClientMode() fails to throw expected * IllegalArgumentException - * @run main/othervm SetClientMode + * @run main/othervm SetClientMode TLSv1 + * @run main/othervm SetClientMode TLSv1.1 + * @run main/othervm SetClientMode TLSv1.2 + * @run main/othervm SetClientMode TLSv1.3 */ /* @@ -47,143 +49,82 @@ * occasionally on the very first iteration. */ -import java.io.*; import java.lang.*; import java.net.*; +import java.util.concurrent.CountDownLatch; import javax.net.ssl.*; -import java.security.*; -import java.security.cert.*; +import jdk.test.lib.security.SecurityUtils; -public class SetClientMode { - private static String[] algorithms = {"TLS", "SSL", "SSLv3", "TLS"}; - volatile int serverPort = 0; +import static jdk.test.lib.Asserts.assertThrows; - /* - * Where do we find the keystores? - */ - static String pathToStores = "../../../../javax/net/ssl/etc"; - static String keyStoreFile = "keystore"; - static String trustStoreFile = "truststore"; - static String passwd = "passphrase"; - - - public SetClientMode() { - // trivial constructor - } +public class SetClientMode extends SSLContextTemplate { + private volatile int serverPort = 0; + private static final CountDownLatch HANDSHAKE_COMPLETE = new CountDownLatch(1); public static void main(String[] args) throws Exception { - String keyFilename = - System.getProperty("test.src", "./") + "/" + pathToStores + - "/" + keyStoreFile; - String trustFilename = - System.getProperty("test.src", "./") + "/" + pathToStores + - "/" + trustStoreFile; - - System.setProperty("javax.net.ssl.keyStore", keyFilename); - System.setProperty("javax.net.ssl.keyStorePassword", passwd); - System.setProperty("javax.net.ssl.trustStore", trustFilename); - System.setProperty("javax.net.ssl.trustStorePassword", passwd); - - new SetClientMode().run(); - } + String protocol = args[0]; - public void run() throws Exception { - for (int i = 0; i < algorithms.length; i++) { - testCombo( algorithms[i] ); + if ("TLSv1".equals(protocol) || "TLSv1.1".equals(protocol)) { + SecurityUtils.removeFromDisabledTlsAlgs(protocol); } - } - public void testCombo(String algorithm) throws Exception { - Exception modeException = null ; + new SetClientMode().run(protocol); + } + public void run(String protocol) throws Exception { // Create a server socket - SSLServerSocketFactory ssf = - (SSLServerSocketFactory)SSLServerSocketFactory.getDefault(); - SSLServerSocket serverSocket = - (SSLServerSocket)ssf.createServerSocket(serverPort); - serverPort = serverSocket.getLocalPort(); - - // Create a client socket - SSLSocketFactory sf = (SSLSocketFactory)SSLSocketFactory.getDefault(); - SSLSocket clientSocket = (SSLSocket)sf.createSocket( - InetAddress.getLocalHost(), - serverPort ); - - // Create a client which will use the SSLSocket to talk to the server - SocketClient client = new SocketClient(clientSocket); - - // Start the client and then accept any connection - client.start(); - - SSLSocket connectedSocket = (SSLSocket)serverSocket.accept(); - - // force handshaking to complete - connectedSocket.getSession(); - - try { - // Now try invoking setClientMode() on one - // or the other of our two sockets. We expect - // to see an IllegalArgumentException because - // handshaking has begun. - clientSocket.setUseClientMode(false); - - modeException = new Exception("no IllegalArgumentException"); - } catch (IllegalArgumentException iae) { - System.out.println("succeeded, we can't set the client mode"); - } catch (Exception e) { - modeException = e; - } finally { - // Shut down. - connectedSocket.close(); - serverSocket.close(); - - if (modeException != null) { - throw modeException; + SSLServerSocketFactory ssf = createServerSSLContext().getServerSocketFactory(); + + try (SSLServerSocket serverSocket = + (SSLServerSocket) ssf.createServerSocket(serverPort)) { + serverSocket.setEnabledProtocols(new String[]{ protocol }); + serverPort = serverSocket.getLocalPort(); + + // Create a client socket + SSLSocketFactory sf = createClientSSLContext().getSocketFactory(); + + try (SSLSocket clientSocket = (SSLSocket) sf.createSocket( + InetAddress.getLocalHost(), + serverPort)) { + + // Create a client which will use the SSLSocket to talk to the server + Client client = new Client(clientSocket); + + // Start the client and then accept any connection + client.start(); + + SSLSocket connectedSocket = (SSLSocket) serverSocket.accept(); + + // force handshaking to complete + connectedSocket.getSession(); + + HANDSHAKE_COMPLETE.await(); + + // Now try invoking setClientMode() on the client socket. + // We expect to see an IllegalArgumentException because + // handshaking has begun. + assertThrows(IllegalArgumentException.class, + () -> clientSocket.setUseClientMode(false)); } } - - return; } // A thread-based client which does nothing except // start handshaking on the socket it's given. - class SocketClient extends Thread { - SSLSocket clientsideSocket; - Exception clientException = null; - boolean done = false; + static class Client extends Thread { + private final SSLSocket socket; - public SocketClient( SSLSocket s ) { - clientsideSocket = s; + public Client(SSLSocket s) { + socket = s; } public void run() { try { - clientsideSocket.startHandshake(); - - // If we were to invoke setUseClientMode() - // here, the expected exception will happen. - //clientsideSocket.getSession(); - //clientsideSocket.setUseClientMode( false ); - } catch ( Exception e ) { + socket.startHandshake(); + HANDSHAKE_COMPLETE.countDown(); + } catch (Exception e) { e.printStackTrace(); - clientException = e; - } finally { - done = true; - try { - clientsideSocket.close(); - } catch ( IOException e ) { - // eat it - } } - return; - } - - boolean isDone() { - return done; - } - - Exception getException() { - return clientException; } } } diff --git a/test/jdk/sun/security/ssl/ServerHandshaker/HelloExtensionsTest.java b/test/jdk/sun/security/ssl/ServerHandshaker/HelloExtensionsTest.java index bb84d5f01f924..392e882a19ae5 100644 --- a/test/jdk/sun/security/ssl/ServerHandshaker/HelloExtensionsTest.java +++ b/test/jdk/sun/security/ssl/ServerHandshaker/HelloExtensionsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -121,7 +121,7 @@ SHA256withECDSA, SHA256withRSA, Unknown (hash:0x3, signature:0x3), Unknown (hash:0x3, signature:0x1), SHA1withECDSA, SHA1withRSA, SHA1withDSA Extension server_name, server_name: - [host_name: bugs.openjdk.java.net] + [host_name: bugs.openjdk.org] */ String hello = "16030300df010000db03035898b7826c8c0cc" + diff --git a/test/jdk/sun/security/ssl/SignatureScheme/AbstractCheckSignatureSchemes.java b/test/jdk/sun/security/ssl/SignatureScheme/AbstractCheckSignatureSchemes.java new file mode 100644 index 0000000000000..df70db86fec7f --- /dev/null +++ b/test/jdk/sun/security/ssl/SignatureScheme/AbstractCheckSignatureSchemes.java @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.nio.ByteBuffer; +import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLException; + +/** + * This is not a test. Actual tests are implemented by concrete subclasses. + * The abstract class AbstractCheckSignatureSchemes provides a base framework + * for checking TLS signature schemes. + */ + +public abstract class AbstractCheckSignatureSchemes extends SSLEngineTemplate { + + // Helper map to correlate integral SignatureScheme identifiers to + // their IANA string name counterparts. + protected static final Map sigSchemeMap = Map.ofEntries( + new SimpleImmutableEntry(0x0401, "rsa_pkcs1_sha256"), + new SimpleImmutableEntry(0x0501, "rsa_pkcs1_sha384"), + new SimpleImmutableEntry(0x0601, "rsa_pkcs1_sha512"), + new SimpleImmutableEntry(0x0403, "ecdsa_secp256r1_sha256"), + new SimpleImmutableEntry(0x0503, "ecdsa_secp384r1_sha384"), + new SimpleImmutableEntry(0x0603, "ecdsa_secp521r1_sha512"), + new SimpleImmutableEntry(0x0804, "rsa_pss_rsae_sha256"), + new SimpleImmutableEntry(0x0805, "rsa_pss_rsae_sha384"), + new SimpleImmutableEntry(0x0806, "rsa_pss_rsae_sha512"), + new SimpleImmutableEntry(0x0807, "ed25519"), + new SimpleImmutableEntry(0x0808, "ed448"), + new SimpleImmutableEntry(0x0809, "rsa_pss_pss_sha256"), + new SimpleImmutableEntry(0x080a, "rsa_pss_pss_sha384"), + new SimpleImmutableEntry(0x080b, "rsa_pss_pss_sha512"), + new SimpleImmutableEntry(0x0101, "rsa_md5"), + new SimpleImmutableEntry(0x0201, "rsa_pkcs1_sha1"), + new SimpleImmutableEntry(0x0202, "dsa_sha1"), + new SimpleImmutableEntry(0x0203, "ecdsa_sha1"), + new SimpleImmutableEntry(0x0301, "rsa_sha224"), + new SimpleImmutableEntry(0x0302, "dsa_sha224"), + new SimpleImmutableEntry(0x0303, "ecdsa_sha224"), + new SimpleImmutableEntry(0x0402, "rsa_pkcs1_sha256")); + + // Other useful TLS definitions for these tests + protected static final int TLS_HS_CLI_HELLO = 1; + protected static final int TLS_HS_CERT_REQ = 13; + protected static final int SIG_ALGS_EXT = 13; + protected static final int SIG_ALGS_CERT_EXT = 50; + + protected AbstractCheckSignatureSchemes() throws Exception { + super(); + } + + // Returns the protocol for test to use. + abstract String getProtocol(); + + protected boolean isDtls() { + return getProtocol().startsWith("DTLS"); + } + + @Override + protected SSLEngine configureClientEngine(SSLEngine clientEngine) { + clientEngine.setUseClientMode(true); + clientEngine.setEnabledProtocols(new String[]{getProtocol()}); + return clientEngine; + } + + @Override + protected SSLEngine configureServerEngine(SSLEngine serverEngine) { + serverEngine.setUseClientMode(false); + serverEngine.setWantClientAuth(true); + serverEngine.setEnabledProtocols(new String[]{getProtocol()}); + return serverEngine; + } + + @Override + protected ContextParameters getServerContextParameters() { + return new ContextParameters(getProtocol(), "PKIX", "NewSunX509"); + } + + @Override + protected ContextParameters getClientContextParameters() { + return new ContextParameters(getProtocol(), "PKIX", "NewSunX509"); + } + + protected ByteBuffer extractHandshakeMsg(ByteBuffer tlsRecord, int hsMsgId) + throws SSLException { + return extractHandshakeMsg(tlsRecord, hsMsgId, isDtls()); + } + + /** + * Parses the ClientHello message and extracts from it a list of + * SignatureScheme values in string form. It is assumed that the provided + * ByteBuffer has its position set at the first byte of the ClientHello + * message body (AFTER the handshake header) and contains the entire + * hello message. Upon successful completion of this method the ByteBuffer + * will have its position reset to the initial offset in the buffer. + * If an exception is thrown the position at the time of the exception + * will be preserved. + * + * @param data The ByteBuffer containing the ClientHello bytes. + * @param extCode Code of the TLS extension from which to extract + * signature schemes. + * @return A List of the signature schemes in string form. + */ + protected List getSigSchemesCliHello( + ByteBuffer data, int extCode) { + Objects.requireNonNull(data); + data.mark(); + + // Skip over the protocol version and client random + data.position(data.position() + 34); + + // Jump past the session ID (if there is one) + int sessLen = Byte.toUnsignedInt(data.get()); + if (sessLen != 0) { + data.position(data.position() + sessLen); + } + + // Skip DTLS-specific opaque cookie if any + if (isDtls()) { + int cookieLen = Byte.toUnsignedInt(data.get()); + if (cookieLen != 0) { + data.position(data.position() + cookieLen); + } + } + + // Jump past the cipher suites + int csLen = Short.toUnsignedInt(data.getShort()); + if (csLen != 0) { + data.position(data.position() + csLen); + } + + // ...and the compression + int compLen = Byte.toUnsignedInt(data.get()); + if (compLen != 0) { + data.position(data.position() + compLen); + } + + // Now for the fun part. Go through the extensions and look + // for the two status request exts. + List extSigAlgs = getSigSchemesFromExt(data, extCode); + + // We should be at the end of the ClientHello + data.reset(); + return extSigAlgs; + } + + /** + * Parses the CertificateRequest message and extracts from it a list of + * SignatureScheme values in string form. It is assumed that the provided + * ByteBuffer has its position set at the first byte of the + * CertificateRequest message body (AFTER the handshake header) and + * contains the entire CR message. Upon successful completion of this + * method the ByteBuffer will have its position reset to the initial + * offset in the buffer. + * If an exception is thrown the position at the time of the exception + * will be preserved. + * + * @param data The ByteBuffer containing the CertificateRequest bytes + * + * @return A List of the signature schemes in string form. If no + * signature_algorithms extension is present in the CertificateRequest + * then an empty list will be returned. + */ + protected List getSigSchemesCertReq(ByteBuffer data) { + Objects.requireNonNull(data); + data.mark(); + + // Jump past the certificate types + int certTypeLen = Byte.toUnsignedInt(data.get()); + if (certTypeLen != 0) { + data.position(data.position() + certTypeLen); + } + + // Collect the SignatureAndHashAlgorithms + List extSigAlgs = new ArrayList(); + int sigSchemeLen = Short.toUnsignedInt(data.getShort()); + for (int ssOff = 0; ssOff < sigSchemeLen; ssOff += 2) { + String schemeName = sigSchemeMap.get( + Short.toUnsignedInt(data.getShort())); + if (schemeName != null) { + extSigAlgs.add(schemeName); + } + } + + data.reset(); + return extSigAlgs; + } + + /** + * Gets signatures schemes from the given TLS extension. + * The buffer should be positioned at the start of the extension. + */ + protected List getSigSchemesFromExt( + ByteBuffer data, int extCode) { + + List extSigAlgs = new ArrayList<>(); + data.getShort(); // read length + + while (data.hasRemaining()) { + int extType = Short.toUnsignedInt(data.getShort()); + int extLen = Short.toUnsignedInt(data.getShort()); + if (extType == extCode) { + // Start processing signature algorithms + int sigSchemeLen = Short.toUnsignedInt(data.getShort()); + for (int ssOff = 0; ssOff < sigSchemeLen; ssOff += 2) { + String schemeName = sigSchemeMap.get( + Short.toUnsignedInt(data.getShort())); + if (schemeName != null) { + extSigAlgs.add(schemeName); + } + } + } else { + // Not the extension we're looking for. Skip past the + // extension data + data.position(data.position() + extLen); + } + } + + return extSigAlgs; + } +} diff --git a/test/jdk/java/awt/im/8132503/bug8132503.java b/test/jdk/sun/security/ssl/SignatureScheme/DisableSHA1inHandshakeSignatureDTLS12.java similarity index 55% rename from test/jdk/java/awt/im/8132503/bug8132503.java rename to test/jdk/sun/security/ssl/SignatureScheme/DisableSHA1inHandshakeSignatureDTLS12.java index 622a3bae3a207..6c597cd257635 100644 --- a/test/jdk/java/awt/im/8132503/bug8132503.java +++ b/test/jdk/sun/security/ssl/SignatureScheme/DisableSHA1inHandshakeSignatureDTLS12.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,27 +21,34 @@ * questions. */ -/* @test - @bug 8132503 - @summary [macosx] Chinese full stop symbol cannot be entered with Pinyin IM on OS X - @author Anton Litvinov - @run applet/manual=yesno bug8132503.html +/* + * @test + * @bug 8340321 + * @summary Disable SHA-1 in TLS/DTLS 1.2 signatures. + * This test only covers DTLS 1.2. + * @library /javax/net/ssl/templates + * /test/lib + * @run main/othervm DisableSHA1inHandshakeSignatureDTLS12 */ -import javax.swing.JApplet; -import javax.swing.JScrollPane; -import javax.swing.JTextArea; -import javax.swing.SwingUtilities; +public class DisableSHA1inHandshakeSignatureDTLS12 extends + DisableSHA1inHandshakeSignatureTLS12 { + + protected DisableSHA1inHandshakeSignatureDTLS12() throws Exception { + super(); + } + + public static void main(String[] args) throws Exception { + new DisableSHA1inHandshakeSignatureDTLS12().run(); + } + + @Override + protected String getProtocol() { + return "DTLSv1.2"; + } -public class bug8132503 extends JApplet { + // No CertificateRequest in DTLS server flight. @Override - public void init() { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - JTextArea textArea = new JTextArea("Text area of the test.", 40, 40); - add(new JScrollPane(textArea)); - } - }); + protected void checkCertificateRequest() { } } diff --git a/test/jdk/sun/security/ssl/SignatureScheme/DisableSHA1inHandshakeSignatureTLS12.java b/test/jdk/sun/security/ssl/SignatureScheme/DisableSHA1inHandshakeSignatureTLS12.java new file mode 100644 index 0000000000000..0389740b7fe3a --- /dev/null +++ b/test/jdk/sun/security/ssl/SignatureScheme/DisableSHA1inHandshakeSignatureTLS12.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8340321 + * @summary Disable SHA-1 in TLS/DTLS 1.2 signatures. + * This test only covers TLS 1.2. + * @library /javax/net/ssl/templates + * /test/lib + * @run main/othervm DisableSHA1inHandshakeSignatureTLS12 + */ + +import static jdk.test.lib.Asserts.assertFalse; +import static jdk.test.lib.Asserts.assertTrue; + +import java.util.List; + +public class DisableSHA1inHandshakeSignatureTLS12 extends + AbstractCheckSignatureSchemes { + + protected DisableSHA1inHandshakeSignatureTLS12() throws Exception { + super(); + } + + public static void main(String[] args) throws Exception { + new DisableSHA1inHandshakeSignatureTLS12().run(); + } + + @Override + protected String getProtocol() { + return "TLSv1.2"; + } + + // Run things in TLS handshake order. + protected void run() throws Exception { + + // Produce client_hello + clientEngine.wrap(clientOut, cTOs); + cTOs.flip(); + + checkClientHello(); + + // Consume client_hello. + serverEngine.unwrap(cTOs, serverIn); + runDelegatedTasks(serverEngine); + + // Produce server_hello. + serverEngine.wrap(serverOut, sTOc); + sTOc.flip(); + + checkCertificateRequest(); + } + + // Returns SHA-1 signature schemes supported for TLSv1.2 handshake + protected List getDisabledSignatureSchemes() { + return List.of( + "ecdsa_sha1", + "rsa_pkcs1_sha1", + "dsa_sha1" + ); + } + + protected void checkClientHello() throws Exception { + // Get signature_algorithms extension signature schemes. + List sigAlgsSS = getSigSchemesCliHello( + extractHandshakeMsg(cTOs, TLS_HS_CLI_HELLO), + SIG_ALGS_EXT); + + // Should not be present in signature_algorithms extension. + getDisabledSignatureSchemes().forEach(ss -> + assertFalse(sigAlgsSS.contains(ss), + "Signature Scheme " + ss + + " present in ClientHello's signature_algorithms extension")); + + // Get signature_algorithms_cert extension signature schemes. + List sigAlgsCertSS = getSigSchemesCliHello( + extractHandshakeMsg(cTOs, TLS_HS_CLI_HELLO), + SIG_ALGS_CERT_EXT); + + // Should be present in signature_algorithms_cert extension. + getDisabledSignatureSchemes().forEach(ss -> + assertTrue(sigAlgsCertSS.contains(ss), + "Signature Scheme " + ss + + " isn't present in ClientHello's" + + " signature_algorithms extension")); + } + + protected void checkCertificateRequest() throws Exception { + // Get CertificateRequest message signature schemes. + List sigAlgsCertSS = getSigSchemesCertReq( + extractHandshakeMsg(sTOc, TLS_HS_CERT_REQ)); + + // Should not be present in CertificateRequest message. + getDisabledSignatureSchemes().forEach(ss -> + assertFalse(sigAlgsCertSS.contains(ss), + "Signature Scheme " + ss + + " present in CertificateRequest")); + } +} diff --git a/test/jdk/sun/security/ssl/SignatureScheme/DisableSHA1inHandshakeSignatureTLS13.java b/test/jdk/sun/security/ssl/SignatureScheme/DisableSHA1inHandshakeSignatureTLS13.java new file mode 100644 index 0000000000000..55f619460d4b9 --- /dev/null +++ b/test/jdk/sun/security/ssl/SignatureScheme/DisableSHA1inHandshakeSignatureTLS13.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8340321 + * @summary Disable SHA-1 in TLS/DTLS 1.2 signatures. + * This test only covers TLS 1.3. + * @library /javax/net/ssl/templates + * /test/lib + * @run main/othervm DisableSHA1inHandshakeSignatureTLS13 + */ + +import java.security.Security; +import java.util.List; + +public class DisableSHA1inHandshakeSignatureTLS13 extends + DisableSHA1inHandshakeSignatureTLS12 { + + protected DisableSHA1inHandshakeSignatureTLS13() throws Exception { + super(); + } + + public static void main(String[] args) throws Exception { + // SHA-1 algorithm MUST NOT be used in any TLSv1.3 handshake signatures. + // This is regardless of jdk.tls.disabledAlgorithms configuration. + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + new DisableSHA1inHandshakeSignatureTLS13().run(); + } + + @Override + protected String getProtocol() { + return "TLSv1.3"; + } + + // Returns SHA-1 signature schemes NOT supported for TLSv1.3 handshake + // signatures, but supported for TLSv1.3 certificate signatures. + @Override + protected List getDisabledSignatureSchemes() { + return List.of("ecdsa_sha1", "rsa_pkcs1_sha1"); + } + + // TLSv1.3 sends CertificateRequest signature schemes in + // signature_algorithms and signature_algorithms_cert extensions. Same as + // ClientHello, but they are encrypted. So we skip CertificateRequest + // signature schemes verification for TLSv1.3. + @Override + protected void checkCertificateRequest() { + } +} diff --git a/test/jdk/sun/security/ssl/SignatureScheme/DisableSignatureSchemePerScopeDTLS12.java b/test/jdk/sun/security/ssl/SignatureScheme/DisableSignatureSchemePerScopeDTLS12.java new file mode 100644 index 0000000000000..55f1002e89470 --- /dev/null +++ b/test/jdk/sun/security/ssl/SignatureScheme/DisableSignatureSchemePerScopeDTLS12.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8349583 + * @summary Add mechanism to disable signature schemes based on their TLS scope. + * This test only covers DTLS 1.2. + * @library /javax/net/ssl/templates + * /test/lib + * @run main/othervm DisableSignatureSchemePerScopeDTLS12 + */ + +import java.security.Security; + +public class DisableSignatureSchemePerScopeDTLS12 + extends DisableSignatureSchemePerScopeTLS12 { + + protected DisableSignatureSchemePerScopeDTLS12() throws Exception { + super(); + } + + public static void main(String[] args) throws Exception { + Security.setProperty( + "jdk.tls.disabledAlgorithms", DISABLED_CONSTRAINTS); + new DisableSignatureSchemePerScopeDTLS12().run(); + } + + @Override + protected String getProtocol() { + return "DTLSv1.2"; + } + + // No CertificateRequest in DTLS server flight. + @Override + protected void checkCertificateRequest() { + } +} diff --git a/test/jdk/sun/security/ssl/SignatureScheme/DisableSignatureSchemePerScopeTLS12.java b/test/jdk/sun/security/ssl/SignatureScheme/DisableSignatureSchemePerScopeTLS12.java new file mode 100644 index 0000000000000..e2ee9f0889a68 --- /dev/null +++ b/test/jdk/sun/security/ssl/SignatureScheme/DisableSignatureSchemePerScopeTLS12.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8349583 + * @summary Add mechanism to disable signature schemes based on their TLS scope. + * This test only covers TLS 1.2. + * @library /javax/net/ssl/templates + * /test/lib + * @run main/othervm DisableSignatureSchemePerScopeTLS12 + */ + +import static jdk.test.lib.Asserts.assertFalse; +import static jdk.test.lib.Asserts.assertTrue; + +import java.security.Security; +import java.util.List; + +public class DisableSignatureSchemePerScopeTLS12 extends + AbstractCheckSignatureSchemes { + + // Disabled for Handshake scope. + protected static final String HANDSHAKE_DISABLED_SIG = "rsa_pss_rsae_sha384"; + + // Disabled for Certificate scope. + protected static final String CERTIFICATE_DISABLED_SIG = "ecdsa_secp384r1_sha384"; + + // jdk.tls.disabledAlgorithms value + // We differ from "HandshakeSignature" and "CertificateSignature" specified + // in java.security to check case-insensitive matching. + protected static final String DISABLED_CONSTRAINTS = + HANDSHAKE_DISABLED_SIG + " usage HandShakesignature, " + + CERTIFICATE_DISABLED_SIG + " usage certificateSignature"; + + protected DisableSignatureSchemePerScopeTLS12() throws Exception { + super(); + } + + public static void main(String[] args) throws Exception { + Security.setProperty( + "jdk.tls.disabledAlgorithms", DISABLED_CONSTRAINTS); + new DisableSignatureSchemePerScopeTLS12().run(); + } + + protected String getProtocol() { + return "TLSv1.2"; + } + + // Run things in TLS handshake order. + protected void run() throws Exception { + + // Produce client_hello + clientEngine.wrap(clientOut, cTOs); + cTOs.flip(); + + checkClientHello(); + + // Consume client_hello. + serverEngine.unwrap(cTOs, serverIn); + runDelegatedTasks(serverEngine); + + // Produce server_hello. + serverEngine.wrap(serverOut, sTOc); + sTOc.flip(); + + checkCertificateRequest(); + } + + protected void checkClientHello() throws Exception { + // --- Check signature_algorithms extension --- + + // Get signature_algorithms extension signature schemes. + List sigAlgsSS = getSigSchemesCliHello( + extractHandshakeMsg(cTOs, TLS_HS_CLI_HELLO), + SIG_ALGS_EXT); + + // signature_algorithms extension MUST NOT contain disabled + // handshake signature scheme. + assertFalse(sigAlgsSS.contains(HANDSHAKE_DISABLED_SIG), + "Signature Scheme " + HANDSHAKE_DISABLED_SIG + + " present in ClientHello's signature_algorithms extension"); + + // signature_algorithms extension MUST contain disabled + // certificate signature scheme. + assertTrue(sigAlgsSS.contains(CERTIFICATE_DISABLED_SIG), + "Signature Scheme " + CERTIFICATE_DISABLED_SIG + + " isn't present in ClientHello's" + + " signature_algorithms extension"); + + // --- Check signature_algorithms_cert extension --- + + // Get signature_algorithms_cert extension signature schemes. + List sigAlgsCertSS = getSigSchemesCliHello( + extractHandshakeMsg(cTOs, TLS_HS_CLI_HELLO), + SIG_ALGS_CERT_EXT); + + // signature_algorithms_cert extension MUST contain disabled + // handshake signature scheme. + assertTrue(sigAlgsCertSS.contains(HANDSHAKE_DISABLED_SIG), + "Signature Scheme " + HANDSHAKE_DISABLED_SIG + + " isn't present in ClientHello's" + + " signature_algorithms extension"); + + // signature_algorithms_cert extension MUST NOT contain disabled + // certificate signature scheme. + assertFalse(sigAlgsCertSS.contains(CERTIFICATE_DISABLED_SIG), + "Signature Scheme " + CERTIFICATE_DISABLED_SIG + + " present in ClientHello's signature_algorithms extension"); + } + + protected void checkCertificateRequest() throws Exception { + // Get CertificateRequest message signature schemes. + List sigAlgsCertSS = getSigSchemesCertReq( + extractHandshakeMsg(sTOc, TLS_HS_CERT_REQ)); + + // TLSv1.2 CertificateRequest message MUST NOT contain both: + // disabled handshake signature scheme and disabled + // certificate signature scheme + + assertFalse(sigAlgsCertSS.contains(HANDSHAKE_DISABLED_SIG), + "Signature Scheme " + HANDSHAKE_DISABLED_SIG + + " present in CertificateRequest"); + + assertFalse(sigAlgsCertSS.contains(CERTIFICATE_DISABLED_SIG), + "Signature Scheme " + CERTIFICATE_DISABLED_SIG + + " present in CertificateRequest"); + } +} diff --git a/test/jdk/sun/security/ssl/SignatureScheme/DisableSignatureSchemePerScopeTLS13.java b/test/jdk/sun/security/ssl/SignatureScheme/DisableSignatureSchemePerScopeTLS13.java new file mode 100644 index 0000000000000..5d5d445cb4506 --- /dev/null +++ b/test/jdk/sun/security/ssl/SignatureScheme/DisableSignatureSchemePerScopeTLS13.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8349583 + * @summary Add mechanism to disable signature schemes based on their TLS scope. + * This test only covers TLS 1.3. + * @library /javax/net/ssl/templates + * /test/lib + * @run main/othervm DisableSignatureSchemePerScopeTLS13 + */ + + +import static jdk.test.lib.Asserts.assertFalse; +import static jdk.test.lib.Asserts.assertTrue; + +import java.security.Security; +import java.util.List; + +public class DisableSignatureSchemePerScopeTLS13 + extends DisableSignatureSchemePerScopeTLS12 { + + // Signature schemes not supported in TLSv1.3 only for the handshake. + // This is regardless of jdk.tls.disabledAlgorithms configuration. + List NOT_SUPPORTED_FOR_HANDSHAKE = List.of( + "rsa_pkcs1_sha1", + "rsa_pkcs1_sha256", + "rsa_pkcs1_sha384", + "rsa_pkcs1_sha512" + ); + + protected DisableSignatureSchemePerScopeTLS13() throws Exception { + super(); + } + + public static void main(String[] args) throws Exception { + Security.setProperty( + "jdk.tls.disabledAlgorithms", DISABLED_CONSTRAINTS); + new DisableSignatureSchemePerScopeTLS13().run(); + } + + @Override + protected String getProtocol() { + return "TLSv1.3"; + } + + @Override + protected void checkClientHello() throws Exception { + super.checkClientHello(); + + // Get signature_algorithms extension signature schemes. + List sigAlgsSS = getSigSchemesCliHello( + extractHandshakeMsg(cTOs, TLS_HS_CLI_HELLO), + SIG_ALGS_EXT); + + // Should not be present in signature_algorithms extension. + NOT_SUPPORTED_FOR_HANDSHAKE.forEach(ss -> + assertFalse(sigAlgsSS.contains(ss), + "Signature Scheme " + ss + + " present in ClientHello's signature_algorithms extension")); + + // Get signature_algorithms_cert extension signature schemes. + List sigAlgsCertSS = getSigSchemesCliHello( + extractHandshakeMsg(cTOs, TLS_HS_CLI_HELLO), + SIG_ALGS_CERT_EXT); + + // Should be present in signature_algorithms_cert extension. + NOT_SUPPORTED_FOR_HANDSHAKE.forEach(ss -> + assertTrue(sigAlgsCertSS.contains(ss), + "Signature Scheme " + ss + + " isn't present in ClientHello's" + + " signature_algorithms extension")); + } + + // TLSv1.3 sends CertificateRequest signature schemes in + // signature_algorithms and signature_algorithms_cert extensions. Same as + // ClientHello, but they are encrypted. So we skip CertificateRequest + // signature schemes verification for TLSv1.3. + @Override + protected void checkCertificateRequest() { + } +} diff --git a/test/jdk/sun/security/ssl/SignatureScheme/MD5NotAllowedInTLS13CertificateSignature.java b/test/jdk/sun/security/ssl/SignatureScheme/MD5NotAllowedInTLS13CertificateSignature.java new file mode 100644 index 0000000000000..6d0625c257c0c --- /dev/null +++ b/test/jdk/sun/security/ssl/SignatureScheme/MD5NotAllowedInTLS13CertificateSignature.java @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8350807 + * @summary Certificates using MD5 algorithm that are disabled by default are + * incorrectly allowed in TLSv1.3 when re-enabled. + * @modules java.base/sun.security.x509 + * java.base/sun.security.util + * @library /javax/net/ssl/templates + * /test/lib + * @run main/othervm MD5NotAllowedInTLS13CertificateSignature + */ + +import static jdk.test.lib.Asserts.assertEquals; +import static jdk.test.lib.Asserts.assertTrue; +import static jdk.test.lib.Utils.runAndCheckException; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Date; +import java.util.List; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.TrustManagerFactory; +import jdk.test.lib.security.CertificateBuilder; +import jdk.test.lib.security.SecurityUtils; +import sun.security.x509.AuthorityKeyIdentifierExtension; +import sun.security.x509.GeneralName; +import sun.security.x509.GeneralNames; +import sun.security.x509.KeyIdentifier; +import sun.security.x509.SerialNumber; +import sun.security.x509.X500Name; + +public class MD5NotAllowedInTLS13CertificateSignature extends + SSLSocketTemplate { + + private final String protocol; + private X509Certificate trustedCert; + private X509Certificate serverCert; + private X509Certificate clientCert; + private KeyPair serverKeys; + private KeyPair clientKeys; + + protected MD5NotAllowedInTLS13CertificateSignature(String protocol) + throws Exception { + super(); + this.protocol = protocol; + setupCertificates(); + } + + public static void main(String[] args) throws Exception { + // MD5 is disabled by default in java.security config file, + // re-enable it for our test. + SecurityUtils.removeFromDisabledAlgs( + "jdk.certpath.disabledAlgorithms", List.of("MD5")); + SecurityUtils.removeFromDisabledAlgs( + "jdk.tls.disabledAlgorithms", List.of("MD5withRSA")); + + // Should fail on TLSv1.3 and up. + runAndCheckException( + // The conditions to reproduce the bug being fixed only met when + // 'TLS' is specified, i.e. when older versions of protocol are + // supported besides TLSv1.3. + () -> new MD5NotAllowedInTLS13CertificateSignature("TLS").run(), + serverEx -> { + Throwable clientEx = serverEx.getSuppressed()[0]; + assertTrue(clientEx instanceof SSLHandshakeException); + assertEquals(clientEx.getMessage(), "" + + "PKIX path validation failed: " + + "java.security.cert.CertPathValidatorException: " + + "Algorithm constraints check failed on signature" + + " algorithm: MD5withRSA"); + }); + + // Should run fine on TLSv1.2. + new MD5NotAllowedInTLS13CertificateSignature("TLSv1.2").run(); + } + + @Override + public SSLContext createServerSSLContext() throws Exception { + return getSSLContext( + trustedCert, serverCert, serverKeys.getPrivate(), protocol); + } + + @Override + public SSLContext createClientSSLContext() throws Exception { + return getSSLContext( + trustedCert, clientCert, clientKeys.getPrivate(), protocol); + } + + private static SSLContext getSSLContext( + X509Certificate trustedCertificate, X509Certificate keyCertificate, + PrivateKey privateKey, String protocol) + throws Exception { + + // create a key store + KeyStore ks = KeyStore.getInstance("PKCS12"); + ks.load(null, null); + + // import the trusted cert + ks.setCertificateEntry("TLS Signer", trustedCertificate); + + // generate certificate chain + Certificate[] chain = new Certificate[2]; + chain[0] = keyCertificate; + chain[1] = trustedCertificate; + + // import the key entry. + final char[] passphrase = "passphrase".toCharArray(); + ks.setKeyEntry("Whatever", privateKey, passphrase, chain); + + // Using PKIX TrustManager - this is where MD5 signature check is done. + TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); + tmf.init(ks); + + // create SSL context + SSLContext ctx = SSLContext.getInstance(protocol); + + // Using "SunX509" which doesn't check peer supported signature + // algorithms, so we check against local supported signature + // algorithms which constitutes the fix being tested. + KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + kmf.init(ks, passphrase); + + ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + + return ctx; + } + + // Certificate-building helper methods. + // Certificates are signed with signature using MD5WithRSA algorithm. + + private void setupCertificates() throws Exception { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); + kpg.initialize(1024); + KeyPair caKeys = kpg.generateKeyPair(); + this.serverKeys = kpg.generateKeyPair(); + this.clientKeys = kpg.generateKeyPair(); + + this.trustedCert = createTrustedCert(caKeys); + + this.serverCert = customCertificateBuilder( + "O=Some-Org, L=Some-City, ST=Some-State, C=US", + serverKeys.getPublic(), caKeys.getPublic()) + .addBasicConstraintsExt(false, false, -1) + .build(trustedCert, caKeys.getPrivate(), "MD5WithRSA"); + + this.clientCert = customCertificateBuilder( + "CN=localhost, OU=SSL-Client, O=Some-Org, L=Some-City, ST=Some-State, C=US", + clientKeys.getPublic(), caKeys.getPublic()) + .addBasicConstraintsExt(false, false, -1) + .build(trustedCert, caKeys.getPrivate(), "MD5WithRSA"); + } + + private static X509Certificate createTrustedCert(KeyPair caKeys) + throws Exception { + SecureRandom random = new SecureRandom(); + + KeyIdentifier kid = new KeyIdentifier(caKeys.getPublic()); + GeneralNames gns = new GeneralNames(); + GeneralName name = new GeneralName(new X500Name( + "O=Some-Org, L=Some-City, ST=Some-State, C=US")); + gns.add(name); + BigInteger serialNumber = BigInteger.valueOf( + random.nextLong(1000000) + 1); + return customCertificateBuilder( + "O=Some-Org, L=Some-City, ST=Some-State, C=US", + caKeys.getPublic(), caKeys.getPublic()) + .setSerialNumber(serialNumber) + .addExtension(new AuthorityKeyIdentifierExtension(kid, gns, + new SerialNumber(serialNumber))) + .addBasicConstraintsExt(true, true, -1) + .build(null, caKeys.getPrivate(), "MD5WithRSA"); + } + + private static CertificateBuilder customCertificateBuilder( + String subjectName, PublicKey publicKey, PublicKey caKey) + throws CertificateException, IOException { + SecureRandom random = new SecureRandom(); + + CertificateBuilder builder = new CertificateBuilder() + .setSubjectName(subjectName) + .setPublicKey(publicKey) + .setNotBefore( + Date.from(Instant.now().minus(1, ChronoUnit.HOURS))) + .setNotAfter(Date.from(Instant.now().plus(1, ChronoUnit.HOURS))) + .setSerialNumber( + BigInteger.valueOf(random.nextLong(1000000) + 1)) + .addSubjectKeyIdExt(publicKey) + .addAuthorityKeyIdExt(caKey); + builder.addKeyUsageExt( + new boolean[]{true, true, true, true, true, true}); + + return builder; + } + +} diff --git a/test/jdk/sun/security/ssl/SignatureScheme/MixingTLSUsageConstraintsWithNonTLS.java b/test/jdk/sun/security/ssl/SignatureScheme/MixingTLSUsageConstraintsWithNonTLS.java new file mode 100644 index 0000000000000..3dc811ca66c57 --- /dev/null +++ b/test/jdk/sun/security/ssl/SignatureScheme/MixingTLSUsageConstraintsWithNonTLS.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8349583 + * @summary Add mechanism to disable signature schemes based on their TLS scope + * @library /javax/net/ssl/templates + * /test/lib + * @run main/othervm MixingTLSUsageConstraintsWithNonTLS + */ + +import static jdk.test.lib.Asserts.assertEquals; +import static jdk.test.lib.Asserts.assertTrue; +import static jdk.test.lib.Utils.runAndCheckException; + +import java.security.Security; + +public class MixingTLSUsageConstraintsWithNonTLS extends SSLSocketTemplate { + + public static void main(String[] args) throws Exception { + Security.setProperty("jdk.tls.disabledAlgorithms", + "rsa_pkcs1_sha1 usage handshakeSignature certificateSignature TLSServer"); + + runAndCheckException( + () -> new MixingTLSUsageConstraintsWithNonTLS().run(), + e -> { + assertTrue(e instanceof ExceptionInInitializerError); + assertTrue( + e.getCause() instanceof IllegalArgumentException); + assertEquals(e.getCause().getMessage(), + "Can't mix TLS protocol specific constraints" + + " with other usage constraints"); + }); + } +} diff --git a/test/jdk/sun/security/ssl/SignatureScheme/SigAlgosExtTestWithTLS12.java b/test/jdk/sun/security/ssl/SignatureScheme/SigAlgosExtTestWithTLS12.java index 30a01552b301e..799f3354ed38e 100644 --- a/test/jdk/sun/security/ssl/SignatureScheme/SigAlgosExtTestWithTLS12.java +++ b/test/jdk/sun/security/ssl/SignatureScheme/SigAlgosExtTestWithTLS12.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2021, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/jdk/sun/security/ssl/SignatureScheme/SigAlgosExtTestWithTLS13.java b/test/jdk/sun/security/ssl/SignatureScheme/SigAlgosExtTestWithTLS13.java index 49e8da8af781d..c4784c09da8e4 100644 --- a/test/jdk/sun/security/ssl/SignatureScheme/SigAlgosExtTestWithTLS13.java +++ b/test/jdk/sun/security/ssl/SignatureScheme/SigAlgosExtTestWithTLS13.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2021, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/jdk/sun/security/ssl/SignatureScheme/SigSchemePropOrdering.java b/test/jdk/sun/security/ssl/SignatureScheme/SigSchemePropOrdering.java index dddbc7bc7de05..9551d30317b8f 100644 --- a/test/jdk/sun/security/ssl/SignatureScheme/SigSchemePropOrdering.java +++ b/test/jdk/sun/security/ssl/SignatureScheme/SigSchemePropOrdering.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,48 +36,14 @@ * @run main/othervm SigSchemePropOrdering */ -import java.nio.ByteBuffer; -import java.util.*; -import java.util.AbstractMap.SimpleImmutableEntry; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLException; +import java.util.Arrays; +import java.util.List; -public class SigSchemePropOrdering extends SSLEngineTemplate { - - // Helper map to correlate integral SignatureScheme identifiers to - // their IANA string name counterparts. - static final Map sigSchemeMap = Map.ofEntries( - new SimpleImmutableEntry(0x0401, "rsa_pkcs1_sha256"), - new SimpleImmutableEntry(0x0501, "rsa_pkcs1_sha384"), - new SimpleImmutableEntry(0x0601, "rsa_pkcs1_sha512"), - new SimpleImmutableEntry(0x0403, "ecdsa_secp256r1_sha256"), - new SimpleImmutableEntry(0x0503, "ecdsa_secp384r1_sha384"), - new SimpleImmutableEntry(0x0603, "ecdsa_secp521r1_sha512"), - new SimpleImmutableEntry(0x0804, "rsa_pss_rsae_sha256"), - new SimpleImmutableEntry(0x0805, "rsa_pss_rsae_sha384"), - new SimpleImmutableEntry(0x0806, "rsa_pss_rsae_sha512"), - new SimpleImmutableEntry(0x0807, "ed25519"), - new SimpleImmutableEntry(0x0808, "ed448"), - new SimpleImmutableEntry(0x0809, "rsa_pss_pss_sha256"), - new SimpleImmutableEntry(0x080a, "rsa_pss_pss_sha384"), - new SimpleImmutableEntry(0x080b, "rsa_pss_pss_sha512"), - new SimpleImmutableEntry(0x0101, "rsa_md5"), - new SimpleImmutableEntry(0x0201, "rsa_pkcs1_sha1"), - new SimpleImmutableEntry(0x0202, "dsa_sha1"), - new SimpleImmutableEntry(0x0203, "ecdsa_sha1"), - new SimpleImmutableEntry(0x0301, "rsa_sha224"), - new SimpleImmutableEntry(0x0302, "dsa_sha224"), - new SimpleImmutableEntry(0x0303, "ecdsa_sha224"), - new SimpleImmutableEntry(0x0402, "rsa_pkcs1_sha256")); - - // Other useful TLS definitions for these tests - private static final int TLS_HS_CLI_HELLO = 1; - private static final int TLS_HS_CERT_REQ = 13; - private static final int HELLO_EXT_SIG_ALGS = 13; +public class SigSchemePropOrdering extends AbstractCheckSignatureSchemes { private static final String SIG_SCHEME_STR = "rsa_pkcs1_sha256,rsa_pss_rsae_sha256,rsa_pss_pss_sha256," + - "ed448,ed25519,ecdsa_secp256r1_sha256"; + "ed448,ed25519,ecdsa_secp256r1_sha256"; SigSchemePropOrdering() throws Exception { super(); @@ -90,18 +56,8 @@ public static void main(String[] args) throws Exception { new SigSchemePropOrdering().run(); } - @Override - protected SSLEngine configureClientEngine(SSLEngine clientEngine) { - clientEngine.setUseClientMode(true); - clientEngine.setEnabledProtocols(new String[] { "TLSv1.2" }); - return clientEngine; - } - - @Override - protected SSLEngine configureServerEngine(SSLEngine serverEngine) { - serverEngine.setUseClientMode(false); - serverEngine.setWantClientAuth(true); - return serverEngine; + protected String getProtocol() { + return "TLSv1.2"; } private void run() throws Exception { @@ -114,7 +70,8 @@ private void run() throws Exception { cTOs.flip(); List actualSS = getSigSchemesCliHello( - extractHandshakeMsg(cTOs, TLS_HS_CLI_HELLO)); + extractHandshakeMsg(cTOs, TLS_HS_CLI_HELLO), + SIG_ALGS_EXT); // Make sure the ordering is correct if (!expectedSS.equals(actualSS)) { @@ -157,177 +114,4 @@ private void run() throws Exception { "FAIL: Expected and Actual values differ."); } } - - /** - * Given a TLS record containing one or more handshake messages, return - * the specific handshake message as a ByteBuffer (a slice of the record) - * - * @param tlsRecord a ByteBuffer containing a TLS record. It is assumed - * that the position of the ByteBuffer is on the first byte of the TLS - * record header. - * @param hsMsgId the message identifier for the handshake message being - * sought. - * - * @return a ByteBuffer containing the TLS handshake message. The position - * of the returned ByteBuffer will be on the first byte of the TLS - * handshake message data, immediately following the handshake header. - * If the message is not found, null will be returned. - * - * @throws SSLException if the incoming ByteBuffer does not contain a - * well-formed TLS message. - */ - private static ByteBuffer extractHandshakeMsg(ByteBuffer tlsRecord, - int hsMsgId) throws SSLException { - Objects.requireNonNull(tlsRecord); - tlsRecord.mark(); - - // Process the TLS record header - int type = Byte.toUnsignedInt(tlsRecord.get()); - int ver_major = Byte.toUnsignedInt(tlsRecord.get()); - int ver_minor = Byte.toUnsignedInt(tlsRecord.get()); - int recLen = Short.toUnsignedInt(tlsRecord.getShort()); - - // Simple sanity checks - if (type != 22) { - throw new SSLException("Not a handshake: Type = " + type); - } else if (recLen > tlsRecord.remaining()) { - throw new SSLException("Incomplete record in buffer: " + - "Record length = " + recLen + ", Remaining = " + - tlsRecord.remaining()); - } - - while (tlsRecord.hasRemaining()) { - // Grab the handshake message header. - int msgHdr = tlsRecord.getInt(); - int msgType = (msgHdr >> 24) & 0x000000FF; - int msgLen = msgHdr & 0x00FFFFFF; - - if (msgType == hsMsgId) { - // Slice the buffer such that it contains the entire - // handshake message (less the handshake header). - ByteBuffer buf = tlsRecord.slice(tlsRecord.position(), msgLen); - tlsRecord.reset(); - return buf; - } else { - // Skip to the next handshake message, if there is one - tlsRecord.position(tlsRecord.position() + msgLen); - } - } - - tlsRecord.reset(); - return null; - } - - - /** - * Parses the ClientHello message and extracts from it a list of - * SignatureScheme values in string form. It is assumed that the provided - * ByteBuffer has its position set at the first byte of the ClientHello - * message body (AFTER the handshake header) and contains the entire - * hello message. Upon successful completion of this method the ByteBuffer - * will have its position reset to the initial offset in the buffer. - * If an exception is thrown the position at the time of the exception - * will be preserved. - * - * @param data the ByteBuffer containing the ClientHello bytes - * - * @returns A List of the signature schemes in string form. If no - * signature_algorithms extension is present in the client hello then - * an empty list will be returned. - */ - private static List getSigSchemesCliHello(ByteBuffer data) { - Objects.requireNonNull(data); - data.mark(); - - // Skip over the protocol version and client random - data.position(data.position() + 34); - - // Jump past the session ID (if there is one) - int sessLen = Byte.toUnsignedInt(data.get()); - if (sessLen != 0) { - data.position(data.position() + sessLen); - } - - // Jump past the cipher suites - int csLen = Short.toUnsignedInt(data.getShort()); - if (csLen != 0) { - data.position(data.position() + csLen); - } - - // ...and the compression - int compLen = Byte.toUnsignedInt(data.get()); - if (compLen != 0) { - data.position(data.position() + compLen); - } - - // Now for the fun part. Go through the extensions and look - // for the two status request exts. - List extSigAlgs = new ArrayList(); - int extsLen = Short.toUnsignedInt(data.getShort()); - while (data.hasRemaining()) { - int extType = Short.toUnsignedInt(data.getShort()); - int extLen = Short.toUnsignedInt(data.getShort()); - if (extType == HELLO_EXT_SIG_ALGS) { - // Start processing signature algorithms - int sigSchemeLen = Short.toUnsignedInt(data.getShort()); - for (int ssOff = 0; ssOff < sigSchemeLen; ssOff += 2) { - String schemeName = sigSchemeMap.get( - Short.toUnsignedInt(data.getShort())); - if (schemeName != null) { - extSigAlgs.add(schemeName); - } - } - } else { - // Not the extension we're looking for. Skip past the - // extension data - data.position(data.position() + extLen); - } - } - - // We should be at the end of the ClientHello - data.reset(); - return extSigAlgs; - } - - /** - * Parses the CertificateRequest message and extracts from it a list of - * SignatureScheme values in string form. It is assumed that the provided - * ByteBuffer has its position set at the first byte of the - * CertificateRequest message body (AFTER the handshake header) and - * contains the entire CR message. Upon successful completion of this - * method the ByteBuffer will have its position reset to the initial - * offset in the buffer. - * If an exception is thrown the position at the time of the exception - * will be preserved. - * - * @param data the ByteBuffer containing the CertificateRequest bytes - * - * @returns A List of the signature schemes in string form. If no - * signature_algorithms extension is present in the CertificateRequest - * then an empty list will be returned. - */ - private static List getSigSchemesCertReq(ByteBuffer data) { - Objects.requireNonNull(data); - data.mark(); - - // Jump past the certificate types - int certTypeLen = Byte.toUnsignedInt(data.get()); - if (certTypeLen != 0) { - data.position(data.position() + certTypeLen); - } - - // Collect the SignatureAndHashAlgorithms - List extSigAlgs = new ArrayList(); - int sigSchemeLen = Short.toUnsignedInt(data.getShort()); - for (int ssOff = 0; ssOff < sigSchemeLen; ssOff += 2) { - String schemeName = sigSchemeMap.get( - Short.toUnsignedInt(data.getShort())); - if (schemeName != null) { - extSigAlgs.add(schemeName); - } - } - - data.reset(); - return extSigAlgs; - } } diff --git a/test/jdk/sun/security/ssl/Stapling/StatusResponseManager.java b/test/jdk/sun/security/ssl/Stapling/StatusResponseManager.java index 58ebb5b876af8..0f66db4064cf2 100644 --- a/test/jdk/sun/security/ssl/Stapling/StatusResponseManager.java +++ b/test/jdk/sun/security/ssl/Stapling/StatusResponseManager.java @@ -23,10 +23,12 @@ /* * @test + * @library /test/lib + * @build jdk.test.lib.security.SimpleOCSPServer + * jdk.test.lib.security.CertificateBuilder * @bug 8046321 - * @library ../../../../java/security/testlibrary - * @build CertificateBuilder SimpleOCSPServer - * @run main/othervm -Djavax.net.debug=ssl:respmgr java.base/sun.security.ssl.StatusResponseManagerTests * @summary OCSP Stapling for TLS + * @run main/othervm -Djavax.net.debug=ssl:respmgr + * java.base/sun.security.ssl.StatusResponseManagerTests */ diff --git a/test/jdk/sun/security/ssl/Stapling/java.base/sun/security/ssl/StatusResponseManagerTests.java b/test/jdk/sun/security/ssl/Stapling/java.base/sun/security/ssl/StatusResponseManagerTests.java index 07058a29c2525..63910c67e2d9d 100644 --- a/test/jdk/sun/security/ssl/Stapling/java.base/sun/security/ssl/StatusResponseManagerTests.java +++ b/test/jdk/sun/security/ssl/Stapling/java.base/sun/security/ssl/StatusResponseManagerTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,8 +33,8 @@ import java.security.PublicKey; import java.util.concurrent.TimeUnit; -import sun.security.testlibrary.SimpleOCSPServer; -import sun.security.testlibrary.CertificateBuilder; +import jdk.test.lib.security.SimpleOCSPServer; +import jdk.test.lib.security.CertificateBuilder; import static sun.security.ssl.CertStatusExtension.*; diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Camerfirma.java b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Camerfirma.java index 50a723441a519..8d9dfe1ef18f5 100644 --- a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Camerfirma.java +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Camerfirma.java @@ -21,17 +21,19 @@ * questions. */ +import javax.net.ssl.X509TrustManager; import java.io.File; import java.security.Security; -import java.time.*; -import java.util.*; -import javax.net.ssl.*; +import java.time.LocalDate; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.Date; -/** +/* * @test - * @bug 8346587 + * @bug 8346587 8350498 * @summary Check that TLS Server certificates chaining back to distrusted - * Camerfirma roots are invalid + * Camerfirma root are invalid * @library /test/lib * @modules java.base/sun.security.validator * @run main/othervm Camerfirma after policyOn invalid @@ -42,13 +44,11 @@ public class Camerfirma { - private static final String certPath = "chains" + File.separator + "camerfirma"; + private static final String CERT_PATH = "chains" + File.separator + "camerfirma"; // Each of the roots have a test certificate chain stored in a file // named "-chain.pem". - private static String[] rootsToTest = new String[] { - "camerfirmachamberscommerceca", "camerfirmachambersca", - "camerfirmachambersignca"}; + private static final String ROOT_TO_TEST = "camerfirmachambersca"; // Date after the restrictions take effect private static final ZonedDateTime DISTRUST_DATE = @@ -56,7 +56,7 @@ public class Camerfirma { public static void main(String[] args) throws Exception { - // All of the test certificates are signed with SHA-1 so we need + // All the test certificates are signed with SHA-1, so we need // to remove the constraint that disallows SHA-1 certificates. String prop = Security.getProperty("jdk.certpath.disabledAlgorithms"); String newProp = prop.replace(", SHA1 jdkCA & usage TLSServer", ""); @@ -70,6 +70,6 @@ public static void main(String[] args) throws Exception { }; Date notBefore = distrust.getNotBefore(DISTRUST_DATE); - distrust.testCertificateChain(certPath, notBefore, tms, rootsToTest); + distrust.testCertificateChain(CERT_PATH, notBefore, tms, ROOT_TO_TEST); } } diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Entrust.java b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Entrust.java index 809674e8f209c..0e7617d1b9cc1 100644 --- a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Entrust.java +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Entrust.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,7 @@ /** * @test - * @bug 8337664 8341059 + * @bug 8337664 8341059 8361212 * @summary Check that TLS Server certificates chaining back to distrusted * Entrust roots are invalid * @library /test/lib @@ -41,14 +41,13 @@ public class Entrust { - private static final String certPath = "chains" + File.separator + "entrust"; + private static final String CERT_PATH = "chains" + File.separator + "entrust"; // Each of the roots have a test certificate chain stored in a file // named "-chain.pem". - private static String[] rootsToTest = new String[]{ - "entrustevca", "entrustrootcaec1", "entrustrootcag2", "entrustrootcag4", - "entrust2048ca", "affirmtrustcommercialca", "affirmtrustnetworkingca", - "affirmtrustpremiumca", "affirmtrustpremiumeccca"}; + private static final String[] ROOTS_TO_TEST = new String[]{ + "entrustevca", "entrustrootcaec1", "entrustrootcag2", + "entrustrootcag4", "entrust2048ca"}; // Date when the restrictions take effect private static final ZonedDateTime DISTRUST_DATE = @@ -63,6 +62,6 @@ public static void main(String[] args) throws Exception { }; Date notBefore = distrust.getNotBefore(DISTRUST_DATE); - distrust.testCertificateChain(certPath, notBefore, tms, rootsToTest); + distrust.testCertificateChain(CERT_PATH, notBefore, tms, ROOTS_TO_TEST); } } diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/camerfirma/camerfirmachamberscommerceca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/camerfirma/camerfirmachamberscommerceca-chain.pem deleted file mode 100644 index b27d46c17c8cf..0000000000000 --- a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/camerfirma/camerfirmachamberscommerceca-chain.pem +++ /dev/null @@ -1,48 +0,0 @@ -Owner: CN=AC Camerfirma Certificados Camerales, - O=AC Camerfirma SA, SERIALNUMBER=A82743287, - L=Madrid (see current address at www.camerfirma.com/address), - EMAILADDRESS=ac_camerfirma_cc@camerfirma.com, C=ES -Issuer: CN=Chambers of Commerce Root, OU=http://www.chambersign.org, - O=AC Camerfirma SA CIF A82743287, C=EU -Serial number: 5 -Valid from: Mon Feb 09 07:42:47 PST 2004 until: Thu Feb 09 07:42:47 PST 2034 -Certificate fingerprints: - SHA1: 9F:36:B4:BE:9D:AF:1C:91:01:B2:D7:61:58:FB:95:CB:53:82:01:10 - SHA256: C7:D8:43:81:E1:1F:7C:57:46:77:1A:F5:B0:50:DC:51:FC:6F:DA:D6:F6:F3:5B:B5:3A:3D:E9:13:82:2E:A0:9E -Signature algorithm name: SHA1withRSA (weak) -Subject Public Key Algorithm: 2048-bit RSA key -Version: 3 - ------BEGIN CERTIFICATE----- -MIIFwDCCBKigAwIBAgIBBTANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEn -MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL -ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMg -b2YgQ29tbWVyY2UgUm9vdDAeFw0wNDAyMDkxNTQyNDdaFw0zNDAyMDkxNTQyNDda -MIHgMQswCQYDVQQGEwJFUzEuMCwGCSqGSIb3DQEJARYfYWNfY2FtZXJmaXJtYV9j -Y0BjYW1lcmZpcm1hLmNvbTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBh -ZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJ -QTgyNzQzMjg3MRkwFwYDVQQKExBBQyBDYW1lcmZpcm1hIFNBMS0wKwYDVQQDEyRB -QyBDYW1lcmZpcm1hIENlcnRpZmljYWRvcyBDYW1lcmFsZXMwggEgMA0GCSqGSIb3 -DQEBAQUAA4IBDQAwggEIAoIBAQCjxnvvj01f36lgGhihRYVf1fAPEXsTJKrY4aLQ -cEUSh5szZE7VTtGiyMTMc2uCmnaXafjYHK8Lgmy6T9xxGEZ5OS4x6rgtuPyy13AP -tu3X3Y2kPVLu7ZMw5HoQC64wBj6YcnxTnBwmVW05DjzRXp6OyBIEKEaAB9vv2qEl -fh/Y234FG6Wd/ut1s0ScRZAo+6CSMNQxaY+ryXKD11uWkzWXJa9UZOasG7z4uPqc -Gr4/Hz2/CTLDTgp0xkMJYuzOztpUvOACrxlkS2utKUwVlAikJnboNwf/en94RbHN -zkKc5t0SAbzCf57ueawbzxSdPa+SAC25FNur64FKkfdq5PPjAgEDo4IB5TCCAeEw -EgYDVR0TAQH/BAgwBgEB/wIBCzA8BgNVHR8ENTAzMDGgL6AthitodHRwOi8vY3Js -LmNoYW1iZXJzaWduLm9yZy9jaGFtYmVyc3Jvb3QuY3JsMB0GA1UdDgQWBBS2H06d -HGiRLjdyYOFGj1qlKjExuTCBqwYDVR0jBIGjMIGggBTjlPWxTenboSlbV4tNdgZ2 -4dGiiqGBhKSBgTB/MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJt -YSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJz -aWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdIIBADAO -BgNVHQ8BAf8EBAMCAYYwKgYDVR0RBCMwIYEfYWNfY2FtZXJmaXJtYV9jY0BjYW1l -cmZpcm1hLmNvbTAnBgNVHRIEIDAegRxjaGFtYmVyc3Jvb3RAY2hhbWJlcnNpZ24u -b3JnMFsGA1UdIARUMFIwUAYLKwYBBAGBhy4KCQEwQTA/BggrBgEFBQcCARYzaHR0 -cDovL2Nwcy5jYW1lcmZpcm1hLmNvbS9jcHMvYWNfY2FtZXJmaXJtYV9jYy5odG1s -MA0GCSqGSIb3DQEBBQUAA4IBAQBl8KoPBYL//EBonqQWS0N+hLfxImP1eQ6nac+v -R5QfF/0w+VCTkShfKwHaa6V/W1dPlVwXSECuvXHkX6DYrtxFGGFB6qxuP1rkIpRs -sTkAlpvOx3REiFjIkhsijKd/ijvqxjbMbuYU+EFACK/jQIRoj+LEEZ+haiqbALZB -Iqq/26HTqX0itDosBj6M94YWcIpbTDefQNWCGsSnZcw2+k+az/wAOZT6xAxlnEim -HpDDlgRsmaLrHpDPDoIRYOih0gbJTnn4mKex9Wgr0sZ+XFl03j+bvcXL1tiuQnwb -9dMRDe/OdXABT35W4ZzLbpost65ZW3Tx+oi/bLbmu6pbKCgs ------END CERTIFICATE----- diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/camerfirma/camerfirmachambersignca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/camerfirma/camerfirmachambersignca-chain.pem deleted file mode 100644 index 2ab3091439cb9..0000000000000 --- a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/camerfirma/camerfirmachambersignca-chain.pem +++ /dev/null @@ -1,62 +0,0 @@ -Owner: CN=AC Camerfirma - 2009, - L=Madrid (see current address at https://www.camerfirma.com/address), - SERIALNUMBER=A82743287, O=AC Camerfirma S.A., C=ES -Issuer: CN=Global Chambersign Root - 2008, - O=AC Camerfirma S.A., SERIALNUMBER=A82743287, - L=Madrid (see current address at www.camerfirma.com/address), C=EU -Serial number: 2 -Valid from: Mon Mar 16 10:16:25 PDT 2009 until: Sun Mar 11 10:16:25 PDT 2029 -Certificate fingerprints: - SHA1: BA:BA:69:CF:D5:CC:C9:4D:05:6B:5B:E7:80:5F:E2:03:CB:EB:5C:57 - SHA256: B6:8D:5D:9B:4E:A6:35:95:7C:0C:32:15:C2:0D:35:B2:21:7B:69:E3:49:C7:A3:04:C4:F9:7F:20:C4:08:1F:88 -Signature algorithm name: SHA1withRSA (weak) -Subject Public Key Algorithm: 4096-bit RSA key -Version: 3 - ------BEGIN CERTIFICATE----- -MIIIPzCCBiegAwIBAgIBAjANBgkqhkiG9w0BAQUFADCBrDELMAkGA1UEBhMCRVUx -QzBBBgNVBAcTOk1hZHJpZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2Ft -ZXJmaXJtYS5jb20vYWRkcmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UE -ChMSQUMgQ2FtZXJmaXJtYSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNp -Z24gUm9vdCAtIDIwMDgwHhcNMDkwMzE2MTcxNjI1WhcNMjkwMzExMTcxNjI1WjCB -qjELMAkGA1UEBhMCRVMxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjESMBAG -A1UEBRMJQTgyNzQzMjg3MUswSQYDVQQHE0JNYWRyaWQgKHNlZSBjdXJyZW50IGFk -ZHJlc3MgYXQgaHR0cHM6Ly93d3cuY2FtZXJmaXJtYS5jb20vYWRkcmVzcykxHTAb -BgNVBAMTFEFDIENhbWVyZmlybWEgLSAyMDA5MIICIjANBgkqhkiG9w0BAQEFAAOC -Ag8AMIICCgKCAgEAmbHxFEYTJmMdPcYiPlWUGZu2+tQo4voohYi3dwCwoVuGdHSp -kyoqs1B3YGx4u5KT4n0A7+Bb8YQ/QzbNy7UQ4JXAK+rT8JpNeKIvfN4lHnQJaChE -4fdn0KpvHWymaNq2k+EbQClquZB6OsTLvsivwSuSnyLcUw5rbajj53wq77fwB12y -phMjwz2AnD1BvHZd3vLOaH1jRQP3zzNmyjT/Oj6+jdux7SBKlJWgQEaKflwcvYyc -DPFPhGM4KPwEGX61PCrS+l8Lw0Kdy6K4lE+GrfgJrXM5m1Ey1R0c9McYQQPAtYcm -cOnHHgkJdEAFVDa76T9C+lcMP6DNckbJIyc/ENrmM2v4rq/JnsJKEEx0VLyLizQx -cGU3gp4ckg0ImQ9hV3H/DLWEqfrPuD++zaV81gpstnc9+pLg0Jibvwg3qvIr7nS5 -acc//qqxH0iJGYoStHW5J5HoM9HcBvhACq5rjzjrNLPYSJqbPJwBHKcql/uUjQ6S -SVWe3/CeJp6/vGuY1aRXAk9c/8oO0ZDrLKE8LsUgZesTLnWGd1LQcyQf6UMG1nb9 -5C3eZRkCVpKma6Hl/SUQNukerlbLOU9InFGNPdeEVq1Jo62XeEi8KMbTPdXou6Yl -rpe99dFnOUjVOdY7gfBGSgIVJjORqf/V70jwsxcYz7j6PKl0XulJs06vpSECAwEA -AaOCAmowggJmMBIGA1UdEwEB/wQIMAYBAf8CAQIwHQYDVR0OBBYEFMgAD/zGUvyf -2ztkLjK5bi5x82V5MIHhBgNVHSMEgdkwgdaAFLkJypwe29NsOmuu7VTxW5MGNS5e -oYGypIGvMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3Vy -cmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAG -A1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAl -BgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwOIIJAMnN0+nVfSPO -MH0GCCsGAQUFBwEBBHEwbzBFBggrBgEFBQcwAoY5aHR0cDovL3d3dy5jYW1lcmZp -cm1hLmNvbS9jZXJ0cy9yb290X2NoYW1iZXJzaWduLTIwMDguY3J0MCYGCCsGAQUF -BzABhhpodHRwOi8vb2NzcC5jYW1lcmZpcm1hLmNvbTAOBgNVHQ8BAf8EBAMCAQYw -PgYDVR0gBDcwNTAzBgRVHSAAMCswKQYIKwYBBQUHAgEWHWh0dHBzOi8vcG9saWN5 -LmNhbWVyZmlybWEuY29tMH4GA1UdHwR3MHUwOKA2oDSGMmh0dHA6Ly9jcmwuY2Ft -ZXJmaXJtYS5jb20vY2hhbWJlcnNpZ25yb290LTIwMDguY3JsMDmgN6A1hjNodHRw -Oi8vY3JsMS5jYW1lcmZpcm1hLmNvbS9jaGFtYmVyc2lnbnJvb3QtMjAwOC5jcmww -DQYJKoZIhvcNAQEFBQADggIBABNYG4jBwoI7e8pCuUyDc6rwpE9H6AgrUdL7O1xK -TgTjDGBrMOBK+ZPS4Si8J3yZngvSrL694a1HmiiblJ+CmCdNGli2nBBM+OPK3tQB -4TW6hgkIe3vSNg/9o9y6+MAJcm8Kn0nPCBkSRME87NwvpehtekuF1G2ng1KDVwAn -F+eCXfNanEwY++vWbJAuPE69Z/0+rCgNyH1PzihiNu6vrUlSlLWKaG34O1DEttX+ -SsWTpEbpH9w5y9Vmw6WQ/B5nfhPM551HaMbiGgSxT9jHmf8APYQ3iT8EktcdTAdw -m1miiyxfKG+WjPT7P/x8Np1spJZw+sNIDTLdZ0T1XQ6obVkBTFUDSULKW8949HDu -VSwdl9Hu9lkDzzh9tyVYwwjEWVFZOiD/4TPVLfphf4ZEiyHt5YpNd9kZJIGGDxdc -CdtzPm2dQODFpv72LnPQHbuBQPJ71zkoAmyeM/1Qj0DlrFsPcYnbRasck1VmYgDc -Xc0+is0wcgCd7Gpx1zpEeVqwMD96am2xZPzd6nsbXvo+6TzsKLRMJo6nOERwrzuI -F+/eq3WXxYMt2UenJsHqwSgPJRMdl3SFz0+SZN0viHeLuwb7qaHN74qC6GP8yHGp -2xe6Z11mJDPLDSrQQ2dOceSJ1LurJgLP7amYmFlWwVnmM7LnfShhMWMV+MDrICnL -2ksL ------END CERTIFICATE----- diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustcommercialca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustcommercialca-chain.pem deleted file mode 100644 index 76aa6d14338a4..0000000000000 --- a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustcommercialca-chain.pem +++ /dev/null @@ -1,77 +0,0 @@ -Root Certificate: - Version: 3 (0x2) - Serial Number: 8608355977964138876 (0x7777062726a9b17c) - Signature Algorithm: sha256WithRSAEncryption - Issuer: C=US, O=AffirmTrust, CN=AffirmTrust Commercial - Validity - Not Before: Jan 29 14:06:06 2010 GMT - Not After : Dec 31 14:06:06 2030 GMT - ------BEGIN CERTIFICATE----- -MIIHHjCCBgagAwIBAgIQAWZjFOyCvT00u/gtkCvS2TANBgkqhkiG9w0BAQsFADCB -gzELMAkGA1UEBhMCQ0ExFDASBgNVBAoTC0FmZmlybVRydXN0MSswKQYDVQQLEyJT -ZWUgd3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MTEwLwYDVQQDEyhBZmZp -cm1UcnVzdCBFeHRlbmRlZCBWYWxpZGF0aW9uIENBIC0gRVYxMB4XDTI0MDYyODIx -MzgwNVoXDTI1MDcyODIxMzgwNFowgdgxCzAJBgNVBAYTAkNBMRAwDgYDVQQIEwdP -bnRhcmlvMQ8wDQYDVQQHEwZPdHRhd2ExEzARBgsrBgEEAYI3PAIBAxMCQ0ExGDAW -BgsrBgEEAYI3PAIBAhMHT250YXJpbzEcMBoGA1UEChMTQWZmaXJtdHJ1c3QgTGlt -aXRlZDEdMBsGA1UEDxMUUHJpdmF0ZSBPcmdhbml6YXRpb24xEDAOBgNVBAUTBzI1 -NDA1NDcxKDAmBgNVBAMTH3ZhbGlkY29tbWVyY2lhbC5hZmZpcm10cnVzdC5jb20w -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDeIT2XO0hJ5wDSbIiIcMvs -P3NpQc7O7v5DqldpME6+Qn2sF5b9hc6j72hgTXREa77uUcP5u1JcMWCSWwYQHMpJ -kFzmIzijhS60wW1epb5QyTgM3ZYh1WKvttFCbHUcrTtd+LoPFYsjw9ZK//K9tPp+ -ddn06/ivWvUO5y5vn0wrCaB9tuLdDn4RCQzK2XoZdDuqhPlBBogJX0vM6lsXjgLy -EbvE+/sKYps/In6VtRvCoYavg3OqaIMeaA7gTiYTb1ZGFOAiltnq7fcp6SZUohK3 -QNihv1DadVc+n8LnEUKKDkgG2YgWEFczaE3qwG3ef6L3MzLGrkgVY+qGHyyv2IE7 -AgMBAAGjggM1MIIDMTAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBT4ARNL47hAsOpa -96VMgKEY3sLIAjAfBgNVHSMEGDAWgBTb72U3C+VHyzXRkB8DwbyIx6fqgDBsBggr -BgEFBQcBAQRgMF4wJwYIKwYBBQUHMAGGG2h0dHA6Ly9vY3NwLmFmZmlybXRydXN0 -LmNvbTAzBggrBgEFBQcwAoYnaHR0cDovL2FpYS5hZmZpcm10cnVzdC5jb20vYWZ0 -ZXYxY2EuY3J0MDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuYWZmaXJtdHJ1 -c3QuY29tL2NybC9hZnRldjFjYS5jcmwwKgYDVR0RBCMwIYIfdmFsaWRjb21tZXJj -aWFsLmFmZmlybXRydXN0LmNvbTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYI -KwYBBQUHAwEGCCsGAQUFBwMCMFYGA1UdIARPME0wBwYFZ4EMAQEwQgYKKwYBBAGC -jwkCATA0MDIGCCsGAQUFBwIBFiZodHRwczovL3d3dy5hZmZpcm10cnVzdC5jb20v -cmVwb3NpdG9yeTCCAYAGCisGAQQB1nkCBAIEggFwBIIBbAFqAHcAEvFONL1TckyE -BhnDjz96E/jntWKHiJxtMAWE6+WGJjoAAAGQYMi3wQAABAMASDBGAiEAjvdsU4G2 -o4BZSOOjaH6gOp7zhKtXQByQUvfHfsi2ePcCIQDnnIO2qlHBm+sskUDlXfR0lCUW -yFPVr9nFZ0L9YPpozgB2AA3h8jAr0w3BQGISCepVLvxHdHyx1+kw7w5CHrR+Tqo0 -AAABkGDIt9MAAAQDAEcwRQIhANh1zS3Qeo9yKF+j3G52JhmDRYBS+1TM0wykoXCY -llpxAiAG+LAlKSbwwgrboUSTDDXWNeoRYZ7fKbU72kKfHrpZvwB3ABoE/0nQVB1A -r/agw7/x2MRnL07s7iNAaJhrF0Au3Il9AAABkGDIt9sAAAQDAEgwRgIhAN8OoC4I -zw8bFJy8ACgK40c9ZfsIfFhePTc9CyrL5uDsAiEA4Jn/IqBB9L5DeTgqw9hBaYag -FmY/2gWDip36ga0WUsAwDQYJKoZIhvcNAQELBQADggEBABywPLJP097Emz6LNeFU -/HvfhaUKv2pgIHf/Kvjs5x78RK9G605THPEHr/TeUjNZ4PBd48WBNVWzyd/8FuOt -r+FsYkRJb9CnrOhZHuCwlcdWXvuY8PiuBmT+xB16BWR5yhYbbiGe4hea0Pf6CfHh -jJoGJw4dQKfgneZOV7IcaWnNTKYawlcZOgxvEwFvj+iZM31WphEPKRAV+N+Tp+ZR -nxlEdjmdbOjqBydlYIEzuFIgxgtnPdK5wqCOWb+z2cARUAO/AkiWrOLTPDc7ydQK -GcfDrSqffHOlwaee08C6STFaJWIcpqxZdXE6Jc+8/85bfPEAG1UepgfnBTqW9RGT -Q3s= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEqDCCA5CgAwIBAgIQFylVHtaOf7Ht9XMA811/1TANBgkqhkiG9w0BAQsFADBE -MQswCQYDVQQGEwJVUzEUMBIGA1UECgwLQWZmaXJtVHJ1c3QxHzAdBgNVBAMMFkFm -ZmlybVRydXN0IENvbW1lcmNpYWwwHhcNMTkwMzIxMjAyNzU0WhcNMzAxMjAyMDQw -MDAwWjCBgzELMAkGA1UEBhMCQ0ExFDASBgNVBAoTC0FmZmlybVRydXN0MSswKQYD -VQQLEyJTZWUgd3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MTEwLwYDVQQD -EyhBZmZpcm1UcnVzdCBFeHRlbmRlZCBWYWxpZGF0aW9uIENBIC0gRVYxMIIBIjAN -BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuPBMIa9VuXJGAw0MHvieGciPFA11 -b9T49YJ7T+zVpoMMQO+ueUKVHb2l26oeCiwIhXMQ5LquOVcx+rofouzcKXY3wKDZ -zHIOnAkU+23Ucn/3dRH7aHJULsBufZq+NvwgYSgJJEDKfqvIV/c5HiRyZ2H+nAI5 -10Q2xC0UxgSBsufccQ+Fwkg6BAGDlTXrvi8wi75UaGue6jv/qcKLybeVUrgqKE64 -d9oa9PG5/g89QwSdsIQEdVSFzFvFpOG9YhJbJ177Zg6DGCxU0lWwFrVpyH/2vnXl -jhMQScn8UxzCJdDg3EDqjgaV0JH2yoLug+QVYgURPu5BEb5ut9vAdP7cLwIDAQAB -o4IBVDCCAVAwNwYIKwYBBQUHAQEEKzApMCcGCCsGAQUFBzABhhtodHRwOi8vb2Nz -cC5hZmZpcm10cnVzdC5jb20wHQYDVR0OBBYEFNvvZTcL5UfLNdGQHwPBvIjHp+qA -MBIGA1UdEwEB/wQIMAYBAf8CAQAwHwYDVR0jBBgwFoAUnZPGU4teyq8/nx4P5ZmV -vCT2lI8wRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8v -d3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MEkGA1UdHwRCMEAwPqA8oDqG -OGh0dHA6Ly9jcmwuYWZmaXJtdHJ1c3QuY29tL2NybC9BZmZpcm1UcnVzdENvbW1l -cmNpYWwuY3JsMA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYI -KwYBBQUHAwIwDQYJKoZIhvcNAQELBQADggEBAATH11fMrINGmQGQqQW0ATteVnUG -LrmRSN2OlmRm+dkUwKXhcQQEfYYlEggPqgvxSUpw13fXSOqVHqAcj3BIqF957kh+ -m3DmC0RX9KaEKD165pf77P5nZcRmZpBl9cctvzIxN19uzcminchusYwLyeWhBtTZ -xpER9LbrfMNaQ7GnrgalMx54QvdjOhw/GJs9/SqEzYmPshL+DzgZX/oAzY63rQIh -rBblf6/2talZqci96oFzNst8rGfPy/xQ7lgkki1hwIYbORMfloBhP+vAZJo0mxdM -ipu3Z0ToK+KU2iqnBxXVr2/kod+CpkHnjUHa1wnQuSaefng3XwZ/vqtSL9c= ------END CERTIFICATE----- diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustnetworkingca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustnetworkingca-chain.pem deleted file mode 100644 index 7384d31152e71..0000000000000 --- a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustnetworkingca-chain.pem +++ /dev/null @@ -1,76 +0,0 @@ -Root Certificate: - Version: 3 (0x2) - Serial Number: 8957382827206547757 (0x7c4f04391cd4992d) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=AffirmTrust, CN=AffirmTrust Networking - Validity - Not Before: Jan 29 14:08:24 2010 GMT - Not After : Dec 31 14:08:24 2030 GMT - ------BEGIN CERTIFICATE----- -MIIHGjCCBgKgAwIBAgIQX2vGPaCJ1tS0ncp2OlBMFjANBgkqhkiG9w0BAQsFADCB -gzELMAkGA1UEBhMCQ0ExFDASBgNVBAoTC0FmZmlybVRydXN0MSswKQYDVQQLEyJT -ZWUgd3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MTEwLwYDVQQDEyhBZmZp -cm1UcnVzdCBFeHRlbmRlZCBWYWxpZGF0aW9uIENBIC0gRVYzMB4XDTI0MDYyODIx -NDU0OVoXDTI1MDcyODIxNDU0OFowgdgxCzAJBgNVBAYTAkNBMRAwDgYDVQQIEwdP -bnRhcmlvMQ8wDQYDVQQHEwZPdHRhd2ExEzARBgsrBgEEAYI3PAIBAxMCQ0ExGDAW -BgsrBgEEAYI3PAIBAhMHT250YXJpbzEcMBoGA1UEChMTQWZmaXJtdHJ1c3QgTGlt -aXRlZDEdMBsGA1UEDxMUUHJpdmF0ZSBPcmdhbml6YXRpb24xEDAOBgNVBAUTBzI1 -NDA1NDcxKDAmBgNVBAMTH3ZhbGlkbmV0d29ya2luZy5hZmZpcm10cnVzdC5jb20w -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCkGknE8kFr+CaIybQrDPRw -z9OKXq77p4CnrkF1/g9w/HiIs6Ps8YqTjsiTKM3wYLbvPA+TbO9DpCSyCP2bVyLf -AjUE617KZSpfy9RqzvGjn/1qH/cBKohhEliMfDj4ZHfY4x+1WYTZPVK/g0Ny5RAP -wz9lJHR2SsVGLvpqXzWaVoxifJ8HZWD7n5z/75WeYko+Hubx3WvzJZcN2Xjn+q6a -7wkDaXPayrvn5uWGPlOLQHqJ5z7wts21jASMTfJAToFyzH6dGwvqxkP3bVJGJ8AF -vtMfqVjcOcjWgmmOEHMPAAqs5QKrYuSLccH6hFTwFEUCdMwVqfloznt2sNUSBoKj -AgMBAAGjggMxMIIDLTAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTrE0z4fRyx9P9M -0FfA6VgGkJiYVDAfBgNVHSMEGDAWgBR5HrHJF8cerLHHFNfD6H+8uVCbFTBsBggr -BgEFBQcBAQRgMF4wJwYIKwYBBQUHMAGGG2h0dHA6Ly9vY3NwLmFmZmlybXRydXN0 -LmNvbTAzBggrBgEFBQcwAoYnaHR0cDovL2FpYS5hZmZpcm10cnVzdC5jb20vYWZ0 -ZXYzY2EuY3J0MDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuYWZmaXJtdHJ1 -c3QuY29tL2NybC9hZnRldjNjYS5jcmwwKgYDVR0RBCMwIYIfdmFsaWRuZXR3b3Jr -aW5nLmFmZmlybXRydXN0LmNvbTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYI -KwYBBQUHAwEGCCsGAQUFBwMCMFYGA1UdIARPME0wBwYFZ4EMAQEwQgYKKwYBBAGC -jwkCAjA0MDIGCCsGAQUFBwIBFiZodHRwczovL3d3dy5hZmZpcm10cnVzdC5jb20v -cmVwb3NpdG9yeTCCAXwGCisGAQQB1nkCBAIEggFsBIIBaAFmAHYADeHyMCvTDcFA -YhIJ6lUu/Ed0fLHX6TDvDkIetH5OqjQAAAGQYM/MjQAABAMARzBFAiBjnehs1mvh -5Xm3uXZ7Bq8gijwiXThwnLSYROQxnWrnbAIhALbgJG+PRZQfzTBbgM/zAwNsBjhe -F5iENnaajJCxzOhaAHUAEvFONL1TckyEBhnDjz96E/jntWKHiJxtMAWE6+WGJjoA -AAGQYM/MgQAABAMARjBEAiAsWOm1IIjaxQP9uaPI9tQmkiJPUOTrBTsTDO+jkgiG -+QIgVNhND82rsFGjrtAAHzzgCVzLDUM3zaHxnP/z3BNuO4QAdQAaBP9J0FQdQK/2 -oMO/8djEZy9O7O4jQGiYaxdALtyJfQAAAZBgz8zLAAAEAwBGMEQCIBIGxtjk7Lw8 -i+oggK7VrPMNTB632t321cwhEm517BbZAiBws3+uytwh59N6qGJUuSFQnOZNPOPj -eQnH2fSdT1J2sDANBgkqhkiG9w0BAQsFAAOCAQEAcSzitESRKlbcUvxvUB7FjK0I -CaBU1Nyu0xDFCoG2pmp7GASJz34wtPYfsiX5+j4hDh/noMcgk7WlD8pzgWYw15Rk -+5kTv2v4U85y/JFjzMOHbz64KjQdGebqhjvC/E/EXxK+AZf4H574/w7rbyJ30vFL -gNvPF9AxS1MuYIO55jXrHMByKnFoQZgPsmAY/x+n+OzMxWOdR18PupypCB5TyJZ8 -pQzwoxmX7qeZHiXyJ8jQUwe1qoQc2SbwfQxfwSPUPSJuQo90N+5nyQMe7vvPBM0Y -/CXaFpfPqh71D4C0Ey+0hYxSt99gYs4P9twUByjIlP0wTyhaoEpt3zw9DdZypQ== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEqDCCA5CgAwIBAgIQNCSh7Pjwo1/nRrcBHEPoRDANBgkqhkiG9w0BAQsFADBE -MQswCQYDVQQGEwJVUzEUMBIGA1UECgwLQWZmaXJtVHJ1c3QxHzAdBgNVBAMMFkFm -ZmlybVRydXN0IE5ldHdvcmtpbmcwHhcNMTkwMzIxMjAzODU5WhcNMzAxMjAyMDQw -MDAwWjCBgzELMAkGA1UEBhMCQ0ExFDASBgNVBAoTC0FmZmlybVRydXN0MSswKQYD -VQQLEyJTZWUgd3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MTEwLwYDVQQD -EyhBZmZpcm1UcnVzdCBFeHRlbmRlZCBWYWxpZGF0aW9uIENBIC0gRVYzMIIBIjAN -BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmHDl/3xr1qiHoe0Rzb3AGLw56e9J -l2a3X59+PAfI5wGBHuK9Dl7XsyoH65X6QIC/rXyVpuNgKbbwIGHB+rCSplyHzGyC -WeM3LXa2q1US7VteeFDS959nxJVRFfwATR9xAK6YTUWQ/yWdw0dZSm0lQNmEMBwS -qi0ufWokiWXZUzWHOu7A6driCohu9sFDwe1INJUPH6uIlovmzGvG3UYbUSymJcjs -Ka0fXXX9zukco8exlOIKWRJSNLxKtSSPDVASrGLQ1xi3qkiLTKci3+jKMNDFf1vw -foZN99HhUcWKXfr2KlWfANdjTMlsTKCfuhfWl1OBVNHGRrACAQCXI/ji0wIDAQAB -o4IBVDCCAVAwNwYIKwYBBQUHAQEEKzApMCcGCCsGAQUFBzABhhtodHRwOi8vb2Nz -cC5hZmZpcm10cnVzdC5jb20wHQYDVR0OBBYEFHkesckXxx6ssccU18Pof7y5UJsV -MBIGA1UdEwEB/wQIMAYBAf8CAQAwHwYDVR0jBBgwFoAUBx/S55zawm6iQLSwelAQ -UHTEyL0wRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8v -d3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MEkGA1UdHwRCMEAwPqA8oDqG -OGh0dHA6Ly9jcmwuYWZmaXJtdHJ1c3QuY29tL2NybC9BZmZpcm1UcnVzdE5ldHdv -cmtpbmcuY3JsMA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYI -KwYBBQUHAwIwDQYJKoZIhvcNAQELBQADggEBAAhmE4I56hNpnWXQ2Si8a/TgQUZr -X5Jlv1LDvl3rkDyfEIHNZ8dth17SakJYJBWHExph/iIYjCJ9YmeyhghV5rPqT+wF -4yyE2ngenIusfnWT2bTpT9u2VZbCNeACE5XnN2UHSA0J9idPjfLuthViWEvSZZUh -DJ53bX+exO366nDY4AI7owIyhz8hdsWyhZ/0ST+eD+kbgd8osd+GdxzRmyKcfl84 -D1K1uff01T9w2dyUaZglQsFljkaO6xmeXZJsPnhwCp/HlMHWzhAneUQ7I9FZSOW+ -WiYbt4RitmBpysadBReikWM4knECzJQ/fMT9vC0k9BLlqUYRwCH9vr0UnZo= ------END CERTIFICATE----- diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustpremiumca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustpremiumca-chain.pem deleted file mode 100644 index 6f108bc1229ef..0000000000000 --- a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustpremiumca-chain.pem +++ /dev/null @@ -1,88 +0,0 @@ -Root Certificate: - Version: 3 (0x2) - Serial Number: 7893706540734352110 (0x6d8c1446b1a60aee) - Signature Algorithm: sha384WithRSAEncryption - Issuer: C=US, O=AffirmTrust, CN=AffirmTrust Premium - Validity - Not Before: Jan 29 14:10:36 2010 GMT - Not After : Dec 31 14:10:36 2040 GMT - ------BEGIN CERTIFICATE----- -MIIIFjCCBv6gAwIBAgIQQVOTWr7tEAJXmRDkCSxkajANBgkqhkiG9w0BAQsFADCB -gzELMAkGA1UEBhMCQ0ExFDASBgNVBAoTC0FmZmlybVRydXN0MSswKQYDVQQLEyJT -ZWUgd3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MTEwLwYDVQQDEyhBZmZp -cm1UcnVzdCBFeHRlbmRlZCBWYWxpZGF0aW9uIENBIC0gRVYyMB4XDTI0MDYyODIx -NDgyN1oXDTI1MDcyODIxNDgyNlowgdUxCzAJBgNVBAYTAkNBMRAwDgYDVQQIEwdP -bnRhcmlvMQ8wDQYDVQQHEwZPdHRhd2ExEzARBgsrBgEEAYI3PAIBAxMCQ0ExGDAW -BgsrBgEEAYI3PAIBAhMHT250YXJpbzEcMBoGA1UEChMTQWZmaXJtdHJ1c3QgTGlt -aXRlZDEdMBsGA1UEDxMUUHJpdmF0ZSBPcmdhbml6YXRpb24xEDAOBgNVBAUTBzI1 -NDA1NDcxJTAjBgNVBAMTHHZhbGlkcHJlbWl1bS5hZmZpcm10cnVzdC5jb20wggIi -MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVRMzwbDq47ivHOKqJdiEJNL2+ -g9Snj/BRctqcQTrIV99RP0pmAh5fHg7vnhVsHqc9sRLVcQWTJk9NuRJ2VnDKWsBa -Xrp5UWaNjS0vaFA4jzCi1gWzTTZgPTQn3VRG3JP1F5CZb405/mtWDaw/CfWkcUqQ -VSilqFlJRsjcPCzQh7ZaXAo+FmzJxNSwjxdP6JSYMeTDRCUpSb3T8PypVI1CEmLZ -jsxrg5oIZn25591g/pzgLE56N0stNY4d3q4YD1t5x46RsqYAJYSkk8rcTN+kHzsY -VSqaRDyPkGbmuCeJUvW24wJ30yQtXQWA+U0dMYLe7LyglJ7dkOzvWNbqrIcvM8My -hxH/wwVH7e4dL/1E58yr1BHENUk7Mp9rzIXj496eLkF5G1lMkNnuVRQqCAOW0rPY -V0rI8yrCMTK52s4mNjQo2J7JOYdTUvAWZ92MKvEjjhQlMH8eK72Km/+mkxpsgGmr -3c6u+Gom7oI5VaLZ+3p2uWaOsutk1tkzWjhzY4L27hwmIdWujfrWMRx8uxcfoJxX -gQ40d1QiSN51BtCPE5UnpLU/YUxMdzWmtUoGUfYIGVqDVToBnunIFMdmFjC0IrNl -hquDQi/OGMpzuOvxX1FoXb+rRwOhhdrcR0BQqUVRTV0U5LlcsDeNMqmqPE9mzGtJ -W69Fsh7crntng/L72wIDAQABo4IDMDCCAywwDAYDVR0TAQH/BAIwADAdBgNVHQ4E -FgQU3PWyi/4usZghgahc/Tj+Q60QLOcwHwYDVR0jBBgwFoAUc3yaOGg8UXxBCP6h -HyoetGHbzTwwbAYIKwYBBQUHAQEEYDBeMCcGCCsGAQUFBzABhhtodHRwOi8vb2Nz -cC5hZmZpcm10cnVzdC5jb20wMwYIKwYBBQUHMAKGJ2h0dHA6Ly9haWEuYWZmaXJt -dHJ1c3QuY29tL2FmdGV2MmNhLmNydDA8BgNVHR8ENTAzMDGgL6AthitodHRwOi8v -Y3JsLmFmZmlybXRydXN0LmNvbS9jcmwvYWZ0ZXYyY2EuY3JsMCcGA1UdEQQgMB6C -HHZhbGlkcHJlbWl1bS5hZmZpcm10cnVzdC5jb20wDgYDVR0PAQH/BAQDAgWgMB0G -A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBWBgNVHSAETzBNMAcGBWeBDAEB -MEIGCisGAQQBgo8JAgMwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly93d3cuYWZmaXJt -dHJ1c3QuY29tL3JlcG9zaXRvcnkwggF+BgorBgEEAdZ5AgQCBIIBbgSCAWoBaAB2 -ABoE/0nQVB1Ar/agw7/x2MRnL07s7iNAaJhrF0Au3Il9AAABkGDSN7EAAAQDAEcw -RQIgVDWwhv7yG6RNnkMZnVq1YYA7ypn/GSH0ibUKnESHRpYCIQCY8gyCX7VFONUI -QuR8daz7ra2FCUI9TwylrR3eFfIgGgB3AN3cyjSV1+EWBeeVMvrHn/g9HFDf2wA6 -FBJ2Ciysu8gqAAABkGDSN5cAAAQDAEgwRgIhAM1edsSyFUKU0Dj1WxTGwziE6fCW -g2ByfL8kDrP260YXAiEA6YQOpJf04N13Nn263BxAl+laH9Ar0eo03fArlv743TQA -dQAN4fIwK9MNwUBiEgnqVS78R3R8sdfpMO8OQh60fk6qNAAAAZBg0je+AAAEAwBG -MEQCIExqK4katETAQo+H0+ImuNJCSeFEI9C+9wrjhl6ZnWb9AiBwkC1vpLYOIm/1 -YCLCQIOmTdg2wf8LITlrQNJA8vbBljANBgkqhkiG9w0BAQsFAAOCAQEASOmPu7ot -yl6MoMns19uI6H2KSUjMFh3/fKMcY/ettmEYalgrytexFMrLnD2UniBlD+nJEshp -5/z7o0YDiRoiLhMAs7VqIdX3erNu/ghNh7P2bDnoMWShSoAKxez1XOGL3rRE0NAi -DsWCaNRHH9rnC97275sbGnua7ZYg+8BiF62vpJlqjrxDHjGiej8qAWSjztbB43Af -bwRscpXTxNkMvOBuRFMH+rSxB8CrOV68W+yxmzPuPxVjM7oJH8Qk5BC53NRqFsVz -JhbNfot0+/drj7JT3jlacUVQcD/BzDuC3+qczQlLjLdHgQM2/e4fXsD6C5S6B11d -BDx6ipGpaASofA== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFojCCA4qgAwIBAgIQU3HI6weE/VEI5dTz4yPsRjANBgkqhkiG9w0BAQsFADBB -MQswCQYDVQQGEwJVUzEUMBIGA1UECgwLQWZmaXJtVHJ1c3QxHDAaBgNVBAMME0Fm -ZmlybVRydXN0IFByZW1pdW0wHhcNMTkwMzIxMjA0NjM1WhcNMzAxMjAyMDQwMDAw -WjCBgzELMAkGA1UEBhMCQ0ExFDASBgNVBAoTC0FmZmlybVRydXN0MSswKQYDVQQL -EyJTZWUgd3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MTEwLwYDVQQDEyhB -ZmZpcm1UcnVzdCBFeHRlbmRlZCBWYWxpZGF0aW9uIENBIC0gRVYyMIIBIjANBgkq -hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvDDZHfxkB1nAGFKdw0VCgV+B/eBtW1o+ -bXzwRcpeFh5saDI+tv1RAMrYFq+AJkXCCJopgMF2Wqfv5myE3JMgxEHuuKUpJz7H -FprrFckVOGCtJKH8Iy9AWPjBwt8lKmxGJF7EZst+QoVt4hMe0qhL0WEKbATFPe41 -DcM7UsyQv6Bvpn424uePy3/1ATIsVL3YmvAbUNR0aqVxYAJzTefvyIet/761bKGc -NyqdOVWFFeTDtr8iL1TBXToAgl0GJ39bFQZsP19VcCpfk9Zj3YHTPRPq5wZOZuUN -F7jiBUEi6DaVOi3Wy4vdySHtWPeBHRYif1I6fcUfdCNORMc4ee6KewIDAQABo4IB -UTCCAU0wNwYIKwYBBQUHAQEEKzApMCcGCCsGAQUFBzABhhtodHRwOi8vb2NzcC5h -ZmZpcm10cnVzdC5jb20wHQYDVR0OBBYEFHN8mjhoPFF8QQj+oR8qHrRh2808MBIG -A1UdEwEB/wQIMAYBAf8CAQAwHwYDVR0jBBgwFoAUncBnpgwi2Sb1RaumZVIRJ9hF -rGMwRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3 -LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MEYGA1UdHwQ/MD0wO6A5oDeGNWh0 -dHA6Ly9jcmwuYWZmaXJtdHJ1c3QuY29tL2NybC9BZmZpcm1UcnVzdFByZW1pdW0u -Y3JsMA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUH -AwIwDQYJKoZIhvcNAQELBQADggIBABi64UEwl3l0yIiuSACyVQQIBI60BUmhseac -4BzCAsJrR5tE/2U9QAa2y6JpR1nqm76DJvw1QQgvFcNe+fkwpvoViCaSTbZkGGwD -mQe2xRSYJcDSMQUc/GgzLcX2c1CrexQXE1vwV/q33af1en5s1GzLl915aNS/k1ch -G7EMruJ/D4cuH9j4j2i+b+llmVBzavBwelN5rc693o+Ot9id/1sTWNugwAu3uXGb -VlhETMnjXGIciegOLdWYhWBln0izYlt9IwlDEpjMVaZ0HZlj2JBGaSe4PfEFpJPO -beuPcQpLQGw2XpW2ZMG5JcRYaoKWjixXAGktRA3H9nvVW92jvzx/RX484w2ZM5Rt -E+I1ikAuQLAyWG7clht387e2RuC3NZTtefSyjE3L9gQDOPC+Z9ycwr0WJHRsxFvh -FJQi3JnxgFZf5mc5n2mh3qAgALTNOUHuDiHrerjTOWbpF/1/NJmo/c/YZ63vZIhc -EaER4HuhbBqlpf6z3WOIQdZm1ChwXYHrEcLDgfwm9cXoaVK2HZapkMwQbPffPlT1 -E+AxRFB4YmT1y2WzdaHfhFA9nH6ByUdL5+FfrDoIIUO2e8OLOAcrJsf5+unhAhc0 -v7N48JWdmpstjkXCaCIaidrZLJxS+pikNgHB1dXF/TxokLTiPB9jcYKdGaYs3XHb -YKLdwubu ------END CERTIFICATE----- diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustpremiumeccca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustpremiumeccca-chain.pem deleted file mode 100644 index 37b1b787084bb..0000000000000 --- a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustpremiumeccca-chain.pem +++ /dev/null @@ -1,63 +0,0 @@ -Root Certificate: - Version: 3 (0x2) - Serial Number: 8401224907861490260 (0x7497258ac73f7a54) - Signature Algorithm: ecdsa-with-SHA384 - Issuer: C=US, O=AffirmTrust, CN=AffirmTrust Premium ECC - Validity - Not Before: Jan 29 14:20:24 2010 GMT - Not After : Dec 31 14:20:24 2040 GMT - ------BEGIN CERTIFICATE----- -MIIF0zCCBVmgAwIBAgIQFVwk9nYUM5SYOnBd+IoGtzAKBggqhkjOPQQDAzCBhTEL -MAkGA1UEBhMCQ0ExFDASBgNVBAoTC0FmZmlybVRydXN0MSswKQYDVQQLEyJTZWUg -d3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MTMwMQYDVQQDEypBZmZpcm1U -cnVzdCBFeHRlbmRlZCBWYWxpZGF0aW9uIENBIC0gRVZFQzEwHhcNMjQwNjI4MjE0 -OTUwWhcNMjUwNzI4MjE0OTQ4WjCB2DELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09u -dGFyaW8xDzANBgNVBAcTBk90dGF3YTETMBEGCysGAQQBgjc8AgEDEwJDQTEYMBYG -CysGAQQBgjc8AgECEwdPbnRhcmlvMRwwGgYDVQQKExNBZmZpcm10cnVzdCBMaW1p -dGVkMR0wGwYDVQQPExRQcml2YXRlIE9yZ2FuaXphdGlvbjEQMA4GA1UEBRMHMjU0 -MDU0NzEoMCYGA1UEAxMfdmFsaWRwcmVtaXVtZWNjLmFmZmlybXRydXN0LmNvbTB2 -MBAGByqGSM49AgEGBSuBBAAiA2IABEkLBzBYSJPRENKDaA1iBPQz+jZUV+OoM9nJ -sr9sMfmHaqr3nlWxAMM99b9/usVfYyUxqyi+YL2Z3ZSxjX2dpyhwMtPpIQkL1pMW -Iv55XBIcYRyl2NjcADS9B06G+nnix6OCAzcwggMzMAwGA1UdEwEB/wQCMAAwHQYD -VR0OBBYEFP+37ywf2YJJ/4CEVy1GY4ioGm1yMB8GA1UdIwQYMBaAFMaQjAKD113j -vjucLtVlfSoQYO7lMG4GCCsGAQUFBwEBBGIwYDAnBggrBgEFBQcwAYYbaHR0cDov -L29jc3AuYWZmaXJtdHJ1c3QuY29tMDUGCCsGAQUFBzAChilodHRwOi8vYWlhLmFm -ZmlybXRydXN0LmNvbS9hZnRldmVjMWNhLmNydDA+BgNVHR8ENzA1MDOgMaAvhi1o -dHRwOi8vY3JsLmFmZmlybXRydXN0LmNvbS9jcmwvYWZ0ZXZlYzFjYS5jcmwwKgYD -VR0RBCMwIYIfdmFsaWRwcmVtaXVtZWNjLmFmZmlybXRydXN0LmNvbTAOBgNVHQ8B -Af8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMFYGA1UdIARP -ME0wBwYFZ4EMAQEwQgYKKwYBBAGCjwkCBDA0MDIGCCsGAQUFBwIBFiZodHRwczov -L3d3dy5hZmZpcm10cnVzdC5jb20vcmVwb3NpdG9yeTCCAX4GCisGAQQB1nkCBAIE -ggFuBIIBagFoAHUA5tIxY0B3jMEQQQbXcbnOwdJA9paEhvu6hzId/R43jlAAAAGQ -YNN5tQAABAMARjBEAiAnainEoBGI9czVh+c9QLPL30S3Rtov8zrnhlXfeKLzZQIg -UGkntBMux0MqHt9Aj60qMsS/C4ZWF7AihVVaUKcrEVgAdgAN4fIwK9MNwUBiEgnq -VS78R3R8sdfpMO8OQh60fk6qNAAAAZBg03m1AAAEAwBHMEUCIGI9kBByoozH4cfS -ECW/O2N/ElkdATkt7EwQ52kcc4ICAiEA9QTh8JlJTb/ytYC1ECX0vQbrYVexg+fu -dw7dfToF9nAAdwAS8U40vVNyTIQGGcOPP3oT+Oe1YoeInG0wBYTr5YYmOgAAAZBg -03ndAAAEAwBIMEYCIQCox5nSCcVB2AfNYXco77zsJnYP7KAU2I4VA2GNL7I4wQIh -AP6WEzyfBoGpYYqFmNnJUavyhKBmeNiR7eNtaFwpSc+UMAoGCCqGSM49BAMDA2gA -MGUCMAGSNMXAAKDRk0ZOtydN95Rkja97+70TatCIIxEAsJD8Hu7lfj2LHCYFQjVY -oaWTrQIxAKUudx7E/JnjsthuL6sNqKVHfD3iLUJyQNK9wE0SVt1xAm7Cu1JXZORE -M64KMKoQFQ== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDXDCCAuKgAwIBAgIQAgKlhME0Bk3J8y0gfqNymDAKBggqhkjOPQQDAzBFMQsw -CQYDVQQGEwJVUzEUMBIGA1UECgwLQWZmaXJtVHJ1c3QxIDAeBgNVBAMMF0FmZmly -bVRydXN0IFByZW1pdW0gRUNDMB4XDTE5MDMyMTIwNTUwN1oXDTMwMTIwMjA0MDAw -MFowgYUxCzAJBgNVBAYTAkNBMRQwEgYDVQQKEwtBZmZpcm1UcnVzdDErMCkGA1UE -CxMiU2VlIHd3dy5hZmZpcm10cnVzdC5jb20vcmVwb3NpdG9yeTEzMDEGA1UEAxMq -QWZmaXJtVHJ1c3QgRXh0ZW5kZWQgVmFsaWRhdGlvbiBDQSAtIEVWRUMxMHYwEAYH -KoZIzj0CAQYFK4EEACIDYgAEu9f5NkumdaVlmaNaxpDB+rBk/S6lhqcUU1zTLcRz -4G0dr4290hezjrvZJxGJ/X15aexpdD2V9cwaPD/yuEJcaaz+rg/qDoqQF3+AFqVc -41jw1E0S59+57XVKLtXI7Xh6o4IBVDCCAVAwNwYIKwYBBQUHAQEEKzApMCcGCCsG -AQUFBzABhhtodHRwOi8vb2NzcC5hZmZpcm10cnVzdC5jb20wHQYDVR0OBBYEFMaQ -jAKD113jvjucLtVlfSoQYO7lMBIGA1UdEwEB/wQIMAYBAf8CAQAwHwYDVR0jBBgw -FoAUmq8pesARNTUmUTAAw2r+QNWu1jwwRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYI -KwYBBQUHAgEWJmh0dHBzOi8vd3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5 -MEkGA1UdHwRCMEAwPqA8oDqGOGh0dHA6Ly9jcmwuYWZmaXJtdHJ1c3QuY29tL2Ny -bC9BZmZpcm1UcnVzdFByZW1pdW1FQ0MuY3JsMA4GA1UdDwEB/wQEAwIBhjAdBgNV -HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwCgYIKoZIzj0EAwMDaAAwZQIwHJ5g -a6sHvQ51DGr0bWq34awuwlWbybC2grHoNp5uYapcXr/qTJusb/6n+dczqFdaAjEA -7VQY06fE9ifMnTgT9824jc3+H6kfhMk4PoIj9ouWdYfc1DyTBS/low9Hb8liQyFr ------END CERTIFICATE----- diff --git a/test/jdk/sun/security/tools/jarsigner/ConciseJarsigner.java b/test/jdk/sun/security/tools/jarsigner/ConciseJarsigner.java index 69b45ea22930d..e81a25712a6b2 100644 --- a/test/jdk/sun/security/tools/jarsigner/ConciseJarsigner.java +++ b/test/jdk/sun/security/tools/jarsigner/ConciseJarsigner.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,8 +43,15 @@ public class ConciseJarsigner { static OutputAnalyzer kt(String cmd) throws Exception { // Choose 2048-bit RSA to make sure it runs fine and fast. In // fact, every keyalg/keysize combination is OK for this test. - return SecurityTools.keytool("-storepass changeit -keypass changeit " - + "-keystore ks -keyalg rsa -keysize 2048 " + cmd); + // The start date is set to -1M to prevent the certificate not yet valid during fast enough execution. + // If -startdate is specified in cmd, cmd version will be used. + if (cmd.contains("-startdate")) { + return SecurityTools.keytool("-storepass changeit -keypass changeit " + + "-keystore ks -keyalg rsa -keysize 2048 " + cmd); + } else { + return SecurityTools.keytool("-storepass changeit -keypass changeit " + + "-keystore ks -keyalg rsa -keysize 2048 -startdate -1M " + cmd); + } } static void gencert(String owner, String cmd) throws Exception { diff --git a/test/jdk/sun/security/tools/jarsigner/EC.java b/test/jdk/sun/security/tools/jarsigner/EC.java index 848dc8bf843f9..1b41c48e2346d 100644 --- a/test/jdk/sun/security/tools/jarsigner/EC.java +++ b/test/jdk/sun/security/tools/jarsigner/EC.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ * @bug 6870812 * @summary enhance security tools to use ECC algorithm * @library /test/lib + * @run main/timeout=300 EC */ import jdk.test.lib.SecurityTools; diff --git a/test/jdk/sun/security/tools/jarsigner/RemovedFiles.java b/test/jdk/sun/security/tools/jarsigner/RemovedFiles.java new file mode 100644 index 0000000000000..7a4e566efa68b --- /dev/null +++ b/test/jdk/sun/security/tools/jarsigner/RemovedFiles.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8309841 + * @summary Jarsigner should print a warning if an entry is removed + * @library /test/lib + */ + +import jdk.test.lib.SecurityTools; +import jdk.test.lib.util.JarUtils; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +public class RemovedFiles { + + private static final String NONEXISTENT_ENTRIES_FOUND + = "This jar contains signed entries for files that do not exist. See the -verbose output for more details."; + + public static void main(String[] args) throws Exception { + JarUtils.createJarFile( + Path.of("a.jar"), + Path.of("."), + Files.writeString(Path.of("a"), "a"), + Files.writeString(Path.of("b"), "b")); + SecurityTools.keytool("-genkeypair -storepass changeit -keystore ks -alias x -dname CN=x -keyalg ed25519"); + SecurityTools.jarsigner("-storepass changeit -keystore ks a.jar x"); + + // All is fine at the beginning. + SecurityTools.jarsigner("-verify a.jar") + .shouldNotContain(NONEXISTENT_ENTRIES_FOUND); + + // Remove an entry after signing. There will be a warning. + JarUtils.deleteEntries(Path.of("a.jar"), "a"); + SecurityTools.jarsigner("-verify a.jar") + .shouldContain(NONEXISTENT_ENTRIES_FOUND); + SecurityTools.jarsigner("-verify -verbose a.jar") + .shouldContain(NONEXISTENT_ENTRIES_FOUND) + .shouldContain("Warning: nonexistent signed entries: [a]"); + + // Remove one more entry. + JarUtils.deleteEntries(Path.of("a.jar"), "b"); + SecurityTools.jarsigner("-verify a.jar") + .shouldContain(NONEXISTENT_ENTRIES_FOUND); + SecurityTools.jarsigner("-verify -verbose a.jar") + .shouldContain(NONEXISTENT_ENTRIES_FOUND) + .shouldContain("Warning: nonexistent signed entries: [a, b]"); + + // Re-sign will not clear the warning. + SecurityTools.jarsigner("-storepass changeit -keystore ks a.jar x"); + SecurityTools.jarsigner("-verify a.jar") + .shouldContain(NONEXISTENT_ENTRIES_FOUND); + + // Unfortunately, if there is a non-file entry in manifest, there will be + // a false alarm. See https://bugs.openjdk.org/browse/JDK-8334261. + var man = new Manifest(); + man.getMainAttributes().putValue("Manifest-Version", "1.0"); + man.getEntries().computeIfAbsent("Hello", key -> new Attributes()) + .putValue("Foo", "Bar"); + JarUtils.createJarFile(Path.of("b.jar"), + man, + Path.of("."), + Path.of("a")); + SecurityTools.jarsigner("-storepass changeit -keystore ks b.jar x"); + SecurityTools.jarsigner("-verbose -verify b.jar") + .shouldContain("Warning: nonexistent signed entries: [Hello]") + .shouldContain(NONEXISTENT_ENTRIES_FOUND); + + } +} diff --git a/test/jdk/sun/security/tools/jarsigner/TimestampCheck.java b/test/jdk/sun/security/tools/jarsigner/TimestampCheck.java index 5596ca3a7c7d9..0c7d634da3aef 100644 --- a/test/jdk/sun/security/tools/jarsigner/TimestampCheck.java +++ b/test/jdk/sun/security/tools/jarsigner/TimestampCheck.java @@ -888,7 +888,7 @@ static void prepare() throws Exception { } gencert("tsold", "-ext eku:critical=ts -startdate -40d -validity 500"); - gencert("tsbefore2019", "-ext eku:critical=ts -startdate 2018/01/01 -validity 3000"); + gencert("tsbefore2019", "-ext eku:critical=ts -startdate 2018/01/01 -validity 5000"); gencert("tsweak", "-ext eku:critical=ts"); gencert("tsdisabled", "-ext eku:critical=ts"); diff --git a/test/jdk/sun/security/tools/jarsigner/TsacertOptionTest.java b/test/jdk/sun/security/tools/jarsigner/TsacertOptionTest.java index 06b5ed082f515..3655c01b5b0b9 100644 --- a/test/jdk/sun/security/tools/jarsigner/TsacertOptionTest.java +++ b/test/jdk/sun/security/tools/jarsigner/TsacertOptionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -103,6 +103,7 @@ void start() throws Throwable { "-alias", CA_KEY_ALIAS, "-keystore", KEYSTORE, "-storepass", PASSWORD, + "-startdate", "-1M", "-keypass", PASSWORD, "-validity", Integer.toString(VALIDITY), "-infile", "certreq", diff --git a/test/jdk/sun/security/tools/jarsigner/VerifyJarEntryName.java b/test/jdk/sun/security/tools/jarsigner/VerifyJarEntryName.java new file mode 100644 index 0000000000000..e2554ee0f9166 --- /dev/null +++ b/test/jdk/sun/security/tools/jarsigner/VerifyJarEntryName.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8339280 + * @summary Test that jarsigner -verify emits a warning when the filename of + * an entry in the LOC is changed + * @library /test/lib + * @run junit VerifyJarEntryName + */ + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.FileOutputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.jar.JarFile; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import jdk.test.lib.SecurityTools; +import static org.junit.jupiter.api.Assertions.fail; + +public class VerifyJarEntryName { + + private static final Path ORIGINAL_JAR = Path.of("test.jar"); + private static final Path MODIFIED_JAR = Path.of("modified_test.jar"); + + @BeforeAll + static void setup() throws Exception { + try (FileOutputStream fos = new FileOutputStream(ORIGINAL_JAR.toFile()); + ZipOutputStream zos = new ZipOutputStream(fos)) { + zos.putNextEntry(new ZipEntry(JarFile.MANIFEST_NAME)); + zos.write("Manifest-Version: 1.0\nCreated-By: Test\n". + getBytes(StandardCharsets.UTF_8)); + zos.closeEntry(); + + // Add hello.txt file + ZipEntry textEntry = new ZipEntry("hello.txt"); + zos.putNextEntry(textEntry); + zos.write("hello".getBytes(StandardCharsets.UTF_8)); + zos.closeEntry(); + } + + SecurityTools.keytool("-genkeypair -keystore ks -storepass changeit " + + "-alias mykey -keyalg rsa -dname CN=me "); + + SecurityTools.jarsigner("-keystore ks -storepass changeit " + + ORIGINAL_JAR + " mykey") + .shouldHaveExitValue(0); + } + + @BeforeEach + void cleanup() throws Exception { + Files.deleteIfExists(MODIFIED_JAR); + } + + /* + * Modify a single byte in "MANIFEST.MF" filename in LOC, and + * validate that jarsigner -verify emits a warning message. + */ + @Test + void verifyManifestEntryName() throws Exception { + modifyJarEntryName(ORIGINAL_JAR, MODIFIED_JAR, "META-INF/MANIFEST.MF"); + SecurityTools.jarsigner("-verify -verbose " + MODIFIED_JAR) + .shouldContain("This JAR file contains internal " + + "inconsistencies that may result in different " + + "contents when reading via JarFile and JarInputStream:") + .shouldContain("- Manifest is missing when " + + "reading via JarInputStream") + .shouldHaveExitValue(0); + } + + /* + * Modify a single byte in signature filename in LOC, and + * validate that jarsigner -verify emits a warning message. + */ + @Test + void verifySignatureEntryName() throws Exception { + modifyJarEntryName(ORIGINAL_JAR, MODIFIED_JAR, "META-INF/MYKEY.SF"); + SecurityTools.jarsigner("-verify -verbose " + MODIFIED_JAR) + .shouldContain("This JAR file contains internal " + + "inconsistencies that may result in different " + + "contents when reading via JarFile and JarInputStream:") + .shouldContain("- Entry XETA-INF/MYKEY.SF is present when reading " + + "via JarInputStream but missing when reading via JarFile") + .shouldHaveExitValue(0); + } + + /* + * Validate that jarsigner -verify on a valid JAR works without + * emitting warnings about internal inconsistencies. + */ + @Test + void verifyOriginalJar() throws Exception { + SecurityTools.jarsigner("-verify -verbose " + ORIGINAL_JAR) + .shouldNotContain("This JAR file contains internal " + + "inconsistencies that may result in different contents when " + + "reading via JarFile and JarInputStream:") + .shouldHaveExitValue(0); + } + + private void modifyJarEntryName(Path origJar, Path modifiedJar, + String entryName) throws Exception { + byte[] jarBytes = Files.readAllBytes(origJar); + byte[] entryNameBytes = entryName.getBytes(StandardCharsets.UTF_8); + int pos = 0; + try { + while (!Arrays.equals(jarBytes, pos, pos + entryNameBytes.length, + entryNameBytes, 0, entryNameBytes.length)) pos++; + } catch (ArrayIndexOutOfBoundsException ignore) { + fail(entryName + " is not present in the JAR"); + } + jarBytes[pos] = 'X'; + Files.write(modifiedJar, jarBytes); + } +} diff --git a/test/jdk/sun/security/tools/jarsigner/compatibility/Compatibility.java b/test/jdk/sun/security/tools/jarsigner/compatibility/Compatibility.java index 82ace9c755f4b..56ff670cd263f 100644 --- a/test/jdk/sun/security/tools/jarsigner/compatibility/Compatibility.java +++ b/test/jdk/sun/security/tools/jarsigner/compatibility/Compatibility.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -67,6 +67,7 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; +import jdk.security.jarsigner.JarSigner; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.util.JarUtils; @@ -1430,7 +1431,9 @@ private SignItem digestAlgorithm(String digestAlgorithm) { String expectedDigestAlg() { return digestAlgorithm != null ? digestAlgorithm - : jdkInfo.majorVersion >= 20 ? "SHA-384" : "SHA-256"; + : jdkInfo.majorVersion >= 20 + ? JarSigner.Builder.getDefaultDigestAlgorithm() + : "SHA-256"; } private SignItem tsaDigestAlgorithm(String tsaDigestAlgorithm) { @@ -1439,7 +1442,11 @@ private SignItem tsaDigestAlgorithm(String tsaDigestAlgorithm) { } String expectedTsaDigestAlg() { - return tsaDigestAlgorithm != null ? tsaDigestAlgorithm : "SHA-256"; + return tsaDigestAlgorithm != null + ? tsaDigestAlgorithm + : jdkInfo.majorVersion >= 20 + ? JarSigner.Builder.getDefaultDigestAlgorithm() + : "SHA-256"; } private SignItem tsaIndex(int tsaIndex) { diff --git a/test/jdk/sun/security/tools/jarsigner/multiRelease/MVJarSigningTest.java b/test/jdk/sun/security/tools/jarsigner/multiRelease/MVJarSigningTest.java index ccc4b4051470d..9c9a8590ac74c 100644 --- a/test/jdk/sun/security/tools/jarsigner/multiRelease/MVJarSigningTest.java +++ b/test/jdk/sun/security/tools/jarsigner/multiRelease/MVJarSigningTest.java @@ -120,7 +120,7 @@ public static void main(String[] args) throws Throwable { "-Djava.security.policy=" + TEST_SRC + File.separator + POLICY_FILE, "version.Main"}; - ProcessTools.executeTestJvm(cmd) + ProcessTools.executeTestJava(cmd) .shouldHaveExitValue(0) .shouldContain(VERSION_MESSAGE); } diff --git a/test/jdk/sun/security/tools/keytool/ExtOptionCamelCase.java b/test/jdk/sun/security/tools/keytool/ExtOptionCamelCase.java index c14b019d798d8..5e3c7bd80c765 100644 --- a/test/jdk/sun/security/tools/keytool/ExtOptionCamelCase.java +++ b/test/jdk/sun/security/tools/keytool/ExtOptionCamelCase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ */ import sun.security.tools.keytool.Main; +import sun.security.util.DerOutputStream; import sun.security.util.DerValue; import sun.security.x509.BasicConstraintsExtension; import sun.security.x509.CertificateExtensions; @@ -41,7 +42,6 @@ import sun.security.x509.KeyIdentifier; import sun.security.x509.KeyUsageExtension; -import java.io.ByteArrayOutputStream; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -203,9 +203,9 @@ static void testCreate(String option, Class clazz, // ATTENTION: the extensions created above might contain raw // extensions (not of a subtype) and we need to store and reload // it to resolve them to subtypes. - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - exts.encode(bout); - exts = new CertificateExtensions(new DerValue(bout.toByteArray()).data); + DerOutputStream dout = new DerOutputStream(); + exts.encode(dout); + exts = new CertificateExtensions(new DerValue(dout.toByteArray()).data); if (clazz == null) { throw new Exception("Should fail"); diff --git a/test/jdk/sun/security/tools/keytool/KeyToolTest.java b/test/jdk/sun/security/tools/keytool/KeyToolTest.java index 4d3563978fe18..4e03e73629ee9 100644 --- a/test/jdk/sun/security/tools/keytool/KeyToolTest.java +++ b/test/jdk/sun/security/tools/keytool/KeyToolTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @library /java/security/testlibrary + * @library /test/lib * @bug 6251120 8231950 8242151 * @summary Testing keytool * @@ -68,10 +68,10 @@ import java.util.*; import java.security.cert.X509Certificate; import jdk.test.lib.util.FileUtils; +import jdk.test.lib.security.HumanInputStream; import jdk.test.lib.security.SecurityUtils; import sun.security.util.ObjectIdentifier; - public class KeyToolTest { // The stdout and stderr outputs after a keytool run diff --git a/test/jdk/sun/security/tools/keytool/NssTest.java b/test/jdk/sun/security/tools/keytool/NssTest.java index 1ed2d4c3f391c..6f82da4b4c8a4 100644 --- a/test/jdk/sun/security/tools/keytool/NssTest.java +++ b/test/jdk/sun/security/tools/keytool/NssTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,7 @@ /* * @test * @summary It tests (almost) all keytool behaviors with NSS. - * @library /test/lib /test/jdk/sun/security/pkcs11 /java/security/testlibrary + * @library /test/lib /test/jdk/sun/security/pkcs11 * @modules java.base/sun.security.tools.keytool * java.base/sun.security.util * java.base/sun.security.x509 diff --git a/test/jdk/sun/security/util/Resources/TEST.properties b/test/jdk/sun/security/util/Resources/TEST.properties deleted file mode 100644 index 908b451f8ea69..0000000000000 --- a/test/jdk/sun/security/util/Resources/TEST.properties +++ /dev/null @@ -1,2 +0,0 @@ -# disabled till JDK-8219408 is fixed -allowSmartActionArgs=false diff --git a/test/jdk/sun/security/util/Resources/early/EarlyResources.java b/test/jdk/sun/security/util/Resources/early/EarlyResources.java index 12da50e311f9e..3ee0a9f576c62 100644 --- a/test/jdk/sun/security/util/Resources/early/EarlyResources.java +++ b/test/jdk/sun/security/util/Resources/early/EarlyResources.java @@ -42,7 +42,7 @@ public static void main(String[] args) throws Exception { String fs = File.separator; String policyPath = testSrc + fs + "malformed.policy"; - OutputAnalyzer out = ProcessTools.executeTestJvm( + OutputAnalyzer out = ProcessTools.executeTestJava( "-Djava.security.manager", "-Djava.security.policy=" + policyPath, "EarlyResources$TestMain"); diff --git a/test/jdk/sun/security/util/asn1StringTypes/StringTypes.java b/test/jdk/sun/security/util/asn1StringTypes/StringTypes.java index be067a89fdb1a..ed74f89a0ca5e 100644 --- a/test/jdk/sun/security/util/asn1StringTypes/StringTypes.java +++ b/test/jdk/sun/security/util/asn1StringTypes/StringTypes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2001, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -63,7 +63,7 @@ public static void main(String[] args) throws Exception { derOut.putT61String(s); derOut.putBMPString(s); - derOut.derEncode(fout); + fout.write(derOut.toByteArray()); fout.close(); FileInputStream fis = new FileInputStream(fileName); diff --git a/test/jdk/sun/security/x509/URICertStore/AIACertTimeout.java b/test/jdk/sun/security/x509/URICertStore/AIACertTimeout.java index 487f5e2471ce4..cabb225bf1cc6 100644 --- a/test/jdk/sun/security/x509/URICertStore/AIACertTimeout.java +++ b/test/jdk/sun/security/x509/URICertStore/AIACertTimeout.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,8 +27,7 @@ * @summary Enhance OCSP, CRL and Certificate Fetch Timeouts * @modules java.base/sun.security.x509 * java.base/sun.security.util - * @library /test/lib ../../../../java/security/testlibrary - * @build CertificateBuilder + * @library /test/lib * @run main/othervm -Dcom.sun.security.enableAIAcaIssuers=true * -Dcom.sun.security.cert.readtimeout=1 AIACertTimeout 5000 false * @run main/othervm -Dcom.sun.security.enableAIAcaIssuers=true @@ -55,10 +54,7 @@ import java.util.*; import java.util.concurrent.TimeUnit; -import sun.security.testlibrary.CertificateBuilder; - -import sun.security.x509.*; -import sun.security.util.*; +import jdk.test.lib.security.CertificateBuilder; public class AIACertTimeout { diff --git a/test/jdk/sun/security/x509/X509CertImpl/V3Certificate.java b/test/jdk/sun/security/x509/X509CertImpl/V3Certificate.java index 95741c9f54fa2..feb2ed5aadd32 100644 --- a/test/jdk/sun/security/x509/X509CertImpl/V3Certificate.java +++ b/test/jdk/sun/security/x509/X509CertImpl/V3Certificate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 8049237 8242151 + * @bug 8049237 8242151 8347841 * @modules java.base/sun.security.x509 * java.base/sun.security.util * jdk.crypto.ec @@ -113,7 +113,7 @@ public static boolean test(String algorithm, String sigAlg, int keyLength) // Validity interval Date firstDate = new Date(); - Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("PST")); + Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("America/Los_Angeles")); cal.set(2014, 03, 10, 12, 30, 30); Date lastDate = cal.getTime(); CertificateValidity interval = new CertificateValidity(firstDate, diff --git a/test/jdk/sun/tools/jcmd/JcmdBase.java b/test/jdk/sun/tools/jcmd/JcmdBase.java index b0315673133a3..9a0f97f5bf495 100644 --- a/test/jdk/sun/tools/jcmd/JcmdBase.java +++ b/test/jdk/sun/tools/jcmd/JcmdBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -102,6 +102,8 @@ private static final OutputAnalyzer jcmd(boolean requestToCurrentProcess, launcher.addVMArg(vmArg); } } + // Some command output may be lengthy, disable streaming output to avoid deadlocks + launcher.addVMArg("-Djdk.attach.allowStreamingOutput=false"); if (requestToCurrentProcess) { launcher.addToolArg(Long.toString(ProcessTools.getProcessId())); } diff --git a/test/jdk/sun/tools/jcmd/TestProcessHelper.java b/test/jdk/sun/tools/jcmd/TestProcessHelper.java index a9053cf5fba98..0318ad090392f 100644 --- a/test/jdk/sun/tools/jcmd/TestProcessHelper.java +++ b/test/jdk/sun/tools/jcmd/TestProcessHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,10 +41,10 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import jdk.internal.module.ModuleInfoWriter; import jdk.test.lib.JDKToolFinder; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.util.JarUtils; +import jdk.test.lib.util.ModuleInfoWriter; /* * @test @@ -54,10 +54,14 @@ * * @requires vm.flagless * @requires os.family == "linux" - * @library /test/lib * @modules jdk.jcmd/sun.tools.common:+open + * java.base/jdk.internal.org.objectweb.asm + * java.base/jdk.internal.org.objectweb.asm.commons * java.base/jdk.internal.module + * @library /test/lib * @build test.TestProcess + * jdk.test.lib.util.JarUtils + * jdk.test.lib.util.ModuleInfoWriter * @run main/othervm TestProcessHelper */ public class TestProcessHelper { diff --git a/test/jdk/sun/tools/jstat/jstatGcCapacityOutput1.sh b/test/jdk/sun/tools/jstat/jstatGcCapacityOutput1.sh index ccc95be281f88..c1908855ea765 100644 --- a/test/jdk/sun/tools/jstat/jstatGcCapacityOutput1.sh +++ b/test/jdk/sun/tools/jstat/jstatGcCapacityOutput1.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2004, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -34,4 +34,9 @@ verify_os JSTAT="${TESTJAVA}/bin/jstat" ${JSTAT} ${COMMON_JSTAT_FLAGS} -gccapacity 0 2>&1 | awk -f ${TESTSRC}/gcCapacityOutput1.awk +RC=$? +if [ $RC -ne 0 ]; then + exit $RC +fi + ${JSTAT} ${COMMON_JSTAT_FLAGS} -J-XX:+UseParallelGC -gccapacity 0 2>&1 | awk -f ${TESTSRC}/gcCapacityOutput1.awk diff --git a/test/jdk/sun/tools/jstat/jstatGcCauseOutput1.sh b/test/jdk/sun/tools/jstat/jstatGcCauseOutput1.sh index c34a1881673e9..8652a25fdb7ad 100644 --- a/test/jdk/sun/tools/jstat/jstatGcCauseOutput1.sh +++ b/test/jdk/sun/tools/jstat/jstatGcCauseOutput1.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2004, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -38,4 +38,9 @@ JSTAT="${TESTJAVA}/bin/jstat" # The UseParallelGC collector does not currently update the gc cause counters. ${JSTAT} ${COMMON_JSTAT_FLAGS} -gccause 0 2>&1 | awk -f ${TESTSRC}/gcCauseOutput1.awk +RC=$? +if [ $RC -ne 0 ]; then + exit $RC +fi + ${JSTAT} ${COMMON_JSTAT_FLAGS} -J-XX:+UseSerialGC -gccause 0 2>&1 | awk -f ${TESTSRC}/gcCauseOutput1.awk diff --git a/test/jdk/sun/tools/jstat/jstatGcMetaCapacityOutput1.sh b/test/jdk/sun/tools/jstat/jstatGcMetaCapacityOutput1.sh index 66446517c507e..b16d0e38d02c9 100644 --- a/test/jdk/sun/tools/jstat/jstatGcMetaCapacityOutput1.sh +++ b/test/jdk/sun/tools/jstat/jstatGcMetaCapacityOutput1.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -34,4 +34,9 @@ verify_os JSTAT="${TESTJAVA}/bin/jstat" ${JSTAT} ${COMMON_JSTAT_FLAGS} -gcmetacapacity 0 2>&1 | awk -f ${TESTSRC}/gcMetaCapacityOutput1.awk +RC=$? +if [ $RC -ne 0 ]; then + exit $RC +fi + ${JSTAT} ${COMMON_JSTAT_FLAGS} -J-XX:+UseParallelGC -gcmetacapacity 0 2>&1 | awk -f ${TESTSRC}/gcMetaCapacityOutput1.awk diff --git a/test/jdk/sun/tools/jstat/jstatGcNewCapacityOutput1.sh b/test/jdk/sun/tools/jstat/jstatGcNewCapacityOutput1.sh index 44aa864b47508..64ce2efd455e1 100644 --- a/test/jdk/sun/tools/jstat/jstatGcNewCapacityOutput1.sh +++ b/test/jdk/sun/tools/jstat/jstatGcNewCapacityOutput1.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2004, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -34,4 +34,9 @@ verify_os JSTAT="${TESTJAVA}/bin/jstat" ${JSTAT} ${COMMON_JSTAT_FLAGS} -gcnewcapacity 0 2>&1 | awk -f ${TESTSRC}/gcNewCapacityOutput1.awk +RC=$? +if [ $RC -ne 0 ]; then + exit $RC +fi + ${JSTAT} ${COMMON_JSTAT_FLAGS} -J-XX:+UseParallelGC -gcnewcapacity 0 2>&1 | awk -f ${TESTSRC}/gcNewCapacityOutput1.awk diff --git a/test/jdk/sun/tools/jstat/jstatGcNewOutput1.sh b/test/jdk/sun/tools/jstat/jstatGcNewOutput1.sh index 0f909f18a9ef8..b15ec02d2b0a9 100644 --- a/test/jdk/sun/tools/jstat/jstatGcNewOutput1.sh +++ b/test/jdk/sun/tools/jstat/jstatGcNewOutput1.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2004, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -34,4 +34,9 @@ verify_os JSTAT="${TESTJAVA}/bin/jstat" ${JSTAT} ${COMMON_JSTAT_FLAGS} -gcnew 0 2>&1 | awk -f ${TESTSRC}/gcNewOutput1.awk +RC=$? +if [ $RC -ne 0 ]; then + exit $RC +fi + ${JSTAT} ${COMMON_JSTAT_FLAGS} -J-XX:+UseParallelGC -gcnew 0 2>&1 | awk -f ${TESTSRC}/gcNewOutput1.awk diff --git a/test/jdk/sun/tools/jstat/jstatGcOldCapacityOutput1.sh b/test/jdk/sun/tools/jstat/jstatGcOldCapacityOutput1.sh index 433072fddfa4b..1c13d6f916d31 100644 --- a/test/jdk/sun/tools/jstat/jstatGcOldCapacityOutput1.sh +++ b/test/jdk/sun/tools/jstat/jstatGcOldCapacityOutput1.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2004, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -34,4 +34,9 @@ verify_os JSTAT="${TESTJAVA}/bin/jstat" ${JSTAT} ${COMMON_JSTAT_FLAGS} -gcoldcapacity 0 2>&1 | awk -f ${TESTSRC}/gcOldCapacityOutput1.awk +RC=$? +if [ $RC -ne 0 ]; then + exit $RC +fi + ${JSTAT} ${COMMON_JSTAT_FLAGS} -J-XX:+UseParallelGC -gcoldcapacity 0 2>&1 | awk -f ${TESTSRC}/gcOldCapacityOutput1.awk diff --git a/test/jdk/sun/tools/jstat/jstatGcOldOutput1.sh b/test/jdk/sun/tools/jstat/jstatGcOldOutput1.sh index ee9e30296e0d5..7f505228b12a0 100644 --- a/test/jdk/sun/tools/jstat/jstatGcOldOutput1.sh +++ b/test/jdk/sun/tools/jstat/jstatGcOldOutput1.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2004, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -34,4 +34,9 @@ verify_os JSTAT="${TESTJAVA}/bin/jstat" ${JSTAT} ${COMMON_JSTAT_FLAGS} -gcold 0 2>&1 | awk -f ${TESTSRC}/gcOldOutput1.awk +RC=$? +if [ $RC -ne 0 ]; then + exit $RC +fi + ${JSTAT} ${COMMON_JSTAT_FLAGS} -J-XX:+UseParallelGC -gcold 0 2>&1 | awk -f ${TESTSRC}/gcOldOutput1.awk diff --git a/test/jdk/sun/tools/jstat/jstatGcOutput1.sh b/test/jdk/sun/tools/jstat/jstatGcOutput1.sh index 167c53606ad65..dfffa2d1a5508 100644 --- a/test/jdk/sun/tools/jstat/jstatGcOutput1.sh +++ b/test/jdk/sun/tools/jstat/jstatGcOutput1.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2004, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -34,4 +34,9 @@ verify_os JSTAT="${TESTJAVA}/bin/jstat" ${JSTAT} ${COMMON_JSTAT_FLAGS} -gc 0 2>&1 | awk -f ${TESTSRC}/gcOutput1.awk +RC=$? +if [ $RC -ne 0 ]; then + exit $RC +fi + ${JSTAT} ${COMMON_JSTAT_FLAGS} -J-XX:+UseParallelGC -gc 0 2>&1 | awk -f ${TESTSRC}/gcOutput1.awk diff --git a/test/jdk/sun/tools/jstat/jstatLineCounts1.sh b/test/jdk/sun/tools/jstat/jstatLineCounts1.sh index 75cb051cccf97..97338b8e793d8 100644 --- a/test/jdk/sun/tools/jstat/jstatLineCounts1.sh +++ b/test/jdk/sun/tools/jstat/jstatLineCounts1.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2004, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -34,4 +34,9 @@ verify_os JSTAT="${TESTJAVA}/bin/jstat" ${JSTAT} ${COMMON_JSTAT_FLAGS} -gcutil 0 250 5 2>&1 | awk -f ${TESTSRC}/lineCounts1.awk +RC=$? +if [ $RC -ne 0 ]; then + exit $RC +fi + ${JSTAT} ${COMMON_JSTAT_FLAGS} -J-XX:+UseParallelGC -gcutil 0 250 5 2>&1 | awk -f ${TESTSRC}/lineCounts1.awk diff --git a/test/jdk/sun/tools/jstat/jstatLineCounts2.sh b/test/jdk/sun/tools/jstat/jstatLineCounts2.sh index 5e0cbafe08dc7..eab19f3931e88 100644 --- a/test/jdk/sun/tools/jstat/jstatLineCounts2.sh +++ b/test/jdk/sun/tools/jstat/jstatLineCounts2.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2004, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -34,4 +34,9 @@ verify_os JSTAT="${TESTJAVA}/bin/jstat" ${JSTAT} ${COMMON_JSTAT_FLAGS} -gcutil 0 2>&1 | awk -f ${TESTSRC}/lineCounts2.awk +RC=$? +if [ $RC -ne 0 ]; then + exit $RC +fi + ${JSTAT} ${COMMON_JSTAT_FLAGS} -J-XX:+UseParallelGC -gcutil 0 2>&1 | awk -f ${TESTSRC}/lineCounts2.awk diff --git a/test/jdk/sun/tools/jstat/jstatLineCounts3.sh b/test/jdk/sun/tools/jstat/jstatLineCounts3.sh index ef8bbc8ad7542..9a769a924648a 100644 --- a/test/jdk/sun/tools/jstat/jstatLineCounts3.sh +++ b/test/jdk/sun/tools/jstat/jstatLineCounts3.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2004, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -34,4 +34,9 @@ verify_os JSTAT="${TESTJAVA}/bin/jstat" ${JSTAT} ${COMMON_JSTAT_FLAGS} -gcutil -h 10 0 250 10 2>&1 | awk -f ${TESTSRC}/lineCounts3.awk +RC=$? +if [ $RC -ne 0 ]; then + exit $RC +fi + ${JSTAT} ${COMMON_JSTAT_FLAGS} -J-XX:+UseParallelGC -gcutil -h 10 0 250 10 2>&1 | awk -f ${TESTSRC}/lineCounts3.awk diff --git a/test/jdk/sun/tools/jstat/jstatLineCounts4.sh b/test/jdk/sun/tools/jstat/jstatLineCounts4.sh index 537172c75e36d..817c3b14f6218 100644 --- a/test/jdk/sun/tools/jstat/jstatLineCounts4.sh +++ b/test/jdk/sun/tools/jstat/jstatLineCounts4.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2004, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -34,4 +34,9 @@ verify_os JSTAT="${TESTJAVA}/bin/jstat" ${JSTAT} ${COMMON_JSTAT_FLAGS} -gcutil -h 10 0 250 11 2>&1 | awk -f ${TESTSRC}/lineCounts4.awk +RC=$? +if [ $RC -ne 0 ]; then + exit $RC +fi + ${JSTAT} ${COMMON_JSTAT_FLAGS} -J-XX:+UseParallelGC -gcutil -h 10 0 250 11 2>&1 | awk -f ${TESTSRC}/lineCounts4.awk diff --git a/test/jdk/sun/tools/jstat/lineCounts1.awk b/test/jdk/sun/tools/jstat/lineCounts1.awk index d7a7c1d9fb7c8..1e05506100b6c 100644 --- a/test/jdk/sun/tools/jstat/lineCounts1.awk +++ b/test/jdk/sun/tools/jstat/lineCounts1.awk @@ -29,7 +29,7 @@ BEGIN { headerlines++; } -/^[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*([0-9]+|-)[ ]*([0-9]+\.[0-9]+|-)[ ]*[0-9]+\.[0-9]+$/ { +/^[ ]*([0-9]+\.[0-9]+|-)[ ]*([0-9]+\.[0-9]+|-)[ ]*([0-9]+\.[0-9]+|-)[ ]*([0-9]+\.[0-9]+|-)[ ]*([0-9]+\.[0-9]+|-)[ ]*([0-9]+\.[0-9]+|-)[ ]*[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*([0-9]+|-)[ ]*([0-9]+\.[0-9]+|-)[ ]*[0-9]+\.[0-9]+$/ { datalines++; } diff --git a/test/jdk/sun/tools/jstat/lineCounts2.awk b/test/jdk/sun/tools/jstat/lineCounts2.awk index 201457a38334f..4d838c2103e1a 100644 --- a/test/jdk/sun/tools/jstat/lineCounts2.awk +++ b/test/jdk/sun/tools/jstat/lineCounts2.awk @@ -21,7 +21,7 @@ BEGIN { headerlines++; } -/^[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*([0-9]+|-)[ ]*([0-9]+\.[0-9]+|-)[ ]*[0-9]+\.[0-9]+$/ { +/^[ ]*([0-9]+\.[0-9]+|-)[ ]*([0-9]+\.[0-9]+|-)[ ]*([0-9]+\.[0-9]+|-)[ ]*([0-9]+\.[0-9]+|-)[ ]*([0-9]+\.[0-9]+|-)[ ]*([0-9]+\.[0-9]+|-)[ ]*[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*([0-9]+|-)[ ]*([0-9]+\.[0-9]+|-)[ ]*[0-9]+\.[0-9]+$/ { datalines++; } diff --git a/test/jdk/sun/tools/jstat/lineCounts3.awk b/test/jdk/sun/tools/jstat/lineCounts3.awk index 1f155a99ed5bf..79ae969a47dba 100644 --- a/test/jdk/sun/tools/jstat/lineCounts3.awk +++ b/test/jdk/sun/tools/jstat/lineCounts3.awk @@ -39,7 +39,7 @@ BEGIN { headerlines++; } -/^[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*([0-9]+|-)[ ]*([0-9]+\.[0-9]+|-)[ ]*[0-9]+\.[0-9]+$/ { +/^[ ]*([0-9]+\.[0-9]+|-)[ ]*([0-9]+\.[0-9]+|-)[ ]*([0-9]+\.[0-9]+|-)[ ]*([0-9]+\.[0-9]+|-)[ ]*([0-9]+\.[0-9]+|-)[ ]*([0-9]+\.[0-9]+|-)[ ]*[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*([0-9]+|-)[ ]*([0-9]+\.[0-9]+|-)[ ]*[0-9]+\.[0-9]+$/ { datalines++; } diff --git a/test/jdk/sun/tools/jstat/lineCounts4.awk b/test/jdk/sun/tools/jstat/lineCounts4.awk index 62aea881e7120..7ceb56b9f27fa 100644 --- a/test/jdk/sun/tools/jstat/lineCounts4.awk +++ b/test/jdk/sun/tools/jstat/lineCounts4.awk @@ -44,7 +44,7 @@ BEGIN { headerlines++; } -/^[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*([0-9]+|-)[ ]*([0-9]+\.[0-9]+|-)[ ]*[0-9]+\.[0-9]+$/ { +/^[ ]*([0-9]+\.[0-9]+|-)[ ]*([0-9]+\.[0-9]+|-)[ ]*([0-9]+\.[0-9]+|-)[ ]*([0-9]+\.[0-9]+|-)[ ]*([0-9]+\.[0-9]+|-)[ ]*([0-9]+\.[0-9]+|-)[ ]*[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*[0-9]+[ ]*[0-9]+\.[0-9]+[ ]*([0-9]+|-)[ ]*([0-9]+\.[0-9]+|-)[ ]*[0-9]+\.[0-9]+$/ { if (headerlines == 2) { datalines2++; } diff --git a/test/jdk/sun/util/calendar/zi/TestZoneInfo310.java b/test/jdk/sun/util/calendar/zi/TestZoneInfo310.java index aaced7bbf5860..cf52fa3907e31 100644 --- a/test/jdk/sun/util/calendar/zi/TestZoneInfo310.java +++ b/test/jdk/sun/util/calendar/zi/TestZoneInfo310.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 8007572 8008161 8157792 8212970 8224560 + * @bug 8007572 8008161 8157792 8212970 8224560 8324065 * @summary Test whether the TimeZone generated from JSR310 tzdb is the same * as the one from the tz data from javazic * @modules java.base/sun.util.calendar:+open @@ -173,9 +173,9 @@ public static void main(String[] args) throws Throwable { ZoneInfoOld zi = toZoneInfoOld(TimeZone.getTimeZone(zid)); ZoneInfoOld ziOLD = (ZoneInfoOld)ZoneInfoOld.getTimeZone(zid); /* - * Temporary ignoring the failing TimeZones which are having zone - * rules defined till year 2037 and/or above and have negative DST - * save time in IANA tzdata. This bug is tracked via JDK-8223388. + * Ignoring the failing TimeZones which have negative DST + * save time in IANA tzdata, as javazic/ZoneInfoOld cannot + * handle the negative DST. * * These are the zones/rules that employ negative DST in vanguard * format (as of 2019a), Palestine added in 2022d: @@ -185,11 +185,6 @@ public static void main(String[] args) throws Throwable { * - Rule "Namibia" * - Rule "Palestine" * - Zone "Europe/Prague" - * - * Tehran/Iran rule has rules beyond 2037, in which javazic assumes - * to be the last year. Thus javazic's rule is based on year 2037 - * (Mar 20th/Sep 20th are the cutover dates), while the real rule - * has year 2087 where Mar 21st/Sep 21st are the cutover dates. */ if (zid.equals("Africa/Casablanca") || // uses "Morocco" rule zid.equals("Africa/El_Aaiun") || // uses "Morocco" rule @@ -198,10 +193,8 @@ public static void main(String[] args) throws Throwable { zid.equals("Europe/Bratislava") || // link to "Europe/Prague" zid.equals("Europe/Dublin") || // uses "Eire" rule zid.equals("Europe/Prague") || - zid.equals("Asia/Tehran") || // last rule mismatch zid.equals("Asia/Gaza") || // uses "Palestine" rule - zid.equals("Asia/Hebron") || // uses "Palestine" rule - zid.equals("Iran")) { // last rule mismatch + zid.equals("Asia/Hebron")) { // uses "Palestine" rule continue; } if (! zi.equalsTo(ziOLD)) { diff --git a/test/jdk/sun/util/calendar/zi/ZoneInfoOld.java b/test/jdk/sun/util/calendar/zi/ZoneInfoOld.java index 45e8a34694685..40f9cb9056cc9 100644 --- a/test/jdk/sun/util/calendar/zi/ZoneInfoOld.java +++ b/test/jdk/sun/util/calendar/zi/ZoneInfoOld.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,14 +51,14 @@ * for the {@link #getOffset(int,int,int,int,int,int) getOffset} * method that takes Gregorian calendar date fields. *

    - * This table covers transitions from 1900 until 2037 (as of version - * 1.4), Before 1900, it assumes that there was no daylight saving + * This table covers transitions from 1900 until 2100 (as of version + * 23), Before 1900, it assumes that there was no daylight saving * time and the getOffset methods always return the * {@link #getRawOffset} value. No Local Mean Time is supported. If a * specified date is beyond the transition table and this time zone is - * supposed to observe daylight saving time in 2037, it delegates + * supposed to observe daylight saving time in 2100, it delegates * operations to a {@link java.util.SimpleTimeZone SimpleTimeZone} - * object created using the daylight saving time schedule as of 2037. + * object created using the daylight saving time schedule as of 2100. *

    * The date items, transitions, GMT offset(s), etc. are read from a database * file. See {@link ZoneInfoFile} for details. diff --git a/test/jdk/sun/util/calendar/zi/Zoneinfo.java b/test/jdk/sun/util/calendar/zi/Zoneinfo.java index a68aa7826b0f6..e125ad2cb87d1 100644 --- a/test/jdk/sun/util/calendar/zi/Zoneinfo.java +++ b/test/jdk/sun/util/calendar/zi/Zoneinfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,7 +37,7 @@ class Zoneinfo { private static final int minYear = 1900; - private static final int maxYear = 2037; + private static final int maxYear = 2100; private static final long minTime = Time.getLocalTime(minYear, Month.JANUARY, 1, 0); private static int startYear = minYear; private static int endYear = maxYear; diff --git a/test/jdk/sun/util/logging/SourceClassName.java b/test/jdk/sun/util/logging/SourceClassName.java index f3023122618b9..3dbc46f3d1189 100644 --- a/test/jdk/sun/util/logging/SourceClassName.java +++ b/test/jdk/sun/util/logging/SourceClassName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,17 +33,21 @@ * @run main/othervm SourceClassName */ +import java.util.Locale; import java.util.logging.*; import java.io.*; import sun.util.logging.PlatformLogger; public class SourceClassName { public static void main(String[] args) throws Exception { + Locale savedLocale = Locale.getDefault(); + Locale.setDefault(Locale.US); File dir = new File(System.getProperty("user.dir", ".")); File log = new File(dir, "testlog.txt"); PrintStream logps = new PrintStream(log); writeLogRecords(logps); checkLogRecords(log); + Locale.setDefault(savedLocale); } private static void writeLogRecords(PrintStream logps) throws Exception { diff --git a/test/jdk/sun/util/resources/TimeZone/Bug4640234.java b/test/jdk/sun/util/resources/TimeZone/Bug4640234.java index 5726bedfdb69b..61e5955a6b419 100644 --- a/test/jdk/sun/util/resources/TimeZone/Bug4640234.java +++ b/test/jdk/sun/util/resources/TimeZone/Bug4640234.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /** * @test - * @bug 4640234 4946057 4938151 4873691 5023181 + * @bug 4640234 4946057 4938151 4873691 5023181 8347841 * @summary Verifies the translation of time zone names, this test will catch * presence of country name for english and selected locales for all * ISO country codes. @@ -42,6 +42,8 @@ import java.text.MessageFormat; import java.text.SimpleDateFormat; +import java.time.ZoneId; +import java.util.Arrays; import java.util.Date; import java.util.Locale; import java.util.Enumeration; @@ -49,6 +51,7 @@ import java.util.Map; import java.util.ResourceBundle; import java.util.TimeZone; +import java.util.function.Predicate; import sun.util.resources.LocaleData; @@ -82,7 +85,9 @@ public static void main(String[] args) throws Exception { StringBuffer errors = new StringBuffer(""); StringBuffer warnings = new StringBuffer(""); - String[] timezones = TimeZone.getAvailableIDs(); + String[] timezones = Arrays.stream(TimeZone.getAvailableIDs()) + .filter(Predicate.not(ZoneId.SHORT_IDS::containsKey)) + .toArray(String[]::new); String[] countries = locEn.getISOCountries(); String[] languages = locEn.getISOLanguages(); diff --git a/test/jdk/sun/util/resources/cldr/Bug8134384.java b/test/jdk/sun/util/resources/cldr/Bug8134384.java index eac8238bdd28a..f27b587ca9838 100644 --- a/test/jdk/sun/util/resources/cldr/Bug8134384.java +++ b/test/jdk/sun/util/resources/cldr/Bug8134384.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 8134384 8234347 8236548 + * @bug 8134384 8234347 8236548 8347841 * @summary Tests CLDR TimeZoneNames has English names for all tzids * @run main/othervm -Djava.locale.providers=CLDR Bug8134384 */ @@ -38,6 +38,9 @@ public static void main(String [] args) { try { for (String tz : TimeZone.getAvailableIDs() ) { + if (ZoneId.SHORT_IDS.containsKey(tz)) { + continue; + } TimeZone.setDefault(TimeZone.getTimeZone(tz)); // Summer solstice String date1 = Date.from(Instant.parse("2015-06-21T00:00:00.00Z")).toString(); diff --git a/test/jdk/sun/util/resources/cldr/Bug8202764.java b/test/jdk/sun/util/resources/cldr/Bug8202764.java index f2c614b6df0be..6f3e40e620121 100644 --- a/test/jdk/sun/util/resources/cldr/Bug8202764.java +++ b/test/jdk/sun/util/resources/cldr/Bug8202764.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 8202764 + * @bug 8202764 8347841 * @modules jdk.localedata * @summary Checks time zone names are consistent with aliased ids, * between DateFormatSymbols.getZoneStrings() and getDisplayName() @@ -49,6 +49,7 @@ public class Bug8202764 { public void testAliasedTZs() { Set zoneIds = ZoneId.getAvailableZoneIds(); Arrays.stream(DateFormatSymbols.getInstance(Locale.US).getZoneStrings()) + .filter(zone -> !ZoneId.SHORT_IDS.containsKey(zone[0])) .forEach(zone -> { System.out.println(zone[0]); TimeZone tz = TimeZone.getTimeZone(zone[0]); diff --git a/test/jdk/tools/jimage/JImageToolTest.java b/test/jdk/tools/jimage/JImageToolTest.java index feb6a56a968c3..d7d1ee35ba1e0 100644 --- a/test/jdk/tools/jimage/JImageToolTest.java +++ b/test/jdk/tools/jimage/JImageToolTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,7 @@ /* * @test * @library /test/lib + * @requires vm.flagless * @build jdk.test.lib.process.ProcessTools * @summary Test to check if jimage tool exists and is working * @run main/timeout=360 JImageToolTest @@ -42,7 +43,7 @@ public class JImageToolTest { private static void jimage(String... jimageArgs) throws Exception { ArrayList args = new ArrayList<>(); - args.add("-ms64m"); + args.add("-Xms64m"); args.add("jdk.tools.jimage.Main"); args.addAll(Arrays.asList(jimageArgs)); diff --git a/test/jdk/tools/jlink/JLinkNegativeTest.java b/test/jdk/tools/jlink/JLinkNegativeTest.java index 6c7a50e6c4306..87184af58d082 100644 --- a/test/jdk/tools/jlink/JLinkNegativeTest.java +++ b/test/jdk/tools/jlink/JLinkNegativeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,15 +28,17 @@ * @bug 8174718 * @bug 8189671 * @author Andrei Eremeev - * @library ../lib - * @modules java.base/jdk.internal.jimage + * @modules java.base/jdk.internal.org.objectweb.asm + * java.base/jdk.internal.org.objectweb.asm.commons + * java.base/jdk.internal.jimage * java.base/jdk.internal.module * jdk.jdeps/com.sun.tools.classfile * jdk.jlink/jdk.tools.jlink.internal * jdk.jlink/jdk.tools.jmod * jdk.jlink/jdk.tools.jimage * jdk.compiler - * @build tests.* + * @library /test/lib ../lib + * @build tests.* jdk.test.lib.util.ModuleInfoWriter * @run testng JLinkNegativeTest */ @@ -48,17 +50,15 @@ import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; -import jdk.internal.module.ModuleInfoWriter; +import jdk.test.lib.util.ModuleInfoWriter; import org.testng.SkipException; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; diff --git a/test/jdk/tools/jpackage/TEST.properties b/test/jdk/tools/jpackage/TEST.properties index ab013166faa45..e01036f0ed36c 100644 --- a/test/jdk/tools/jpackage/TEST.properties +++ b/test/jdk/tools/jpackage/TEST.properties @@ -10,3 +10,6 @@ maxOutputSize=2000000 # } # but conditionals are not supported by jtreg configuration files. exclusiveAccess.dirs=share windows + +modules=jdk.jpackage/jdk.jpackage.internal:+open \ + java.base/jdk.internal.util diff --git a/test/jdk/tools/jpackage/apps/UseShutdownHook.java b/test/jdk/tools/jpackage/apps/UseShutdownHook.java new file mode 100644 index 0000000000000..c558ee85701d4 --- /dev/null +++ b/test/jdk/tools/jpackage/apps/UseShutdownHook.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; + +public class UseShutdownHook { + + public static void main(String[] args) throws InterruptedException { + trace("Started"); + + var outputFile = Path.of(args[0]); + trace(String.format("Write output in [%s] file", outputFile)); + + var shutdownTimeoutSeconds = Integer.parseInt(args[1]); + trace(String.format("Automatically shutdown the app in %ss", shutdownTimeoutSeconds)); + + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + output(outputFile, "shutdown hook executed"); + } + }); + + var startTime = System.currentTimeMillis(); + var lock = new Object(); + do { + synchronized (lock) { + lock.wait(shutdownTimeoutSeconds * 1000); + } + } while ((System.currentTimeMillis() - startTime) < (shutdownTimeoutSeconds * 1000)); + + output(outputFile, "exit"); + } + + private static void output(Path outputFilePath, String msg) { + + trace(String.format("Writing [%s] into [%s]", msg, outputFilePath)); + + try { + Files.createDirectories(outputFilePath.getParent()); + Files.writeString(outputFilePath, msg, StandardOpenOption.APPEND, StandardOpenOption.CREATE); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + private static void trace(String msg) { + Date time = new Date(System.currentTimeMillis()); + msg = String.format("UseShutdownHook [%s]: %s", SDF.format(time), msg); + System.out.println(msg); + try { + Files.write(traceFile, List.of(msg), StandardOpenOption.APPEND, StandardOpenOption.CREATE); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + private static final SimpleDateFormat SDF = new SimpleDateFormat("HH:mm:ss.SSS"); + + private static final Path traceFile = Path.of(System.getProperty("jpackage.test.trace-file")); +} diff --git a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JavaAppDescTest.java b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JavaAppDescTest.java new file mode 100644 index 0000000000000..a2cde44f00951 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JavaAppDescTest.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import java.nio.file.Path; +import java.util.List; +import java.util.function.UnaryOperator; +import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.Annotations.Parameters; + +public class JavaAppDescTest { + + public JavaAppDescTest(JavaAppDesc expectedAppDesc, JavaAppDesc actualAppDesc) { + this.expectedAppDesc = expectedAppDesc; + this.actualAppDesc = actualAppDesc; + } + + @Test + public void test() { + TKit.assertEquals(expectedAppDesc.toString(), actualAppDesc.toString(), null); + TKit.assertTrue(expectedAppDesc.equals(actualAppDesc), null); + } + + @Test + @Parameter({"Foo", "Foo.class"}) + @Parameter({"com.bar.A", "com/bar/A.class"}) + @Parameter({"module/com.bar.A", "com/bar/A.class"}) + public static void testClassFilePath(String... args) { + var appDesc = args[0]; + var expectedClassFilePath = Path.of(args[1]); + TKit.assertEquals(expectedClassFilePath.toString(), JavaAppDesc.parse( + appDesc).classFilePath().toString(), null); + } + + @Parameters + public static List input() { + return List.of(new Object[][] { + createTestCase("", "hello.jar:Hello"), + createTestCase("foo.jar*", "foo.jar*hello.jar:Hello"), + createTestCase("Bye", "hello.jar:Bye"), + createTestCase("bye.jar:", "bye.jar:Hello"), + createTestCase("duke.jar:com.other/com.other.foo.bar.Buz!@3.7", appDesc -> { + return appDesc + .setBundleFileName("duke.jar") + .setModuleName("com.other") + .setClassName("com.other.foo.bar.Buz") + .setWithMainClass(true) + .setModuleVersion("3.7"); + }), + }); + } + + private static JavaAppDesc[] createTestCase(String inputAppDesc, String expectedAppDescStr) { + return createTestCase(inputAppDesc, appDesc -> { + return stripDefaultSrcJavaPath(JavaAppDesc.parse(expectedAppDescStr)); + }); + } + + private static JavaAppDesc stripDefaultSrcJavaPath(JavaAppDesc appDesc) { + var defaultAppDesc = HelloApp.createDefaltAppDesc(); + if (appDesc.srcJavaPath().equals(defaultAppDesc.srcJavaPath())) { + appDesc.setSrcJavaPath(null); + } + return appDesc; + } + + private static JavaAppDesc[] createTestCase(String appDesc, UnaryOperator config) { + var actualAppDesc = stripDefaultSrcJavaPath(JavaAppDesc.parse(appDesc)); + + var expectedAppDesc = config.apply(stripDefaultSrcJavaPath(HelloApp.createDefaltAppDesc())); + + return new JavaAppDesc[] {expectedAppDesc, actualAppDesc}; + } + + private final JavaAppDesc expectedAppDesc; + private final JavaAppDesc actualAppDesc; +} diff --git a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/TKitTest.java b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/TKitTest.java new file mode 100644 index 0000000000000..3f55c3c50aea9 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/TKitTest.java @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; +import jdk.jpackage.test.Annotations.Parameters; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.Functional.ThrowingRunnable; +import static jdk.jpackage.test.Functional.ThrowingRunnable.toRunnable; +import static jdk.jpackage.test.Functional.ThrowingSupplier.toSupplier; + +public class TKitTest { + + @Parameters + public static Collection assertTestsData() { + List data = new ArrayList<>(); + + var assertFunc = MethodCallConfig.build("assertTrue", boolean.class, String.class); + data.addAll(List.of(assertFunc.args(true).pass().expectLog("assertTrue()").createForMessage("Catbird"))); + data.addAll(List.of(assertFunc.args(false).fail().expectLog("Failed").createForMessage("Catbird"))); + + assertFunc = MethodCallConfig.build("assertFalse", boolean.class, String.class); + data.addAll(List.of(assertFunc.args(false).pass().expectLog("assertFalse()").createForMessage("Stork"))); + data.addAll(List.of(assertFunc.args(true).fail().expectLog("Failed").createForMessage("Stork"))); + + assertFunc = MethodCallConfig.build("assertEquals", String.class, String.class, String.class); + data.addAll(List.of(assertFunc.args("a", "a").pass().expectLog("assertEquals(a)").createForMessage("Crow"))); + data.addAll(List.of(assertFunc.args("a", "b").fail().expectLog("Expected [a]. Actual [b]").createForMessage("Crow"))); + + assertFunc = MethodCallConfig.build("assertEquals", long.class, long.class, String.class); + data.addAll(List.of(assertFunc.args(7, 7).pass().expectLog("assertEquals(7)").createForMessage("Owl"))); + data.addAll(List.of(assertFunc.args(7, 10).fail().expectLog("Expected [7]. Actual [10]").createForMessage("Owl"))); + + assertFunc = MethodCallConfig.build("assertNotEquals", String.class, String.class, String.class); + data.addAll(List.of(assertFunc.args("a", "b").pass().expectLog("assertNotEquals(a, b)").createForMessage("Tit"))); + data.addAll(List.of(assertFunc.args("a", "a").fail().expectLog("Unexpected [a] value").createForMessage("Tit"))); + + assertFunc = MethodCallConfig.build("assertNotEquals", long.class, long.class, String.class); + data.addAll(List.of(assertFunc.args(7, 10).pass().expectLog("assertNotEquals(7, 10)").createForMessage("Duck"))); + data.addAll(List.of(assertFunc.args(7, 7).fail().expectLog("Unexpected [7] value").createForMessage("Duck"))); + + assertFunc = MethodCallConfig.build("assertNull", Object.class, String.class); + data.addAll(List.of(assertFunc.args((Object) null).pass().expectLog("assertNull()").createForMessage("Ibis"))); + data.addAll(List.of(assertFunc.args("v").fail().expectLog("Unexpected not null value [v]").createForMessage("Ibis"))); + + assertFunc = MethodCallConfig.build("assertNotNull", Object.class, String.class); + data.addAll(List.of(assertFunc.args("v").pass().expectLog("assertNotNull(v)").createForMessage("Pigeon"))); + data.addAll(List.of(assertFunc.args((Object) null).fail().expectLog("Unexpected null value").createForMessage("Pigeon"))); + + assertFunc = MethodCallConfig.build("assertStringListEquals", List.class, List.class, String.class); + data.addAll(List.of(assertFunc.args(List.of(), List.of()).pass().expectLog( + "assertStringListEquals()").createForMessage("Gull"))); + + data.addAll(List.of(assertFunc.args(List.of("a", "b"), List.of("a", "b")).pass().expectLog( + "assertStringListEquals()", + "assertStringListEquals(1, a)", + "assertStringListEquals(2, b)").createForMessage("Pelican"))); + + assertFunc.fail().withAutoExpectLogPrefix(false); + for (var msg : new String[] { "Raven", null }) { + data.addAll(List.of(assertFunc.args(List.of("a"), List.of("a", "b"), msg).expectLog( + concatMessages("TRACE: assertStringListEquals()", msg), + "TRACE: assertStringListEquals(1, a)", + concatMessages("ERROR: Actual list is longer than expected by 1 elements", msg) + ).create())); + + data.addAll(List.of(assertFunc.args(List.of("n", "m"), List.of("n"), msg).expectLog( + concatMessages("TRACE: assertStringListEquals()", msg), + "TRACE: assertStringListEquals(1, n)", + concatMessages("ERROR: Actual list is shorter than expected by 1 elements", msg) + ).create())); + + data.addAll(List.of(assertFunc.args(List.of("a", "b"), List.of("n", "m"), msg).expectLog( + concatMessages("TRACE: assertStringListEquals()", msg), + concatMessages("ERROR: (1) Expected [a]. Actual [n]", msg) + ).create())); + } + + return data.stream().map(v -> { + return new Object[]{v}; + }).toList(); + } + + public record MethodCallConfig(Method method, Object[] args, boolean expectFail, String[] expectLog) { + @Override + public String toString() { + return String.format("%s%s%s", method.getName(), Arrays.toString(args), expectFail ? "!" : ""); + } + + static Builder build(String name, Class ... parameterTypes) { + return new Builder(name, parameterTypes); + } + + private static class Builder { + Builder(Method method) { + Objects.requireNonNull(method); + this.method = method; + } + + Builder(String name, Class ... parameterTypes) { + method = toSupplier(() -> TKit.class.getMethod(name, parameterTypes)).get(); + } + + MethodCallConfig create() { + String[] effectiveExpectLog; + if (!withAutoExpectLogPrefix) { + effectiveExpectLog = expectLog; + } else { + var prefix = expectFail ? "ERROR: " : "TRACE: "; + effectiveExpectLog = Stream.of(expectLog).map(line -> { + return prefix + line; + }).toArray(String[]::new); + } + return new MethodCallConfig(method, args, expectFail, effectiveExpectLog); + } + + MethodCallConfig[] createForMessage(String msg) { + return Arrays.asList(msg, null).stream().map(curMsg -> { + var builder = new Builder(method); + builder.expectFail = expectFail; + builder.withAutoExpectLogPrefix = withAutoExpectLogPrefix; + builder.args = Stream.concat(Stream.of(args), Stream.of(curMsg)).toArray(); + builder.expectLog = Arrays.copyOf(expectLog, expectLog.length); + builder.expectLog[0] = concatMessages(builder.expectLog[0], curMsg); + return builder.create(); + }).toArray(MethodCallConfig[]::new); + } + + Builder fail() { + expectFail = true; + return this; + } + + Builder pass() { + expectFail = false; + return this; + } + + Builder args(Object ... v) { + args = v; + return this; + } + + Builder expectLog(String expectLogFirstStr, String ... extra) { + expectLog = Stream.concat(Stream.of(expectLogFirstStr), Stream.of(extra)).toArray(String[]::new); + return this; + } + + Builder withAutoExpectLogPrefix(boolean v) { + withAutoExpectLogPrefix = v; + return this; + } + + private final Method method; + private Object[] args = new Object[0]; + private boolean expectFail; + private String[] expectLog; + private boolean withAutoExpectLogPrefix = true; + } + } + + public TKitTest(MethodCallConfig methodCall) { + this.methodCall = methodCall; + } + + @Test + public void test() { + runAssertWithExpectedLogOutput(() -> { + methodCall.method.invoke(null, methodCall.args); + }, methodCall.expectFail, methodCall.expectLog); + } + + private static void runAssertWithExpectedLogOutput(ThrowingRunnable action, + boolean expectFail, String... expectLogStrings) { + runWithExpectedLogOutput(() -> { + TKit.assertAssert(!expectFail, toRunnable(action)); + }, expectLogStrings); + } + + private static void runWithExpectedLogOutput(ThrowingRunnable action, + String... expectLogStrings) { + final var buf = new ByteArrayOutputStream(); + try (PrintStream ps = new PrintStream(buf, true, StandardCharsets.UTF_8)) { + TKit.withExtraLogStream(action, ps); + } finally { + toRunnable(() -> { + var output = new BufferedReader(new InputStreamReader( + new ByteArrayInputStream(buf.toByteArray()), + StandardCharsets.UTF_8)).lines().map(line -> { + // Skip timestamp + return line.substring(LOG_MSG_TIMESTAMP_LENGTH); + }).toList(); + if (output.size() == 1 && expectLogStrings.length == 1) { + TKit.assertEquals(expectLogStrings[0], output.get(0), null); + } else { + TKit.assertStringListEquals(List.of(expectLogStrings), output, null); + } + }).run(); + } + } + + private static String concatMessages(String msg, String msg2) { + if (msg2 != null && !msg2.isBlank()) { + return msg + ": " + msg2; + } + return msg; + } + + private final MethodCallConfig methodCall; + + private static final int LOG_MSG_TIMESTAMP_LENGTH = "[HH:mm:ss.SSS] ".length(); +} diff --git a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/TestSuite.java b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/TestSuite.java new file mode 100644 index 0000000000000..f9dda8514146d --- /dev/null +++ b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/TestSuite.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +/* + * @test + * @summary Unit tests for jpackage test library + * @library /test/jdk/tools/jpackage/helpers + * @library /test/jdk/tools/jpackage/helpers-test + * @build jdk.jpackage.test.* + * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.TestSuite + */ + +public final class TestSuite { + public static void main(String args[]) throws Throwable { + final var pkgName = TestSuite.class.getPackageName(); + final var javaSuffix = ".java"; + final var testSrcNameSuffix = "Test" + javaSuffix; + + final var unitTestDir = TKit.TEST_SRC_ROOT.resolve(Path.of("helpers-test", pkgName.split("\\."))); + + final List runTestArgs = new ArrayList<>(); + runTestArgs.addAll(List.of(args)); + + try (var javaSources = Files.list(unitTestDir)) { + runTestArgs.addAll(javaSources.filter(path -> { + return path.getFileName().toString().endsWith(testSrcNameSuffix); + }).map(path -> { + var filename = path.getFileName().toString(); + return String.join(".", pkgName, filename.substring(0, filename.length() - javaSuffix.length())); + }).map(testClassName -> { + return "--jpt-run=" + testClassName; + }).toList()); + } + + Main.main(runTestArgs.toArray(String[]::new)); + } +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CfgFile.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CfgFile.java index 31bf0520a315c..9e3a894ec781b 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CfgFile.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CfgFile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,53 +25,115 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Stream; public final class CfgFile { - public String getValue(String section, String key) { - Objects.requireNonNull(section); - Objects.requireNonNull(key); + public String getValue(String sectionName, String key) { + var section = getSection(sectionName); + TKit.assertTrue(section != null, String.format( + "Check section [%s] is found in [%s] cfg file", sectionName, id)); - Map entries = data.get(section); - TKit.assertTrue(entries != null, String.format( - "Check section [%s] is found in [%s] cfg file", section, id)); - - String value = entries.get(key); + String value = section.getValue(key); TKit.assertNotNull(value, String.format( "Check key [%s] is found in [%s] section of [%s] cfg file", key, - section, id)); + sectionName, id)); return value; } - private CfgFile(Map> data, String id) { + public String getValueUnchecked(String sectionName, String key) { + var section = getSection(sectionName); + if (section != null) { + return section.getValue(key); + } else { + return null; + } + } + + public CfgFile addValue(String sectionName, String key, String value) { + var section = getSection(sectionName); + if (section == null) { + section = new Section(sectionName, new ArrayList<>()); + data.add(section); + } + section.data.add(Map.entry(key, value)); + return this; + } + + public CfgFile add(CfgFile other) { + return combine(this, other); + } + + public CfgFile() { + this(new ArrayList<>(), "*"); + } + + public static CfgFile combine(CfgFile base, CfgFile mods) { + var cfgFile = new CfgFile(new ArrayList<>(), "*"); + for (var src : List.of(base, mods)) { + for (var section : src.data) { + for (var kvp : section.data) { + cfgFile.addValue(section.name, kvp.getKey(), kvp.getValue()); + } + } + } + return cfgFile; + } + + private CfgFile(List

    data, String id) { this.data = data; this.id = id; } - public static CfgFile readFromFile(Path path) throws IOException { + public CfgFile save(Path path) { + var lines = data.stream().flatMap(section -> { + return Stream.concat( + Stream.of(String.format("[%s]", section.name)), + section.data.stream().map(kvp -> { + return String.format("%s=%s", kvp.getKey(), kvp.getValue()); + })); + }); + TKit.createTextFile(path, lines); + return this; + } + + private Section getSection(String name) { + Objects.requireNonNull(name); + for (int i = data.size()-1; i >=0; i--) { + var section = data.get(i); + if (name.equals(section.name)) { + return section; + } + } + return null; + } + + public static CfgFile load(Path path) throws IOException { TKit.trace(String.format("Read [%s] jpackage cfg file", path)); final Pattern sectionBeginRegex = Pattern.compile( "\\s*\\[([^]]*)\\]\\s*"); final Pattern keyRegex = Pattern.compile( "\\s*([^=]*)=(.*)" ); - Map> result = new HashMap<>(); + List
    sections = new ArrayList<>(); String currentSectionName = null; - Map currentSection = new HashMap<>(); + List> currentSection = new ArrayList<>(); for (String line : Files.readAllLines(path)) { Matcher matcher = sectionBeginRegex.matcher(line); if (matcher.find()) { if (currentSectionName != null) { - result.put(currentSectionName, Collections.unmodifiableMap( - new HashMap<>(currentSection))); + sections.add(new Section(currentSectionName, + Collections.unmodifiableList(new ArrayList<>( + currentSection)))); } currentSectionName = matcher.group(1); currentSection.clear(); @@ -80,19 +142,32 @@ public static CfgFile readFromFile(Path path) throws IOException { matcher = keyRegex.matcher(line); if (matcher.find()) { - currentSection.put(matcher.group(1), matcher.group(2)); - continue; + currentSection.add(Map.entry(matcher.group(1), matcher.group(2))); } } if (!currentSection.isEmpty()) { - result.put(Optional.ofNullable(currentSectionName).orElse(""), - Collections.unmodifiableMap(currentSection)); + sections.add(new Section( + Optional.ofNullable(currentSectionName).orElse(""), + Collections.unmodifiableList(currentSection))); } - return new CfgFile(Collections.unmodifiableMap(result), path.toString()); + return new CfgFile(sections, path.toString()); + } + + private static record Section(String name, List> data) { + String getValue(String key) { + Objects.requireNonNull(key); + for (int i = data.size()-1; i >= 0; i--) { + var kvp = data.get(i); + if (key.equals(kvp.getKey())) { + return kvp.getValue(); + } + } + return null; + } } - private final Map> data; + private final List
    data; private final String id; } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Comm.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Comm.java new file mode 100644 index 0000000000000..23798f326fecf --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Comm.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import java.util.HashSet; +import java.util.Set; + +record Comm(Set common, Set unique1, Set unique2) { + + static Comm compare(Set a, Set b) { + Set common = new HashSet<>(a); + common.retainAll(b); + Set unique1 = new HashSet<>(a); + unique1.removeAll(common); + Set unique2 = new HashSet<>(b); + unique2.removeAll(common); + return new Comm(common, unique1, unique2); + } +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java index 3fed83ddb361c..c1c51f1ef5557 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,7 +36,9 @@ import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.Set; +import java.util.function.Supplier; import java.util.regex.Pattern; import java.util.spi.ToolProvider; import java.util.stream.Collectors; @@ -52,7 +54,7 @@ public static Executor of(String... cmdline) { public Executor() { saveOutputType = new HashSet<>(Set.of(SaveOutputType.NONE)); - removePath = false; + winEnglishOutput = false; } public Executor setExecutable(String v) { @@ -75,6 +77,10 @@ public Executor setToolProvider(JavaTool v) { return setToolProvider(v.asToolProvider()); } + public Optional getExecutable() { + return Optional.ofNullable(executable); + } + public Executor setDirectory(Path v) { directory = v; return this; @@ -84,8 +90,17 @@ public Executor setExecutable(JavaTool v) { return setExecutable(v.getPath()); } - public Executor setRemovePath(boolean value) { - removePath = value; + public Executor removeEnvVar(String envVarName) { + removeEnvVars.add(Objects.requireNonNull(envVarName)); + return this; + } + + public Executor setWinRunWithEnglishOutput(boolean value) { + if (!TKit.isWindows()) { + throw new UnsupportedOperationException( + "setWinRunWithEnglishOutput is only valid on Windows platform"); + } + winEnglishOutput = value; return this; } @@ -163,10 +178,10 @@ public Executor dumpOutput(boolean v) { return this; } - public class Result { + public record Result(int exitCode, List output, Supplier cmdline) { - Result(int exitCode) { - this.exitCode = exitCode; + public Result { + Objects.requireNonNull(cmdline); } public String getFirstLineOfOutput() { @@ -177,14 +192,10 @@ public List getOutput() { return output; } - public String getPrintableCommandLine() { - return Executor.this.getPrintableCommandLine(); - } - public Result assertExitCodeIs(int expectedExitCode) { TKit.assertEquals(expectedExitCode, exitCode, String.format( "Check command %s exited with %d code", - getPrintableCommandLine(), expectedExitCode)); + cmdline.get(), expectedExitCode)); return this; } @@ -195,9 +206,6 @@ public Result assertExitCodeIsZero() { public int getExitCode() { return exitCode; } - - final int exitCode; - private List output; } public Result executeWithoutExitCodeCheck() { @@ -206,6 +214,11 @@ public Result executeWithoutExitCodeCheck() { "Can't change directory when using tool provider"); } + if (toolProvider != null && winEnglishOutput) { + throw new IllegalArgumentException( + "Can't change locale when using tool provider"); + } + return ThrowingSupplier.toSupplier(() -> { if (toolProvider != null) { return runToolProvider(); @@ -235,18 +248,49 @@ public List executeAndGetOutput() { return saveOutput().execute().getOutput(); } + private static class BadResultException extends RuntimeException { + BadResultException(Result v) { + value = v; + } + + Result getValue() { + return value; + } + + private final Result value; + } + /* * Repeates command "max" times and waits for "wait" seconds between each * execution until command returns expected error code. */ public Result executeAndRepeatUntilExitCode(int expectedCode, int max, int wait) { - Result result; + try { + return tryRunMultipleTimes(() -> { + Result result = executeWithoutExitCodeCheck(); + if (result.getExitCode() != expectedCode) { + throw new BadResultException(result); + } + return result; + }, max, wait).assertExitCodeIs(expectedCode); + } catch (BadResultException ex) { + return ex.getValue().assertExitCodeIs(expectedCode); + } + } + + /* + * Repeates a "task" "max" times and waits for "wait" seconds between each + * execution until the "task" returns without throwing an exception. + */ + public static T tryRunMultipleTimes(Supplier task, int max, int wait) { + RuntimeException lastException = null; int count = 0; do { - result = executeWithoutExitCodeCheck(); - if (result.getExitCode() == expectedCode) { - return result; + try { + return task.get(); + } catch (RuntimeException ex) { + lastException = ex; } try { @@ -258,7 +302,14 @@ public Result executeAndRepeatUntilExitCode(int expectedCode, int max, int wait) count++; } while (count < max); - return result.assertExitCodeIs(expectedCode); + throw lastException; + } + + public static void tryRunMultipleTimes(Runnable task, int max, int wait) { + tryRunMultipleTimes(() -> { + task.run(); + return null; + }, max, wait); } public List executeWithoutExitCodeCheckAndGetOutput() { @@ -285,8 +336,17 @@ private Path executablePath() { return executable.toAbsolutePath(); } + private List prefixCommandLineArgs() { + if (winEnglishOutput) { + return List.of("cmd.exe", "/c", "chcp", "437", ">nul", "2>&1", "&&"); + } else { + return List.of(); + } + } + private Result runExecutable() throws IOException, InterruptedException { List command = new ArrayList<>(); + command.addAll(prefixCommandLineArgs()); command.add(executablePath().toString()); command.addAll(args); ProcessBuilder builder = new ProcessBuilder(command); @@ -309,10 +369,12 @@ private Result runExecutable() throws IOException, InterruptedException { builder.directory(directory.toFile()); sb.append(String.format("; in directory [%s]", directory)); } - if (removePath) { - // run this with cleared Path in Environment - TKit.trace("Clearing PATH in environment"); - builder.environment().remove("PATH"); + if (!removeEnvVars.isEmpty()) { + final var envComm = Comm.compare(builder.environment().keySet(), removeEnvVars); + builder.environment().keySet().removeAll(envComm.common()); + envComm.common().forEach(envVar -> { + TKit.trace(String.format("Clearing %s in environment", envVar)); + }); } trace("Execute " + sb.toString() + "..."); @@ -343,28 +405,34 @@ private Result runExecutable() throws IOException, InterruptedException { } } - Result reply = new Result(process.waitFor()); - trace("Done. Exit code: " + reply.exitCode); + final int exitCode = process.waitFor(); + trace("Done. Exit code: " + exitCode); + final List output; if (outputLines != null) { - reply.output = Collections.unmodifiableList(outputLines); + output = Collections.unmodifiableList(outputLines); + } else { + output = null; } - return reply; + return createResult(exitCode, output); } - private Result runToolProvider(PrintStream out, PrintStream err) { + private int runToolProvider(PrintStream out, PrintStream err) { trace("Execute " + getPrintableCommandLine() + "..."); - Result reply = new Result(toolProvider.run(out, err, args.toArray( - String[]::new))); - trace("Done. Exit code: " + reply.exitCode); - return reply; + final int exitCode = toolProvider.run(out, err, args.toArray( + String[]::new)); + trace("Done. Exit code: " + exitCode); + return exitCode; } + private Result createResult(int exitCode, List output) { + return new Result(exitCode, output, this::getPrintableCommandLine); + } private Result runToolProvider() throws IOException { if (!withSavedOutput()) { if (saveOutputType.contains(SaveOutputType.DUMP)) { - return runToolProvider(System.out, System.err); + return createResult(runToolProvider(System.out, System.err), null); } PrintStream nullPrintStream = new PrintStream(new OutputStream() { @@ -373,36 +441,40 @@ public void write(int b) { // Nop } }); - return runToolProvider(nullPrintStream, nullPrintStream); + return createResult(runToolProvider(nullPrintStream, nullPrintStream), null); } try (ByteArrayOutputStream buf = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(buf)) { - Result reply = runToolProvider(ps, ps); + final var exitCode = runToolProvider(ps, ps); ps.flush(); + final List output; try (BufferedReader bufReader = new BufferedReader(new StringReader( buf.toString()))) { if (saveOutputType.contains(SaveOutputType.FIRST_LINE)) { String firstLine = bufReader.lines().findFirst().orElse(null); if (firstLine != null) { - reply.output = List.of(firstLine); + output = List.of(firstLine); + } else { + output = null; } } else if (saveOutputType.contains(SaveOutputType.FULL)) { - reply.output = bufReader.lines().collect( - Collectors.toUnmodifiableList()); + output = bufReader.lines().collect(Collectors.toUnmodifiableList()); + } else { + output = null; } if (saveOutputType.contains(SaveOutputType.DUMP)) { Stream lines; if (saveOutputType.contains(SaveOutputType.FULL)) { - lines = reply.output.stream(); + lines = output.stream(); } else { lines = bufReader.lines(); } lines.forEach(System.out::println); } } - return reply; + return createResult(exitCode, output); } } @@ -418,15 +490,17 @@ public String getPrintableCommandLine() { exec = executablePath().toString(); } - return String.format(format, printCommandLine(exec, args), - args.size() + 1); + var cmdline = Stream.of(prefixCommandLineArgs(), List.of(exec), args).flatMap( + List::stream).toList(); + + return String.format(format, printCommandLine(cmdline), cmdline.size()); } - private static String printCommandLine(String executable, List args) { + private static String printCommandLine(List cmdline) { // Want command line printed in a way it can be easily copy/pasted - // to be executed manally + // to be executed manually Pattern regex = Pattern.compile("\\s"); - return Stream.concat(Stream.of(executable), args.stream()).map( + return cmdline.stream().map( v -> (v.isEmpty() || regex.matcher(v).find()) ? "\"" + v + "\"" : v).collect( Collectors.joining(" ")); } @@ -439,7 +513,8 @@ private static void trace(String msg) { private Path executable; private Set saveOutputType; private Path directory; - private boolean removePath; + private Set removeEnvVars = new HashSet<>(); + private boolean winEnglishOutput; private String winTmpDir = null; private static enum SaveOutputType { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Functional.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Functional.java index 6fe7e56ffb7b1..a57caa92cb20e 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Functional.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Functional.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -149,13 +149,18 @@ public ExceptionBox(Throwable throwable) { } @SuppressWarnings("unchecked") - public static void rethrowUnchecked(Throwable throwable) throws ExceptionBox { - if (throwable instanceof RuntimeException) { - throw (RuntimeException)throwable; + public static RuntimeException rethrowUnchecked(Throwable throwable) throws + ExceptionBox { + if (throwable instanceof RuntimeException err) { + throw err; } - if (throwable instanceof InvocationTargetException) { - throw new ExceptionBox(throwable.getCause()); + if (throwable instanceof Error err) { + throw err; + } + + if (throwable instanceof InvocationTargetException err) { + throw rethrowUnchecked(err.getCause()); } throw new ExceptionBox(throwable); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java index 7d5472fd5be87..46aa2f1375f17 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,7 +22,6 @@ */ package jdk.jpackage.test; -import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -57,15 +56,10 @@ public final class HelloApp { private JarBuilder prepareSources(Path srcDir) throws IOException { final String srcClassName = appDesc.srcClassName(); - - final String qualifiedClassName = appDesc.className(); - - final String className = qualifiedClassName.substring( - qualifiedClassName.lastIndexOf('.') + 1); + final String className = appDesc.shortClassName(); final String packageName = appDesc.packageName(); - final Path srcFile = srcDir.resolve(Path.of(String.join( - File.separator, qualifiedClassName.split("\\.")) + ".java")); + final Path srcFile = srcDir.resolve(appDesc.classNameAsPath(".java")); Files.createDirectories(srcFile.getParent()); JarBuilder jarBuilder = createJarBuilder().addSourceFile(srcFile); @@ -317,20 +311,23 @@ public static Path createBundle(JavaAppDesc appDesc, Path outputDir) { public static void executeLauncherAndVerifyOutput(JPackageCommand cmd, String... args) { - AppOutputVerifier av = getVerifier(cmd, args); + AppOutputVerifier av = assertMainLauncher(cmd, args); if (av != null) { - // when running app launchers, clear users environment - av.executeAndVerifyOutput(true, args); + av.executeAndVerifyOutput(args); } } public static Executor.Result executeLauncher(JPackageCommand cmd, String... args) { - AppOutputVerifier av = getVerifier(cmd, args); - return av.executeOnly(true, args); + AppOutputVerifier av = assertMainLauncher(cmd, args); + if (av != null) { + return av.saveOutput(true).execute(args); + } else { + return null; + } } - private static AppOutputVerifier getVerifier(JPackageCommand cmd, + public static AppOutputVerifier assertMainLauncher(JPackageCommand cmd, String... args) { final Path launcherPath = cmd.appLauncherPath(); if (!cmd.canRunLauncher(String.format("Not running [%s] launcher", @@ -348,7 +345,7 @@ private static AppOutputVerifier getVerifier(JPackageCommand cmd, } - public final static class AppOutputVerifier { + public static final class AppOutputVerifier { AppOutputVerifier(Path helloAppLauncher) { this.launcherPath = helloAppLauncher; this.outputFilePath = TKit.workDir().resolve(OUTPUT_FILENAME); @@ -356,6 +353,16 @@ public final static class AppOutputVerifier { this.defaultLauncherArgs = new ArrayList<>(); } + public AppOutputVerifier saveOutput(boolean v) { + saveOutput = v; + return this; + } + + public AppOutputVerifier expectedExitCode(int v) { + expectedExitCode = v; + return this; + } + public AppOutputVerifier addDefaultArguments(String... v) { return addDefaultArguments(List.of(v)); } @@ -372,6 +379,8 @@ public AppOutputVerifier addParam(String name, String value) { outputFilePath = Path.of(value); } else if ("jpackage.test.exitCode".equals(name)) { expectedExitCode = Integer.parseInt(value); + } else if ("jpackage.test.noexit".equals(name)) { + launcherNoExit = Boolean.parseBoolean(value); } return this; } @@ -415,36 +424,16 @@ public void verifyOutput(String... args) { } public void executeAndVerifyOutput(String... args) { - executeAndVerifyOutput(false, args); - } - - public void executeAndVerifyOutput(boolean removePath, - List launcherArgs, List appArgs) { - final int attempts = 3; - final int waitBetweenAttemptsSeconds = 5; - getExecutor(launcherArgs.toArray(new String[0])).dumpOutput().setRemovePath( - removePath).executeAndRepeatUntilExitCode(expectedExitCode, - attempts, waitBetweenAttemptsSeconds); - verifyOutputFile(outputFilePath, appArgs, params); + execute(args); + verifyOutput(args); } - public void executeAndVerifyOutput(boolean removePath, String... args) { - final List launcherArgs = List.of(args); - final List appArgs; - if (launcherArgs.isEmpty()) { - appArgs = defaultLauncherArgs; + public Executor.Result execute(String... args) { + if (launcherNoExit) { + return getExecutor(args).executeWithoutExitCodeCheck(); } else { - appArgs = launcherArgs; + return HelloApp.execute(expectedExitCode, getExecutor(args)); } - - executeAndVerifyOutput(removePath, launcherArgs, appArgs); - } - - public Executor.Result executeOnly(boolean removePath, String...args) { - return getExecutor(args) - .saveOutput() - .setRemovePath(removePath) - .executeWithoutExitCodeCheck(); } private Executor getExecutor(String...args) { @@ -462,12 +451,18 @@ private Executor getExecutor(String...args) { } final List launcherArgs = List.of(args); - return new Executor() + final var executor = new Executor() .setDirectory(outputFile.getParent()) + .saveOutput(saveOutput) + .dumpOutput() .setExecutable(executablePath) - .addArguments(launcherArgs); + .addArguments(List.of(args)); + + return configureEnvironment(executor); } + private boolean launcherNoExit; + private boolean saveOutput; private final Path launcherPath; private Path outputFilePath; private int expectedExitCode; @@ -479,13 +474,39 @@ public static AppOutputVerifier assertApp(Path helloAppLauncher) { return new AppOutputVerifier(helloAppLauncher); } - final static String OUTPUT_FILENAME = "appOutput.txt"; + public static Executor.Result configureAndExecute(int expectedExitCode, Executor executor) { + return execute(expectedExitCode, configureEnvironment(executor)); + } + + private static Executor.Result execute(int expectedExitCode, Executor executor) { + if (TKit.isLinux()) { + final int attempts = 3; + final int waitBetweenAttemptsSeconds = 5; + return executor.executeAndRepeatUntilExitCode(expectedExitCode, attempts, + waitBetweenAttemptsSeconds); + } else { + return executor.execute(expectedExitCode); + } + } + + private static Executor configureEnvironment(Executor executor) { + if (CLEAR_JAVA_ENV_VARS) { + executor.removeEnvVar("JAVA_TOOL_OPTIONS"); + executor.removeEnvVar("_JAVA_OPTIONS"); + } + return executor; + } + + static final String OUTPUT_FILENAME = "appOutput.txt"; private final JavaAppDesc appDesc; private static final Path HELLO_JAVA = TKit.TEST_SRC_ROOT.resolve( "apps/Hello.java"); - private final static String CLASS_NAME = HELLO_JAVA.getFileName().toString().split( + private static final String CLASS_NAME = HELLO_JAVA.getFileName().toString().split( "\\.", 2)[0]; + + private static final boolean CLEAR_JAVA_ENV_VARS = Optional.ofNullable( + TKit.getConfigProperty("clear-app-launcher-java-env-vars")).map(Boolean::parseBoolean).orElse(false); } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java index 6477860235873..54532579a4f5e 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java @@ -57,7 +57,7 @@ * anything. The simplest is to compile test application and pack in a jar for * use on jpackage command line. */ -public final class JPackageCommand extends CommandArguments { +public class JPackageCommand extends CommandArguments { public JPackageCommand() { prerequisiteActions = new Actions(); @@ -570,8 +570,18 @@ public Path appLauncherCfgPath(String launcherName) { } public boolean isFakeRuntime(String msg) { - Path runtimeDir = appRuntimeDirectory(); + if (isFakeRuntime()) { + // Fake runtime + Path runtimeDir = appRuntimeDirectory(); + TKit.trace(String.format( + "%s because application runtime directory [%s] is incomplete", + msg, runtimeDir)); + return true; + } + return false; + } + private boolean isFakeRuntime() { final Collection criticalRuntimeFiles; if (TKit.isWindows()) { criticalRuntimeFiles = WindowsHelper.CRITICAL_RUNTIME_FILES; @@ -583,16 +593,9 @@ public boolean isFakeRuntime(String msg) { throw TKit.throwUnknownPlatformError(); } - if (!criticalRuntimeFiles.stream().anyMatch(v -> { - return runtimeDir.resolve(v).toFile().exists(); - })) { - // Fake runtime - TKit.trace(String.format( - "%s because application runtime directory [%s] is incomplete", - msg, runtimeDir)); - return true; - } - return false; + Path runtimeDir = appRuntimeDirectory(); + return !criticalRuntimeFiles.stream().map(runtimeDir::resolve).allMatch( + Files::exists); } public boolean canRunLauncher(String msg) { @@ -657,6 +660,13 @@ public JPackageCommand ignoreDefaultRuntime(boolean v) { return this; } + public JPackageCommand ignoreFakeRuntime() { + if (isFakeRuntime()) { + ignoreDefaultRuntime(true); + } + return this; + } + public JPackageCommand ignoreDefaultVerbose(boolean v) { verifyMutable(); ignoreDefaultVerbose = v; @@ -733,7 +743,7 @@ public Executor.Result execute(int expectedExitCode) { .createExecutor() .execute(expectedExitCode); - if (result.exitCode == 0) { + if (result.exitCode() == 0) { executeVerifyActions(); } @@ -876,7 +886,7 @@ public CfgFile readLauncherCfgFile(String launcherName) { if (isRuntime()) { return null; } - return ThrowingFunction.toFunction(CfgFile::readFromFile).apply( + return ThrowingFunction.toFunction(CfgFile::load).apply( appLauncherCfgPath(launcherName)); } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JavaAppDesc.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JavaAppDesc.java index 6a798012ca78a..c0807deef3b08 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JavaAppDesc.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JavaAppDesc.java @@ -22,8 +22,9 @@ */ package jdk.jpackage.test; -import java.io.File; import java.nio.file.Path; +import java.util.Objects; +import java.util.stream.Stream; public final class JavaAppDesc { @@ -73,9 +74,18 @@ public String className() { return qualifiedClassName; } + public String shortClassName() { + return qualifiedClassName.substring(qualifiedClassName.lastIndexOf('.') + 1); + } + + Path classNameAsPath(String extension) { + final String[] pathComponents = qualifiedClassName.split("\\."); + pathComponents[pathComponents.length - 1] = shortClassName() + extension; + return Stream.of(pathComponents).map(Path::of).reduce(Path::resolve).get(); + } + public Path classFilePath() { - return Path.of(qualifiedClassName.replace(".", File.separator) - + ".class"); + return classNameAsPath(".class"); } public String moduleName() { @@ -124,6 +134,48 @@ public boolean isWithMainClass() { return withMainClass; } + @Override + public int hashCode() { + int hash = 5; + hash = 79 * hash + Objects.hashCode(this.srcJavaPath); + hash = 79 * hash + Objects.hashCode(this.qualifiedClassName); + hash = 79 * hash + Objects.hashCode(this.moduleName); + hash = 79 * hash + Objects.hashCode(this.bundleFileName); + hash = 79 * hash + Objects.hashCode(this.moduleVersion); + hash = 79 * hash + (this.withMainClass ? 1 : 0); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final JavaAppDesc other = (JavaAppDesc) obj; + if (this.withMainClass != other.withMainClass) { + return false; + } + if (!Objects.equals(this.qualifiedClassName, other.qualifiedClassName)) { + return false; + } + if (!Objects.equals(this.moduleName, other.moduleName)) { + return false; + } + if (!Objects.equals(this.bundleFileName, other.bundleFileName)) { + return false; + } + if (!Objects.equals(this.moduleVersion, other.moduleVersion)) { + return false; + } + return Objects.equals(this.srcJavaPath, other.srcJavaPath); + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); @@ -216,7 +268,9 @@ public static JavaAppDesc parse(final String javaAppDesc) { components[0].length() - 1); desc.setWithMainClass(true); } - desc.setClassName(components[0]); + if (!components[0].isEmpty()) { + desc.setClassName(components[0]); + } if (components.length == 2) { desc.setModuleVersion(components[1]); } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherIconVerifier.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherIconVerifier.java index 77f71f01673fa..b9f1fc6806eb7 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherIconVerifier.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherIconVerifier.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,9 +23,14 @@ package jdk.jpackage.test; +import java.awt.image.BufferedImage; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Optional; +import javax.imageio.ImageIO; public final class LauncherIconVerifier { public LauncherIconVerifier() { @@ -60,7 +65,11 @@ public void applyTo(JPackageCommand cmd) throws IOException { Path iconPath = cmd.appLayout().destktopIntegrationDirectory().resolve( curLauncherName + TKit.ICON_SUFFIX); - if (expectedDefault) { + if (TKit.isWindows()) { + TKit.assertPathExists(iconPath, false); + WinIconVerifier.instance.verifyLauncherIcon(cmd, launcherName, + expectedIcon, expectedDefault); + } else if (expectedDefault) { TKit.assertPathExists(iconPath, true); } else if (expectedIcon == null) { TKit.assertPathExists(iconPath, false); @@ -73,6 +82,179 @@ public void applyTo(JPackageCommand cmd) throws IOException { } } + private static class WinIconVerifier { + + void verifyLauncherIcon(JPackageCommand cmd, String launcherName, + Path expectedIcon, boolean expectedDefault) { + TKit.withTempDirectory("icons", tmpDir -> { + Path launcher = cmd.appLauncherPath(launcherName); + Path iconWorkDir = tmpDir.resolve(launcher.getFileName()); + Path iconContainer = iconWorkDir.resolve("container.exe"); + Files.createDirectories(iconContainer.getParent()); + Files.copy(getDefaultAppLauncher(expectedIcon == null + && !expectedDefault), iconContainer); + if (expectedIcon != null) { + Executor.tryRunMultipleTimes(() -> { + setIcon(expectedIcon, iconContainer); + }, 3, 5); + } + + Path extractedExpectedIcon = extractIconFromExecutable( + iconWorkDir, iconContainer, "expected"); + Path extractedActualIcon = extractIconFromExecutable(iconWorkDir, + launcher, "actual"); + + TKit.trace(String.format( + "Check icon file [%s] of %s launcher is a copy of source icon file [%s]", + extractedActualIcon, + Optional.ofNullable(launcherName).orElse("main"), + extractedExpectedIcon)); + + if (Files.mismatch(extractedExpectedIcon, extractedActualIcon) + != -1) { + // On Windows11 .NET API extracting icons from executables + // produce slightly different output for the same icon. + // To workaround it, compare pixels of images and if the + // number of off pixels is below a threshold, assume + // equality. + BufferedImage expectedImg = ImageIO.read( + extractedExpectedIcon.toFile()); + BufferedImage actualImg = ImageIO.read( + extractedActualIcon.toFile()); + + int w = expectedImg.getWidth(); + int h = expectedImg.getHeight(); + + TKit.assertEquals(w, actualImg.getWidth(), + "Check expected and actual icons have the same width"); + TKit.assertEquals(h, actualImg.getHeight(), + "Check expected and actual icons have the same height"); + + int diffPixelCount = 0; + + for (int i = 0; i != w; ++i) { + for (int j = 0; j != h; ++j) { + int expectedRGB = expectedImg.getRGB(i, j); + int actualRGB = actualImg.getRGB(i, j); + + if (expectedRGB != actualRGB) { + TKit.trace(String.format( + "Images mismatch at [%d, %d] pixel", i, + j)); + diffPixelCount++; + } + } + } + + double threshold = 0.1; + TKit.assertTrue(((double) diffPixelCount) / (w * h) + < threshold, + String.format( + "Check the number of mismatched pixels [%d] of [%d] is < [%f] threshold", + diffPixelCount, (w * h), threshold)); + } + }); + } + + private WinIconVerifier() { + try { + executableRebranderClass = Class.forName( + "jdk.jpackage.internal.ExecutableRebrander"); + + lockResource = executableRebranderClass.getDeclaredMethod( + "lockResource", String.class); + // Note: this reflection call requires + // --add-opens jdk.jpackage/jdk.jpackage.internal=ALL-UNNAMED + lockResource.setAccessible(true); + + unlockResource = executableRebranderClass.getDeclaredMethod( + "unlockResource", long.class); + unlockResource.setAccessible(true); + + iconSwap = executableRebranderClass.getDeclaredMethod("iconSwap", + long.class, String.class); + iconSwap.setAccessible(true); + } catch (ClassNotFoundException | NoSuchMethodException + | SecurityException ex) { + throw Functional.rethrowUnchecked(ex); + } + } + + private Path extractIconFromExecutable(Path outputDir, Path executable, + String label) { + Path extractedIcon = outputDir.resolve(label + ".bmp"); + String script = String.join(";", + "[System.Reflection.Assembly]::LoadWithPartialName('System.Drawing')", + String.format( + "[System.Drawing.Icon]::ExtractAssociatedIcon('%s').ToBitmap().Save('%s', [System.Drawing.Imaging.ImageFormat]::Bmp)", + executable.toAbsolutePath().normalize(), + extractedIcon.toAbsolutePath().normalize())); + + Executor.of("powershell", "-NoLogo", "-NoProfile", "-Command", + script).execute(); + + return extractedIcon; + } + + private Path getDefaultAppLauncher(boolean noIcon) { + // Create app image with the sole purpose to get the default app launcher + Path defaultAppOutputDir = TKit.workDir().resolve(String.format( + "out-%d", ProcessHandle.current().pid())); + JPackageCommand cmd = JPackageCommand.helloAppImage().setFakeRuntime().setArgumentValue( + "--dest", defaultAppOutputDir); + + String launcherName; + if (noIcon) { + launcherName = "no-icon"; + new AdditionalLauncher(launcherName).setNoIcon().applyTo(cmd); + } else { + launcherName = null; + } + + if (!Files.isExecutable(cmd.appLauncherPath(launcherName))) { + cmd.execute(); + } + return cmd.appLauncherPath(launcherName); + } + + private void setIcon(Path iconPath, Path launcherPath) { + TKit.trace(String.format("Set icon of [%s] launcher to [%s] file", + launcherPath, iconPath)); + try { + launcherPath.toFile().setWritable(true, true); + try { + long lock = 0; + try { + lock = (Long) lockResource.invoke(null, new Object[]{ + launcherPath.toAbsolutePath().normalize().toString()}); + if (lock == 0) { + throw new RuntimeException(String.format( + "Failed to lock [%s] executable", + launcherPath)); + } + iconSwap.invoke(null, new Object[]{lock, + iconPath.toAbsolutePath().normalize().toString()}); + } finally { + if (lock != 0) { + unlockResource.invoke(null, new Object[]{lock}); + } + } + } catch (IllegalAccessException | InvocationTargetException ex) { + throw Functional.rethrowUnchecked(ex); + } + } finally { + launcherPath.toFile().setWritable(false, true); + } + } + + final static WinIconVerifier instance = new WinIconVerifier(); + + private final Class executableRebranderClass; + private final Method lockResource; + private final Method unlockResource; + private final Method iconSwap; + } + private String launcherName; private Path expectedIcon; private boolean expectedDefault; diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java index 0884b41dc5530..116712afd7399 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java @@ -24,10 +24,10 @@ import java.awt.Desktop; import java.awt.GraphicsEnvironment; -import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.time.Duration; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -271,7 +271,7 @@ PackageTest addHelloAppFileAssociationsVerifier(FileAssociations fa, TKit.trace(String.format("Use desktop to open [%s] file", testFile)); Desktop.getDesktop().open(testFile.toFile()); - TKit.waitForFileCreated(appOutput, 7); + TKit.waitForFileCreated(appOutput, Duration.ofSeconds(7), Duration.ofSeconds(3)); List expectedArgs = new ArrayList<>(List.of( faLauncherDefaultArgs)); @@ -709,7 +709,7 @@ private static Map createDefaultPackageHandlers() private Set namedInitializers; private Map packageHandlers; - private final static File BUNDLE_OUTPUT_DIR; + private static final Path BUNDLE_OUTPUT_DIR; static { final String propertyName = "output"; @@ -717,9 +717,9 @@ private static Map createDefaultPackageHandlers() if (val == null) { BUNDLE_OUTPUT_DIR = null; } else { - BUNDLE_OUTPUT_DIR = new File(val).getAbsoluteFile(); + BUNDLE_OUTPUT_DIR = Path.of(val).toAbsolutePath(); - if (!BUNDLE_OUTPUT_DIR.isDirectory()) { + if (!Files.isDirectory(BUNDLE_OUTPUT_DIR)) { throw new IllegalArgumentException(String.format("Invalid value of %s sytem property: [%s]. Should be existing directory", TKit.getConfigPropertyName(propertyName), BUNDLE_OUTPUT_DIR)); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java index 2298ada561217..419f0ca129f99 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java @@ -22,7 +22,6 @@ */ package jdk.jpackage.test; -import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; @@ -36,13 +35,16 @@ import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; import java.nio.file.WatchEvent; import java.nio.file.WatchKey; -import java.nio.file.WatchService; import java.text.SimpleDateFormat; +import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Date; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -55,13 +57,14 @@ import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Collectors; +import static java.util.stream.Collectors.toSet; import java.util.stream.Stream; import jdk.jpackage.test.Functional.ExceptionBox; import jdk.jpackage.test.Functional.ThrowingConsumer; import jdk.jpackage.test.Functional.ThrowingRunnable; import jdk.jpackage.test.Functional.ThrowingSupplier; -final public class TKit { +public final class TKit { private static final String OS = System.getProperty("os.name").toLowerCase(); @@ -82,7 +85,7 @@ final public class TKit { return TEST_SRC_ROOT.resolve("../../../../src/jdk.jpackage").normalize().toAbsolutePath(); }).get(); - public final static String ICON_SUFFIX = Functional.identity(() -> { + public static final String ICON_SUFFIX = Functional.identity(() -> { if (isOSX()) { return ".icns"; } @@ -103,14 +106,21 @@ static void withExtraLogStream(ThrowingRunnable action) { ThrowingRunnable.toRunnable(action).run(); } else { try (PrintStream logStream = openLogStream()) { - extraLogStream = logStream; - ThrowingRunnable.toRunnable(action).run(); - } finally { - extraLogStream = null; + withExtraLogStream(action, logStream); } } } + static void withExtraLogStream(ThrowingRunnable action, PrintStream logStream) { + var oldExtraLogStream = extraLogStream; + try { + extraLogStream = logStream; + ThrowingRunnable.toRunnable(action).run(); + } finally { + extraLogStream = oldExtraLogStream; + } + } + static void runTests(List tests) { if (currentTest != null) { throw new IllegalStateException( @@ -183,11 +193,7 @@ public static boolean isLinux() { } public static boolean isLinuxAPT() { - if (!isLinux()) { - return false; - } - File aptFile = new File("/usr/bin/apt-get"); - return aptFile.exists(); + return isLinux() && Files.exists(Path.of("/usr/bin/apt-get")); } private static String addTimestamp(String msg) { @@ -237,6 +243,13 @@ public static void createPropertiesFile(Path propsFilename, trace("Done"); } + public static void traceFileContents(Path path, String label) throws IOException { + assertFileExists(path); + trace(String.format("Dump [%s] %s...", path, label)); + Files.readAllLines(path).forEach(TKit::trace); + trace("Done"); + } + public static void createPropertiesFile(Path propsFilename, Map.Entry... props) { createPropertiesFile(propsFilename, List.of(props)); @@ -264,6 +277,22 @@ public static void error(String v) { throw new AssertionError(v); } + static void assertAssert(boolean expectedSuccess, Runnable runnable) { + try { + runnable.run(); + } catch (AssertionError err) { + if (expectedSuccess) { + assertUnexpected("Assertion failed"); + } else { + return; + } + } + + if (!expectedSuccess) { + assertUnexpected("Assertion passed"); + } + } + private final static String TEMP_FILE_PREFIX = null; private static Path createUniqueFileName(String defaultName) { @@ -500,49 +529,57 @@ public static Path createRelativePathCopy(final Path file) { return file; } - static void waitForFileCreated(Path fileToWaitFor, - long timeoutSeconds) throws IOException { + public static void waitForFileCreated(Path fileToWaitFor, + Duration timeout, Duration afterCreatedTimeout) throws IOException { + waitForFileCreated(fileToWaitFor, timeout); + // Wait after the file has been created to ensure it is fully written. + ThrowingConsumer.toConsumer(Thread::sleep).accept(afterCreatedTimeout.getSeconds()); + } + + private static void waitForFileCreated(Path fileToWaitFor, Duration timeout) throws IOException { trace(String.format("Wait for file [%s] to be available", fileToWaitFor.toAbsolutePath())); - WatchService ws = FileSystems.getDefault().newWatchService(); - - Path watchDirectory = fileToWaitFor.toAbsolutePath().getParent(); - watchDirectory.register(ws, ENTRY_CREATE, ENTRY_MODIFY); - - long waitUntil = System.currentTimeMillis() + timeoutSeconds * 1000; - for (;;) { - long timeout = waitUntil - System.currentTimeMillis(); - assertTrue(timeout > 0, String.format( - "Check timeout value %d is positive", timeout)); - - WatchKey key = ThrowingSupplier.toSupplier(() -> ws.poll(timeout, - TimeUnit.MILLISECONDS)).get(); - if (key == null) { - if (fileToWaitFor.toFile().exists()) { - trace(String.format( - "File [%s] is available after poll timeout expired", - fileToWaitFor)); - return; + try (var ws = FileSystems.getDefault().newWatchService()) { + + Path watchDirectory = fileToWaitFor.toAbsolutePath().getParent(); + watchDirectory.register(ws, ENTRY_CREATE, ENTRY_MODIFY); + + var waitUntil = Instant.now().plus(timeout); + for (;;) { + Instant n = Instant.now(); + Duration remainderTimeout = Duration.between(n, waitUntil); + assertTrue(!remainderTimeout.isNegative() && !remainderTimeout.isZero(), String.format( + "Check timeout value %dms is positive", remainderTimeout.toMillis())); + + WatchKey key = ThrowingSupplier.toSupplier(() -> { + return ws.poll(remainderTimeout.toMillis(), TimeUnit.MILLISECONDS); + }).get(); + if (key == null) { + if (Files.exists(fileToWaitFor)) { + trace(String.format( + "File [%s] is available after poll timeout expired", + fileToWaitFor)); + return; + } + assertUnexpected(String.format("Timeout %dms expired", remainderTimeout.toMillis())); } - assertUnexpected(String.format("Timeout expired", timeout)); - } - for (WatchEvent event : key.pollEvents()) { - if (event.kind() == StandardWatchEventKinds.OVERFLOW) { - continue; - } - Path contextPath = (Path) event.context(); - if (Files.isSameFile(watchDirectory.resolve(contextPath), - fileToWaitFor)) { - trace(String.format("File [%s] is available", fileToWaitFor)); - return; + for (WatchEvent event : key.pollEvents()) { + if (event.kind() == StandardWatchEventKinds.OVERFLOW) { + continue; + } + Path contextPath = (Path) event.context(); + if (Files.exists(fileToWaitFor) && Files.isSameFile(watchDirectory.resolve(contextPath), fileToWaitFor)) { + trace(String.format("File [%s] is available", fileToWaitFor)); + return; + } } - } - if (!key.reset()) { - assertUnexpected("Watch key invalidated"); + if (!key.reset()) { + assertUnexpected("Watch key invalidated"); + } } } } @@ -569,7 +606,7 @@ public static void assertEquals(long expected, long actual, String msg) { msg)); } - traceAssert(String.format("assertEquals(%d): %s", expected, msg)); + traceAssert(concatMessages(String.format("assertEquals(%d)", expected), msg)); } public static void assertNotEquals(long expected, long actual, String msg) { @@ -579,8 +616,8 @@ public static void assertNotEquals(long expected, long actual, String msg) { msg)); } - traceAssert(String.format("assertNotEquals(%d, %d): %s", expected, - actual, msg)); + traceAssert(concatMessages(String.format("assertNotEquals(%d, %d)", expected, + actual), msg)); } public static void assertEquals(String expected, String actual, String msg) { @@ -592,7 +629,7 @@ public static void assertEquals(String expected, String actual, String msg) { msg)); } - traceAssert(String.format("assertEquals(%s): %s", expected, msg)); + traceAssert(concatMessages(String.format("assertEquals(%s)", expected), msg)); } public static void assertNotEquals(String expected, String actual, String msg) { @@ -600,8 +637,8 @@ public static void assertNotEquals(String expected, String actual, String msg) { if ((actual != null && !actual.equals(expected)) || (expected != null && !expected.equals(actual))) { - traceAssert(String.format("assertNotEquals(%s, %s): %s", expected, - actual, msg)); + traceAssert(concatMessages(String.format("assertNotEquals(%s, %s)", expected, + actual), msg)); return; } @@ -615,7 +652,7 @@ public static void assertNull(Object value, String msg) { value), msg)); } - traceAssert(String.format("assertNull(): %s", msg)); + traceAssert(concatMessages("assertNull()", msg)); } public static void assertNotNull(Object value, String msg) { @@ -624,7 +661,7 @@ public static void assertNotNull(Object value, String msg) { error(concatMessages("Unexpected null value", msg)); } - traceAssert(String.format("assertNotNull(%s): %s", value, msg)); + traceAssert(concatMessages(String.format("assertNotNull(%s)", value), msg)); } public static void assertTrue(boolean actual, String msg) { @@ -644,7 +681,7 @@ public static void assertTrue(boolean actual, String msg, Runnable onFail) { error(concatMessages("Failed", msg)); } - traceAssert(String.format("assertTrue(): %s", msg)); + traceAssert(concatMessages("assertTrue()", msg)); } public static void assertFalse(boolean actual, String msg, Runnable onFail) { @@ -656,7 +693,7 @@ public static void assertFalse(boolean actual, String msg, Runnable onFail) { error(concatMessages("Failed", msg)); } - traceAssert(String.format("assertFalse(): %s", msg)); + traceAssert(concatMessages("assertFalse()", msg)); } public static void assertPathExists(Path path, boolean exists) { @@ -723,11 +760,80 @@ public static void assertUnexpected(String msg) { error(concatMessages("Unexpected", msg)); } + public static DirectoryContentVerifier assertDirectoryContent(Path dir) { + return new DirectoryContentVerifier(dir); + } + public static final class DirectoryContentVerifier { + public DirectoryContentVerifier(Path baseDir) { + this(baseDir, ThrowingSupplier.toSupplier(() -> { + try (var files = Files.list(baseDir)) { + return files.map(Path::getFileName).collect(toSet()); + } + }).get()); + } + public void match(Path ... expected) { + DirectoryContentVerifier.this.match(Set.of(expected)); + } + public void match(Set expected) { + currentTest.notifyAssert(); + var comm = Comm.compare(content, expected); + if (!comm.unique1().isEmpty() && !comm.unique2().isEmpty()) { + error(String.format( + "assertDirectoryContentEquals(%s): Some expected %s. Unexpected %s. Missing %s", + baseDir, format(comm.common()), format(comm.unique1()), format(comm.unique2()))); + } else if (!comm.unique1().isEmpty()) { + error(String.format( + "assertDirectoryContentEquals%s: Expected %s. Unexpected %s", + baseDir, format(comm.common()), format(comm.unique1()))); + } else if (!comm.unique2().isEmpty()) { + error(String.format( + "assertDirectoryContentEquals(%s): Some expected %s. Missing %s", + baseDir, format(comm.common()), format(comm.unique2()))); + } else { + traceAssert(String.format( + "assertDirectoryContentEquals(%s): Expected %s", + baseDir, format(expected))); + } + } + public void contains(Path ... expected) { + contains(Set.of(expected)); + } + public void contains(Set expected) { + currentTest.notifyAssert(); + var comm = Comm.compare(content, expected); + if (!comm.unique2().isEmpty()) { + error(String.format( + "assertDirectoryContentContains(%s): Some expected %s. Missing %s", + baseDir, format(comm.common()), format(comm.unique2()))); + } else { + traceAssert(String.format( + "assertDirectoryContentContains(%s): Expected %s", + baseDir, format(expected))); + } + } + public DirectoryContentVerifier removeAll(Path ... paths) { + Set newContent = new HashSet<>(content); + newContent.removeAll(List.of(paths)); + return new DirectoryContentVerifier(baseDir, newContent); + } + private DirectoryContentVerifier(Path baseDir, Set contents) { + this.baseDir = baseDir; + this.content = contents; + } + private static String format(Set paths) { + return Arrays.toString( + paths.stream().sorted().map(Path::toString).toArray( + String[]::new)); + } + private final Path baseDir; + private final Set content; + } + public static void assertStringListEquals(List expected, List actual, String msg) { currentTest.notifyAssert(); - traceAssert(String.format("assertStringListEquals(): %s", msg)); + traceAssert(concatMessages("assertStringListEquals()", msg)); String idxFieldFormat = Functional.identity(() -> { int listSize = expected.size(); @@ -757,7 +863,7 @@ public static void assertStringListEquals(List expected, expectedStr)); }); - if (expected.size() < actual.size()) { + if (actual.size() > expected.size()) { // Actual string list is longer than expected error(concatMessages(String.format( "Actual list is longer than expected by %d elements", @@ -767,12 +873,12 @@ public static void assertStringListEquals(List expected, if (actual.size() < expected.size()) { // Actual string list is shorter than expected error(concatMessages(String.format( - "Actual list is longer than expected by %d elements", + "Actual list is shorter than expected by %d elements", expected.size() - actual.size()), msg)); } } - public final static class TextStreamVerifier { + public static final class TextStreamVerifier { TextStreamVerifier(String value) { this.value = value; predicate(String::contains); @@ -835,7 +941,7 @@ public void apply(Stream lines) { private String label; private boolean negate; private Supplier createException; - final private String value; + private final String value; } public static TextStreamVerifier assertTextStream(String what) { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java index a19b12ebf2768..7a853ca83f1e5 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java @@ -28,9 +28,12 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.function.BiConsumer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.jpackage.test.Functional.ThrowingRunnable; @@ -61,28 +64,12 @@ private static Path getInstallationSubDirectory(JPackageCommand cmd) { return Path.of(cmd.getArgumentValue("--install-dir", cmd::name)); } - // Tests have problems on windows where path in the temp dir are too long - // for the wix tools. We can't use a tempDir outside the TKit's WorkDir, so - // we minimize both the tempRoot directory name (above) and the tempDir name - // (below) to the extension part (which is necessary to differenciate between - // the multiple PackageTypes that will be run for one JPackageCommand). - // It might be beter if the whole work dir name was shortened from: - // jtreg_open_test_jdk_tools_jpackage_share_jdk_jpackage_tests_BasicTest_java. - public static Path getTempDirectory(JPackageCommand cmd, Path tempRoot) { - String ext = cmd.outputBundle().getFileName().toString(); - int i = ext.lastIndexOf("."); - if (i > 0 && i < (ext.length() - 1)) { - ext = ext.substring(i+1); - } - return tempRoot.resolve(ext); - } - private static void runMsiexecWithRetries(Executor misexec) { Executor.Result result = null; for (int attempt = 0; attempt < 8; ++attempt) { result = misexec.executeWithoutExitCodeCheck(); - if (result.exitCode == 1605) { + if (result.exitCode() == 1605) { // ERROR_UNKNOWN_PRODUCT, attempt to uninstall not installed // package return; @@ -91,7 +78,7 @@ private static void runMsiexecWithRetries(Executor misexec) { // The given Executor may either be of an msiexec command or an // unpack.bat script containing the msiexec command. In the later // case, when misexec returns 1618, the unpack.bat may return 1603 - if ((result.exitCode == 1618) || (result.exitCode == 1603)) { + if ((result.exitCode() == 1618) || (result.exitCode() == 1603)) { // Another installation is already in progress. // Wait a little and try again. Long timeout = 1000L * (attempt + 3); // from 3 to 10 seconds @@ -181,6 +168,90 @@ public static String getMsiProperty(JPackageCommand cmd, String propertyName) { .executeAndGetOutput().stream().collect(Collectors.joining("\n")); } + public static void killProcess(long pid) { + Executor.of("taskkill", "/F", "/PID", Long.toString(pid)).dumpOutput(true).execute(); + } + + public static void killAppLauncherProcess(JPackageCommand cmd, + String launcherName, int expectedCount) { + var pids = findAppLauncherPIDs(cmd, launcherName); + try { + TKit.assertEquals(expectedCount, pids.length, String.format( + "Check [%d] %s app launcher processes found running", + expectedCount, Optional.ofNullable(launcherName).map( + str -> "[" + str + "]").orElse("
    "))); + } finally { + if (pids.length != 0) { + killProcess(pids[0]); + } + } + } + + private static long[] findAppLauncherPIDs(JPackageCommand cmd, String launcherName) { + // Get the list of PIDs and PPIDs of app launcher processes. + // wmic process where (name = "foo.exe") get ProcessID,ParentProcessID + List output = Executor.of("wmic", "process", "where", "(name", + "=", + "\"" + cmd.appLauncherPath(launcherName).getFileName().toString() + "\"", + ")", "get", "ProcessID,ParentProcessID").dumpOutput(true). + saveOutput().executeAndGetOutput(); + + if ("No Instance(s) Available.".equals(output.get(0).trim())) { + return new long[0]; + } + + String[] headers = Stream.of(output.get(0).split("\\s+", 2)).map( + String::trim).map(String::toLowerCase).toArray(String[]::new); + Pattern pattern; + if (headers[0].equals("parentprocessid") && headers[1].equals( + "processid")) { + pattern = Pattern.compile("^(?\\d+)\\s+(?\\d+)\\s+$"); + } else if (headers[1].equals("parentprocessid") && headers[0].equals( + "processid")) { + pattern = Pattern.compile("^(?\\d+)\\s+(?\\d+)\\s+$"); + } else { + throw new RuntimeException( + "Unrecognizable output of \'wmic process\' command"); + } + + List processes = output.stream().skip(1).map(line -> { + Matcher m = pattern.matcher(line); + long[] pids = null; + if (m.matches()) { + pids = new long[]{Long.parseLong(m.group("pid")), Long. + parseLong(m.group("ppid"))}; + } + return pids; + }).filter(Objects::nonNull).toList(); + + switch (processes.size()) { + case 2 -> { + final long parentPID; + final long childPID; + if (processes.get(0)[0] == processes.get(1)[1]) { + parentPID = processes.get(0)[0]; + childPID = processes.get(1)[0]; + } else if (processes.get(1)[0] == processes.get(0)[1]) { + parentPID = processes.get(1)[0]; + childPID = processes.get(0)[0]; + } else { + TKit.assertUnexpected("App launcher processes unrelated"); + return null; // Unreachable + } + return new long[]{parentPID, childPID}; + } + case 1 -> { + return new long[]{processes.get(0)[0]}; + } + default -> { + TKit.assertUnexpected(String.format( + "Unexpected number of running processes [%d]", + processes.size())); + return null; // Unreachable + } + } + } + private static boolean isUserLocalInstall(JPackageCommand cmd) { return cmd.hasArgument("--win-per-user-install"); } @@ -353,7 +424,7 @@ private static String queryRegistryValue(String keyPath, String valueName) { var status = Executor.of("reg", "query", keyPath, "/v", valueName) .saveOutput() .executeWithoutExitCodeCheck(); - if (status.exitCode == 1) { + if (status.exitCode() == 1) { // Should be the case of no such registry value or key String lookupString = "ERROR: The system was unable to find the specified registry key or value."; TKit.assertTextStream(lookupString) @@ -394,15 +465,15 @@ private static String queryRegistryValueCache(String keyPath, "bin\\server\\jvm.dll")); // jtreg resets %ProgramFiles% environment variable by some reason. - private final static Path PROGRAM_FILES = Path.of(Optional.ofNullable( + private static final Path PROGRAM_FILES = Path.of(Optional.ofNullable( System.getenv("ProgramFiles")).orElse("C:\\Program Files")); - private final static Path USER_LOCAL = Path.of(System.getProperty( + private static final Path USER_LOCAL = Path.of(System.getProperty( "user.home"), "AppData", "Local"); - private final static String SYSTEM_SHELL_FOLDERS_REGKEY = "HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"; - private final static String USER_SHELL_FOLDERS_REGKEY = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"; + private static final String SYSTEM_SHELL_FOLDERS_REGKEY = "HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"; + private static final String USER_SHELL_FOLDERS_REGKEY = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"; private static final Map REGISTRY_VALUES = new HashMap<>(); } diff --git a/test/jdk/tools/jpackage/linux/AppAboutUrlTest.java b/test/jdk/tools/jpackage/linux/AppAboutUrlTest.java index e3812b0fcee4b..0d8f0ec4805e1 100644 --- a/test/jdk/tools/jpackage/linux/AppAboutUrlTest.java +++ b/test/jdk/tools/jpackage/linux/AppAboutUrlTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,13 +44,12 @@ /* * @test * @summary jpackage with --about-url - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* * @build AppAboutUrlTest * @requires (os.family == "linux") * @requires (jpackage.test.SQETest == null) - * @modules jdk.jpackage/jdk.jpackage.internal * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main * --jpt-run=AppAboutUrlTest */ @@ -58,13 +57,12 @@ /* * @test * @summary jpackage with --about-url - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* * @build AppAboutUrlTest * @requires (os.family == "linux") * @requires (jpackage.test.SQETest != null) - * @modules jdk.jpackage/jdk.jpackage.internal * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main * --jpt-run=AppAboutUrlTest.test */ diff --git a/test/jdk/tools/jpackage/linux/AppCategoryTest.java b/test/jdk/tools/jpackage/linux/AppCategoryTest.java index d52b537604d8e..5fd34ab3bfd70 100644 --- a/test/jdk/tools/jpackage/linux/AppCategoryTest.java +++ b/test/jdk/tools/jpackage/linux/AppCategoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,12 +43,11 @@ /* * @test * @summary jpackage with --linux-app-category - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* * @build AppCategoryTest * @requires (os.family == "linux") - * @modules jdk.jpackage/jdk.jpackage.internal * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main * --jpt-run=AppCategoryTest */ diff --git a/test/jdk/tools/jpackage/linux/LicenseTypeTest.java b/test/jdk/tools/jpackage/linux/LicenseTypeTest.java index 5a8837a424e02..f949284112ddf 100644 --- a/test/jdk/tools/jpackage/linux/LicenseTypeTest.java +++ b/test/jdk/tools/jpackage/linux/LicenseTypeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,12 +38,11 @@ /* * @test * @summary jpackage with --linux-rpm-license-type - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* * @build LicenseTypeTest * @requires (os.family == "linux") - * @modules jdk.jpackage/jdk.jpackage.internal * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main * --jpt-run=LicenseTypeTest */ diff --git a/test/jdk/tools/jpackage/linux/LinuxBundleNameTest.java b/test/jdk/tools/jpackage/linux/LinuxBundleNameTest.java index 269f176b368a1..e39af0fb76536 100644 --- a/test/jdk/tools/jpackage/linux/LinuxBundleNameTest.java +++ b/test/jdk/tools/jpackage/linux/LinuxBundleNameTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,12 +43,11 @@ /* * @test * @summary jpackage with --linux-package-name - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* * @build LinuxBundleNameTest * @requires (os.family == "linux") - * @modules jdk.jpackage/jdk.jpackage.internal * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main * --jpt-run=LinuxBundleNameTest */ diff --git a/test/jdk/tools/jpackage/linux/LinuxResourceTest.java b/test/jdk/tools/jpackage/linux/LinuxResourceTest.java index 250b71cd8761e..37f2545d95f28 100644 --- a/test/jdk/tools/jpackage/linux/LinuxResourceTest.java +++ b/test/jdk/tools/jpackage/linux/LinuxResourceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,10 +33,9 @@ /* * @test * @summary jpackage with --resource-dir - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* * @requires (os.family == "linux") - * @modules jdk.jpackage/jdk.jpackage.internal * @compile LinuxResourceTest.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main * --jpt-run=LinuxResourceTest diff --git a/test/jdk/tools/jpackage/linux/LinuxWeirdOutputDirTest.java b/test/jdk/tools/jpackage/linux/LinuxWeirdOutputDirTest.java index 4ce3c1a461456..a30c172461103 100644 --- a/test/jdk/tools/jpackage/linux/LinuxWeirdOutputDirTest.java +++ b/test/jdk/tools/jpackage/linux/LinuxWeirdOutputDirTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,9 +30,8 @@ * @summary jpackage with values of --dest parameter breaking jpackage launcher * @requires (os.family == "linux") * @bug 8268974 - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile LinuxWeirdOutputDirTest.java * @run main/othervm/timeout=540 -Xmx512m jdk.jpackage.test.Main * --jpt-run=LinuxWeirdOutputDirTest diff --git a/test/jdk/tools/jpackage/linux/MaintainerTest.java b/test/jdk/tools/jpackage/linux/MaintainerTest.java index aac4317a26b99..a257a5ff92df9 100644 --- a/test/jdk/tools/jpackage/linux/MaintainerTest.java +++ b/test/jdk/tools/jpackage/linux/MaintainerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,12 +39,11 @@ /* * @test * @summary jpackage with --linux-deb-maintainer - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* * @build MaintainerTest * @requires (os.family == "linux") - * @modules jdk.jpackage/jdk.jpackage.internal * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main * --jpt-run=MaintainerTest */ diff --git a/test/jdk/tools/jpackage/linux/PackageDepsTest.java b/test/jdk/tools/jpackage/linux/PackageDepsTest.java index 457d8f01592c4..1a69582fc841e 100644 --- a/test/jdk/tools/jpackage/linux/PackageDepsTest.java +++ b/test/jdk/tools/jpackage/linux/PackageDepsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -46,11 +46,10 @@ /* * @test * @summary jpackage with --linux-package-deps - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* * @requires (os.family == "linux") - * @modules jdk.jpackage/jdk.jpackage.internal * @compile PackageDepsTest.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main * --jpt-run=PackageDepsTest diff --git a/test/jdk/tools/jpackage/linux/ReleaseTest.java b/test/jdk/tools/jpackage/linux/ReleaseTest.java index 0d9cfdcb5c62f..bccec49841005 100644 --- a/test/jdk/tools/jpackage/linux/ReleaseTest.java +++ b/test/jdk/tools/jpackage/linux/ReleaseTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,12 +42,11 @@ /* * @test * @summary jpackage with --linux-app-release - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* * @build ReleaseTest * @requires (os.family == "linux") - * @modules jdk.jpackage/jdk.jpackage.internal * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main * --jpt-run=ReleaseTest */ diff --git a/test/jdk/tools/jpackage/linux/ShortcutHintTest.java b/test/jdk/tools/jpackage/linux/ShortcutHintTest.java index 812a194133309..01e890751388a 100644 --- a/test/jdk/tools/jpackage/linux/ShortcutHintTest.java +++ b/test/jdk/tools/jpackage/linux/ShortcutHintTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -53,12 +53,11 @@ /* * @test * @summary jpackage with --linux-shortcut - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @requires jpackage.test.SQETest == null * @build jdk.jpackage.test.* * @requires (os.family == "linux") - * @modules jdk.jpackage/jdk.jpackage.internal * @compile ShortcutHintTest.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main * --jpt-run=ShortcutHintTest @@ -67,12 +66,11 @@ /* * @test * @summary jpackage with --linux-shortcut - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* * @requires (os.family == "linux") * @requires jpackage.test.SQETest != null - * @modules jdk.jpackage/jdk.jpackage.internal * @compile ShortcutHintTest.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main * --jpt-run=ShortcutHintTest.testBasic diff --git a/test/jdk/tools/jpackage/linux/jdk/jpackage/tests/UsrTreeTest.java b/test/jdk/tools/jpackage/linux/UsrTreeTest.java similarity index 96% rename from test/jdk/tools/jpackage/linux/jdk/jpackage/tests/UsrTreeTest.java rename to test/jdk/tools/jpackage/linux/UsrTreeTest.java index 13237cc5aa3da..819ea8f245a12 100644 --- a/test/jdk/tools/jpackage/linux/jdk/jpackage/tests/UsrTreeTest.java +++ b/test/jdk/tools/jpackage/linux/UsrTreeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,12 +40,11 @@ /* * @test * @summary jpackage command run installing app in /usr directory tree - * @library ../../../../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @requires jpackage.test.SQETest == null * @requires (os.family == "linux") * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile UsrTreeTest.java * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main * --jpt-run=UsrTreeTest diff --git a/test/jdk/tools/jpackage/macosx/ArgumentsFilteringTest.java b/test/jdk/tools/jpackage/macosx/ArgumentsFilteringTest.java index 44dcecddc36ab..9b57cd7fbe56e 100644 --- a/test/jdk/tools/jpackage/macosx/ArgumentsFilteringTest.java +++ b/test/jdk/tools/jpackage/macosx/ArgumentsFilteringTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,9 +21,6 @@ * questions. */ -import java.nio.file.Path; -import java.util.List; -import java.util.ArrayList; import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.HelloApp; import jdk.jpackage.test.Annotations.Test; @@ -39,9 +36,8 @@ /* * @test * @summary jpackage with -psn - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile ArgumentsFilteringTest.java * @requires (os.family == "mac") * @run main/othervm/timeout=540 -Xmx512m jdk.jpackage.test.Main @@ -53,10 +49,11 @@ public class ArgumentsFilteringTest { public void test1() { JPackageCommand cmd = JPackageCommand.helloAppImage(); cmd.executeAndAssertHelloAppImageCreated(); - Path launcherPath = cmd.appLauncherPath(); - HelloApp.assertApp(launcherPath) - .executeAndVerifyOutput(false, List.of("-psn_1_1"), - new ArrayList<>()); + var appVerifier = HelloApp.assertMainLauncher(cmd); + if (appVerifier != null) { + appVerifier.execute("-psn_1_1"); + appVerifier.verifyOutput(); + } } @Test @@ -64,9 +61,10 @@ public void test2() { JPackageCommand cmd = JPackageCommand.helloAppImage() .addArguments("--arguments", "-psn_2_2"); cmd.executeAndAssertHelloAppImageCreated(); - Path launcherPath = cmd.appLauncherPath(); - HelloApp.assertApp(launcherPath) - .executeAndVerifyOutput(false, List.of("-psn_1_1"), - List.of("-psn_2_2")); + var appVerifier = HelloApp.assertMainLauncher(cmd); + if (appVerifier != null) { + appVerifier.execute("-psn_1_1"); + appVerifier.verifyOutput("-psn_2_2"); + } } } diff --git a/test/jdk/tools/jpackage/macosx/HostArchPkgTest.java b/test/jdk/tools/jpackage/macosx/HostArchPkgTest.java index 7d510c0a81971..5829aba23cc11 100644 --- a/test/jdk/tools/jpackage/macosx/HostArchPkgTest.java +++ b/test/jdk/tools/jpackage/macosx/HostArchPkgTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,9 +42,8 @@ /* * @test * @summary jpackage test to validate "hostArchitectures" attribute - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile HostArchPkgTest.java * @requires (os.family == "mac") * @key jpackagePlatformPackage diff --git a/test/jdk/tools/jpackage/macosx/MacAppStoreJlinkOptionsTest.java b/test/jdk/tools/jpackage/macosx/MacAppStoreJlinkOptionsTest.java index 8b1cec04ab9c7..1501fd15d8fdf 100644 --- a/test/jdk/tools/jpackage/macosx/MacAppStoreJlinkOptionsTest.java +++ b/test/jdk/tools/jpackage/macosx/MacAppStoreJlinkOptionsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,11 +33,9 @@ /* * @test * @summary jpackage with --mac-app-store and --jlink-options - * @library ../helpers - * @library /test/lib + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* * @build MacAppStoreJLinkOptionsTest - * @modules jdk.jpackage/jdk.jpackage.internal * @requires (os.family == "mac") * @run main/othervm/timeout=540 -Xmx512m jdk.jpackage.test.Main * --jpt-run=MacAppStoreJLinkOptionsTest diff --git a/test/jdk/tools/jpackage/macosx/MacAppStoreRuntimeTest.java b/test/jdk/tools/jpackage/macosx/MacAppStoreRuntimeTest.java index ea2cee77b4ef9..ab8b8590b33de 100644 --- a/test/jdk/tools/jpackage/macosx/MacAppStoreRuntimeTest.java +++ b/test/jdk/tools/jpackage/macosx/MacAppStoreRuntimeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,11 +43,9 @@ /* * @test * @summary jpackage with --mac-app-store and --runtime-image - * @library ../helpers - * @library /test/lib + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* * @build MacAppStoreRuntimeTest - * @modules jdk.jpackage/jdk.jpackage.internal * @requires (os.family == "mac") * @run main/othervm -Xmx512m jdk.jpackage.test.Main * --jpt-run=MacAppStoreRuntimeTest diff --git a/test/jdk/tools/jpackage/macosx/MacFileAssociationsTest.java b/test/jdk/tools/jpackage/macosx/MacFileAssociationsTest.java index 0bf972cf51be0..01c9e48b5fde4 100644 --- a/test/jdk/tools/jpackage/macosx/MacFileAssociationsTest.java +++ b/test/jdk/tools/jpackage/macosx/MacFileAssociationsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,10 +40,9 @@ /* * @test * @summary jpackage with --file-associations and mac specific file association args - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* * @build MacFileAssociationsTest - * @modules jdk.jpackage/jdk.jpackage.internal * @requires (os.family == "mac") * @run main/othervm -Xmx512m jdk.jpackage.test.Main * --jpt-run=MacFileAssociationsTest diff --git a/test/jdk/tools/jpackage/macosx/MacPropertiesTest.java b/test/jdk/tools/jpackage/macosx/MacPropertiesTest.java index 9d4324e22227f..5482a3ea508ce 100644 --- a/test/jdk/tools/jpackage/macosx/MacPropertiesTest.java +++ b/test/jdk/tools/jpackage/macosx/MacPropertiesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,10 +35,9 @@ /* * @test * @summary jpackage with --mac-package-name, --mac-package-identifier - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* * @requires (os.family == "mac") - * @modules jdk.jpackage/jdk.jpackage.internal * @compile MacPropertiesTest.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main * --jpt-run=MacPropertiesTest diff --git a/test/jdk/tools/jpackage/macosx/NameWithSpaceTest.java b/test/jdk/tools/jpackage/macosx/NameWithSpaceTest.java index ac505e5ca8100..ea95ad964f381 100644 --- a/test/jdk/tools/jpackage/macosx/NameWithSpaceTest.java +++ b/test/jdk/tools/jpackage/macosx/NameWithSpaceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,9 +38,8 @@ /* * @test * @summary jpackage test with name containing spaces - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile NameWithSpaceTest.java * @requires (os.family == "mac") * @key jpackagePlatformPackage diff --git a/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java b/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java index fab782c843fb9..5fcc01ad498e7 100644 --- a/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -46,7 +46,7 @@ /* * @test * @summary jpackage with --type app-image --mac-sign - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @library /test/lib * @library base * @build SigningBase @@ -54,7 +54,6 @@ * @build jtreg.SkippedException * @build jdk.jpackage.test.* * @build SigningAppImageTest - * @modules jdk.jpackage/jdk.jpackage.internal * @requires (os.family == "mac") * @run main/othervm -Xmx512m jdk.jpackage.test.Main * --jpt-run=SigningAppImageTest diff --git a/test/jdk/tools/jpackage/macosx/SigningPackageTest.java b/test/jdk/tools/jpackage/macosx/SigningPackageTest.java index 351e354357b72..b9a3c052b285c 100644 --- a/test/jdk/tools/jpackage/macosx/SigningPackageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningPackageTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -46,7 +46,7 @@ /* * @test * @summary jpackage with --type pkg,dmg --mac-sign - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @library /test/lib * @library base * @key jpackagePlatformPackage @@ -55,7 +55,6 @@ * @build jtreg.SkippedException * @build jdk.jpackage.test.* * @build SigningPackageTest - * @modules jdk.jpackage/jdk.jpackage.internal * @requires (os.family == "mac") * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main * --jpt-run=SigningPackageTest diff --git a/test/jdk/tools/jpackage/resources/Win8365790Test.ps1 b/test/jdk/tools/jpackage/resources/Win8365790Test.ps1 new file mode 100644 index 0000000000000..3a7d8c9a90ba3 --- /dev/null +++ b/test/jdk/tools/jpackage/resources/Win8365790Test.ps1 @@ -0,0 +1,83 @@ +# +# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +param ( + # Path to executable to start. + [Parameter(Mandatory=$true)] + [string]$Executable, + + # Timeout to wait after the executable has been started. + [Parameter(Mandatory=$true)] + [double]$TimeoutSeconds +) + +$type = @{ + TypeDefinition = @' +using System; +using System.Runtime.InteropServices; + +namespace Stuff { + + internal struct Details { + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool GenerateConsoleCtrlEvent(uint dwCtrlEvent, uint dwProcessGroupId); + } + + public struct Facade { + public static void GenerateConsoleCtrlEvent() { + if (!Details.GenerateConsoleCtrlEvent(0, 0)) { + reportLastErrorAndExit("GenerateConsoleCtrlEvent"); + } + } + + internal static void reportLastErrorAndExit(String func) { + int errorCode = Marshal.GetLastWin32Error(); + Console.Error.WriteLine(func + " function failed with error code: " + errorCode); + Environment.Exit(100); + } + } +} +'@ +} +Add-Type @type + +Set-PSDebug -Trace 2 + +# Launch the target executable. +# `-NoNewWindow` parameter will attach the started process to the existing console. +$childProc = Start-Process -PassThru -NoNewWindow $Executable + +# Wait a bit to let the started process complete initialization. +Start-Sleep -Seconds $TimeoutSeconds + +# Call GenerateConsoleCtrlEvent to send a CTRL+C event to the launched executable. +# CTRL+C event will be sent to all processes attached to the console of the current process, +# i.e., it will be sent to this PowerShell process and to the started $Executable process because +# it was configured to attach to the existing console (the console of this PowerShell process). +[Stuff.Facade]::GenerateConsoleCtrlEvent() + +# Wait for child process termination +Wait-Process -InputObject $childProc + +Exit 0 diff --git a/test/jdk/tools/jpackage/run_tests.sh b/test/jdk/tools/jpackage/run_tests.sh index 2639f38cd6816..55e46fc7e3488 100644 --- a/test/jdk/tools/jpackage/run_tests.sh +++ b/test/jdk/tools/jpackage/run_tests.sh @@ -29,7 +29,7 @@ # Fail fast set -e; set -o pipefail; -# $JT_BUNDLE_URL (Link can be obtained from https://openjdk.java.net/jtreg/ page) +# $JT_BUNDLE_URL (Link can be obtained from https://openjdk.org/jtreg/ page) jtreg_bundle=$JT_BUNDLE_URL workdir=/tmp/jpackage_jtreg_testing jtreg_jar=$workdir/jtreg/lib/jtreg.jar @@ -231,7 +231,7 @@ fi if [ -z "$JT_HOME" ]; then if [ -z "$JT_BUNDLE_URL" ]; then - fatal 'JT_HOME or JT_BUNDLE_URL environment variable is not set. Link to JTREG bundle can be found at https://openjdk.java.net/jtreg/'. + fatal 'JT_HOME or JT_BUNDLE_URL environment variable is not set. Link to JTREG bundle can be found at https://openjdk.org/jtreg/'. fi fi diff --git a/test/jdk/tools/jpackage/share/AddLauncherTest.java b/test/jdk/tools/jpackage/share/AddLauncherTest.java index d1eca67b336d4..29008896dd0b6 100644 --- a/test/jdk/tools/jpackage/share/AddLauncherTest.java +++ b/test/jdk/tools/jpackage/share/AddLauncherTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,7 +22,6 @@ */ import java.nio.file.Path; -import java.io.File; import java.util.Map; import java.lang.invoke.MethodHandles; import jdk.jpackage.test.PackageTest; @@ -48,11 +47,12 @@ * @summary jpackage with --add-launcher * @key jpackagePlatformPackage * @requires (jpackage.test.SQETest != null) - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile AddLauncherTest.java - * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * @run main/othervm/timeout=360 -Xmx512m + * --add-opens jdk.jpackage/jdk.jpackage.internal=ALL-UNNAMED + * jdk.jpackage.test.Main * --jpt-run=AddLauncherTest.test */ @@ -61,11 +61,12 @@ * @summary jpackage with --add-launcher * @key jpackagePlatformPackage * @requires (jpackage.test.SQETest == null) - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile AddLauncherTest.java - * @run main/othervm/timeout=540 -Xmx512m jdk.jpackage.test.Main + * @run main/othervm/timeout=540 -Xmx512m + * --add-opens jdk.jpackage/jdk.jpackage.internal=ALL-UNNAMED + * jdk.jpackage.test.Main * --jpt-run=AddLauncherTest */ @@ -225,11 +226,11 @@ public void testMainLauncherIsModular(boolean mainLauncherIsModular) { TKit.assertEquals(ExpectedCN, mainClass, String.format("Check value of app.mainclass=[%s]" + "in NonModularAppLauncher cfg file is as expected", ExpectedCN)); - TKit.assertTrue(classpath.startsWith("$APPDIR" + File.separator - + nonModularAppDesc.jarFileName()), + TKit.assertTrue(classpath.startsWith(Path.of("$APPDIR", + nonModularAppDesc.jarFileName()).toString()), "Check app.classpath value in ModularAppLauncher cfg file"); } - private final static Path GOLDEN_ICON = TKit.TEST_SRC_ROOT.resolve(Path.of( + private static final Path GOLDEN_ICON = TKit.TEST_SRC_ROOT.resolve(Path.of( "resources", "icon" + TKit.ICON_SUFFIX)); } diff --git a/test/jdk/tools/jpackage/share/AppImagePackageTest.java b/test/jdk/tools/jpackage/share/AppImagePackageTest.java index ae9f6a21ffcc7..6e71b45e6fa2c 100644 --- a/test/jdk/tools/jpackage/share/AppImagePackageTest.java +++ b/test/jdk/tools/jpackage/share/AppImagePackageTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,10 +42,9 @@ * @test * @summary jpackage with --app-image * @key jpackagePlatformPackage - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @requires (jpackage.test.SQETest == null) * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile AppImagePackageTest.java * @run main/othervm/timeout=540 -Xmx512m jdk.jpackage.test.Main * --jpt-run=AppImagePackageTest diff --git a/test/jdk/tools/jpackage/share/AppLauncherEnvTest.java b/test/jdk/tools/jpackage/share/AppLauncherEnvTest.java index cc52b285fbe37..772370b0f8c05 100644 --- a/test/jdk/tools/jpackage/share/AppLauncherEnvTest.java +++ b/test/jdk/tools/jpackage/share/AppLauncherEnvTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,7 @@ import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.Executor; +import static jdk.jpackage.test.HelloApp.configureAndExecute; import jdk.jpackage.test.TKit; /** @@ -39,12 +40,9 @@ /* * @test * @summary Tests values of environment variables altered by jpackage launcher - * @library ../helpers - * @library /test/lib - * @build AppLauncherEnvTest + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* * @build AppLauncherEnvTest - * @modules jdk.jpackage/jdk.jpackage.internal * @run main/othervm -Xmx512m jdk.jpackage.test.Main * --jpt-run=AppLauncherEnvTest */ @@ -56,6 +54,7 @@ public static void test() throws Exception { JPackageCommand cmd = JPackageCommand .helloAppImage(TEST_APP_JAVA + "*Hello") + .ignoreFakeRuntime() .addArguments("--java-options", "-D" + testAddDirProp + "=$APPDIR"); @@ -63,16 +62,12 @@ public static void test() throws Exception { final String envVarName = envVarName(); - final int attempts = 3; - final int waitBetweenAttemptsSeconds = 5; - List output = new Executor() + List output = configureAndExecute(0, new Executor() .saveOutput() .setExecutable(cmd.appLauncherPath().toAbsolutePath()) .addArguments("--print-env-var=" + envVarName) .addArguments("--print-sys-prop=" + testAddDirProp) - .addArguments("--print-sys-prop=" + "java.library.path") - .executeAndRepeatUntilExitCode(0, attempts, - waitBetweenAttemptsSeconds).getOutput(); + .addArguments("--print-sys-prop=" + "java.library.path")).getOutput(); BiFunction getValue = (idx, name) -> { return output.get(idx).substring((name + "=").length()); diff --git a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/AppVersionTest.java b/test/jdk/tools/jpackage/share/AppVersionTest.java similarity index 95% rename from test/jdk/tools/jpackage/share/jdk/jpackage/tests/AppVersionTest.java rename to test/jdk/tools/jpackage/share/AppVersionTest.java index f137fabf3d629..efd025590d28a 100644 --- a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/AppVersionTest.java +++ b/test/jdk/tools/jpackage/share/AppVersionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,7 +21,6 @@ * questions. */ -package jdk.jpackage.tests; import java.io.IOException; import java.util.Collection; @@ -42,12 +41,11 @@ /* * @test * @summary jpackage application version testing - * @library ../../../../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile AppVersionTest.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=jdk.jpackage.tests.AppVersionTest + * --jpt-run=AppVersionTest */ public final class AppVersionTest { diff --git a/test/jdk/tools/jpackage/share/ArgumentsTest.java b/test/jdk/tools/jpackage/share/ArgumentsTest.java index a628d407c5c3e..3be6c36a25c89 100644 --- a/test/jdk/tools/jpackage/share/ArgumentsTest.java +++ b/test/jdk/tools/jpackage/share/ArgumentsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,6 @@ import java.nio.file.Path; import java.util.List; import jdk.jpackage.test.HelloApp; -import jdk.jpackage.test.Functional.ThrowingConsumer; import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.Annotations.BeforeEach; import jdk.jpackage.test.Annotations.Test; @@ -47,9 +46,8 @@ /* * @test * @summary jpackage create image with --arguments test - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile ArgumentsTest.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main * --jpt-run=ArgumentsTest @@ -65,27 +63,19 @@ public static void useJPackageToolProvider() { @Parameter("Goodbye") @Parameter("com.hello/com.hello.Hello") public static void testApp(String javaAppDesc) { - testIt(javaAppDesc, null); - } - - private static void testIt(String javaAppDesc, - ThrowingConsumer initializer) { - JPackageCommand cmd = JPackageCommand.helloAppImage(javaAppDesc).addArguments( "--arguments", JPackageCommand.escapeAndJoin(TRICKY_ARGUMENTS)); - if (initializer != null) { - ThrowingConsumer.toConsumer(initializer).accept(cmd); - } cmd.executeAndAssertImageCreated(); - Path launcherPath = cmd.appLauncherPath(); - if (!cmd.isFakeRuntime(String.format( - "Not running [%s] launcher", launcherPath))) { - HelloApp.assertApp(launcherPath) - .addDefaultArguments(TRICKY_ARGUMENTS) - .executeAndVerifyOutput(); + if (!cmd.canRunLauncher("Not running the test")) { + return; } + + Path launcherPath = cmd.appLauncherPath(); + HelloApp.assertApp(launcherPath) + .addDefaultArguments(TRICKY_ARGUMENTS) + .executeAndVerifyOutput(); } private final static List TRICKY_ARGUMENTS = List.of( diff --git a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/BasicTest.java b/test/jdk/tools/jpackage/share/BasicTest.java similarity index 94% rename from test/jdk/tools/jpackage/share/jdk/jpackage/tests/BasicTest.java rename to test/jdk/tools/jpackage/share/BasicTest.java index 72550fd365ba7..0b967373dc9c9 100644 --- a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/BasicTest.java +++ b/test/jdk/tools/jpackage/share/BasicTest.java @@ -21,7 +21,6 @@ * questions. */ -package jdk.jpackage.tests; import java.io.IOException; import java.nio.file.Files; @@ -42,17 +41,14 @@ import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.Annotations.Parameter; -import static jdk.jpackage.test.WindowsHelper.getTempDirectory; - /* * @test * @summary jpackage basic testing - * @library ../../../../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile BasicTest.java * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=jdk.jpackage.tests.BasicTest + * --jpt-run=BasicTest */ public final class BasicTest { @@ -76,7 +72,7 @@ public void testJpackageProps() { .saveConsoleOutput(true) .addArguments("--app-version", appVersion, "--arguments", "jpackage.app-version jpackage.app-path") - .ignoreDefaultRuntime(true); + .ignoreFakeRuntime(); cmd.executeAndAssertImageCreated(); Path launcherPath = cmd.appLauncherPath(); @@ -286,7 +282,7 @@ public void testTemp(TestTempType type) throws IOException { // Force save of package bundle in test work directory. .addInitializer(JPackageCommand::setDefaultInputOutput) .addInitializer(cmd -> { - Path tempDir = getTempDirectory(cmd, tempRoot); + Path tempDir = tempRoot.resolve(cmd.packageType().name()); switch (type) { case TEMPDIR_EMPTY -> Files.createDirectories(tempDir); case TEMPDIR_NOT_EXIST -> Files.createDirectories(tempDir.getParent()); @@ -303,20 +299,16 @@ public void testTemp(TestTempType type) throws IOException { if (TestTempType.TEMPDIR_NOT_EMPTY.equals(type)) { pkgTest.setExpectedExitCode(1).addBundleVerifier(cmd -> { // Check jpackage didn't use the supplied directory. - Path tempDir = getTempDirectory(cmd, tempRoot); - String[] tempDirContents = tempDir.toFile().list(); - TKit.assertStringListEquals(List.of("foo.txt"), List.of( - tempDirContents), String.format( - "Check the contents of the supplied temporary directory [%s]", - tempDir)); + Path tempDir = Path.of(cmd.getArgumentValue("--temp")); + TKit.assertDirectoryContent(tempDir).match(Path.of("foo.txt")); TKit.assertStringListEquals(List.of("Hello Duke!"), - Files.readAllLines(tempDir.resolve(tempDirContents[0])), + Files.readAllLines(tempDir.resolve("foo.txt")), "Check the contents of the file in the supplied temporary directory"); }); } else { pkgTest.addBundleVerifier(cmd -> { // Check jpackage used the supplied directory. - Path tempDir = getTempDirectory(cmd, tempRoot); + Path tempDir = Path.of(cmd.getArgumentValue("--temp")); TKit.assertDirectoryNotEmpty(tempDir); }); } diff --git a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/CookedRuntimeTest.java b/test/jdk/tools/jpackage/share/CookedRuntimeTest.java similarity index 95% rename from test/jdk/tools/jpackage/share/jdk/jpackage/tests/CookedRuntimeTest.java rename to test/jdk/tools/jpackage/share/CookedRuntimeTest.java index f61fff69fe63a..e8bd034bfb4b1 100644 --- a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/CookedRuntimeTest.java +++ b/test/jdk/tools/jpackage/share/CookedRuntimeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,7 +21,6 @@ * questions. */ -package jdk.jpackage.tests; import java.io.IOException; import java.nio.file.Files; @@ -42,12 +41,11 @@ /* * @test * @summary test '--runtime-image' option of jpackage - * @library ../../../../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile CookedRuntimeTest.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=jdk.jpackage.tests.CookedRuntimeTest + * --jpt-run=CookedRuntimeTest */ public final class CookedRuntimeTest { diff --git a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/DotInNameTest.java b/test/jdk/tools/jpackage/share/DotInNameTest.java similarity index 89% rename from test/jdk/tools/jpackage/share/jdk/jpackage/tests/DotInNameTest.java rename to test/jdk/tools/jpackage/share/DotInNameTest.java index 87c9381bd2d90..2617db26183e7 100644 --- a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/DotInNameTest.java +++ b/test/jdk/tools/jpackage/share/DotInNameTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,7 +21,6 @@ * questions. */ -package jdk.jpackage.tests; import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.HelloApp; @@ -33,12 +32,11 @@ /* * @test * @summary jpackage create image with --java-options test - * @library ../../../../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile DotInNameTest.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=jdk.jpackage.tests.DotInNameTest + * --jpt-run=DotInNameTest * --jpt-before-run=jdk.jpackage.test.JPackageCommand.useToolProviderByDefault */ diff --git a/test/jdk/tools/jpackage/share/EmptyFolderTest.java b/test/jdk/tools/jpackage/share/EmptyFolderTest.java index 230d8a039ea8d..40a4db03d6f69 100644 --- a/test/jdk/tools/jpackage/share/EmptyFolderTest.java +++ b/test/jdk/tools/jpackage/share/EmptyFolderTest.java @@ -37,12 +37,10 @@ /* * @test * @summary jpackage for package with input containing empty folders - * @library ../helpers - * @library /test/lib + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* * @build EmptyFolderTest - * @modules jdk.jpackage/jdk.jpackage.internal * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main * --jpt-run=EmptyFolderTest.testPackage */ @@ -50,11 +48,9 @@ /* * @test * @summary jpackage for app image with input containing empty folders - * @library ../helpers - * @library /test/lib + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* * @build EmptyFolderTest - * @modules jdk.jpackage/jdk.jpackage.internal * @run main/othervm -Xmx512m jdk.jpackage.test.Main * --jpt-run=EmptyFolderTest.testAppImage */ diff --git a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/ErrorTest.java b/test/jdk/tools/jpackage/share/ErrorTest.java similarity index 92% rename from test/jdk/tools/jpackage/share/jdk/jpackage/tests/ErrorTest.java rename to test/jdk/tools/jpackage/share/ErrorTest.java index 8fcd3c8a73057..f012eac1ced95 100644 --- a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/ErrorTest.java +++ b/test/jdk/tools/jpackage/share/ErrorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,7 +21,6 @@ * questions. */ -package jdk.jpackage.tests; import java.util.Collection; import java.util.List; @@ -33,24 +32,22 @@ /* * @test * @summary Test jpackage output for erroneous input - * @library ../../../../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile ErrorTest.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=jdk.jpackage.tests.ErrorTest + * --jpt-run=ErrorTest * --jpt-before-run=jdk.jpackage.test.JPackageCommand.useExecutableByDefault */ /* * @test * @summary Test jpackage output for erroneous input - * @library ../../../../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile ErrorTest.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=jdk.jpackage.tests.ErrorTest + * --jpt-run=ErrorTest * --jpt-before-run=jdk.jpackage.test.JPackageCommand.useToolProviderByDefault */ diff --git a/test/jdk/tools/jpackage/share/FileAssociationsTest.java b/test/jdk/tools/jpackage/share/FileAssociationsTest.java index 52dd20f9297b1..2eebeff3981da 100644 --- a/test/jdk/tools/jpackage/share/FileAssociationsTest.java +++ b/test/jdk/tools/jpackage/share/FileAssociationsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -57,11 +57,10 @@ /* * @test * @summary jpackage with --file-associations - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @requires jpackage.test.SQETest == null * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile FileAssociationsTest.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main * --jpt-run=FileAssociationsTest @@ -70,11 +69,10 @@ /* * @test * @summary jpackage with --file-associations - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @requires jpackage.test.SQETest != null * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile FileAssociationsTest.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main * --jpt-run=FileAssociationsTest.test diff --git a/test/jdk/tools/jpackage/share/IconTest.java b/test/jdk/tools/jpackage/share/IconTest.java index ff57d8aea276b..c2a70022db535 100644 --- a/test/jdk/tools/jpackage/share/IconTest.java +++ b/test/jdk/tools/jpackage/share/IconTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -49,11 +49,12 @@ /* * @test * @summary jpackage create image and package with custom icons for the main and additional launcher - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile IconTest.java - * @run main/othervm/timeout=540 -Xmx512m jdk.jpackage.test.Main + * @run main/othervm/timeout=540 -Xmx512m + * --add-opens jdk.jpackage/jdk.jpackage.internal=ALL-UNNAMED + * jdk.jpackage.test.Main * --jpt-run=IconTest */ diff --git a/test/jdk/tools/jpackage/share/InstallDirTest.java b/test/jdk/tools/jpackage/share/InstallDirTest.java index 1f969149c688e..7047f35e87bf0 100644 --- a/test/jdk/tools/jpackage/share/InstallDirTest.java +++ b/test/jdk/tools/jpackage/share/InstallDirTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -54,11 +54,10 @@ /* * @test * @summary jpackage with --install-dir - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* * @compile InstallDirTest.java - * @modules jdk.jpackage/jdk.jpackage.internal * @run main/othervm/timeout=540 -Xmx512m jdk.jpackage.test.Main * --jpt-run=InstallDirTest.testCommon */ @@ -66,11 +65,10 @@ /* * @test * @summary jpackage with --install-dir - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* * @compile InstallDirTest.java - * @modules jdk.jpackage/jdk.jpackage.internal * @requires (os.family == "linux") * @requires (jpackage.test.SQETest == null) * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main diff --git a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/JLinkOptionsTest.java b/test/jdk/tools/jpackage/share/JLinkOptionsTest.java similarity index 95% rename from test/jdk/tools/jpackage/share/jdk/jpackage/tests/JLinkOptionsTest.java rename to test/jdk/tools/jpackage/share/JLinkOptionsTest.java index 5f97df482bf23..9885c69c55452 100644 --- a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/JLinkOptionsTest.java +++ b/test/jdk/tools/jpackage/share/JLinkOptionsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,7 +21,6 @@ * questions. */ -package jdk.jpackage.tests; import java.util.Collection; import java.util.List; @@ -33,12 +32,11 @@ /* * @test * @summary jpackage application version testing - * @library ../../../../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile JLinkOptionsTest.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=jdk.jpackage.tests.JLinkOptionsTest + * --jpt-run=JLinkOptionsTest */ public final class JLinkOptionsTest { diff --git a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/JavaOptionsEqualsTest.java b/test/jdk/tools/jpackage/share/JavaOptionsEqualsTest.java similarity index 89% rename from test/jdk/tools/jpackage/share/jdk/jpackage/tests/JavaOptionsEqualsTest.java rename to test/jdk/tools/jpackage/share/JavaOptionsEqualsTest.java index c63892a2ca060..b1f69c6395f14 100644 --- a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/JavaOptionsEqualsTest.java +++ b/test/jdk/tools/jpackage/share/JavaOptionsEqualsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,7 +21,6 @@ * questions. */ -package jdk.jpackage.tests; import java.util.Collection; import java.util.List; @@ -35,24 +34,22 @@ /* * @test * @summary jpackage create image with --java-options test - * @library ../../../../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile JavaOptionsEqualsTest.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=jdk.jpackage.tests.JavaOptionsEqualsTest + * --jpt-run=JavaOptionsEqualsTest * --jpt-before-run=jdk.jpackage.test.JPackageCommand.useExecutableByDefault */ /* * @test * @summary jpackage create image with --java-options test - * @library ../../../../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile JavaOptionsEqualsTest.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=jdk.jpackage.tests.JavaOptionsEqualsTest + * --jpt-run=JavaOptionsEqualsTest * --jpt-before-run=jdk.jpackage.test.JPackageCommand.useToolProviderByDefault */ diff --git a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/JavaOptionsTest.java b/test/jdk/tools/jpackage/share/JavaOptionsTest.java similarity index 94% rename from test/jdk/tools/jpackage/share/jdk/jpackage/tests/JavaOptionsTest.java rename to test/jdk/tools/jpackage/share/JavaOptionsTest.java index ab2f7a3d15d50..befc90accea13 100644 --- a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/JavaOptionsTest.java +++ b/test/jdk/tools/jpackage/share/JavaOptionsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,7 +21,6 @@ * questions. */ -package jdk.jpackage.tests; import java.util.Collection; import java.util.List; @@ -35,12 +34,11 @@ /* * @test * @summary jpackage create image with --java-options test - * @library ../../../../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile JavaOptionsTest.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=jdk.jpackage.tests.JavaOptionsTest + * --jpt-run=JavaOptionsTest * --jpt-before-run=jdk.jpackage.test.JPackageCommand.useToolProviderByDefault */ diff --git a/test/jdk/tools/jpackage/share/LicenseTest.java b/test/jdk/tools/jpackage/share/LicenseTest.java index 97e9cd1bfdd8f..205c42d42b859 100644 --- a/test/jdk/tools/jpackage/share/LicenseTest.java +++ b/test/jdk/tools/jpackage/share/LicenseTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -63,11 +63,10 @@ /* * @test * @summary jpackage with --license-file - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* * @compile LicenseTest.java - * @modules jdk.jpackage/jdk.jpackage.internal * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main * --jpt-run=LicenseTest.testCommon */ @@ -75,13 +74,12 @@ /* * @test * @summary jpackage with --license-file - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* * @compile LicenseTest.java * @requires (os.family == "linux") * @requires (jpackage.test.SQETest == null) - * @modules jdk.jpackage/jdk.jpackage.internal * @run main/othervm/timeout=1440 -Xmx512m jdk.jpackage.test.Main * --jpt-run=LicenseTest.testCustomDebianCopyright * --jpt-run=LicenseTest.testCustomDebianCopyrightSubst diff --git a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/MainClassTest.java b/test/jdk/tools/jpackage/share/MainClassTest.java similarity index 91% rename from test/jdk/tools/jpackage/share/jdk/jpackage/tests/MainClassTest.java rename to test/jdk/tools/jpackage/share/MainClassTest.java index 2a14a4ea115e4..53af9769ff3a8 100644 --- a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/MainClassTest.java +++ b/test/jdk/tools/jpackage/share/MainClassTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,7 +21,6 @@ * questions. */ -package jdk.jpackage.tests; import java.io.IOException; import java.nio.file.Files; @@ -45,18 +44,17 @@ import jdk.jpackage.test.Annotations.Parameters; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.Functional.ThrowingConsumer; -import static jdk.jpackage.tests.MainClassTest.Script.MainClassType.*; + /* * @test * @summary test different settings of main class name for jpackage - * @library ../../../../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile MainClassTest.java * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=jdk.jpackage.tests.MainClassTest + * --jpt-run=MainClassTest */ public final class MainClassTest { @@ -82,7 +80,7 @@ Script withMainClass(MainClassType v) { } Script withJarMainClass(MainClassType v) { - appDesc.setWithMainClass(v != NotSet); + appDesc.setWithMainClass(v != MainClassType.NotSet); jarMainClass = v; return this; } @@ -172,7 +170,8 @@ public MainClassTest(Script script) { @Parameters public static Collection scripts() { - final var withMainClass = Set.of(SetWrong, SetRight); + final var withMainClass = Set.of(Script.MainClassType.SetWrong, + Script.MainClassType.SetRight); List scripts = new ArrayList<>(); for (var withJLink : List.of(true, false)) { @@ -205,7 +204,7 @@ public static Collection scripts() { @Test public void test() throws IOException { - if (script.jarMainClass == SetWrong) { + if (script.jarMainClass == Script.MainClassType.SetWrong) { initJarWithWrongMainClass(); } @@ -224,23 +223,22 @@ public void test() throws IOException { boolean appShouldSucceed = false; // Should succeed if valid main class is set on the command line. - appShouldSucceed |= (script.mainClass == SetRight); + appShouldSucceed |= (script.mainClass == Script.MainClassType.SetRight); // Should succeed if main class is not set on the command line but set // to valid value in the jar. - appShouldSucceed |= (script.mainClass == NotSet && script.jarMainClass == SetRight); + appShouldSucceed |= (script.mainClass == Script.MainClassType.NotSet && script.jarMainClass == Script.MainClassType.SetRight); if (appShouldSucceed) { cmd.executeAndAssertHelloAppImageCreated(); } else { cmd.executeAndAssertImageCreated(); - if (!cmd.isFakeRuntime(String.format("Not running [%s]", - cmd.appLauncherPath()))) { - List output = new Executor() - .setDirectory(cmd.outputDir()) - .setExecutable(cmd.appLauncherPath()) - .dumpOutput().saveOutput() - .execute(1).getOutput(); + var appVerifier = HelloApp.assertMainLauncher(cmd); + if (appVerifier != null) { + List output = appVerifier + .saveOutput(true) + .expectedExitCode(1) + .execute().getOutput(); TKit.assertTextStream(String.format( "Error: Could not find or load main class %s", nonExistingMainClass)).apply(output.stream()); diff --git a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/ModulePathTest.java b/test/jdk/tools/jpackage/share/ModulePathTest.java similarity index 96% rename from test/jdk/tools/jpackage/share/jdk/jpackage/tests/ModulePathTest.java rename to test/jdk/tools/jpackage/share/ModulePathTest.java index 1c5ead2ad653c..7d1e86f8ae2c7 100644 --- a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/ModulePathTest.java +++ b/test/jdk/tools/jpackage/share/ModulePathTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,7 +21,6 @@ * questions. */ -package jdk.jpackage.tests; import java.io.File; import java.io.IOException; @@ -44,12 +43,11 @@ /* * @test * @summary jpackage with --module-path testing - * @library ../../../../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile ModulePathTest.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=jdk.jpackage.tests.ModulePathTest + * --jpt-run=ModulePathTest */ public final class ModulePathTest { diff --git a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/ModulePathTest2.java b/test/jdk/tools/jpackage/share/ModulePathTest2.java similarity index 92% rename from test/jdk/tools/jpackage/share/jdk/jpackage/tests/ModulePathTest2.java rename to test/jdk/tools/jpackage/share/ModulePathTest2.java index 7bfe998014caf..34144907bdbee 100644 --- a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/ModulePathTest2.java +++ b/test/jdk/tools/jpackage/share/ModulePathTest2.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,7 +21,6 @@ * questions. */ -package jdk.jpackage.tests; import java.io.IOException; import java.nio.file.Path; @@ -36,12 +35,11 @@ /* * @test * @summary jpackage with --module-path testing - * @library ../../../../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile ModulePathTest2.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=jdk.jpackage.tests.ModulePathTest2 + * --jpt-run=ModulePathTest2 */ public final class ModulePathTest2 { diff --git a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/ModulePathTest3.java b/test/jdk/tools/jpackage/share/ModulePathTest3.java similarity index 95% rename from test/jdk/tools/jpackage/share/jdk/jpackage/tests/ModulePathTest3.java rename to test/jdk/tools/jpackage/share/ModulePathTest3.java index 662b2633207a7..b4b51e1adfcf5 100644 --- a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/ModulePathTest3.java +++ b/test/jdk/tools/jpackage/share/ModulePathTest3.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,7 +21,6 @@ * questions. */ -package jdk.jpackage.tests; import java.io.IOException; import java.nio.file.Files; @@ -49,12 +48,11 @@ /* * @test * @summary jpackage for app's module linked in external runtime - * @library ../../../../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile ModulePathTest3.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=jdk.jpackage.tests.ModulePathTest3 + * --jpt-run=ModulePathTest3 */ public final class ModulePathTest3 { diff --git a/test/jdk/tools/jpackage/share/MultiLauncherTwoPhaseTest.java b/test/jdk/tools/jpackage/share/MultiLauncherTwoPhaseTest.java index b048d6db335ca..9dc6705936ae6 100644 --- a/test/jdk/tools/jpackage/share/MultiLauncherTwoPhaseTest.java +++ b/test/jdk/tools/jpackage/share/MultiLauncherTwoPhaseTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,13 +43,13 @@ /* * @test * @summary Multiple launchers in two phases - * @library ../helpers - * @library /test/lib + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile MultiLauncherTwoPhaseTest.java - * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * @run main/othervm/timeout=360 -Xmx512m + * --add-opens jdk.jpackage/jdk.jpackage.internal=ALL-UNNAMED + * jdk.jpackage.test.Main * --jpt-run=MultiLauncherTwoPhaseTest */ diff --git a/test/jdk/tools/jpackage/share/MultiNameTwoPhaseTest.java b/test/jdk/tools/jpackage/share/MultiNameTwoPhaseTest.java index 7b3f484d62d6b..870804661a7f8 100644 --- a/test/jdk/tools/jpackage/share/MultiNameTwoPhaseTest.java +++ b/test/jdk/tools/jpackage/share/MultiNameTwoPhaseTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,12 +41,10 @@ /* * @test * @summary Multiple names in two phases - * @library ../helpers - * @library /test/lib + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @requires (jpackage.test.SQETest == null) * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile MultiNameTwoPhaseTest.java * @run main/othervm/timeout=540 -Xmx512m jdk.jpackage.test.Main * --jpt-run=MultiNameTwoPhaseTest diff --git a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/MultipleJarAppTest.java b/test/jdk/tools/jpackage/share/MultipleJarAppTest.java similarity index 89% rename from test/jdk/tools/jpackage/share/jdk/jpackage/tests/MultipleJarAppTest.java rename to test/jdk/tools/jpackage/share/MultipleJarAppTest.java index 3ce9f62d8d271..0726b131d69e7 100644 --- a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/MultipleJarAppTest.java +++ b/test/jdk/tools/jpackage/share/MultipleJarAppTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,7 +21,6 @@ * questions. */ -package jdk.jpackage.tests; import java.nio.file.Path; import jdk.jpackage.test.Annotations.Test; @@ -33,12 +32,11 @@ /* * @test * @summary jpackage application packed in multiple jars - * @library ../../../../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile MultipleJarAppTest.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=jdk.jpackage.tests.MultipleJarAppTest + * --jpt-run=MultipleJarAppTest */ public final class MultipleJarAppTest { diff --git a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/NoMPathRuntimeTest.java b/test/jdk/tools/jpackage/share/NoMPathRuntimeTest.java similarity index 95% rename from test/jdk/tools/jpackage/share/jdk/jpackage/tests/NoMPathRuntimeTest.java rename to test/jdk/tools/jpackage/share/NoMPathRuntimeTest.java index fea6324e46b7e..3731997118334 100644 --- a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/NoMPathRuntimeTest.java +++ b/test/jdk/tools/jpackage/share/NoMPathRuntimeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,7 +21,6 @@ * questions. */ -package jdk.jpackage.tests; import java.io.IOException; import java.nio.file.Files; @@ -43,12 +42,11 @@ /* * @test * @summary test '--runtime-image' option of jpackage - * @library ../../../../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile NoMPathRuntimeTest.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=jdk.jpackage.tests.NoMPathRuntimeTest + * --jpt-run=NoMPathRuntimeTest */ public final class NoMPathRuntimeTest { diff --git a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/NonExistentTest.java b/test/jdk/tools/jpackage/share/NonExistentTest.java similarity index 93% rename from test/jdk/tools/jpackage/share/jdk/jpackage/tests/NonExistentTest.java rename to test/jdk/tools/jpackage/share/NonExistentTest.java index 3790c9002ca7e..4d50f2a5850ae 100644 --- a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/NonExistentTest.java +++ b/test/jdk/tools/jpackage/share/NonExistentTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,7 +21,6 @@ * questions. */ -package jdk.jpackage.tests; import java.util.Collection; import java.util.List; @@ -33,12 +32,11 @@ /* * @test * @summary jpackage application version testing - * @library ../../../../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile NonExistentTest.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=jdk.jpackage.tests.NonExistentTest + * --jpt-run=NonExistentTest */ public final class NonExistentTest { diff --git a/test/jdk/tools/jpackage/share/RuntimeImageTest.java b/test/jdk/tools/jpackage/share/RuntimeImageTest.java index 3b57536213b62..fe4169ad0a060 100644 --- a/test/jdk/tools/jpackage/share/RuntimeImageTest.java +++ b/test/jdk/tools/jpackage/share/RuntimeImageTest.java @@ -42,10 +42,9 @@ /* * @test * @summary jpackage with --runtime-image - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* - * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal * @compile RuntimeImageTest.java * @run main/othervm/timeout=1400 jdk.jpackage.test.Main * --jpt-run=RuntimeImageTest diff --git a/test/jdk/tools/jpackage/share/RuntimePackageTest.java b/test/jdk/tools/jpackage/share/RuntimePackageTest.java index e3ebdd3327eb3..b00d7a7be1419 100644 --- a/test/jdk/tools/jpackage/share/RuntimePackageTest.java +++ b/test/jdk/tools/jpackage/share/RuntimePackageTest.java @@ -25,7 +25,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.HashSet; -import java.util.Optional; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -34,6 +33,8 @@ import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.TKit; import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.Executor; +import jdk.jpackage.test.JavaTool; import jdk.jpackage.test.LinuxHelper; import static jdk.jpackage.test.TKit.assertTrue; import static jdk.jpackage.test.TKit.assertFalse; @@ -54,11 +55,10 @@ /* * @test * @summary jpackage with --runtime-image - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* * @requires (jpackage.test.SQETest == null) - * @modules jdk.jpackage/jdk.jpackage.internal * @compile RuntimePackageTest.java * @run main/othervm/timeout=1400 -Xmx512m jdk.jpackage.test.Main * --jpt-run=RuntimePackageTest @@ -67,11 +67,10 @@ /* * @test * @summary jpackage with --runtime-image - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* * @requires (jpackage.test.SQETest != null) - * @modules jdk.jpackage/jdk.jpackage.internal * @compile RuntimePackageTest.java * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main * --jpt-run=RuntimePackageTest.test @@ -101,9 +100,25 @@ private static PackageTest init(Set types) { return new PackageTest() .forTypes(types) .addInitializer(cmd -> { - cmd.addArguments("--runtime-image", Optional.ofNullable( - JPackageCommand.DEFAULT_RUNTIME_IMAGE).orElse(Path.of( - System.getProperty("java.home")))); + final Path runtimeImageDir; + if (JPackageCommand.DEFAULT_RUNTIME_IMAGE != null) { + runtimeImageDir = JPackageCommand.DEFAULT_RUNTIME_IMAGE; + } else { + runtimeImageDir = TKit.createTempDirectory("runtime").resolve("data"); + + new Executor() + .setToolProvider(JavaTool.JLINK) + .dumpOutput() + .addArguments( + "--output", runtimeImageDir.toString(), + "--compress=0", + "--add-modules", "ALL-MODULE-PATH", + "--strip-debug", + "--no-header-files", + "--no-man-pages") + .execute(); + } + cmd.addArguments("--runtime-image", runtimeImageDir); // Remove --input parameter from jpackage command line as we don't // create input directory in the test and jpackage fails // if --input references non existant directory. diff --git a/test/jdk/tools/jpackage/share/SimplePackageTest.java b/test/jdk/tools/jpackage/share/SimplePackageTest.java index d2e8da31c031e..e3945b668e0e0 100644 --- a/test/jdk/tools/jpackage/share/SimplePackageTest.java +++ b/test/jdk/tools/jpackage/share/SimplePackageTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,10 +40,9 @@ /* * @test * @summary Simple jpackage command run - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile SimplePackageTest.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main * --jpt-run=SimplePackageTest diff --git a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/UnicodeArgsTest.java b/test/jdk/tools/jpackage/share/UnicodeArgsTest.java similarity index 92% rename from test/jdk/tools/jpackage/share/jdk/jpackage/tests/UnicodeArgsTest.java rename to test/jdk/tools/jpackage/share/UnicodeArgsTest.java index 7b945f0873113..6fa2717d15e7e 100644 --- a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/UnicodeArgsTest.java +++ b/test/jdk/tools/jpackage/share/UnicodeArgsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,7 +21,6 @@ * questions. */ -package jdk.jpackage.tests; import java.util.stream.Collectors; import jdk.jpackage.test.TKit; @@ -33,13 +32,12 @@ /* * @test * @summary test how app launcher handles unicode command line arguments - * @library ../../../../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile UnicodeArgsTest.java * @requires (os.family == "windows") * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=jdk.jpackage.tests.UnicodeArgsTest + * --jpt-run=UnicodeArgsTest */ public final class UnicodeArgsTest { diff --git a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/VendorTest.java b/test/jdk/tools/jpackage/share/VendorTest.java similarity index 90% rename from test/jdk/tools/jpackage/share/jdk/jpackage/tests/VendorTest.java rename to test/jdk/tools/jpackage/share/VendorTest.java index eb469ea130a8d..4c3a0ffcee26e 100644 --- a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/VendorTest.java +++ b/test/jdk/tools/jpackage/share/VendorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,7 +21,6 @@ * questions. */ -package jdk.jpackage.tests; import jdk.jpackage.test.PackageTest; import jdk.jpackage.test.PackageType; @@ -54,29 +53,27 @@ /* * @test * @summary Test --vendor jpackage command option - * @library ../../../../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @requires (os.family == "windows") * @requires jpackage.test.SQETest != null * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile VendorTest.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=jdk.jpackage.tests.VendorTest + * --jpt-run=VendorTest */ /* * @test * @summary Test --vendor jpackage command option - * @library ../../../../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @requires (os.family != "mac") * @requires jpackage.test.SQETest == null * @build jdk.jpackage.test.* - * @modules jdk.jpackage/jdk.jpackage.internal * @compile VendorTest.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=jdk.jpackage.tests.VendorTest + * --jpt-run=VendorTest */ public class VendorTest { diff --git a/test/jdk/tools/jpackage/windows/Win8282351Test.java b/test/jdk/tools/jpackage/windows/Win8282351Test.java index 3b38b9e7f96fa..17ea5e7d9ab1a 100644 --- a/test/jdk/tools/jpackage/windows/Win8282351Test.java +++ b/test/jdk/tools/jpackage/windows/Win8282351Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,11 +39,10 @@ /* * @test * @summary Test case for JDK-8248254 - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* * @build Win8282351Test * @requires (os.family == "windows") - * @modules jdk.jpackage/jdk.jpackage.internal * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main * --jpt-run=Win8282351Test */ diff --git a/test/jdk/tools/jpackage/windows/Win8301247Test.java b/test/jdk/tools/jpackage/windows/Win8301247Test.java index ed435bb9cc29e..6ecae27a02775 100644 --- a/test/jdk/tools/jpackage/windows/Win8301247Test.java +++ b/test/jdk/tools/jpackage/windows/Win8301247Test.java @@ -24,7 +24,6 @@ import java.io.IOException; import java.time.Duration; import java.util.List; -import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; import java.util.concurrent.ExecutorService; @@ -46,11 +45,10 @@ /* * @test * @summary Test case for JDK-8301247 - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* * @build Win8301247Test * @requires (os.family == "windows") - * @modules jdk.jpackage/jdk.jpackage.internal * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main * --jpt-run=Win8301247Test */ @@ -100,40 +98,36 @@ public void test() throws IOException, InterruptedException { private static Optional findMainAppLauncherPID(JPackageCommand cmd, int expectedCount) { - // Get the list of PIDs and PPIDs of app launcher processes. - // wmic process where (name = "foo.exe") get ProcessID,ParentProcessID - List output = Executor.of("wmic", "process", "where", "(name", - "=", - "\"" + cmd.appLauncherPath().getFileName().toString() + "\"", - ")", "get", "ProcessID,ParentProcessID").dumpOutput(true). - saveOutput().executeAndGetOutput(); - - if (output.isEmpty()) { - throw new NoSuchElementException(); - } - String first = output.get(0); + // Get the list of PIDs and PPIDs of app launcher processes. Run setWinRunWithEnglishOutput(true) for JDK-8344275. + // powershell -NoLogo -NoProfile -NonInteractive -Command + // "Get-CimInstance Win32_Process -Filter \"Name = 'foo.exe'\" | select ProcessID,ParentProcessID" + String command = "Get-CimInstance Win32_Process -Filter \\\"Name = '" + + cmd.appLauncherPath().getFileName().toString() + + "'\\\" | select ProcessID,ParentProcessID"; + List output = Executor.of("powershell", "-NoLogo", "-NoProfile", "-NonInteractive", "-Command", command) + .dumpOutput(true).saveOutput().setWinRunWithEnglishOutput(true).executeAndGetOutput(); if (expectedCount == 0) { - TKit.assertEquals("No Instance(s) Available.", first. - trim(), "Check no app launcher processes found running"); - return Optional.empty(); + if (output.size() < 1) { + return Optional.empty(); + } } - String[] headers = Stream.of(first.split("\\s+", 2)).map( + String[] headers = Stream.of(output.get(1).split("\\s+", 2)).map( String::trim).map(String::toLowerCase).toArray(String[]::new); Pattern pattern; if (headers[0].equals("parentprocessid") && headers[1].equals( "processid")) { - pattern = Pattern.compile("^(?\\d+)\\s+(?\\d+)\\s+$"); + pattern = Pattern.compile("^\\s+(?\\d+)\\s+(?\\d+)$"); } else if (headers[1].equals("parentprocessid") && headers[0].equals( "processid")) { - pattern = Pattern.compile("^(?\\d+)\\s+(?\\d+)\\s+$"); + pattern = Pattern.compile("^\\s+(?\\d+)\\s+(?\\d+)$"); } else { throw new RuntimeException( - "Unrecognizable output of \'wmic process\' command"); + "Unrecognizable output of \'Get-CimInstance Win32_Process\' command"); } - List processes = output.stream().skip(1).map(line -> { + List processes = output.stream().skip(3).map(line -> { Matcher m = pattern.matcher(line); long[] pids = null; if (m.matches()) { diff --git a/test/jdk/tools/jpackage/windows/Win8365790Test.java b/test/jdk/tools/jpackage/windows/Win8365790Test.java new file mode 100644 index 0000000000000..4b2a18b04f695 --- /dev/null +++ b/test/jdk/tools/jpackage/windows/Win8365790Test.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import static jdk.jpackage.test.HelloApp.configureAndExecute; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Duration; +import jdk.jpackage.test.AdditionalLauncher; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.CfgFile; +import jdk.jpackage.test.Executor; +import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.TKit; + +/** + * Test the child process has a chance to handle Ctrl+C signal. + */ + +/* + * @test + * @summary Test case for JDK-8365790 + * @library /test/jdk/tools/jpackage/helpers + * @build jdk.jpackage.test.* + * @build Win8365790Test + * @requires (os.family == "windows") + * @run main/othervm/timeout=100 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=Win8365790Test + */ +public class Win8365790Test { + + @Test + public void test() throws InterruptedException, IOException { + + var outputDir = TKit.createTempDirectory("response-dir"); + + var mainOutputFile = outputDir.resolve("output.txt"); + var mainTraceFile = outputDir.resolve("trace.txt"); + + var probeOutputFile = outputDir.resolve("probe-output.txt"); + var probeTraceFile = outputDir.resolve("probe-trace.txt"); + + var cmd = JPackageCommand + .helloAppImage(TEST_APP_JAVA + "*UseShutdownHook") + .ignoreFakeRuntime() + .addArguments("--java-options", "-Djpackage.test.trace-file=" + mainTraceFile.toString()) + .addArguments("--arguments", mainOutputFile.toString()) + .addArguments("--arguments", Long.toString(Duration.ofSeconds(TETS_APP_AUTOCLOSE_TIMEOUT_SECONDS).getSeconds())); + + new AdditionalLauncher("probe") { + @Override + protected void verify(JPackageCommand cmd) { + } + }.addJavaOptions("-Djpackage.test.trace-file=" + probeTraceFile.toString()) + .addDefaultArguments(probeOutputFile.toString(), Long.toString(Duration.ofSeconds(TETS_APP_AUTOCLOSE_TIMEOUT_SECONDS).getSeconds())) + .applyTo(cmd); + + cmd.executeAndAssertImageCreated(); + + cmd.readLauncherCfgFile("probe") + .add(new CfgFile().addValue("Application", "win.norestart", Boolean.TRUE.toString())) + .save(cmd.appLauncherCfgPath("probe")); + + // Try Ctrl+C signal on a launcher with disabled restart functionality. + // It will create a single launcher process instead of the parent and the child processes. + // Ctrl+C always worked for launcher with disabled restart functionality. + var probeOutput = runLauncher(cmd, "probe", probeTraceFile, probeOutputFile); + + if (!probeOutput.equals("shutdown hook executed")) { + // Ctrl+C signal didn't make it. Test environment doesn't support Ctrl+C signal + // delivery from the prowershell process to a child process, don't run the main + // test. + TKit.throwSkippedException( + "The environment does NOT support Ctrl+C signal delivery from the prowershell process to a child process"); + } + + var mainOutput = runLauncher(cmd, null, mainTraceFile, mainOutputFile); + + TKit.assertEquals("shutdown hook executed", mainOutput, "Check shutdown hook executed"); + } + + private static String runLauncher(JPackageCommand cmd, String launcherName, Path traceFile, Path outputFile) throws IOException { + // Launch the specified launcher and send Ctrl+C signal to it. + Thread t = new Thread (() -> { + configureAndExecute(0, Executor.of("powershell", "-NonInteractive", "-NoLogo", "-NoProfile", "-ExecutionPolicy", "Unrestricted") + .addArgument("-File").addArgument(TEST_PS1) + .addArguments("-TimeoutSeconds", Long.toString(Duration.ofSeconds(5).getSeconds())) + .addArgument("-Executable").addArgument(cmd.appLauncherPath(launcherName)) + .dumpOutput()); + }); + t.start(); + + TKit.waitForFileCreated(traceFile, Duration.ofSeconds(20), Duration.ofSeconds(2)); + + try { + TKit.waitForFileCreated(outputFile, Duration.ofSeconds(TETS_APP_AUTOCLOSE_TIMEOUT_SECONDS * 2), Duration.ofSeconds(2)); + } finally { + TKit.traceFileContents(traceFile, "Test app trace"); + } + + TKit.assertFileExists(outputFile); + return Files.readString(outputFile); + } + + private static final long TETS_APP_AUTOCLOSE_TIMEOUT_SECONDS = 30; + + private static final Path TEST_APP_JAVA = TKit.TEST_SRC_ROOT.resolve("apps/UseShutdownHook.java"); + private static final Path TEST_PS1 = TKit.TEST_SRC_ROOT.resolve(Path.of("resources/Win8365790Test.ps1")).normalize(); +} diff --git a/test/jdk/tools/jpackage/windows/WinChildProcessTest.java b/test/jdk/tools/jpackage/windows/WinChildProcessTest.java index 4fa7581d2b5c9..a83ef8373312f 100644 --- a/test/jdk/tools/jpackage/windows/WinChildProcessTest.java +++ b/test/jdk/tools/jpackage/windows/WinChildProcessTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,13 +25,10 @@ * @bug 8325203 * @summary Test that Jpackage windows executable application kills the launched 3rd party application * when System.exit(0) is invoked along with terminating java program. - * @library ../helpers - * @library /test/lib + * @library /test/jdk/tools/jpackage/helpers * @requires os.family == "windows" - * @build WinChildProcessTest * @build jdk.jpackage.test.* * @build WinChildProcessTest - * @modules jdk.jpackage/jdk.jpackage.internal * @run main/othervm -Xmx512m jdk.jpackage.test.Main * --jpt-run=WinChildProcessTest * @@ -43,29 +40,32 @@ import java.nio.file.Path; import jdk.jpackage.test.JPackageCommand; +import static jdk.jpackage.test.HelloApp.configureAndExecute; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.Executor; import jdk.jpackage.test.TKit; +import static jdk.jpackage.test.WindowsHelper.killProcess; public class WinChildProcessTest { private static final Path TEST_APP_JAVA = TKit.TEST_SRC_ROOT .resolve("apps/ChildProcessAppLauncher.java"); @Test - public static void test() throws Throwable { + public static void test() { long childPid = 0; try { JPackageCommand cmd = JPackageCommand - .helloAppImage(TEST_APP_JAVA + "*Hello"); + .helloAppImage(TEST_APP_JAVA + "*Hello") + .ignoreFakeRuntime(); // Create the image of the third party application launcher cmd.executeAndAssertImageCreated(); // Start the third party application launcher and dump and save the // output of the application - List output = new Executor().saveOutput().dumpOutput() - .setExecutable(cmd.appLauncherPath().toAbsolutePath()) - .execute(0).getOutput(); + List output = configureAndExecute(0, new Executor().saveOutput().dumpOutput() + .setExecutable(cmd.appLauncherPath().toAbsolutePath())) + .getOutput(); String pidStr = output.get(0); // parse child PID @@ -78,10 +78,12 @@ public static void test() throws Throwable { Optional processHandle = ProcessHandle.of(childPid); boolean isAlive = processHandle.isPresent() && processHandle.get().isAlive(); - TKit.assertTrue(isAlive, "Check is child process is alive"); + TKit.assertTrue(isAlive, "Check child process is alive"); } finally { - // Kill only a specific child instance - Runtime.getRuntime().exec("taskkill /F /PID " + childPid); + if (childPid != 0) { + // Kill only a specific child instance + killProcess(childPid); + } } } } diff --git a/test/jdk/tools/jpackage/windows/WinConsoleTest.java b/test/jdk/tools/jpackage/windows/WinConsoleTest.java index 7a4e9ccfa5a0c..6a1e94a8e83ef 100644 --- a/test/jdk/tools/jpackage/windows/WinConsoleTest.java +++ b/test/jdk/tools/jpackage/windows/WinConsoleTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,10 +34,9 @@ /* * @test * @summary jpackage with --win-console - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* * @requires (os.family == "windows") - * @modules jdk.jpackage/jdk.jpackage.internal * @compile WinConsoleTest.java * * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main diff --git a/test/jdk/tools/jpackage/windows/WinDirChooserTest.java b/test/jdk/tools/jpackage/windows/WinDirChooserTest.java index 355b89dba3eda..a4db21dc6189e 100644 --- a/test/jdk/tools/jpackage/windows/WinDirChooserTest.java +++ b/test/jdk/tools/jpackage/windows/WinDirChooserTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,12 +36,11 @@ /* * @test * @summary jpackage with --win-dir-chooser - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* * @build WinDirChooserTest * @requires (os.family == "windows") - * @modules jdk.jpackage/jdk.jpackage.internal * @run main/othervm/timeout=540 -Xmx512m jdk.jpackage.test.Main * --jpt-run=WinDirChooserTest */ diff --git a/test/jdk/tools/jpackage/windows/WinInstallerIconTest.java b/test/jdk/tools/jpackage/windows/WinInstallerIconTest.java index 63194d6b25680..f104abc9f5e6b 100644 --- a/test/jdk/tools/jpackage/windows/WinInstallerIconTest.java +++ b/test/jdk/tools/jpackage/windows/WinInstallerIconTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,12 +38,11 @@ /* * @test * @summary jpackage with --icon parameter for exe installer - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* * @build WinInstallerIconTest * @requires (os.family == "windows") - * @modules jdk.jpackage/jdk.jpackage.internal * @run main/othervm/timeout=540 -Xmx512m jdk.jpackage.test.Main * --jpt-run=WinInstallerIconTest */ diff --git a/test/jdk/tools/jpackage/windows/WinInstallerUiTest.java b/test/jdk/tools/jpackage/windows/WinInstallerUiTest.java index 99c6eb490d5ec..c6489ab68cf4c 100644 --- a/test/jdk/tools/jpackage/windows/WinInstallerUiTest.java +++ b/test/jdk/tools/jpackage/windows/WinInstallerUiTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,12 +39,11 @@ /* * @test * @summary jpackage with --win-dir-chooser, --win-shortcut-prompt and --license parameters - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* * @build WinInstallerUiTest * @requires (os.family == "windows") - * @modules jdk.jpackage/jdk.jpackage.internal * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main * --jpt-run=WinInstallerUiTest */ diff --git a/test/jdk/tools/jpackage/windows/WinL10nTest.java b/test/jdk/tools/jpackage/windows/WinL10nTest.java index 4f7e726a3afef..e26d47db9df00 100644 --- a/test/jdk/tools/jpackage/windows/WinL10nTest.java +++ b/test/jdk/tools/jpackage/windows/WinL10nTest.java @@ -22,6 +22,7 @@ */ import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import jdk.jpackage.test.TKit; import jdk.jpackage.test.PackageTest; @@ -37,14 +38,13 @@ /* * @test * @summary Custom l10n of msi installers in jpackage - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @requires (jpackage.test.SQETest == null) * @build jdk.jpackage.test.* * @requires (os.family == "windows") - * @modules jdk.jpackage/jdk.jpackage.internal * @compile WinL10nTest.java - * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * @run main/othervm/timeout=1440 -Xmx512m jdk.jpackage.test.Main * --jpt-run=WinL10nTest */ diff --git a/test/jdk/tools/jpackage/windows/WinMenuGroupTest.java b/test/jdk/tools/jpackage/windows/WinMenuGroupTest.java index 5950418f6096c..bdf5cf2f0a5e2 100644 --- a/test/jdk/tools/jpackage/windows/WinMenuGroupTest.java +++ b/test/jdk/tools/jpackage/windows/WinMenuGroupTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,11 +39,10 @@ /* * @test * @summary jpackage with --win-menu and --win-menu-group - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* * @requires (os.family == "windows") - * @modules jdk.jpackage/jdk.jpackage.internal * @compile WinMenuGroupTest.java * @run main/othervm/timeout=540 -Xmx512m jdk.jpackage.test.Main * --jpt-run=WinMenuGroupTest diff --git a/test/jdk/tools/jpackage/windows/WinMenuTest.java b/test/jdk/tools/jpackage/windows/WinMenuTest.java index a28d28e24ece7..8bacaa054706d 100644 --- a/test/jdk/tools/jpackage/windows/WinMenuTest.java +++ b/test/jdk/tools/jpackage/windows/WinMenuTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,11 +36,10 @@ /* * @test * @summary jpackage with --win-menu - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* * @requires (os.family == "windows") - * @modules jdk.jpackage/jdk.jpackage.internal * @compile WinMenuTest.java * @run main/othervm/timeout=540 -Xmx512m jdk.jpackage.test.Main * --jpt-run=WinMenuTest diff --git a/test/jdk/tools/jpackage/windows/WinPerUserInstallTest.java b/test/jdk/tools/jpackage/windows/WinPerUserInstallTest.java index 5d97bb9357e27..48fca9644ea98 100644 --- a/test/jdk/tools/jpackage/windows/WinPerUserInstallTest.java +++ b/test/jdk/tools/jpackage/windows/WinPerUserInstallTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,11 +37,10 @@ /* * @test * @summary jpackage with --win-per-user-install, --win-menu, --win-menu-group - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* * @requires (os.family == "windows") - * @modules jdk.jpackage/jdk.jpackage.internal * @compile WinPerUserInstallTest.java * @run main/othervm/timeout=540 -Xmx512m jdk.jpackage.test.Main * --jpt-run=WinPerUserInstallTest diff --git a/test/jdk/tools/jpackage/windows/WinRenameTest.java b/test/jdk/tools/jpackage/windows/WinRenameTest.java index aa07a52964985..59107c4392170 100644 --- a/test/jdk/tools/jpackage/windows/WinRenameTest.java +++ b/test/jdk/tools/jpackage/windows/WinRenameTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,19 +26,17 @@ import java.nio.file.Files; import jdk.jpackage.test.HelloApp; import jdk.jpackage.test.TKit; -import jdk.jpackage.test.Functional.ThrowingConsumer; import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.Annotations.Test; /* * @test * @summary jpackage test app can run after changing executable's extension - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* * @build WinRenameTest * @requires (os.family == "windows") - * @modules jdk.jpackage/jdk.jpackage.internal * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main * --jpt-run=WinRenameTest */ @@ -51,11 +49,16 @@ public static void test() throws IOException { cmd.executeAndAssertImageCreated(); + if (!cmd.canRunLauncher("Not running the test")) { + return; + } + + HelloApp.executeLauncherAndVerifyOutput(cmd); + Path launcherPath = cmd.appLauncherPath(); - HelloApp.assertApp(launcherPath).executeAndVerifyOutput(); String lp = launcherPath.toString(); - TKit.assertTrue(lp.endsWith(".exe"), "UNexpected launcher path: " + lp); + TKit.assertTrue(lp.endsWith(".exe"), "Unexpected launcher path: " + lp); Path newLauncherPath = Path.of(lp.replaceAll(".exe", ".anything")); Files.move(launcherPath, newLauncherPath); diff --git a/test/jdk/tools/jpackage/windows/WinResourceTest.java b/test/jdk/tools/jpackage/windows/WinResourceTest.java index 3411172345778..72e805d0a48b5 100644 --- a/test/jdk/tools/jpackage/windows/WinResourceTest.java +++ b/test/jdk/tools/jpackage/windows/WinResourceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,10 +40,9 @@ /* * @test * @summary jpackage with --resource-dir - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* * @requires (os.family == "windows") - * @modules jdk.jpackage/jdk.jpackage.internal * @compile WinResourceTest.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main * --jpt-run=WinResourceTest diff --git a/test/jdk/tools/jpackage/windows/WinScriptTest.java b/test/jdk/tools/jpackage/windows/WinScriptTest.java index 775102cd0141c..296da482bb075 100644 --- a/test/jdk/tools/jpackage/windows/WinScriptTest.java +++ b/test/jdk/tools/jpackage/windows/WinScriptTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,10 +37,9 @@ /* * @test usage of scripts from resource dir * @summary jpackage with - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* * @requires (os.family == "windows") - * @modules jdk.jpackage/jdk.jpackage.internal * @compile WinScriptTest.java * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main * --jpt-run=WinScriptTest diff --git a/test/jdk/tools/jpackage/windows/WinShortcutPromptTest.java b/test/jdk/tools/jpackage/windows/WinShortcutPromptTest.java index 6c27b6852eb3d..e202f7e5d2abc 100644 --- a/test/jdk/tools/jpackage/windows/WinShortcutPromptTest.java +++ b/test/jdk/tools/jpackage/windows/WinShortcutPromptTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,12 +37,11 @@ /* * @test * @summary jpackage with --win-shortcut-prompt, --win-menu and --win-shortcut parameters - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* * @build WinShortcutPromptTest * @requires (os.family == "windows") - * @modules jdk.jpackage/jdk.jpackage.internal * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main * --jpt-run=WinShortcutPromptTest */ diff --git a/test/jdk/tools/jpackage/windows/WinShortcutTest.java b/test/jdk/tools/jpackage/windows/WinShortcutTest.java index 3efa8b68817d0..1d2e7e762a533 100644 --- a/test/jdk/tools/jpackage/windows/WinShortcutTest.java +++ b/test/jdk/tools/jpackage/windows/WinShortcutTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,11 +37,10 @@ /* * @test * @summary jpackage with --win-shortcut - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* * @requires (os.family == "windows") - * @modules jdk.jpackage/jdk.jpackage.internal * @compile WinShortcutTest.java * @run main/othervm/timeout=540 -Xmx512m jdk.jpackage.test.Main * --jpt-run=WinShortcutTest diff --git a/test/jdk/tools/jpackage/windows/WinUpgradeUUIDTest.java b/test/jdk/tools/jpackage/windows/WinUpgradeUUIDTest.java index 968ed94b34570..c48803cfb5f67 100644 --- a/test/jdk/tools/jpackage/windows/WinUpgradeUUIDTest.java +++ b/test/jdk/tools/jpackage/windows/WinUpgradeUUIDTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -46,12 +46,11 @@ /* * @test * @summary jpackage with --win-upgrade-uuid and --app-version - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @requires (jpackage.test.SQETest != null) * @build jdk.jpackage.test.* * @requires (os.family == "windows") - * @modules jdk.jpackage/jdk.jpackage.internal * @compile WinUpgradeUUIDTest.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main * --jpt-run=WinUpgradeUUIDTest.test @@ -60,12 +59,11 @@ /* * @test * @summary jpackage with --win-upgrade-uuid and --app-version - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @requires (jpackage.test.SQETest == null) * @build jdk.jpackage.test.* * @requires (os.family == "windows") - * @modules jdk.jpackage/jdk.jpackage.internal * @compile WinUpgradeUUIDTest.java * @run main/othervm/timeout=540 -Xmx512m jdk.jpackage.test.Main * --jpt-run=WinUpgradeUUIDTest diff --git a/test/jdk/tools/jpackage/windows/WinUrlTest.java b/test/jdk/tools/jpackage/windows/WinUrlTest.java index 208d16d931adc..f2e9528594bea 100644 --- a/test/jdk/tools/jpackage/windows/WinUrlTest.java +++ b/test/jdk/tools/jpackage/windows/WinUrlTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,12 +40,11 @@ * @test * @summary jpackage with --about-url, --win-update-url and --win-help-url * parameters - * @library ../helpers + * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* * @build WinUrlTest * @requires (os.family == "windows") - * @modules jdk.jpackage/jdk.jpackage.internal * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main * --jpt-run=WinUrlTest */ diff --git a/test/jdk/tools/launcher/DisableBestFitMappingTest.java b/test/jdk/tools/launcher/DisableBestFitMappingTest.java new file mode 100644 index 0000000000000..6602aae60a9fe --- /dev/null +++ b/test/jdk/tools/launcher/DisableBestFitMappingTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8337506 + * @summary Verify Command Line arguments are not mapped with + * "best-fit" mappings on Windows + * @requires (os.family == "windows") + * @library /test/lib + * @run junit DisableBestFitMappingTest + */ +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.util.stream.Stream; +import jdk.test.lib.process.ProcessTools; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assumptions.*; + +public class DisableBestFitMappingTest { + private static final CharsetEncoder NATIVE_ENC = + Charset.forName(System.getProperty("native.encoding")).newEncoder(); + private static final String REPLACEMENT = + NATIVE_ENC.charset().decode(ByteBuffer.wrap(NATIVE_ENC.replacement())).toString(); + private static final int EXIT_SUCCESS = 0; + private static final int EXIT_FAILURE = -1; + + static Stream CMD_ARGS() { + return Stream.of( + Arguments.of("aa\uff02 \uff02bb", "aa" + REPLACEMENT + " " + REPLACEMENT + "bb"), + Arguments.of("aa\uff01bb", "aa" + REPLACEMENT + "bb"), + Arguments.of("aa\u221ebb", "aa" + REPLACEMENT + "bb") + ); + } + + @ParameterizedTest + @MethodSource("CMD_ARGS") + void testDisableBestFitMapping(String arg, String expected) throws Exception { + // Only execute if the arg cannot be encoded + assumeFalse(NATIVE_ENC.canEncode(arg), + "native.encoding (%s) can encode the argument '%s'. Test ignored." + .formatted(NATIVE_ENC.charset(), arg)); + + var result= ProcessTools.executeTestJava( + DisableBestFitMappingTest.class.getSimpleName(), arg, expected); + result.asLines().forEach(System.out::println); + assertEquals(EXIT_SUCCESS, result.getExitValue(), + "Disabling best-fit mapping failed"); + } + + public static void main(String... args) { + System.out.println(args[0]); + System.out.println(args[1]); + System.exit(args[0].equals(args[1]) ? EXIT_SUCCESS : EXIT_FAILURE); + } +} diff --git a/test/jdk/tools/launcher/Settings.java b/test/jdk/tools/launcher/Settings.java index 36ae06640a13d..18fbaad715a23 100644 --- a/test/jdk/tools/launcher/Settings.java +++ b/test/jdk/tools/launcher/Settings.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,7 @@ /* * @test - * @bug 6994753 7123582 8305950 8281658 8310201 + * @bug 6994753 7123582 8305950 8281658 8310201 8343804 * @summary tests -XshowSettings options * @modules jdk.compiler * jdk.zipfs @@ -79,6 +79,7 @@ static void checkNotContains(TestResult tr, String str) { private static final String BAD_SEC_OPTION_MSG = "Unrecognized security subcommand"; private static final String SYSTEM_SETTINGS = "Operating System Metrics:"; private static final String STACKSIZE_SETTINGS = "Stack Size:"; + private static final String TIMEZONE_SETTINGS = "default timezone"; private static final String TZDATA_SETTINGS = "tzdata version"; static void containsAllOptions(TestResult tr) { @@ -92,6 +93,7 @@ static void containsAllOptions(TestResult tr) { checkContains(tr, SEC_SUMMARY_PROPS_SETTINGS); checkContains(tr, SEC_PROVIDER_SETTINGS); checkContains(tr, SEC_TLS_SETTINGS); + checkContains(tr, TIMEZONE_SETTINGS); checkContains(tr, TZDATA_SETTINGS); if (System.getProperty("os.name").contains("Linux")) { checkContains(tr, SYSTEM_SETTINGS); @@ -160,6 +162,7 @@ static void runTestOptionLocale() throws IOException { checkContains(tr, LOCALE_SETTINGS); checkContains(tr, AVAILABLE_LOCALES); checkNotContains(tr, LOCALE_SUMMARY_SETTINGS); + checkContains(tr, TIMEZONE_SETTINGS); checkContains(tr, TZDATA_SETTINGS); } diff --git a/test/jtreg-ext/requires/VMProps.java b/test/jtreg-ext/requires/VMProps.java index 59cc20799392e..ed8920c8f21a0 100644 --- a/test/jtreg-ext/requires/VMProps.java +++ b/test/jtreg-ext/requires/VMProps.java @@ -127,7 +127,7 @@ public Map call() { map.put("vm.graal.enabled", this::isGraalEnabled); map.put("vm.compiler1.enabled", this::isCompiler1Enabled); map.put("vm.compiler2.enabled", this::isCompiler2Enabled); - map.put("docker.support", this::dockerSupport); + map.put("container.support", this::containerSupport); map.put("vm.musl", this::isMusl); map.put("release.implementor", this::implementor); map.put("jdk.containerized", this::jdkContainerized); @@ -339,6 +339,7 @@ protected void vmOptFinalFlags(SafeMap map) { vmOptFinalFlag(map, "ClassUnloading"); vmOptFinalFlag(map, "ClassUnloadingWithConcurrentMark"); vmOptFinalFlag(map, "UseCompressedOops"); + vmOptFinalFlag(map, "UseLargePages"); vmOptFinalFlag(map, "UseVectorizedMismatchIntrinsic"); vmOptFinalFlag(map, "EnableJVMCI"); vmOptFinalFlag(map, "EliminateAllocations"); @@ -454,16 +455,16 @@ protected String isCompiler2Enabled() { } /** - * A simple check for docker support + * A simple check for container support * - * @return true if docker is supported in a given environment + * @return true if container is supported in a given environment */ - protected String dockerSupport() { - log("Entering dockerSupport()"); + protected String containerSupport() { + log("Entering containerSupport()"); boolean isSupported = false; if (Platform.isLinux()) { - // currently docker testing is only supported for Linux, + // currently container testing is only supported for Linux, // on certain platforms String arch = System.getProperty("os.arch"); @@ -479,17 +480,17 @@ protected String dockerSupport() { } } - log("dockerSupport(): platform check: isSupported = " + isSupported); + log("containerSupport(): platform check: isSupported = " + isSupported); if (isSupported) { try { - isSupported = checkDockerSupport(); + isSupported = checkProgramSupport("checkContainerSupport()", Container.ENGINE_COMMAND); } catch (Exception e) { isSupported = false; } } - log("dockerSupport(): returning isSupported = " + isSupported); + log("containerSupport(): returning isSupported = " + isSupported); return "" + isSupported; } @@ -526,18 +527,17 @@ private void printLogfileContent(Map logFileNames) { log("-------------"); }); } - - private boolean checkDockerSupport() throws IOException, InterruptedException { - log("checkDockerSupport(): entering"); - ProcessBuilder pb = new ProcessBuilder("which", Container.ENGINE_COMMAND); + private boolean checkProgramSupport(String logString, String cmd) throws IOException, InterruptedException { + log(logString + ": entering"); + ProcessBuilder pb = new ProcessBuilder("which", cmd); Map logFileNames = - redirectOutputToLogFile("checkDockerSupport(): which ", - pb, "which-container"); + redirectOutputToLogFile(logString + ": which " + cmd, + pb, "which-cmd"); Process p = pb.start(); p.waitFor(10, TimeUnit.SECONDS); int exitValue = p.exitValue(); - log(String.format("checkDockerSupport(): exitValue = %s, pid = %s", exitValue, p.pid())); + log(String.format("%s: exitValue = %s, pid = %s", logString, exitValue, p.pid())); if (exitValue != 0) { printLogfileContent(logFileNames); } diff --git a/test/langtools/jdk/javadoc/doclet/testModules/TestModulePackages.java b/test/langtools/jdk/javadoc/doclet/testModules/TestModulePackages.java index 59ab70c810485..b2353809c8e69 100644 --- a/test/langtools/jdk/javadoc/doclet/testModules/TestModulePackages.java +++ b/test/langtools/jdk/javadoc/doclet/testModules/TestModulePackages.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -57,7 +57,7 @@ public TestModulePackages() { tb = new ToolBox(); } - // @Test: See: https://bugs.openjdk.java.net/browse/JDK-8193107 + // @Test: See: https://bugs.openjdk.org/browse/JDK-8193107 public void empty(Path base) throws Exception { Path src = base.resolve("src"); new ModuleBuilder(tb, "m") diff --git a/test/langtools/jdk/jshell/FailOverDirectExecutionControlTest.java b/test/langtools/jdk/jshell/FailOverDirectExecutionControlTest.java index a094ed4a86f3d..99457ea2ce6dc 100644 --- a/test/langtools/jdk/jshell/FailOverDirectExecutionControlTest.java +++ b/test/langtools/jdk/jshell/FailOverDirectExecutionControlTest.java @@ -62,6 +62,7 @@ public class FailOverDirectExecutionControlTest extends ExecutionControlTestBase ClassLoader ccl; ExecutionControlProvider provider; + Logger logger; LogTestHandler hndlr; Map> logged; @@ -95,7 +96,7 @@ public void close() throws SecurityException { @BeforeMethod @Override public void setUp() { - Logger logger = Logger.getLogger("jdk.jshell.execution"); + logger = Logger.getLogger("jdk.jshell.execution"); logger.setLevel(Level.ALL); hndlr = new LogTestHandler(); logger.addHandler(hndlr); @@ -137,8 +138,8 @@ public void setUp() { @Override public void tearDown() { super.tearDown(); - Logger logger = Logger.getLogger("jdk.jshell.execution"); logger.removeHandler(hndlr); + logger = null; Thread.currentThread().setContextClassLoader(ccl); } diff --git a/test/langtools/tools/javac/MethodParameters/LambdaTest.java b/test/langtools/tools/javac/MethodParameters/LambdaTest.java index 66ec67c7fa4e2..0326888b6e6a2 100644 --- a/test/langtools/tools/javac/MethodParameters/LambdaTest.java +++ b/test/langtools/tools/javac/MethodParameters/LambdaTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,7 +32,7 @@ */ /** - * Post https://bugs.openjdk.java.net/browse/JDK-8138729, this test verifies + * Post https://bugs.openjdk.org/browse/JDK-8138729, this test verifies * that MethodParameters attribute are NOT emitted for lambdas. */ class LambdaTest { diff --git a/test/langtools/tools/javac/MethodParameters/LegacyOutputTest/LegacyOutputTest.java b/test/langtools/tools/javac/MethodParameters/LegacyOutputTest/LegacyOutputTest.java index 49a54d0a42b8f..759fd90baefa6 100644 --- a/test/langtools/tools/javac/MethodParameters/LegacyOutputTest/LegacyOutputTest.java +++ b/test/langtools/tools/javac/MethodParameters/LegacyOutputTest/LegacyOutputTest.java @@ -48,7 +48,7 @@ import javax.tools.ToolProvider; /** - * Post https://bugs.openjdk.java.net/browse/JDK-8190452, the test verifies that MethodParameters + * Post https://bugs.openjdk.org/browse/JDK-8190452, the test verifies that MethodParameters * attributes are not emitted when targeting --release < 8. */ public class LegacyOutputTest { diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsInConstantInit.java b/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsInConstantInit.java new file mode 100644 index 0000000000000..b7b91119bc9da --- /dev/null +++ b/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsInConstantInit.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8346751 + * @summary Verify type annotations inside constant expression field initializers + are handled correctly + * @library /tools/lib + * @modules + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * jdk.compiler/com.sun.tools.javac.code + * jdk.compiler/com.sun.tools.javac.util + * @build toolbox.ToolBox toolbox.JavacTask + * @run main TypeAnnotationsInConstantInit + */ + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import toolbox.JavacTask; +import toolbox.ToolBox; + +public class TypeAnnotationsInConstantInit { + + public static void main(String... args) throws Exception { + new TypeAnnotationsInConstantInit().run(); + } + + ToolBox tb = new ToolBox(); + + void run() throws Exception { + typeAnnotationInConstantExpressionFieldInit(Paths.get(".")); + } + + void typeAnnotationInConstantExpressionFieldInit(Path base) throws Exception { + Path src = base.resolve("src"); + Path classes = base.resolve("classes"); + tb.writeJavaFiles(src, + """ + import java.lang.annotation.*; + + @SuppressWarnings(Decl.VALUE) + public class Decl { + public static final @Nullable String VALUE = (@Nullable String) ""; + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE_USE }) + @interface Nullable {} + """); + Files.createDirectories(classes); + new JavacTask(tb) + .options("-d", classes.toString()) + .files(tb.findJavaFiles(src)) + .run() + .writeAll(); + } + +} diff --git a/test/langtools/tools/javac/api/ToolProvider/ToolProviderTest.java b/test/langtools/tools/javac/api/ToolProvider/ToolProviderTest.java index ceedbcb50e5bb..a212cd9a5c6e6 100644 --- a/test/langtools/tools/javac/api/ToolProvider/ToolProviderTest.java +++ b/test/langtools/tools/javac/api/ToolProvider/ToolProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,7 +41,7 @@ public class ToolProviderTest { public static void main(String... args) { // The following code allows the test to be skipped when run on // an exploded image. - // See https://bugs.openjdk.java.net/browse/JDK-8155858 + // See https://bugs.openjdk.org/browse/JDK-8155858 Path javaHome = Paths.get(System.getProperty("java.home")); Path image = javaHome.resolve("lib").resolve("modules"); Path modules = javaHome.resolve("modules"); diff --git a/test/langtools/tools/javac/jvm/ClassRefDupInConstantPoolTest.java b/test/langtools/tools/javac/jvm/ClassRefDupInConstantPoolTest.java index a84c87245afff..34fbefa988327 100644 --- a/test/langtools/tools/javac/jvm/ClassRefDupInConstantPoolTest.java +++ b/test/langtools/tools/javac/jvm/ClassRefDupInConstantPoolTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +26,7 @@ * @bug 8015927 * @summary Class reference duplicates in constant pool * @modules jdk.jdeps/com.sun.tools.classfile - * @clean ClassRefDupInConstantPoolTest$Duplicates + * @clean ClassRefDupInConstantPoolTest ClassRefDupInConstantPoolTest$Duplicates * @run main ClassRefDupInConstantPoolTest */ diff --git a/test/langtools/tools/javac/lambda/LambdaExpr02.java b/test/langtools/tools/javac/lambda/LambdaExpr02.java index d2fb358dbe3d4..d3526b5437353 100644 --- a/test/langtools/tools/javac/lambda/LambdaExpr02.java +++ b/test/langtools/tools/javac/lambda/LambdaExpr02.java @@ -28,7 +28,7 @@ * basic test for simple lambda expressions in multiple scopes * @author Brian Goetz * @author Maurizio Cimadamore - * @run main LambdaExpr01 + * @run main LambdaExpr02 */ public class LambdaExpr02 { diff --git a/test/langtools/tools/javac/lambda/MethodReferenceGenericTarget.java b/test/langtools/tools/javac/lambda/MethodReferenceGenericTarget.java index 2683d32e840de..3f20fe6c79207 100644 --- a/test/langtools/tools/javac/lambda/MethodReferenceGenericTarget.java +++ b/test/langtools/tools/javac/lambda/MethodReferenceGenericTarget.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -59,7 +59,7 @@ class E { } } -// snippet from https://bugs.openjdk.java.net/browse/JDK-8065303 +// snippet from https://bugs.openjdk.org/browse/JDK-8065303 class MethodReferenceTestPrivateTypeConversion { class MethodReferenceTestTypeConversion_E { diff --git a/test/langtools/tools/jdeps/jdkinternals/src/q/NoRepl.java b/test/langtools/tools/jdeps/jdkinternals/src/q/NoRepl.java index 16731f55b26af..6fa175193a0f5 100644 --- a/test/langtools/tools/jdeps/jdkinternals/src/q/NoRepl.java +++ b/test/langtools/tools/jdeps/jdkinternals/src/q/NoRepl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,11 +24,11 @@ package q; import java.io.IOException; -import java.io.OutputStream; import sun.security.util.DerEncoder; +import sun.security.util.DerOutputStream; public class NoRepl implements DerEncoder { - public void derEncode(OutputStream out) throws IOException { + public void derEncode(DerOutputStream out) throws IOException { throw new IOException(); } } diff --git a/test/lib-test/jdk/test/lib/RandomGeneratorTest.java b/test/lib-test/jdk/test/lib/RandomGeneratorTest.java index ce57ebfe65f34..71e36bdc9c471 100644 --- a/test/lib-test/jdk/test/lib/RandomGeneratorTest.java +++ b/test/lib-test/jdk/test/lib/RandomGeneratorTest.java @@ -69,7 +69,7 @@ public static void main( String[] args) throws Throwable { jvmArgs.add(origFileName); int fileNameIndex = jvmArgs.size() - 1; String[] cmdLineArgs = jvmArgs.toArray(new String[jvmArgs.size()]); - ProcessTools.executeTestJvm(cmdLineArgs).shouldHaveExitValue(0); + ProcessTools.executeTestJava(cmdLineArgs).shouldHaveExitValue(0); String etalon = Utils.fileAsString(origFileName).trim(); cmdLineArgs[fileNameIndex] = seedOpt.name(); seedOpt.verify(etalon, cmdLineArgs); @@ -143,7 +143,7 @@ public void verify(String orig, String[] cmdLine) { String output; OutputAnalyzer oa; try { - oa = ProcessTools.executeTestJvm(cmdLine); + oa = ProcessTools.executeTestJava(cmdLine); } catch (Throwable t) { throw new Error("TESTBUG: Unexpedted exception during jvm execution.", t); } diff --git a/test/lib-test/jdk/test/lib/process/ProcessToolsExecuteLimitedTestJavaTest.java b/test/lib-test/jdk/test/lib/process/ProcessToolsExecuteLimitedTestJavaTest.java new file mode 100644 index 0000000000000..4331f9ab374f7 --- /dev/null +++ b/test/lib-test/jdk/test/lib/process/ProcessToolsExecuteLimitedTestJavaTest.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Unit test for ProcessTools.executeLimitedTestJava() + * @library /test/lib + * @run main/othervm -Dtest.java.opts=-XX:MaxMetaspaceSize=123456789 ProcessToolsExecuteLimitedTestJavaTest + */ + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class ProcessToolsExecuteLimitedTestJavaTest { + public static void main(String[] args) throws Exception { + if (args.length > 0) { + // Do nothing. Just let the JVM log its output. + } else { + // In comparison to executeTestJava, executeLimitedTestJava should not add the + // -Dtest.java.opts flags. Check that it doesn't. + OutputAnalyzer output = ProcessTools.executeLimitedTestJava("-XX:+PrintFlagsFinal", "-version"); + output.stdoutShouldNotMatch(".*MaxMetaspaceSize.* = 123456789.*"); + } + } +} diff --git a/test/lib-test/jdk/test/lib/util/JarUtilsTest.java b/test/lib-test/jdk/test/lib/util/JarUtilsTest.java new file mode 100644 index 0000000000000..eb9dced32569a --- /dev/null +++ b/test/lib-test/jdk/test/lib/util/JarUtilsTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8309841 + * @summary Unit Test for a common Test API in jdk.test.lib.util.JarUtils + * @library /test/lib + */ + +import jdk.test.lib.Asserts; +import jdk.test.lib.util.JarUtils; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.stream.Collectors; + +public class JarUtilsTest { + public static void main(String[] args) throws Exception { + Files.createDirectory(Path.of("bx")); + JarUtils.createJarFile(Path.of("a.jar"), + Path.of("."), + Files.writeString(Path.of("a"), ""), + Files.writeString(Path.of("b1"), ""), + Files.writeString(Path.of("b2"), ""), + Files.writeString(Path.of("bx/x"), ""), + Files.writeString(Path.of("c"), ""), + Files.writeString(Path.of("e1"), ""), + Files.writeString(Path.of("e2"), "")); + checkContent("a", "b1", "b2", "bx/x", "c", "e1", "e2"); + + JarUtils.deleteEntries(Path.of("a.jar"), "a"); + checkContent("b1", "b2", "bx/x", "c", "e1", "e2"); + + // Note: b* covers everything starting with b, even bx/x + JarUtils.deleteEntries(Path.of("a.jar"), "b*"); + checkContent("c", "e1", "e2"); + + // d* does not match + JarUtils.deleteEntries(Path.of("a.jar"), "d*"); + checkContent("c", "e1", "e2"); + + // multiple patterns + JarUtils.deleteEntries(Path.of("a.jar"), "d*", "e*"); + checkContent("c"); + } + + static void checkContent(String... expected) throws IOException { + try (var jf = new JarFile("a.jar")) { + Asserts.assertEquals(Set.of(expected), + jf.stream().map(JarEntry::getName).collect(Collectors.toSet())); + } + } +} diff --git a/test/lib-test/jdk/test/whitebox/vm_flags/SizeTTest.java b/test/lib-test/jdk/test/whitebox/vm_flags/SizeTTest.java index ae57d6701fd90..e66af775b0811 100644 --- a/test/lib-test/jdk/test/whitebox/vm_flags/SizeTTest.java +++ b/test/lib-test/jdk/test/whitebox/vm_flags/SizeTTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,7 +35,7 @@ import jdk.test.lib.Platform; public class SizeTTest { - private static final String FLAG_NAME = "ArrayAllocatorMallocLimit"; + private static final String FLAG_NAME = "LargePageHeapSizeThreshold"; private static final Long[] TESTS = {0L, 100L, (long) Integer.MAX_VALUE, (1L << 32L) - 1L, 1L << 32L}; private static final Long[] EXPECTED_64 = TESTS; diff --git a/test/lib/jdk/test/lib/Asserts.java b/test/lib/jdk/test/lib/Asserts.java index 5abe03d6c47cb..0b9e0be3f0687 100644 --- a/test/lib/jdk/test/lib/Asserts.java +++ b/test/lib/jdk/test/lib/Asserts.java @@ -23,6 +23,10 @@ package jdk.test.lib; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Objects; /** @@ -199,10 +203,7 @@ public static void assertEquals(Object lhs, Object rhs) { */ public static void assertEquals(Object lhs, Object rhs, String msg) { if ((lhs != rhs) && ((lhs == null) || !(lhs.equals(rhs)))) { - msg = Objects.toString(msg, "assertEquals") - + ": expected " + Objects.toString(lhs) - + " to equal " + Objects.toString(rhs); - fail(msg); + fail((msg == null ? "assertEquals" : msg) + " expected: " + lhs + " but was: " + rhs); } } @@ -552,6 +553,46 @@ public static void assertStringsEqual(String str1, String str2, } } + /** + * A functional interface for executing tests in assertThrownException + */ + @FunctionalInterface + public interface TestMethod { + void execute() throws Throwable; + } + + + public static T assertThrows(Class expected, TestMethod testMethod) { + return assertThrows(expected, testMethod, "An unexpected exception was thrown."); + } + + /** + * Asserts that the given exception (or a subclass of it) is thrown when + * executing the test method. + * + * If the test method throws the correct exception, the exception is returned + * to the caller for additional validation e.g., comparing the exception + * message. + * + * @param expected The expected exception + * @param testMethod The code to execute that should throw the exception + * @param msg A description of the assumption + * @return The thrown exception. + */ + public static T assertThrows(Class expected, TestMethod testMethod, String msg) { + try { + testMethod.execute(); + } catch (Throwable exc) { + if (expected.isInstance(exc)) { + return (T) exc; + } else { + fail(Objects.toString(msg, "An unexpected exception was thrown.") + + " Expected " + expected.getName(), exc); + } + } + throw new RuntimeException("No exception was thrown. Expected: " + expected.getName()); + } + /** * Returns a string formatted with a message and expected and actual values. * @param lhs the actual value @@ -607,6 +648,28 @@ public static void fail(Object lhs, Object rhs, String message, String relation) throw new RuntimeException(format(lhs, rhs, message, relation)); } + /** + * Asserts that contents of two files are equal. + * + * @param f1 The path of the first file to compare + * @param f2 The path of the second file to compare + * @throws RuntimeException on mismatch or I/O failure + */ + public static void assertFileContentsEqual(Path f1, Path f2) { + long mismatchIndex = 0; + try { + mismatchIndex = Files.mismatch(f1, f2); + } catch (IOException exception) { + throw new UncheckedIOException(exception); + } + if (mismatchIndex >= 0) { + String message = String.format( + "Contents of files '%s' and '%s' mismatch at index %d", + f1, f2, mismatchIndex); + fail(message); + } + } + /** * Fail reports a failure with a message and a cause. * @param message to be format before the expected and actual values diff --git a/test/lib/jdk/test/lib/Container.java b/test/lib/jdk/test/lib/Container.java index e0ca4851e144b..83fd265980f26 100644 --- a/test/lib/jdk/test/lib/Container.java +++ b/test/lib/jdk/test/lib/Container.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2019, Red Hat Inc. + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,9 +24,9 @@ package jdk.test.lib; public class Container { - // Use this property to specify docker location on your system. + // Use this property to specify container runtime location (e.g. docker) on your system. // E.g.: "/usr/local/bin/docker". We define this constant here so - // that it can be used in VMProps as well which checks docker support + // that it can be used in VMProps as well which checks container support // via this command public static final String ENGINE_COMMAND = System.getProperty("jdk.test.container.command", "docker"); diff --git a/test/lib/jdk/test/lib/SecurityTools.java b/test/lib/jdk/test/lib/SecurityTools.java index bc198a712b843..c554ea4b15d12 100644 --- a/test/lib/jdk/test/lib/SecurityTools.java +++ b/test/lib/jdk/test/lib/SecurityTools.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -238,6 +238,17 @@ public static OutputAnalyzer kinit(String args) throws Exception { return execute(getProcessBuilder("kinit", makeList(args))); } + /** + * Runs jar. + * + * @param args arguments to jar in the list. + * @return an {@link OutputAnalyzer} object + * @throws Exception if there is an error + */ + public static OutputAnalyzer jar(final List args) throws Exception { + return execute(getProcessBuilder("jar", args)); + } + /** * Runs jar. * @@ -246,8 +257,20 @@ public static OutputAnalyzer kinit(String args) throws Exception { * @return an {@link OutputAnalyzer} object * @throws Exception if there is an error */ - public static OutputAnalyzer jar(String args) throws Exception { - return execute(getProcessBuilder("jar", makeList(args))); + public static OutputAnalyzer jar(final String args) throws Exception { + return jar(makeList(args)); + } + + /** + * Runs jar. + * + * @param args arguments to jar in multiple strings. + * Converted to be a List with List.of. + * @return an {@link OutputAnalyzer} object + * @throws Exception if there is an error + */ + public static OutputAnalyzer jar(final String... args) throws Exception { + return jar(List.of(args)); } /** diff --git a/test/lib/jdk/test/lib/Utils.java b/test/lib/jdk/test/lib/Utils.java index 50146100677ba..5e7db513dbd9e 100644 --- a/test/lib/jdk/test/lib/Utils.java +++ b/test/lib/jdk/test/lib/Utils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,6 +24,7 @@ package jdk.test.lib; import java.io.BufferedReader; +import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; @@ -71,6 +72,7 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; +import static java.lang.System.lineSeparator; import static jdk.test.lib.Asserts.assertTrue; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; @@ -813,6 +815,62 @@ public static Path createTempFile(String prefix, String suffix, FileAttribute return Files.createTempFile(dir, prefix, suffix, attrs); } + /** + * Creates a file in {@code user.dir} and populates it with random ASCII + * characters of given length. + *

    + * If the {@code user.dir} property is not set, {@code .} will be used. + * This choice of parent directory, compared to + * {@link Files#createTempFile(String, String, FileAttribute[])} using the + * {@code java.io.tmpdir} property, doesn't leave files behind in the + * {@code /tmp} directory of the test machine. + *

    + * + * @param prefix the prefix string to be used in generating the file's name; + * may be null + * @param suffix the suffix string to be used in generating the file's name; + * may be null, in which case ".tmp" is used + * @param size the size in bytes of the temporary file to be populated + * @param attrs an optional list of file attributes to set atomically when creating the file + * @return the path to the newly created file that did not exist before this + * method was invoked + * @throws IOException if an I/O error occurs or dir does not exist + * @throws IllegalArgumentException if size is negative + * + * @see #createTempFile(String, String, FileAttribute...) + */ + public static Path createTempFileOfSize(String prefix, String suffix, long size, FileAttribute... attrs) throws IOException { + + // Check arguments + if (size < 0) { + throw new IllegalArgumentException("file size cannot be negative: " + size); + } + + // Entropy ingredients + int prime1 = 2_147_483_647; + int prime2 = 1_047_483_649; + int seed = Long.hashCode(SEED); + + // Create & populate the file + Path path = createTempFile(prefix, suffix, attrs); + try (BufferedWriter writer = Files.newBufferedWriter(path, StandardCharsets.US_ASCII)) { + long remainingSize = size; + for (int rowIndex = 0; remainingSize > 0; rowIndex++) { + for (int colIndex = 0; remainingSize > 0 && colIndex < 80; remainingSize--, colIndex++) { + int r = ((rowIndex ^ seed) * prime1) ^ ((colIndex ^ seed) * prime2); + char c = (char) (0x21 + Math.abs(r) % (0x7e - 0x21)); + writer.append(c); + } + if (remainingSize > lineSeparator().length()) { + writer.write(lineSeparator()); + remainingSize -= lineSeparator().length(); + } + } + } + return path; + + } + /** * Creates an empty directory in "user.dir" or "." *

    diff --git a/test/lib/jdk/test/lib/artifacts/ArtifactResolver.java b/test/lib/jdk/test/lib/artifacts/ArtifactResolver.java index 0dbdd11dd526a..c9ae31ecb7833 100644 --- a/test/lib/jdk/test/lib/artifacts/ArtifactResolver.java +++ b/test/lib/jdk/test/lib/artifacts/ArtifactResolver.java @@ -23,6 +23,8 @@ package jdk.test.lib.artifacts; +import jtreg.SkippedException; + import java.nio.file.Path; import java.util.HashMap; import java.util.Map; @@ -59,6 +61,38 @@ public static Map resolve(Class klass) throws ArtifactResolverE return locations; } + /** + * Retrieve an artifact/library/file from a repository or local file system. + *

    + * Artifacts are defined with the {@link jdk.test.lib.artifacts.Artifact} + * annotation. + *

    + * If you have a local version of a dependency that you want to use, you can + * specify that by setting the system property: + * jdk.test.lib.artifacts.ARTIFACT_NAME. Where ARTIFACT_NAME + * is the name field of the Artifact annotation. + *

    + * Generally, tests that use this method should be run with make test. + * However, tests can also be run with jtreg but you must have a + * local copy of the artifact and the system property must be set as specified + * above. + * + * @param klass a class annotated with {@link jdk.test.lib.artifacts.Artifact} + * @return the local path to the artifact. If the artifact is a compressed + * file that gets unpacked, this path will point to the root + * directory of the uncompressed file(s). + * @throws SkippedException thrown if the artifact cannot be found + */ + public static Path fetchOne(Class klass) { + try { + return ArtifactResolver.resolve(klass).entrySet().stream() + .findAny().get().getValue(); + } catch (ArtifactResolverException e) { + Artifact artifact = klass.getAnnotation(Artifact.class); + throw new SkippedException("Cannot find the artifact " + artifact.name(), e); + } + } + private static String artifactName(Artifact artifact) { // Format of the artifact name is .-(-) String name = String.format("%s.%s-%s", artifact.organization(), artifact.name(), artifact.revision()); diff --git a/test/lib/jdk/test/lib/cds/CDSArchiveUtils.java b/test/lib/jdk/test/lib/cds/CDSArchiveUtils.java new file mode 100644 index 0000000000000..b4ead0ab0957b --- /dev/null +++ b/test/lib/jdk/test/lib/cds/CDSArchiveUtils.java @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.test.lib.cds; + +import java.io.IOException; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Random; + +import java.nio.file.Files; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.FileChannel; +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import java.nio.file.StandardOpenOption; +import static java.nio.file.StandardOpenOption.READ; +import static java.nio.file.StandardOpenOption.WRITE; + +import jdk.test.lib.Utils; +import jdk.test.whitebox.WhiteBox; + +// This class performs operations on shared archive file +public class CDSArchiveUtils { + public static int offsetMagic; // CDSFileMapHeaderBase::_magic + public static int offsetVersion; // CDSFileMapHeaderBase::_version + public static int offsetJvmIdent; // FileMapHeader::_jvm_ident + public static int spOffsetCrc; // CDSFileMapRegion::_crc + public static int fileHeaderSize; // total size of header, aligned with alignment + public static int cdsFileMapRegionSize; // size of CDSFileMapRegion + public static int spOffset; // offset of CDSFileMapRegion + public static int spUsedOffset; // offset of CDSFileMapRegion::_used + public static int sizetSize; // size of size_t + public static int intSize; // size of int + public static long alignment; // MetaspaceShared::core_region_alignment + + // The following should be consistent with the enum in the C++ MetaspaceShared class + public static String[] shared_region_name = { + "rw", // ReadWrite + "ro", // ReadOnly + "bm", // relocation bitmaps + "first_closed_archive", + "last_closed_archive", + "first_open_archive", + "last_open_archive" + }; + public static int num_regions = shared_region_name.length; + + static { + WhiteBox wb; + try { + wb = WhiteBox.getWhiteBox(); + offsetMagic = wb.getOffsetForName("FileMapHeader::_magic"); + offsetVersion = wb.getOffsetForName("FileMapHeader::_version"); + offsetJvmIdent = wb.getOffsetForName("FileMapHeader::_jvm_ident"); + spOffsetCrc = wb.getOffsetForName("CDSFileMapRegion::_crc"); + spOffset = wb.getOffsetForName("FileMapHeader::_space[0]") - offsetMagic; + spUsedOffset = wb.getOffsetForName("CDSFileMapRegion::_used") - spOffsetCrc; + sizetSize = wb.getOffsetForName("size_t_size"); + intSize = wb.getOffsetForName("int_size"); + cdsFileMapRegionSize = wb.getOffsetForName("CDSFileMapRegion_size"); + alignment = wb.metaspaceSharedRegionAlignment(); + // file_header_size is structure size, real size aligned with alignment + // so must be calculated after alignment is available + fileHeaderSize = (int)alignUpWithAlignment(wb.getOffsetForName("file_header_size")); + } catch (Exception e) { + throw new RuntimeException(e.getMessage()); + } + + try { + int nonExistOffset = wb.getOffsetForName("FileMapHeader::_non_exist_offset"); + System.exit(-1); // should fail + } catch (Exception e) { + // success + } + } + + private static long alignUpWithAlignment(long l) { + return (l + alignment - 1) & (~ (alignment - 1)); + } + + private static void setReadWritePermission(File file) throws Exception { + if (!file.canRead()) { + if (!file.setReadable(true)) { + throw new IOException("Cannot modify file " + file + " as readable"); + } + } + if (!file.canWrite()) { + if (!file.setWritable(true)) { + throw new IOException("Cannot modify file " + file + " as writable"); + } + } + } + + public static long getRandomBetween(long start, long end) throws Exception { + if (start > end) { + throw new IllegalArgumentException("start must be less than end"); + } + Random aRandom = Utils.getRandomInstance(); + int d = aRandom.nextInt((int)(end - start)); + if (d < 1) { + d = 1; + } + return start + d; + } + + public static void modifyContentRandomly(File jsaFile) throws Exception { + // corrupt random area in the data areas + long[] used = new long[num_regions]; // record used bytes + long start0, start, end, offset; + int bufSize; + + System.out.printf("%-24s%12s%12s%16s\n", "Space Name", "Used bytes", "Reg Start", "Random Offset"); + start0 = fileHeaderSize; + for (int i = 0; i < num_regions; i++) { + used[i] = usedRegionSizeAligned(jsaFile, i); + start = start0; + for (int j = 0; j < i; j++) { + start += alignUpWithAlignment(used[j]); + } + end = start + used[i]; + if (start == end) { + continue; // Ignore empty regions + } + offset = getRandomBetween(start, end); + System.out.printf("%-24s%12d%12d%16d\n", shared_region_name[i], used[i], start, offset); + if (end - offset < 1024) { + bufSize = (int)(end - offset + 1); + } else { + bufSize = 1024; + } + writeData(jsaFile, offset, new byte[bufSize]); + } + } + + public static void modifyRegionContentRandomly(File jsaFile) throws Exception { + // corrupt random area in the data areas + long[] used = new long[num_regions]; // record used bytes + long start0, start, end, offset; + int bufSize; + + System.out.printf("%-24s%12s%12s%16s\n", "Space Name", "Used bytes", "Reg Start", "Random Offset"); + start0 = fileHeaderSize; + for (int i = 0; i < num_regions; i++) { + used[i] = usedRegionSizeAligned(jsaFile, i); + start = start0; + for (int j = 0; j < i; j++) { + start += alignUpWithAlignment(used[j]); + } + end = start + used[i]; + if (start == end) { + continue; // Ignore empty regions + } + offset = getRandomBetween(start, end); + System.out.printf("%-24s%12d%12d%16d\n", shared_region_name[i], used[i], start, offset); + if (end - offset < 1024) { + bufSize = (int)(end - offset + 1); + } else { + bufSize = 1024; + } + writeData(jsaFile, offset, new byte[bufSize]); + } + } + + public static boolean modifyRegionContent(int region, File jsaFile) throws Exception { + long total = 0L; + long[] used = new long[num_regions]; + System.out.printf("%-24s%12s\n", "Space name", "Used bytes"); + for (int i = 0; i < num_regions; i++) { + used[i] = usedRegionSizeAligned(jsaFile, i); + System.out.printf("%-24s%12d\n", shared_region_name[i], used[i]); + total += used[i]; + } + if (used[region] == 0) { + System.out.println("Region " + shared_region_name[region] + " is empty. Nothing to corrupt."); + return false; + } + byte[] buf = new byte[4096]; + System.out.printf("%-24s%12d\n", "Total: ", total); + long regionStartOffset = fileHeaderSize; + for (int i = 0; i < region; i++) { + regionStartOffset += used[i]; + } + System.out.println("Corrupt " + shared_region_name[region] + " section, start = " + regionStartOffset + + " (header_size + 0x" + Long.toHexString(regionStartOffset - fileHeaderSize) + ")"); + long bytesWritten = 0L; + while (bytesWritten < used[region]) { + bytesWritten += writeData(jsaFile, regionStartOffset + bytesWritten, buf); + } + return true; + } + + public static void modifyFileHeader(File jsaFile) throws Exception { + // screw up header info + byte[] buf = new byte[fileHeaderSize]; + writeData(jsaFile, 0, buf); + } + + public static void modifyJvmIdent(File jsaFile, String newJvmIdent) throws Exception { + byte[] buf = newJvmIdent.getBytes(); + writeData(jsaFile, (long)offsetJvmIdent, buf); + } + + public static void modifyHeaderIntField(File jsaFile, long offset, int value) throws Exception { + System.out.println(" offset " + offset); + + byte[] bytes = ByteBuffer.allocate(4).putInt(value).array(); + writeData(jsaFile, offset, bytes); + } + + // copy archive and set copied read/write permit + public static File copyArchiveFile(File orgJsaFile, String newName) throws Exception { + File newJsaFile = new File(newName); + if (newJsaFile.exists()) { + if (!newJsaFile.delete()) { + throw new IOException("Could not delete file " + newJsaFile); + } + } + Files.copy(orgJsaFile.toPath(), newJsaFile.toPath(), REPLACE_EXISTING); + + // change permission + setReadWritePermission(newJsaFile); + + return newJsaFile; + } + + private static FileChannel getFileChannel(File file) throws Exception { + List arry = new ArrayList(); + arry.add(READ); + arry.add(WRITE); + return FileChannel.open(file.toPath(), new HashSet(arry)); + } + + public static long readInt(File file, long offset, int nBytes) throws Exception { + try (FileChannel fc = getFileChannel(file)) { + ByteBuffer bb = ByteBuffer.allocate(nBytes); + bb.order(ByteOrder.nativeOrder()); + fc.position(offset); + fc.read(bb); + return (nBytes > 4 ? bb.getLong(0) : bb.getInt(0)); + } + } + + private static long writeData(FileChannel fc, long offset, ByteBuffer bb) throws Exception { + fc.position(offset); + return fc.write(bb); + } + + public static long writeData(File file, long offset, byte[] array) throws Exception { + try (FileChannel fc = getFileChannel(file)) { + ByteBuffer bbuf = ByteBuffer.wrap(array); + return writeData(fc, offset, bbuf); + } + } + + // dstFile will keep original size so will remove corresponding bytes.length bytes at end of file + public static File insertBytesRandomlyAfterHeader(File orgFile, String newFileName, byte[] bytes) throws Exception { + long offset = fileHeaderSize + getRandomBetween(0L, 4096L); + File dstFile = new File(newFileName); + try (FileChannel inputChannel = new FileInputStream(orgFile).getChannel(); + FileChannel outputChannel = new FileOutputStream(dstFile).getChannel()) { + long orgSize = inputChannel.size(); + outputChannel.transferFrom(inputChannel, 0, offset); + outputChannel.position(offset); + outputChannel.write(ByteBuffer.wrap(bytes)); + outputChannel.transferFrom(inputChannel, offset + bytes.length, orgSize - bytes.length); + } + return dstFile; + } + + // delete nBytes bytes from offset, so new file will be smaller than the original + public static File deleteBytesAtRandomPositionAfterHeader(File orgFile, String newFileName, int nBytes) throws Exception { + long offset = fileHeaderSize + getRandomBetween(0L, 4096L); + File dstFile = new File(newFileName); + try (FileChannel inputChannel = new FileInputStream(orgFile).getChannel(); + FileChannel outputChannel = new FileOutputStream(dstFile).getChannel()) { + long orgSize = inputChannel.size(); + outputChannel.transferFrom(inputChannel, 0, offset); + inputChannel.position(offset + nBytes); + outputChannel.transferFrom(inputChannel, offset, orgSize - nBytes); + } + return dstFile; + } + + // used region size + public static long usedRegionSizeAligned(File archiveFile, int region) throws Exception { + long offset = spOffset + cdsFileMapRegionSize * region + spUsedOffset; + long used = readInt(archiveFile, offset, sizetSize); + return alignUpWithAlignment(used); + } +} diff --git a/test/lib/jdk/test/lib/classloader/ClassUnloadCommon.java b/test/lib/jdk/test/lib/classloader/ClassUnloadCommon.java index ae8ae12cf977b..11bea44fa33ae 100644 --- a/test/lib/jdk/test/lib/classloader/ClassUnloadCommon.java +++ b/test/lib/jdk/test/lib/classloader/ClassUnloadCommon.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,6 +38,9 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.stream.Stream; public class ClassUnloadCommon { @@ -67,6 +70,41 @@ public static void triggerUnloading() { wb.fullGC(); // will do class unloading } + /** + * Calls triggerUnloading() in a retry loop for 2 seconds or until WhiteBox.isClassAlive + * determines that no classes named in classNames are alive. + * + * This variant of triggerUnloading() accommodates the inherent raciness + * of class unloading. For example, it's possible for a JIT compilation to hold + * strong roots to types (e.g. in virtual call or instanceof profiles) that + * are not released or converted to weak roots until the compilation completes. + * + * @param classNames the set of classes that are expected to be unloaded + * @return the set of classes that have not been unloaded after exiting the retry loop + */ + public static Set triggerUnloading(List classNames) { + WhiteBox wb = WhiteBox.getWhiteBox(); + Set aliveClasses = new HashSet<>(classNames); + int attempt = 0; + while (!aliveClasses.isEmpty() && attempt < 20) { + ClassUnloadCommon.triggerUnloading(); + for (String className : classNames) { + if (aliveClasses.contains(className)) { + if (wb.isClassAlive(className)) { + try { + Thread.sleep(100); + } catch (InterruptedException ex) { + } + } else { + aliveClasses.remove(className); + } + } + } + attempt++; + } + return aliveClasses; + } + /** * Creates a class loader that loads classes from {@code ${test.class.path}} * before delegating to the system class loader. diff --git a/test/lib/jdk/test/lib/cli/CommandLineOptionTest.java b/test/lib/jdk/test/lib/cli/CommandLineOptionTest.java index 31cff4b8c25f2..6e65a3277d53d 100644 --- a/test/lib/jdk/test/lib/cli/CommandLineOptionTest.java +++ b/test/lib/jdk/test/lib/cli/CommandLineOptionTest.java @@ -122,8 +122,8 @@ public static void verifyJVMStartup(String expectedMessages[], outputAnalyzer.shouldHaveExitValue(exitCode.value); } catch (RuntimeException e) { String errorMessage = String.format( - "JVM process should have exit value '%d'.%n%s", - exitCode.value, exitErrorMessage); + "JVM process should have exit value '%d', but has '%d'.%n%s", + exitCode.value, outputAnalyzer.getExitValue(), exitErrorMessage); throw new AssertionError(errorMessage, e); } @@ -300,9 +300,12 @@ public static void verifyOptionValue(String optionName, CommandLineOptionTest.PRINT_FLAGS_FINAL_FORMAT, optionName, expectedValue)); } catch (RuntimeException e) { + String observedValue = outputAnalyzer.firstMatch(String.format( + CommandLineOptionTest.PRINT_FLAGS_FINAL_FORMAT, + optionName, "\\S")); String errorMessage = String.format( - "Option '%s' is expected to have '%s' value%n%s", - optionName, expectedValue, + "Option '%s' is expected to have '%s' value, but is '%s'.%n%s", + optionName, expectedValue, observedValue, optionErrorString); throw new AssertionError(errorMessage, e); } diff --git a/test/lib/jdk/test/lib/compiler/InMemoryJavaCompiler.java b/test/lib/jdk/test/lib/compiler/InMemoryJavaCompiler.java index 6016e48bf4e56..4722ef3b67a95 100644 --- a/test/lib/jdk/test/lib/compiler/InMemoryJavaCompiler.java +++ b/test/lib/jdk/test/lib/compiler/InMemoryJavaCompiler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,11 +26,18 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.io.StringWriter; +import java.io.Writer; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedList; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import javax.tools.FileObject; import javax.tools.ForwardingJavaFileManager; @@ -76,36 +83,6 @@ * */ public class InMemoryJavaCompiler { - private static class MemoryJavaFileObject extends SimpleJavaFileObject { - private final String className; - private final CharSequence sourceCode; - private final ByteArrayOutputStream byteCode; - - public MemoryJavaFileObject(String className, CharSequence sourceCode) { - super(URI.create("string:///" + className.replace('.','/') + Kind.SOURCE.extension), Kind.SOURCE); - this.className = className; - this.sourceCode = sourceCode; - this.byteCode = new ByteArrayOutputStream(); - } - - @Override - public CharSequence getCharContent(boolean ignoreEncodingErrors) { - return sourceCode; - } - - @Override - public OutputStream openOutputStream() throws IOException { - return byteCode; - } - - public byte[] getByteCode() { - return byteCode.toByteArray(); - } - - public String getClassName() { - return className; - } - } private static class FileManagerWrapper extends ForwardingJavaFileManager { private static final Location PATCH_LOCATION = new Location() { @@ -119,12 +96,13 @@ public boolean isOutputLocation() { return false; } }; - private final MemoryJavaFileObject file; + private final SourceFile srcFile; + private ClassFile clsFile; private final String moduleOverride; - public FileManagerWrapper(MemoryJavaFileObject file, String moduleOverride) { + public FileManagerWrapper(SourceFile file, String moduleOverride) { super(getCompiler().getStandardFileManager(null, null, null)); - this.file = file; + this.srcFile = file; this.moduleOverride = moduleOverride; } @@ -132,16 +110,17 @@ public FileManagerWrapper(MemoryJavaFileObject file, String moduleOverride) { public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException { - if (!file.getClassName().equals(className)) { - throw new IOException("Expected class with name " + file.getClassName() + + if (!srcFile.getClassName().equals(className)) { + throw new IOException("Expected class with name " + srcFile.getClassName() + ", but got " + className); } - return file; + clsFile = new ClassFile(className); + return clsFile; } @Override public Location getLocationForModule(Location location, JavaFileObject fo) throws IOException { - if (fo == file && moduleOverride != null) { + if (fo == srcFile && moduleOverride != null) { return PATCH_LOCATION; } return super.getLocationForModule(location, fo); @@ -160,6 +139,100 @@ public boolean hasLocation(Location location) { return super.hasLocation(location) || location == StandardLocation.PATCH_MODULE_PATH; } + public byte[] getByteCode() { + return clsFile.toByteArray(); + } + + } + + // Wraper for class file + static class ClassFile extends SimpleJavaFileObject { + + private final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + protected ClassFile(String name) { + super(URI.create("memo:///" + name.replace('.', '/') + Kind.CLASS.extension), Kind.CLASS); + } + + @Override + public ByteArrayOutputStream openOutputStream() { return this.baos; } + + byte[] toByteArray() { return baos.toByteArray(); } + } + + // File manager which spawns ClassFile instances by demand + static class FileManager extends ForwardingJavaFileManager { + + private Map classesMap = new HashMap(); + + protected FileManager(JavaFileManager fileManager) { + super(fileManager); + } + + @Override + public ClassFile getJavaFileForOutput(Location location, String name, JavaFileObject.Kind kind, FileObject source) { + ClassFile classFile = new ClassFile(name); + classesMap.put(name, classFile); + return classFile; + } + + public Map getByteCode() { + Map result = new HashMap(); + for (Entry entry : classesMap.entrySet()) { + result.put(entry.getKey(), entry.getValue().toByteArray()); + } + return result; + } + } + + // Wrapper for source file + static class SourceFile extends SimpleJavaFileObject { + + private CharSequence sourceCode; + private String className; + + public SourceFile(String name, CharSequence sourceCode) { + super(URI.create("memo:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE); + this.sourceCode = sourceCode; + this.className = name; + } + + @Override + public CharSequence getCharContent(boolean ignore) { + return this.sourceCode; + } + + public String getClassName() { + return this.className; + } + } + + /** + * Compiles the list of classes with the given map of name and source code. + * This overloaded version of compile is useful for batch compile use cases. + * + * @param inputMap The map containing the name of the class and corresponding source code + * @throws RuntimeException if the compilation did not succeed + * @return The resulting byte code from the compilation + */ + public static Map compile(Map inputMap) { + Collection sourceFiles = new LinkedList(); + for (Entry entry : inputMap.entrySet()) { + sourceFiles.add(new SourceFile(entry.getKey(), entry.getValue())); + } + + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + FileManager fileManager = new FileManager(compiler.getStandardFileManager(null, null, null)); + + Writer writer = new StringWriter(); + Boolean exitCode = compiler.getTask(writer, fileManager, null, null, null, sourceFiles).call(); + if (!exitCode) { + System.out.println("*********** javac output begin ***********"); + System.out.println(writer.toString()); + System.out.println("*********** javac output end ***********"); + throw new RuntimeException("Test bug: in memory compilation failed."); + } + return fileManager.getByteCode(); } /** @@ -173,7 +246,7 @@ public boolean hasLocation(Location location) { * @return The resulting byte code from the compilation */ public static byte[] compile(String className, CharSequence sourceCode, String... options) { - MemoryJavaFileObject file = new MemoryJavaFileObject(className, sourceCode); + SourceFile file = new SourceFile(className, sourceCode); List opts = new ArrayList<>(); String moduleOverride = null; for (String opt : options) { @@ -183,13 +256,13 @@ public static byte[] compile(String className, CharSequence sourceCode, String.. opts.add(opt); } } - try (JavaFileManager fileManager = new FileManagerWrapper(file, moduleOverride)) { + try (FileManagerWrapper fileManager = new FileManagerWrapper(file, moduleOverride)) { CompilationTask task = getCompiler().getTask(null, fileManager, null, opts, null, Arrays.asList(file)); if (!task.call()) { throw new RuntimeException("Could not compile " + className + " with source code " + sourceCode); } - return file.getByteCode(); + return fileManager.getByteCode(); } catch (IOException ioe) { throw new RuntimeException(ioe); } diff --git a/test/lib/jdk/test/lib/containers/cgroup/MetricsTesterCgroupV2.java b/test/lib/jdk/test/lib/containers/cgroup/MetricsTesterCgroupV2.java index a3723e2eda274..2a756102dede8 100644 --- a/test/lib/jdk/test/lib/containers/cgroup/MetricsTesterCgroupV2.java +++ b/test/lib/jdk/test/lib/containers/cgroup/MetricsTesterCgroupV2.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Red Hat Inc. + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -447,13 +448,13 @@ private void testIOStat() { Metrics metrics = Metrics.systemMetrics(); long oldVal = metrics.getBlkIOServiceCount(); long newVal = getIoStatAccumulate(new String[] { "rios", "wios" }); - if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) { + if (newVal < oldVal) { fail("io.stat->rios/wios: ", oldVal, newVal); } oldVal = metrics.getBlkIOServiced(); newVal = getIoStatAccumulate(new String[] { "rbytes", "wbytes" }); - if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) { + if (newVal < oldVal) { fail("io.stat->rbytes/wbytes: ", oldVal, newVal); } } diff --git a/test/lib/jdk/test/lib/containers/docker/DockerTestUtils.java b/test/lib/jdk/test/lib/containers/docker/DockerTestUtils.java index efabd79296420..2404394690ebd 100644 --- a/test/lib/jdk/test/lib/containers/docker/DockerTestUtils.java +++ b/test/lib/jdk/test/lib/containers/docker/DockerTestUtils.java @@ -275,7 +275,6 @@ public static OutputAnalyzer execute(List command) throws Exception { * @throws Exception */ public static OutputAnalyzer execute(String... command) throws Exception { - ProcessBuilder pb = new ProcessBuilder(command); System.out.println("[COMMAND]\n" + Utils.getCommandLine(pb)); @@ -284,14 +283,19 @@ public static OutputAnalyzer execute(String... command) throws Exception { long pid = p.pid(); OutputAnalyzer output = new OutputAnalyzer(p); - String stdoutLogFile = String.format("docker-stdout-%d.log", pid); + int max = MAX_LINES_TO_COPY_FOR_CHILD_STDOUT; + String stdout = output.getStdout(); + String stdoutLimited = limitLines(stdout, max); System.out.println("[ELAPSED: " + (System.currentTimeMillis() - started) + " ms]"); System.out.println("[STDERR]\n" + output.getStderr()); - System.out.println("[STDOUT]\n" + - trimLines(output.getStdout(),MAX_LINES_TO_COPY_FOR_CHILD_STDOUT)); - System.out.printf("Child process STDOUT is trimmed to %d lines \n", - MAX_LINES_TO_COPY_FOR_CHILD_STDOUT); - writeOutputToFile(output.getStdout(), stdoutLogFile); + System.out.println("[STDOUT]\n" + stdoutLimited); + if (stdout != stdoutLimited) { + System.out.printf("Child process STDOUT is limited to %d lines\n", + max); + } + + String stdoutLogFile = String.format("docker-stdout-%d.log", pid); + writeOutputToFile(stdout, stdoutLogFile); System.out.println("Full child process STDOUT was saved to " + stdoutLogFile); return output; @@ -305,7 +309,7 @@ private static void writeOutputToFile(String output, String fileName) throws Exc } - private static String trimLines(String buffer, int nrOfLines) { + private static String limitLines(String buffer, int nrOfLines) { List l = Arrays.asList(buffer.split("\\R")); if (l.size() < nrOfLines) { return buffer; diff --git a/test/jdk/jdk/jfr/tool/JSONValue.java b/test/lib/jdk/test/lib/json/JSONValue.java similarity index 99% rename from test/jdk/jdk/jfr/tool/JSONValue.java rename to test/lib/jdk/test/lib/json/JSONValue.java index 27c63116c8825..d1092f06c3e13 100644 --- a/test/jdk/jdk/jfr/tool/JSONValue.java +++ b/test/lib/jdk/test/lib/json/JSONValue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -20,7 +20,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package jdk.jfr.tool; +package jdk.test.lib.json; import java.util.ArrayList; import java.util.HashMap; diff --git a/test/lib/jdk/test/lib/process/OutputAnalyzer.java b/test/lib/jdk/test/lib/process/OutputAnalyzer.java index f554b969f4eee..5ea90a1c6db9c 100644 --- a/test/lib/jdk/test/lib/process/OutputAnalyzer.java +++ b/test/lib/jdk/test/lib/process/OutputAnalyzer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -106,6 +106,14 @@ public OutputAnalyzer(String stdout, String stderr, int exitValue) buffer = OutputBuffer.of(stdout, stderr, exitValue); } + /** + * Delegate waitFor to the OutputBuffer. This ensures that + * the progress and timestamps are logged correctly. + */ + public void waitFor() { + buffer.waitFor(); + } + /** * Verify that the stdout contents of output buffer is empty * @@ -207,6 +215,13 @@ public OutputAnalyzer stderrShouldNotBeEmpty() { return this; } + /** + * Returns true if stdout contains the given string + */ + public boolean stdoutContains(String expectedString) { + return getStdout().contains(expectedString); + } + /** * Verify that the stdout and stderr contents of output buffer contains the string * @@ -626,7 +641,7 @@ public OutputAnalyzer shouldBeEmptyIgnoreVMWarnings() { /** * Verify that the stderr contents of output buffer matches the pattern, - * after filtering out the Hotespot warning messages + * after filtering out the Hotspot warning messages * * @param pattern * @throws RuntimeException If the pattern was not found @@ -642,6 +657,24 @@ public OutputAnalyzer stderrShouldMatchIgnoreVMWarnings(String pattern) { return this; } + /** + * Verify that the stderr contents of output buffer matches the pattern, + * after filtering out the Hotspot deprecation warning messages + * + * @param pattern + * @throws RuntimeException If the pattern was not found + */ + public OutputAnalyzer stderrShouldMatchIgnoreDeprecatedWarnings(String pattern) { + String stderr = getStderr().replaceAll(deprecatedmsg + "\\R", ""); + Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stderr); + if (!matcher.find()) { + reportDiagnosticSummary(); + throw new RuntimeException("'" + pattern + + "' missing from stderr"); + } + return this; + } + /** * Returns the contents of the output buffer (stdout and stderr), without those * JVM warning msgs, as list of strings. Output is split by newlines. diff --git a/test/lib/jdk/test/lib/process/OutputBuffer.java b/test/lib/jdk/test/lib/process/OutputBuffer.java index fba2a5f6dcf3f..f032591f3584c 100644 --- a/test/lib/jdk/test/lib/process/OutputBuffer.java +++ b/test/lib/jdk/test/lib/process/OutputBuffer.java @@ -44,6 +44,12 @@ public OutputBufferException(Throwable cause) { } } + /** + * Waits for a process to finish, if there is one assocated with + * this OutputBuffer. + */ + public void waitFor(); + /** * Returns the stdout result * @@ -67,6 +73,13 @@ default public List getStdoutAsList() { * @return stderr result */ public String getStderr(); + + + /** + * Returns the exit value + * + * @return exit value + */ public int getExitValue(); /** @@ -136,20 +149,12 @@ private LazyOutputBuffer(Process p, Charset cs) { } @Override - public String getStdout() { - return outTask.get(); - } - - @Override - public String getStderr() { - return errTask.get(); - } - - @Override - public int getExitValue() { + public void waitFor() { if (exitValue != null) { - return exitValue; + // Already waited for this process + return; } + try { logProgress("Waiting for completion"); boolean aborted = true; @@ -157,7 +162,6 @@ public int getExitValue() { exitValue = p.waitFor(); logProgress("Waiting for completion finished"); aborted = false; - return exitValue; } finally { if (aborted) { logProgress("Waiting for completion FAILED"); @@ -169,6 +173,22 @@ public int getExitValue() { } } + @Override + public String getStdout() { + return outTask.get(); + } + + @Override + public String getStderr() { + return errTask.get(); + } + + @Override + public int getExitValue() { + waitFor(); + return exitValue; + } + @Override public long pid() { return p.pid(); @@ -186,6 +206,11 @@ private EagerOutputBuffer(String stdout, String stderr, int exitValue) { this.exitValue = exitValue; } + @Override + public void waitFor() { + // Nothing to do since this buffer is not associated with a Process. + } + @Override public String getStdout() { return stdout; diff --git a/test/lib/jdk/test/lib/process/ProcessTools.java b/test/lib/jdk/test/lib/process/ProcessTools.java index 439539e7009af..c508af1036eb9 100644 --- a/test/lib/jdk/test/lib/process/ProcessTools.java +++ b/test/lib/jdk/test/lib/process/ProcessTools.java @@ -401,12 +401,21 @@ private static void printStack(Thread t, StackTraceElement[] stack) { } /** - * Create ProcessBuilder using the java launcher from the jdk to be tested. - * The default jvm options from jtreg, test.vm.opts and test.java.opts, are added. - *

    - * The command line will be like: - * {test.jdk}/bin/java {test.vm.opts} {test.java.opts} cmds - * Create ProcessBuilder using the java launcher from the jdk to be tested. + * Create ProcessBuilder using the java launcher from the jdk to + * be tested. The default jvm options from jtreg, test.vm.opts and + * test.java.opts, are added. + * + *

    Unless the "test.noclasspath" property is "true" the + * classpath property "java.class.path" is appended to the command + * line and the environment of the ProcessBuilder is modified to + * remove "CLASSPATH". If the property "test.thread.factory" is + * provided the command args are updated and appended to invoke + * ProcessTools main() and provide the name of the thread factory. + * + *

    The "-Dtest.thread.factory" is appended to the arguments + * with the thread factory value. The remaining command args are + * scanned for unsupported options and are appended to the + * ProcessBuilder. * * @param command Arguments to pass to the java command. * @return The ProcessBuilder instance representing the java command. @@ -416,12 +425,21 @@ public static ProcessBuilder createTestJavaProcessBuilder(List command) } /** - * Create ProcessBuilder using the java launcher from the jdk to be tested. - * The default jvm options from jtreg, test.vm.opts and test.java.opts, are added. - *

    - * The command line will be like: - * {test.jdk}/bin/java {test.vm.opts} {test.java.opts} cmds - * Create ProcessBuilder using the java launcher from the jdk to be tested. + * Create ProcessBuilder using the java launcher from the jdk to + * be tested. The default jvm options from jtreg, test.vm.opts and + * test.java.opts, are added. + * + *

    Unless the "test.noclasspath" property is "true" the + * classpath property "java.class.path" is appended to the command + * line and the environment of the ProcessBuilder is modified to + * remove "CLASSPATH". If the property "test.thread.factory" is + * provided the command args are updated and appended to invoke + * ProcessTools main() and provide the name of the thread factory. + * + *

    The "-Dtest.thread.factory" is appended to the arguments + * with the thread factory value. The remaining command args are + * scanned for unsupported options and are appended to the + * ProcessBuilder. * * @param command Arguments to pass to the java command. * @return The ProcessBuilder instance representing the java command. @@ -445,6 +463,18 @@ public static ProcessBuilder createTestJavaProcessBuilder(String... command) { * it in combination with @requires vm.flagless JTREG * anotation as to not waste energy and test resources. * + *

    Unless the "test.noclasspath" property is "true" the + * classpath property "java.class.path" is appended to the command + * line and the environment of the ProcessBuilder is modified to + * remove "CLASSPATH". If the property "test.thread.factory" is + * provided the command args are updated and appended to invoke + * ProcessTools main() and provide the name of the thread factory. + * + *

    The "-Dtest.thread.factory" is appended to the arguments + * with the thread factory value. The remaining command args are + * scanned for unsupported options and are appended to the + * ProcessBuilder. + * * @param command Arguments to pass to the java command. * @return The ProcessBuilder instance representing the java command. */ @@ -467,6 +497,18 @@ public static ProcessBuilder createLimitedTestJavaProcessBuilder(List co * it in combination with @requires vm.flagless JTREG * anotation as to not waste energy and test resources. * + *

    Unless the "test.noclasspath" property is "true" the + * classpath property "java.class.path" is appended to the command + * line and the environment of the ProcessBuilder is modified to + * remove "CLASSPATH". If the property "test.thread.factory" is + * provided the command args are updated and appended to invoke + * ProcessTools main() and provide the name of the thread factory. + * + *

    The "-Dtest.thread.factory" is appended to the arguments + * with the thread factory value. The remaining command args are + * scanned for unsupported options and are appended to the + * ProcessBuilder. + * * @param command Arguments to pass to the java command. * @return The ProcessBuilder instance representing the java command. */ @@ -475,47 +517,69 @@ public static ProcessBuilder createLimitedTestJavaProcessBuilder(String... comma } /** - * Executes a test jvm process, waits for it to finish and returns the process output. - * The default jvm options from jtreg, test.vm.opts and test.java.opts, are added. - * The java from the test.jdk is used to execute the command. - *

    - * The command line will be like: - * {test.jdk}/bin/java {test.vm.opts} {test.java.opts} cmds - *

    - * The jvm process will have exited before this method returns. + * Executes a process using the java launcher from the jdk to + * be tested, waits for it to finish and returns + * the process output. + * + *

    The process is created using runtime flags set up by: + * {@link #createTestJavaProcessBuilder(String...)}. The + * jvm process will have exited before this method returns. * - * @param cmds User specified arguments. + * @param command User specified arguments. * @return The output from the process. */ - public static OutputAnalyzer executeTestJvm(List cmds) throws Exception { - return executeTestJvm(cmds.toArray(String[]::new)); + public static OutputAnalyzer executeTestJava(List command) throws Exception { + return executeTestJava(command.toArray(String[]::new)); } /** - * Executes a test jvm process, waits for it to finish and returns the process output. - * The default jvm options from jtreg, test.vm.opts and test.java.opts, are added. - * The java from the test.jdk is used to execute the command. - *

    - * The command line will be like: - * {test.jdk}/bin/java {test.vm.opts} {test.java.opts} cmds - *

    - * The jvm process will have exited before this method returns. + * Executes a process using the java launcher from the jdk to + * be tested, waits for it to finish and returns + * the process output. * - * @param cmds User specified arguments. + *

    The process is created using runtime flags set up by: + * {@link #createTestJavaProcessBuilder(String...)}. The + * jvm process will have exited before this method returns. + * + * @param command User specified arguments. * @return The output from the process. */ - public static OutputAnalyzer executeTestJvm(String... cmds) throws Exception { - ProcessBuilder pb = createTestJavaProcessBuilder(cmds); + public static OutputAnalyzer executeTestJava(String... command) throws Exception { + ProcessBuilder pb = createTestJavaProcessBuilder(command); return executeProcess(pb); } /** - * @param cmds User specified arguments. + * Executes a process using the java launcher from the jdk to + * be tested, waits for it to finish and returns + * the process output. + * + *

    The process is created using runtime flags set up by: + * {@link #createLimitedTestJavaProcessBuilder(String...)}. The + * jvm process will have exited before this method returns. + * + * @param command User specified arguments. + * @return The output from the process. + */ + public static OutputAnalyzer executeLimitedTestJava(List command) throws Exception { + return executeLimitedTestJava(command.toArray(String[]::new)); + } + + /** + * Executes a process using the java launcher from the jdk to + * be tested, waits for it to finish and returns + * the process output. + * + *

    The process is created using runtime flags set up by: + * {@link #createLimitedTestJavaProcessBuilder(String...)}. The + * jvm process will have exited before this method returns. + * + * @param command User specified arguments. * @return The output from the process. - * @see #executeTestJvm(String...) */ - public static OutputAnalyzer executeTestJava(String... cmds) throws Exception { - return executeTestJvm(cmds); + public static OutputAnalyzer executeLimitedTestJava(String... command) throws Exception { + ProcessBuilder pb = createLimitedTestJavaProcessBuilder(command); + return executeProcess(pb); } /** @@ -568,7 +632,10 @@ public static OutputAnalyzer executeProcess(ProcessBuilder pb, String input, } output = new OutputAnalyzer(p, cs); - p.waitFor(); + + // Wait for the process to finish. Call through the output + // analyzer to get correct logging and timestamps. + output.waitFor(); { // Dumping the process output to a separate file var fileName = String.format("pid-%d-output.log", p.pid()); diff --git a/test/jdk/java/security/testlibrary/CertificateBuilder.java b/test/lib/jdk/test/lib/security/CertificateBuilder.java similarity index 83% rename from test/jdk/java/security/testlibrary/CertificateBuilder.java rename to test/lib/jdk/test/lib/security/CertificateBuilder.java index 53c36531da18d..60253d79648ab 100644 --- a/test/jdk/java/security/testlibrary/CertificateBuilder.java +++ b/test/lib/jdk/test/lib/security/CertificateBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,15 +23,15 @@ * questions. */ -package sun.security.testlibrary; +package jdk.test.lib.security; import java.io.*; +import java.security.cert.*; +import java.security.cert.Extension; import java.util.*; import java.security.*; -import java.security.cert.X509Certificate; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.Extension; +import java.time.temporal.ChronoUnit; +import java.time.Instant; import javax.security.auth.x500.X500Principal; import java.math.BigInteger; @@ -43,6 +43,7 @@ import sun.security.x509.AlgorithmId; import sun.security.x509.AuthorityInfoAccessExtension; import sun.security.x509.AuthorityKeyIdentifierExtension; +import sun.security.x509.IPAddressName; import sun.security.x509.SubjectKeyIdentifierExtension; import sun.security.x509.BasicConstraintsExtension; import sun.security.x509.ExtendedKeyUsageExtension; @@ -54,6 +55,8 @@ import sun.security.x509.SubjectAlternativeNameExtension; import sun.security.x509.URIName; import sun.security.x509.KeyIdentifier; +import sun.security.x509.X500Name; + /** * Helper class that builds and signs X.509 certificates. @@ -90,7 +93,7 @@ public class CertificateBuilder { private final CertificateFactory factory; - private X500Principal subjectName = null; + private X500Name subjectName = null; private BigInteger serialNumber = null; private PublicKey publicKey = null; private Date notBefore = null; @@ -99,6 +102,89 @@ public class CertificateBuilder { private byte[] tbsCertBytes; private byte[] signatureBytes; + public enum KeyUsage { + DIGITAL_SIGNATURE, + NONREPUDIATION, + KEY_ENCIPHERMENT, + DATA_ENCIPHERMENT, + KEY_AGREEMENT, + KEY_CERT_SIGN, + CRL_SIGN, + ENCIPHER_ONLY, + DECIPHER_ONLY; + } + + /** + * Create a new CertificateBuilder instance. This method sets the subject name, + * public key, authority key id, and serial number. + * + * @param subjectName entity associated with the public key + * @param publicKey the entity's public key + * @param caKey public key of certificate signer + * @param keyUsages list of key uses + * @return + * @throws CertificateException + * @throws IOException + */ + public static CertificateBuilder newCertificateBuilder(String subjectName, + PublicKey publicKey, PublicKey caKey, KeyUsage... keyUsages) + throws CertificateException, IOException { + SecureRandom random = new SecureRandom(); + + boolean [] keyUsage = new boolean[KeyUsage.values().length]; + for (KeyUsage ku : keyUsages) { + keyUsage[ku.ordinal()] = true; + } + + CertificateBuilder builder = new CertificateBuilder() + .setSubjectName(subjectName) + .setPublicKey(publicKey) + .setSerialNumber(BigInteger.valueOf(random.nextLong(1000000)+1)) + .addSubjectKeyIdExt(publicKey) + .addAuthorityKeyIdExt(caKey); + if (keyUsages.length != 0) { + builder.addKeyUsageExt(keyUsage); + } + return builder; + } + + /** + * Create a Subject Alternative Name extension for the given DNS name + * @param critical Sets the extension to critical or non-critical + * @param dnsName DNS name to use in the extension + * @throws IOException + */ + public static SubjectAlternativeNameExtension createDNSSubjectAltNameExt( + boolean critical, String dnsName) throws IOException { + GeneralNames gns = new GeneralNames(); + gns.add(new GeneralName(new DNSName(dnsName))); + return new SubjectAlternativeNameExtension(critical, gns); + } + + /** + * Create a Subject Alternative Name extension for the given IP address + * @param critical Sets the extension to critical or non-critical + * @param ipAddress IP address to use in the extension + * @throws IOException + */ + public static SubjectAlternativeNameExtension createIPSubjectAltNameExt( + boolean critical, String ipAddress) throws IOException { + GeneralNames gns = new GeneralNames(); + gns.add(new GeneralName(new IPAddressName(ipAddress))); + return new SubjectAlternativeNameExtension(critical, gns); + } + + public static void printCertificate(X509Certificate certificate, PrintStream ps) { + try { + Base64.Encoder encoder = Base64.getEncoder(); + ps.println("-----BEGIN CERTIFICATE-----"); + ps.println(encoder.encodeToString(certificate.getEncoded())); + ps.println("-----END CERTIFICATE-----"); + } catch (CertificateEncodingException exc) { + exc.printStackTrace(ps); + } + } + /** * Default constructor for a {@code CertificateBuilder} object. * @@ -116,7 +202,7 @@ public CertificateBuilder() throws CertificateException { * on this certificate. */ public CertificateBuilder setSubjectName(X500Principal name) { - subjectName = name; + subjectName = X500Name.asX500Name(name); return this; } @@ -126,7 +212,23 @@ public CertificateBuilder setSubjectName(X500Principal name) { * @param name The subject name in RFC 2253 format */ public CertificateBuilder setSubjectName(String name) { - subjectName = new X500Principal(name); + try { + subjectName = new X500Name(name); + } catch (IOException ioe) { + throw new IllegalArgumentException(ioe); + } + return this; + } + + /** + * Set the subject name for the certificate. This method is useful when + * you need more control over the contents of the subject name. + * + * @param name an {@code X500Name} to be used as the subject name + * on this certificate + */ + public CertificateBuilder setSubjectName(X500Name name) { + subjectName = name; return this; } @@ -176,6 +278,11 @@ public CertificateBuilder setValidity(Date nbDate, Date naDate) { return setNotBefore(nbDate).setNotAfter(naDate); } + public CertificateBuilder setOneHourValidity() { + return setNotBefore(Date.from(Instant.now().minus(5, ChronoUnit.MINUTES))) + .setNotAfter(Date.from(Instant.now().plus(1, ChronoUnit.HOURS))); + } + /** * Set the serial number on the certificate. * diff --git a/test/jdk/java/security/testlibrary/HumanInputStream.java b/test/lib/jdk/test/lib/security/HumanInputStream.java similarity index 98% rename from test/jdk/java/security/testlibrary/HumanInputStream.java rename to test/lib/jdk/test/lib/security/HumanInputStream.java index 50e5dd0bcc8c4..c68f515881e05 100644 --- a/test/jdk/java/security/testlibrary/HumanInputStream.java +++ b/test/lib/jdk/test/lib/security/HumanInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,6 +21,7 @@ * questions. */ +package jdk.test.lib.security; import java.io.BufferedReader; import java.io.IOException; @@ -82,6 +83,7 @@ public HumanInputStream(String input) { } return re; } + @Override public int read(byte[] buffer, int offset, int len) { inLine = true; try { @@ -92,6 +94,7 @@ public HumanInputStream(String input) { inLine = false; } } + @Override public int available() { if (pos < length) return 1; return 0; diff --git a/test/lib/jdk/test/lib/security/OpensslArtifactFetcher.java b/test/lib/jdk/test/lib/security/OpensslArtifactFetcher.java index 55ff8c9ef98b0..f0356fbe485f4 100644 --- a/test/lib/jdk/test/lib/security/OpensslArtifactFetcher.java +++ b/test/lib/jdk/test/lib/security/OpensslArtifactFetcher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,53 +23,68 @@ package jdk.test.lib.security; -import java.io.File; - +import java.nio.file.Path; import jdk.test.lib.Platform; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.artifacts.Artifact; import jdk.test.lib.artifacts.ArtifactResolver; -import jdk.test.lib.artifacts.ArtifactResolverException; +import jtreg.SkippedException; public class OpensslArtifactFetcher { + private static final String OPENSSL_BUNDLE_VERSION = "3.0.14"; + private static final String OPENSSL_ORG = "jpg.tests.jdk.openssl"; + /** - * Gets the openssl binary path of version 1.1.* + * Gets the openssl binary path of OPENSSL_BUNDLE_VERSION * * Openssl selection flow: 1. Check whether property test.openssl.path is set and it's the - preferred version(1.1.*) of openssl, then return that path. - 2. Else look for already installed openssl (version 1.1.*) in system + current version of openssl, then return that path. + 2. Else look for already installed openssl in system path /usr/bin/openssl or /usr/local/bin/openssl, then return that path. - 3. Else try to download openssl (version 1.1.*) from the artifactory + 3. Else try to download the current version of openssl from the artifactory and return that path, if download fails then return null. * - * @return openssl binary path of version 1.1.* + * @return openssl binary path of the current version + * @throws SkippedException if a valid version of OpenSSL cannot be found */ - public static String getOpenssl1dot1dotStar() { - String version = "1.1."; - String path = getOpensslFromSystemProp(version); + public static String getOpensslPath() { + String path = getOpensslFromSystemProp(OPENSSL_BUNDLE_VERSION); if (path != null) { return path; - } else { - path = getDefaultSystemOpensslPath(version); - if (path != null) { - return path; - } else if (Platform.is64bit()) { - if (Platform.isLinux()) { - path = fetchOpenssl(LINUX_X64.class); - } else if (Platform.isOSX()) { - path = fetchOpenssl(MACOSX_X64.class); - } else if (Platform.isWindows()) { - path = fetchOpenssl(WINDOWS_X64.class); - } - if (verifyOpensslVersion(path, version)) { - return path; - } + } + path = getDefaultSystemOpensslPath(OPENSSL_BUNDLE_VERSION); + if (path != null) { + return path; + } + if (Platform.isX64()) { + if (Platform.isLinux()) { + path = fetchOpenssl(LINUX_X64.class); + } else if (Platform.isOSX()) { + path = fetchOpenssl(MACOSX_X64.class); + } else if (Platform.isWindows()) { + path = fetchOpenssl(WINDOWS_X64.class); + } + } else if (Platform.isAArch64()) { + if (Platform.isLinux()) { + path = fetchOpenssl(LINUX_AARCH64.class); + } + if (Platform.isOSX()) { + path = fetchOpenssl(MACOSX_AARCH64.class); } } - return null; + + if (!verifyOpensslVersion(path, OPENSSL_BUNDLE_VERSION)) { + String exMsg = "Can't find the version: " + + OpensslArtifactFetcher.getTestOpensslBundleVersion() + + " of openssl binary on this machine, please install" + + " and set openssl path with property 'test.openssl.path'"; + throw new SkippedException(exMsg); + } else { + return path; + } } private static String getOpensslFromSystemProp(String version) { @@ -105,43 +120,57 @@ private static boolean verifyOpensslVersion(String path, String version) { } private static String fetchOpenssl(Class clazz) { - String path = null; - try { - path = ArtifactResolver.resolve(clazz).entrySet().stream() - .findAny().get().getValue() + File.separator + "openssl" - + File.separator + "bin" + File.separator + "openssl"; - System.out.println("path: " + path); - } catch (ArtifactResolverException e) { - Throwable cause = e.getCause(); - if (cause == null) { - System.out.println("Cannot resolve artifact, " - + "please check if JIB jar is present in classpath."); - } else { - throw new RuntimeException("Fetch artifact failed: " + clazz - + "\nPlease make sure the artifact is available.", e); - } + return ArtifactResolver.fetchOne(clazz) + .resolve("openssl").resolve("bin").resolve("openssl") + .toString(); + } + + // retrieve the provider directory path from /bin/openssl + public static Path getProviderPath(String opensslPath) { + Path openSslRootPath = Path.of(opensslPath).getParent().getParent(); + String libDir = "lib"; + if (Platform.isX64() && (Platform.isLinux() || Platform.isWindows())) { + libDir = "lib64"; } - return path; + return openSslRootPath.resolve(libDir).resolve("ossl-modules"); + } + + public static String getTestOpensslBundleVersion() { + return OPENSSL_BUNDLE_VERSION; } @Artifact( - organization = "jpg.tests.jdk.openssl", + organization = OPENSSL_ORG, name = "openssl-linux_x64", - revision = "1.1.1g", + revision = OPENSSL_BUNDLE_VERSION, extension = "zip") private static class LINUX_X64 { } @Artifact( - organization = "jpg.tests.jdk.openssl", + organization = OPENSSL_ORG, + name = "openssl-linux_aarch64", + revision = OPENSSL_BUNDLE_VERSION, + extension = "zip") + private static class LINUX_AARCH64{ } + + @Artifact( + organization = OPENSSL_ORG, name = "openssl-macosx_x64", - revision = "1.1.1g", + revision = OPENSSL_BUNDLE_VERSION, extension = "zip") private static class MACOSX_X64 { } @Artifact( - organization = "jpg.tests.jdk.openssl", + organization = OPENSSL_ORG, + name = "openssl-macosx_aarch64", + revision = OPENSSL_BUNDLE_VERSION, + extension = "zip") + private static class MACOSX_AARCH64 { } + + @Artifact( + organization = OPENSSL_ORG, name = "openssl-windows_x64", - revision = "1.1.1g", + revision = OPENSSL_BUNDLE_VERSION, extension = "zip") private static class WINDOWS_X64 { } } diff --git a/test/jdk/java/security/testlibrary/Providers.java b/test/lib/jdk/test/lib/security/Providers.java similarity index 92% rename from test/jdk/java/security/testlibrary/Providers.java rename to test/lib/jdk/test/lib/security/Providers.java index b3e9f3e96b645..42dd698403649 100644 --- a/test/jdk/java/security/testlibrary/Providers.java +++ b/test/lib/jdk/test/lib/security/Providers.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,6 +21,8 @@ * questions. */ +package jdk.test.lib.security; + import java.security.Provider; import java.security.Security; diff --git a/test/jdk/java/security/testlibrary/ProvidersSnapshot.java b/test/lib/jdk/test/lib/security/ProvidersSnapshot.java similarity index 93% rename from test/jdk/java/security/testlibrary/ProvidersSnapshot.java rename to test/lib/jdk/test/lib/security/ProvidersSnapshot.java index 33c45329e3f48..cef02f814befb 100644 --- a/test/jdk/java/security/testlibrary/ProvidersSnapshot.java +++ b/test/lib/jdk/test/lib/security/ProvidersSnapshot.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,6 +21,8 @@ * questions. */ +package jdk.test.lib.security; + import java.security.Provider; import java.security.Security; diff --git a/test/jdk/java/security/testlibrary/SimpleOCSPServer.java b/test/lib/jdk/test/lib/security/SimpleOCSPServer.java similarity index 99% rename from test/jdk/java/security/testlibrary/SimpleOCSPServer.java rename to test/lib/jdk/test/lib/security/SimpleOCSPServer.java index b2a7d1ca80fd7..ded4f6cf5e766 100644 --- a/test/jdk/java/security/testlibrary/SimpleOCSPServer.java +++ b/test/lib/jdk/test/lib/security/SimpleOCSPServer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ * questions. */ -package sun.security.testlibrary; +package jdk.test.lib.security; import java.io.*; import java.net.*; diff --git a/test/lib/jdk/test/lib/security/XMLUtils.java b/test/lib/jdk/test/lib/security/XMLUtils.java index 4616dfa00a4ea..ad9e2c2ec9e73 100644 --- a/test/lib/jdk/test/lib/security/XMLUtils.java +++ b/test/lib/jdk/test/lib/security/XMLUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -198,6 +198,7 @@ public static class Signer { String dm = DigestMethod.SHA256; String cm = CanonicalizationMethod.EXCLUSIVE; String tr = Transform.ENVELOPED; + Map props = new HashMap<>(); public Signer(PrivateKey privateKey) { this.privateKey = Objects.requireNonNull(privateKey); @@ -247,6 +248,11 @@ public Signer sm(String method) throws Exception { return sm(method, null); } + public Signer prop(String name, Object o) { + props.put(name, o); + return this; + } + // Signs different sources // Signs an XML file in detached mode @@ -254,7 +260,7 @@ public Document sign(URI uri) throws Exception { Document newDocument = DocumentBuilderFactory.newInstance() .newDocumentBuilder().newDocument(); FAC.newXMLSignature(buildSignedInfo(uri.toString()), buildKeyInfo()).sign( - new DOMSignContext(privateKey, newDocument)); + withProps(new DOMSignContext(privateKey, newDocument))); return newDocument; } @@ -264,7 +270,8 @@ public Document sign(URI base, URI ref) throws Exception { .newDocumentBuilder().newDocument(); DOMSignContext ctxt = new DOMSignContext(privateKey, newDocument); ctxt.setBaseURI(base.toString()); - FAC.newXMLSignature(buildSignedInfo(ref.toString()), buildKeyInfo()).sign(ctxt); + FAC.newXMLSignature(buildSignedInfo(ref.toString()), buildKeyInfo()) + .sign(withProps(ctxt)); return newDocument; } @@ -275,7 +282,7 @@ public Document sign(Document document) throws Exception { .transform(new DOMSource(document), result); Document newDocument = (Document) result.getNode(); FAC.newXMLSignature(buildSignedInfo(""), buildKeyInfo()).sign( - new DOMSignContext(privateKey, newDocument.getDocumentElement())); + withProps(new DOMSignContext(privateKey, newDocument.getDocumentElement()))); return newDocument; } @@ -290,7 +297,7 @@ public Document signEnveloping(Document document, String id, String ref) throws id, null, null)), null, null) - .sign(new DOMSignContext(privateKey, newDocument)); + .sign(withProps(new DOMSignContext(privateKey, newDocument))); return newDocument; } @@ -308,7 +315,7 @@ public Document sign(byte[] data) throws Exception { "object", null, null)), null, null) - .sign(new DOMSignContext(privateKey, newDocument)); + .sign(withProps(new DOMSignContext(privateKey, newDocument))); return newDocument; } @@ -325,10 +332,18 @@ public Document sign(String str) throws Exception { "object", null, null)), null, null) - .sign(new DOMSignContext(privateKey, newDocument)); + .sign(withProps(new DOMSignContext(privateKey, newDocument))); return newDocument; } + // Add props to a context + private DOMSignContext withProps(DOMSignContext ctxt) { + for (var e : props.entrySet()) { + ctxt.setProperty(e.getKey(), e.getValue()); + } + return ctxt; + } + // Builds a SignedInfo for a string reference private SignedInfo buildSignedInfo(String ref) throws Exception { return buildSignedInfo(FAC.newReference( @@ -426,6 +441,7 @@ public static class Validator { private Boolean secureValidation = null; private String baseURI = null; private final KeyStore ks; + Map props = new HashMap<>(); public Validator(KeyStore ks) { this.ks = ks; @@ -441,6 +457,11 @@ public Validator baseURI(String base) { return this; } + public Validator prop(String name, Object o) { + props.put(name, o); + return this; + } + public boolean validate(Document document) throws Exception { return validate(document, null); } @@ -471,12 +492,21 @@ public KeySelectorResult select(KeyInfo ki, Purpose p, secureValidation); } return XMLSignatureFactory.getInstance("DOM") - .unmarshalXMLSignature(valContext).validate(valContext); + .unmarshalXMLSignature(valContext) + .validate(withProps(valContext)); } } return false; } + // Add props to a context + private DOMValidateContext withProps(DOMValidateContext ctxt) { + for (var e : props.entrySet()) { + ctxt.setProperty(e.getKey(), e.getValue()); + } + return ctxt; + } + // Find public key from KeyInfo, ks will be used if it's KeyName private static class MyKeySelector extends KeySelector { private final KeyStore ks; diff --git a/test/lib/jdk/test/lib/security/timestamp/TsaServer.java b/test/lib/jdk/test/lib/security/timestamp/TsaServer.java index 9bffa1a240ff8..e103821c9b81c 100644 --- a/test/lib/jdk/test/lib/security/timestamp/TsaServer.java +++ b/test/lib/jdk/test/lib/security/timestamp/TsaServer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -87,7 +87,7 @@ public TsaServer(int port, KeyStore keyStore, String passphrase) * * @param handler a {@link TsaHandler} */ - public void setHandler(TsaHandler handler) { + public final void setHandler(TsaHandler handler) { server.createContext("/", handler); } @@ -113,7 +113,7 @@ public int getPort() { } @Override - public void close() throws Exception { + public void close() { stop(); } } diff --git a/test/lib/jdk/test/lib/util/JarUtils.java b/test/lib/jdk/test/lib/util/JarUtils.java index e1b3ccac19fc5..3aa4ada5197ad 100644 --- a/test/lib/jdk/test/lib/util/JarUtils.java +++ b/test/lib/jdk/test/lib/util/JarUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -320,6 +320,57 @@ public static void updateManifest(String src, String dest, Manifest man) updateJar(src, dest, Map.of(JarFile.MANIFEST_NAME, bout.toByteArray())); } + /** + * Remove entries from a ZIP file. + * + * Each entry can be a name or a name ending with "*". + * + * @return number of removed entries + * @throws IOException if there is any I/O error + */ + public static int deleteEntries(Path jarfile, String... patterns) + throws IOException { + Path tmpfile = Files.createTempFile("jar", "jar"); + int count = 0; + + try (OutputStream out = Files.newOutputStream(tmpfile); + JarOutputStream jos = new JarOutputStream(out)) { + try (JarFile jf = new JarFile(jarfile.toString())) { + Enumeration jentries = jf.entries(); + top: while (jentries.hasMoreElements()) { + JarEntry jentry = jentries.nextElement(); + String name = jentry.getName(); + for (String pattern : patterns) { + if (pattern.endsWith("*")) { + if (name.startsWith(pattern.substring( + 0, pattern.length() - 1))) { + // Go directly to next entry. This + // one is not written into `jos` and + // therefore removed. + count++; + continue top; + } + } else { + if (name.equals(pattern)) { + // Same as above + count++; + continue top; + } + } + } + // No pattern matched, file retained + jos.putNextEntry(copyEntry(jentry)); + jf.getInputStream(jentry).transferTo(jos); + } + } + } + + // replace the original JAR file + Files.move(tmpfile, jarfile, StandardCopyOption.REPLACE_EXISTING); + + return count; + } + private static void updateEntry(JarOutputStream jos, String name, Object content) throws IOException { if (content instanceof Boolean) { diff --git a/src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java b/test/lib/jdk/test/lib/util/ModuleInfoWriter.java similarity index 96% rename from src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java rename to test/lib/jdk/test/lib/util/ModuleInfoWriter.java index 1a751423358f5..7a155dd57367c 100644 --- a/src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java +++ b/test/lib/jdk/test/lib/util/ModuleInfoWriter.java @@ -1,12 +1,10 @@ /* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or @@ -22,7 +20,8 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package jdk.internal.module; + +package jdk.test.lib.util; import java.io.IOException; import java.io.OutputStream; @@ -37,6 +36,8 @@ import jdk.internal.org.objectweb.asm.commons.ModuleResolutionAttribute; import jdk.internal.org.objectweb.asm.commons.ModuleTargetAttribute; import static jdk.internal.org.objectweb.asm.Opcodes.*; +import jdk.internal.module.ModuleResolution; +import jdk.internal.module.ModuleTarget; /** * Utility class to write a ModuleDescriptor as a module-info.class. diff --git a/test/lib/jdk/test/whitebox/WhiteBox.java b/test/lib/jdk/test/whitebox/WhiteBox.java index 03db68e91d18f..104c40baff7e4 100644 --- a/test/lib/jdk/test/whitebox/WhiteBox.java +++ b/test/lib/jdk/test/whitebox/WhiteBox.java @@ -24,7 +24,11 @@ package jdk.test.whitebox; import java.lang.management.MemoryUsage; +import java.lang.ref.Reference; import java.lang.reflect.Executable; +import java.lang.reflect.InaccessibleObjectException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; import java.util.function.BiFunction; @@ -434,6 +438,53 @@ public void clearInlineCaches(boolean preserve_static_stubs) { // Force Full GC public native void fullGC(); + // Infrastructure for waitForReferenceProcessing() + private static volatile Method waitForReferenceProcessingMethod = null; + + private static Method getWaitForReferenceProcessingMethod() { + Method wfrp = waitForReferenceProcessingMethod; + if (wfrp == null) { + try { + wfrp = Reference.class.getDeclaredMethod("waitForReferenceProcessing"); + wfrp.setAccessible(true); + assert wfrp.getReturnType() == Boolean.class; + Class[] ev = wfrp.getExceptionTypes(); + assert ev.length == 1; + assert ev[0] == InterruptedException.class; + waitForReferenceProcessingMethod = wfrp; + } catch (InaccessibleObjectException e) { + throw new RuntimeException("Need to add @modules java.base/java.lang.ref:open to test?", e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + return wfrp; + } + + /** + * Wait for reference processing, via Reference.waitForReferenceProcessing(). + * Callers of this method will need the + * @modules java.base/java.lang.ref:open + * jtreg tag. + * + * This method should usually be called after a call to WhiteBox.fullGC(). + */ + public boolean waitForReferenceProcessing() throws InterruptedException { + try { + Method wfrp = getWaitForReferenceProcessingMethod(); + return (Boolean) wfrp.invoke(null); + } catch (IllegalAccessException e) { + throw new RuntimeException("Shouldn't happen, we call setAccessible()", e); + } catch (InvocationTargetException e) { + Throwable cause = e.getCause(); + if (cause instanceof InterruptedException) { + throw (InterruptedException) cause; + } else { + throw new RuntimeException(e); + } + } + } + // Returns true if the current GC supports concurrent collection control. public native boolean supportsConcurrentGCBreakpoints(); diff --git a/test/micro/org/openjdk/bench/java/io/FileDescriptorSync.java b/test/micro/org/openjdk/bench/java/io/FileDescriptorSync.java new file mode 100644 index 0000000000000..ee6f5fce2f93d --- /dev/null +++ b/test/micro/org/openjdk/bench/java/io/FileDescriptorSync.java @@ -0,0 +1,70 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.io; + +import org.openjdk.jmh.annotations.*; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.SyncFailedException; +import java.util.concurrent.TimeUnit; + +/** + * Tests the cost of FileDescriptor.sync + */ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(3) +public class FileDescriptorSync { + + private FileOutputStream fos; + private FileDescriptor fd; + + @Setup + public void setup() throws IOException { + File tmp = File.createTempFile("FileDescriptorSync", "bin"); + fos = new FileOutputStream(tmp); + fd = fos.getFD(); + } + + @TearDown + public void tearDown() throws IOException { + fos.close(); + } + + @Benchmark + public void sync() { + try { + fd.sync(); + } catch (SyncFailedException e) { + // The test assumes the temp filesystem accepts syncs. + // Avoid failing if it does not, measure the exceptional path then. + } + } + +} diff --git a/test/micro/org/openjdk/bench/java/lang/StringDecode.java b/test/micro/org/openjdk/bench/java/lang/StringDecode.java index 186d2aed6d95b..c5609ce4e51fd 100644 --- a/test/micro/org/openjdk/bench/java/lang/StringDecode.java +++ b/test/micro/org/openjdk/bench/java/lang/StringDecode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,17 +22,7 @@ */ package org.openjdk.bench.java.lang; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Param; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.infra.Blackhole; import java.nio.charset.Charset; @@ -40,28 +30,30 @@ @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) -@Fork(value = 3) -@Warmup(iterations = 5, time = 2) +@Fork(value = 2) +@Warmup(iterations = 5, time = 3) @Measurement(iterations = 5, time = 3) @State(Scope.Thread) public class StringDecode { - @Param({"US-ASCII", "ISO-8859-1", "UTF-8", "MS932", "ISO-8859-6", "ISO-2022-KR"}) + // Reduced by default to only UTF-8, previous coverage: + // @Param({"US-ASCII", "ISO-8859-1", "UTF-8", "MS932", "ISO-8859-6", "ISO-2022-KR"}) + @Param({"UTF-8"}) private String charsetName; private Charset charset; private byte[] asciiString; + private byte[] longAsciiString; private byte[] utf16String; - private byte[] longUtf16String; + private byte[] longUtf16EndString; private byte[] longUtf16StartString; - private byte[] longLatin1String; + private byte[] longUtf16OnlyString; + private byte[] latin1String; + private byte[] longLatin1EndString; + private byte[] longLatin1StartString; + private byte[] longLatin1OnlyString; - @Setup - public void setup() { - charset = Charset.forName(charsetName); - asciiString = "ascii string".getBytes(charset); - utf16String = "UTF-\uFF11\uFF16 string".getBytes(charset); - longUtf16String = """ + private static final String LOREM = """ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam ac sem eu urna egestas placerat. Etiam finibus ipsum nulla, non mattis dolor cursus a. Nulla nec nisl consectetur, lacinia neque id, accumsan ante. Curabitur et @@ -70,90 +62,145 @@ public void setup() { per conubia nostra, per inceptos himenaeos. Suspendisse erat diam, fringilla sed massa sed, posuere viverra orci. Suspendisse tempor libero non gravida efficitur. Vivamus lacinia risus non orci viverra, at consectetur odio laoreet. - Suspendisse potenti. - - Phasellus vel nisi iaculis, accumsan quam sed, bibendum eros. Sed venenatis - nulla tortor, et eleifend urna sodales id. Nullam tempus ac metus sit amet - sollicitudin. Nam sed ex diam. Praesent vitae eros et neque condimentum - consectetur eget non tortor. Praesent bibendum vel felis nec dignissim. - Maecenas a enim diam. Suspendisse quis ligula at nisi accumsan lacinia id - hendrerit sapien. \uFF11Donec aliquam mattis lectus eu ultrices. Duis eu nisl\uFF11 - euismod, blandit mauris vel, \uFF11placerat urna. Etiam malesuada enim purus, - tristique mollis odio blandit quis.\uFF11 Vivamus posuere. \uFF11 - \uFF11 - """.getBytes(charset); - longUtf16StartString = """ - \uFF11 - Lorem ipsum dolor sit amet, \uFF11consectetur adipiscing elit. Aliquam ac sem eu - urna egestas \uFF11placerat. Etiam finibus ipsum nulla, non mattis dolor cursus a. - Nulla \uFF11nec nisl consectetur, lacinia neque id, accumsan ante. Curabitur et - sapien in \uFF11magna porta ultricies. \uFF11Sed vel pellentesque nibh. Pellentesque dictum - dignissim diam eu ultricies. Class aptent taciti sociosqu ad litora torquent - per conubia nostra, per inceptos himenaeos. Suspendisse erat diam, fringilla - sed massa sed, posuere viverra orci. Suspendisse tempor libero non gravida - efficitur. Vivamus lacinia risus non orci viverra, at consectetur odio laoreet. - Suspendisse potenti. - - Phasellus vel nisi iaculis, accumsan quam sed, bibendum eros. Sed venenatis - nulla tortor, et eleifend urna sodales id. Nullam tempus ac metus sit amet - sollicitudin. Nam sed ex diam. Praesent vitae eros et neque condimentum - consectetur eget non tortor. Praesent bibendum vel felis nec dignissim. - Maecenas a enim diam. Suspendisse quis ligula at nisi accumsan lacinia id - hendrerit sapien. Donec aliquam mattis lectus eu ultrices. Duis eu nisl - euismod, blandit mauris vel, placerat urna. Etiam malesuada enim purus, - tristique mollis odio blandit quis. Vivamus posuere. - """.getBytes(charset); - - longLatin1String = """ - a\u00B6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6 - b\u00F6\u00F6\u00B6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6 - c\u00F6\u00F6\u00F6\u00B6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6 - d\u00F6\u00F6\u00F6\u00F6\u00B6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6 - e\u00F6\u00F6\u00F6\u00F6\u00F6\u00B6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6 - f\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00B6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6 - g\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00B6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6 - h\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00B6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6 - i\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00B6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6 - j\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00B6\u00F6\u00F6\u00F6\u00F6\u00F6 - k\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00B6\u00F6\u00F6\u00F6\u00F6 - l\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00B6\u00F6\u00F6\u00F6 - m\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00F6\u00B6\u00F6\u00F6 - """.getBytes(charset); + Suspendisse potenti."""; + private static final String UTF16_STRING = "\uFF11".repeat(31); + private static final String LATIN1_STRING = "\u00B6".repeat(31); + + @Setup + public void setup() { + charset = Charset.forName(charsetName); + asciiString = LOREM.substring(0, 32).getBytes(charset); + longAsciiString = LOREM.repeat(200).getBytes(charset); + utf16String = "UTF-\uFF11\uFF16 string".getBytes(charset); + longUtf16EndString = LOREM.repeat(4).concat(UTF16_STRING).getBytes(charset); + longUtf16StartString = UTF16_STRING.concat(LOREM.repeat(4)).getBytes(charset); + longUtf16OnlyString = UTF16_STRING.repeat(10).getBytes(charset); + latin1String = LATIN1_STRING.getBytes(charset); + longLatin1EndString = LOREM.repeat(4).concat(LATIN1_STRING).getBytes(charset); + longLatin1StartString = LATIN1_STRING.concat(LOREM.repeat(4)).getBytes(charset); + longLatin1OnlyString = LATIN1_STRING.repeat(10).getBytes(charset); } @Benchmark - public String decodeAsciiCharsetName() throws Exception { - return new String(asciiString, charsetName); + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public void decodeAsciiShort(Blackhole bh) throws Exception { + bh.consume(new String(asciiString, charset)); + bh.consume(new String(longAsciiString, 0, 15, charset)); + bh.consume(new String(asciiString, 0, 3, charset)); + bh.consume(new String(longAsciiString, 512, 512 + 7, charset)); } @Benchmark - public String decodeAscii() throws Exception { - return new String(asciiString, charset); + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public void decodeAsciiLong(Blackhole bh) throws Exception { + bh.consume(new String(longAsciiString, charset)); + bh.consume(new String(longAsciiString, 0, 1024 + 31, charset)); } @Benchmark - public String decodeLatin1Long() throws Exception { - return new String(longLatin1String, charset); + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public void decodeLatin1Short(Blackhole bh) throws Exception { + bh.consume(new String(latin1String, charset)); + bh.consume(new String(latin1String, 0, 15, charset)); + bh.consume(new String(latin1String, 0, 3, charset)); + bh.consume(new String(longLatin1OnlyString, 512, 512 + 7, charset)); } @Benchmark - public String decodeUTF16Short() throws Exception { - return new String(utf16String, charset); + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public String decodeLatin1LongStart() throws Exception { + return new String(longLatin1StartString, charset); } @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public String decodeLatin1LongEnd() throws Exception { + return new String(longLatin1EndString, charset); + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public String decodeLatin1LongOnly() throws Exception { + return new String(longLatin1OnlyString, charset); + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public void decodeLatin1Mixed(Blackhole bh) throws Exception { + bh.consume(new String(longLatin1EndString, charset)); + bh.consume(new String(longLatin1StartString, charset)); + bh.consume(new String(latin1String, charset)); + bh.consume(new String(longLatin1OnlyString, charset)); + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public void decodeUTF16Short(Blackhole bh) throws Exception { + bh.consume(new String(utf16String, charset)); + bh.consume(new String(utf16String, 0, 15, charset)); + bh.consume(new String(utf16String, 0, 3, charset)); + bh.consume(new String(utf16String, 0, 7, charset)); + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) public String decodeUTF16LongEnd() throws Exception { - return new String(longUtf16String, charset); + return new String(longUtf16EndString, charset); } @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) public String decodeUTF16LongStart() throws Exception { return new String(longUtf16StartString, charset); } @Benchmark - public void decodeUTF16LongMixed(Blackhole bh) throws Exception { + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public String decodeUTF16LongOnly() throws Exception { + return new String(longUtf16OnlyString, charset); + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public void decodeUTF16Mixed(Blackhole bh) throws Exception { + bh.consume(new String(longUtf16StartString, charset)); + bh.consume(new String(longUtf16EndString, charset)); + bh.consume(new String(utf16String, charset)); + bh.consume(new String(longUtf16OnlyString, charset)); + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public void decodeAllMixed(Blackhole bh) throws Exception { + bh.consume(new String(utf16String, charset)); + bh.consume(new String(longUtf16EndString, charset)); + bh.consume(new String(utf16String, 0, 15, charset)); bh.consume(new String(longUtf16StartString, charset)); - bh.consume(new String(longUtf16String, charset)); + bh.consume(new String(asciiString, 0, 3, charset)); + bh.consume(new String(longUtf16OnlyString, charset)); + bh.consume(new String(latin1String, charset)); + bh.consume(new String(longLatin1EndString, charset)); + bh.consume(new String(longLatin1StartString, charset)); + bh.consume(new String(latin1String, 0, 7, charset)); + bh.consume(new String(longLatin1OnlyString, charset)); + bh.consume(new String(asciiString, charset)); + bh.consume(new String(longAsciiString, charset)); + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public void decodeShortMixed(Blackhole bh) throws Exception { + bh.consume(new String(utf16String, 0, 15, charset)); + bh.consume(new String(latin1String, 0, 15, charset)); + bh.consume(new String(asciiString, charset)); + bh.consume(new String(utf16String, charset)); + bh.consume(new String(latin1String, 0, 3, charset)); + bh.consume(new String(asciiString, 0, 3, charset)); + bh.consume(new String(utf16String, 0, 7, charset)); + bh.consume(new String(latin1String, charset)); + bh.consume(new String(asciiString, 0, 7, charset)); + bh.consume(new String(utf16String, 0, 3, charset)); + bh.consume(new String(latin1String, 0, 7, charset)); + bh.consume(new String(asciiString, 0, 15, charset)); } } diff --git a/test/micro/org/openjdk/bench/java/lang/StringEncode.java b/test/micro/org/openjdk/bench/java/lang/StringEncode.java index 6e67d3e8cee74..30f5a18450ee7 100644 --- a/test/micro/org/openjdk/bench/java/lang/StringEncode.java +++ b/test/micro/org/openjdk/bench/java/lang/StringEncode.java @@ -30,97 +30,197 @@ @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) -@Fork(value = 3) -@Warmup(iterations = 5, time = 2) +@Fork(value = 2) +@Warmup(iterations = 5, time = 3) @Measurement(iterations = 5, time = 3) @State(Scope.Thread) public class StringEncode { - @Param({"US-ASCII", "ISO-8859-1", "UTF-8", "MS932", "ISO-8859-6"}) + // Reduced by default to only UTF-8, previous coverage: + // @Param({"US-ASCII", "ISO-8859-1", "UTF-8", "MS932", "ISO-8859-6", "ISO-2022-KR"}) + @Param({"UTF-8"}) private String charsetName; + private Charset charset; private String asciiString; + private String asciiString3; + private String asciiString7; + private String asciiString15; + private String longAsciiString; + private String longAsciiString1055; private String utf16String; - private String longUtf16String; + private String utf16String3; + private String utf16String7; + private String utf16String15; + private String longUtf16EndString; private String longUtf16StartString; + private String longUtf16OnlyString; + private String latin1String; + private String latin1String3; + private String latin1String7; + private String latin1String15; + private String longLatin1EndString; + private String longLatin1StartString; + private String longLatin1OnlyString; + + private static final String LOREM = """ + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam ac sem eu + urna egestas placerat. Etiam finibus ipsum nulla, non mattis dolor cursus a. + Nulla nec nisl consectetur, lacinia neque id, accumsan ante. Curabitur et + sapien in magna porta ultricies. Sed vel pellentesque nibh. Pellentesque dictum + dignissim diam eu ultricies. Class aptent taciti sociosqu ad litora torquent + per conubia nostra, per inceptos himenaeos. Suspendisse erat diam, fringilla + sed massa sed, posuere viverra orci. Suspendisse tempor libero non gravida + efficitur. Vivamus lacinia risus non orci viverra, at consectetur odio laoreet. + Suspendisse potenti."""; + private static final String UTF16_STRING = "\uFF11".repeat(31); + private static final String LATIN1_STRING = "\u00B6".repeat(31); @Setup public void setup() { charset = Charset.forName(charsetName); - asciiString = "ascii string"; - utf16String = "UTF-\uFF11\uFF16 string"; - longUtf16String = """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam ac sem eu - urna egestas placerat. Etiam finibus ipsum nulla, non mattis dolor cursus a. - Nulla nec nisl consectetur, lacinia neque id, accumsan ante. Curabitur et - sapien in magna porta ultricies. Sed vel pellentesque nibh. Pellentesque dictum - dignissim diam eu ultricies. Class aptent taciti sociosqu ad litora torquent - per conubia nostra, per inceptos himenaeos. Suspendisse erat diam, fringilla - sed massa sed, posuere viverra orci. Suspendisse tempor libero non gravida - efficitur. Vivamus lacinia risus non orci viverra, at consectetur odio laoreet. - Suspendisse potenti. - - Phasellus vel nisi iaculis, accumsan quam sed, bibendum eros. Sed venenatis - nulla tortor, et eleifend urna sodales id. Nullam tempus ac metus sit amet - sollicitudin. Nam sed ex diam. Praesent vitae eros et neque condimentum - consectetur eget non tortor. Praesent bibendum vel felis nec dignissim. - Maecenas a enim diam. Suspendisse quis ligula at nisi accumsan lacinia id - hendrerit sapien. Donec aliquam mattis lectus eu ultrices. Duis eu nisl - euismod, blandit mauris vel, placerat urna. Etiam malesuada enim purus, - tristique mollis odio blandit quis. Vivamus posuere. - \uFF11 - """; - longUtf16StartString = """ - \uFF11 - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam ac sem eu - urna egestas placerat. Etiam finibus ipsum nulla, non mattis dolor cursus a. - Nulla nec nisl consectetur, lacinia neque id, accumsan ante. Curabitur et - sapien in magna porta ultricies. Sed vel pellentesque nibh. Pellentesque dictum - dignissim diam eu ultricies. Class aptent taciti sociosqu ad litora torquent - per conubia nostra, per inceptos himenaeos. Suspendisse erat diam, fringilla - sed massa sed, posuere viverra orci. Suspendisse tempor libero non gravida - efficitur. Vivamus lacinia risus non orci viverra, at consectetur odio laoreet. - Suspendisse potenti. - - Phasellus vel nisi iaculis, accumsan quam sed, bibendum eros. Sed venenatis - nulla tortor, et eleifend urna sodales id. Nullam tempus ac metus sit amet - sollicitudin. Nam sed ex diam. Praesent vitae eros et neque condimentum - consectetur eget non tortor. Praesent bibendum vel felis nec dignissim. - Maecenas a enim diam. Suspendisse quis ligula at nisi accumsan lacinia id - hendrerit sapien. Donec aliquam mattis lectus eu ultrices. Duis eu nisl - euismod, blandit mauris vel, placerat urna. Etiam malesuada enim purus, - tristique mollis odio blandit quis. Vivamus posuere. - """; + asciiString = LOREM.substring(0, 32); + asciiString3 = LOREM.substring(0, 3); + asciiString7 = LOREM.substring(0, 7); + asciiString15 = LOREM.substring(0, 15); + longAsciiString = LOREM.repeat(200); + longAsciiString1055 = longAsciiString.substring(0, 1055); + utf16String = UTF16_STRING; + utf16String3 = UTF16_STRING.substring(0, 3); + utf16String7 = UTF16_STRING.substring(0, 7); + utf16String15 = UTF16_STRING.substring(0, 15); + longUtf16EndString = LOREM.repeat(4).concat(UTF16_STRING); + longUtf16StartString = UTF16_STRING.concat(LOREM.repeat(4)); + longUtf16OnlyString = UTF16_STRING.repeat(10); + latin1String = LATIN1_STRING; + latin1String3 = LATIN1_STRING.substring(0, 3); + latin1String7 = LATIN1_STRING.substring(0, 7); + latin1String15 = LATIN1_STRING.substring(0, 15); + longLatin1EndString = LOREM.repeat(4).concat(LATIN1_STRING); + longLatin1StartString = LATIN1_STRING.concat(LOREM.repeat(4)); + longLatin1OnlyString = LATIN1_STRING.repeat(10); } @Benchmark - public byte[] encodeAsciiCharsetName() throws Exception { - return asciiString.getBytes(charset); + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public void encodeAsciiShort(Blackhole bh) throws Exception { + bh.consume(asciiString.getBytes(charset)); + bh.consume(asciiString3.getBytes(charset)); + bh.consume(asciiString15.getBytes(charset)); + bh.consume(asciiString7.getBytes(charset)); } @Benchmark - public byte[] encodeAscii() throws Exception { - return asciiString.getBytes(charset); + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public void encodeAsciiLong(Blackhole bh) throws Exception { + bh.consume(longAsciiString.getBytes(charset)); + bh.consume(longAsciiString1055.getBytes(charset)); } @Benchmark - public void encodeMix(Blackhole bh) throws Exception { - bh.consume(asciiString.getBytes(charset)); + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public void encodeUTF16Short(Blackhole bh) throws Exception { bh.consume(utf16String.getBytes(charset)); + bh.consume(utf16String3.getBytes(charset)); + bh.consume(utf16String15.getBytes(charset)); + bh.consume(utf16String7.getBytes(charset)); } @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) public byte[] encodeUTF16LongEnd() throws Exception { - return longUtf16String.getBytes(charset); + return longUtf16EndString.getBytes(charset); } @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) public byte[] encodeUTF16LongStart() throws Exception { return longUtf16StartString.getBytes(charset); } @Benchmark - public byte[] encodeUTF16() throws Exception { - return utf16String.getBytes(charset); + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public byte[] encodeUTF16LongOnly() throws Exception { + return longUtf16OnlyString.getBytes(charset); + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public void encodeUTF16Mixed(Blackhole bh) throws Exception { + bh.consume(utf16String.getBytes(charset)); + bh.consume(longUtf16StartString.getBytes(charset)); + bh.consume(longUtf16EndString.getBytes(charset)); + bh.consume(longUtf16OnlyString.getBytes(charset)); + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public void encodeLatin1Short(Blackhole bh) throws Exception { + bh.consume(latin1String.getBytes(charset)); + bh.consume(latin1String3.getBytes(charset)); + bh.consume(latin1String15.getBytes(charset)); + bh.consume(latin1String7.getBytes(charset)); + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public byte[] encodeLatin1LongOnly() throws Exception { + return longLatin1OnlyString.getBytes(charset); + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public byte[] encodeLatin1LongStart() throws Exception { + return longLatin1StartString.getBytes(charset); + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public byte[] encodeLatin1LongEnd() throws Exception { + return longLatin1EndString.getBytes(charset); + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public void encodeLatin1Mixed(Blackhole bh) throws Exception { + bh.consume(longLatin1EndString.getBytes(charset)); + bh.consume(longLatin1StartString.getBytes(charset)); + bh.consume(longLatin1OnlyString.getBytes(charset)); + bh.consume(latin1String.getBytes(charset)); + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public void encodeAllMixed(Blackhole bh) throws Exception { + bh.consume(utf16String.getBytes(charset)); + bh.consume(longUtf16StartString.getBytes(charset)); + bh.consume(asciiString7.getBytes(charset)); + bh.consume(longUtf16EndString.getBytes(charset)); + bh.consume(latin1String3.getBytes(charset)); + bh.consume(longUtf16OnlyString.getBytes(charset)); + bh.consume(longLatin1EndString.getBytes(charset)); + bh.consume(longLatin1StartString.getBytes(charset)); + bh.consume(utf16String15.getBytes(charset)); + bh.consume(longLatin1OnlyString.getBytes(charset)); + bh.consume(latin1String.getBytes(charset)); + bh.consume(asciiString.getBytes(charset)); + bh.consume(longAsciiString.getBytes(charset)); + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public void encodeShortMixed(Blackhole bh) throws Exception { + bh.consume(utf16String3.getBytes(charset)); + bh.consume(latin1String7.getBytes(charset)); + bh.consume(asciiString15.getBytes(charset)); + bh.consume(utf16String.getBytes(charset)); + bh.consume(latin1String3.getBytes(charset)); + bh.consume(asciiString7.getBytes(charset)); + bh.consume(utf16String15.getBytes(charset)); + bh.consume(latin1String.getBytes(charset)); + bh.consume(asciiString3.getBytes(charset)); + bh.consume(utf16String7.getBytes(charset)); + bh.consume(latin1String15.getBytes(charset)); + bh.consume(asciiString.getBytes(charset)); } } diff --git a/test/micro/org/openjdk/bench/java/net/UnixSocketChannelReadWrite.java b/test/micro/org/openjdk/bench/java/net/UnixSocketChannelReadWrite.java index 9e75b0d556c62..8a3f24cc63892 100644 --- a/test/micro/org/openjdk/bench/java/net/UnixSocketChannelReadWrite.java +++ b/test/micro/org/openjdk/bench/java/net/UnixSocketChannelReadWrite.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,7 +31,6 @@ import java.nio.channels.SocketChannel; import java.nio.file.*; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; import org.openjdk.jmh.annotations.*; @@ -49,22 +48,10 @@ public class UnixSocketChannelReadWrite { private ReadThread rt; private ByteBuffer bb = ByteBuffer.allocate(1); - private static volatile String tempDir; - private static final AtomicInteger count = new AtomicInteger(0); private volatile Path socket; - static { - try { - Path p = Files.createTempDirectory("readWriteTest"); - tempDir = p.toString(); - } catch (IOException e) { - tempDir = null; - } - } - private ServerSocketChannel getServerSocketChannel() throws IOException { - int next = count.incrementAndGet(); - socket = Paths.get(tempDir, Integer.toString(next)); + socket = Files.createTempDirectory(UnixSocketChannelReadWrite.class.getSimpleName()).resolve("sock"); UnixDomainSocketAddress addr = UnixDomainSocketAddress.of(socket); ServerSocketChannel c = ServerSocketChannel.open(StandardProtocolFamily.UNIX); c.bind(addr); @@ -90,7 +77,7 @@ public void afterRun() throws IOException, InterruptedException { s2.close(); ssc.close(); Files.delete(socket); - Files.delete(Path.of(tempDir)); + Files.delete(socket.getParent()); rt.join(); } diff --git a/test/micro/org/openjdk/bench/java/security/Signatures.java b/test/micro/org/openjdk/bench/java/security/Signatures.java index 7a14cb24b88b2..b32e5dd35cfca 100644 --- a/test/micro/org/openjdk/bench/java/security/Signatures.java +++ b/test/micro/org/openjdk/bench/java/security/Signatures.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -194,4 +194,3 @@ public void setup() throws Exception { } } } - diff --git a/test/micro/org/openjdk/bench/java/time/format/ZonedDateTimeFormatterBenchmark.java b/test/micro/org/openjdk/bench/java/time/format/ZonedDateTimeFormatterBenchmark.java index 00a3729a337ae..93f7e5e1e7719 100644 --- a/test/micro/org/openjdk/bench/java/time/format/ZonedDateTimeFormatterBenchmark.java +++ b/test/micro/org/openjdk/bench/java/time/format/ZonedDateTimeFormatterBenchmark.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,7 +40,7 @@ public class ZonedDateTimeFormatterBenchmark { private static final DateTimeFormatter df = new DateTimeFormatterBuilder() .appendPattern("yyyy:MM:dd:HH:mm:v") .toFormatter(); - private static final String TEXT = "2015:03:10:12:13:ECT"; + private static final String TEXT = "2015:03:10:12:13:PST"; @Setup public void setUp() { diff --git a/test/micro/org/openjdk/bench/java/util/concurrent/ProducerConsumer.java b/test/micro/org/openjdk/bench/java/util/concurrent/ProducerConsumer.java index 9505760dd52dd..65aa2d81b0efb 100644 --- a/test/micro/org/openjdk/bench/java/util/concurrent/ProducerConsumer.java +++ b/test/micro/org/openjdk/bench/java/util/concurrent/ProducerConsumer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,6 +32,7 @@ import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.TearDown; import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.infra.ThreadParams; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; @@ -49,7 +50,7 @@ @State(Scope.Benchmark) public class ProducerConsumer { - @Param("100") + @Param("100") // Will be expanded to at least the number of threads used private int capacity; @Param @@ -59,7 +60,9 @@ public class ProducerConsumer { private Producer prod; @Setup - public void prepare() { + public void prepare(ThreadParams params) { + capacity = Math.max(params.getThreadCount(), capacity); + switch (type) { case ABQ_F: q = new ArrayBlockingQueue<>(capacity, true); diff --git a/test/micro/org/openjdk/bench/java/util/concurrent/Queues.java b/test/micro/org/openjdk/bench/java/util/concurrent/Queues.java index 9c324ad1fca78..71e240ce3ef43 100644 --- a/test/micro/org/openjdk/bench/java/util/concurrent/Queues.java +++ b/test/micro/org/openjdk/bench/java/util/concurrent/Queues.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,7 @@ import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.infra.ThreadParams; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; @@ -43,7 +44,7 @@ @State(Scope.Benchmark) public class Queues { - @Param("100") + @Param("100") // Will be expanded to at least the number of threads used private int capacity; @Param @@ -59,7 +60,9 @@ public enum QueueType { private BlockingQueue q; @Setup - public void setup() { + public void setup(ThreadParams params) { + capacity = Math.max(params.getThreadCount(), capacity); + switch (type) { case ABQ_F: q = new ArrayBlockingQueue<>(capacity, true); diff --git a/test/micro/org/openjdk/bench/jdk/incubator/vector/MaskQueryOperationsBenchmark.java b/test/micro/org/openjdk/bench/jdk/incubator/vector/MaskQueryOperationsBenchmark.java index 5fa4741fad2c1..18cff1c3cc826 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/vector/MaskQueryOperationsBenchmark.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/vector/MaskQueryOperationsBenchmark.java @@ -1,5 +1,5 @@ // -// Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. +// Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. // // This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,7 @@ @OutputTimeUnit(TimeUnit.MILLISECONDS) @State(Scope.Thread) +@Fork(jvmArgs = {"--add-modules=jdk.incubator.vector"}) public class MaskQueryOperationsBenchmark { @Param({"128","256","512"}) int bits; diff --git a/test/micro/org/openjdk/bench/vm/compiler/LoopUnroll.java b/test/micro/org/openjdk/bench/vm/compiler/LoopUnroll.java index 84630e70b8935..776ba7a12514d 100644 --- a/test/micro/org/openjdk/bench/vm/compiler/LoopUnroll.java +++ b/test/micro/org/openjdk/bench/vm/compiler/LoopUnroll.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2021, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it