Bug 40079 - "File/Save (as)" inoperant if macro, but not dialog library loaded
Summary: "File/Save (as)" inoperant if macro, but not dialog library loaded
Alias: None
Product: LibreOffice
Classification: Unclassified
Component: BASIC (show other bugs)
(earliest affected)
3.4 Daily
Hardware: All All
: medium major
Assignee: Not Assigned
Keywords: patch
Depends on:
Reported: 2011-08-14 07:12 UTC by Lionel Elie Mamane
Modified: 2013-08-02 20:55 UTC (History)
2 users (show)

See Also:
Crash report or crash signature:

Solution #1 (2.79 KB, patch)
2011-08-14 07:36 UTC, Lionel Elie Mamane
Solution #2 (1022 bytes, patch)
2011-08-14 07:40 UTC, Lionel Elie Mamane
load library before getting it (968 bytes, patch)
2011-08-14 08:05 UTC, Lionel Elie Mamane
testcase (33.62 KB, application/vnd.sun.xml.base)
2011-08-17 09:17 UTC, Lionel Elie Mamane

Note You need to log in before you can comment on or make changes to this bug.
Description Lionel Elie Mamane 2011-08-14 07:12:22 UTC
1) Create new database (.odb file)
2) Create a macro in the database, e.g.:

Sub Tst()
    MsgBox "Tst"
End Tst()

3) Create a dialog in the database
4) save as e.g. foo.odb
5) exit LibreOffice
6) Launch LibreOffice
7) Open foo.odb
8) Run the macro; this forces the loading of the "Standard" Basic library of the .odb file. DO NOT load the Dialog library in any way.
9) Make a change to the database: e.g. create a new report.
10) "File/save" (or the equivalent toolbar button) does not do anything

If one either does not run any macro (not load the basic library) or loads the Dialog library, then the save works.

I traced this back to:

In dbaccess/source/core/dataaccess/ModelImpl.[hc]xx, m_xDialogLibraries is set (as a reference), but the library is NOT LOADED. On save:
 - ODatabaseDocument::store calls
 - ODatabaseDocument::impl_storeToStorage_throw calls
 - ODatabaseDocument::impl_writeStorage_throw calls
 - ODatabaseModelImpl::storeLibraryContainersTo has this code:

    if ( m_xDialogLibraries.is() )
        m_xDialogLibraries->storeLibrariesToStorage( _rxToRootStorage );

That is, it calls SfxDialogLibraryContainer::storeLibrariesToStorage (baisc/source/uno/dlgcont.cxx) as soon as m_xDialogLibraries is a valid reference. Then:

 - SfxDialogLibraryContainer::storeLibrariesToStorage iterates over the libraries
 - within a library (variable xLib) it iterates over the dialogs in that library:

            Sequence< OUString > sDialogs = xLib->getElementNames();
            sal_Int32 nDialogs( sDialogs.getLength() );
            for ( sal_Int32 j=0; j < nDialogs; ++j )
                // Each Dialog has an associated xISP
                Reference< io::XInputStreamProvider > xISP;
                xLib->getByName( sDialogs[ j ] ) >>= xISP;

And here you have it: getByName is called on xLib, but xLib is not loaded. Meaning (in file basic/source/uno/namecont.cxx):

 - SfxLibraryContainer::getByName calls 
 - SfxLibrary::getByName

The first thing that SfxLibrary::getByName does is
which raises  WrappedTargetException (with an empty message!), because the library is not loaded.

I see three places where this can be "fixed" and I'm not sure which is the "right fix". Please advise.

1) Remove call to impl_checkLoaded() from SfxLibrary::getByName

Then it returns a NULL reference, and SfxDialogLibraryContainer::storeLibrariesToStorage handles that well:

                xLib->getByName( sDialogs[ j ] ) >>= xISP;
                if ( xISP.is() )

2) Add to SfxDialogLibraryContainer::storeLibrariesToStorage before the code above:

                if ( ! xLib->isLibraryLoaded(sDialogs[ j ]) ) {

3) Change ODatabaseModelImpl::storeLibraryContainersTo from

    if ( m_xDialogLibraries.is() )
       m_xDialogLibraries->storeLibrariesToStorage( _rxToRootStorage );

    if ( m_xDialogLibraries.is() &&  m_xDialogLibraries->isModified() )
       m_xDialogLibraries->storeLibrariesToStorage( _rxToRootStorage );
And the same for m_xBasicLibraries, obviously.


I have a bad feeling about number "3)"; it corrects this specific bug, but it may still be the case that some Dialog libraries are modified and others are not loaded... and then we are in trouble again.

Number 1) essentially changes the guarantees given by getByName; it was guaranteed to return a valid Reference or throw an exception, and now it can return a NULL reference, which can break existing code. OTOH, if some other code somewhere _also_ calls getByName without _first_ checking whether the library is loaded (but properly checks whether it got a valid reference afterwards), it will also fix that yet undiscovered bug :)

Number 2) has the symmetric advantages and disadvantages of number 1)... It won't make previously valid code invalid (the guarantees given by getByName don't change), but it lets responsibility to check for isLibraryLoaded _before_ to callers of getByName, which is a recipe for future similar bugs IMHO.
Comment 1 Lionel Elie Mamane 2011-08-14 07:36:04 UTC
Created attachment 50199 [details]
Solution #1
Comment 2 Lionel Elie Mamane 2011-08-14 07:40:40 UTC
Created attachment 50204 [details]
Solution #2
Comment 3 Lionel Elie Mamane 2011-08-14 08:05:05 UTC
Created attachment 50206 [details]
load library before getting it

Sorry, I got on a completely wrong track in my previous thoughts. A comment shows that this code expects the libraries to already be loaded. As this is not the case, here is a patch that ... simply loads the library before getting it. The only small thing I'm not 100% sure of is whether this load should or should not go under scope of the "if xLib.is()".
Comment 4 Cor Nouws 2011-08-15 00:45:13 UTC
@ Lionel:since you propose a patch, pls mail to the dev-list and attach the patch
Comment 5 Lionel Elie Mamane 2011-08-17 09:17:49 UTC
Created attachment 50310 [details]
Comment 6 Lionel Elie Mamane 2011-08-17 09:26:25 UTC
I reproduced with master, a clone as of earlier today 17 August 2011.

Precise reproduction instructions:

 - start LO with Macro Security to Medium (or Low)
 - open testcase attached to this bug
 - when asked click on button "enable macros"
 - a MessageBox saying "Main! Main!" pops up
 - click "OK" to dismiss
 - click on "Reports" (left part of screen, last entry)
 - right-click on "Report11"
 - choose "delete" in the pop-up menu
 - confirm by clicking on button "delete"
 - click on "save" toolbar button (floppy disk image)

Notice that the toolbar button does not get deactivated. Exit LO; discard changes. Open LO again, notice Report11 is still there.
Comment 7 Lionel Elie Mamane 2011-08-17 10:56:31 UTC
Since I cloned, someone pushed my patch, so fixed in master, commit 03e9161e2eca9d389d7ce419495538c31f6aed31.