Package blackberry.web.widget.bf

Source Code of blackberry.web.widget.bf.WidgetBrowserFieldListener

/*
* Copyright 2010-2011 Research In Motion Limited.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package blackberry.web.widget.bf;

import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.Enumeration;
import java.util.Hashtable;

import net.rim.device.api.browser.field.ContentReadEvent;
import net.rim.device.api.browser.field2.BrowserField;
import net.rim.device.api.browser.field2.BrowserFieldListener;
import net.rim.device.api.io.IOUtilities;
import net.rim.device.api.script.ScriptEngine;
import net.rim.device.api.script.Scriptable;
import net.rim.device.api.script.ScriptableFunction;
import net.rim.device.api.ui.Screen;
import net.rim.device.api.util.SimpleSortingVector;
import net.rim.device.api.web.WidgetAccess;
import net.rim.device.api.web.WidgetConfig;
import net.rim.device.api.web.WidgetExtension;
import net.rim.device.api.web.WidgetFeature;

import blackberry.core.ScriptableFunctionWrapper;
import blackberry.core.ScriptableWrapper;
import org.w3c.dom.Document;
import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;
import org.w3c.dom.events.EventTarget;
import org.w3c.dom.html2.HTMLDocument;

import blackberry.common.util.JSUtilities;
import blackberry.core.IJSExtension;
import blackberry.web.widget.device.DeviceInfo;
import blackberry.web.widget.impl.WidgetConfigImpl;
import blackberry.web.widget.policy.WidgetPolicy;
import blackberry.web.widget.policy.WidgetPolicyFactory;

/**
*
*/
public class WidgetBrowserFieldListener extends BrowserFieldListener {

    /**
     * A proxy object for the ScriptEngine that will wrap all Scriptable extension in a unique instanced object. This ensures any
     * singleton instances will not be prematurely removed when the browser does the cleanup.
     */
    private static class ProxyScriptEngine implements ScriptEngine {

        private ScriptEngine _scriptEngine;

        /**
         * Wrap the script engine and proxy all the methods to the real object
         *
         * @param scriptEngine the script engine to proxy
         */
        public ProxyScriptEngine( ScriptEngine scriptEngine ) {
            _scriptEngine = scriptEngine;
        }

        /*
         * (non-Javadoc)
         *
         * @see net.rim.device.api.script.ScriptEngine#addExtension(java.lang.String, java.lang.Object)
         */
        public void addExtension( String name, Object ext ) throws Exception {
            /*
             * When the browser cleans up extensions of a previous page. It does not check to see
             * if the current page is using that extension too. It will unload multiple instances as well.
             */
            if( ext instanceof ScriptableFunction ) {
                _scriptEngine.addExtension( name, new ScriptableFunctionWrapper( (ScriptableFunction) ext ) );
            } else if( ext instanceof Scriptable ) {
                _scriptEngine.addExtension( name, new ScriptableWrapper( (Scriptable) ext ) );
            } else {
                _scriptEngine.addExtension( name, ext );
            }

        }

        /*
         * (non-Javadoc)
         *
         * @see net.rim.device.api.script.ScriptEngine#compileScript(java.lang.String)
         */
        public Object compileScript( String script ) throws IllegalArgumentException {
            return _scriptEngine.compileScript( script );
        }

        /*
         * (non-Javadoc)
         *
         * @see net.rim.device.api.script.ScriptEngine#executeCompiledScript(java.lang.Object, java.lang.Object)
         */
        public Object executeCompiledScript( Object arg0, Object arg1 ) throws IllegalStateException, IllegalArgumentException,
                RuntimeException {
            return _scriptEngine.executeCompiledScript( arg0, arg1 );
        }

        /*
         * (non-Javadoc)
         *
         * @see net.rim.device.api.script.ScriptEngine#executeScript(java.lang.String, java.lang.Object)
         */
        public Object executeScript( String arg0, Object arg1 ) throws IllegalStateException, IllegalArgumentException,
                RuntimeException {
            return _scriptEngine.executeScript( arg0, arg1 );
        }
    }

    private WidgetConfig _widgetConfig;
    private WidgetPolicy _widgetPolicy;
    private WidgetAccess[] _accessList;
    private WidgetAccess _prevAccess;
    private ScriptEngine _scriptEngine = null;
   
