Posted by BGoulette in SWF Studio V3 on Jun 03 2008, 10:09 am

I'm trying to get the browser to work through an admittedly hamfisted series of functions, and in some cases, it does, but I'm not sure what I'm doing wrong here:

I'm trying to assign a URL to the browser, and I think it's working, but I've used setNotify for onDocumentComplete, but when it fires (and it does -- at least I have a trace that tells me it does!), it's almost immediately followed by Error executing callback: Error #2007 in the trace output window.

Any clues what I'm doing wrong? Here's the spaghetti plate of horrible code (apologies):


      public function BrowserContainer (pPanel:Panel, pIsShort:Boolean=false)
      {
         panel=pPanel;
         isShort=pIsShort;
         
         browserHeight=isShort ? BrowserContainer.HEIGHT-BrowserContainer.FIELD_LEADING : BrowserContainer.HEIGHT;
         
         this.addEventListener(Event.ADDED_TO_STAGE, doRecreateBrowser);
         this.addEventListener(Event.REMOVED_FROM_STAGE, doDestroyBrowser);
         
         renderBrowserBase();
         renderBrowserChrome();
         
         ssDebug.trace("Panel: "+panel);
      }

// Here's where I left out a bunch of otherwise insignificant stuff

      public function doRecreateBrowser (evt:Event):void
      {
         arrHistory=new Array();

         ssCore.Browser.setNotify({event:"onProgress"}, {callback:showProgress});
         ssCore.Browser.setNotify({event:"onDocumentComplete"}, {callback:showBrowser});
         
         ssCore.Browser.setBrowser({browser:"IE"});
         ssCore.Browser.allowMenu({flag:false});
         ssCore.Browser.showBorder({flag:false});
         ssCore.Browser.setSilent({flag:true});
         ssCore.Browser.setSize({width:BrowserContainer.WIDTH, height:browserHeight});
         
         var tmpPoint:Point=new Point(panel.x, panel.y);
         tmpPoint=localToGlobal(tmpPoint);
         ssCore.Browser.setPosition({x:tmpPoint.x-(PanelButton.WIDTH*panel.panelIndex), y:tmpPoint.y});
         
         ssCore.Browser.setURL({url:panel.panelURL});
      }
      
      public function doDestroyBrowser (evt:Event):void
      {
         // Make sure the browser for this panel is closed.
         ssCore.Browser.close();
         if (browserAddress) browserAddress.browserURL.text="";
      }
      
      public function doToggleBrowser (pShow:Boolean=false):void
      {
         ssCore.Browser.setURL({url:panel.panelURL});
         
         var tmpURL:Object=ssCore.Browser.getURL();
         ssDebug.trace("Browser url: "+tmpURL.result);
         
         if (pShow) {
            ssCore.Browser.open();
         }
         else {
            /**
             * If the browser is in the process of loading something and we don't stop
             * it, when it finishes, it'll load regardless of where we are, which is
             * usually some place we don't want to be.
             */
            ssCore.Browser.close();
            ssCore.Browser.stop();
         }
      }
      
      public function showProgress (pReturn:Object, pCallback:Object, pError:Object):void
      {
         if (pReturn.success) {
            
            ssDebug.trace("Progress: "+pReturn.result);
            
            var a:Array=pReturn.result.split(",");
            if (uint(a[0])<uint(a[1])) {
               crawler.doStart();
            }
            else {
               crawler.doStop();
            }
         }
      }
      
      public function showBrowser (pReturn:Object, pCallback:Object, pError:Object):void
      {
         if (pReturn.success) {
            ssDebug.trace("onDocumentComplete fired!");
            
            crawler.doStop();
            ssCore.Browser.open();
                  
            if (browserAddress) {
               var tmpResult:Object=ssCore.Browser.getURL();
               browserAddress.browserURL.text=tmpResult.result;
            }
         }
      }


(All this lives within a class called BrowserContainer.as: I've left out the boring stuff.)

Please help?! Thanks!!!


Posted by mbd in SWF Studio V3 on Jun 03 2008, 11:35 am

Can you try building your project with the debug Flash player? Then run it again. The error message in the Trace tab should have a little more to it. You might also get other errors being displayed by the Flash player.


Posted by BGoulette in SWF Studio V3 on Jun 03 2008, 12:12 pm

All right, after doing that, I discovered it was flaking out because getURL() wasn't actually returning anything ('cept null). When I changed the function to this:


      public function showBrowser (pReturn:Object, pCallback:Object, pError:Object):void
      {
         if (pReturn.success) {
            ssDebug.trace("onDocumentComplete fired!");
            
            crawler.doStop();
            ssCore.Browser.open();
                  
            if (browserAddress) {
               var tmpResult:Object=ssCore.Browser.getURL({}, {sync:true});
               ssDebug.trace("tmpResult: "+tmpResult.result);
               browserAddress.browserURL.text=tmpResult.result;
            }
         }
      }


I don't get the callback error. (It seems that for some reason I have to call most methods synchronously or they blow up in my face. Is there some system setting that might be requiring this?)

What I also don't get, however, is the browser actually showing up. Am I calling ssCore.Browser.open() at the wrong time? Shouldn't it at least show a "Can't load page" type message if it's trying to load an invalid URL?

Also, "showBrowser" is my callback for the onDocumentComplete event. I'm trying a few more things in the meantime, but as always, I'd appreciate any insight anyone's willing to provide!


Posted by mbd in SWF Studio V3 on Jun 03 2008, 01:09 pm

What's happening with the callback error is that the API traps errors when attempting to execute the callback. The idea here was to trap an error, such as when a callback doesn't exist and report that, even if you didn't build with the debug Flash player. However, if there's a runtime error IN your callback function, it will trap that as well. Since you were trying to access an undefined property the API was catching that and reporting it.

Because you corrected the issue with ssCore.Browser.getURL the remainder of the code is allowed to execute and you don't get the runtime error being reported.

QUOTE:
What I also don't get, however, is the browser actually showing up. Am I calling ssCore.Browser.open() at the wrong time? Shouldn't it at least show a "Can't load page" type message if it's trying to load an invalid URL?
I'm not sure I understand what is happening here. Are you seeing a blank page or a valid web page?

You might want to place trace statements throughout your code to track what is being called and when.

QUOTE:
It seems that for some reason I have to call most methods synchronously or they blow up in my face. Is there some system setting that might be requiring this?
Take a look at the ActionScript API section of the help, specifically the sync and async commands sections.

In summary, by default all commands are asynchronous. If the command will return data and you want the data you need to specify a callback function that will be called to receive the data. You can also use that for error checking to make sure a command completes successfully before moving on.

You can change the default behaviour by setting ssDefaults.synchronousCommands to true (typically right after the call to ssCore.init()). That means all commands will execute synchronously, with a few exceptions:

1. Specifying a callback function will always mean the command will be executed asynchronously.
2. Specifying a callback object with the sync property set to false will force the command to execute asynchronously.
3. Async-only commands (noted in the documentation) will always execute asynchronously, unless you try to force it to be sync by setting the sync property of the callback object to true. In that case a return object will be returned with a failure notification and the command will not execute.

What's the difference between sync and async, really?

Asynchronous commands, under the covers use fscommand to communicate with the SWF Studio player. fscommands are queued by the Flash player and only sent to the external application once all ActionScript in a frame time has executed. This prohibits fscommands from being synchronous since the earliest data will be returned to ActionScript is at the start of the next frame. fscommand is how getURL and a few other Flash-based commands to communicate to the container application are executed. Think of async commands and callbacks like XML.load and XML.onLoad, respectively.

Synchronous commands, under the covers use ExternalInterface to communicate with the SWF Studio player. This method of communication is synchronous, stopping all ActionScript execution until the container application responds. Data is then available for the next line of code, similar to calling a simple function in ActionScript.

Keep these things in mind when you are writing your code. If you expect data from a method call, you should understand whether it will be available to the next line of code (synchronous) or only in a callback function (asynchronous).

This is why your call to ssCore.Browser.getURL() was giving you null. The SWF Studio API does not return a return object when the command is called asynchronously. Forcing the call to be synchronous worked in that case because you are trying to use data immediately in the next line of code.


Posted by BGoulette in SWF Studio V3 on Jun 03 2008, 01:18 pm

Ah, ok, thanks for that (async v. sync); it helps!

As for the browser issue, I have a bunch of traces in there; I see progress in the trace output window, and I even see the onDocumentComplete message -- what I don't see is any browser anything :( In my tests, I'm tracing the URL (set on a "Panel" instance in my code).

I'm going to try rewriting the "BrowserContainer" class and see if I can improve it, though I did discover something else that's a little odd to me...

If I hardcode www.aa.com into a browser instance and ultimately get to a place where I can print a boarding pass, the popped-up window tells me, in effect, "Go away -- too many browser windows open!" (this is something American Airlines has programmed into their site, apparently). I'm not sure what would cause it to think that too many browsers have been opened, and I'm not sure how to send a functional thingy that would allow you to see the error because you need a boarding pass, blah blah blah...Maybe after I rewrite my junk it'll work better?

:(

I really do appreciate the help!


Posted by mbd in SWF Studio V3 on Jun 03 2008, 01:37 pm

Ah, I misread your post, sorry about that. You should be calling ssCore.Browser.open() right after you set the URL for the first time. If you just want to hide it while it's loading or toggle its visibility, take a look at ssCore.Browser.setVisible.

Not sure about the site message. I don't know what their scripts might be doing so I'm not sure how they're determining how many windows are open. It might be trying to determine the parent/opener and failing.


Posted by BGoulette in SWF Studio V3 on Jun 03 2008, 02:49 pm

Welp, I think it must be something wacky with American's web site. If you run the attached spx and type "http://www.aa.com" into the text input, then click GO!, the page loads fine, but if you go through the whole rigamarole of getting to a place where you can print your boarding pass, you still get the "Go away!" message :(

Oh well. I'll have to use Shell.invoke and just launch a separate process for the browser (but at least now I can reliably use Firefox! :) Silver lining? :D ) And, I'm assuming that through WindowList, I'll be able to kill any Firefox processes when the user does return to the kiosk itself...I hope... :)

Edit: VVV Haha, yeah, I just came across killApp, and that's what I'll be using! Thanks sooo much for all the help you've provided! I greatly appreciate it!!!

attachments: ss_browser_test.spx  


Posted by mbd in SWF Studio V3 on Jun 03 2008, 03:10 pm

You could also use ssCore.SysTools.killApp and pass it firefox.exe. That will kill all running instances nicely, unless you use the force parameter, then not so nicely.