From 4c8029c229f442f8718b978c125e4b15d3e83434 Mon Sep 17 00:00:00 2001 From: Yury Delendik Date: Sat, 29 Nov 2014 14:33:04 -0600 Subject: [PATCH] Fixes variable bound AVM1 text field. --- src/avm1/context.ts | 2 + src/avm1/flash.d.ts | 17 +++++--- src/avm1/interpreter.ts | 46 +++++++++++++++++---- src/avm1/lib/AVM1TextField.ts | 50 +++++++++++++++++------ test/swfs/avm1/text-bind/text-bind-1.fla | Bin 0 -> 4744 bytes test/swfs/avm1/text-bind/text-bind-1.swf | Bin 0 -> 745 bytes test/test_manifest.json | 5 +++ 7 files changed, 95 insertions(+), 25 deletions(-) create mode 100644 test/swfs/avm1/text-bind/text-bind-1.fla create mode 100644 test/swfs/avm1/text-bind/text-bind-1.swf diff --git a/src/avm1/context.ts b/src/avm1/context.ts index 51d2dbe8f..96267019c 100644 --- a/src/avm1/context.ts +++ b/src/avm1/context.ts @@ -34,7 +34,9 @@ module Shumway.AVM1 { } export interface IAVM1RuntimeUtils { + hasProperty(obj, name); getProperty(obj, name); + setProperty(obj, name, value); } export class AVM1Context { diff --git a/src/avm1/flash.d.ts b/src/avm1/flash.d.ts index ff82e56d1..4e11f5253 100644 --- a/src/avm1/flash.d.ts +++ b/src/avm1/flash.d.ts @@ -51,6 +51,7 @@ declare module Shumway.AVM2.AS.flash { _mouseDown: boolean; _children: DisplayObject []; _depth: number; + _symbol: DisplaySymbol; getBounds(obj: DisplayObject): flash.geom.Rectangle; play(); stop(); @@ -129,9 +130,7 @@ declare module Shumway.AVM2.AS.flash { constructor(); } class SimpleButton extends DisplayObject { - _symbol: { - data: {buttonActions: Shumway.Timeline.AVM1ButtonAction[]} - } + _symbol: ButtonSymbol; } class Stage extends DisplayObject { @@ -144,8 +143,12 @@ declare module Shumway.AVM2.AS.flash { stageHeight: number; } - class BitmapSymbol {} - class SpriteSymbol { + class DisplaySymbol {} + class BitmapSymbol extends DisplaySymbol {} + class ButtonSymbol extends DisplaySymbol { + data: { buttonActions: Shumway.Timeline.AVM1ButtonAction[] } + } + class SpriteSymbol extends DisplaySymbol { avm1Name: string; avm1SymbolClass; } @@ -276,10 +279,14 @@ declare module Shumway.AVM2.AS.flash { textWidth: number; textHeight: number; defaultTextFormat: TextFormat; + _symbol: TextSymbol; } class TextFormat extends ASNative { constructor(...args); } + class TextSymbol extends display.DisplaySymbol { + variableName: string; + } } module ui { class ContextMenu extends ASNative {} diff --git a/src/avm1/interpreter.ts b/src/avm1/interpreter.ts index df1dd9b82..aec7569f9 100644 --- a/src/avm1/interpreter.ts +++ b/src/avm1/interpreter.ts @@ -197,10 +197,27 @@ module Shumway.AVM1 { this.errorsIgnored = 0; this.deferScriptExecution = true; this.pendingScripts = []; + + var context = this; this.utils = { + hasProperty(obj, name) { + var result: boolean; + context.enterContext(function () { + result = as2HasProperty(obj, name); + }, obj); + return result; + }, getProperty(obj, name) { - var resolved = avm1ResolveProperty(obj, name, false); - return resolved ? resolved.link.asGetPublicProperty(name) : undefined; + var result; + context.enterContext(function () { + result = as2GetProperty(obj, name); + }, obj); + return result; + }, + setProperty(obj, name, value) { + context.enterContext(function () { + as2SetProperty(obj, name, value); + }, obj); } }; } @@ -554,6 +571,11 @@ module Shumway.AVM1 { // versions 6 and below ignore identifier case if (isNumeric(name) || as2GetCurrentSwfVersion() > 6) { + if (normalize) { + __resolvePropertyResult.link = obj; + __resolvePropertyResult.name = name; + return __resolvePropertyResult; + } return null; } @@ -596,18 +618,26 @@ module Shumway.AVM1 { return null; } + function as2ResolveProperty(obj, name: string, normalize: boolean): string { + var resolved = avm1ResolveProperty(obj, name, normalize); + return resolved ? resolved.name : null; + } + function as2HasProperty(obj, name: string): boolean { return !!avm1ResolveProperty(obj, name, false); } - function as2ResolveProperty(obj, name: string, normalize: boolean): string { - var result = avm1ResolveProperty(obj, name, normalize); - return result ? result.name : null; + function as2GetProperty(obj, name: string): any { + var resolved = avm1ResolveProperty(obj, name, false); + return resolved ? resolved.link.asGetPublicProperty(resolved.name) : undefined; } - function as2GetProperty(obj, name: string): any { - var result = avm1ResolveProperty(obj, name, false); - return result ? result.link.asGetPublicProperty(name) : undefined; + function as2SetProperty(obj, name: string, value: any): any { + var resolved = avm1ResolveProperty(obj, name, true); + if (!resolved) { + return; // probably obj is undefined or null + } + resolved.link.asSetPublicProperty(resolved.name, value); } function as2CastError(ex) { diff --git a/src/avm1/lib/AVM1TextField.ts b/src/avm1/lib/AVM1TextField.ts index e71f9adda..744cae0b7 100644 --- a/src/avm1/lib/AVM1TextField.ts +++ b/src/avm1/lib/AVM1TextField.ts @@ -39,13 +39,20 @@ module Shumway.AVM1.Lib { return wrapped; } - _variable: string; + private _variable: string; + private _exitFrameHandler: (event: flash.events.Event) => void; - public initAVM1Instance(as2Object: flash.text.TextField, context: AVM1Context) { - super.initAVM1Instance(as2Object, context); + + public initAVM1Instance(as3Object: flash.text.TextField, context: AVM1Context) { + super.initAVM1Instance(as3Object, context); this._variable = ''; + this._exitFrameHandler = null; initDefaultListeners(this); + + if (as3Object._symbol) { + this.variable = as3Object._symbol.variableName || ''; + } } public get _alpha() { @@ -351,14 +358,33 @@ module Shumway.AVM1.Lib { if (name === this._variable) { return; } - this._variable = name; var instance = this.as3Object; - var hasPath = name.indexOf('.') >= 0 || name.indexOf(':') >= 0; + if (this._exitFrameHandler && !name) { + instance.removeEventListener('exitFrame', this._exitFrameHandler); + this._exitFrameHandler = null; + } + this._variable = name; + if (!this._exitFrameHandler && name) { + this._exitFrameHandler = this._onAS3ObjectExitFrame.bind(this); + instance.addEventListener('exitFrame', this._exitFrameHandler); + } + } + + private _onAS3ObjectExitFrame() { + this._syncTextFieldValue(this.as3Object, this._variable); + } + + private _syncTextFieldValue(instance, name) { var clip; + var hasPath = name.indexOf('.') >= 0 || name.indexOf(':') >= 0; + var avm1ContextUtils = this.context.utils; if (hasPath) { var targetPath = name.split(/[.:\/]/g); name = targetPath.pop(); if (targetPath[0] == '_root' || targetPath[0] === '') { + if (instance.root === null) { + return; // text field is not part of the stage yet + } clip = getAVM1Object(instance.root, this.context); targetPath.shift(); if (targetPath[0] === '') { @@ -369,20 +395,20 @@ module Shumway.AVM1.Lib { } while (targetPath.length > 0) { var childName = targetPath.shift(); - clip = clip.asGetPublicProperty(childName) || clip[childName]; + clip = avm1ContextUtils.getProperty(clip, childName); if (!clip) { - throw new Error('Cannot find ' + childName + ' variable'); + return; // cannot find child clip } } } else { clip = getAVM1Object(instance._parent, this.context); } - if (!clip.asHasProperty(undefined, name, 0)) { - clip.asSetPublicProperty(name, instance.text); + // Sets default values as defined in SWF if this property was not found. + if (!avm1ContextUtils.hasProperty(clip, name)) { + avm1ContextUtils.setProperty(clip, name, instance.text); } - instance.addEventListener('advanceFrame', function () { - instance.text = '' + clip.asGetPublicProperty(name); - }); + + instance.text = '' + avm1ContextUtils.getProperty(clip, name); } public get _visible() { diff --git a/test/swfs/avm1/text-bind/text-bind-1.fla b/test/swfs/avm1/text-bind/text-bind-1.fla new file mode 100644 index 0000000000000000000000000000000000000000..6f89fec76ad277ea9ff8a9a81226733705d14329 GIT binary patch literal 4744 zcmZ`-1yq#V79K*nyGBA{=#WkahLG+S7`kR?7$kI%66sVL3?v09K|&Pi5b%;BodXOZ z2m+|V#-FMd7|2gMh``ahh-v7V%r>}*BO9KE9006VTVXCiF7or&f0KlbSUIAck zFem?DZzrg?x2Kx})ZfhuE*uDV6oNW>**gg#oIUmb0SA&g;t*nBmqLyae*YrOZz&i#$dslx@!Mb!zLj3`vh_D zNHQ}V0HBQQ#}*)ZIuI|10GJcpUkCy7+%~h)n-O6ME2z7e4|onD?f;dEC6h1bNMn z5s__NCf{iroYGU;wch0;;}7vNkKp#J(;jnC@MMt#EH`s2-uIhInJNet`{c8H z;262ivWowtMxj94INOIm%Dar>R%c!O-mTYrbi|@eaJok10 z2Y8Y)UH!^BO{c}qKJqnh#PuVipe3{YV?K=tR4gGKbEY_d(ykm&>EK=s#SXqv77mzr zN}y9PK2_7eD9984ae?QKuL1>S!Gj?RI^U z#F-CbHL}(*FA~?FXmbmQ>j)&rvQR1m5W)3!=TNUAYk2OERj%w^8Soi%yndZwq0%Hhqob=WlJ#w(!VBIIolFV(;s_%cD}8b4 ziSWasL|Pc!*HZ29zy_a&<7rKvybdu!;fZxD_}Z;^v$yV4zYZ_RD&`RG7iP9U+6!?*;wJqC=Z~nsctLeHmws2TTZ?K8XmondSgssVr+u22_FIMor zjaAymuN$;kzAqO|cQz+Kq38<$%Nx|^U3G|8F%(hb3CLz_Ft{P<1LOG<1#SgoSx|#3Y64;yzJzVG?u^ zq+}ZoVk2Q$DPP&Jku56UbPgKTkC^Y{r z+^>&i@&MtRj@rjNUY$h}_JgYH+BQ)HSTp0vZc~!qa7loS`dO@2s@x5po+NL=K}-2( zK&K9>x!1L=xh=lw85F$HBEx=yXb*vqNLOi=O!4TVKC%oiDln160=--+PLftrK~yP$ z1$LjuOFNGRkS2L<{CUgb7f`x)W%lFMS_V4KD2=g&lqSxsSwyM-gEl4Waz6fnq)+Gs)K41@C7x6z87M zd^Q>yf23D3EKW&(n5#fO$8DY5S<<*;PFE*FQVl2bcd%ohWwO@QiL7#2_jMh7R^`Sv z(J;`L<>z{D-08b)W@^avJg|=E1KxI)K>BG8gtgGC$hGHFq;=K8;PJo--z@&O^OoDM zD5FfKd3&V=^Mcav9ctp}+n3>r?v)=dnV}RII%+pj)w^EG9ye^)hRwu-J(BYMTXo+~ z87^}^o=6&936fWx{y^HlFmzz4T+KMbeq{M%I|N~tq33$yd$+q$WrpSAd>ta2qWgq; zcv81JUi6-%JoI06>j#PW=FTPd7hTBPV}zw~?KRUvdGWdZEkQIsb8=@UwUpZu(eC1^Q&L`Y{g~%nXxmh(7czC!C%;*U1FouSY76L{9xGrNWC)HSvwmm*c9 zS#e|DI~RqS)x+hgO0R{#ts)LO#OIMXR1e;{J&F^Ajew{Zo z?Kmm4^bDoP<3WRO-aM7|y*DS)sG08bkzs?Q2Hex`)4cN`Mhf-lbtVpdNVj-E zA@4enS8NiEoS(vXC2B5Q-l75B%q7sEKqIBGkhNDS6kL_4?*Z?_l^+8-%|-OTYA4pS z=9*kjKH`1gMQjM0^UL;;=ItDHzMXS!anKL}ekcC_Yg|t`YJ0?n7iHqq5zL9{f zs%7q<8AmoqTx&^gxT%c1rs-ERzh7R+s~)eXZ!vGKF;hVoo1FcGqfRhA#kTqbD8=Df z^}MndwRMyNPYO>PDc`~iE=__vWJ#wmm9l8|a1GZl8jktM_tefXs552lLwJ{XW`kAk zI3!LUJnWF0@s=t_LZ$L%q_CZsdvcu7;PqT({1w?pjzdaAUc{4f2X`{Inj|{sM;^)# z;Rh>1SO(Q;R%KW_zhDguck+(O7PJ*CZG8`vjfuUL?9`jw!*EbOg%F>}0e3Hr+sR&3 zt~*C(2E{rp+4CmJ)7MZJsX^KBH7lGT;F03)z|_nOH7?p|{CkB1(P~;Bis3PARyz#}e zl2j%&94FwaeF;A!e?XgI1#mFASeLbgV3hDX9^uo((Dn|Z*=WUtS2EZ3NLQ;_k~Zxs zq+vV>nkgH@?24)yuR>ScMdEJ2*EP{@U2!-xm7NRvBPL^o%l@&CaYae+S7N>zcH!~& zvLnYx>acmENNfoaqC4yvlUccG>1SeMv>t&^*Tqb}c4dq5luz0j*{FFJAYqJn3O;?e z3kmOmcpc7$_aIR>-ftW*)F5yUm?4z8@|&ELDw2yC+sT}nst6YUkFxyZooVuS)s~U$ zNAetxCX?9tsVJ>n)TyJXp8fipx9>#Dp9Oxl=LJn^Jm&n#+ZJ zl$G78>$qE78HqMfGn8Qu2s)tMh*P)V6}ztt(4q-U#z6pTy7P6?nvHv+=}EAFQ7ulO zh2}fO6W88*64Jlxw7KfTg1&k-xaF?Wg|3qcUrc6q?JZ{>A+xg5-=>`|U;UVse-uw2 zRL3C$vY@Jx9#(3YdB9Rurd@HuPW5yU3K_~B}TbxGVF=rgNZSN7M4Qv$t0?fOO|Bz57%$v??=hh6* z=AFSt;&?=Fb@(QZ?LgtM4Zo@q=oEex=QSXC09!VsGA5N%|E$AutNsFg68Lz6uW?4V zYL4Mk#B^2tMat5O*3+Z*kaL_cEeolNnyPYs)3yEH07NKN?vdh+eEN9B@J>-KqPyQ- zP2aLiqD(S|vO0n<$z-7^Lh{x2UtUL?g`&JK~J1gVp@q6AD{M=3z zRh}0S}4q}s3|l|vVpb+9lD}KIg*wGz2`WMqw!l&k~ zUB*;or{6$#Ta-Fw25Lh>cW)p(aB%7!s}E;PW|;Bfsp$jXgyyvdw6wVAErg#3aKAVi zM+ma>akyGLiN~#sT@xX=aO3xWsKDGGWsL@V)VmM+T z{DsVPO}jWeUE+yP0wVFM_lcyS77P9dWpA_ZB&yT3%>~4~^drzLwp|bI@v-EFJ20te zw7(BD95RO;w@dyr-}zC}V^W;|H7d+V$IIT$)9Lqg=ZF4J3Sgem0LX&TyOhiP2vfcb z8wJB4PTo$K!ruYv;OgXpdHorm7=Q-=0>}V#02$2uvb9xW(kU%0Y#PA-XB2*D{(S!A z7yd;3NhJK#2LO~X!#`<-KbHKZ{DZ{!F{i+E0T}tkF8p(&zoRbsg&*w_m4lJr*oGgl zpS8}Pe8YcnnC>k`{$L+|9`h&o&qC=JSPl2LHGeOg{xtDtI{VAS65hWix4#VhyMO#L sFovOU{>@wdd2fIE%P%|*=JNi3Jwj?5XVR_ zXei-Gk=bAb3(EE>4Xo`%1zQ2;J@|%Ek8m#WIDm~#8LIHG?*}d{ZB%OLvU7n&Z27ee zO)sjtt{T;Sy=WSmxuL0A+0?WJ?^rpuwu!(l7I>MtWNOBu`GR(0r>^gv+{ipv!Wz1i zHSMCI+vQR*ZSdxD=?HQiL;{lh8ke_JPbY@!m>y*z78Z`