    public WidgetBrowserFieldListener( WidgetConfig wc ) {
        _widgetConfig = wc;
        _widgetPolicy = WidgetPolicyFactory.getPolicy();
        _accessList = _widgetConfig.getAccessList();
    }

    /**
     * @see net.rim.device.api.browser.field2.BrowserFieldListener
     */
    public void documentCreated( BrowserField browserField, ScriptEngine scriptEngine, Document document ) throws Exception {
        // Clean up extensions that were loaded from previous request
        if( _prevAccess != null ) {
            unloadFeatures( _prevAccess.getFeatures() );
            _prevAccess = null;
        }

        _scriptEngine = null;

        ProxyScriptEngine proxyScriptEngine = null;
        if( scriptEngine != null ) {
            // Clobber the troublesome "blackberry" namespace as it won't free some extensions, but keep a backup
            scriptEngine.executeScript( "(function () { if ( typeof( this.blackberry ) != 'undefined' ) { this.__blackberry__ = this.blackberry; delete this.blackberry; } } )(); ", null );

            // Create proxy for the script engine that will ensure unique namespaces being added as extensions
            proxyScriptEngine = new ProxyScriptEngine( scriptEngine );
           
            // Inject global scripts - must be done before the ScriptableObjects get injected
            injectJavaScript( proxyScriptEngine );
           
            _scriptEngine = proxyScriptEngine;
        }

        // Load features for the current URL of the BrowserField.
        WidgetAccess access = _widgetPolicy.getElement( document.getDocumentURI(), _accessList );
        if( access != null && access.getFeatures() != null ) {
            loadFeatures( access.getFeatures(), document, proxyScriptEngine );
            _prevAccess = access;
        }

        if( browserField.getScreen() instanceof BrowserFieldScreen ) {
            BrowserFieldScreen bfScreen = (BrowserFieldScreen) browserField.getScreen();

            // Push the loading screen at this time so that history.back() will use loading screens
            if( !bfScreen.getPageManager().isFirstLaunch() && !bfScreen.getPageManager().isSuppressingLoadingScreen()
                    && bfScreen.getPageManager().isLoadingScreenRequired( document.getDocumentURI() ) ) {
                bfScreen.getPageManager().showLoadingScreen();
                Thread.yield();
                Thread.sleep( 100 );
            }

            /*
             * Fix the issue where the screen becomes blank after clicking the link in the frame. Only reset the flag(s) for the
             * root document.
             */
            if( browserField.getDocument() == document ) {
                bfScreen.getPageManager().clearFlags();
            }

            // For navigation mode, add blackberry.focus namespace to JavaScriptExtension.
            if( bfScreen.getAppNavigationMode() && document instanceof HTMLDocument ) {
                // Add our JS navigation extension to JavaScript Engine.
                if( proxyScriptEngine != null ) {
                    proxyScriptEngine.addExtension( NavigationNamespace.NAME, bfScreen.getNavigationExtension() );
                    // Load navmode.js into script engine, must be done after blackberry.focus namespace is defined
                    bfScreen.getNavigationJS().loadFeature( null, null, null, scriptEngine );                 
                }
                bfScreen.getNavigationController().reset();
            }

            // Inject HTML5 to gears shim for 5.0 devices\r
            if( scriptEngine != null && DeviceInfo.isBlackBerry5() ) {
                WidgetExtension HTML5Extension = bfScreen.getHTML5Extension();
                HTML5Extension.loadFeature( null, null, null, proxyScriptEngine );
            }
           
            if( scriptEngine != null ) {
                // Restore any clobbered members. 
                // Note: blackberry.network does not show up when enumerated. Add it manually.
                scriptEngine.executeScript( "(function () { if ( typeof( this.__blackberry__ ) != 'undefined' ) { for ( var name in this.__blackberry__ ) { this.blackberry[name] = this.__blackberry__[name]; } this.blackberry.network = this.__blackberry__.network; delete this.__blackberry__; } } )(); ", null );
            }
        }
    }

    /**
     * @see net.rim.device.api.browser.field2.BrowserFieldListener
     */
    public void documentUnloading( BrowserField browserField, Document document ) throws Exception {
        if( browserField.getScreen() instanceof BrowserFieldScreen ) {
            BrowserFieldScreen bfScreen = (BrowserFieldScreen) browserField.getScreen();

            // For navigation mode, reset the navigation map when the original document is unloading.
            if( bfScreen.getAppNavigationMode() && browserField.getDocument() == null ) {
                bfScreen.getNavigationController().reset();
            }
        }
    }

