From 58efdfd89861175ac1dcc2393ed2a2e990489532 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 22 Jun 2026 17:49:47 +0200 Subject: [PATCH] Related tdf#168623 Fix renaming empty Java/BeanShell libraries The base script provider in Java for several languages was throwing an exception when trying to rename an empty library. The browsenodes member variable was being lazily created when getChildNodes was called but in the case where there were no children it was being left as null which was causing a Java runtime exception. Change-Id: I0d9eac2006bc26d47c22f2e1a7a4424fd67a512f Reviewed-on: https://gerrit.libreoffice.org/c/core/+/207152 Tested-by: Jenkins Reviewed-by: Neil Roberts --- scripting/CppunitTest_scripting_beanshell.mk | 50 ++++++ scripting/Module_scripting.mk | 1 + .../framework/browse/ParcelBrowseNode.java | 10 +- .../qa/cppunit/beanshell/beanshelltest.cxx | 162 ++++++++++++++++++ 4 files changed, 219 insertions(+), 4 deletions(-) create mode 100644 scripting/CppunitTest_scripting_beanshell.mk create mode 100644 scripting/qa/cppunit/beanshell/beanshelltest.cxx diff --git a/scripting/CppunitTest_scripting_beanshell.mk b/scripting/CppunitTest_scripting_beanshell.mk new file mode 100644 index 000000000000..43e6f5ea4b90 --- /dev/null +++ b/scripting/CppunitTest_scripting_beanshell.mk @@ -0,0 +1,50 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t; fill-column: 100 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +$(eval $(call gb_CppunitTest_CppunitTest,scripting_beanshell)) + +$(eval $(call gb_CppunitTest_add_exception_objects,scripting_beanshell, \ + scripting/qa/cppunit/beanshell/beanshelltest \ +)) + +$(eval $(call gb_CppunitTest_use_libraries,scripting_beanshell, \ + cppu \ + cppuhelper \ + jvmfwk \ + sal \ + subsequenttest \ + test \ + unotest \ + utl \ + tl \ +)) + +$(eval $(call gb_CppunitTest_use_externals,scripting_beanshell,\ + boost_headers \ + libxml2 \ +)) + +$(eval $(call gb_CppunitTest_use_api,scripting_beanshell,\ + udkapi \ + offapi \ +)) + +$(eval $(call gb_CppunitTest_use_jars,scripting_beanshell, \ + ScriptProviderForBeanShell \ +)) + +$(eval $(call gb_CppunitTest_use_ure,scripting_beanshell)) +$(eval $(call gb_CppunitTest_use_vcl,scripting_beanshell)) +$(eval $(call gb_CppunitTest_use_rdbs,scripting_beanshell, \ + services \ + scriptproviderforbeanshell \ +)) +$(eval $(call gb_CppunitTest_use_configuration,scripting_beanshell)) + +# vim: set noet sw=4 ts=4: diff --git a/scripting/Module_scripting.mk b/scripting/Module_scripting.mk index f244c267ecb7..285d694fbca3 100644 --- a/scripting/Module_scripting.mk +++ b/scripting/Module_scripting.mk @@ -49,6 +49,7 @@ $(eval $(call gb_Module_add_targets,scripting,\ $(eval $(call gb_Module_add_check_targets,scripting,\ $(if $(ENABLE_QUICKJS),CppunitTest_scripting_jsprov) \ + $(if $(ENABLE_SCRIPTING_BEANSHELL),CppunitTest_scripting_beanshell) \ )) endif diff --git a/scripting/java/com/sun/star/script/framework/browse/ParcelBrowseNode.java b/scripting/java/com/sun/star/script/framework/browse/ParcelBrowseNode.java index cdb8715a5cbe..5dcd2b74d89a 100644 --- a/scripting/java/com/sun/star/script/framework/browse/ParcelBrowseNode.java +++ b/scripting/java/com/sun/star/script/framework/browse/ParcelBrowseNode.java @@ -267,11 +267,13 @@ public class ParcelBrowseNode extends PropertySet implements getChildNodes(); } - ScriptBrowseNode[] childNodes = - browsenodes.toArray(new ScriptBrowseNode[browsenodes.size()]); + if (browsenodes != null) { + ScriptBrowseNode[] childNodes = + browsenodes.toArray(new ScriptBrowseNode[browsenodes.size()]); - for (int index = 0; index < childNodes.length; index++) { - childNodes[ index ].updateURI(p); + for (int index = 0; index < childNodes.length; index++) { + childNodes[ index ].updateURI(p); + } } result = new Any(new Type(XBrowseNode.class), this); diff --git a/scripting/qa/cppunit/beanshell/beanshelltest.cxx b/scripting/qa/cppunit/beanshell/beanshelltest.cxx new file mode 100644 index 000000000000..d4966ffc08e0 --- /dev/null +++ b/scripting/qa/cppunit/beanshell/beanshelltest.cxx @@ -0,0 +1,162 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include + +#include + +#include +#include +#include +#include + +namespace +{ +class BeanShellTest : public UnoApiTest +{ +public: + BeanShellTest() + : UnoApiTest(u"/scripting/qa/extras"_ustr) + { + } + + void setUp() override; + +private: + void testRenameEmptyLibrary(); + void testRenameNonEmptyLibrary(); + + CPPUNIT_TEST_SUITE(BeanShellTest); + CPPUNIT_TEST(testRenameEmptyLibrary); + CPPUNIT_TEST(testRenameNonEmptyLibrary); + CPPUNIT_TEST_SUITE_END(); +}; + +void BeanShellTest::setUp() +{ + UnoApiTest::setUp(); + + // The ExpandContentProvider is needed for ScriptURIHelper to work + css::uno::Reference xUcb + = css::ucb::UniversalContentBroker::create(m_xContext); + css::uno::Reference xExpandProvider( + m_xSFactory->createInstance(u"com.sun.star.ucb.ExpandContentProvider"_ustr), + css::uno::UNO_QUERY_THROW); + xUcb->registerContentProvider(xExpandProvider, u"vnd.sun.star.expand"_ustr, true); +} + +void createChild(css::uno::Reference& xInvocation, const OUString& sName) +{ + css::uno::Sequence aArgs = { css::uno::Any(sName) }; + css::uno::Sequence aOutParamIndex; + css::uno::Sequence aOutParam; + xInvocation->invoke("Creatable", aArgs, aOutParamIndex, aOutParam); +} + +void renameLibrary(css::uno::Reference& xInvocation, + const OUString& sName) +{ + css::uno::Sequence aArgs = { css::uno::Any(sName) }; + css::uno::Sequence aOutParamIndex; + css::uno::Sequence aOutParam; + css::uno::Any xResult = xInvocation->invoke("Renamable", aArgs, aOutParamIndex, aOutParam); + + css::uno::Reference xRenamedInvocation; + CPPUNIT_ASSERT(xResult >>= xRenamedInvocation); + CPPUNIT_ASSERT_EQUAL(xInvocation, xRenamedInvocation); +} + +css::uno::Reference +findChild(const css::uno::Reference& xParent, + std::u16string_view sName) +{ + for (const auto& xChild : xParent->getChildNodes()) + { + if (xChild->getName() == sName) + return xChild; + } + + OString sError + = OUStringToOString(Concat2View(u"Couldn’t find "_ustr + sName), RTL_TEXTENCODING_UTF8); + CPPUNIT_FAIL(sError.getStr()); +} + +void BeanShellTest::testRenameEmptyLibrary() +{ + css::uno::Sequence aArgs = { css::uno::Any(u"user"_ustr) }; + + css::uno::Reference xRoot( + m_xFactory->createInstanceWithArgumentsAndContext( + u"com.sun.star.script.provider.ScriptProviderForBeanShell"_ustr, aArgs, m_xContext), + css::uno::UNO_QUERY_THROW); + + css::uno::Reference xInvocation(xRoot, css::uno::UNO_QUERY_THROW); + + // Create an empty library + createChild(xInvocation, "EmptyLibrary"); + + // Make sure the library we just created exists in the hierarchy + css::uno::Reference xEmptyLibrary(findChild(xRoot, u"EmptyLibrary"), + css::uno::UNO_QUERY_THROW); + + // Try renaming it + renameLibrary(xEmptyLibrary, "RenamedEmptyLibrary"); + + // Make sure it now exists in the hierarchy with the new name + findChild(xRoot, u"RenamedEmptyLibrary"); +} + +void BeanShellTest::testRenameNonEmptyLibrary() +{ + css::uno::Sequence aArgs = { css::uno::Any(u"user"_ustr) }; + + css::uno::Reference xRoot( + m_xFactory->createInstanceWithArgumentsAndContext( + u"com.sun.star.script.provider.ScriptProviderForBeanShell"_ustr, aArgs, m_xContext), + css::uno::UNO_QUERY_THROW); + + css::uno::Reference xInvocation(xRoot, css::uno::UNO_QUERY_THROW); + + // Create an empty library + createChild(xInvocation, "NonEmptyLibrary"); + + // Make sure the library we just created exists in the hierarchy + css::uno::Reference xNewLibrary(findChild(xRoot, u"NonEmptyLibrary"), + css::uno::UNO_QUERY_THROW); + + // Create a script within the new library + createChild(xNewLibrary, "SomeScript"); + + // Try renaming the library + renameLibrary(xNewLibrary, "RenamedNonEmptyLibrary"); + + // Make sure it now exists in the hierarchy with the new name + css::uno::Reference xNewNode + = findChild(xRoot, u"RenamedNonEmptyLibrary"); + + // Make sure it still has the child + css::uno::Sequence> xChildren + = xNewNode->getChildNodes(); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xChildren.getLength()); + CPPUNIT_ASSERT_EQUAL(u"SomeScript.bsh"_ustr, xChildren[0]->getName()); + + // Make sure the child has an updated URI + css::uno::Reference xChildProps(xChildren[0], + css::uno::UNO_QUERY_THROW); + OUString sUri; + CPPUNIT_ASSERT(xChildProps->getPropertyValue("URI") >>= sUri); + CPPUNIT_ASSERT(sUri.indexOf("RenamedNonEmptyLibrary") != -1); +} + +CPPUNIT_TEST_SUITE_REGISTRATION(BeanShellTest); + +} // namespace +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */