This is an automated email from the ASF dual-hosted git repository.

gregdove pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/royale-asjs.git

commit fb031ff116371853344b5ab2a76fa3b3c7945608
Author: greg-dove <[email protected]>
AuthorDate: Mon Feb 23 12:41:26 2026 +1300

    Adding an enhanced iframe component that supports quick configuration for 
message filtering.
---
 .../HTML/src/main/resources/html-manifest.xml      |   1 +
 .../apache/royale/html/elements/SecurityIframe.as  | 214 +++++++++++++++++++++
 2 files changed, 215 insertions(+)

diff --git a/frameworks/projects/HTML/src/main/resources/html-manifest.xml 
b/frameworks/projects/HTML/src/main/resources/html-manifest.xml
index b40f0d7f17..738aaa8af7 100644
--- a/frameworks/projects/HTML/src/main/resources/html-manifest.xml
+++ b/frameworks/projects/HTML/src/main/resources/html-manifest.xml
@@ -56,6 +56,7 @@
     <component id="S" class="org.apache.royale.html.elements.S" />
     <component id="Section" class="org.apache.royale.html.elements.Section"/>
     <component id="Select" class="org.apache.royale.html.elements.Select"/>
+    <component id="SecurityIframe" 
class="org.apache.royale.html.elements.SecurityIframe" />
     <component id="Small" class="org.apache.royale.html.elements.Small"/>
     <component id="Span" class="org.apache.royale.html.elements.Span" />
     <component id="Strong" class="org.apache.royale.html.elements.Strong" />
diff --git 
a/frameworks/projects/HTML/src/main/royale/org/apache/royale/html/elements/SecurityIframe.as
 