    // Override other methods ? documentAborted, documentError, documentLoaded, documentProgress.
    // Synchronized to ensure features are loaded properly
    private synchronized void loadFeatures( WidgetFeature[] features, Document doc, ScriptEngine scriptEngine ) {
        int fSize = features.length;
        WidgetFeature feature = null;
        Object extension = null;
        Hashtable jsExtensionsByFeatureId = new Hashtable();
        // Go through the list of all features, store all JS extension in a map first
        for( int i = 0; i < fSize; i++ ) {
            feature = features[ i ];
            extension = ( (WidgetConfigImpl) _widgetConfig ).getExtensionObjectForFeature( feature.getID() );
            if( extension instanceof IJSExtension ) {
                jsExtensionsByFeatureId.put( feature.getID(), extension );
            }
        }

        // Sort by feature id so that the JS gets loaded in the right order
        if( !jsExtensionsByFeatureId.isEmpty() ) {
            SimpleSortingVector featureIds = new SimpleSortingVector();
            Enumeration keys = jsExtensionsByFeatureId.keys();
            while( keys.hasMoreElements() ) {
                featureIds.addElement( keys.nextElement() );
            }

            featureIds.setSortComparator( JSUtilities.getStringComparator() );
            featureIds.reSort();

            Enumeration sortedFeatureIds = featureIds.elements();
            while( sortedFeatureIds.hasMoreElements() ) {
                IJSExtension jsExtension = (IJSExtension) jsExtensionsByFeatureId.get( sortedFeatureIds.nextElement() );
                jsExtension.loadFeature( feature.getID(), feature.getVersion(), doc, scriptEngine, ( (WidgetConfigImpl) _widgetConfig ).getJSInjectionPaths() );
            }
        }

        // Load widget extensions after all JS extensions are loaded
        for( int i = 0; i < fSize; i++ ) {
            feature = features[ i ];
            extension = ( (WidgetConfigImpl) _widgetConfig ).getExtensionObjectForFeature( feature.getID() );
            if( extension != null && extension instanceof WidgetExtension ) {
                try {
                    ( (WidgetExtension) extension ).loadFeature( feature.getID(), feature.getVersion(), doc, scriptEngine );
                } catch( Exception x ) {
                    // ignore feature
                }
            }
        }      
    }

    private synchronized void unloadFeatures( WidgetFeature[] features ) {
        int fSize = features.length;
        WidgetFeature feature = null;
        Object extension = null;

        for( int i = 0; i < fSize; i++ ) {
            feature = features[ i ];
            extension = ( (WidgetConfigImpl) _widgetConfig ).getExtensionObjectForFeature( feature.getID() );
            if( extension instanceof IJSExtension ) {
                ( (IJSExtension) extension ).unloadFeatures();
            } else if( extension instanceof WidgetExtension ) {
                ( (WidgetExtension) extension ).unloadFeatures( null );
            }
        }
    }

    /**
     * @see net.rim.device.api.browser.field2.BrowserFieldListener
     */
    public void documentLoaded( BrowserField browserField, Document document ) throws Exception {
       
        if( _scriptEngine != null ) {
            // Inform the js framework that we are ready to process requests
            try {
                _scriptEngine.executeScript( "try { var event = document.createEvent(\"Event\"); "
                        + " event.initEvent(\"frameworkready\", true, true); "
                        + " window.dispatchEvent(event); } catch (e) { } ", null );
            } catch( Exception e ) {
            }
        }
       
        /*
         * Fix the issue "click the link in the frame, the screen becomes blank" Only reset the flag(s) for the root document.
         */
        if( browserField.getDocument() == document ) {
            // For navigation mode.
//            if( browserField.getScreen() instanceof BrowserFieldScreen ) {
//                BrowserFieldScreen bfScreen = (BrowserFieldScreen) browserField.getScreen();
//
//                if( bfScreen.getAppNavigationMode() && browserField.getDocument() == document ) {
//                    bfScreen.getNavigationController().update();
//
//                    // Add Layout update event listener.
//                    if( document instanceof EventTarget ) {
//                        EventTarget target = (EventTarget) document;
//                        EventListener listener = new UpdateBinsEventListener( browserField );
//                        target.addEventListener( "DOMNodeInserted", listener, false );
//                        target.addEventListener( "DOMNodeRemoved", listener, false );
//                    }
//                }
//            }

            // Pop the loading screeen if it is displayed.
            if( browserField.getScreen() instanceof BrowserFieldScreen ) {
                BrowserFieldScreen bfScreen = (BrowserFieldScreen) browserField.getScreen();
                if( bfScreen.getPageManager().isLoadingScreenDisplayed() ) {
                    bfScreen.getPageManager().hideLoadingScreen();
                }
            }
        }
    }

