1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <cppuhelper/implementationentry.hxx>
21 #include <cppuhelper/factory.hxx>
22 #include <cppuhelper/exc_hlp.hxx>
23 #include <util/MiscUtils.hxx>
24 
25 #include <com/sun/star/beans/XPropertySet.hpp>
26 #include <com/sun/star/util/XMacroExpander.hpp>
27 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
28 
29 #include "MasterScriptProvider.hxx"
30 #include "ActiveMSPList.hxx"
31 
32 #include <tools/diagnose_ex.h>
33 
34 using namespace com::sun::star;
35 using namespace com::sun::star::uno;
36 using namespace com::sun::star::script;
37 using namespace ::sf_misc;
38 
39 namespace func_provider
40 {
41 
42 ActiveMSPList::ActiveMSPList(  const Reference< XComponentContext > & xContext ) : m_xContext( xContext )
43 {
44     userDirString = "user";
45     shareDirString = "share";
46     bundledDirString = "bundled";
47 }
48 
49 ActiveMSPList::~ActiveMSPList()
50 {
51 }
52 
53 Reference< provider::XScriptProvider >
54 ActiveMSPList::createNewMSP( const uno::Any& context )
55 {
56     Sequence< Any > args( &context, 1 );
57 
58     Reference< provider::XScriptProvider > msp(
59         m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
60             "com.sun.star.script.provider.MasterScriptProvider", args, m_xContext ), UNO_QUERY );
61     return msp;
62 }
63 
64 class NonDocMSPCreator
65 {
66 public:
67     explicit NonDocMSPCreator(ActiveMSPList *pList)
68     {
69         pList->createNonDocMSPs();
70     }
71 };
72 
73 namespace
74 {
75     //thread-safe double-locked class to ensure createNonDocMSPs is called once
76     class theNonDocMSPCreator : public rtl::StaticWithArg<NonDocMSPCreator, ActiveMSPList*, theNonDocMSPCreator> {};
77 
78     void ensureNonDocMSPs(ActiveMSPList *pList)
79     {
80         theNonDocMSPCreator::get(pList);
81     }
82 }
83 
84 Reference< provider::XScriptProvider >
85 ActiveMSPList::getMSPFromAnyContext( const Any& aContext )
86 {
87     Reference< provider::XScriptProvider > msp;
88     OUString sContext;
89     if ( aContext >>= sContext )
90     {
91         msp = getMSPFromStringContext( sContext );
92         return msp;
93     }
94 
95     Reference< frame::XModel > xModel( aContext, UNO_QUERY );
96 
97     Reference< document::XScriptInvocationContext > xScriptContext( aContext, UNO_QUERY );
98     if ( xScriptContext.is() )
99     {
100         try
101         {
102             // the component supports executing scripts embedded in a - possibly foreign document.
103             // Check whether this other document it's the component itself.
104             if ( !xModel.is() || ( xModel != xScriptContext->getScriptContainer() ) )
105             {
106                 msp = getMSPFromInvocationContext( xScriptContext );
107                 return msp;
108             }
109         }
110         catch( const lang::IllegalArgumentException& )
111         {
112             xModel.set( Reference< frame::XModel >() );
113         }
114     }
115 
116     if ( xModel.is() )
117     {
118         sContext = MiscUtils::xModelToTdocUrl( xModel, m_xContext );
119         msp = getMSPFromStringContext( sContext );
120         return msp;
121     }
122 
123     ensureNonDocMSPs(this);
124     return m_hMsps[ shareDirString ];
125 }
126 
127 Reference< provider::XScriptProvider >
128     ActiveMSPList::getMSPFromInvocationContext( const Reference< document::XScriptInvocationContext >& xContext )
129 {
130     Reference< provider::XScriptProvider > msp;
131 
132     Reference< document::XEmbeddedScripts > xScripts;
133     if ( xContext.is() )
134         xScripts.set( xContext->getScriptContainer() );
135     if ( !xScripts.is() )
136     {
137         throw lang::IllegalArgumentException(
138             "Failed to create MasterScriptProvider for ScriptInvocationContext: "
139             "Component supporting XEmbeddScripts interface not found.",
140             nullptr, 1 );
141     }
142 
143     ::osl::MutexGuard guard( m_mutex );
144 
145     Reference< XInterface > xNormalized( xContext, UNO_QUERY );
146     ScriptComponent_map::const_iterator pos = m_mScriptComponents.find( xNormalized );
147     if ( pos == m_mScriptComponents.end() )
148     {
149         // TODO
150         msp = createNewMSP( uno::makeAny( xContext ) );
151         addActiveMSP( xNormalized, msp );
152     }
153     else
154     {
155         msp = pos->second;
156     }
157 
158     return msp;
159 }
160 
161 Reference< provider::XScriptProvider >
162     ActiveMSPList::getMSPFromStringContext( const OUString& context )
163 {
164     Reference< provider::XScriptProvider > msp;
165     try
166     {
167         if ( context.startsWith( "vnd.sun.star.tdoc" ) )
168         {
169             Reference< frame::XModel > xModel( MiscUtils::tDocUrlToModel( context ) );
170 
171             Reference< document::XEmbeddedScripts > xScripts( xModel, UNO_QUERY );
172             Reference< document::XScriptInvocationContext > xScriptsContext( xModel, UNO_QUERY );
173             if ( !xScripts.is() && !xScriptsContext.is() )
174             {
175                 throw lang::IllegalArgumentException(
176                     "Failed to create MasterScriptProvider for '"
177                     + context +
178                     "': Either XEmbeddScripts or XScriptInvocationContext need to be supported by the document.",
179                     nullptr, 1 );
180             }
181 
182             ::osl::MutexGuard guard( m_mutex );
183             Reference< XInterface > xNormalized( xModel, UNO_QUERY );
184             ScriptComponent_map::const_iterator pos = m_mScriptComponents.find( xNormalized );
185             if ( pos == m_mScriptComponents.end() )
186             {
187                 msp = createNewMSP( context );
188                 addActiveMSP( xNormalized, msp );
189             }
190             else
191             {
192                 msp = pos->second;
193             }
194         }
195         else
196         {
197             ::osl::MutexGuard guard( m_mutex );
198             Msp_hash::iterator h_itEnd =  m_hMsps.end();
199             Msp_hash::const_iterator itr = m_hMsps.find( context );
200             if ( itr ==  h_itEnd )
201             {
202                 msp = createNewMSP( context );
203                 m_hMsps[ context ] = msp;
204             }
205             else
206             {
207                 msp = m_hMsps[ context ];
208             }
209         }
210     }
211     catch( const lang::IllegalArgumentException& )
212     {
213         // allowed to leave
214     }
215     catch( const RuntimeException& )
216     {
217         // allowed to leave
218     }
219     catch( const Exception& )
220     {
221         css::uno::Any anyEx = cppu::getCaughtException();
222         throw lang::WrappedTargetRuntimeException(
223             "Failed to create MasterScriptProvider for context '"
224             + context + "'.",
225             *this, anyEx );
226     }
227     return msp;
228 }
229 
230 void
231 ActiveMSPList::addActiveMSP( const Reference< uno::XInterface >& xComponent,
232                const Reference< provider::XScriptProvider >& msp )
233 {
234     ::osl::MutexGuard guard( m_mutex );
235     Reference< XInterface > xNormalized( xComponent, UNO_QUERY );
236     ScriptComponent_map::const_iterator pos = m_mScriptComponents.find( xNormalized );
237     if ( pos == m_mScriptComponents.end() )
238     {
239         m_mScriptComponents[ xNormalized ] = msp;
240 
241         // add self as listener for component disposal
242         // should probably throw from this method!!, reexamine
243         try
244         {
245             Reference< lang::XComponent > xBroadcaster =
246                 Reference< lang::XComponent >( xComponent, UNO_QUERY_THROW );
247             xBroadcaster->addEventListener( this );
248         }
249         catch ( const Exception& )
250         {
251             DBG_UNHANDLED_EXCEPTION("scripting");
252         }
253     }
254 }
255 
256 
257 void SAL_CALL ActiveMSPList::disposing( const css::lang::EventObject& Source )
258 
259 {
260     try
261     {
262         Reference< XInterface > xNormalized( Source.Source, UNO_QUERY );
263         if ( xNormalized.is() )
264         {
265             ::osl::MutexGuard guard( m_mutex );
266             ScriptComponent_map::iterator pos = m_mScriptComponents.find( xNormalized );
267             if ( pos != m_mScriptComponents.end() )
268                 m_mScriptComponents.erase( pos );
269         }
270     }
271     catch ( const Exception& )
272     {
273         // if we get an exception here, there is not much we can do about
274         // it can't throw as it will screw up the model that is calling dispose
275         DBG_UNHANDLED_EXCEPTION("scripting");
276     }
277 }
278 
279 void
280 ActiveMSPList::createNonDocMSPs()
281 {
282     // do creation of user and share MSPs here
283     OUString serviceName("com.sun.star.script.provider.MasterScriptProvider");
284     Sequence< Any > args(1);
285 
286     args[ 0 ] <<= userDirString;
287     Reference< provider::XScriptProvider > userMsp( m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( serviceName, args, m_xContext ), UNO_QUERY );
288     // should check if provider reference is valid
289     m_hMsps[ userDirString ] = userMsp;
290 
291     args[ 0 ] <<= shareDirString;
292     Reference< provider::XScriptProvider > shareMsp( m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( serviceName, args, m_xContext ), UNO_QUERY );
293     // should check if provider reference is valid
294     m_hMsps[ shareDirString ] = shareMsp;
295 
296     args[ 0 ] <<= bundledDirString;
297     Reference< provider::XScriptProvider > bundledMsp( m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( serviceName, args, m_xContext ), UNO_QUERY );
298     // should check if provider reference is valid
299     m_hMsps[ bundledDirString ] = bundledMsp;
300 }
301 
302 } // namespace func_provider
303 
304 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
305