b/frameworks/projects/HTML/src/main/royale/org/apache/royale/html/elements/SecurityIframe.as
new file mode 100644
index 0000000000..36601113e9
--- /dev/null
+++ 
b/frameworks/projects/HTML/src/main/royale/org/apache/royale/html/elements/SecurityIframe.as
@@ -0,0 +1,214 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You 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 org.apache.royale.html.elements {
+       import org.apache.royale.html.elements.Iframe;
+       import org.apache.royale.events.Event;
+       import org.apache.royale.events.ValueEvent;
+       
+       [Event(name='message',type='org.apache.royale.events.ValueEvent')]
+       [Event(name='iframeLoad',type='org.apache.royale.events.Event')]
+       [Event(name='iframeError',type='org.apache.royale.events.Event')]
+       /**
+        *  Enhanced version of the Iframe base component, with additional 
security support for message filtering
+        *
+        *  @langversion 3.0
+        *  @playerversion Flash 10.2
+        *  @playerversion AIR 2.6
+        *  @productversion Royale 0.9.8
+        */
+       public class SecurityIframe extends Iframe {
+               public function SecurityIframe() {
+                       super();
+                       COMPILE::JS
+                       {
+                               (element as 
HTMLIFrameElement).addEventListener('load', onStatus);
+                               (element as 
HTMLIFrameElement).addEventListener('error', onStatus)
+                       }
+               }
+               
+               
+               private var _checkExpectedOrigin:Boolean = true;
+               /**
+                * This is true by default. Setting it to false will also allow 
message handling from 
+                * potential opaque origins ("" or 'null' origins)  which is 
NOT considered secure
+                * @param value
+                */
+               public function set checkExpectedOrigin(value:Boolean):void{
+                       _checkExpectedOrigin = value;
+               }
+               public function get checkExpectedOrigin():Boolean{
+                       return _checkExpectedOrigin;
+               }
+               
+               
+               COMPILE::JS
+               private static function isDescendantWindow(source:Window, 
root:Window):Boolean
+               {
+                       var w:Window = source;
+                       
+                       // Walk up the parent chain until we reach the top 
window
+                       while (w && w !== w.parent)
+                       {
+                               if (w === root)
+                                       return true;
+                               
+                               w = w.parent as Window;
+                       }
+                       
+                       return false;
+               }
+               
+               
+               private var _allowNestedFrames:Boolean = false;
+               /**
+                * whether or not to filter messaging from deeper nested iframes
+                * @param value true if messaging from nested iframes is 
supported. false by default
+                */
+               public function set allowNestedFrames(value:Boolean):void{
+                       _allowNestedFrames = value;
+               }
+               public function get allowNestedFrames():Boolean{
+                       return _allowNestedFrames;
+               }
+               
+               
+               override public function set src(value:String):void
+               {
+                       super.src = value;
+                       
+                       COMPILE::JS
+                       {
+                               try{
+                                       expectedOrigin = new URL(value, 
window.location.href).origin;
+                               } catch(e:Error) {
+                                       expectedOrigin = null;
+                               }
+                               
+                               if ((_listeners.length || 
hasEventListener('message')) && expectedOrigin) {
+                                       activateMessaging(true);
+                               }
+                       }
+               }
+               
+               
+               COMPILE::JS
+               private var _listeners:Array = []
+
+               /* the royale version needs fixing... this might be a candidate 
*/
+               override public function 
addMessageListener(handler:(e:MessageEvent)=>void):void
+               {
+                       COMPILE::JS
+                       {
+                               var idx:int = _listeners.indexOf(handler);
+                               if (idx== -1 ) _listeners.push(handler)
+                               if (expectedOrigin) activateMessaging(true);
+                       }
+               }
+               
+               public function 
removeMessageListener(handler:(e:MessageEvent)=>void):void
+               {
+                       COMPILE::JS
+                       {
+                               var idx:int = _listeners.indexOf(handler);
+                               if (idx!= -1 ) _listeners.splice(idx,1);
+                               if (!_listeners.length) {
+                                       activateMessaging(false);
+                               }
+                       }
+               }
+               COMPILE::JS
+               private function onStatus(e:Object):void{
+                       var localEventType:String = 
'iframe'+e.type.charAt(0).toUpperCase() + e.type.substr(1)
+                       dispatchEvent(new Event(localEventType))
+               }
+               
+               COMPILE::JS
+               private var expectedOrigin:String;
+               
+               COMPILE::JS
+               private var messagingActive:Boolean;
+               COMPILE::JS
+               private function activateMessaging(on:Boolean):void{
+                       if (on) {
+                               if (!messagingActive) {
+                                       window.addEventListener('message', 
filterMessaging);
+                                       messagingActive = true;
+                               }
+                       } else {
+                               if (messagingActive) {
+                                       window.removeEventListener('message', 
filterMessaging);
+                                       messagingActive = false;
+                               }
+                       }
+               }
+
+               private var _messageShapeChecker:(dataObject:Object)=>Boolean;
+
+               /**
+                * extra optional check to make sure the data received conforms 
to some expectations
+                * for example, check if a namespace field matches 
expectations, or data appears to be a certain 'shape'
+                * @param checker the function which should examine the data 
object and return true if it conforms to expectations
+                */
+               public function 
setMessageValidationCheck(checker:(dataObject:Object)=>Boolean):void{
+                               _messageShapeChecker = checker;
+               }
+                               
+               
+               COMPILE::JS
+               private function filterMessaging(e:MessageEvent):void{
+                       //opaque origins (cases including "" and "null" origin) 
will always be excluded unless  checkExpectedOrigin is false
+                       if (checkExpectedOrigin) {
+                               if (!e.origin || e.origin === "null")
+                                       return;
+                               if (e.origin != expectedOrigin) return;
+                       }
+                       
+                       if (!allowNestedFrames) {
+                               // default: only accept messages from the 
iframe itself
+                               if (e.source != contentWindow) return;
+                       } else {
+                               //accept messages also from nested iframes
+                               if (!isDescendantWindow(e.source as Window, 
contentWindow as Window))
+                                       return;
+                               
+                       }
+                       
+                       //extra optional check to make sure the data received 
conforms to some expectations
+                       if (_messageShapeChecker && 
!_messageShapeChecker(e.data)) return;
+                       
+                       for each(var _listener:(e:MessageEvent)=>void in 
_listeners) {
+                               _listener(e);
+                       }
+                       if (hasEventListener('message')) {
+                               //send the native MessageEvent via a ValueEvent
+                               dispatchEvent(new ValueEvent('message', e))
+                       }
+               }
+
+               COMPILE::JS
+               override public function addEventListener(type:String, 
handler:Function, opt_capture:Boolean = false, opt_handlerScope:Object = 
null):void
+               {
+                       if (type=='message' && expectedOrigin || 
!_checkExpectedOrigin) {
+                               activateMessaging(true)
+                       }
+                       super.addEventListener(type, handler, opt_capture, 
opt_handlerScope);
+               }
+               
+       }
+}
\ No newline at end of file

Reply via email to