    /**
     * @see net.rim.device.api.browser.field2.BrowserFieldListener The downloadProgress event will be fired for links to
     *      non-document URIs in our case, it should function both documentCreated handler and documentLoaded handler
     */
    public void downloadProgress( BrowserField browserField, ContentReadEvent event ) throws Exception {
        if( browserField.getScreen() instanceof BrowserFieldScreen ) {
            BrowserFieldScreen bfScreen = (BrowserFieldScreen) browserField.getScreen();

            // Clear the flags set during the documentCreated event
            bfScreen.getPageManager().clearFlags();

            // Clear the flags set during the documentCreated event for OS versions prior to 6.0
            if( event.getItemsRead() == event.getItemsToRead() && !DeviceInfo.isBlackBerry6() ) {
                if( bfScreen.getPageManager().isLoadingScreenDisplayed() ) {
                    bfScreen.getPageManager().hideLoadingScreen();
                }
            }
        }
    }

    private void injectJavaScript( ScriptEngine scriptEngine ) {
        try {
            if( _widgetConfig instanceof WidgetConfigImpl ) {
                WidgetConfigImpl wConfigImpl = (WidgetConfigImpl) _widgetConfig;

                // shared global JS files
                SimpleSortingVector sharedGlobalJSPaths = wConfigImpl.getSharedGlobalJSInjectionPaths();
                sharedGlobalJSPaths.setSortComparator( JSUtilities.getStringComparator() );
                // sort to ensure JS is loaded in correct order
                sharedGlobalJSPaths.reSort();
                Enumeration sharedGlobalJSPathElems = sharedGlobalJSPaths.elements();
                while( sharedGlobalJSPathElems.hasMoreElements() ) {
                    String jsPath = getValidPath( (String) sharedGlobalJSPathElems.nextElement() );
                    Object compiledScript = scriptEngine.compileScript( readJSContent( jsPath ) );
                    scriptEngine.executeCompiledScript( compiledScript, null );
                }
            }
        } catch( Exception e ) {
            System.out.println( "Error Injection: " + e.getMessage() );
        }
    }

    private String getValidPath( String jsPath ) {
        String SLASH_FWD = "/";

        if( !jsPath.startsWith( SLASH_FWD ) ) {
            return SLASH_FWD + jsPath;
        }

        return jsPath;
    }

    private String readJSContent( String jsURI ) {
        String jsContent = "";
        InputStream is = null;
        try {
            is = getClass().getResourceAsStream( jsURI );
            byte[] data = IOUtilities.streamToBytes( is );
            jsContent = new String( data );
        } catch( Exception e ) {
        } finally {
            try {
                if( is != null ) {
                    is.close();
                    is = null;
                }
            } catch( IOException e ) {
            }
        }
        return jsContent;
    }

//    private static class UpdateBinsEventListener implements EventListener {
//        private WeakReference _browserFieldWeakReference;
//
//        UpdateBinsEventListener( BrowserField browserField ) {
//            super();
//            _browserFieldWeakReference = new WeakReference( browserField );
//        }
//
//        private BrowserField getBrowserField() {
//            Object o = _browserFieldWeakReference.get();
//            if( o instanceof BrowserField ) {
//                return (BrowserField) o;
//            } else {
//                return null;
//            }
//        }
//
//        public void handleEvent( Event evt ) {
//            Screen screen = getBrowserField().getScreen();
//            if( screen instanceof BrowserFieldScreen ) {
//                BrowserFieldScreen bfScreen = (BrowserFieldScreen) screen;
//
//                if( bfScreen.getAppNavigationMode() ) {
//                    bfScreen.getNavigationController().update();
//                }
//            }
//        }
//    }

}
TOP

Related Classes of blackberry.web.widget.bf.WidgetBrowserFieldListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.