From c6e1bfdcf25f63d02e4ae10a303f2a9ba99bfb93 Mon Sep 17 00:00:00 2001 From: roytam1 Date: Wed, 24 Apr 2024 23:53:16 +0800 Subject: [PATCH] =?UTF-8?q?import=20changes=20from=20`dev'=20branch=20of?= =?UTF-8?q?=20rmottola/Arctic-Fox:=20-=20Bug=201261578=20-=20Part=201.=20C?= =?UTF-8?q?orrect=20text=20color=20in=20selection=20range;=20r=3Djfkthame?= =?UTF-8?q?=20(6895ebe313)=20-=20Bug=201261578=20-=20Part=202.=20Correct?= =?UTF-8?q?=20text=20decoration=20color;=20r=3Djfkthame=20(348ba5a946)=20-?= =?UTF-8?q?=20Bug=201261578=20-=20Part=203.=20Correct=20MathML=20text=20co?= =?UTF-8?q?lor;=20r=3Djfkthame=20(99dfb99af4)=20-=20Bug=201261578=20-=20Pa?= =?UTF-8?q?rt=204.=20Correct=20text=20overflow=20color;=20r=3Djfkthame=20(?= =?UTF-8?q?96318becf6)=20-=20Bug=201261578=20-=20Part=205.=20web-platform-?= =?UTF-8?q?test=20reftest;=20r=3Djfkthame=20(781bba0317)=20-=20Bug=2011104?= =?UTF-8?q?60,=20part=201=20-=20Add=20operator=3D=20to=20nsCSSValuePair.?= =?UTF-8?q?=20r=3Ddholbert=20(7d836b786b)=20-=20Bug=201110460,=20part=202?= =?UTF-8?q?=20-=20Move=20AddPositions()=20further=20up=20StyleAnimationVal?= =?UTF-8?q?ue.cpp=20and=20add=20asserts.=20r=3Ddholbert=20(5f00295917)=20-?= =?UTF-8?q?=20Bug=201110460,=20part=203=20-=20Factor=20out=20a=20AddCSSVal?= =?UTF-8?q?uePair=20helper=20in=20StyleAnimationValue.cpp.=20r=3Ddholbert?= =?UTF-8?q?=20(9947835334)=20-=20Bug=201110460,=20part=204=20-=20Fix=20MOZ?= =?UTF-8?q?=5FASSERT=20bug=20in=20nsComputedDOMStyle.cpp.=20r=3Ddholbert?= =?UTF-8?q?=20(5403c0c377)=20-=20Bug=201110460,=20part=205=20-=20Factor=20?= =?UTF-8?q?out=20a=20nsStyleBasicShape::GetShapeTypeName=20method.=20r=3Dd?= =?UTF-8?q?holbert=20(d27623d42f)=20-=20Bug=201110460,=20part=206=20-=20Ma?= =?UTF-8?q?ke=20StyleAnimationValue=20support=20css::URLValue=20backed=20n?= =?UTF-8?q?sCSSValue=20objects.=20r=3Ddholbert=20(dc006d1cae)=20-=20Bug=20?= =?UTF-8?q?1110460,=20part=207=20-=20Factor=20out=20code=20for=20adding=20?= =?UTF-8?q?two=20nsCSSValuePairList=20lists.=20r=3Ddholbert=20(f27f780005)?= =?UTF-8?q?=20-=20Bug=201260655=20-=20Allow=20StyleAnimationValue::Uncompu?= =?UTF-8?q?teValue=20to=20produce=20values=20whose=20storage=20is=20indepe?= =?UTF-8?q?ndent=20of=20the=20passed-in=20computed=20value;=20r=3Dheycam?= =?UTF-8?q?=20(abcc77ede5)=20-=20Bug=201110460,=20part=208=20-=20Support?= =?UTF-8?q?=20CSS=20animation=20of=20clip-path=20basic=20shapes.=20r=3Ddho?= =?UTF-8?q?lbert=20(cfe5bf883b)=20-=20Bug=201238243=20-=20Reftests=20invol?= =?UTF-8?q?ving=20possibly-ignorable=20hangul=20choseong=20filler.=20r=3Dx?= =?UTF-8?q?idorn=20Bug=201238243=20followup=20-=20Mark=20test=201238243-2.?= =?UTF-8?q?html=20as=20random=20on=20OS=20X=2010.6,=20due=20to=20dependenc?= =?UTF-8?q?y=20on=20available=20fonts.=20Bug=201239564=20-=20Add=20reftest?= =?UTF-8?q?=20for=20mask=20layer=20composition.=20r=3Droc=20(bb3bf56d52)?= =?UTF-8?q?=20-=20Bug=201260543=20-=20Treat=20currentcolor=20as=20computed?= =?UTF-8?q?=20value=20which=20is=20not=20interpolatable=20with=20actual=20?= =?UTF-8?q?color=20for=20text-emphasis-color=20and=20-webkit-text-fill-col?= =?UTF-8?q?or.=20r=3Dbirtles=20(7b90f37e2f)=20-=20Bug=201260543=20followup?= =?UTF-8?q?=20-=20Fix=20the=20function=20name=20in=20test=5Ftransitions=5F?= =?UTF-8?q?per=5Fproperty.html.=20DONTBUILD=20(1362d30c6a)=20-=20Bug=20124?= =?UTF-8?q?8708=20-=20Part1:=20parse=20and=20compute=20-webkit-text-stroke?= =?UTF-8?q?=20property.=20r=3Dheycam=20(c77332031d)=20-=20Bug=201248708=20?= =?UTF-8?q?-=20Part2.1:=20use=20mfbt/TypedEnumBits.h=20for=20DrawMode.=20r?= =?UTF-8?q?=3Djwatt=20(55bfbdf5dd)=20-=20Bug=201266101,=20part=201=20-=20R?= =?UTF-8?q?emove=20various=20bits=20of=20dead=20code=20related=20to=20pain?= =?UTF-8?q?ting=20SVG=20text.=20r=3Dheycam=20(c5140a911c)=20-=20Bug=201266?= =?UTF-8?q?101,=20part=202=20-=20Remove=20most=20of=20the=20GLYPH=5FSTROKE?= =?UTF-8?q?/GLYPH=5FSTROKE=5FUNDERNEATH=20checking=20code.=20r=3Dheycam=20?= =?UTF-8?q?(6d8f17bc19)=20-=20Bug=201248708=20-=20Part2.2:=20render=20-web?= =?UTF-8?q?kit-text-stroke=20property.=20r=3Djfkthame=20(bccc308ddc)=20-?= =?UTF-8?q?=20Bug=201248708=20-=20Part3:=20add=20reftests.=20r=3Djfkthame?= =?UTF-8?q?=20(0fc0864749)=20-=20Bug=201248708=20followup:=20Use=20MOZ=5FA?= =?UTF-8?q?RRAY=5FLENGTH=20instead=20of=20ArrayLength=20for=20expression?= =?UTF-8?q?=20used=20at=20compile=20time,=20to=20fix=20MSVC=202013=20busta?= =?UTF-8?q?ge.=20(trivial/bustage-fix,=20no=20review)=20(25cecd7585)=20-?= =?UTF-8?q?=20Bug=201266341=20-=20Pass=20the=20right=20kind=20of=20enum=20?= =?UTF-8?q?constants=20for=20script=20codes.=20r=3Dmasayuki=20(723e784a37)?= =?UTF-8?q?=20-=20Bug=201245866=20-=20A=20better=20buffer=20size=20check?= =?UTF-8?q?=20in=20nsIDNService::IDNA2008ToUnicode,=20r=3Djfkthame=20(683d?= =?UTF-8?q?b7d93c)=20-=20Bug=201233610=20-=20IDN=20service=20should=20retu?= =?UTF-8?q?rn=20NS=5FERROR=5FMALFORMED=5FURI=20instead=20of=20NS=5FERROR?= =?UTF-8?q?=5FFAILURE=20r=3Dmcmanus=20(d70d4425ca)=20-=20Bug=201266391=20-?= =?UTF-8?q?=20Introduce=20an=20enum=20class=20mozilla::unicode::Script,=20?= =?UTF-8?q?and=20use=20this=20instead=20of=20bare=20integers=20to=20specif?= =?UTF-8?q?y=20script=20codes=20for=20better=20type=20checking.=20r=3Dmasa?= =?UTF-8?q?yuki=20(363a23049f)=20-=20fix=201389436=20backport=20(a8f96f6b3?= =?UTF-8?q?3)=20-=20Bug=201258228=20-=20create=20special=20child=20accessi?= =?UTF-8?q?bles=20in=20owner's=20constructor,=20part1,=20r=3Dmarcoz=20(2bb?= =?UTF-8?q?b819f76)=20-=20Bug=201258228=20-=20create=20special=20child=20a?= =?UTF-8?q?ccessibles=20in=20owner's=20constructor,=20part2,=20r=3Dmarcoz?= =?UTF-8?q?=20(5e20a7761a)=20-=20Bug=201258228=20-=20create=20special=20ch?= =?UTF-8?q?ild=20accessibles=20in=20owner's=20constructor,=20part3,=20r=3D?= =?UTF-8?q?marcoz=20(6574efb1e7)=20-=20Bug=201258228=20-=20create=20specia?= =?UTF-8?q?l=20child=20accessibles=20in=20owner's=20constructor,=20part4,?= =?UTF-8?q?=20r=3Dmarcoz=20(15420c5a12)=20-=20Bug=201188818=20-=20DataTran?= =?UTF-8?q?sfer=20must=20deal=20with=20nsIFile=20of=20directories,=20r=3Dj?= =?UTF-8?q?watt=20(715a857dd1)=20-=20Bug=201126295=20-=20Move=20TestAtoms.?= =?UTF-8?q?cpp=20to=20gtest=20and=20enable=20it;=20r=3Dfroydnj=20(9275ebeb?= =?UTF-8?q?3c)=20-=20Bug=201257128=20(part=201)=20-=20Remove=20nsIAtom.equ?= =?UTF-8?q?alsUTF8.=20r=3Dfroydnj.=20(17238a04e6)=20-=20Bug=201257128=20(p?= =?UTF-8?q?art=202)=20-=20Remove=20nsIAtomService.get{,Permanent}AtomUTF8.?= =?UTF-8?q?=20r=3Dfroydnj.=20(f4131f1024)=20-=20Bug=201257128=20(part=203)?= =?UTF-8?q?=20-=20Remove=20unused=20createAtom()=20function=20in=20a=20tes?= =?UTF-8?q?t.=20r=3Dtbsaunde.=20(e082c1bc17)=20-=20Bug=201257128=20(part?= =?UTF-8?q?=204)=20-=20Remove=20unused=20nsIAtomService=20references=20in?= =?UTF-8?q?=20toolkit/.=20r=3Dfroydnj.=20(5ddae71ae8)=20-=20Bug=201257402?= =?UTF-8?q?=20-=20Remove=20NS=5FNewPermanentAtom()=20and=20nsIAtomService.?= =?UTF-8?q?getPermanentAtom().=20r=3Dfroydnj,kmag.=20(4b9ad7894e)=20-=20Bu?= =?UTF-8?q?g=201145395=20-=20Pass=20Element&=20to=20nsHTMLEditor::RemoveEl?= =?UTF-8?q?ementIfNoStyleOrIdOrClass;=20r=3Dehsan=20(83080cb024)=20-=20Bug?= =?UTF-8?q?=201140105=20-=20Can't=20query=20for=20a=20specific=20font=20fa?= =?UTF-8?q?ce=20when=20the=20selection=20is=20collapsed.=20r=3Dehsan=20(b5?= =?UTF-8?q?34c04e7f)=20-=20Bug=201145395=20-=20Introduce=20an=20overload?= =?UTF-8?q?=20of=20nsHTMLEditor::RemoveStyleInside=20that=20takes=20nsICon?= =?UTF-8?q?tent&;=20r=3Dehsan=20(c5cb3bfb1d)=20-=20Bug=201170326=20-=20nsH?= =?UTF-8?q?TMLCSSUtils::ParseLength=20should=20check=20if=20the=20input=20?= =?UTF-8?q?is=20an=20empty=20string,=20r=3Dehsan=20(1950f67c9f)=20-=20Bug?= =?UTF-8?q?=201260871=20-=20Remove=20do=5FGetAtom()=20and=20rename=20NS=5F?= =?UTF-8?q?NewAtom()=20as=20NS=5FAtomize().=20r=3Derahm.=20(984bc596bf)=20?= =?UTF-8?q?-=20Bug=201254128=20-=20Make=20the=20Mozilla=20.lldbinit=20use?= =?UTF-8?q?=20the=20full=20path=20for=20topsrcdir.=20r=3Dsparky=20(20856a9?= =?UTF-8?q?4b4)=20-=20Bug=201259659=20-=20rename=20from=20InternalUIEvent.?= =?UTF-8?q?detail=20to=20InternalUIEvent.mDetail.=20r=3Dmasayuki=20(37165b?= =?UTF-8?q?d487)=20-=20Bug=201249664=20-=20Make=20stateful=20frames=20resp?= =?UTF-8?q?onsible=20for=20their=20own=20keys=20r=3Ddbaron=20(03a4365aa1)?= =?UTF-8?q?=20-=20Bug=201232338=20-=20part=201,=20Add=20better=20mouse=20s?= =?UTF-8?q?upport=20to=20InputData=20MouseInput=20r=3Dkats=20(8be234631e)?= =?UTF-8?q?=20-=20Bug=201259655=20-=20part=201=20Rename=20WidgetEventTime:?= =?UTF-8?q?:time=20to=20WidgetEventTime::mTime=20r=3Dmasayuki=20(cd679b408?= =?UTF-8?q?e)=20-=20Bug=201235908=20-=20measure=20total=20event=20wait=20a?= =?UTF-8?q?nd=20process=20time.=20r=3Dsmaug=20(4364242809)=20-=20Bug=20125?= =?UTF-8?q?9655=20-=20part=202=20Rename=20WidgetEventTime::timeStamp=20to?= =?UTF-8?q?=20WidgetEventTime::mTimeStamp=20r=3Dmasayuki=20(94a507904d)=20?= =?UTF-8?q?-=20Bug=201263785=20-=20Kill=20off=20the=20deprecated=20nsINode?= =?UTF-8?q?::GetCurrentDoc.=20r=3Dbaku=20(670e175017)=20-=20Bug=201245748?= =?UTF-8?q?=20-=20Remove=20no-longer-needed=20code=20for=20directly=20sett?= =?UTF-8?q?ing=20up=20properties=20in=20KeyframeEffect(ReadOnly)=20constru?= =?UTF-8?q?ctor;=20r=3Dheycam=20(166ed7697c)=20-=20Bug=201259285=20-=20Par?= =?UTF-8?q?t1=20-=20Move=20CSS/Web=20Animations-specific=20visibility=20ha?= =?UTF-8?q?ndling.=20r=3Dbirtles=20(13a4ac2b94)=20-=20Bug=201259285=20-=20?= =?UTF-8?q?Part2=20-=20Add=20web-platform=20test=20for=20visibility=20hand?= =?UTF-8?q?ling.=20r=3Dbirtles=20(b7cee58cf7)=20-=20Bug=201260655=20-=20Ad?= =?UTF-8?q?d=20KeyframeEffectReadOnly::SetFrames;=20r=3Dheycam=20(7da27828?= =?UTF-8?q?da)=20-=20Bug=201260655=20-=20Wrap=20lines=20in=20keyframe-effe?= =?UTF-8?q?ct/constructor.html=20to=2080=20chars;=20r=3Dwhitespace-only=20?= =?UTF-8?q?(32b6412db2)=20-=20Bug=201260655=20-=20Update=20keyframe-effect?= =?UTF-8?q?/constructor.html=20to=20no=20longer=20refer=20to=20PropertyInd?= =?UTF-8?q?exedKeyframes=20or=20Keyframe;=20r=3Dheycam=20(dbe0acf873)=20-?= =?UTF-8?q?=20Bug=201260655=20-=20Return=20the=20stored=20Keyframe=20objec?= =?UTF-8?q?ts=20from=20GetFrames,=20when=20available=20;=20r=3Dheycam=20(5?= =?UTF-8?q?b8cbc1773)=20-=20Bug=201245748=20-=20Add=20a=20Move=20construct?= =?UTF-8?q?or=20to=20Keyframe;=20r=3Dheycam=20(eeabc9396a)=20-=20Bug=20126?= =?UTF-8?q?0655=20-=20Add=20an=20assignment=20operator=20to=20Keyframe=20t?= =?UTF-8?q?hat=20takes=20an=20rvalue=20reference;=20r=3Dheycam=20(e81f1f6d?= =?UTF-8?q?7c)=20-=20Bug=201260655=20-=20Add=20methods=20to=20CSSAnimation?= =?UTF-8?q?Builder=20to=20build=20a=20set=20of=20Keyframe=20objects;=20r?= =?UTF-8?q?=3Dheycam=20(93e2a7b3aa)=20-=20Bug=201260655=20-=20Use=20CSSAni?= =?UTF-8?q?mationBuilder::BuildAnimationFrames=20to=20set=20up=20CSS=20ani?= =?UTF-8?q?mations=20using=20Keyframe=20objects;=20r=3Dheycam=20(4df3ad20c?= =?UTF-8?q?d)=20-=20Bug=201260655=20-=20Drop=20some=20no-longer-needed=20c?= =?UTF-8?q?ode=20for=20setting=20up=20CSS=20animations=20using=20Animation?= =?UTF-8?q?Property=20objects;=20r=3Dheycam=20(e1d8e020aa)=20-=20Bug=20126?= =?UTF-8?q?0655=20-=20Add=20a=20copy=20constructor=20and=20copy=20assignme?= =?UTF-8?q?nt=20operator=20to=20Keyframe;=20r=3Dheycam=20(d9a1ca5d24)=20-?= =?UTF-8?q?=20Bug=201249564=20-=20Part=201:=20Assign=20the=20parent=20obje?= =?UTF-8?q?ct=20of=20AnimationEffectTiming(ReadOnly).=20r=3Dbirtles,bz=20(?= =?UTF-8?q?298bdcf084)=20-=20Bug=201249564=20-=20Part=202:=20Cycle=20colle?= =?UTF-8?q?ct=20AnimationEffectTimingReadOnly.=20r=3Dbirtles=20(25d959d057?= =?UTF-8?q?)=20-=20Bug=201263063=20-=20Part=201:=20Remove=20unnecessary=20?= =?UTF-8?q?clamping=20of=20TimingParams::mIterationStart,=20since=20it's?= =?UTF-8?q?=20guaranteed=20to=20be=20nonnegative.=20r=3Ddholbert=20(353627?= =?UTF-8?q?49d7)=20-=20Bug=201263063=20-=20Part=202:=20Add=20an=20assertio?= =?UTF-8?q?n=20to=20ensure=20that=20iteration=20count=20is=20nonnegative?= =?UTF-8?q?=20and=20finite.=20r=3Ddholbert=20(7df13df139)=20-=20Bug=201263?= =?UTF-8?q?063=20-=20Part=203:=20Change=20logic=20in=20KeyFrameEffect=20to?= =?UTF-8?q?=20assume=20that=20TimingParams'=20iteration-count=20has=20alre?= =?UTF-8?q?ady=20been=20validated=20as=20nonnegative=20&=20finite.=20r=3Dd?= =?UTF-8?q?holbert=20(6dd3f0d984)=20-=20Bug=201263063=20-=20Part=204:=20Mo?= =?UTF-8?q?ve=20ActiveDuration()=20into=20TimingParams.=20r=3Ddholbert=20(?= =?UTF-8?q?6760bdeaa7)=20-=20Bug=201263063=20-=20Part=205:=20Introduce=20T?= =?UTF-8?q?imingParams::EndTime().=20r=3Ddholbert=20(af9a4ddd95)=20-=20Bug?= =?UTF-8?q?=201260983=20-=20Allow=20creating=20animations=20with=20a=20tar?= =?UTF-8?q?get=20element=20not=20bound=20to=20a=20document;=20r=3Dheycam?= =?UTF-8?q?=20(7d71619cfb)=20-=20Bug=201244591=20-=20Part=201:=20Implement?= =?UTF-8?q?=20KeyframeEffect.setFrames=20r=3Dbirtles,smaug=20(e090fa68b6)?= =?UTF-8?q?=20-=20Bug=201244591=20-=20Part=202:=20Extract=20useful=20keyfr?= =?UTF-8?q?ames=20tests=20to=20a=20new=20file=20r=3Dbirtles=20(780797fe53)?= =?UTF-8?q?=20-=20Bug=201244591=20-=20Part=203:=20Add=20web-platform=20tes?= =?UTF-8?q?ts=20for=20KeyframeEffect.setFrames=20r=3Dbirtles=20(997d26427b?= =?UTF-8?q?)=20-=20Bug=201259878=20-=20remove=20unnecessary=20isSome().=20?= =?UTF-8?q?r=3Dbirtles=20(cb21268a0a)=20-=20Bug=201248532=20-=20Part=201:?= =?UTF-8?q?=20steps-start=20does=20not=20produce=20correct=20value=20at=20?= =?UTF-8?q?the=20beginning=20of=20the=20interval.=20r=3Dbirtles=20(9131923?= =?UTF-8?q?815)=20-=20Bug=201248532=20-=20Part=202:=20Add=20tests=20for=20?= =?UTF-8?q?step-start.=20r=3Dbirtles=20(04ae44375d)=20-=20Bug=201248532=20?= =?UTF-8?q?-=20Part=203:=20add=20a=20test=20for=20step-end=20with=20iterat?= =?UTF-8?q?ionStart.=20r=3Dbirtles=20(769827746b)=20-=20Bug=201266257=20-?= =?UTF-8?q?=20Revise=20timing=20model=20calculations=20to=20use=20fraction?= =?UTF-8?q?-based=20approach;=20r=3Dhiro=20(c1de969ef0)=20-=20Bug=20126057?= =?UTF-8?q?2=20-=20Replace=20AnimValuesStyleRule::AddEmptyValue=20with=20a?= =?UTF-8?q?n=20overload=20of=20AddValue=20that=20takes=20an=20rvalue=20ref?= =?UTF-8?q?erence;=20r=3Dheycam=20(08e01a4cdc)=20-=20Bug=201260572=20-=20U?= =?UTF-8?q?se=2050%=20switch=20behavior=20if=20StyleAnimationValue::Interp?= =?UTF-8?q?olate=20fails;=20r=3Dheycam=20(e67754e770)=20-=20Bug=201260976?= =?UTF-8?q?=20-=20Remove=20the=20old=20AnimationProperty-based=20GetFrames?= =?UTF-8?q?;=20r=3Dheycam=20(efc88117e6)=20-=20Bug=201259248.=20Add=20an?= =?UTF-8?q?=20ArrayView=20class.=20r=3Dbotond=20(5bb9e22500)=20-=20Bug=201?= =?UTF-8?q?265648=20-=20Remove=20the=20global=20nsTextFrameTextRunCache,?= =?UTF-8?q?=20as=20it=20no=20longer=20serves=20any=20useful=20purpose.=20r?= =?UTF-8?q?=3Dmats=20(3ffbf42634)=20-=20Bug=201238243=20-=20Don't=20filter?= =?UTF-8?q?=20out=20Hangul=20jamo=20fillers=20as=20'ignorable',=20because?= =?UTF-8?q?=20the=20font=20may=20require=20them=20to=20provide=20advance?= =?UTF-8?q?=20width.=20r=3Dxidorn=20(9fc2f82853)=20-=20Bug=201265648=20fol?= =?UTF-8?q?lowup=20-=20Use=20an=20array=20of=20UniquePtr<>=20for=20mTextRu?= =?UTF-8?q?nsToDelete.=20r=3Dmats=20(8745a7566e)=20-=20Bug=201263956=20-?= =?UTF-8?q?=20Adopt=20the=20new=20harfbuzz=20API=20for=20char-to-glyph=20m?= =?UTF-8?q?apping=20functions.=20r=3Djrmuizel=20(1f061d7b0f)=20-=20Bug=201?= =?UTF-8?q?265568=20-=20Don't=20require=20component=20alpha=20layers=20for?= =?UTF-8?q?=20text=20with=20-moz-osx-font-smoothing:grayscale.=20r=3Djfkth?= =?UTF-8?q?ame,mattwoodrow=20(f819591273)=20-=20Bug=201199547=20-=20Backou?= =?UTF-8?q?t=20workaround=20from=20bug=20722676=20as=20it=20causes=20issue?= =?UTF-8?q?s=20on=20at=20least=20OSX=2010.10=20and=2010.11.=20r=3Dmstange?= =?UTF-8?q?=20(c96fb3a69d)=20-=20Bug=201265953,=20part=201=20-=20Convert?= =?UTF-8?q?=20most=20of=20the=20cocoa=20widget=20code=20from=20nsAutoPtr?= =?UTF-8?q?=20to=20UniquePtr.=20r=3Dmstange=20(74aa93eaf9)=20-=20Bug=20124?= =?UTF-8?q?9915=20-=20Add=20ability=20to=20synthesize=20native=20touch=20e?= =?UTF-8?q?vents=20on=20GTK=20for=20mochitests.=20r=3Dkarlt=20(b734a366fb)?= =?UTF-8?q?=20-=20Bug=201259670=20Rename=20WidgetPluginEvent::retargetToFo?= =?UTF-8?q?cusedDocument=20to=20WidgetPluginEvent::mRetargetToFocusedDocum?= =?UTF-8?q?ent=20r=3Dsmaug=20(24c12b3bb4)=20-=20Bug=201249915=20-=20Fix=20?= =?UTF-8?q?synthesized=20touch=20injection=20code=20on=20Windows=20to=20no?= =?UTF-8?q?t=20apply=20the=20scale=20factor=20twice.=20r=3Djimm=20(b590300?= =?UTF-8?q?aa3)=20-=20Bug=201250505=20-=20Convert=20SynthesizeNativeTouchP?= =?UTF-8?q?oint=20and=20SynthesizeNativeTouchTap=20to=20take=20a=20LayoutD?= =?UTF-8?q?eviceIntPoint=20instead=20of=20a=20ScreenPoint.=20r=3Dnjn=20(8d?= =?UTF-8?q?dd066519)=20-=20Bug=201256731=20-=20patch=201=20-=20Don't=20app?= =?UTF-8?q?ly=20theme-dpi=20scaling=20to=20metrics=20of=20window=20border?= =?UTF-8?q?=20elements,=20because=20Windows=20doesn't=20respect=20per-moni?= =?UTF-8?q?tor=20dpi=20scaling=20when=20it=20draws=20them.=20r=3Demk=20(58?= =?UTF-8?q?2676e33e)=20-=20Bug=201247935=20-=20Part=201:=20double-buffer?= =?UTF-8?q?=20nsShmImage.=20r=3Djrmuizel=20(2392a1e916)=20-=20Bug=20124793?= =?UTF-8?q?5=20-=20Part=202:=20use=20shared=20pixmaps=20with=20XShm=20for?= =?UTF-8?q?=20nsShmImage.=20r=3Djrmuizel=20(b3dd105fcc)=20-=20Bug=20126595?= =?UTF-8?q?3,=20part=202=20-=20Convert=20much=20of=20the=20rest=20of=20the?= =?UTF-8?q?=20widget=20code=20from=20nsAutoPtr=20to=20UniquePtr.=20r=3Dmst?= =?UTF-8?q?ange=20(e0c1c49ef8)=20-=20Bug=201265953,=20part=203=20-=20Conve?= =?UTF-8?q?rt=20more=20widget=20code=20from=20nsAutoPtr=20to=20UniquePtr.?= =?UTF-8?q?=20r=3Dmstange=20(17e1be57d2)=20-=20Bug=201265953,=20part=204?= =?UTF-8?q?=20-=20Convert=20nsMenuX::LoadSubMenu=20and=20nsMenuX::AddMenu?= =?UTF-8?q?=20to=20UniquePtr.=20r=3Dmstange=20(f4ea50b802)=20-=20Bug=20125?= =?UTF-8?q?9664=20part.1=20Rename=20WidgetWheelEvent::deltaX=20to=20Widget?= =?UTF-8?q?WheelEvent::mDeltaX=20r=3Dsmaug=20(8c7ef861eb)=20-=20Bug=201259?= =?UTF-8?q?664=20part.2=20Rename=20WidgetWheelEvent::deltaY=20to=20WidgetW?= =?UTF-8?q?heelEvent::mDeltaY=20r=3Dsmaug=20(03529452fb)=20-=20Bug=2012596?= =?UTF-8?q?64=20part.3=20Rename=20WidgetWheelEvent::deltaZ=20to=20WidgetWh?= =?UTF-8?q?eelEvent::mDeltaZ=20r=3Dsmaug=20(0d664b2900)=20-=20Bug=20125966?= =?UTF-8?q?4=20part.4=20Rename=20WidgetWheelEvent::deltaMode=20to=20Widget?= =?UTF-8?q?WheelEvent::mDeltaMode=20r=3Dsmaug=20(dc8283ee70)=20-=20Bug=201?= =?UTF-8?q?253041=20-=20Don't=20apply=20user=20wheel=20prefs=20more=20than?= =?UTF-8?q?=20once.=20r=3Dsmaug=20(2386a9da14)=20-=20Bug=201259664=20part.?= =?UTF-8?q?5=20Rename=20WidgetWheelEvent::customizedByUserPrefs=20to=20Wid?= =?UTF-8?q?getWheelEvent::mCustomizedByUserPrefs=20r=3Dsmaug=20(3b16757c1e?= =?UTF-8?q?)=20-=20Bug=201258820=20-=20making=20root=20scrollable=20elemen?= =?UTF-8?q?t=20not=20scroll=20if=20mouse=20wheel=20is=20used=20on=20xul=20?= =?UTF-8?q?dropdown=20r=3Dmasayuki=20(58ab750981)=20-=20Bug=201250050=20Ad?= =?UTF-8?q?d=20a=20pref=20to=20disable=20supporting=20mouse=20wheel=20of?= =?UTF-8?q?=20windowless=20plugins=20on=20Windows=20r=3Dsmaug=20(1560cfcb3?= =?UTF-8?q?5)=20-=20Bug=201256162,=20use=20last=20drag=20target=20for=20dr?= =?UTF-8?q?agexit=20event=20when=20comparing=20to=20a=20remote=20browser,?= =?UTF-8?q?=20r=3Dsmaug=20(2d03394037)=20-=20Bug=201110030=20-=20part1=20-?= =?UTF-8?q?=20add=20flags:IsSynthesizedByTIP=20and=20InputMethodAppState?= =?UTF-8?q?=20in=20WidgetKeyboardEvent.=20r=3Dmasayuki=20(c8f7802ee0)=20-?= =?UTF-8?q?=20Bug=201110030=20-=20part2=20-=20Prevent=20PostHandleKeyboard?= =?UTF-8?q?Event=20if=20it's=20handling=20by=20IME.=20r=3Dmasayuki=20(29fa?= =?UTF-8?q?b0d4fe)=20-=20Bug=201110030=20-=20part3=20-=20Interface=20betwe?= =?UTF-8?q?en=20PresShell=20and=20HardwareKeyHandler.=20r=3Dmasayuki,=20r?= =?UTF-8?q?=3Dsmaug=20(cb152d6b4b)=20-=20Bug=201110030=20-=20part4=20-=20H?= =?UTF-8?q?ardwareKeyHandler=20component.=20r=3Dmasayuki,=20r=3Dsmaug=20(9?= =?UTF-8?q?e372e8f19)=20-=20Bug=201110030=20-=20part5=20-=20Expose=20Keybo?= =?UTF-8?q?ardEventInit=20dictionary.=20r=3Dmasayuki,=20r=3Dsmaug=20(3c38b?= =?UTF-8?q?f2f37)=20-=20Bug=201110030=20-=20part6=20-=20Expose=20DefaultPr?= =?UTF-8?q?eventedBy*.=20r=3Dmasayuki,=20r=3Dsmaug=20(5c94c067be)=20-=20Bu?= =?UTF-8?q?g=201110030=20-=20part7=20-=20Interface=20between=20HardwareKey?= =?UTF-8?q?Handler=20and=20Input=20Method=20App.=20r=3Dmasayuki,=20r=3Dsma?= =?UTF-8?q?ug=20(f33b7f5ad5)=20-=20Bug=201244546=20part=201=20-=20Apply=20?= =?UTF-8?q?proper=20unit=20conversion=20for=20SynthesizeNativeMouseEvent.?= =?UTF-8?q?=20r=3Dkarlt=20(8596ff8068)=20-=20Bug=201244546=20part=202=20-?= =?UTF-8?q?=20Align=20the=20center=20point=20for=20pointerlock=20to=20meet?= =?UTF-8?q?=20widget's=20requirement.=20r=3Dsmaug=20(c447b8c770)=20-=20Bug?= =?UTF-8?q?=201259664=20part.6=20Rename=20WidgetWheelEvent::mayHaveMomentu?= =?UTF-8?q?m=20to=20WidgetWheelEvent::mMayHaveMomentum=20r=3Dsmaug=20(ef95?= =?UTF-8?q?784e26)=20-=20Bug=201259664=20part.7=20Rename=20WidgetWheelEven?= =?UTF-8?q?t::isMomentum=20to=20WidgetWheelEvent::mIsMomentum=20r=3Dsmaug?= =?UTF-8?q?=20(983c8dc9e3)=20-=20Bug=201259664=20part.8=20Rename=20WidgetW?= =?UTF-8?q?heelEvent::scrollType=20to=20WidgetWheelEvent::mScrollType=20r?= =?UTF-8?q?=3Dsmaug=20(c6731aee2d)=20-=20Bug=201259664=20part.9=20Rename?= =?UTF-8?q?=20WidgetWheelEvent::lineOrPageDeltaX=20to=20WidgetWheelEvent::?= =?UTF-8?q?mLineOrPageDeltaX=20r=3Dsmaug=20(9c6d3d7f6b)=20-=20Bug=20125966?= =?UTF-8?q?4=20part.10=20Rename=20WidgetWheelEvent::lineOrPageDeltaY=20to?= =?UTF-8?q?=20WidgetWheelEvent::mLineOrPageDeltaY=20r=3Dsmaug=20(8a0d549ee?= =?UTF-8?q?e)=20-=20Bug=201259664=20part.11=20Rename=20WidgetWheelEvent::o?= =?UTF-8?q?verflowDeltaX=20to=20WidgetWheelEvent::mOverflowDeltaX=20r=3Dsm?= =?UTF-8?q?aug=20(6200509b12)=20-=20Bug=201259664=20part.12=20Rename=20Wid?= =?UTF-8?q?getWheelEvent::overflowDeltaY=20to=20Widget=C3=82=C2=A6WheelEve?= =?UTF-8?q?nt::mOverflowDeltaY=20r=3Dsmaug=20(3827fc7cef)=20-=20Bug=201259?= =?UTF-8?q?664=20part.13=20Reorder=20the=20definition=20of=20members=20of?= =?UTF-8?q?=20WidgetWheelEvent=20r=3Dsmaug=20(ea69305e49)=20-=20Bug=201243?= =?UTF-8?q?628=20-=20Allow=20for=20presshell=20to=20have=20been=20destroye?= =?UTF-8?q?d=20and=20disconnected=20from=20prescontext=20during=20event=20?= =?UTF-8?q?dispatch.=20r=3Ddholbert=20(b98e7ed1a2)=20-=20Bug=201256952,=20?= =?UTF-8?q?send=20a=20dragexit=20at=20remote=20process=20when=20leaving=20?= =?UTF-8?q?the=20remote=20frame,=20r=3Dsmaug=20(1037c6a425)=20-=20Bug=2012?= =?UTF-8?q?61818=20-=20don't=20pass=20nsAutoCStrings=20into=20nsBaseWidget?= =?UTF-8?q?=20debugging=20methods;=20r=3Dkarlt=20(36b2b66795)=20-=20Bug=20?= =?UTF-8?q?1250560=20-=20Fix=20crash=20in=20HandleTouchEvent.=20r=3Droc=20?= =?UTF-8?q?(9586890edf)=20-=20Bug=201121468=20-=20Go=20to=20NoActionState?= =?UTF-8?q?=20after=20receiving=20release=20on=20LongTapState.=20r=3Droc?= =?UTF-8?q?=20(4e05f9ad70)=20-=20Bug=201248847=20-=20Assert=20AccessibleCa?= =?UTF-8?q?retEventHub=20mRefCnt=20>=201=20in=20all=20its=20entry=20points?= =?UTF-8?q?.=20r=3Dmats=20(89ced3c3e2)=20-=20Bug=201251915=20-=20Correct?= =?UTF-8?q?=20AccessibleCaretEventHub=20coding=20style=20by=20clang-format?= =?UTF-8?q?.=20r=3Droc=20(12d5b507d7)=20-=20Bug=201259668=20Rename=20Widge?= =?UTF-8?q?tTouchEvent::touches=20to=20WidgetTouchEvent::mTouches=20r=3Dsm?= =?UTF-8?q?aug=20(9c53824e17)=20-=20Bug=201259657=20Rename=20WidgetGUIEven?= =?UTF-8?q?t::widget=20to=20WidgetGUIEvent::mWidget=20r=3Dsmaug=20(8707f57?= =?UTF-8?q?a28)=20-=20Bug=201246477=20-=20Fix=20carets=20not=20updated=20b?= =?UTF-8?q?y=20scroll=20events=20in=20LongTapState.=20r=3Droc=20(ba6f12001?= =?UTF-8?q?c)=20-=20Bug=201259662=20part.1=20Rename=20WidgetDragEvent::dat?= =?UTF-8?q?aTransfer=20to=20WidgetDragEvent::mDataTransfer=20r=3Dsmaug=20(?= =?UTF-8?q?79ffebe3ae)=20-=20Bug=201259662=20part.2=20Rename=20WidgetDragE?= =?UTF-8?q?vent::userCancelled=20to=20WidgetDragEvent::mUserCancelled=20r?= =?UTF-8?q?=3Dsmaug=20(4b7ee565e7)=20-=20Bug=201241008=20-=20Add=20prefere?= =?UTF-8?q?nce=20to=20show=20or=20hide=20selection=20bars.=20r=3Dmtseng=20?= =?UTF-8?q?(aefd4c430d)=20-=20Bug=201242349=20-=20Provide=20unique=20Acces?= =?UTF-8?q?sibleCaret=20pref=20for=20JS=20selection=20changes,=20r=3DTYLin?= =?UTF-8?q?=20(e964cb16cf)=20-=20Bug=201240917=20-=20Do=20not=20show=20car?= =?UTF-8?q?et=20in=20empty=20input=20on=20Fennec.=20r=3Droc=20(d73c6bd94d)?= =?UTF-8?q?=20-=20Bug=201121468=20-=20Show=20carets=20when=20long-pressing?= =?UTF-8?q?=20on=20selection=20highlight.=20r=3Droc=20(bf134067de)=20-=20B?= =?UTF-8?q?ug=201246064=20-=20Support=20long=20press=20to=20show=20Accessi?= =?UTF-8?q?bleCaret=20on=20empty=20input=20for=20Fennec.=20r=3Droc=20(9079?= =?UTF-8?q?1443cb)=20-=20Bug=201246918=20-=20Fix=20carets=20missing=20afte?= =?UTF-8?q?r=20scrolling=20down=20in=20selection=20mode=20on=20Fennec.=20r?= =?UTF-8?q?=3Droc=20(bc0915ad70)=20-=20Bug=201246918=20-=20Handle=20PresSh?= =?UTF-8?q?ell=20gone=20after=20FlushLayout().=20r=3Droc=20(b2f18c9a03)=20?= =?UTF-8?q?-=20Bug=201251915=20-=20Ignore=20handling=20eTouchCancel=20even?= =?UTF-8?q?ts.=20r=3Droc=20(a04c3ad8eb)=20-=20Bug=201121468=20-=20Use=20au?= =?UTF-8?q?to*=20to=20explicit=20declare=20'self'=20as=20a=20pointer.=20r?= =?UTF-8?q?=3Droc=20(d83a6020e3)=20-=20Bug=201251346=20-=20Fennec=20should?= =?UTF-8?q?=20not=20generate=20touch=20events=20from=20mouse=20events.=20r?= =?UTF-8?q?=3Dkats=20(d8077748fc)=20-=20Bug=201259656=20part.1=20Rename=20?= =?UTF-8?q?WidgetEvent::refPoint=20to=20WidgetEvent::mRefPoint=20r=3Dsmaug?= =?UTF-8?q?=20(fa66825fc9)=20-=20Bug=201259656=20part.2=20Rename=20WidgetE?= =?UTF-8?q?vent::lastRefPoint=20to=20WidgetEvent::mLastRefPoint=20r=3Dsmau?= =?UTF-8?q?g=20(c964d62185)=20-=20Bug=201259656=20part.3=20Rename=20Widget?= =?UTF-8?q?Event::userType=20to=20WidgetEvent::mSpecifiedEventType=20r=3Ds?= =?UTF-8?q?maug=20(218ae50355)=20-=20Bug=201259656=20part.4=20Rename=20Wid?= =?UTF-8?q?getEvent::typeString=20to=20WidgetEvent::mSpecifiedEventTypeStr?= =?UTF-8?q?ing=20r=3Dsmaug=20(0192c890c9)=20-=20Bug=201259656=20part.5=20R?= =?UTF-8?q?ename=20WidgetEvent::target=20to=20WidgetEvent::mTarget=20r=3Ds?= =?UTF-8?q?maug=20(031356f40d)=20-=20Bug=201259656=20part.6=20Rename=20Wid?= =?UTF-8?q?getEvent::currentTarget=20to=20WidgetEvent::mCurrentTarget=20r?= =?UTF-8?q?=3Dsmaug=20(451810f6d3)=20-=20Bug=201259656=20part.7=20Rename?= =?UTF-8?q?=20WidgetEvent::originalTarget=20to=20WidgetEvent::mOriginalTar?= =?UTF-8?q?get=20r=3Dsmaug=20(643379c9cb)=20-=20Bug=201259673=20rename=20f?= =?UTF-8?q?rom=20InternalClipboardEvent.clipboardData=20to=20InternalClipb?= =?UTF-8?q?oardEvent.mClipboardData.=20r=3Dmasayuki=20(5ad3c180fe)=20-=20B?= =?UTF-8?q?ug=201259674=20Part=201=20rename=20InternalFocusEvent::fromRais?= =?UTF-8?q?e=20to=20InternalFocusEvent::mFrameRaise=20r=3Dmasayuki=20(400f?= =?UTF-8?q?1ba6e9)=20-=20Bug=201259674=20Part=202=20rename=20InternalFocus?= =?UTF-8?q?Event::isRefocus=20to=20InternalFocusEvent::mIsRefocus=20r=3Dma?= =?UTF-8?q?sayuki=20(93b5799c31)=20-=20Bug=201259674=20Part=203=20rename?= =?UTF-8?q?=20InternalFocusEvent::relatedTarget=20to=20InternalFocusEvent:?= =?UTF-8?q?:mRelatedTarget=20r=3Dmasayuki=20(2382b8de82)=20-=20Bug=2012565?= =?UTF-8?q?89=20part.7=20Add=20PropagationStopped()=20to=20WidgetEvent=20r?= =?UTF-8?q?=3Dsmaug=20(09325f188d)=20-=20Bug=201259663=20-=20Clean=20up=20?= =?UTF-8?q?WidgetMouseScrollEvent.=20r=3Dmasayuki=20(48e1389e22)=20-=20Bug?= =?UTF-8?q?=201263782=20-=20Kill=20off=20the=20deprecated=20nsINode::IsInD?= =?UTF-8?q?oc().=20r=3Dbaku=20(ae80ec21f7)=20-=20Bug=201223751=20-=20Assum?= =?UTF-8?q?e=20all=20frames=20are=20visible=20in=20subdocuments=20of=20a?= =?UTF-8?q?=20top=20level=20content=20document=20that's=20assuming=20all?= =?UTF-8?q?=20frames=20are=20visible.=20r=3Dtn=20(3b4f99b2fc)=20-=20Bug=20?= =?UTF-8?q?1223747=20-=20Don't=20assume=20all=20frames=20are=20visible=20i?= =?UTF-8?q?n=20XUL=20documents.=20r=3Dtn=20(7f26104ff8)=20-=20Bug=20118026?= =?UTF-8?q?7=20-=20Switch=20Fennec=20over=20to=20using=20the=20MobileViewp?= =?UTF-8?q?ortManager=20for=20computing=20the=20CSS=20viewport.=20r=3Dsnor?= =?UTF-8?q?p=20(b85a3a5fb7)=20-=20put=20back=20some=20SPS=20stuff=20(96041?= =?UTF-8?q?4c383)=20-=20Bug=201151152=20-=20Change=20behavior=20when=20poi?= =?UTF-8?q?nter=20is=20dragged=20out=20of=20the=20document.=20r=3Dsmaug=20?= =?UTF-8?q?(64779386f0)=20-=20Bug=201263787=20-=20Kill=20off=20the=20depre?= =?UTF-8?q?cated=20nsINode::GetCrossShadowCurrentDoc.=20r=3Dbaku=20(ed4901?= =?UTF-8?q?73ff)=20-=20Bug=201261933=20-=20Continue=20unlocking=20pointer?= =?UTF-8?q?=20even=20if=20the=20widget=20has=20gone.=20r=3Dsmaug=20(93a781?= =?UTF-8?q?887a)=20-=20Bug=201259296=20-=20Do=20not=20scroll=20snap=20on?= =?UTF-8?q?=20the=20main=20thread=20for=20wheel=20events=20handled=20by=20?= =?UTF-8?q?APZ.=20r=3Dkats=20(f72a7fef66)=20-=20Bug=201208371=20-=20Pass?= =?UTF-8?q?=20parent=20window=20to=20DOMMediaStream=20through=20constructo?= =?UTF-8?q?r.=20r=3Droc=20(1c2cc7e7c7)=20-=20Bug=201208371=20-=20Make=20Au?= =?UTF-8?q?dioCaptureStream=20startable.=20r=3Dpadenot=20(15849ae011)=20-?= =?UTF-8?q?=20Bug=201208371=20-=20Move=20OnTracksAvailableCallback=20out?= =?UTF-8?q?=20of=20DOMMediaStream.=20r=3Droc=20(a772c949e0)=20-=20Bug=2012?= =?UTF-8?q?08371=20-=20Remove=20unused=20MediaManager::NotifyMediaStreamTr?= =?UTF-8?q?ackEnded.=20r=3Djib=20(9157aa056e)=20-=20Bug=201208371=20-=20In?= =?UTF-8?q?troduce=20MediaStreamTrack=20logs.=20r=3Droc,jib=20(72ff4d4c59)?= =?UTF-8?q?=20-=20Bug=201208371=20-=20Track=20original=20track=20in=20Medi?= =?UTF-8?q?aStreamTrack=20clones.=20r=3Djib=20(39e9ae1200)=20-=20Bug=20120?= =?UTF-8?q?8371=20-=20Un-nest=20MediaEngineSource::PhotoCallback.=20r=3Dro?= =?UTF-8?q?c=20(b1e0b48012)=20-=20Bug=201253333:=20Don't=20cause=20a=20sec?= =?UTF-8?q?ond=20MediaManager=20to=20be=20created=20if=20there's=20a=20rac?= =?UTF-8?q?e=20with=20NotifyFinished=20r=3Djib=20(d22d6d01e5)=20-=20Bug=20?= =?UTF-8?q?1251357=20-=20fix=20regression=20where=20last=20MediaStreamTrac?= =?UTF-8?q?k.stop=20did=20not=20turn=20off=20the=20camera=20light.=20r=3Dj?= =?UTF-8?q?esup=20(11b4880c02)=20-=20Bug=201208371=20-=20Add=20a=20MediaSt?= =?UTF-8?q?reamTrackSource=20interface.=20r=3Droc=20(bb1880524b)=20-=20Bug?= =?UTF-8?q?=201208371=20-=20Add=20MediaStreamTrackSourceGetter=20interface?= =?UTF-8?q?.=20r=3Droc=20(c57a1c6a74)=20-=20Bug=201208371=20-=20Let=20Medi?= =?UTF-8?q?aStreamTracks=20know=20their=20TrackID=20at=20the=20source.=20r?= =?UTF-8?q?=3Droc=20(62739d72bf)=20-=20Bug=201259236=20-=20throw=20NotSupp?= =?UTF-8?q?ortedError=20on=20pc.addTrack=20of=20track=20in=20constructed?= =?UTF-8?q?=20stream.=20r=3Dbwc=20(8b828e80e4)=20-=20Bug=201208371=20-=20L?= =?UTF-8?q?et=20FindOwnedDOMTrack=20operate=20on=20input=20stream.=20r=3Dr?= =?UTF-8?q?oc=20(26c24ad3c7)=20-=20Bug=201208371=20-=20Add=20some=20MediaS?= =?UTF-8?q?treamTrack=20helper=20methods.=20r=3Droc=20(1627cd36f8)=20-=20B?= =?UTF-8?q?ug=201208371=20-=20Count=20the=20users=20of=20a=20MediaStream?= =?UTF-8?q?=20to=20ease=20Destroy()=20resonsibility.=20r=3Droc=20(613d446b?= =?UTF-8?q?39)=20-=20bug=201223670=20assert=20that=20connected=20streams?= =?UTF-8?q?=20have=20the=20same=20graph=20r=3Dpadenot=20(17ea987392)=20-?= =?UTF-8?q?=20Bug=201208371=20-=20Add=20convenience=20method=20for=20check?= =?UTF-8?q?ing=20if=20TrackID=20is=20explicit.=20r=3Droc=20(32c9d5fe45)=20?= =?UTF-8?q?-=20Bug=201208371=20-=20Allow=20MediaInputPorts=20mapped=20to?= =?UTF-8?q?=20a=20destination=20TrackID.=20r=3Droc=20(71d3c66c4d)=20-=20Bu?= =?UTF-8?q?g=201208371=20-=20Remove=20obsolete=20SetTrackEnabled()=20from?= =?UTF-8?q?=20DOMMediaStream=20r=3Droc=20(cbcf54a342)=20-=20Bug=201208371?= =?UTF-8?q?=20-=20Add=20MediaStreamTrack::Graph().=20r=3Djib=20(044b348267?= =?UTF-8?q?)=20-=20Bug=201208371=20-=20Add=20DOMMediaStream::GetTrackById/?= =?UTF-8?q?GetOwnedTrackById.=20r=3Djib=20(6f58360808)=20-=20Bug=201208371?= =?UTF-8?q?=20-=20Add=20a=20generic=20PrincipalChangeObserver=20interface.?= =?UTF-8?q?=20r=3Dmt=20(79dfb91a89)=20-=20Bug=201208371=20-=20Turn=20DOMMe?= =?UTF-8?q?diaStream::PrincipalChangeObserver=20into=20PrincipalChangeObse?= =?UTF-8?q?rver.=20r=3Dmt=20(41fdd835de)=20-=20Bug=2012083?= =?UTF-8?q?71=20-=20Add=20principal=20APIs=20to=20MediaStreamTrack.=20r=3D?= =?UTF-8?q?mt=20(a8aab0e0d9)=20-=20Bug=201208371=20-=20Create=20MediaStrea?= =?UTF-8?q?mTrackSource::Sink=20that=20forwards=20principal=20changes=20fr?= =?UTF-8?q?om=20the=20source.=20r=3Dmt=20(d9fefd4e3c)=20-=20Bug=201208371?= =?UTF-8?q?=20-=20Let=20DOMMediaStream=20base=20its=20principal=20on=20the?= =?UTF-8?q?=20tracks=20it=20contains.=20r=3Dmt=20(0c046c7c20)=20-=20Bug=20?= =?UTF-8?q?1208371=20-=20Add=20an=20interface=20DecoderPrincipalChangeObse?= =?UTF-8?q?rver=20to=20HTMLMediaElement.=20r=3Droc=20(ecde0ddae0)=20-=20Bu?= =?UTF-8?q?g=201208371=20-=20Make=20HTMLMediaElement::CaptureStream=20pass?= =?UTF-8?q?=20its=20principal=20to=20MediaStreamTrack.=20r=3Dmt=20(03ccd4a?= =?UTF-8?q?b12)=20-=20Bug=201208371=20-=20Make=20MediaManager=20pass=20its?= =?UTF-8?q?=20principal=20to=20MediaStreamTrack=20for=20gUM.=20r=3Dmt=20(5?= =?UTF-8?q?a4b1eb0bc)=20-=20Bug=201208371=20-=20Make=20ImageCapture=20list?= =?UTF-8?q?en=20to=20principal=20changes=20of=20MediaStreamTrack=20instead?= =?UTF-8?q?.=20r=3Dmt=20(d29fc5c36e)=20-=20Bug=201208371=20-=20Break=20PCI?= =?UTF-8?q?mpl::SetRemoteDescription=20into=20smaller=20pieces.=20r=3Dmt,b?= =?UTF-8?q?wc=20(e4337cc413)=20-=20Bug=201208371=20-=20Make=20PeerConnecti?= =?UTF-8?q?onImpl=20pass=20its=20principal=20to=20MediaStreamTrack=20throu?= =?UTF-8?q?gh=20a=20new=20RemoteTrackSource.=20r=3Dmt=20(9a7c744a19)=20-?= =?UTF-8?q?=20Bug=201208371=20-=20Make=20remaining=20DOMMediaStream=20prin?= =?UTF-8?q?cipal=20sources=20use=20MediaStreamTrack.=20r=3Dmt=20(b30919c68?= =?UTF-8?q?1)=20-=20Bug=201208371=20-=20Remove=20some=20no=20longer=20nece?= =?UTF-8?q?ssary=20principal=20methods=20on=20DOMMediaStream.=20r=3Dmt=20(?= =?UTF-8?q?5279935d2b)=20-=20Bug=201208371=20-=20Add=20a=20MediaStreamTrac?= =?UTF-8?q?kListener=20to=20MediaStreamGraph.=20r=3Droc=20(5d7048793e)=20-?= =?UTF-8?q?=20Bug=201208371=20-=20Move=20ImageCapture=20to=20a=20MediaStre?= =?UTF-8?q?amTrackListener.=20r=3Droc=20(98d081b034)=20-=20Bug=201208371?= =?UTF-8?q?=20-=20Make=20it=20possible=20to=20look=20up=20stream=20id=20by?= =?UTF-8?q?=20track=20in=20PeerConnectionImpl.=20r=3Djib=20(c65d07cb55)=20?= =?UTF-8?q?-=20Bug=201208371=20-=20Fix=20DOMMediaStream::OwnsTrack.=20r=3D?= =?UTF-8?q?roc=20(092dad3654)=20-=20Bug=201208371=20-=20Remove=20MediaStre?= =?UTF-8?q?amTrack::GetStream.=20r=3Djib=20(a78f873d89)=20-=20Bug=20120837?= =?UTF-8?q?1=20-=20Route=20ApplyConstraints=20through=20MediaStreamTrackSo?= =?UTF-8?q?urce.=20r=3Djib=20(32dfd76245)=20-=20Bug=201208371=20-=20Kill?= =?UTF-8?q?=20nsDOMUserMediaStream=20with=20fire.=20r=3Djib=20(9550ad61be)?= =?UTF-8?q?=20-=20Bug=201208371=20-=20Make=20it=20possible=20to=20block=20?= =?UTF-8?q?tracks=20in=20a=20MediaInputPort=20initally.=20r=3Droc=20(39fdd?= =?UTF-8?q?2322f)=20-=20Bug=201208371=20-=20Implement=20MediaStreamTrack::?= =?UTF-8?q?Clone().=20r=3Dsmaug,jib,roc=20(1995b87f10)=20-=20Bug=201208371?= =?UTF-8?q?=20-=20Implement=20DOMMediaStream::Clone()=20r=3Dsmaug,jib,roc?= =?UTF-8?q?=20(d445dffd30)=20-=20Bug=201208371=20-=20Various=20cleanups=20?= =?UTF-8?q?in=20DOMMediaStream/MediaStreamTrack.=20r=3Djib=20(a174781a1b)?= =?UTF-8?q?=20-=20Bug=201208371=20-=20Forward=20input=20stream=20and=20tra?= =?UTF-8?q?ck=20id=20on=20regular=20track=20changes=20for=20union=20stream?= =?UTF-8?q?s.=20r=3Droc=20(36ed98f3e4)=20-=20Bug=201208371=20-=20Move=20tr?= =?UTF-8?q?ack.stop()=20helpers=20to=20MediaStreamPlayback.=20r=3Djib=20(1?= =?UTF-8?q?23bcd8192)=20-=20Bug=201240478=20-=20Add=20test=20for=20video?= =?UTF-8?q?=20size=20on=20'loadedmetadata'.=20r=3Djesup,jib=20Bug=20120837?= =?UTF-8?q?1=20-=20Test=20DOMMediaStream::Clone().=20r=3Djib=20(5ff908fcad?= =?UTF-8?q?)=20-=20Bug=201208371=20-=20Test=20DOMMediaStream::Clone().=20r?= =?UTF-8?q?=3Djib=20(445556e6b5)=20-=20Bug=201208371=20-=20Rename=20Create?= =?UTF-8?q?OwnDOMTrack/CreateClonedDOMTrack=20to=20CreateDOMTrack/CloneDOM?= =?UTF-8?q?Track.=20r=3Djib=20(41c71b073c)=20-=20Bug=201208371=20-=20Resol?= =?UTF-8?q?ve=20ambiguous=20symbol=20MediaStreamTrack.=20r=3Dbwc=20(f99835?= =?UTF-8?q?4d96)=20-=20Bug=201208371=20-=20Test=20MediaStreamTrack::Clone(?= =?UTF-8?q?).=20r=3Djib=20(34865902b0)=20-=20Bug=201208371=20-=20Add=20COR?= =?UTF-8?q?SMode=20methods=20to=20MediaStreamTrack.=20r=3Dmt=20(2244b06567?= =?UTF-8?q?)=20-=20Bug=201208371=20-=20Add=20convenience=20method=20for=20?= =?UTF-8?q?forwarding=20a=20track=20to=20an=20MSG-stream.=20r=3Droc=20(f21?= =?UTF-8?q?675220b)=20-=20bug=201223670=20throw=20not=20supported=20when?= =?UTF-8?q?=20creating=20a=20node=20from=20a=20stream=20with=20different?= =?UTF-8?q?=20channel=20r=3Dbaku=20(55e1d67356)=20-=20Bug=201208371=20-=20?= =?UTF-8?q?Lock=20MediaStreamAudioSourceNode=20onto=20the=20first=20AudioS?= =?UTF-8?q?treamTrack=20in=20mInputStream=20at=20time=20of=20construction.?= =?UTF-8?q?=20r=3Dmt,padenot=20(77640fa752)=20-=20Bug=201208371=20-=20Move?= =?UTF-8?q?=20HTMLMediaElement::CaptureStream=20to=20forward=20CORSMode=20?= =?UTF-8?q?changes=20through=20MediaStreamTrackSource.=20r=3Dmt=20(aa533e8?= =?UTF-8?q?945)=20-=20Bug=201208371=20-=20Remove=20CORSMode=20methods=20fr?= =?UTF-8?q?om=20DOMMediaStream.=20r=3Dmt=20(41447ccea1)=20-=20Bug=20120837?= =?UTF-8?q?1=20-=20Change=20HTMLMediaElement=20video=20sinks=20to=20check?= =?UTF-8?q?=20principal=20for=20video=20only=20tracks.=20r=3Dmt=20(b8168de?= =?UTF-8?q?792)=20-=20Bug=201208371=20-=20Remove=20ref=20counting=20from?= =?UTF-8?q?=20DOMMediaStream::TrackListener.=20r=3Droc=20(fe275597ca)=20-?= =?UTF-8?q?=20Bug=201208371=20-=20Update=20tests=20to=20accomodate=20the?= =?UTF-8?q?=20fact=20that=20MediaStreamAudioSourceNodes=20lock=20onto=20th?= =?UTF-8?q?e=20first=20audio=20track.=20r=3Dpadenot=20(ee97d9f274)=20-=20b?= =?UTF-8?q?ug=20916387=20remove=20workarounds=20in=20tests=20r=3Dpadenot?= =?UTF-8?q?=20(0285f2b98a)=20-=20Bug=201208371=20-=20Modify=20WebAudio=20s?= =?UTF-8?q?ource=20nodes=20tests=20to=20wait=20for=20tracks=20befoure=20me?= =?UTF-8?q?asuring=20nr=20of=20samples.=20r=3Dpadenot=20(bb3afcdd12)=20-?= =?UTF-8?q?=20Bug=201208371=20-=20Make=20PeerIdentity=20RefCounted.=20r=3D?= =?UTF-8?q?mt=20(74320ca464)=20-=20Bug=201208371=20-=20Add=20GetPeerIdenti?= =?UTF-8?q?ty()=20to=20MediaStreamTrackSource.=20r=3Dmt=20(77bffa988f)=20-?= =?UTF-8?q?=20Bug=201208371=20-=20Add=20PeerIdentity=20to=20LocalTrackSour?= =?UTF-8?q?ce=20for=20gUM=20tracks.=20r=3Dmt=20(4b4163b66d)=20-=20Bug=2012?= =?UTF-8?q?08371=20-=20Move=20PeerConnection=20to=20use=20PeerIdentity=20o?= =?UTF-8?q?n=20MediaStreamTrack.=20r=3Dmt=20(30b5ab4b5d)=20-=20Bug=2012083?= =?UTF-8?q?71=20-=20Remove=20PeerIdentity=20from=20DOMMediaStream.=20r=3Dm?= =?UTF-8?q?t=20(0d6b66614e)=20-=20Bug=201208371=20-=20Add=20convenience=20?= =?UTF-8?q?method=20for=20checking=20track=20forwarding=20to=20MediaStream?= =?UTF-8?q?Track.=20r=3Droc=20(d9eb5210e0)=20-=20Bug=201208371=20-=20Remov?= =?UTF-8?q?e=20test=5Fmediarecorder=5Frecord=5Fnosrc.html.=20r=3Droc=20(5d?= =?UTF-8?q?7b83b51c)=20-=20Bug=201208371=20-=20Inline=20MediaRecorder::Ses?= =?UTF-8?q?sion::SetupStreams.=20r=3Droc=20(1f8463266e)=20-=20Bug=20115369?= =?UTF-8?q?0=20-=20Release=20the=20Session=20and=20MediaRecorder=20objects?= =?UTF-8?q?=20correctly=20if=20there=20is=20no=20ExtractRunnable=20running?= =?UTF-8?q?.=20r=3Droc=20(424117e697)=20-=20Bug=201257318:=20Move=20MediaR?= =?UTF-8?q?ecorder=20to=20use=20DirectListeners=20wherever=20possible.=20r?= =?UTF-8?q?=3Droc=20(1d6336641b)=20-=20Bug=201208371=20-=20Check=20princip?= =?UTF-8?q?al=20per=20track=20instead=20of=20per=20stream=20in=20MediaReco?= =?UTF-8?q?rder.=20r=3Droc=20(946080d5e7)=20-=20Bug=201219711=20-=20Add=20?= =?UTF-8?q?mochitest=20for=20track=20disabling=20over=20a=20peer=20connect?= =?UTF-8?q?ion.=20r=3Djib=20(ad345e05e2)=20-=20Bug=201166832=20-=20Add=20t?= =?UTF-8?q?est=20to=20verify=20audio=20(using=20AudioStreamAnalyser)=20aft?= =?UTF-8?q?er=20renegotiation.=20r=3Dbwc=20(ad11535ee3)=20-=20Bug=20125093?= =?UTF-8?q?4:=20Make=20tests=20use=20the=20correct=20audio=20frequency=20w?= =?UTF-8?q?hen=20fake=20devices=20are=20used=20r=3Ddrno=20(1b8cb18b8f)=20-?= =?UTF-8?q?=20Bug=201182426=20-=20Test=20that=20we=20can=20record=20Canvas?= =?UTF-8?q?CaptureMediaStreams.=20r=3Droc=20(e65a5dce61)=20-=20Bug=2012197?= =?UTF-8?q?11=20-=20Refactor=20captureStream=5Fcommon.js=20to=20accept=20g?= =?UTF-8?q?eneric=20pixel=20testing=20method.=20r=3Djib=20(e55194c8f5)=20-?= =?UTF-8?q?=20Bug=201223696=20-=20Make=20canvas=20captureStream=20helper?= =?UTF-8?q?=20resilient=20to=20exceptions=20when=20there's=20no=20video.?= =?UTF-8?q?=20r=3Droc=20(215711b89d)=20-=20Bug=201223696.=20Don't=20destro?= =?UTF-8?q?y=20VideoFrameContainer=20when=20we=20reach=20MetadataLoaded=20?= =?UTF-8?q?without=20a=20video=20track.=20r=3Djwwang=20(2c83a23b24)=20-=20?= =?UTF-8?q?Bug=201166832=20-=20Add=20test=20for=20canvas=20capture=20on=20?= =?UTF-8?q?multiple=20streams.=20r=3Dbwc=20(592db27fc4)=20-=20Bug=20122402?= =?UTF-8?q?9:=20ensure=20video=20elements=20that=20may=20be=20disabled=20(?= =?UTF-8?q?black)=20are=20scaled=20r=3Dmattwoodrow=20(bfef6af17d)=20-=20Bu?= =?UTF-8?q?g=201208371=20-=20Simplify=20track=20disabling=20test=20code.?= =?UTF-8?q?=20r=3Djib=20(771d560f40)=20-=20Bug=201208371=20-=20Test=20disa?= =?UTF-8?q?bling=20track=20across=20peerconnection=20not=20affecting=20clo?= =?UTF-8?q?nes.=20r=3Djib=20(31913dd9e6)=20-=20Bug=201208371=20-=20Add=20D?= =?UTF-8?q?irectTrackLister=20to=20MediaStreamGraph=20and=20MediaStreamTra?= =?UTF-8?q?ck.=20r=3Droc,jesup=20(ae86375502)=20-=20Bug=201208371=20-=20Sw?= =?UTF-8?q?itch=20MediaPipeline=20to=20use=20direct=20listeners=20on=20tra?= =?UTF-8?q?cks.=20r=3Djesup,bwc=20(aa32ce43e1)=20-=20Bug=201208371=20-=20L?= =?UTF-8?q?et=20PeerConnection=20consume=20principals=20from=20tracks=20in?= =?UTF-8?q?stead=20of=20streams.=20r=3Dmt=20(825d6775d0)=20-=20Bug=2012573?= =?UTF-8?q?18:=20Make=20recorder.pause()/resume()=20work=20with=20DirectLi?= =?UTF-8?q?steners=20r=3Dpadenot=20(65ff6bff4e)=20-=20Bug=201208371=20-=20?= =?UTF-8?q?Move=20FindTrack=20from=20SourceMediaStream=20to=20MediaStream.?= =?UTF-8?q?=20r=3Droc=20(5725a863ae)=20-=20Bug=201208371=20-=20Switch=20Me?= =?UTF-8?q?diaStreamTrack=20to=20enable/disable=20tracks=20on=20owned=20st?= =?UTF-8?q?ream.=20r=3Djesup=20(ea7445369d)=20-=20Bug=201208371=20-=20Make?= =?UTF-8?q?=20GraphDriver=20callback=20log=20calls=20verbose.=20r=3Dpadeno?= =?UTF-8?q?t=20(a502ceea55)=20-=20Bug=201208371=20-=20Do=20not=20require?= =?UTF-8?q?=20DOMLocalMediaStream=20for=20gUM=20(audioCapture).=20r=3Djib?= =?UTF-8?q?=20(c27ee1c308)=20-=20Bug=201208371=20-=20Add=20a=20track=20get?= =?UTF-8?q?ter=20to=20gUM=20for=20fake=20tracks.=20r=3Djib=20(4958d2b392)?= =?UTF-8?q?=20-=20Bug=201208371=20-=20Remove=20unnecessary=20fakeTracks:tr?= =?UTF-8?q?ue=20from=20test=5Fstreams=5Findividual=5Fpause.html.=20r=3Djib?= =?UTF-8?q?=20(2437b932b2)=20-=20Bug=201208371=20-=20Remove=20debug=20canv?= =?UTF-8?q?as=20from=20addTrackRemoveTrack=20test=20to=20help=20android=20?= =?UTF-8?q?perf.=20r=3Dpadenot=20(38b1ad91e8)=20-=20Bug=201208371=20-=20Re?= =?UTF-8?q?move=20debug=20canvas=20from=20peerConnection=5FtrackDisabling?= =?UTF-8?q?=20test=20to=20help=20android=20perf.=20r=3Dpadenot=20(a8b5a62a?= =?UTF-8?q?d6)=20-=20Bug=201208371=20-=20Enable=20test=5FpeerConnection=5F?= =?UTF-8?q?trackDisabling.html=20on=20android=204.3=20debug.=20r=3Djib=20(?= =?UTF-8?q?19e14785f3)=20-=20Bug=201208371=20-=20Remove=20debug=20canvas?= =?UTF-8?q?=20from=20mediaStreamConstructors=20test=20to=20help=20android?= =?UTF-8?q?=20perf.=20r=3Dpadenot=20(b7e3280b68)=20-=20Bug=201208371=20-?= =?UTF-8?q?=20Add=20disconnect=20method=20to=20test=20helper=20AudioStream?= =?UTF-8?q?Analyser=20to=20improve=20performance=20on=20slow=20devices.=20?= =?UTF-8?q?r=3Dpadenot=20(2a18d693a7)=20-=20Bug=201208371=20-=20Guard=20Lo?= =?UTF-8?q?adManagerSingleton=20with=20a=20WeakPtr.=20r=3Dpkerr=20(0f6d425?= =?UTF-8?q?cf4)=20-=20Bug=201208371=20-=20Clear=20output=20canvas=20on=20e?= =?UTF-8?q?ach=20drawImage().=20r=3Djib=20(45731c0c9a)=20-=20Bug=201208371?= =?UTF-8?q?=20-=20Wait=20for=20"loadedmetadata"=20before=20setting=20up=20?= =?UTF-8?q?nodes=20in=20test=5FmediaElementAudioSourceNodeCrossOrigin.html?= =?UTF-8?q?.=20r=3Dpadenot=20(8e9398fe88)=20-=20Bug=201181051=20-=20Remove?= =?UTF-8?q?=20BOM=20from=20dom/media/test/test*.html.=20r=3Dkinetik=20(c98?= =?UTF-8?q?b2c9b96)=20-=20Bug=201070110=20-=20Ensure=20the=20testcase=20wi?= =?UTF-8?q?ll=20receive=20at=20least=202=20blob.=20r=3Djwwang=20(028cb09b0?= =?UTF-8?q?8)=20-=20Bug=201208371=20-=20Improve=20reliability=20of=20test?= =?UTF-8?q?=5Fmediarecorder=5Frecord=5Ftimeslice.html.=20r=3Djwwang=20(c89?= =?UTF-8?q?bd3fe0b)=20-=20Bug=201251494=20-=20Remove=20remaining=20referen?= =?UTF-8?q?ces=20to=20MOZILLA=5FXPCOMRT=5FAPI=20from=20dom.=20r=3Djesup=20?= =?UTF-8?q?(5b8e7ef603)=20-=20Bug=201234230:=20Don't=20pass=20null=20image?= =?UTF-8?q?s=20for=20video=20encoding,=20and=20don't=20reencode=20the=20sa?= =?UTF-8?q?me=20image=20r=3Droc=20(eb09c07d8d)=20-=20Bug=201258567=20-=20p?= =?UTF-8?q?er=20comment=203,=20ensure=20volume=20is=20only=20applied=20onc?= =?UTF-8?q?e=20to=20the=20AudioSegment.=20r=3Dkinetik.=20(a7e184e483)=20-?= =?UTF-8?q?=20Bug=201250934:=20Modify=20MediaEngine=20shutdown=20to=20allo?= =?UTF-8?q?w=20neutering=20the=20AudioDataListener=20r=3Dpadenot=20(70f483?= =?UTF-8?q?1508)=20-=20Bug=201208371=20-=20Add=20PrincipalHandle=20to=20Me?= =?UTF-8?q?diaChunks.=20r=3Dmt,jesup=20(0662c26a97)=20-=20Bug=201208371=20?= =?UTF-8?q?-=20Add=20NotifyPrincipalHandleChanged=20to=20MediaStreamTrackL?= =?UTF-8?q?istener.=20r=3Dmt,jesup=20(54d896b1ff)=20-=20Bug=201208371=20-?= =?UTF-8?q?=20Add=20a=20PrincipalHandleListener=20to=20MediaStreamTrack.?= =?UTF-8?q?=20r=3Dmt,jesup=20(9b4d2e0996)=20-=20Bug=201256520=20-=20use=20?= =?UTF-8?q?SyncRunnable=20to=20create=20DecodedStreamData=20synchronously?= =?UTF-8?q?=20to=20ensure=20the=20creation=20and=20destruction=20of=20Deco?= =?UTF-8?q?dedStreamData=20happen=20in=20order.=20r=3Dkikuo.=20(7d28eaebba?= =?UTF-8?q?)=20-=20Bug=201237482=20-=20Remove=20MediaDecoderStateMachine::?= =?UTF-8?q?mStreamSink.=20r=3Dkikuo.=20(609efed70f)=20-=20Bug=201208371=20?= =?UTF-8?q?-=20Hook=20up=20DecodedStream=20with=20PrincipalHandle.=20r=3Dm?= =?UTF-8?q?t,jwwang=20(9290efb216)=20-=20Bug=201208371=20-=20Hook=20up=20M?= =?UTF-8?q?ediaPipeline=20with=20PrincipalHandle.=20r=3Dmt,bwc=20(b00ed70d?= =?UTF-8?q?dc)=20-=20Bug=201208371=20-=20Ensure=20DOMMediaStream=20princip?= =?UTF-8?q?als=20reflect=20what=20could=20reside=20in=20their=20playback?= =?UTF-8?q?=20streams.=20r=3Dmt,jesup=20(bd3e32ef16)=20-=20Bug=201208371?= =?UTF-8?q?=20-=20Ensure=20a=20media=20element's=20ImageContainer=20is=20p?= =?UTF-8?q?rotected=20when=20playing=20a=20stream.=20r=3Dmt,jesup=20(7914f?= =?UTF-8?q?aa5dc)=20-=20Bug=201208371=20-=20Forward=20declare=20MediaStrea?= =?UTF-8?q?mTrack=20in=20MediaPipeline.h.=20r=3Dbwc=20(4c88b79137)=20-=20B?= =?UTF-8?q?ug=201208371=20-=20Clean=20up=20unnecessary=20virtuals=20in=20M?= =?UTF-8?q?ediaPipeline.=20r=3Dbwc=20(b7112f4b34)=20-=20Bug=201208371=20-?= =?UTF-8?q?=20Move=20MediaStreamTrack's=20label=20to=20MediaStreamTrackSou?= =?UTF-8?q?rce.=20r=3Djib=20(045bfcc738)=20-=20Bug=201259590=20-=20Remove?= =?UTF-8?q?=20B2G=20ACL=20code.=20r=3Dkhuey=20(e13223c0f6)=20-=20Bug=20126?= =?UTF-8?q?5452=20followup=20-=20Make=20mGlyphExtentsArray=20an=20array=20?= =?UTF-8?q?of=20UniquePtr.=20r=3Djrmuizel=20(d12ce46244)=20-=20Bug=2012505?= =?UTF-8?q?40=20-=20have=20media.navigator.permission.disabled=20no=20long?= =?UTF-8?q?er=20bypass=20media.getusermedia.screensharing.allowed=5Fdomain?= =?UTF-8?q?s.=20r=3Djesup=20(e55aa02580)=20-=20Bug=201247806=20-=20Fix=20H?= =?UTF-8?q?STS=20redirect=20check=20in=20WebSocketChannel,=20r=3Dmcmanus?= =?UTF-8?q?=20(4e44becf9e)=20-=20Bug=201251530:=20Use=20'MakeUnique'=20ins?= =?UTF-8?q?tead=20of=20'new'=20to=20populate=20UniquePtr=20variable=20in?= =?UTF-8?q?=20AddCSSValuePair.=20r=3Djwatt=20(286bf2289a)=20-=20Bug=201264?= =?UTF-8?q?787:=20Make=20nsCSSValue's=20AdoptListValue=20&=20AdoptPairList?= =?UTF-8?q?Value=20methods=20take=20a=20UniquePtr.=20r=3Dheycam=20(0f7dc71?= =?UTF-8?q?64f)=20-=20Bug=201151243=20part=203=20-=20[css-grid]=20Add=20a?= =?UTF-8?q?=20generic=20nsHTMLReflowState::STATIC=5FPOS=5FIS=5FCB=5FORIGIN?= =?UTF-8?q?=20flag=20to=20place=20the=20static-position=20at=20the=20CB=20?= =?UTF-8?q?origin,=20and=20make=20nsAbsoluteContainingBlock=20use=20it=20i?= =?UTF-8?q?n=20Grid=20containers=20where=20the=20placeholder=20is=20a=20ch?= =?UTF-8?q?ild=20too.=20r=3Ddholbert=20(7bde498cc4)=20-=20Bug=201151243=20?= =?UTF-8?q?part=204=20-=20Some=20code=20cleanup=20in=20nsHTMLReflowState::?= =?UTF-8?q?CalculateHypotheticalPosition,=20and=20make=20a=20few=20methods?= =?UTF-8?q?=20'const'=20(idempotent=20patch).=20r=3Ddholbert=20(e430f9b99d?= =?UTF-8?q?)=20-=20Bug=201250540=20-=20fix=20string-parsing=20regression?= =?UTF-8?q?=20that=20made=20screen=20share=20whitelisting=20stop=20working?= =?UTF-8?q?.=20r=3Djesup=20(f8af7c3053)=20-=20Bug=201265641:=20Move=20AEC?= =?UTF-8?q?=20tail=20length=20and=20delay-agnostic=20settings=20to=20getUs?= =?UTF-8?q?erMedia=20r=3Dpadenot=20(3feaf716ee)=20-=20Bug=20950936=20-=20I?= =?UTF-8?q?ntroduce=20ui.popup.disable=5Fautohide=20pref=20to=20ease=20deb?= =?UTF-8?q?ugging=20popups.=20r=3Dneil=20(bc28dcd531)=20-=20bug=201211892?= =?UTF-8?q?=20read=20-unico-border-gradient=20of=20early=20Unico=20version?= =?UTF-8?q?s=20for=20ThreeDHighlight=20and=20ThreeDShadow=20r=3Dacomminos?= =?UTF-8?q?=20(39c83b1865)=20-=20bug=201257695=20disable=20GTK3=20deprecat?= =?UTF-8?q?ion=20warnings=20in=20widget/gtk=20r=3Dacomminos=20(1ad2ba0271)?= =?UTF-8?q?=20-=20Bug=201258989=20-=20Switch=20gtk3drawing=20to=20a=20C++?= =?UTF-8?q?=20file,=20r=3Dkarlt=20(55b8ea3cdf)=20-=20Bug=201259433=20-=20r?= =?UTF-8?q?emove=20gtk=5Fwidget=5Fstyle=5Fget()=20from=20gtk3drawing,=20r?= =?UTF-8?q?=3Dkarlt=20a=3Dkwierso=20(a9fc049140)=20-=20Bug=201266680=20-?= =?UTF-8?q?=20Rename=20BlockTrackId=20to=20BlockSourceTrackId.=20r=3Djesup?= =?UTF-8?q?=20(5066bbf870)=20-=20Bug=201262808:=20Refactor=20|PrincipalID|?= =?UTF-8?q?=20to=20|PrincipalHandle|=20in=20Gonk=20media=20code,=20r=3Drje?= =?UTF-8?q?sup=20(49ec99e852)=20-=20Bug=201144096=20part=201=20-=20[css-gr?= =?UTF-8?q?id]=20Refactor=20nsGridContainerFrame=20state=20and=20methods.?= =?UTF-8?q?=20r=3Ddholbert=20(502fb7d424)=20-=20Bug=201144096=20part=202?= =?UTF-8?q?=20-=20[css-grid]=20Make=20GridItemInfo::mFrame=20available=20a?= =?UTF-8?q?lso=20in=20non-DEBUG=20builds=20since=20we'll=20need=20it=20to?= =?UTF-8?q?=20support=20fragmentation.=20r=3Ddholbert=20(fe0b396741)=20-?= =?UTF-8?q?=20Bug=201144096=20part=203=20-=20[css-grid]=20Remove=20CellMap?= =?UTF-8?q?::ClearOccupied()=20since=20it's=20not=20needed=20anymore.=20r?= =?UTF-8?q?=3Ddholbert=20(7aeacdccab)=20-=20Bug=201144096=20part=204=20-?= =?UTF-8?q?=20[css-grid]=20Move=20more=20local=20nsGridContainerFrame=20cl?= =?UTF-8?q?asses=20into=20.cpp=20file.=20r=3Ddholbert=20(896fc410ac)=20-?= =?UTF-8?q?=20Bug=201144096=20part=205=20-=20[css-grid]=20Create=20a=20cou?= =?UTF-8?q?ple=20of=20Grid=20container=20frame=20bits.=20r=3Ddholbert=20(a?= =?UTF-8?q?bd007fd90)=20-=20Bug=201144096=20part=206=20-=20[css-grid]=20Ad?= =?UTF-8?q?d=20support=20for=20creating=20Grid=20container=20continuations?= =?UTF-8?q?=20and=20deal=20with=20overflow=20containers.=20r=3Ddholbert=20?= =?UTF-8?q?(3f02728f24)=20-=20Bug=201144096=20part=207=20-=20[css-grid]=20?= =?UTF-8?q?Don't=20create=20PageBreakFrames=20inside=20a=20Grid=20containe?= =?UTF-8?q?r.=20The=20container=20will=20handle=20forced=20breaks=20on=20i?= =?UTF-8?q?ts=20items.=20r=3Ddholbert=20(5a0c64369c)=20-=20Bug=201144096?= =?UTF-8?q?=20part=208=20-=20[css-grid]=20Add=20a=20new=20state=20flag,=20?= =?UTF-8?q?eBreakBefore,=20to=20record=20where=20breaks=20occur=20between?= =?UTF-8?q?=20tracks.=20r=3Ddholbert=20(5f5d8761c6)=20-=20Bug=201144096=20?= =?UTF-8?q?part=209=20-=20[css-grid]=20Create=20a=20SharedGridData=20objec?= =?UTF-8?q?t=20owned=20by=20the=20first-in-flow=20Grid=20container=20to=20?= =?UTF-8?q?share=20state=20between=20its=20continuations.=20r=3Ddholbert?= =?UTF-8?q?=20(7e510f0bf0)=20-=20Bug=201144096=20part=2010=20-=20[css-grid?= =?UTF-8?q?]=20Add=20a=20few=20helper=20methods=20to=20do=20a=20break=20be?= =?UTF-8?q?fore=20a=20row,=20and=20resize=20a=20row.=20r=3Ddholbert=20(d3f?= =?UTF-8?q?ec771a6)=20-=20Bug=201144096=20part=2011=20-=20[css-grid]=20Add?= =?UTF-8?q?=20a=20GetNearestFragmentainer()=20method=20that=20collects=20s?= =?UTF-8?q?ome=20data=20from=20the=20nearest=20enclosing=20fragmentainer?= =?UTF-8?q?=20needed=20for=20fragmentation.=20r=3Ddholbert=20(20d4736a1c)?= =?UTF-8?q?=20-=20Bug=201144096=20part=2012=20-=20[css-grid]=20Collect=20a?= =?UTF-8?q?nd=20merge=20child=20frames=20we=20need=20for=20reflow.=20r=3Dd?= =?UTF-8?q?holbert=20(3b5c619f34)=20-=20Bug=201144096=20part=2013=20-=20[c?= =?UTF-8?q?ss-grid]=20Refactor=20ReflowChildren()=20by=20separating=20out?= =?UTF-8?q?=20the=20code=20that=20reflows=20normal=20flow=20children=20(gr?= =?UTF-8?q?id=20items=20and=20placeholders)=20into=20a=20new=20method=20Re?= =?UTF-8?q?flowInFlowChild().=20r=3Ddholbert=20(ba8a1edc7b)=20-=20Bug=2011?= =?UTF-8?q?44096=20part=2014=20-=20[css-grid]=20Make=20ReflowInFlowChild()?= =?UTF-8?q?=20deal=20with=20a=20constrained=20available=20block-size.=20r?= =?UTF-8?q?=3Ddholbert=20(292df4167a)=20-=20Bug=201144096=20part=2015=20-?= =?UTF-8?q?=20[css-grid]=20Compute=20our=20pre-reflow=20logical=20skip=20s?= =?UTF-8?q?ides=20and=20cache=20the=20result=20of=20ComputedLogicalBorderP?= =?UTF-8?q?adding()=20with=20that=20applied.=20r=3Ddholbert=20(0c7f59389e)?= =?UTF-8?q?=20-=20Bug=201144096=20part=2016=20-=20[css-grid]=20Implement?= =?UTF-8?q?=20fragmentation.=20r=3Ddholbert=20(af8b8aac41)=20-=20Bug=20114?= =?UTF-8?q?4096=20part=2017=20-=20[css-grid]=20Add=20helper=20methods=20th?= =?UTF-8?q?at=20add=20a=20sorted=20list=20of=20child=20frames=20to=20the?= =?UTF-8?q?=20Overflow=20and=20ExcessOverflowContainers=20child=20lists.?= =?UTF-8?q?=20r=3Ddholbert=20(a6c80c9e9c)=20-=20Bug=201144096=20part=2018?= =?UTF-8?q?=20-=20[css-grid]=20Fix=20a=20couple=20of=20bugs=20in=20how=20w?= =?UTF-8?q?e=20handle=20child=20existing=20continuations=20when=20pushing/?= =?UTF-8?q?pulling=20children.=20r=3Ddholbert=20(b8a62fab65)=20-=20Bug=201?= =?UTF-8?q?144096=20part=2019=20-=20[css-grid]=20Sanity=20check=20the=20in?= =?UTF-8?q?itial=20child=20lists=20we=20get=20from=20the=20frame=20constru?= =?UTF-8?q?ctor=20(DEBUG=20only).=20r=3Ddholbert=20(f56955c3ec)=20-=20Bug?= =?UTF-8?q?=201144096=20part=2020=20-=20[css-grid]=20Sanity=20check=20our?= =?UTF-8?q?=20child=20lists=20before=20starting=20a=20Reflow=20(DEBUG=20on?= =?UTF-8?q?ly).=20r=3Ddholbert=20(d89c771238)=20-=20Bug=201144096=20part?= =?UTF-8?q?=2021=20-=20[css-grid]=20Deal=20with=20dynamically=20inserted/a?= =?UTF-8?q?ppended/removed=20child=20frames.=20r=3Ddholbert=20(a07ecc06e2)?= =?UTF-8?q?=20-=20Bug=201144096=20part=2022=20-=20[css-grid]=20Check=20NS?= =?UTF-8?q?=5FINLINE=5FIS=5FBREAK=5FBEFORE=20before=20checking=20other=20c?= =?UTF-8?q?ompletion=20status.=20r=3Ddholbert=20(731dcd0ac5)=20-=20Bug=201?= =?UTF-8?q?144096=20part=2023=20-=20[css-grid]=20A=20grid=20container=20fr?= =?UTF-8?q?agment=20that=20is=20an=20overflow=20container=20can't=20be=20I?= =?UTF-8?q?NCOMPLETE,=20only=20OVERFLOW=5FINCOMPLETE=20and=20it=20should?= =?UTF-8?q?=20always=20have=20zero=20BSize.=20r=3Ddholbert=20(afcd6c1741)?= =?UTF-8?q?=20-=20Bug=201144096=20part=2024=20-=20[css-grid]=20Move=20the?= =?UTF-8?q?=20child=20frame=20merging=20code=20at=20the=20start=20of=20Ref?= =?UTF-8?q?lowOverflowContainerChildren=20into=20a=20new=20method:=20Drain?= =?UTF-8?q?ExcessOverflowContainersList.=20Make=20both=20take=20a=20param?= =?UTF-8?q?=20so=20that=20we=20can=20override=20how=20the=20OC=20child=20l?= =?UTF-8?q?ists=20are=20merged=20together=20(normally=20just=20an=20Append?= =?UTF-8?q?;=20MergeSortedFrameLists=20for=20Grid).=20r=3Ddholbert=20(9532?= =?UTF-8?q?3b0a56)=20-=20Bug=201144096=20part=2025=20-=20[css-grid]=20Enab?= =?UTF-8?q?le=20fragmentation=20to=20occur=20by=20reporting=20our=20actual?= =?UTF-8?q?=20reflow=20status.=20r=3Ddholbert=20(767c389238)=20-=20Bug=201?= =?UTF-8?q?144096=20part=2026=20-=20[css-grid]=20Fragmentation=20reftests.?= =?UTF-8?q?=20(07e62f8fd6)=20-=20Bug=201244006=20-=20Use=20const=20instead?= =?UTF-8?q?=20of=20MOZ=5FCONSTEXPR=20to=20avoid=20startup=20crash;=20r=3Dd?= =?UTF-8?q?baron=20(39b0bb32fb)=20-=20Bug=201053986=20-=20Fix=20comment=20?= =?UTF-8?q?referring=20to=20a=20GetBorder=20method=20that=20no=20longer=20?= =?UTF-8?q?exists.=20r=3Ddholbert=20(2cde22a42c)=20-=20Bug=201264784=20-?= =?UTF-8?q?=20part=201=20-=20call=20nsIFrame::{GetScreenRect,GetView}=20in?= =?UTF-8?q?stead=20of=20their=20*External=20counterparts;=20r=3Ddholbert?= =?UTF-8?q?=20(a2cd84b7a2)=20-=20Bug=20963238:=20Support=20isTypeSupported?= =?UTF-8?q?()=20in=20MediaRecorder,=20and=20throw=20on=20invalid=20mimetyp?= =?UTF-8?q?es=20at=20construction=20r=3Dcpearce,khuey=20(5a3709e45f)=20-?= =?UTF-8?q?=20Bug=20963238:=20Fix=20compilation=20errors.=20r=3Dme=20(09b5?= =?UTF-8?q?41337f)=20-=20Bug=201157654=20-=201.=20Do=20not=20call=20MediaR?= =?UTF-8?q?ecorder.stop=20immediately=20after=20MediaRecorder.start,=20bec?= =?UTF-8?q?ause=20we=20want=20to=20receive=20an=20onerror=20callback.=202.?= =?UTF-8?q?=20Make=20the=20NofityError=20async.=20r=3Droc=20(ad56918042)?= =?UTF-8?q?=20-=20Bug=201128448=20-=201.=20Close=20the=20pref=20media.enco?= =?UTF-8?q?der.omx.enabled=20for=20newer=20android=20version.=202.=20Modif?= =?UTF-8?q?y=20testcase=20and=20MR=20to=20eusure=20no=20timing=20issue.=20?= =?UTF-8?q?r=3Droc=20(9dfff44a2c)=20-=20Bug=201197669=20-=20Part3=20-=20En?= =?UTF-8?q?sure=20the=20start=20event=20comes=20before=20any=20blobs.=20r?= =?UTF-8?q?=3Djwwang=20(d4e950fa49)=20-=20Bug=201225327=20-=20fix=20|mEnco?= =?UTF-8?q?der|=20null-dereference.=20r=3Djwwang=20(5ffce18e33)=20-=20Bug?= =?UTF-8?q?=201260702:=20Don't=20crash=20on=20input=20sources=20from=20Web?= =?UTF-8?q?Audio=20in=20MediaRecorder=20r=3Dpadenot=20(28bcb80a03)=20-=20B?= =?UTF-8?q?ug=201018299=20-=20Throw=20security=20error=20if=20principal=20?= =?UTF-8?q?check=20fails=20in=20MediaRecorder::Start().=20r=3Djib,=20r=3Dm?= =?UTF-8?q?t=20(1130168c8d)=20-=20Bug=201261007=20-=20Part=201=20-=20Force?= =?UTF-8?q?=20to=20send=20video=20sample=20into=20encoder=20if=20we=20got?= =?UTF-8?q?=20the=20same=20video=20sample=20more=20than=201=20seconds.=20E?= =?UTF-8?q?nable=20testcases.=20r=3Djolin=20(5698b39b45)=20-=20Bug=2011824?= =?UTF-8?q?26=20-=20Test=20that=20changing=20video=20resolution=20of=20a?= =?UTF-8?q?=20recorded=20stream=20throws=20an=20error.=20r=3Droc=20(edaf49?= =?UTF-8?q?f2cf)=20-=20Bug=201261007=20-=20Part=202=20-=20fix=20test=5Fmed?= =?UTF-8?q?iarecorder=5Fwebm=5Fsupport.=20r=3Drillian=20(16ef931949)=20-?= =?UTF-8?q?=20Bug=201250054.=20Part=201=20-=20implement=20MediaDecoderRead?= =?UTF-8?q?erWrapper.=20r=3Djya.=20(4b78b4335d)=20-=20Bug=201242841=20-=20?= =?UTF-8?q?Make=20MDSM::mDecodedAudioEndTime=20zero-based.=20r=3Dkikuo.=20?= =?UTF-8?q?(c6c756d554)=20-=20Bug=201242843=20-=20Make=20MDSM::mDecodedVid?= =?UTF-8?q?eoEndTime=20zero-based.=20r=3Dkikuo.=20(c194178323)=20-=20Bug?= =?UTF-8?q?=201242783.=20Part=201=20-=20per=20comment=2011,=20increase=20m?= =?UTF-8?q?AmpleAudioThresholdUsecs=20to=202s=20to=20avoid=20audio=20under?= =?UTF-8?q?run=20when=20BT=20is=20connected.=20r=3Dkinetik.=20(49567f587e)?= =?UTF-8?q?=20-=20Bug=201242783.=20Part=202=20-=20per=20comment=2012,=20bu?= =?UTF-8?q?ffer=20only=201s=20when=20audio=20is=20captured.=20r=3Dkinetik.?= =?UTF-8?q?=20(e0f0cd9826)=20-=20Bug=201230641:=20P1.=20Use=20UYVY=20(YUV4?= =?UTF-8?q?22)=20format=20in=20decoders.=20r=3Dmattwoodrow=20(17d4de398c)?= =?UTF-8?q?=20-=20Bug=201230641:=20P2.=20Increase=20the=20video=20queue=20?= =?UTF-8?q?size=20on=20mac.=20r=3Dcpearce=20(e575c85f44)=20-=20Bug=2012306?= =?UTF-8?q?41=20-=20Make=20our=20NSOpenGLContext=20opaque=20when=20in=20fu?= =?UTF-8?q?llscreen=20mode.=20r=3Dmstange=20(51da9e1e70)=20-=20Bug=2012570?= =?UTF-8?q?94=20-=20Remove=20MediaDecoderStateMachine::DECODER=5FSTATE=5FD?= =?UTF-8?q?ECODING=5FNONE.=20r=3Dcpearce.=20(f0993582c7)=20-=20Bug=2012523?= =?UTF-8?q?44.=20Part=201=20-=20remove=20unnecessary=20checks=20from=20Med?= =?UTF-8?q?iaDecoderStateMachine::NeedToDecodeVideo().=20r=3Dbechen.=20(1c?= =?UTF-8?q?9b7aadf4)=20-=20Bug=201252344.=20Part=202=20-=20remove=20unnece?= =?UTF-8?q?ssary=20checks=20from=20MediaDecoderStateMachine::NeedToDecodeA?= =?UTF-8?q?udio().=20r=3Dbechen.=20(6896d7d1f6)=20-=20Bug=201252360=20-=20?= =?UTF-8?q?remove=20some=20check=20from=20NeedToDecodeAudio().=20r=3Dbeche?= =?UTF-8?q?n.=20(1c064a9985)=20-=20Bug=201252762=20-=20Decode=20at=20most?= =?UTF-8?q?=20one=20audio/video=20sample=20before=20finishing=20seeking.?= =?UTF-8?q?=20r=3Dcpearce.=20(73ec7691df)=20-=20Bug=201253928=20-=20adjust?= =?UTF-8?q?=20the=20time=20passed=20to=20RequestVideoData()=20by=20the=20s?= =?UTF-8?q?tart=20time=20to=20avoid=20incorrectly=20skipping=20key=20frame?= =?UTF-8?q?s.=20r=3Djya.=20(1bc203d8e6)=20-=20Bug=201252766=20-=20Remove?= =?UTF-8?q?=20MediaDecoderStateMachine::mDecodeToSeekTarget=20which=20is?= =?UTF-8?q?=20never=20read.=20r=3Dkaku.=20(aac1fe8018)=20-=20Bug=201251460?= =?UTF-8?q?=20-=20MDSM=20now=20waits=20on=20a=20promise=20to=20enqueue=20f?= =?UTF-8?q?irst=20frame=20loaded.=20r=3Djya=20(0e7e67e006)=20-=20Bug=20125?= =?UTF-8?q?7013=20-=20Part=203:=20Stop=20checking=20for=20corrupt=20frames?= =?UTF-8?q?=20in=20MediaDecoderStateMachine=20since=20we=20no=20longer=20p?= =?UTF-8?q?roduce=20them.=20r=3Dajones=20(fbbbabafbb)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .lldbinit | 2 +- accessible/base/AccIterator.cpp | 2 +- accessible/base/TextAttrs.cpp | 2 +- accessible/base/nsAccessibilityService.cpp | 6 +- accessible/base/nsCoreUtils.cpp | 12 +- accessible/generic/Accessible.cpp | 6 +- accessible/generic/DocAccessible.cpp | 4 +- accessible/generic/OuterDocAccessible.cpp | 26 +- accessible/generic/OuterDocAccessible.h | 3 - accessible/html/HTMLImageMapAccessible.cpp | 21 +- accessible/html/HTMLImageMapAccessible.h | 7 +- accessible/html/HTMLSelectAccessible.cpp | 30 +- accessible/mac/RootAccessibleWrap.mm | 2 +- accessible/tests/mochitest/treeview.js | 6 - accessible/windows/msaa/AccessibleWrap.cpp | 2 +- .../msaa/HTMLWin32ObjectAccessible.cpp | 6 +- b2g/installer/package-manifest.in | 3 + config/system-headers | 1 + docshell/base/nsDocShell.cpp | 2 +- dom/animation/AnimValuesStyleRule.h | 7 +- dom/animation/AnimationEffectTiming.h | 6 +- .../AnimationEffectTimingReadOnly.cpp | 2 +- dom/animation/AnimationEffectTimingReadOnly.h | 6 +- dom/animation/ComputedTiming.h | 4 + dom/animation/ComputedTimingFunction.cpp | 45 +- dom/animation/ComputedTimingFunction.h | 13 +- dom/animation/KeyframeEffect.cpp | 538 ++-- dom/animation/KeyframeEffect.h | 49 +- dom/animation/KeyframeUtils.cpp | 600 +--- dom/animation/KeyframeUtils.h | 19 - dom/animation/TimingParams.h | 20 + .../file_keyframeeffect-getframes.html | 252 +- dom/apps/PermissionsTable.jsm | 5 - dom/base/AnonymousContent.cpp | 2 +- dom/base/DOMImplementation.cpp | 2 +- dom/base/Element.cpp | 24 +- dom/base/Element.h | 2 +- dom/base/FragmentOrElement.cpp | 17 +- dom/base/TextInputProcessor.cpp | 11 +- dom/base/domerr.msg | 1 - dom/base/nsAttrValue.cpp | 2 +- dom/base/nsContentList.cpp | 6 +- dom/base/nsContentSink.cpp | 2 +- dom/base/nsContentUtils.cpp | 46 +- dom/base/nsCopySupport.cpp | 2 +- dom/base/nsDOMMutationObserver.cpp | 2 +- dom/base/nsDOMWindowUtils.cpp | 103 +- dom/base/nsDocument.cpp | 82 +- dom/base/nsFocusManager.cpp | 4 +- dom/base/nsGenericDOMDataNode.cpp | 8 +- dom/base/nsGkAtomList.h | 1 - dom/base/nsGlobalWindow.cpp | 8 +- dom/base/nsIContent.h | 8 +- dom/base/nsINode.cpp | 30 +- dom/base/nsINode.h | 26 +- dom/base/nsNodeInfoManager.cpp | 4 +- dom/base/nsRange.cpp | 6 +- dom/base/nsReferencedElement.cpp | 4 +- dom/base/nsScriptElement.cpp | 2 +- dom/base/nsStyledElement.cpp | 2 +- dom/base/nsTreeSanitizer.cpp | 2 +- dom/browser-element/BrowserElementParent.cpp | 2 +- dom/camera/DOMCameraControl.cpp | 16 +- dom/canvas/CanvasRenderingContext2D.cpp | 10 +- dom/canvas/ImageBitmap.cpp | 2 +- dom/canvas/test/captureStream_common.js | 124 +- dom/canvas/test/test_capture.html | 49 +- .../test/webgl-mochitest/test_capture.html | 48 +- dom/events/AnimationEvent.cpp | 2 +- dom/events/BeforeAfterKeyboardEvent.cpp | 2 +- dom/events/ClipboardEvent.cpp | 12 +- dom/events/CommandEvent.cpp | 4 +- dom/events/CompositionEvent.cpp | 2 +- dom/events/ContentEventHandler.cpp | 25 +- dom/events/DataTransfer.cpp | 26 +- dom/events/DragEvent.cpp | 10 +- dom/events/Event.cpp | 114 +- dom/events/Event.h | 10 + dom/events/EventDispatcher.cpp | 52 +- dom/events/EventDispatcher.h | 4 +- dom/events/EventListenerManager.cpp | 22 +- dom/events/EventListenerManager.h | 4 +- dom/events/EventStateManager.cpp | 453 +-- dom/events/EventStateManager.h | 42 +- dom/events/EventTarget.cpp | 2 +- dom/events/EventTarget.h | 2 +- dom/events/FocusEvent.cpp | 6 +- dom/events/IMEContentObserver.cpp | 14 +- dom/events/IMEStateManager.cpp | 28 +- dom/events/InputEvent.cpp | 2 +- dom/events/KeyboardEvent.cpp | 37 +- dom/events/KeyboardEvent.h | 1 + dom/events/MouseEvent.cpp | 25 +- dom/events/MouseScrollEvent.cpp | 11 +- dom/events/MutationEvent.cpp | 6 +- dom/events/PointerEvent.cpp | 4 +- dom/events/SimpleGestureEvent.cpp | 4 +- dom/events/TextComposition.cpp | 16 +- dom/events/TouchEvent.cpp | 18 +- dom/events/TransitionEvent.cpp | 2 +- dom/events/UIEvent.cpp | 30 +- dom/events/UIEvent.h | 4 +- dom/events/WheelEvent.cpp | 28 +- dom/events/WheelHandlingHelper.cpp | 18 +- dom/events/XULCommandEvent.cpp | 2 +- dom/html/HTMLAllCollection.cpp | 2 +- dom/html/HTMLBodyElement.cpp | 2 +- dom/html/HTMLButtonElement.cpp | 2 +- dom/html/HTMLCanvasElement.cpp | 13 +- dom/html/HTMLExtAppElement.cpp | 185 -- dom/html/HTMLExtAppElement.h | 79 - dom/html/HTMLFormElement.cpp | 8 +- dom/html/HTMLImageElement.cpp | 2 +- dom/html/HTMLInputElement.cpp | 16 +- dom/html/HTMLLabelElement.cpp | 6 +- dom/html/HTMLMediaElement.cpp | 249 +- dom/html/HTMLMediaElement.h | 59 +- dom/html/HTMLMenuItemElement.cpp | 2 +- dom/html/HTMLScriptElement.cpp | 2 +- dom/html/HTMLSharedElement.cpp | 4 +- dom/html/moz.build | 2 - dom/html/nsDOMStringMap.cpp | 4 +- dom/html/nsFormSubmission.cpp | 2 + dom/html/nsGenericHTMLElement.cpp | 8 +- dom/html/nsGenericHTMLElement.h | 7 +- dom/html/nsHTMLDocument.cpp | 6 +- dom/html/nsTextEditorState.cpp | 2 +- dom/html/test/mochitest.ini | 1 - dom/html/test/test_extapp.html | 11 - .../conformancetest/test_runtest.html.json | 1 + dom/inputmethod/HardwareKeyHandler.cpp | 566 ++++ dom/inputmethod/HardwareKeyHandler.h | 222 ++ dom/inputmethod/Keyboard.jsm | 64 +- dom/inputmethod/MozKeyboard.js | 86 +- dom/inputmethod/moz.build | 25 +- dom/inputmethod/nsIHardwareKeyHandler.idl | 142 + dom/ipc/PBrowser.ipdl | 4 +- dom/ipc/TabChild.cpp | 16 +- dom/ipc/TabParent.cpp | 49 +- dom/ipc/TabParent.h | 4 +- dom/locales/en-US/chrome/dom/dom.properties | 2 + dom/mathml/nsMathMLElement.cpp | 2 +- dom/media/AudioCaptureStream.cpp | 31 +- dom/media/AudioCaptureStream.h | 3 + dom/media/AudioSegment.cpp | 2 - dom/media/AudioSegment.h | 21 +- dom/media/AudioStreamTrack.h | 18 +- dom/media/CanvasCaptureMediaStream.cpp | 42 +- dom/media/CanvasCaptureMediaStream.h | 12 +- dom/media/DOMMediaStream.cpp | 704 +++-- dom/media/DOMMediaStream.h | 289 +- dom/media/FrameStatistics.h | 17 +- dom/media/GraphDriver.cpp | 2 +- dom/media/MediaDecoder.cpp | 4 + dom/media/MediaDecoder.h | 7 + dom/media/MediaDecoderReader.h | 14 +- dom/media/MediaDecoderReaderWrapper.cpp | 296 ++ dom/media/MediaDecoderReaderWrapper.h | 67 + dom/media/MediaDecoderStateMachine.cpp | 214 +- dom/media/MediaDecoderStateMachine.h | 38 +- dom/media/MediaDevices.cpp | 2 +- dom/media/MediaFormatReader.cpp | 33 +- dom/media/MediaFormatReader.h | 6 +- dom/media/MediaManager.cpp | 399 ++- dom/media/MediaManager.h | 21 +- dom/media/MediaRecorder.cpp | 494 ++- dom/media/MediaRecorder.h | 7 +- dom/media/MediaSegment.h | 66 +- dom/media/MediaStreamGraph.cpp | 400 ++- dom/media/MediaStreamGraph.h | 285 +- dom/media/MediaStreamGraphImpl.h | 15 + dom/media/MediaStreamTrack.cpp | 361 ++- dom/media/MediaStreamTrack.h | 345 ++- dom/media/PeerConnection.js | 8 +- dom/media/PrincipalChangeObserver.h | 30 + dom/media/StreamBuffer.h | 4 + dom/media/TrackUnionStream.cpp | 169 +- dom/media/TrackUnionStream.h | 15 + dom/media/VideoFrameContainer.cpp | 49 +- dom/media/VideoFrameContainer.h | 23 +- dom/media/VideoSegment.cpp | 11 +- dom/media/VideoSegment.h | 22 +- dom/media/VideoStreamTrack.h | 18 +- dom/media/encoder/MediaEncoder.cpp | 62 +- dom/media/encoder/MediaEncoder.h | 46 +- dom/media/encoder/TrackEncoder.cpp | 29 +- dom/media/encoder/TrackEncoder.h | 6 +- dom/media/encoder/VorbisTrackEncoder.cpp | 2 +- dom/media/gtest/TestVideoSegment.cpp | 4 +- dom/media/gtest/TestVideoTrackEncoder.cpp | 5 +- dom/media/imagecapture/CaptureTask.cpp | 114 +- dom/media/imagecapture/CaptureTask.h | 48 +- dom/media/imagecapture/ImageCapture.cpp | 40 +- dom/media/mediasink/DecodedStream.cpp | 146 +- dom/media/mediasink/DecodedStream.h | 11 +- dom/media/moz.build | 2 + dom/media/platforms/apple/AppleVDADecoder.cpp | 20 +- dom/media/standalone/moz.build | 1 - dom/media/systemservices/LoadManager.h | 22 +- dom/media/test/mochitest.ini | 19 +- dom/media/test/test_bug879717.html | 2 +- .../test/test_mediarecorder_creation.html | 2 +- .../test_mediarecorder_creation_fail.html | 2 +- .../test_mediarecorder_getencodeddata.html | 3 +- .../test/test_mediarecorder_mp4_support.html | 16 + ...mediarecorder_record_4ch_audiocontext.html | 2 +- ...est_mediarecorder_record_audiocontext.html | 2 +- ...arecorder_record_canvas_captureStream.html | 73 + ...rder_record_changing_video_resolution.html | 69 + ...diarecorder_record_getdata_afterstart.html | 2 +- ...t_mediarecorder_record_immediate_stop.html | 2 +- ...est_mediarecorder_record_no_timeslice.html | 2 +- .../test/test_mediarecorder_record_nosrc.html | 33 - .../test_mediarecorder_record_timeslice.html | 103 +- .../test_mediarecorder_state_transition.html | 2 +- .../test_mediarecorder_unsupported_src.html | 24 +- .../test/test_mediarecorder_webm_support.html | 18 + ...st_mediatrack_consuming_mediaresource.html | 2 +- ...test_mediatrack_consuming_mediastream.html | 2 +- dom/media/test/test_mediatrack_events.html | 2 +- .../test/test_mediatrack_parsing_ogg.html | 2 +- .../test/test_mediatrack_replay_from_end.html | 2 +- .../test/test_multiple_mediastreamtracks.html | 2 +- dom/media/test/test_streams_autoplay.html | 2 +- .../test/test_streams_individual_pause.html | 6 +- dom/media/test/test_streams_tracks.html | 2 +- dom/media/tests/mochitest/head.js | 41 + .../tests/mochitest/mediaStreamPlayback.js | 139 +- dom/media/tests/mochitest/mochitest.ini | 14 +- ...test_getUserMedia_addTrackRemoveTrack.html | 161 + .../test_getUserMedia_bug1223696.html | 48 + .../test_getUserMedia_loadedmetadata.html | 36 + .../test_getUserMedia_mediaStreamClone.html | 249 ++ ..._getUserMedia_mediaStreamConstructors.html | 137 + ...st_getUserMedia_mediaStreamTrackClone.html | 163 + ...eerConnection_captureStream_canvas_2d.html | 6 +- ...Connection_captureStream_canvas_webgl.html | 6 +- ...tion_multiple_captureStream_canvas_2d.html | 100 + .../test_peerConnection_trackDisabling.html | 96 + ..._peerConnection_trackDisabling_clones.html | 147 + ...nection_verifyAudioAfterRenegotiation.html | 123 + dom/media/webaudio/AudioNode.cpp | 4 +- .../MediaStreamAudioDestinationNode.cpp | 13 +- .../webaudio/MediaStreamAudioSourceNode.cpp | 96 +- .../webaudio/MediaStreamAudioSourceNode.h | 22 +- dom/media/webaudio/test/test_bug867104.html | 2 +- .../test_delayNodeTailWithDisconnect.html | 2 - .../test/test_delayNodeTailWithReconnect.html | 2 - .../test_mediaElementAudioSourceNode.html | 8 +- ...ediaElementAudioSourceNodeCrossOrigin.html | 34 +- ..._mediaStreamAudioSourceNodeResampling.html | 9 +- .../test/test_oscillatorNodeStart.html | 4 +- dom/media/webaudio/test/webaudio.js | 3 +- dom/media/webrtc/MediaEngine.h | 47 +- .../webrtc/MediaEngineCameraVideoSource.cpp | 5 +- .../webrtc/MediaEngineCameraVideoSource.h | 10 +- dom/media/webrtc/MediaEngineDefault.cpp | 17 +- dom/media/webrtc/MediaEngineDefault.h | 18 +- .../webrtc/MediaEngineGonkVideoSource.cpp | 30 +- dom/media/webrtc/MediaEngineGonkVideoSource.h | 4 +- .../webrtc/MediaEngineRemoteVideoSource.cpp | 22 +- .../webrtc/MediaEngineRemoteVideoSource.h | 5 +- .../webrtc/MediaEngineTabVideoSource.cpp | 9 +- dom/media/webrtc/MediaEngineTabVideoSource.h | 6 +- dom/media/webrtc/MediaEngineWebRTC.cpp | 9 +- dom/media/webrtc/MediaEngineWebRTC.h | 62 +- dom/media/webrtc/MediaEngineWebRTCAudio.cpp | 39 +- dom/media/webrtc/PeerIdentity.h | 4 +- .../recognition/SpeechRecognition.cpp | 5 +- dom/media/webspeech/synth/nsSpeechTask.cpp | 3 +- dom/plugins/base/nsPluginInstanceOwner.cpp | 41 +- dom/smil/TimeEvent.cpp | 2 +- dom/smil/nsSMILAnimationFunction.cpp | 10 + dom/smil/nsSMILCSSProperty.cpp | 2 +- dom/smil/nsSMILCSSValueType.cpp | 20 +- dom/smil/nsSMILCSSValueType.h | 10 + dom/smil/nsSMILMappedAttribute.cpp | 4 +- dom/smil/nsSMILParserUtils.cpp | 2 +- dom/smil/nsSMILTimeValueSpec.cpp | 6 +- dom/smil/nsSMILTimedElement.cpp | 4 +- dom/svg/SVGAnimationElement.cpp | 2 +- dom/svg/SVGMPathElement.cpp | 2 +- dom/svg/SVGSVGElement.h | 4 +- dom/svg/SVGZoomEvent.cpp | 2 +- dom/svg/nsSVGElement.cpp | 2 +- .../mochitest/general/test_interfaces.html | 4 - dom/tv/TVTuner.cpp | 2 +- dom/webidl/Event.webidl | 4 + dom/webidl/ExternalAppEvent.webidl | 17 - dom/webidl/HTMLExtAppElement.webidl | 16 - dom/webidl/InputMethod.webidl | 22 +- dom/webidl/KeyboardEvent.webidl | 5 + dom/webidl/KeyframeEffect.webidl | 4 +- dom/webidl/MediaRecorder.webidl | 2 + dom/webidl/MediaStream.webidl | 2 +- dom/webidl/MediaStreamTrack.webidl | 2 +- dom/webidl/moz.build | 2 - dom/xbl/XBLChildrenElement.cpp | 2 +- dom/xbl/nsXBLBinding.cpp | 2 +- dom/xbl/nsXBLPrototypeBinding.cpp | 24 +- dom/xbl/nsXBLPrototypeHandler.cpp | 6 +- dom/xbl/nsXBLResourceLoader.cpp | 2 +- dom/xbl/nsXBLService.cpp | 8 +- dom/xbl/nsXBLWindowKeyHandler.cpp | 12 +- dom/xml/ProcessingInstruction.cpp | 2 +- .../XMLStylesheetProcessingInstruction.cpp | 2 +- dom/xml/nsXMLContentSink.cpp | 2 +- dom/xslt/base/txExpandedName.cpp | 6 +- dom/xslt/base/txNamespaceMap.cpp | 2 +- dom/xslt/base/txStringUtils.h | 2 +- dom/xslt/xml/txXMLUtils.cpp | 10 +- dom/xslt/xpath/txExpr.h | 2 +- dom/xslt/xpath/txExprParser.cpp | 10 +- dom/xslt/xpath/txMozillaXPathTreeWalker.cpp | 10 +- dom/xslt/xslt/txInstructions.cpp | 4 +- dom/xslt/xslt/txMozillaXMLOutput.cpp | 14 +- dom/xslt/xslt/txMozillaXSLTProcessor.cpp | 10 +- dom/xslt/xslt/txPatternParser.cpp | 2 +- dom/xslt/xslt/txStylesheetCompileHandlers.cpp | 4 +- dom/xslt/xslt/txXSLTPatterns.cpp | 2 +- dom/xul/XULDocument.cpp | 14 +- dom/xul/nsXULElement.cpp | 12 +- dom/xul/nsXULPrototypeDocument.cpp | 2 +- dom/xul/templates/nsXULContentBuilder.cpp | 2 +- dom/xul/templates/nsXULSortService.cpp | 6 +- dom/xul/templates/nsXULTemplateBuilder.cpp | 30 +- .../nsXULTemplateQueryProcessorRDF.cpp | 14 +- .../nsXULTemplateQueryProcessorStorage.cpp | 2 +- .../nsXULTemplateQueryProcessorXML.cpp | 2 +- dom/xul/templates/nsXULTreeBuilder.cpp | 4 +- editor/composer/nsComposerCommands.cpp | 6 +- editor/composer/nsEditorSpellCheck.cpp | 2 +- editor/libeditor/nsEditor.cpp | 14 +- editor/libeditor/nsEditorEventListener.cpp | 2 +- editor/libeditor/nsHTMLAnonymousUtils.cpp | 8 +- editor/libeditor/nsHTMLCSSUtils.cpp | 19 +- editor/libeditor/nsHTMLEditRules.cpp | 8 +- editor/libeditor/nsHTMLEditUtils.cpp | 19 - editor/libeditor/nsHTMLEditUtils.h | 4 - editor/libeditor/nsHTMLEditor.cpp | 12 +- editor/libeditor/nsHTMLEditor.h | 6 +- editor/libeditor/nsHTMLEditorStyle.cpp | 98 +- editor/libeditor/tests/test_bug1140105.html | 70 + embedding/browser/nsDocShellTreeOwner.cpp | 6 +- gfx/layers/ImageContainer.h | 2 +- gfx/layers/apz/src/APZCTreeManager.cpp | 49 +- gfx/layers/apz/src/AsyncPanZoomController.cpp | 6 +- gfx/layers/apz/src/Axis.cpp | 6 +- gfx/layers/apz/util/APZCCallbackHelper.cpp | 34 +- gfx/layers/apz/util/APZEventState.cpp | 10 +- gfx/layers/client/TextureClient.h | 2 +- .../composite/AsyncCompositionManager.cpp | 3 +- gfx/src/ArrayView.h | 46 + gfx/src/moz.build | 1 + gfx/src/nsDeviceContext.cpp | 4 +- gfx/src/nsRegion.h | 10 +- gfx/src/nsThebesFontEnumerator.cpp | 4 +- gfx/tests/gtest/TestArrayView.cpp | 18 + gfx/tests/gtest/gfxFontSelectionTests.h | 4 +- gfx/tests/gtest/gfxTextRunPerfTest.cpp | 2 +- gfx/tests/gtest/gfxWordCacheTest.cpp | 141 - gfx/tests/gtest/moz.build | 3 +- gfx/thebes/DrawMode.h | 13 +- gfx/thebes/gfxAndroidPlatform.cpp | 2 +- gfx/thebes/gfxAndroidPlatform.h | 2 +- gfx/thebes/gfxCoreTextShaper.cpp | 7 +- gfx/thebes/gfxCoreTextShaper.h | 2 +- gfx/thebes/gfxDWriteFontList.cpp | 2 +- gfx/thebes/gfxDWriteFontList.h | 2 +- gfx/thebes/gfxFT2Fonts.cpp | 2 +- gfx/thebes/gfxFT2Fonts.h | 2 +- gfx/thebes/gfxFont.cpp | 150 +- gfx/thebes/gfxFont.h | 75 +- gfx/thebes/gfxFontEntry.cpp | 21 +- gfx/thebes/gfxFontEntry.h | 16 +- gfx/thebes/gfxFontconfigFonts.cpp | 6 +- gfx/thebes/gfxFontconfigFonts.h | 2 +- gfx/thebes/gfxGDIFont.cpp | 2 +- gfx/thebes/gfxGDIFont.h | 2 +- gfx/thebes/gfxGraphiteShaper.cpp | 2 +- gfx/thebes/gfxGraphiteShaper.h | 2 +- gfx/thebes/gfxHarfBuzzShaper.cpp | 135 +- gfx/thebes/gfxHarfBuzzShaper.h | 11 +- gfx/thebes/gfxMacFont.cpp | 2 +- gfx/thebes/gfxMacFont.h | 2 +- gfx/thebes/gfxMacPlatformFontList.h | 2 +- gfx/thebes/gfxMacPlatformFontList.mm | 2 +- gfx/thebes/gfxPlatform.h | 10 +- gfx/thebes/gfxPlatformFontList.cpp | 11 +- gfx/thebes/gfxPlatformFontList.h | 8 +- gfx/thebes/gfxPlatformGtk.cpp | 2 +- gfx/thebes/gfxPlatformGtk.h | 2 +- gfx/thebes/gfxPlatformMac.cpp | 2 +- gfx/thebes/gfxPlatformMac.h | 7 +- gfx/thebes/gfxSVGGlyphs.cpp | 9 +- gfx/thebes/gfxSVGGlyphs.h | 4 +- gfx/thebes/gfxScriptItemizer.cpp | 22 +- gfx/thebes/gfxScriptItemizer.h | 14 +- gfx/thebes/gfxTextRun.cpp | 37 +- gfx/thebes/gfxTextRun.h | 26 +- gfx/thebes/gfxWindowsPlatform.cpp | 2 +- gfx/thebes/gfxWindowsPlatform.h | 2 +- .../hyphenation/glue/nsHyphenationManager.cpp | 10 +- intl/locale/nsLanguageAtomService.cpp | 8 +- .../tools/genUnicodePropertyData.pl | 14 +- intl/unicharutil/util/nsUnicodeProperties.cpp | 14 +- intl/unicharutil/util/nsUnicodeProperties.h | 4 +- .../util/nsUnicodePropertyData.cpp | 2 +- intl/unicharutil/util/nsUnicodeScriptCodes.h | 346 ++- ipc/chromium/src/base/message_loop.h | 2 +- ipc/chromium/src/base/task.h | 2 +- ipc/chromium/src/base/tracked.h | 4 +- js/xpconnect/src/XPCConvert.cpp | 2 +- js/xpconnect/src/XPCJSRuntime.cpp | 2 +- layout/base/AccessibleCaret.h | 4 +- layout/base/AccessibleCaretEventHub.cpp | 204 +- layout/base/AccessibleCaretManager.cpp | 135 +- layout/base/AccessibleCaretManager.h | 57 +- layout/base/PositionedEventTargeting.cpp | 4 +- layout/base/RestyleTracker.cpp | 12 +- layout/base/TouchManager.cpp | 23 +- .../doc/AccessibleCaretEventHubStates.png | Bin 92942 -> 97614 bytes .../gtest/TestAccessibleCaretEventHub.cpp | 164 +- .../base/gtest/TestAccessibleCaretManager.cpp | 302 +- layout/base/nsCSSFrameConstructor.cpp | 15 +- layout/base/nsCSSRendering.cpp | 4 +- layout/base/nsDisplayList.cpp | 6 - layout/base/nsDocumentViewer.cpp | 4 +- layout/base/nsFrameManager.cpp | 8 +- layout/base/nsIPresShell.h | 15 + layout/base/nsLayoutUtils.cpp | 16 +- layout/base/nsLayoutUtils.h | 4 - layout/base/nsPresContext.cpp | 4 +- layout/base/nsPresContext.h | 3 + layout/base/nsPresShell.cpp | 314 +- layout/base/nsPresShell.h | 25 +- layout/build/nsLayoutModule.cpp | 20 + layout/build/nsLayoutStatics.cpp | 3 - layout/forms/nsComboboxControlFrame.cpp | 2 +- layout/forms/nsListControlFrame.cpp | 4 +- layout/forms/nsListControlFrame.h | 3 - layout/forms/nsNumberControlFrame.cpp | 10 +- layout/forms/nsRangeFrame.cpp | 10 +- layout/generic/MathMLTextRunFactory.cpp | 4 +- layout/generic/TextOverflow.cpp | 4 +- layout/generic/nsAbsoluteContainingBlock.cpp | 22 +- layout/generic/nsContainerFrame.cpp | 86 +- layout/generic/nsContainerFrame.h | 26 +- layout/generic/nsFrame.cpp | 2 +- layout/generic/nsFrameSetFrame.cpp | 8 +- layout/generic/nsFrameState.cpp | 1 + layout/generic/nsFrameStateBits.h | 14 + layout/generic/nsGfxScrollFrame.cpp | 26 +- layout/generic/nsGfxScrollFrame.h | 7 - layout/generic/nsGridContainerFrame.cpp | 2750 ++++++++++++++--- layout/generic/nsGridContainerFrame.h | 662 +--- layout/generic/nsHTMLReflowState.cpp | 75 +- layout/generic/nsHTMLReflowState.h | 17 +- layout/generic/nsIFrame.h | 24 +- layout/generic/nsIScrollableFrame.h | 2 - layout/generic/nsIStatefulFrame.h | 8 + layout/generic/nsImageMap.cpp | 2 +- layout/generic/nsLineLayout.cpp | 6 +- layout/generic/nsPluginFrame.cpp | 4 +- layout/generic/nsSelection.cpp | 2 +- layout/generic/nsSplittableFrame.cpp | 14 + layout/generic/nsSplittableFrame.h | 12 + layout/generic/nsTextFrame.cpp | 277 +- layout/generic/nsTextFrame.h | 8 +- layout/generic/nsTextRunTransformations.cpp | 2 +- layout/generic/test/test_bug666225.html | 2 +- layout/inspector/inDOMUtils.cpp | 6 +- layout/mathml/nsMathMLChar.cpp | 5 +- layout/mathml/nsMathMLFrame.cpp | 3 +- layout/mathml/nsMathMLmencloseFrame.cpp | 3 +- layout/mathml/nsMathMLmfracFrame.cpp | 3 +- layout/media/symbols.def.in | 2 + layout/printing/nsPrintPreviewListener.cpp | 2 +- layout/reftests/bugs/1238243-1-notref.html | 17 + layout/reftests/bugs/1238243-1.html | 18 + layout/reftests/bugs/1238243-2-ref.html | 16 + layout/reftests/bugs/1238243-2.html | 17 + layout/reftests/bugs/1239564-ref.html | 21 + layout/reftests/bugs/1239564.html | 29 + layout/reftests/bugs/1260543-1-ref.html | 10 + layout/reftests/bugs/1260543-1.html | 35 + layout/reftests/bugs/reftest.list | 7 +- .../css-grid/grid-fragmentation-001-ref.html | 161 + .../css-grid/grid-fragmentation-001.html | 122 + .../css-grid/grid-fragmentation-002-ref.html | 135 + .../css-grid/grid-fragmentation-002.html | 110 + .../css-grid/grid-fragmentation-003-ref.html | 138 + .../css-grid/grid-fragmentation-003.html | 105 + .../css-grid/grid-fragmentation-004-ref.html | 166 + .../css-grid/grid-fragmentation-004.html | 115 + .../css-grid/grid-fragmentation-005-ref.html | 149 + .../css-grid/grid-fragmentation-005.html | 133 + .../css-grid/grid-fragmentation-006-ref.html | 116 + .../css-grid/grid-fragmentation-006.html | 108 + .../css-grid/grid-fragmentation-007-ref.html | 134 + .../css-grid/grid-fragmentation-007.html | 104 + .../css-grid/grid-fragmentation-008-ref.html | 132 + .../css-grid/grid-fragmentation-008.html | 95 + .../css-grid/grid-fragmentation-009-ref.html | 161 + .../css-grid/grid-fragmentation-009.html | 144 + .../css-grid/grid-fragmentation-010-ref.html | 48 + .../css-grid/grid-fragmentation-010.html | 43 + .../css-grid/grid-fragmentation-011-ref.html | 49 + .../css-grid/grid-fragmentation-011.html | 44 + .../css-grid/grid-fragmentation-012-ref.html | 49 + .../css-grid/grid-fragmentation-012.html | 46 + .../css-grid/grid-fragmentation-013-ref.html | 52 + .../css-grid/grid-fragmentation-013.html | 44 + .../css-grid/grid-fragmentation-014-ref.html | 39 + .../css-grid/grid-fragmentation-014.html | 44 + .../css-grid/grid-fragmentation-015-ref.html | 128 + .../css-grid/grid-fragmentation-015.html | 134 + .../css-grid/grid-fragmentation-016-ref.html | 167 + .../css-grid/grid-fragmentation-016.html | 128 + .../css-grid/grid-fragmentation-017-ref.html | 139 + .../css-grid/grid-fragmentation-017.html | 107 + .../css-grid/grid-fragmentation-018-ref.html | 161 + .../css-grid/grid-fragmentation-018.html | 145 + .../css-grid/grid-fragmentation-019-ref.html | 150 + .../css-grid/grid-fragmentation-019.html | 115 + .../css-grid/grid-fragmentation-020-ref.html | 208 ++ .../css-grid/grid-fragmentation-020.html | 215 ++ .../css-grid/grid-fragmentation-021-ref.html | 188 ++ .../css-grid/grid-fragmentation-021.html | 133 + .../css-grid/grid-fragmentation-022.html | 104 + .../css-grid/grid-fragmentation-023-ref.html | 138 + .../css-grid/grid-fragmentation-023.html | 105 + .../css-grid/grid-fragmentation-024-ref.html | 172 ++ .../css-grid/grid-fragmentation-024.html | 126 + .../css-grid/grid-fragmentation-025-ref.html | 177 ++ .../css-grid/grid-fragmentation-025.html | 126 + .../css-grid/grid-fragmentation-026-ref.html | 166 + .../css-grid/grid-fragmentation-026.html | 134 + .../css-grid/grid-fragmentation-027.html | 127 + .../css-grid/grid-fragmentation-028-ref.html | 174 ++ .../css-grid/grid-fragmentation-028.html | 125 + .../css-grid/grid-fragmentation-029-ref.html | 207 ++ .../css-grid/grid-fragmentation-029.html | 141 + .../css-grid/grid-fragmentation-030-ref.html | 111 + .../css-grid/grid-fragmentation-030.html | 114 + .../css-grid/grid-fragmentation-031-ref.html | 115 + .../css-grid/grid-fragmentation-031.html | 114 + .../css-grid/grid-fragmentation-dyn1-002.html | 24 + .../css-grid/grid-fragmentation-dyn1-006.html | 24 + .../css-grid/grid-fragmentation-dyn1-015.html | 24 + .../css-grid/grid-fragmentation-dyn1-016.html | 24 + .../css-grid/grid-fragmentation-dyn1-019.html | 24 + .../css-grid/grid-fragmentation-dyn1-020.html | 24 + .../css-grid/grid-fragmentation-dyn1-021.html | 24 + .../css-grid/grid-fragmentation-dyn1-023.html | 24 + .../css-grid/grid-fragmentation-dyn1-024.html | 24 + .../css-grid/grid-fragmentation-dyn1-026.html | 24 + .../css-grid/grid-fragmentation-dyn1-027.html | 24 + .../css-grid/grid-fragmentation-dyn1-028.html | 24 + .../css-grid/grid-fragmentation-dyn1-029.html | 24 + .../css-grid/grid-fragmentation-dyn2-018.html | 24 + .../css-grid/grid-fragmentation-dyn2-019.html | 24 + .../css-grid/grid-fragmentation-dyn2-020.html | 24 + .../css-grid/grid-fragmentation-dyn2-021.html | 24 + .../css-grid/grid-fragmentation-dyn2-022.html | 24 + .../css-grid/grid-fragmentation-dyn2-023.html | 24 + .../css-grid/grid-fragmentation-dyn2-025.html | 24 + .../css-grid/grid-fragmentation-dyn2-026.html | 24 + .../css-grid/grid-fragmentation-dyn2-027.html | 24 + .../css-grid/grid-fragmentation-dyn2-028.html | 24 + .../css-grid/grid-fragmentation-dyn2-029.html | 24 + .../css-grid/grid-fragmentation-dyn2-030.html | 24 + .../css-grid/grid-fragmentation-dyn2-031.html | 24 + .../css-grid/grid-fragmentation-dyn3-001.html | 24 + .../css-grid/grid-fragmentation-dyn3-002.html | 24 + .../css-grid/grid-fragmentation-dyn3-003.html | 24 + .../css-grid/grid-fragmentation-dyn3-007.html | 24 + .../css-grid/grid-fragmentation-dyn3-009.html | 24 + .../css-grid/grid-fragmentation-dyn3-017.html | 24 + .../css-grid/grid-fragmentation-dyn3-019.html | 24 + .../css-grid/grid-fragmentation-dyn3-021.html | 24 + .../css-grid/grid-fragmentation-dyn3-023.html | 24 + .../css-grid/grid-fragmentation-dyn3-026.html | 24 + .../css-grid/grid-fragmentation-dyn3-027.html | 24 + .../css-grid/grid-fragmentation-dyn3-028.html | 24 + .../css-grid/grid-fragmentation-dyn4-001.html | 24 + .../css-grid/grid-fragmentation-dyn4-004.html | 24 + .../css-grid/grid-fragmentation-dyn4-005.html | 24 + .../css-grid/grid-fragmentation-dyn4-015.html | 24 + .../css-grid/grid-fragmentation-dyn4-019.html | 24 + .../css-grid/grid-fragmentation-dyn4-021.html | 24 + .../css-grid/grid-fragmentation-dyn4-023.html | 24 + .../css-grid/grid-fragmentation-dyn4-026.html | 24 + .../css-grid/grid-fragmentation-dyn4-027.html | 24 + .../css-grid/grid-fragmentation-dyn4-028.html | 24 + .../css-grid/grid-fragmentation-dyn5-005.html | 24 + .../css-grid/grid-fragmentation-dyn5-007.html | 24 + .../css-grid/grid-fragmentation-dyn5-008.html | 24 + .../css-grid/grid-fragmentation-dyn5-016.html | 24 + .../css-grid/grid-fragmentation-dyn5-019.html | 24 + .../css-grid/grid-fragmentation-dyn5-021.html | 24 + .../css-grid/grid-fragmentation-dyn5-023.html | 24 + .../css-grid/grid-fragmentation-dyn5-026.html | 24 + .../css-grid/grid-fragmentation-dyn5-027.html | 24 + .../css-grid/grid-fragmentation-dyn5-028.html | 24 + layout/reftests/css-grid/reftest.list | 93 + layout/reftests/css-grid/support/dyn.js | 127 + layout/reftests/reftest.list | 3 + layout/reftests/text-stroke/reftest.list | 7 + .../webkit-text-stroke-property-001-ref.html | 15 + .../webkit-text-stroke-property-001.html | 17 + .../webkit-text-stroke-property-002-ref.html | 15 + .../webkit-text-stroke-property-002.html | 17 + .../webkit-text-stroke-property-003-ref.html | 15 + .../webkit-text-stroke-property-003.html | 17 + .../webkit-text-stroke-property-004-ref.html | 15 + .../webkit-text-stroke-property-004.html | 17 + .../webkit-text-stroke-property-005-ref.html | 15 + .../webkit-text-stroke-property-005.html | 26 + layout/style/AnimationCommon.h | 7 - layout/style/Declaration.cpp | 22 + layout/style/StyleAnimationValue.cpp | 640 +++- layout/style/StyleAnimationValue.h | 49 +- layout/style/StyleRule.cpp | 14 +- layout/style/nsAnimationManager.cpp | 593 ++-- layout/style/nsCSSParser.cpp | 44 +- layout/style/nsCSSPropList.h | 34 +- layout/style/nsCSSProps.cpp | 6 + layout/style/nsCSSRuleProcessor.cpp | 4 +- layout/style/nsCSSValue.cpp | 29 + layout/style/nsCSSValue.h | 15 +- layout/style/nsComputedDOMStyle.cpp | 52 +- layout/style/nsComputedDOMStyle.h | 3 +- layout/style/nsComputedDOMStylePropertyList.h | 3 + layout/style/nsDOMCSSAttrDeclaration.cpp | 2 +- layout/style/nsRuleNode.cpp | 45 +- layout/style/nsStyleContext.cpp | 11 +- layout/style/nsStyleContext.h | 20 +- layout/style/nsStyleSet.cpp | 2 +- layout/style/nsStyleStruct.cpp | 35 +- layout/style/nsStyleStruct.h | 8 + layout/style/nsTransitionManager.cpp | 2 +- ...ile_animations_effect_timing_duration.html | 5 +- layout/style/test/property_database.js | 27 + layout/style/test/test_animations.html | 10 +- layout/style/test/test_animations_omta.html | 10 +- .../style/test/test_transitions_events.html | 22 - .../test/test_transitions_per_property.html | 290 +- .../test/test_transitions_step_functions.html | 10 +- layout/svg/SVGTextFrame.cpp | 8 +- layout/svg/nsSVGFilterFrame.cpp | 2 +- layout/svg/nsSVGGradientFrame.cpp | 2 +- layout/svg/nsSVGOuterSVGFrame.cpp | 4 +- layout/svg/nsSVGPatternFrame.cpp | 2 +- layout/svg/nsSVGUtils.cpp | 1 - layout/svg/nsSVGUtils.h | 3 - layout/tools/reftest/remotereftest.py | 3 + layout/xul/BoxObject.cpp | 2 +- layout/xul/nsBoxFrame.cpp | 6 +- layout/xul/nsImageBoxFrame.cpp | 8 +- layout/xul/nsMenuBarListener.cpp | 2 +- layout/xul/nsMenuPopupFrame.cpp | 2 +- layout/xul/nsResizerFrame.cpp | 7 +- layout/xul/nsTextBoxFrame.cpp | 4 +- layout/xul/nsTitleBarFrame.cpp | 4 +- layout/xul/nsXULPopupManager.cpp | 46 +- layout/xul/nsXULPopupManager.h | 3 + layout/xul/nsXULTooltipListener.cpp | 2 +- layout/xul/tree/nsTreeBodyFrame.cpp | 10 +- layout/xul/tree/nsTreeColumns.cpp | 2 +- layout/xul/tree/nsTreeUtils.cpp | 2 +- .../src/media-conduit/AudioConduit.cpp | 18 +- .../src/mediapipeline/MediaPipeline.cpp | 191 +- .../src/mediapipeline/MediaPipeline.h | 231 +- .../peerconnection/MediaPipelineFactory.cpp | 27 +- .../src/peerconnection/PeerConnectionImpl.cpp | 392 ++- .../src/peerconnection/PeerConnectionImpl.h | 19 +- .../peerconnection/PeerConnectionMedia.cpp | 105 +- .../src/peerconnection/PeerConnectionMedia.h | 110 +- .../webrtc/signaling/test/FakeMediaStreams.h | 155 +- .../signaling/test/FakeMediaStreamsImpl.h | 25 +- media/webrtc/signaling/test/FakePCObserver.h | 4 +- .../signaling/test/mediapipeline_unittest.cpp | 12 +- modules/libpref/init/all.js | 19 +- netwerk/dns/nsIDNService.cpp | 59 +- netwerk/dns/nsIDNService.h | 4 +- .../protocol/websocket/WebSocketChannel.cpp | 25 +- .../streamconv/nsStreamConverterService.cpp | 2 +- parser/html/nsHtml5Atom.cpp | 7 - parser/html/nsHtml5StringParser.cpp | 2 +- parser/htmlparser/nsElementTable.cpp | 4 - parser/htmlparser/nsHTMLTagList.h | 1 - parser/htmlparser/nsHTMLTags.cpp | 6 +- rdf/base/nsRDFContentSink.cpp | 4 +- rdf/base/nsRDFXMLSerializer.cpp | 6 +- testing/web-platform/meta/MANIFEST.json | 4 + ...bkit-text-fill-color-property-002.html.ini | 3 + ...bkit-text-fill-color-property-003.html.ini | 3 + ...bkit-text-fill-color-property-004.html.ini | 3 + ...bkit-text-fill-color-property-005.html.ini | 3 + ...bkit-text-fill-color-property-006.html.ini | 3 + .../keyframe-effect/constructor.html.ini | 30 +- .../keyframe-effect/effect-easing.html.ini | 14 - .../keyframe-effect/setFrames.html.ini | 29 + ...bkit-text-fill-color-property-002-ref.html | 25 + .../webkit-text-fill-color-property-002.html | 29 + ...bkit-text-fill-color-property-003-ref.html | 28 + .../webkit-text-fill-color-property-003.html | 35 + ...bkit-text-fill-color-property-004-ref.html | 34 + .../webkit-text-fill-color-property-004.html | 38 + ...bkit-text-fill-color-property-005-ref.html | 13 + .../webkit-text-fill-color-property-005.html | 17 + ...bkit-text-fill-color-property-006-ref.html | 24 + .../webkit-text-fill-color-property-006.html | 28 + .../animation-types/discrete-animation.html | 136 + .../keyframe-effect/constructor.html | 398 +-- .../keyframe-effect/effect-easing.html | 282 ++ .../keyframe-effect/keyframe-handling.html | 40 + .../keyframe-effect/setFrames.html | 50 + .../resources/keyframe-utils.js | 522 ++++ .../autocomplete/nsAutoCompleteController.cpp | 1 - toolkit/components/telemetry/Histograms.json | 9 + .../typeaheadfind/nsTypeAheadFind.cpp | 4 +- .../components/viewconfig/content/config.js | 2 - .../tests/chrome/test_mousecapture.xul | 2 +- toolkit/content/widgets/browser.xml | 5 - view/nsView.cpp | 6 +- widget/BasicEvents.h | 142 +- widget/ContentCache.cpp | 2 +- widget/ContentEvents.h | 20 +- widget/EventMessageList.h | 4 +- widget/GfxInfoBase.cpp | 1 - widget/InputData.cpp | 185 +- widget/InputData.h | 17 +- widget/MiscEvents.h | 10 +- widget/MouseEvents.h | 198 +- widget/PuppetWidget.cpp | 23 +- widget/PuppetWidget.h | 4 +- widget/TextEventDispatcher.cpp | 4 +- widget/TextEventDispatcher.h | 2 +- widget/TextEvents.h | 24 +- widget/TextRange.h | 1 - widget/TouchEvents.h | 20 +- widget/WidgetEventImpl.cpp | 18 +- widget/cocoa/SwipeTracker.mm | 6 +- widget/cocoa/TextInputHandler.mm | 18 +- widget/cocoa/nsChildView.h | 8 +- widget/cocoa/nsChildView.mm | 61 +- widget/cocoa/nsCocoaUtils.mm | 4 +- widget/cocoa/nsCocoaWindow.h | 2 +- widget/cocoa/nsCocoaWindow.mm | 5 +- widget/cocoa/nsDeviceContextSpecX.mm | 2 +- widget/cocoa/nsMenuBarX.h | 27 +- widget/cocoa/nsMenuBarX.mm | 127 +- widget/cocoa/nsMenuGroupOwnerX.h | 2 - widget/cocoa/nsMenuItemIconX.h | 2 +- widget/cocoa/nsMenuItemX.h | 2 +- widget/cocoa/nsMenuItemX.mm | 6 +- widget/cocoa/nsMenuX.h | 7 +- widget/cocoa/nsMenuX.mm | 51 +- widget/cocoa/nsScreenManagerCocoa.h | 2 +- widget/cocoa/nsSystemStatusBarCocoa.h | 2 +- widget/gonk/nativewindow/GonkConsumerBaseKK.h | 2 +- widget/gonk/nativewindow/GonkNativeWindowKK.h | 6 +- widget/gonk/nsAppShell.cpp | 4 +- widget/gonk/nsWindow.cpp | 20 +- widget/gonk/nsWindow.h | 5 +- widget/gtk/IMContextWrapper.cpp | 2 +- widget/gtk/IMContextWrapper.h | 1 - widget/gtk/{gtk3drawing.c => gtk3drawing.cpp} | 251 +- widget/gtk/gtkdrawing.h | 4 + widget/gtk/moz.build | 5 +- widget/gtk/mozgtk/mozgtk.c | 4 + widget/gtk/nsDragService.h | 2 +- widget/gtk/nsGtkKeyUtils.cpp | 44 +- widget/gtk/nsImageToPixbuf.cpp | 1 - widget/gtk/nsLookAndFeel.cpp | 125 +- widget/gtk/nsScreenManagerGtk.cpp | 3 +- widget/gtk/nsSound.cpp | 1 - widget/gtk/nsWidgetFactory.cpp | 1 - widget/gtk/nsWindow.cpp | 166 +- widget/gtk/nsWindow.h | 21 +- widget/moz.build | 4 +- widget/nsBaseDragService.cpp | 10 +- widget/nsBaseWidget.cpp | 32 +- widget/nsBaseWidget.h | 11 +- widget/nsGUIEventIPC.h | 94 +- widget/nsIWidget.h | 14 +- widget/nsPrimitiveHelpers.cpp | 1 - widget/nsPrintOptionsImpl.cpp | 2 +- widget/nsShmImage.cpp | 196 +- widget/nsShmImage.h | 31 +- widget/nsTransferable.cpp | 1 - widget/qt/nsWindow.cpp | 29 +- widget/qt/nsWindow.h | 1 - widget/uikit/nsWindow.mm | 14 +- widget/windows/AudioSession.cpp | 2 +- widget/windows/AudioSession.h | 1 - widget/windows/JumpListBuilder.cpp | 1 - widget/windows/JumpListItem.h | 2 +- widget/windows/KeyboardLayout.cpp | 2 +- widget/windows/KeyboardLayout.h | 2 +- widget/windows/TSFTextStore.h | 2 +- widget/windows/TaskbarPreview.h | 2 +- widget/windows/TaskbarPreviewButton.h | 2 +- widget/windows/WidgetTraceEvent.cpp | 2 +- widget/windows/WinMouseScrollHandler.cpp | 42 +- widget/windows/WinTaskbar.cpp | 2 +- widget/windows/WinUtils.h | 1 - widget/windows/nsDataObjCollection.h | 2 +- widget/windows/nsDeviceContextSpecWin.cpp | 2 +- widget/windows/nsDragService.cpp | 3 +- widget/windows/nsFilePicker.h | 1 - widget/windows/nsNativeDragTarget.cpp | 6 +- widget/windows/nsNativeThemeWin.cpp | 5 - widget/windows/nsWinGesture.cpp | 24 +- widget/windows/nsWindow.cpp | 49 +- widget/windows/nsWindow.h | 2 +- widget/windows/nsWindowBase.cpp | 17 +- widget/windows/nsWindowBase.h | 8 +- widget/windows/nsWindowGfx.cpp | 2 +- xpcom/base/ErrorList.h | 1 - xpcom/base/nscore.h | 6 - xpcom/ds/nsAtomService.cpp | 35 +- xpcom/ds/nsAtomTable.cpp | 43 +- xpcom/ds/nsIAtom.idl | 38 +- xpcom/ds/nsIAtomService.idl | 14 +- xpcom/io/nsDirectoryService.cpp | 2 +- xpcom/string/nsReadableUtils.h | 2 +- xpcom/string/nsTSubstring.h | 6 +- xpcom/system/moz.build | 1 - xpcom/tests/TestAtoms.cpp | 278 -- xpcom/tests/TestStaticAtoms.cpp | 8 +- xpcom/tests/gtest/TestAtoms.cpp | 178 ++ xpcom/tests/gtest/moz.build | 1 + xpcom/tests/moz.build | 5 - 836 files changed, 28642 insertions(+), 9297 deletions(-) delete mode 100644 dom/html/HTMLExtAppElement.cpp delete mode 100644 dom/html/HTMLExtAppElement.h delete mode 100644 dom/html/test/test_extapp.html create mode 100644 dom/inputmethod/HardwareKeyHandler.cpp create mode 100644 dom/inputmethod/HardwareKeyHandler.h create mode 100644 dom/inputmethod/nsIHardwareKeyHandler.idl create mode 100644 dom/media/MediaDecoderReaderWrapper.cpp create mode 100644 dom/media/MediaDecoderReaderWrapper.h create mode 100644 dom/media/PrincipalChangeObserver.h create mode 100644 dom/media/test/test_mediarecorder_mp4_support.html create mode 100644 dom/media/test/test_mediarecorder_record_canvas_captureStream.html create mode 100644 dom/media/test/test_mediarecorder_record_changing_video_resolution.html delete mode 100644 dom/media/test/test_mediarecorder_record_nosrc.html create mode 100644 dom/media/test/test_mediarecorder_webm_support.html create mode 100644 dom/media/tests/mochitest/test_getUserMedia_addTrackRemoveTrack.html create mode 100644 dom/media/tests/mochitest/test_getUserMedia_bug1223696.html create mode 100644 dom/media/tests/mochitest/test_getUserMedia_loadedmetadata.html create mode 100644 dom/media/tests/mochitest/test_getUserMedia_mediaStreamClone.html create mode 100644 dom/media/tests/mochitest/test_getUserMedia_mediaStreamConstructors.html create mode 100644 dom/media/tests/mochitest/test_getUserMedia_mediaStreamTrackClone.html create mode 100644 dom/media/tests/mochitest/test_peerConnection_multiple_captureStream_canvas_2d.html create mode 100644 dom/media/tests/mochitest/test_peerConnection_trackDisabling.html create mode 100644 dom/media/tests/mochitest/test_peerConnection_trackDisabling_clones.html create mode 100644 dom/media/tests/mochitest/test_peerConnection_verifyAudioAfterRenegotiation.html delete mode 100644 dom/webidl/ExternalAppEvent.webidl delete mode 100644 dom/webidl/HTMLExtAppElement.webidl create mode 100644 editor/libeditor/tests/test_bug1140105.html create mode 100644 gfx/src/ArrayView.h create mode 100644 gfx/tests/gtest/TestArrayView.cpp delete mode 100644 gfx/tests/gtest/gfxWordCacheTest.cpp create mode 100644 layout/reftests/bugs/1238243-1-notref.html create mode 100644 layout/reftests/bugs/1238243-1.html create mode 100644 layout/reftests/bugs/1238243-2-ref.html create mode 100644 layout/reftests/bugs/1238243-2.html create mode 100644 layout/reftests/bugs/1239564-ref.html create mode 100644 layout/reftests/bugs/1239564.html create mode 100644 layout/reftests/bugs/1260543-1-ref.html create mode 100644 layout/reftests/bugs/1260543-1.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-001-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-001.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-002-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-002.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-003-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-003.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-004-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-004.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-005-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-005.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-006-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-006.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-007-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-007.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-008-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-008.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-009-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-009.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-010-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-010.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-011-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-011.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-012-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-012.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-013-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-013.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-014-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-014.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-015-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-015.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-016-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-016.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-017-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-017.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-018-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-018.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-019-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-019.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-020-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-020.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-021-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-021.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-022.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-023-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-023.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-024-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-024.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-025-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-025.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-026-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-026.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-027.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-028-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-028.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-029-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-029.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-030-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-030.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-031-ref.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-031.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn1-002.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn1-006.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn1-015.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn1-016.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn1-019.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn1-020.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn1-021.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn1-023.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn1-024.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn1-026.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn1-027.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn1-028.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn1-029.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn2-018.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn2-019.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn2-020.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn2-021.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn2-022.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn2-023.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn2-025.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn2-026.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn2-027.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn2-028.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn2-029.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn2-030.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn2-031.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn3-001.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn3-002.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn3-003.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn3-007.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn3-009.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn3-017.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn3-019.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn3-021.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn3-023.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn3-026.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn3-027.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn3-028.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn4-001.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn4-004.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn4-005.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn4-015.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn4-019.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn4-021.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn4-023.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn4-026.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn4-027.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn4-028.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn5-005.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn5-007.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn5-008.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn5-016.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn5-019.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn5-021.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn5-023.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn5-026.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn5-027.html create mode 100644 layout/reftests/css-grid/grid-fragmentation-dyn5-028.html create mode 100644 layout/reftests/css-grid/support/dyn.js create mode 100644 layout/reftests/text-stroke/reftest.list create mode 100644 layout/reftests/text-stroke/webkit-text-stroke-property-001-ref.html create mode 100644 layout/reftests/text-stroke/webkit-text-stroke-property-001.html create mode 100644 layout/reftests/text-stroke/webkit-text-stroke-property-002-ref.html create mode 100644 layout/reftests/text-stroke/webkit-text-stroke-property-002.html create mode 100644 layout/reftests/text-stroke/webkit-text-stroke-property-003-ref.html create mode 100644 layout/reftests/text-stroke/webkit-text-stroke-property-003.html create mode 100644 layout/reftests/text-stroke/webkit-text-stroke-property-004-ref.html create mode 100644 layout/reftests/text-stroke/webkit-text-stroke-property-004.html create mode 100644 layout/reftests/text-stroke/webkit-text-stroke-property-005-ref.html create mode 100644 layout/reftests/text-stroke/webkit-text-stroke-property-005.html create mode 100644 testing/web-platform/meta/compat/webkit-text-fill-color-property-002.html.ini create mode 100644 testing/web-platform/meta/compat/webkit-text-fill-color-property-003.html.ini create mode 100644 testing/web-platform/meta/compat/webkit-text-fill-color-property-004.html.ini create mode 100644 testing/web-platform/meta/compat/webkit-text-fill-color-property-005.html.ini create mode 100644 testing/web-platform/meta/compat/webkit-text-fill-color-property-006.html.ini delete mode 100644 testing/web-platform/meta/web-animations/keyframe-effect/effect-easing.html.ini create mode 100644 testing/web-platform/meta/web-animations/keyframe-effect/setFrames.html.ini create mode 100644 testing/web-platform/tests/compat/webkit-text-fill-color-property-002-ref.html create mode 100644 testing/web-platform/tests/compat/webkit-text-fill-color-property-002.html create mode 100644 testing/web-platform/tests/compat/webkit-text-fill-color-property-003-ref.html create mode 100644 testing/web-platform/tests/compat/webkit-text-fill-color-property-003.html create mode 100644 testing/web-platform/tests/compat/webkit-text-fill-color-property-004-ref.html create mode 100644 testing/web-platform/tests/compat/webkit-text-fill-color-property-004.html create mode 100644 testing/web-platform/tests/compat/webkit-text-fill-color-property-005-ref.html create mode 100644 testing/web-platform/tests/compat/webkit-text-fill-color-property-005.html create mode 100644 testing/web-platform/tests/compat/webkit-text-fill-color-property-006-ref.html create mode 100644 testing/web-platform/tests/compat/webkit-text-fill-color-property-006.html create mode 100644 testing/web-platform/tests/web-animations/animation-model/animation-types/discrete-animation.html create mode 100644 testing/web-platform/tests/web-animations/keyframe-effect/setFrames.html create mode 100644 testing/web-platform/tests/web-animations/resources/keyframe-utils.js rename widget/gtk/{gtk3drawing.c => gtk3drawing.cpp} (92%) delete mode 100644 xpcom/tests/TestAtoms.cpp create mode 100644 xpcom/tests/gtest/TestAtoms.cpp diff --git a/.lldbinit b/.lldbinit index b5cc7e7f11..5284f42ac7 100644 --- a/.lldbinit +++ b/.lldbinit @@ -9,7 +9,7 @@ # you are either running lldb from the top level source directory, the objdir, # or the dist/bin directory. (.lldbinit files in the objdir and dist/bin set # topsrcdir appropriately.) -script topsrcdir = topsrcdir if locals().has_key("topsrcdir") else "."; sys.path.append(os.path.join(topsrcdir, "python/lldbutils")); import lldbutils; lldbutils.init() +script topsrcdir = topsrcdir if locals().has_key("topsrcdir") else os.getcwd(); sys.path.append(os.path.join(topsrcdir, "python/lldbutils")); import lldbutils; lldbutils.init() # Mozilla's use of UNIFIED_SOURCES to include multiple source files into a # single compiled file breaks lldb breakpoint setting. This works around that. diff --git a/accessible/base/AccIterator.cpp b/accessible/base/AccIterator.cpp index c407c8e867..f6e890c502 100644 --- a/accessible/base/AccIterator.cpp +++ b/accessible/base/AccIterator.cpp @@ -257,7 +257,7 @@ IDRefsIterator:: nsIAtom* aIDRefsAttr) : mContent(aContent), mDoc(aDoc), mCurrIdx(0) { - if (mContent->IsInDoc()) + if (mContent->IsInUncomposedDoc()) mContent->GetAttr(kNameSpaceID_None, aIDRefsAttr, mIDs); } diff --git a/accessible/base/TextAttrs.cpp b/accessible/base/TextAttrs.cpp index ba83c9666a..a776c47d8f 100644 --- a/accessible/base/TextAttrs.cpp +++ b/accessible/base/TextAttrs.cpp @@ -702,7 +702,7 @@ TextAttrsMgr::TextDecorValue:: bool isForegroundColor = false; textReset->GetDecorationColor(mColor, isForegroundColor); if (isForegroundColor) - mColor = aFrame->StyleColor()->mColor; + mColor = aFrame->StyleContext()->GetTextFillColor(); mLine = textReset->mTextDecorationLine & (NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE | diff --git a/accessible/base/nsAccessibilityService.cpp b/accessible/base/nsAccessibilityService.cpp index 9fd252a9df..45fbd968b7 100644 --- a/accessible/base/nsAccessibilityService.cpp +++ b/accessible/base/nsAccessibilityService.cpp @@ -360,7 +360,7 @@ nsAccessibilityService::Observe(nsISupports *aSubject, const char *aTopic, void nsAccessibilityService::NotifyOfAnchorJumpTo(nsIContent* aTargetNode) { - nsIDocument* documentNode = aTargetNode->GetCurrentDoc(); + nsIDocument* documentNode = aTargetNode->GetUncomposedDoc(); if (documentNode) { DocAccessible* document = GetDocAccessible(documentNode); if (document) @@ -416,7 +416,7 @@ public: NS_IMETHODIMP Notify(nsITimer* aTimer) final { - if (!mContent->IsInDoc()) + if (!mContent->IsInUncomposedDoc()) return NS_OK; nsIPresShell* ps = mContent->OwnerDoc()->GetShell(); @@ -1039,7 +1039,7 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode, } // We have a content node. - if (!aNode->GetCrossShadowCurrentDoc()) { + if (!aNode->GetComposedDoc()) { NS_WARNING("Creating accessible for node with no document"); return nullptr; } diff --git a/accessible/base/nsCoreUtils.cpp b/accessible/base/nsCoreUtils.cpp index 3c2055e51d..640a5af0b6 100644 --- a/accessible/base/nsCoreUtils.cpp +++ b/accessible/base/nsCoreUtils.cpp @@ -78,7 +78,7 @@ nsCoreUtils::DispatchClickEvent(nsITreeBoxObject *aTreeBoxObj, return; nsCOMPtr tcContent(do_QueryInterface(tcElm)); - nsIDocument *document = tcContent->GetCurrentDoc(); + nsIDocument *document = tcContent->GetUncomposedDoc(); if (!document) return; @@ -113,7 +113,7 @@ nsCoreUtils::DispatchClickEvent(nsITreeBoxObject *aTreeBoxObj, nsPoint offset; nsIWidget *rootWidget = - rootFrame->GetViewExternal()->GetNearestWidget(&offset); + rootFrame->GetView()->GetNearestWidget(&offset); RefPtr presContext = presShell->GetPresContext(); @@ -138,11 +138,11 @@ nsCoreUtils::DispatchMouseEvent(EventMessage aMessage, int32_t aX, int32_t aY, WidgetMouseEvent event(true, aMessage, aRootWidget, WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal); - event.refPoint = LayoutDeviceIntPoint(aX, aY); + event.mRefPoint = LayoutDeviceIntPoint(aX, aY); event.clickCount = 1; event.button = WidgetMouseEvent::eLeftButton; - event.time = PR_IntervalNow(); + event.mTime = PR_IntervalNow(); event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN; nsEventStatus status = nsEventStatus_eIgnore; @@ -159,13 +159,13 @@ nsCoreUtils::DispatchTouchEvent(EventMessage aMessage, int32_t aX, int32_t aY, WidgetTouchEvent event(true, aMessage, aRootWidget); - event.time = PR_IntervalNow(); + event.mTime = PR_IntervalNow(); // XXX: Touch has an identifier of -1 to hint that it is synthesized. RefPtr t = new dom::Touch(-1, LayoutDeviceIntPoint(aX, aY), LayoutDeviceIntPoint(1, 1), 0.0f, 1.0f); t->SetTarget(aContent); - event.touches.AppendElement(t); + event.mTouches.AppendElement(t); nsEventStatus status = nsEventStatus_eIgnore; aPresShell->HandleEventWithTarget(&event, aFrame, aContent, &status); } diff --git a/accessible/generic/Accessible.cpp b/accessible/generic/Accessible.cpp index 783900b585..dcc5be91fd 100644 --- a/accessible/generic/Accessible.cpp +++ b/accessible/generic/Accessible.cpp @@ -275,7 +275,7 @@ Accessible::AccessKey() const } // Determine the access modifier used in this context. - nsIDocument* document = mContent->GetCurrentDoc(); + nsIDocument* document = mContent->GetUncomposedDoc(); if (!document) return KeyBinding(); @@ -537,7 +537,7 @@ Accessible::ChildAtPoint(int32_t aX, int32_t aY, WidgetMouseEvent dummyEvent(true, eMouseMove, rootWidget, WidgetMouseEvent::eSynthesized); - dummyEvent.refPoint = LayoutDeviceIntPoint(aX - rootRect.x, aY - rootRect.y); + dummyEvent.mRefPoint = LayoutDeviceIntPoint(aX - rootRect.x, aY - rootRect.y); nsIFrame* popupFrame = nsLayoutUtils:: GetPopupFrameForEventCoordinates(accDocument->PresContext()->GetRootPresContext(), @@ -1614,7 +1614,7 @@ Accessible::RelationByType(RelationType aType) // above it). nsIFrame *frame = GetFrame(); if (frame) { - nsView *view = frame->GetViewExternal(); + nsView *view = frame->GetView(); if (view) { nsIScrollableFrame *scrollFrame = do_QueryFrame(frame); if (scrollFrame || view->GetWidget() || !frame->GetParent()) diff --git a/accessible/generic/DocAccessible.cpp b/accessible/generic/DocAccessible.cpp index 5b56e411a4..ee297f4e98 100644 --- a/accessible/generic/DocAccessible.cpp +++ b/accessible/generic/DocAccessible.cpp @@ -123,7 +123,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DocAccessible, Accessible) AttrRelProvider* provider = (*providers)[jdx]; cb.NoteXPCOMChild(provider->mContent); - NS_ASSERTION(provider->mContent->IsInDoc(), + NS_ASSERTION(provider->mContent->IsInUncomposedDoc(), "Referred content is not in document!"); } } @@ -1227,7 +1227,7 @@ DocAccessible::GetAccessibleByUniqueIDInSubtree(void* aUniqueID) Accessible* DocAccessible::GetAccessibleOrContainer(nsINode* aNode) const { - if (!aNode || !aNode->GetCrossShadowCurrentDoc()) + if (!aNode || !aNode->GetComposedDoc()) return nullptr; nsINode* currNode = aNode; diff --git a/accessible/generic/OuterDocAccessible.cpp b/accessible/generic/OuterDocAccessible.cpp index daf7b77de8..66305cf871 100644 --- a/accessible/generic/OuterDocAccessible.cpp +++ b/accessible/generic/OuterDocAccessible.cpp @@ -29,6 +29,15 @@ OuterDocAccessible:: AccessibleWrap(aContent, aDoc) { mType = eOuterDocType; + + // Request document accessible for the content document to make sure it's + // created. It will appended to outerdoc accessible children asynchronously. + nsIDocument* outerDoc = mContent->GetUncomposedDoc(); + if (outerDoc) { + nsIDocument* innerDoc = outerDoc->GetSubDocumentFor(mContent); + if (innerDoc) + GetAccService()->GetDocAccessible(innerDoc); + } } OuterDocAccessible::~OuterDocAccessible() @@ -168,23 +177,6 @@ OuterDocAccessible::RemoveChild(Accessible* aAccessible) return wasRemoved; } - -//////////////////////////////////////////////////////////////////////////////// -// Accessible protected - -void -OuterDocAccessible::CacheChildren() -{ - // Request document accessible for the content document to make sure it's - // created. It will appended to outerdoc accessible children asynchronously. - nsIDocument* outerDoc = mContent->GetCurrentDoc(); - if (outerDoc) { - nsIDocument* innerDoc = outerDoc->GetSubDocumentFor(mContent); - if (innerDoc) - GetAccService()->GetDocAccessible(innerDoc); - } -} - ProxyAccessible* OuterDocAccessible::RemoteChildDoc() const { diff --git a/accessible/generic/OuterDocAccessible.h b/accessible/generic/OuterDocAccessible.h index 5d0489a1be..680ab60d87 100644 --- a/accessible/generic/OuterDocAccessible.h +++ b/accessible/generic/OuterDocAccessible.h @@ -42,9 +42,6 @@ public: protected: virtual ~OuterDocAccessible() override; - - // Accessible - virtual void CacheChildren() override; }; inline OuterDocAccessible* diff --git a/accessible/html/HTMLImageMapAccessible.cpp b/accessible/html/HTMLImageMapAccessible.cpp index 2d09fc9b67..1284980f93 100644 --- a/accessible/html/HTMLImageMapAccessible.cpp +++ b/accessible/html/HTMLImageMapAccessible.cpp @@ -30,6 +30,8 @@ HTMLImageMapAccessible:: ImageAccessibleWrap(aContent, aDoc) { mType = eImageMapType; + + UpdateChildAreas(false); } //////////////////////////////////////////////////////////////////////////////// @@ -150,16 +152,6 @@ HTMLImageMapAccessible::GetChildAccessibleFor(const nsINode* aNode) const return nullptr; } -//////////////////////////////////////////////////////////////////////////////// -// HTMLImageMapAccessible: Accessible protected - -void -HTMLImageMapAccessible::CacheChildren() -{ - UpdateChildAreas(false); -} - - //////////////////////////////////////////////////////////////////////////////// // HTMLAreaAccessible //////////////////////////////////////////////////////////////////////////////// @@ -231,15 +223,6 @@ HTMLAreaAccessible::EndOffset() return IndexInParent() + 1; } -//////////////////////////////////////////////////////////////////////////////// -// HTMLAreaAccessible: Accessible protected - -void -HTMLAreaAccessible::CacheChildren() -{ - // No children for aria accessible. -} - nsRect HTMLAreaAccessible::RelativeBounds(nsIFrame** aBoundingFrame) const { diff --git a/accessible/html/HTMLImageMapAccessible.h b/accessible/html/HTMLImageMapAccessible.h index 66593d2f41..1668245dc4 100644 --- a/accessible/html/HTMLImageMapAccessible.h +++ b/accessible/html/HTMLImageMapAccessible.h @@ -44,9 +44,6 @@ public: protected: virtual ~HTMLImageMapAccessible() { } - - // Accessible - virtual void CacheChildren() override; }; /** @@ -68,10 +65,12 @@ public: virtual uint32_t StartOffset() override; virtual uint32_t EndOffset() override; + virtual bool IsAcceptableChild(nsIContent* aEl) const override + { return false; } + protected: // Accessible virtual ENameValueFlag NativeName(nsString& aName) override; - virtual void CacheChildren() override; }; diff --git a/accessible/html/HTMLSelectAccessible.cpp b/accessible/html/HTMLSelectAccessible.cpp index cc13a15b99..87c0b0f421 100644 --- a/accessible/html/HTMLSelectAccessible.cpp +++ b/accessible/html/HTMLSelectAccessible.cpp @@ -360,6 +360,16 @@ HTMLComboboxAccessible:: { mType = eHTMLComboboxType; mGenericTypes |= eCombobox; + + nsIComboboxControlFrame* comboFrame = do_QueryFrame(GetFrame()); + if (comboFrame) { + nsIFrame* listFrame = comboFrame->GetDropDown(); + if (listFrame) { + mListAccessible = new HTMLComboboxListAccessible(mParent, mContent, mDoc); + Document()->BindToDocument(mListAccessible, nullptr); + AppendChild(mListAccessible); + } + } } //////////////////////////////////////////////////////////////////////////////// @@ -394,26 +404,6 @@ HTMLComboboxAccessible::RemoveChild(Accessible* aChild) void HTMLComboboxAccessible::CacheChildren() { - nsIComboboxControlFrame* comboFrame = do_QueryFrame(GetFrame()); - if (!comboFrame) - return; - - nsIFrame* listFrame = comboFrame->GetDropDown(); - if (!listFrame) - return; - - if (!mListAccessible) { - mListAccessible = new HTMLComboboxListAccessible(mParent, mContent, mDoc); - - // Initialize and put into cache. - Document()->BindToDocument(mListAccessible, nullptr); - } - - if (AppendChild(mListAccessible)) { - // Cache combobox option accessibles so that we build complete accessible - // tree for combobox. - mListAccessible->EnsureChildren(); - } } void diff --git a/accessible/mac/RootAccessibleWrap.mm b/accessible/mac/RootAccessibleWrap.mm index 60674c99c2..037545cce2 100644 --- a/accessible/mac/RootAccessibleWrap.mm +++ b/accessible/mac/RootAccessibleWrap.mm @@ -40,7 +40,7 @@ RootAccessibleWrap::GetNativeWidget(void** aOutView) { nsIFrame *frame = GetFrame(); if (frame) { - nsView *view = frame->GetViewExternal(); + nsView *view = frame->GetView(); if (view) { nsIWidget *widget = view->GetWidget(); if (widget) { diff --git a/accessible/tests/mochitest/treeview.js b/accessible/tests/mochitest/treeview.js index 4e5f7c84fb..869471c857 100644 --- a/accessible/tests/mochitest/treeview.js +++ b/accessible/tests/mochitest/treeview.js @@ -277,12 +277,6 @@ function treeItem(aText, aOpen, aChildren) this.children = aChildren; } -function createAtom(aName) -{ - return Components.classes["@mozilla.org/atom-service;1"] - .getService(Components.interfaces.nsIAtomService).getAtom(aName); -} - /** * Used in conjunction with loadXULTreeAndDoTest and addA11yXULTreeLoadEvent. */ diff --git a/accessible/windows/msaa/AccessibleWrap.cpp b/accessible/windows/msaa/AccessibleWrap.cpp index a605fac266..e3ab6003ba 100644 --- a/accessible/windows/msaa/AccessibleWrap.cpp +++ b/accessible/windows/msaa/AccessibleWrap.cpp @@ -496,7 +496,7 @@ AccessibleWrap::get_accRole( nsAutoString roleString; if (msaaRole != ROLE_SYSTEM_CLIENT && !content->GetAttr(kNameSpaceID_None, nsGkAtoms::role, roleString)) { - nsIDocument * document = content->GetCurrentDoc(); + nsIDocument * document = content->GetUncomposedDoc(); if (!document) return E_FAIL; diff --git a/accessible/windows/msaa/HTMLWin32ObjectAccessible.cpp b/accessible/windows/msaa/HTMLWin32ObjectAccessible.cpp index 7744e19ec2..ef54c45e70 100644 --- a/accessible/windows/msaa/HTMLWin32ObjectAccessible.cpp +++ b/accessible/windows/msaa/HTMLWin32ObjectAccessible.cpp @@ -20,8 +20,10 @@ HTMLWin32ObjectOwnerAccessible:: AccessibleWrap(aContent, aDoc), mHwnd(aHwnd) { // Our only child is a HTMLWin32ObjectAccessible object. - if (mHwnd) + if (mHwnd) { mNativeAccessible = new HTMLWin32ObjectAccessible(mHwnd, aDoc); + AppendChild(mNativeAccessible); + } } //////////////////////////////////////////////////////////////////////////////// @@ -54,8 +56,6 @@ HTMLWin32ObjectOwnerAccessible::NativelyUnavailable() const void HTMLWin32ObjectOwnerAccessible::CacheChildren() { - if (mNativeAccessible) - AppendChild(mNativeAccessible); } diff --git a/b2g/installer/package-manifest.in b/b2g/installer/package-manifest.in index 41e774abe7..33eafd25ef 100644 --- a/b2g/installer/package-manifest.in +++ b/b2g/installer/package-manifest.in @@ -702,6 +702,9 @@ ; InputMethod API @RESPATH@/components/MozKeyboard.js @RESPATH@/components/InputMethod.manifest +#ifdef MOZ_B2G +@RESPATH@/components/inputmethod.xpt +#endif @RESPATH@/components/EngineeringMode.manifest @RESPATH@/components/EngineeringModeAPI.js diff --git a/config/system-headers b/config/system-headers index d034668984..811086b63a 100644 --- a/config/system-headers +++ b/config/system-headers @@ -245,6 +245,7 @@ cairo-atsui.h cairo-beos.h cairo-ft.h cairo-glitz.h +cairo-gobject.h cairo-pdf.h cairo-ps.h cairo-tee.h diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index d7bbc72bd9..7ac59e7cc7 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -348,7 +348,7 @@ ForEachPing(nsIContent* aContent, ForEachPingCallback aCallback, void* aClosure) return; } - nsCOMPtr pingAtom = do_GetAtom("ping"); + nsCOMPtr pingAtom = NS_Atomize("ping"); if (!pingAtom) { return; } diff --git a/dom/animation/AnimValuesStyleRule.h b/dom/animation/AnimValuesStyleRule.h index 2899f9e39d..2d80ddb531 100644 --- a/dom/animation/AnimValuesStyleRule.h +++ b/dom/animation/AnimValuesStyleRule.h @@ -36,7 +36,7 @@ public: void List(FILE* out = stdout, int32_t aIndent = 0) const override; #endif - void AddValue(nsCSSProperty aProperty, StyleAnimationValue &aStartValue) + void AddValue(nsCSSProperty aProperty, const StyleAnimationValue &aStartValue) { PropertyValuePair v = { aProperty, aStartValue }; mPropertyValuePairs.AppendElement(v); @@ -44,14 +44,13 @@ public: nsCachedStyleData::GetBitForSID(nsCSSProps::kSIDTable[aProperty]); } - // Caller must fill in returned value. - StyleAnimationValue* AddEmptyValue(nsCSSProperty aProperty) + void AddValue(nsCSSProperty aProperty, StyleAnimationValue&& aStartValue) { PropertyValuePair *p = mPropertyValuePairs.AppendElement(); p->mProperty = aProperty; + p->mValue = Move(aStartValue); mStyleBits |= nsCachedStyleData::GetBitForSID(nsCSSProps::kSIDTable[aProperty]); - return &p->mValue; } struct PropertyValuePair { diff --git a/dom/animation/AnimationEffectTiming.h b/dom/animation/AnimationEffectTiming.h index 82e98c1dda..7a8cfb2c86 100644 --- a/dom/animation/AnimationEffectTiming.h +++ b/dom/animation/AnimationEffectTiming.h @@ -16,8 +16,10 @@ namespace dom { class AnimationEffectTiming : public AnimationEffectTimingReadOnly { public: - AnimationEffectTiming(const TimingParams& aTiming, KeyframeEffect* aEffect) - : AnimationEffectTimingReadOnly(aTiming) + AnimationEffectTiming(nsISupports* aParent, + const TimingParams& aTiming, + KeyframeEffect* aEffect) + : AnimationEffectTimingReadOnly(aParent, aTiming) , mEffect(aEffect) { } JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; diff --git a/dom/animation/AnimationEffectTimingReadOnly.cpp b/dom/animation/AnimationEffectTimingReadOnly.cpp index 52c7179830..93df07f89b 100644 --- a/dom/animation/AnimationEffectTimingReadOnly.cpp +++ b/dom/animation/AnimationEffectTimingReadOnly.cpp @@ -40,7 +40,7 @@ AnimationEffectTimingReadOnly::GetDuration( void AnimationEffectTimingReadOnly::GetEasing(nsString& aRetVal) const { - if (mTiming.mFunction.isSome()) { + if (mTiming.mFunction) { mTiming.mFunction->AppendToString(aRetVal); } else { aRetVal.AssignLiteral("linear"); diff --git a/dom/animation/AnimationEffectTimingReadOnly.h b/dom/animation/AnimationEffectTimingReadOnly.h index 6c570342b1..c809a9762d 100644 --- a/dom/animation/AnimationEffectTimingReadOnly.h +++ b/dom/animation/AnimationEffectTimingReadOnly.h @@ -23,8 +23,10 @@ class AnimationEffectTimingReadOnly : public nsWrapperCache { public: AnimationEffectTimingReadOnly() = default; - explicit AnimationEffectTimingReadOnly(const TimingParams& aTiming) - : mTiming(aTiming) { } + AnimationEffectTimingReadOnly(nsISupports* aParent, + const TimingParams& aTiming) + : mParent(aParent) + , mTiming(aTiming) { } NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AnimationEffectTimingReadOnly) NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(AnimationEffectTimingReadOnly) diff --git a/dom/animation/ComputedTiming.h b/dom/animation/ComputedTiming.h index 13b902f0d4..4a98e39337 100644 --- a/dom/animation/ComputedTiming.h +++ b/dom/animation/ComputedTiming.h @@ -9,6 +9,7 @@ #include "mozilla/dom/Nullable.h" #include "mozilla/StickyTimeDuration.h" +#include "mozilla/ComputedTimingFunction.h" // X11 has a #define for None #ifdef None @@ -67,6 +68,9 @@ struct ComputedTiming After // Sampled after (or at) the end of the active interval }; AnimationPhase mPhase = AnimationPhase::Null; + + ComputedTimingFunction::BeforeFlag mBeforeFlag = + ComputedTimingFunction::BeforeFlag::Unset; }; } // namespace mozilla diff --git a/dom/animation/ComputedTimingFunction.cpp b/dom/animation/ComputedTimingFunction.cpp index f36f8b219f..6319ec89ef 100644 --- a/dom/animation/ComputedTimingFunction.cpp +++ b/dom/animation/ComputedTimingFunction.cpp @@ -24,15 +24,44 @@ ComputedTimingFunction::Init(const nsTimingFunction &aFunction) } static inline double -StepEnd(uint32_t aSteps, double aPortion) +StepTiming(uint32_t aSteps, + double aPortion, + ComputedTimingFunction::BeforeFlag aBeforeFlag, + nsTimingFunction::Type aType) { MOZ_ASSERT(0.0 <= aPortion && aPortion <= 1.0, "out of range"); + MOZ_ASSERT(aType != nsTimingFunction::Type::StepStart || + aType != nsTimingFunction::Type::StepEnd, "invalid type"); + + if (aPortion == 1.0) { + return 1.0; + } + + // Calculate current step using step-end behavior uint32_t step = uint32_t(aPortion * aSteps); // floor + + // step-start is one step ahead + if (aType == nsTimingFunction::Type::StepStart) { + step++; + } + + // If the "before flag" is set and we are at a transition point, + // drop back a step (but only if we are not already at the zero point-- + // we do this clamping here since |step| is an unsigned integer) + if (step != 0 && + aBeforeFlag == ComputedTimingFunction::BeforeFlag::Set && + fmod(aPortion * aSteps, 1) == 0) { + step--; + } + + // Convert to a progress value return double(step) / double(aSteps); } double -ComputedTimingFunction::GetValue(double aPortion) const +ComputedTimingFunction::GetValue( + double aPortion, + ComputedTimingFunction::BeforeFlag aBeforeFlag) const { if (HasSpline()) { // Check for a linear curve. @@ -84,17 +113,7 @@ ComputedTimingFunction::GetValue(double aPortion) const // Clamp in case of steps(end) and steps(start) for values greater than 1. aPortion = clamped(aPortion, 0.0, 1.0); - if (mType == nsTimingFunction::Type::StepStart) { - // There are diagrams in the spec that seem to suggest this check - // and the bounds point should not be symmetric with StepEnd, but - // should actually step up at rather than immediately after the - // fraction points. However, we rely on rounding negative values - // up to zero, so we can't do that. And it's not clear the spec - // really meant it. - return 1.0 - StepEnd(mSteps, 1.0 - aPortion); - } - MOZ_ASSERT(mType == nsTimingFunction::Type::StepEnd, "bad type"); - return StepEnd(mSteps, aPortion); + return StepTiming(mSteps, aPortion, aBeforeFlag, mType); } int32_t diff --git a/dom/animation/ComputedTimingFunction.h b/dom/animation/ComputedTimingFunction.h index 5c7e578f27..aa15df7115 100644 --- a/dom/animation/ComputedTimingFunction.h +++ b/dom/animation/ComputedTimingFunction.h @@ -15,8 +15,14 @@ namespace mozilla { class ComputedTimingFunction { public: + // BeforeFlag is used in step timing function. + // https://w3c.github.io/web-animations/#before-flag + enum class BeforeFlag { + Unset, + Set + }; void Init(const nsTimingFunction &aFunction); - double GetValue(double aPortion) const; + double GetValue(double aPortion, BeforeFlag aBeforeFlag) const; const nsSMILKeySpline* GetFunction() const { NS_ASSERTION(HasSpline(), "Type mismatch"); @@ -42,9 +48,10 @@ public: void AppendToString(nsAString& aResult) const; static double GetPortion(const Maybe& aFunction, - double aPortion) + double aPortion, + BeforeFlag aBeforeFlag) { - return aFunction.isSome() ? aFunction->GetValue(aPortion) : aPortion; + return aFunction ? aFunction->GetValue(aPortion, aBeforeFlag) : aPortion; } static int32_t Compare(const Maybe& aLhs, const Maybe& aRhs); diff --git a/dom/animation/KeyframeEffect.cpp b/dom/animation/KeyframeEffect.cpp index bb9f788e6c..c48f09e20b 100644 --- a/dom/animation/KeyframeEffect.cpp +++ b/dom/animation/KeyframeEffect.cpp @@ -20,7 +20,6 @@ #include "nsCSSProps.h" // For nsCSSProps::PropHasFlags #include "nsCSSPseudoElements.h" // For CSSPseudoElementType #include "nsDOMMutationObserver.h" // For nsAutoAnimationMutationBatch -#include // For std::max namespace mozilla { @@ -62,7 +61,8 @@ namespace dom { NS_IMPL_CYCLE_COLLECTION_INHERITED(KeyframeEffectReadOnly, AnimationEffectReadOnly, mTarget, - mAnimation) + mAnimation, + mTiming) NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(KeyframeEffectReadOnly, AnimationEffectReadOnly) @@ -80,7 +80,8 @@ KeyframeEffectReadOnly::KeyframeEffectReadOnly( CSSPseudoElementType aPseudoType, const TimingParams& aTiming) : KeyframeEffectReadOnly(aDocument, aTarget, aPseudoType, - new AnimationEffectTimingReadOnly(aTiming)) + new AnimationEffectTimingReadOnly(aDocument, + aTiming)) { } @@ -91,7 +92,7 @@ KeyframeEffectReadOnly::KeyframeEffectReadOnly( AnimationEffectTimingReadOnly* aTiming) : AnimationEffectReadOnly(aDocument) , mTarget(aTarget) - , mTiming(*aTiming) + , mTiming(aTiming) , mPseudoType(aPseudoType) , mInEffectOnLastAnimationTimingUpdate(false) { @@ -217,7 +218,8 @@ KeyframeEffectReadOnly::GetLocalTime() const } void -KeyframeEffectReadOnly::GetComputedTimingAsDict(ComputedTimingProperties& aRetVal) const +KeyframeEffectReadOnly::GetComputedTimingAsDict( + ComputedTimingProperties& aRetVal) const { const Nullable currentTime = GetLocalTime(); GetComputedTimingDictionary(GetComputedTimingAt(currentTime, @@ -229,8 +231,8 @@ KeyframeEffectReadOnly::GetComputedTimingAsDict(ComputedTimingProperties& aRetVa ComputedTiming KeyframeEffectReadOnly::GetComputedTimingAt( - const Nullable& aLocalTime, - const TimingParams& aTiming) + const Nullable& aLocalTime, + const TimingParams& aTiming) { const StickyTimeDuration zeroDuration; @@ -243,14 +245,18 @@ KeyframeEffectReadOnly::GetComputedTimingAt( result.mDuration = aTiming.mDuration.ref(); } - result.mIterations = IsNaN(aTiming.mIterations) || aTiming.mIterations < 0.0f ? - 1.0f : - aTiming.mIterations; - result.mIterationStart = std::max(aTiming.mIterationStart, 0.0); + MOZ_ASSERT(aTiming.mIterations >= 0.0 && !IsNaN(aTiming.mIterations), + "mIterations should be nonnegative & finite, as ensured by " + "ValidateIterations or CSSParser"); + result.mIterations = aTiming.mIterations; - result.mActiveDuration = ActiveDuration(result.mDuration, result.mIterations); - result.mEndTime = aTiming.mDelay + result.mActiveDuration + - aTiming.mEndDelay; + MOZ_ASSERT(aTiming.mIterationStart >= 0.0, + "mIterationStart should be nonnegative, as ensured by " + "ValidateIterationStart"); + result.mIterationStart = aTiming.mIterationStart; + + result.mActiveDuration = aTiming.ActiveDuration(); + result.mEndTime = aTiming.EndTime(); result.mFill = aTiming.mFill == dom::FillMode::Auto ? dom::FillMode::None : aTiming.mFill; @@ -262,12 +268,8 @@ KeyframeEffectReadOnly::GetComputedTimingAt( } const TimeDuration& localTime = aLocalTime.Value(); - // When we finish exactly at the end of an iteration we need to report - // the end of the final iteration and not the start of the next iteration - // so we set up a flag for that case. - bool isEndOfFinalIteration = false; - - // Get the normalized time within the active interval. + // Calculate the time within the active interval. + // https://w3c.github.io/web-animations/#active-time StickyTimeDuration activeTime; if (localTime >= std::min(StickyTimeDuration(aTiming.mDelay + result.mActiveDuration), @@ -275,21 +277,14 @@ KeyframeEffectReadOnly::GetComputedTimingAt( result.mPhase = ComputedTiming::AnimationPhase::After; if (!result.FillsForwards()) { // The animation isn't active or filling at this time. - result.mProgress.SetNull(); return result; } activeTime = result.mActiveDuration; - double finiteProgress = - (IsInfinite(result.mIterations) ? 0.0 : result.mIterations) - + result.mIterationStart; - isEndOfFinalIteration = result.mIterations != 0.0 && - fmod(finiteProgress, 1.0) == 0; } else if (localTime < std::min(StickyTimeDuration(aTiming.mDelay), result.mEndTime)) { result.mPhase = ComputedTiming::AnimationPhase::Before; if (!result.FillsBackwards()) { // The animation isn't active or filling at this time. - result.mProgress.SetNull(); return result; } // activeTime is zero @@ -300,70 +295,50 @@ KeyframeEffectReadOnly::GetComputedTimingAt( activeTime = localTime - aTiming.mDelay; } - // Calculate the scaled active time - // (We handle the case where the iterationStart is zero separately in case - // the duration is infinity, since 0 * Infinity is undefined.) - StickyTimeDuration startOffset = - result.mIterationStart == 0.0 - ? StickyTimeDuration(0) - : result.mDuration.MultDouble(result.mIterationStart); - StickyTimeDuration scaledActiveTime = activeTime + startOffset; + // Convert active time to a multiple of iterations. + // https://w3c.github.io/web-animations/#overall-progress + double overallProgress; + if (result.mDuration == zeroDuration) { + overallProgress = result.mPhase == ComputedTiming::AnimationPhase::Before + ? 0.0 + : result.mIterations; + } else { + overallProgress = activeTime / result.mDuration; + } - // Get the position within the current iteration. - StickyTimeDuration iterationTime; - if (result.mDuration != zeroDuration && - scaledActiveTime != StickyTimeDuration::Forever()) { - iterationTime = isEndOfFinalIteration - ? result.mDuration - : scaledActiveTime % result.mDuration; - } /* else, either the duration is zero and iterationTime is zero, - or the scaledActiveTime is infinity in which case the iterationTime - should become infinity but we will not use the iterationTime in that - case so we just leave it as zero */ + // Factor in iteration start offset. + if (IsFinite(overallProgress)) { + overallProgress += result.mIterationStart; + } // Determine the 0-based index of the current iteration. - if (result.mPhase == ComputedTiming::AnimationPhase::Before || - result.mIterations == 0) { - result.mCurrentIteration = static_cast(result.mIterationStart); - } else if (result.mPhase == ComputedTiming::AnimationPhase::After) { - result.mCurrentIteration = - IsInfinite(result.mIterations) - ? UINT64_MAX // In GetComputedTimingDictionary(), we will convert this - // into Infinity. - : static_cast(ceil(result.mIterations + - result.mIterationStart)) - 1; - } else if (result.mDuration == StickyTimeDuration::Forever()) { - result.mCurrentIteration = static_cast(result.mIterationStart); - } else { - result.mCurrentIteration = - static_cast(scaledActiveTime / result.mDuration); // floor - } + // https://w3c.github.io/web-animations/#current-iteration + result.mCurrentIteration = + IsInfinite(result.mIterations) && + result.mPhase == ComputedTiming::AnimationPhase::After + ? UINT64_MAX // In GetComputedTimingDictionary(), + // we will convert this into Infinity + : static_cast(overallProgress); - // Normalize the iteration time into a fraction of the iteration duration. - if (result.mPhase == ComputedTiming::AnimationPhase::Before || - result.mIterations == 0) { - double progress = fmod(result.mIterationStart, 1.0); - result.mProgress.SetValue(progress); - } else if (result.mPhase == ComputedTiming::AnimationPhase::After) { - double progress; - if (isEndOfFinalIteration) { - progress = 1.0; - } else if (IsInfinite(result.mIterations)) { - progress = fmod(result.mIterationStart, 1.0); - } else { - progress = fmod(result.mIterations + result.mIterationStart, 1.0); + // Convert the overall progress to a fraction of a single iteration--the + // simply iteration progress. + // https://w3c.github.io/web-animations/#simple-iteration-progress + double progress = IsFinite(overallProgress) + ? fmod(overallProgress, 1.0) + : fmod(result.mIterationStart, 1.0); + + // When we finish exactly at the end of an iteration we need to report + // the end of the final iteration and not the start of the next iteration. + if (result.mPhase == ComputedTiming::AnimationPhase::After && + progress == 0.0 && + result.mIterations != 0.0) { + progress = 1.0; + if (result.mCurrentIteration != UINT64_MAX) { + result.mCurrentIteration--; } - result.mProgress.SetValue(progress); - } else { - // We are in the active phase so the iteration duration can't be zero. - MOZ_ASSERT(result.mDuration != zeroDuration, - "In the active phase of a zero-duration animation?"); - double progress = result.mDuration == StickyTimeDuration::Forever() - ? fmod(result.mIterationStart, 1.0) - : iterationTime / result.mDuration; - result.mProgress.SetValue(progress); } + // Factor in the direction. bool thisIterationReverse = false; switch (aTiming.mDirection) { case PlaybackDirection::Normal: @@ -382,34 +357,28 @@ KeyframeEffectReadOnly::GetComputedTimingAt( MOZ_ASSERT(true, "Unknown PlaybackDirection type"); } if (thisIterationReverse) { - result.mProgress.SetValue(1.0 - result.mProgress.Value()); + progress = 1.0 - progress; } + // Calculate the 'before flag' which we use when applying step timing + // functions. + if ((result.mPhase == ComputedTiming::AnimationPhase::After && + thisIterationReverse) || + (result.mPhase == ComputedTiming::AnimationPhase::Before && + !thisIterationReverse)) { + result.mBeforeFlag = ComputedTimingFunction::BeforeFlag::Set; + } + + // Apply the easing. if (aTiming.mFunction) { - result.mProgress.SetValue( - aTiming.mFunction->GetValue(result.mProgress.Value())); + progress = aTiming.mFunction->GetValue(progress, result.mBeforeFlag); } + MOZ_ASSERT(IsFinite(progress), "Progress value should be finite"); + result.mProgress.SetValue(progress); return result; } -StickyTimeDuration -KeyframeEffectReadOnly::ActiveDuration( - const StickyTimeDuration& aIterationDuration, - double aIterationCount) -{ - // If either the iteration duration or iteration count is zero, - // Web Animations says that the active duration is zero. This is to - // ensure that the result is defined when the other argument is Infinity. - const StickyTimeDuration zeroDuration; - if (aIterationDuration == zeroDuration || - aIterationCount == 0.0) { - return zeroDuration; - } - - return aIterationDuration.MultDouble(aIterationCount); -} - // https://w3c.github.io/web-animations/#in-play bool KeyframeEffectReadOnly::IsInPlay() const @@ -449,6 +418,77 @@ KeyframeEffectReadOnly::SetAnimation(Animation* aAnimation) NotifyAnimationTimingUpdated(); } +static bool +KeyframesEqualIgnoringComputedOffsets(const nsTArray& aLhs, + const nsTArray& aRhs) +{ + if (aLhs.Length() != aRhs.Length()) { + return false; + } + + for (size_t i = 0, len = aLhs.Length(); i < len; ++i) { + const Keyframe& a = aLhs[i]; + const Keyframe& b = aRhs[i]; + if (a.mOffset != b.mOffset || + a.mTimingFunction != b.mTimingFunction || + a.mPropertyValues != b.mPropertyValues) { + return false; + } + } + return true; +} + +// https://w3c.github.io/web-animations/#dom-keyframeeffect-setframes +void +KeyframeEffectReadOnly::SetFrames(JSContext* aContext, + JS::Handle aFrames, + ErrorResult& aRv) +{ + nsIDocument* doc = AnimationUtils::GetCurrentRealmDocument(aContext); + if (!doc) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + nsTArray keyframes = + KeyframeUtils::GetKeyframesFromObject(aContext, aFrames, aRv); + if (aRv.Failed()) { + return; + } + + RefPtr styleContext; + nsIPresShell* shell = doc->GetShell(); + if (shell && mTarget) { + nsIAtom* pseudo = + mPseudoType < CSSPseudoElementType::Count ? + nsCSSPseudoElements::GetPseudoAtom(mPseudoType) : nullptr; + styleContext = + nsComputedDOMStyle::GetStyleContextForElement(mTarget, pseudo, shell); + } + + SetFrames(Move(keyframes), styleContext); +} + +void +KeyframeEffectReadOnly::SetFrames(nsTArray&& aFrames, + nsStyleContext* aStyleContext) +{ + if (KeyframesEqualIgnoringComputedOffsets(aFrames, mFrames)) { + return; + } + + mFrames = Move(aFrames); + KeyframeUtils::ApplyDistributeSpacing(mFrames); + + if (mAnimation && mAnimation->IsRelevant()) { + nsNodeUtils::AnimationChanged(mAnimation); + } + + if (aStyleContext) { + UpdateProperties(aStyleContext); + } +} + const AnimationProperty* KeyframeEffectReadOnly::GetAnimationOfProperty(nsCSSProperty aProperty) const { @@ -478,17 +518,25 @@ KeyframeEffectReadOnly::HasAnimationOfProperties( return false; } -bool -KeyframeEffectReadOnly::UpdateProperties( - const InfallibleTArray& aProperties) +void +KeyframeEffectReadOnly::UpdateProperties(nsStyleContext* aStyleContext) { - // AnimationProperty::operator== does not compare mWinsInCascade and - // mIsRunningOnCompositor, we don't need to update anything here because - // we want to preserve - if (mProperties == aProperties) { - return false; + MOZ_ASSERT(aStyleContext); + + nsTArray properties; + if (mTarget) { + properties = + KeyframeUtils::GetAnimationPropertiesFromKeyframes(aStyleContext, + mTarget, + mPseudoType, + mFrames); } + if (mProperties == properties) { + return; + } + + // Preserve the state of mWinsInCascade and mIsRunningOnCompositor flags. nsCSSPropertySet winningInCascadeProperties; nsCSSPropertySet runningOnCompositorProperties; @@ -501,7 +549,7 @@ KeyframeEffectReadOnly::UpdateProperties( } } - mProperties = aProperties; + mProperties = Move(properties); for (AnimationProperty& property : mProperties) { property.mWinsInCascade = @@ -510,6 +558,13 @@ KeyframeEffectReadOnly::UpdateProperties( runningOnCompositorProperties.HasProperty(property.mProperty); } + if (mTarget) { + EffectSet* effectSet = EffectSet::GetEffectSet(mTarget, mPseudoType); + if (effectSet) { + effectSet->MarkCascadeNeedsUpdate(); + } + } + if (mAnimation) { nsPresContext* presContext = GetPresContext(); if (presContext) { @@ -519,8 +574,6 @@ KeyframeEffectReadOnly::UpdateProperties( mAnimation->CascadeLevel()); } } - - return true; } void @@ -590,14 +643,12 @@ KeyframeEffectReadOnly::ComposeStyle(RefPtr& aStyleRule, aStyleRule = new AnimValuesStyleRule(); } - StyleAnimationValue* val = aStyleRule->AddEmptyValue(prop.mProperty); - // Special handling for zero-length segments if (segment->mToKey == segment->mFromKey) { if (computedTiming.mProgress.Value() < 0) { - *val = segment->mFromValue; + aStyleRule->AddValue(prop.mProperty, segment->mFromValue); } else { - *val = segment->mToValue; + aStyleRule->AddValue(prop.mProperty, segment->mToValue); } continue; } @@ -607,16 +658,20 @@ KeyframeEffectReadOnly::ComposeStyle(RefPtr& aStyleRule, (segment->mToKey - segment->mFromKey); double valuePosition = ComputedTimingFunction::GetPortion(segment->mTimingFunction, - positionInSegment); + positionInSegment, + computedTiming.mBeforeFlag); -#ifdef DEBUG - bool result = -#endif - StyleAnimationValue::Interpolate(prop.mProperty, - segment->mFromValue, - segment->mToValue, - valuePosition, *val); - MOZ_ASSERT(result, "interpolate must succeed now"); + StyleAnimationValue val; + if (StyleAnimationValue::Interpolate(prop.mProperty, + segment->mFromValue, + segment->mToValue, + valuePosition, val)) { + aStyleRule->AddValue(prop.mProperty, Move(val)); + } else if (valuePosition < 0.5) { + aStyleRule->AddValue(prop.mProperty, segment->mFromValue); + } else { + aStyleRule->AddValue(prop.mProperty, segment->mToValue); + } } } @@ -701,42 +756,14 @@ KeyframeEffectReadOnly::ConstructKeyframeEffect( pseudoType = target.GetAsCSSPseudoElement().GetType(); } - if (!targetElement->GetComposedDoc()) { - aRv.Throw(NS_ERROR_DOM_ANIM_TARGET_NOT_IN_DOC_ERR); - return nullptr; - } - - nsTArray keyframes = - KeyframeUtils::GetKeyframesFromObject(aGlobal.Context(), aFrames, aRv); - if (aRv.Failed()) { - return nullptr; - } - KeyframeUtils::ApplyDistributeSpacing(keyframes); - - RefPtr styleContext; - nsIPresShell* shell = doc->GetShell(); - if (shell && targetElement) { - nsIAtom* pseudo = - pseudoType < CSSPseudoElementType::Count ? - nsCSSPseudoElements::GetPseudoAtom(pseudoType) : nullptr; - styleContext = - nsComputedDOMStyle::GetStyleContextForElement(targetElement, pseudo, - shell); - } - - nsTArray animationProperties; - if (styleContext) { - animationProperties = - KeyframeUtils::GetAnimationPropertiesFromKeyframes(styleContext, - targetElement, - pseudoType, - keyframes); - } - RefPtr effect = new KeyframeEffectType(targetElement->OwnerDoc(), targetElement, pseudoType, timingParams); - effect->mProperties = Move(animationProperties); + effect->SetFrames(aGlobal.Context(), aFrames, aRv); + if (aRv.Failed()) { + return nullptr; + } + return effect.forget(); } @@ -810,81 +837,6 @@ struct KeyframeValue StyleAnimationValue mValue; }; -/** - * Represents a relative position for a value in a keyframe animation. - */ -enum class ValuePosition -{ - First, // value at 0 used for reverse filling - Left, // value coming in to a given offset - Right, // value coming out from a given offset - Last // value at 1 used for forward filling -}; - -/** - * A single value in a keyframe animation, used by GetFrames to produce a - * minimal set of keyframe objects. - */ -struct OrderedKeyframeValueEntry : KeyframeValue -{ - float mOffset; - const Maybe* mTimingFunction; - ValuePosition mPosition; - - bool SameKeyframe(const OrderedKeyframeValueEntry& aOther) const - { - return mOffset == aOther.mOffset && - !!mTimingFunction == !!aOther.mTimingFunction && - (!mTimingFunction || *mTimingFunction == *aOther.mTimingFunction) && - mPosition == aOther.mPosition; - } - - struct ForKeyframeGenerationComparator - { - static bool Equals(const OrderedKeyframeValueEntry& aLhs, - const OrderedKeyframeValueEntry& aRhs) - { - return aLhs.SameKeyframe(aRhs) && - aLhs.mProperty == aRhs.mProperty; - } - static bool LessThan(const OrderedKeyframeValueEntry& aLhs, - const OrderedKeyframeValueEntry& aRhs) - { - // First, sort by offset. - if (aLhs.mOffset != aRhs.mOffset) { - return aLhs.mOffset < aRhs.mOffset; - } - - // Second, by position. - if (aLhs.mPosition != aRhs.mPosition) { - return aLhs.mPosition < aRhs.mPosition; - } - - // Third, by easing. - if (aLhs.mTimingFunction) { - if (aRhs.mTimingFunction) { - int32_t order = - ComputedTimingFunction::Compare(*aLhs.mTimingFunction, - *aRhs.mTimingFunction); - if (order != 0) { - return order < 0; - } - } else { - return true; - } - } else { - if (aRhs.mTimingFunction) { - return false; - } - } - - // Last, by property IDL name. - return nsCSSProps::PropertyIDLNameSortPosition(aLhs.mProperty) < - nsCSSProps::PropertyIDLNameSortPosition(aRhs.mProperty); - } - }; -}; - /* static */ already_AddRefed KeyframeEffectReadOnly::Constructor( const GlobalObject& aGlobal, @@ -1016,67 +968,25 @@ KeyframeEffectReadOnly::GetFrames(JSContext*& aCx, nsTArray& aResult, ErrorResult& aRv) { - nsTArray entries; + MOZ_ASSERT(aResult.IsEmpty()); + MOZ_ASSERT(!aRv.Failed()); - for (const AnimationProperty& property : mProperties) { - for (size_t i = 0, n = property.mSegments.Length(); i < n; i++) { - const AnimationPropertySegment& segment = property.mSegments[i]; - - // We append the mFromValue for each segment. If the mToValue - // differs from the following segment's mFromValue, or if we're on - // the last segment, then we append the mToValue as well. - // - // Each value is annotated with whether it is a "first", "left", "right", - // or "last" value. "left" and "right" values represent the value coming - // in to and out of a given offset, in the middle of an animation. For - // most segments, the mToValue is the "left" and the following segment's - // mFromValue is the "right". The "first" and "last" values are the - // additional values assigned to offset 0 or 1 for reverse and forward - // filling. These annotations are used to ensure multiple values for a - // given property are sorted correctly and that we do not merge Keyframes - // with different values for the same offset. - - OrderedKeyframeValueEntry* entry = entries.AppendElement(); - entry->mProperty = property.mProperty; - entry->mValue = segment.mFromValue; - entry->mOffset = segment.mFromKey; - entry->mTimingFunction = &segment.mTimingFunction; - entry->mPosition = - segment.mFromKey == segment.mToKey && segment.mFromKey == 0.0f ? - ValuePosition::First : - ValuePosition::Right; - - if (i == n - 1 || - segment.mToValue != property.mSegments[i + 1].mFromValue) { - entry = entries.AppendElement(); - entry->mProperty = property.mProperty; - entry->mValue = segment.mToValue; - entry->mOffset = segment.mToKey; - entry->mTimingFunction = segment.mToKey == 1.0f ? - nullptr : &segment.mTimingFunction; - entry->mPosition = - segment.mFromKey == segment.mToKey && segment.mToKey == 1.0f ? - ValuePosition::Last : - ValuePosition::Left; - } - } + if (!aResult.SetCapacity(mFrames.Length(), mozilla::fallible)) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; } - entries.Sort(OrderedKeyframeValueEntry::ForKeyframeGenerationComparator()); - - for (size_t i = 0, n = entries.Length(); i < n; ) { - OrderedKeyframeValueEntry* entry = &entries[i]; - OrderedKeyframeValueEntry* previousEntry = nullptr; - - // Create a JS object with the BaseComputedKeyframe dictionary members. + for (const Keyframe& keyframe : mFrames) { + // Set up a dictionary object for the explicit members BaseComputedKeyframe keyframeDict; - keyframeDict.mOffset.SetValue(entry->mOffset); - keyframeDict.mComputedOffset.Construct(entry->mOffset); - if (entry->mTimingFunction && entry->mTimingFunction->isSome()) { - // If null, leave easing as its default "linear". - keyframeDict.mEasing.Truncate(); - entry->mTimingFunction->value().AppendToString(keyframeDict.mEasing); + if (keyframe.mOffset) { + keyframeDict.mOffset.SetValue(keyframe.mOffset.value()); } + keyframeDict.mComputedOffset.Construct(keyframe.mComputedOffset); + if (keyframe.mTimingFunction) { + keyframeDict.mEasing.Truncate(); + keyframe.mTimingFunction.ref().AppendToString(keyframeDict.mEasing); + } // else if null, leave easing as its default "linear". JS::Rooted keyframeJSValue(aCx); if (!ToJSValue(aCx, keyframeDict, &keyframeJSValue)) { @@ -1084,27 +994,33 @@ KeyframeEffectReadOnly::GetFrames(JSContext*& aCx, return; } - JS::Rooted keyframe(aCx, &keyframeJSValue.toObject()); - do { - const char* name = nsCSSProps::PropertyIDLName(entry->mProperty); - nsString stringValue; - StyleAnimationValue::UncomputeValue(entry->mProperty, - entry->mValue, - stringValue); + JS::Rooted keyframeObject(aCx, &keyframeJSValue.toObject()); + for (const PropertyValuePair& propertyValue : keyframe.mPropertyValues) { + + const char* name = nsCSSProps::PropertyIDLName(propertyValue.mProperty); + + // nsCSSValue::AppendToString does not accept shorthands properties but + // works with token stream values if we pass eCSSProperty_UNKNOWN as + // the property. + nsCSSProperty propertyForSerializing = + nsCSSProps::IsShorthand(propertyValue.mProperty) + ? eCSSProperty_UNKNOWN + : propertyValue.mProperty; + + nsAutoString stringValue; + propertyValue.mValue.AppendToString( + propertyForSerializing, stringValue, nsCSSValue::eNormalized); + JS::Rooted value(aCx); if (!ToJSValue(aCx, stringValue, &value) || - !JS_DefineProperty(aCx, keyframe, name, value, JSPROP_ENUMERATE)) { + !JS_DefineProperty(aCx, keyframeObject, name, value, + JSPROP_ENUMERATE)) { aRv.Throw(NS_ERROR_FAILURE); return; } - if (++i == n) { - break; - } - previousEntry = entry; - entry = &entries[i]; - } while (entry->SameKeyframe(*previousEntry)); + } - aResult.AppendElement(keyframe); + aResult.AppendElement(keyframeObject); } } @@ -1405,7 +1321,7 @@ KeyframeEffect::KeyframeEffect(nsIDocument* aDocument, CSSPseudoElementType aPseudoType, const TimingParams& aTiming) : KeyframeEffectReadOnly(aDocument, aTarget, aPseudoType, - new AnimationEffectTiming(aTiming, this)) + new AnimationEffectTiming(aDocument, aTiming, this)) { } @@ -1464,7 +1380,11 @@ void KeyframeEffect::NotifySpecifiedTimingUpdated() KeyframeEffect::~KeyframeEffect() { - mTiming->Unlink(); + // mTiming is cycle collected, so we have to do null check first even though + // mTiming shouldn't be null during the lifetime of KeyframeEffect. + if (mTiming) { + mTiming->Unlink(); + } } } // namespace dom diff --git a/dom/animation/KeyframeEffect.h b/dom/animation/KeyframeEffect.h index 32afff0e43..98e19da36c 100644 --- a/dom/animation/KeyframeEffect.h +++ b/dom/animation/KeyframeEffect.h @@ -63,6 +63,11 @@ struct PropertyValuePair // property values, we store the specified property value as a token stream // (string). nsCSSValue mValue; + + bool operator==(const PropertyValuePair& aOther) const { + return mProperty == aOther.mProperty && + mValue == aOther.mValue; + } }; /** @@ -82,6 +87,23 @@ struct PropertyValuePair */ struct Keyframe { + Keyframe() = default; + Keyframe(const Keyframe& aOther) = default; + Keyframe(Keyframe&& aOther) + { + *this = Move(aOther); + } + + Keyframe& operator=(const Keyframe& aOther) = default; + Keyframe& operator=(Keyframe&& aOther) + { + mOffset = aOther.mOffset; + mComputedOffset = aOther.mComputedOffset; + mTimingFunction = Move(aOther.mTimingFunction); + mPropertyValues = Move(aOther.mPropertyValues); + return *this; + } + Maybe mOffset; double mComputedOffset = 0.0; Maybe mTimingFunction; // Nothing() here means @@ -252,12 +274,6 @@ public: void GetComputedTimingAsDict(ComputedTimingProperties& aRetVal) const override; - // Return the duration of the active interval for the given duration and - // iteration count. - static StickyTimeDuration - ActiveDuration(const StickyTimeDuration& aIterationDuration, - double aIterationCount); - bool IsInPlay() const; bool IsCurrent() const; bool IsInEffect() const; @@ -265,6 +281,9 @@ public: void SetAnimation(Animation* aAnimation); Animation* GetAnimation() const { return mAnimation; } + void SetFrames(JSContext* aContext, JS::Handle aFrames, + ErrorResult& aRv); + void SetFrames(nsTArray&& aFrames, nsStyleContext* aStyleContext); const AnimationProperty* GetAnimationOfProperty(nsCSSProperty aProperty) const; bool HasAnimationOfProperty(nsCSSProperty aProperty) const { @@ -278,12 +297,10 @@ public: InfallibleTArray& Properties() { return mProperties; } - // Updates the set of properties using the supplied list whilst preserving - // the mWinsInCascade and mIsRunningOnCompositor state of any matching - // properties. - // Returns true if we updated anything in the properties. - bool UpdateProperties( - const InfallibleTArray& aProperties); + + // Update |mProperties| by recalculating from |mFrames| using |aStyleContext| + // to resolve specified values. + void UpdateProperties(nsStyleContext* aStyleContext); // Updates |aStyleRule| with the animation values produced by this // AnimationEffect for the current time except any properties already @@ -351,10 +368,14 @@ protected: nsCOMPtr mTarget; RefPtr mAnimation; - OwningNonNull mTiming; + RefPtr mTiming; CSSPseudoElementType mPseudoType; - InfallibleTArray mProperties; + // The specified keyframes. + nsTArray mFrames; + + // A set of per-property value arrays, derived from |mFrames|. + nsTArray mProperties; // The computed progress last time we composed the style rule. This is // used to detect when the progress is not changing (e.g. due to a step diff --git a/dom/animation/KeyframeUtils.cpp b/dom/animation/KeyframeUtils.cpp index 5f342ed4d9..bb35dd4615 100644 --- a/dom/animation/KeyframeUtils.cpp +++ b/dom/animation/KeyframeUtils.cpp @@ -21,11 +21,6 @@ #include "nsTArray.h" #include // For std::stable_sort -// TODO: Remove once we drop LookupStyleContext -#include "nsComputedDOMStyle.h" -#include "nsIDocument.h" -#include "nsIPresShell.h" - namespace mozilla { // ------------------------------------------------------------------ @@ -231,17 +226,6 @@ struct PropertyValuesPair typedef TPropertyPriorityComparator Comparator; }; -/** - * The result of parsing a JS object as a BaseKeyframe dictionary - * and getting its property-value pairs from its open-ended - * properties. - */ -struct OffsetIndexedKeyframe -{ - dom::binding_detail::FastBaseKeyframe mKeyframeDict; - nsTArray mPropertyValuePairs; -}; - /** * An additional property (for a property-values pair) found on a * BaseKeyframe or BasePropertyIndexedKeyframe object. @@ -267,25 +251,17 @@ struct AdditionalProperty }; }; -/** - * A property and StyleAnimationValue pair. - */ -struct KeyframeValue -{ - nsCSSProperty mProperty; - StyleAnimationValue mValue; -}; - /** * Data for a segment in a keyframe animation of a given property * whose value is a StyleAnimationValue. * - * KeyframeValueEntry is used in BuildAnimationPropertyListFromKeyframeSequence - * to gather data for each individual segment described by an author-supplied - * an IDL sequence value so that they can be parsed into mProperties. + * KeyframeValueEntry is used in GetAnimationPropertiesFromKeyframes + * to gather data for each individual segment. */ -struct KeyframeValueEntry : KeyframeValue +struct KeyframeValueEntry { + nsCSSProperty mProperty; + StyleAnimationValue mValue; float mOffset; Maybe mTimingFunction; @@ -334,26 +310,12 @@ public: // // ------------------------------------------------------------------ -static void -BuildAnimationPropertyListFromKeyframeSequence( - JSContext* aCx, - Element* aTarget, - CSSPseudoElementType aPseudoType, - JS::ForOfIterator& aIterator, - nsTArray& aResult, - ErrorResult& aRv); - static void GetKeyframeListFromKeyframeSequence(JSContext* aCx, JS::ForOfIterator& aIterator, nsTArray& aResult, ErrorResult& aRv); -static bool -ConvertKeyframeSequence(JSContext* aCx, - JS::ForOfIterator& aIterator, - nsTArray& aResult); - static bool ConvertKeyframeSequence(JSContext* aCx, JS::ForOfIterator& aIterator, @@ -380,35 +342,13 @@ static PropertyValuePair MakePropertyValuePair(nsCSSProperty aProperty, const nsAString& aStringValue, nsCSSParser& aParser, nsIDocument* aDocument); -static bool -HasValidOffsets(const nsTArray& aKeyframes); - static bool HasValidOffsets(const nsTArray& aKeyframes); -static void -ApplyDistributeSpacing(nsTArray& aKeyframes); - -static void -GenerateValueEntries(Element* aTarget, - CSSPseudoElementType aPseudoType, - nsTArray& aKeyframes, - nsTArray& aResult, - ErrorResult& aRv); - static void BuildSegmentsFromValueEntries(nsTArray& aEntries, nsTArray& aResult); -static void -BuildAnimationPropertyListFromPropertyIndexedKeyframes( - JSContext* aCx, - Element* aTarget, - CSSPseudoElementType aPseudoType, - JS::Handle aValue, - InfallibleTArray& aResult, - ErrorResult& aRv); - static void GetKeyframeListFromPropertyIndexedKeyframe(JSContext* aCx, JS::Handle aValue, @@ -420,64 +360,12 @@ RequiresAdditiveAnimation(const nsTArray& aKeyframes, nsIDocument* aDocument); -// TODO: This is only temporary until we remove the call sites for this. -already_AddRefed -LookupStyleContext(dom::Element* aElement, CSSPseudoElementType aPseudoType); - // ------------------------------------------------------------------ // // Public API // // ------------------------------------------------------------------ -/* static */ void -KeyframeUtils::BuildAnimationPropertyList( - JSContext* aCx, - Element* aTarget, - CSSPseudoElementType aPseudoType, - JS::Handle aFrames, - InfallibleTArray& aResult, - ErrorResult& aRv) -{ - MOZ_ASSERT(aResult.IsEmpty()); - - // See the description of frame lists in the spec: - // - // https://w3c.github.io/web-animations/#processing-a-frames-argument - // - // We don't support SharedKeyframeList yet, but we do support the other - // types of arguments. We manually implement the parts of JS-to-IDL union - // conversion algorithm from the Web IDL spec, since we have to represent - // this as an object? so we can look at the open-ended set of properties - // on these objects. - - if (!aFrames) { - // The argument was explicitly null. In this case, the default dictionary - // value for PropertyIndexedKeyframe would result in no keyframes. - return; - } - - // At this point we know we have an object. We try to convert it to a - // sequence first, and if that fails due to not being iterable, - // we try to convert it to PropertyIndexedKeyframe. - JS::Rooted objectValue(aCx, JS::ObjectValue(*aFrames)); - JS::ForOfIterator iter(aCx); - if (!iter.init(objectValue, JS::ForOfIterator::AllowNonIterable)) { - aRv.Throw(NS_ERROR_FAILURE); - return; - } - - if (iter.valueIsIterable()) { - BuildAnimationPropertyListFromKeyframeSequence(aCx, aTarget, aPseudoType, - iter, aResult, aRv); - } else { - BuildAnimationPropertyListFromPropertyIndexedKeyframes(aCx, aTarget, - aPseudoType, - objectValue, aResult, - aRv); - } -} - /* static */ nsTArray KeyframeUtils::GetKeyframesFromObject(JSContext* aCx, JS::Handle aFrames, @@ -579,6 +467,9 @@ KeyframeUtils::GetAnimationPropertiesFromKeyframes( CSSPseudoElementType aPseudoType, const nsTArray& aFrames) { + MOZ_ASSERT(aStyleContext); + MOZ_ASSERT(aElement); + nsTArray entries; for (const Keyframe& frame : aFrames) { @@ -614,19 +505,6 @@ KeyframeUtils::GetAnimationPropertiesFromKeyframes( MOZ_ASSERT(values.Length() == 1, "Longhand properties should produce a single" " StyleAnimationValue"); - - // 'visibility' requires special handling that is unique to CSS - // Transitions/CSS Animations/Web Animations (i.e. not SMIL) so we - // apply that here. - // - // Bug 1259285 - Move this code to StyleAnimationValue - if (pair.mProperty == eCSSProperty_visibility) { - MOZ_ASSERT(values[0].mValue.GetUnit() == - StyleAnimationValue::eUnit_Enumerated, - "unexpected unit"); - values[0].mValue.SetIntValue(values[0].mValue.GetIntValue(), - StyleAnimationValue::eUnit_Visibility); - } } for (auto& value : values) { @@ -660,65 +538,6 @@ KeyframeUtils::GetAnimationPropertiesFromKeyframes( // // ------------------------------------------------------------------ -/** - * Converts a JS object to an IDL sequence and builds an - * array of AnimationProperty objects for the keyframe animation - * that it specifies. - * - * @param aTarget The target of the animation. - * @param aIterator An already-initialized ForOfIterator for the JS - * object to iterate over as a sequence. - * @param aResult The array into which the resulting AnimationProperty - * objects will be appended. - */ -static void -BuildAnimationPropertyListFromKeyframeSequence( - JSContext* aCx, - Element* aTarget, - CSSPseudoElementType aPseudoType, - JS::ForOfIterator& aIterator, - nsTArray& aResult, - ErrorResult& aRv) -{ - // Convert the object in aIterator to sequence, producing - // an array of OffsetIndexedKeyframe objects. - AutoTArray keyframes; - if (!ConvertKeyframeSequence(aCx, aIterator, keyframes)) { - aRv.Throw(NS_ERROR_FAILURE); - return; - } - - // If the sequence<> had zero elements, we won't generate any - // keyframes. - if (keyframes.IsEmpty()) { - return; - } - - // Check that the keyframes are loosely sorted and with values all - // between 0% and 100%. - if (!HasValidOffsets(keyframes)) { - aRv.ThrowTypeError(); - return; - } - - // Fill in 0%/100% values if the first/element keyframes don't have - // a specified offset, and evenly space those that have a missing - // offset. (We don't support paced spacing yet.) - ApplyDistributeSpacing(keyframes); - - // Convert the OffsetIndexedKeyframes into a list of KeyframeValueEntry - // objects. - nsTArray entries; - GenerateValueEntries(aTarget, aPseudoType, keyframes, entries, aRv); - if (aRv.Failed()) { - return; - } - - // Finally, build an array of AnimationProperty objects in aResult - // corresponding to the entries. - BuildSegmentsFromValueEntries(entries, aResult); -} - /** * Converts a JS object to an IDL sequence. * @@ -761,52 +580,6 @@ GetKeyframeListFromKeyframeSequence(JSContext* aCx, } } -/** - * Converts a JS object wrapped by the given JS::ForIfIterator to an - * IDL sequence and stores the resulting OffsetIndexedKeyframe - * objects in aResult. - */ -static bool -ConvertKeyframeSequence(JSContext* aCx, - JS::ForOfIterator& aIterator, - nsTArray& aResult) -{ - JS::Rooted value(aCx); - for (;;) { - bool done; - if (!aIterator.next(&value, &done)) { - return false; - } - if (done) { - break; - } - // Each value found when iterating the object must be an object - // or null/undefined (which gets treated as a default {} dictionary - // value). - if (!value.isObject() && !value.isNullOrUndefined()) { - dom::ThrowErrorMessage(aCx, dom::MSG_NOT_OBJECT, - "Element of sequence argument"); - return false; - } - // Convert the JS value into a BaseKeyframe dictionary value. - OffsetIndexedKeyframe* keyframe = aResult.AppendElement(); - if (!keyframe->mKeyframeDict.Init( - aCx, value, "Element of sequence argument")) { - return false; - } - // Look for additional property-values pairs on the object. - if (value.isObject()) { - JS::Rooted object(aCx, &value.toObject()); - if (!GetPropertyValuesPairs(aCx, object, - ListAllowance::eDisallow, - keyframe->mPropertyValuePairs)) { - return false; - } - } - } - return true; -} - /** * Converts a JS object wrapped by the given JS::ForIfIterator to an * IDL sequence and stores the resulting Keyframe objects in @@ -1058,30 +831,6 @@ MakePropertyValuePair(nsCSSProperty aProperty, const nsAString& aStringValue, return { aProperty, value }; } -/** - * Checks that the given keyframes are loosely ordered (each keyframe's - * offset that is not null is greater than or equal to the previous - * non-null offset) and that all values are within the range [0.0, 1.0]. - * - * @return true if the keyframes' offsets are correctly ordered and - * within range; false otherwise. - */ -static bool -HasValidOffsets(const nsTArray& aKeyframes) -{ - double offset = 0.0; - for (const OffsetIndexedKeyframe& keyframe : aKeyframes) { - if (!keyframe.mKeyframeDict.mOffset.IsNull()) { - double thisOffset = keyframe.mKeyframeDict.mOffset.Value(); - if (thisOffset < offset || thisOffset > 1.0f) { - return false; - } - offset = thisOffset; - } - } - return true; -} - /** * Checks that the given keyframes are loosely ordered (each keyframe's * offset that is not null is greater than or equal to the previous @@ -1106,147 +855,6 @@ HasValidOffsets(const nsTArray& aKeyframes) return true; } -/** - * Fills in any null offsets for the given keyframes by applying the - * "distribute" spacing algorithm. - * - * http://w3c.github.io/web-animations/#distribute-keyframe-spacing-mode - */ -static void -ApplyDistributeSpacing(nsTArray& aKeyframes) -{ - // If the first or last keyframes have an unspecified offset, - // fill them in with 0% and 100%. If there is only a single keyframe, - // then it gets 100%. - if (aKeyframes.LastElement().mKeyframeDict.mOffset.IsNull()) { - aKeyframes.LastElement().mKeyframeDict.mOffset.SetValue(1.0); - } - if (aKeyframes[0].mKeyframeDict.mOffset.IsNull()) { - aKeyframes[0].mKeyframeDict.mOffset.SetValue(0.0); - } - - // Fill in remaining missing offsets. - size_t i = 0; - while (i < aKeyframes.Length() - 1) { - MOZ_ASSERT(!aKeyframes[i].mKeyframeDict.mOffset.IsNull()); - double start = aKeyframes[i].mKeyframeDict.mOffset.Value(); - size_t j = i + 1; - while (aKeyframes[j].mKeyframeDict.mOffset.IsNull()) { - ++j; - } - double end = aKeyframes[j].mKeyframeDict.mOffset.Value(); - size_t n = j - i; - for (size_t k = 1; k < n; ++k) { - double offset = start + double(k) / n * (end - start); - aKeyframes[i + k].mKeyframeDict.mOffset.SetValue(offset); - } - i = j; - } -} - -/** - * Splits out each property's keyframe animation segment information - * from the OffsetIndexedKeyframe objects into an array of KeyframeValueEntry. - * - * The easing string value in OffsetIndexedKeyframe objects is parsed - * into a ComputedTimingFunction value in the corresponding KeyframeValueEntry - * objects. - * - * @param aTarget The target of the animation. - * @param aPseudoType The pseudo type of the target if it is a pseudo element. - * @param aKeyframes The keyframes to read. - * @param aResult The array to append the resulting KeyframeValueEntry - * objects to. - */ -static void -GenerateValueEntries(Element* aTarget, - CSSPseudoElementType aPseudoType, - nsTArray& aKeyframes, - nsTArray& aResult, - ErrorResult& aRv) -{ - RefPtr styleContext = - LookupStyleContext(aTarget, aPseudoType); - if (!styleContext) { - aRv.Throw(NS_ERROR_FAILURE); - return; - } - - nsCSSPropertySet properties; // All properties encountered. - nsCSSPropertySet propertiesWithFromValue; // Those with a defined 0% value. - nsCSSPropertySet propertiesWithToValue; // Those with a defined 100% value. - - for (OffsetIndexedKeyframe& keyframe : aKeyframes) { - Maybe easing = - TimingParams::ParseEasing(keyframe.mKeyframeDict.mEasing, - aTarget->OwnerDoc(), aRv); - if (aRv.Failed()) { - return; - } - float offset = float(keyframe.mKeyframeDict.mOffset.Value()); - // We ignore keyframe.mKeyframeDict.mComposite since we don't support - // composite modes on keyframes yet. - - // keyframe.mPropertyValuePairs is currently sorted by CSS property IDL - // name, since that was the order we read the properties from the JS - // object. Re-sort the list so that longhand properties appear before - // shorthands, and with shorthands all appearing in increasing order of - // number of components. For two longhand properties, or two shorthands - // with the same number of components, sort by IDL name. - // - // @see PropertyPriorityComparator. - keyframe.mPropertyValuePairs.Sort(PropertyValuesPair::Comparator()); - - nsCSSPropertySet propertiesOnThisKeyframe; - for (const PropertyValuesPair& pair : keyframe.mPropertyValuePairs) { - MOZ_ASSERT(pair.mValues.Length() == 1, - "ConvertKeyframeSequence should have parsed single " - "DOMString values from the property-values pairs"); - // Parse the property's string value and produce a KeyframeValueEntry (or - // more than one, for shorthands) for it. - nsTArray values; - if (StyleAnimationValue::ComputeValues(pair.mProperty, - nsCSSProps::eEnabledForAllContent, - aTarget, - styleContext, - pair.mValues[0], - /* aUseSVGMode */ false, - values)) { - for (auto& value : values) { - // If we already got a value for this property on the keyframe, - // skip this one. - if (propertiesOnThisKeyframe.HasProperty(value.mProperty)) { - continue; - } - - KeyframeValueEntry* entry = aResult.AppendElement(); - entry->mOffset = offset; - entry->mProperty = value.mProperty; - entry->mValue = value.mValue; - entry->mTimingFunction = easing; - - if (offset == 0.0) { - propertiesWithFromValue.AddProperty(value.mProperty); - } else if (offset == 1.0) { - propertiesWithToValue.AddProperty(value.mProperty); - } - propertiesOnThisKeyframe.AddProperty(value.mProperty); - properties.AddProperty(value.mProperty); - } - } - } - } - - // We don't support additive segments and so can't support missing properties - // using their underlying value in 0% and 100% keyframes. Throw an exception - // until we do support this. - if (!propertiesWithFromValue.Equals(properties) || - !propertiesWithToValue.Equals(properties)) { - aRv.Throw(NS_ERROR_DOM_ANIM_MISSING_PROPS_ERR); - return; - } -} - /** * Builds an array of AnimationProperty objects to represent the keyframe * animation segments in aEntries. @@ -1347,183 +955,6 @@ BuildSegmentsFromValueEntries(nsTArray& aEntries, } } -/** - * Converts a JS object to an IDL PropertyIndexedKeyframe and builds an - * array of AnimationProperty objects for the keyframe animation - * that it specifies. - * - * @param aTarget The target of the animation. - * @param aValue The JS object. - * @param aResult The array into which the resulting AnimationProperty - * objects will be appended. - */ -static void -BuildAnimationPropertyListFromPropertyIndexedKeyframes( - JSContext* aCx, - Element* aTarget, - CSSPseudoElementType aPseudoType, - JS::Handle aValue, - InfallibleTArray& aResult, - ErrorResult& aRv) -{ - MOZ_ASSERT(aValue.isObject()); - - RefPtr styleContext = - LookupStyleContext(aTarget, aPseudoType); - if (!styleContext) { - aRv.Throw(NS_ERROR_FAILURE); - return; - } - - // Convert the object to a PropertyIndexedKeyframe dictionary to - // get its explicit dictionary members. - dom::binding_detail::FastBasePropertyIndexedKeyframe keyframes; - if (!keyframes.Init(aCx, aValue, "BasePropertyIndexedKeyframe argument", - false)) { - aRv.Throw(NS_ERROR_FAILURE); - return; - } - - Maybe easing = - TimingParams::ParseEasing(keyframes.mEasing, aTarget->OwnerDoc(), aRv); - - // We ignore easing.mComposite since we don't support composite modes on - // keyframes yet. - - // Get all the property--value-list pairs off the object. - JS::Rooted object(aCx, &aValue.toObject()); - nsTArray propertyValuesPairs; - if (!GetPropertyValuesPairs(aCx, object, ListAllowance::eAllow, - propertyValuesPairs)) { - aRv.Throw(NS_ERROR_FAILURE); - return; - } - - // We must keep track of which properties we've already generated - // an AnimationProperty since the author could have specified both a - // shorthand and one of its component longhands on the - // PropertyIndexedKeyframe. - nsCSSPropertySet properties; - - // Create AnimationProperty objects for each PropertyValuesPair, applying - // the "distribute" spacing algorithm to the segments. - for (const PropertyValuesPair& pair : propertyValuesPairs) { - size_t count = pair.mValues.Length(); - if (count == 0) { - // No animation values for this property. - continue; - } - if (count == 1) { - // We don't support additive segments and so can't support an - // animation that goes from the underlying value to this - // specified value. Throw an exception until we do support this. - aRv.Throw(NS_ERROR_DOM_ANIM_MISSING_PROPS_ERR); - return; - } - - // If we find an invalid value, we don't create a segment for it, but - // we adjust the surrounding segments so that the timing of the segments - // is the same as if we did support it. For example, animating with - // values ["red", "green", "yellow", "invalid", "blue"] will generate - // segments with this timing: - // - // 0.00 -> 0.25 : red -> green - // 0.25 -> 0.50 : green -> yellow - // 0.50 -> 1.00 : yellow -> blue - // - // With future spec clarifications we might decide to preserve the invalid - // value on the segment and make the animation code deal with the invalid - // value instead. - nsTArray fromValues; - float fromKey = 0.0f; - if (!StyleAnimationValue::ComputeValues(pair.mProperty, - nsCSSProps::eEnabledForAllContent, - aTarget, - styleContext, - pair.mValues[0], - /* aUseSVGMode */ false, - fromValues)) { - // We need to throw for an invalid first value, since that would imply an - // additive animation, which we don't support yet. - aRv.Throw(NS_ERROR_DOM_ANIM_MISSING_PROPS_ERR); - return; - } - - if (fromValues.IsEmpty()) { - // All longhand components of a shorthand pair.mProperty must be disabled. - continue; - } - - // Create AnimationProperty objects for each property that had a - // value computed. When pair.mProperty is a longhand, it is just - // that property. When pair.mProperty is a shorthand, we'll have - // one property per longhand component. - nsTArray animationPropertyIndexes; - animationPropertyIndexes.SetLength(fromValues.Length()); - for (size_t i = 0, n = fromValues.Length(); i < n; ++i) { - nsCSSProperty p = fromValues[i].mProperty; - bool found = false; - if (properties.HasProperty(p)) { - // We have already dealt with this property. Look up and - // overwrite the old AnimationProperty object. - for (size_t j = 0, m = aResult.Length(); j < m; ++j) { - if (aResult[j].mProperty == p) { - aResult[j].mSegments.Clear(); - animationPropertyIndexes[i] = j; - found = true; - break; - } - } - MOZ_ASSERT(found, "properties is inconsistent with aResult"); - } - if (!found) { - // This is the first time we've encountered this property. - animationPropertyIndexes[i] = aResult.Length(); - AnimationProperty* animationProperty = aResult.AppendElement(); - animationProperty->mProperty = p; - properties.AddProperty(p); - } - } - - double portion = 1.0 / (count - 1); - for (size_t i = 0; i < count - 1; ++i) { - nsTArray toValues; - float toKey = (i + 1) * portion; - if (!StyleAnimationValue::ComputeValues(pair.mProperty, - nsCSSProps::eEnabledForAllContent, - aTarget, - styleContext, - pair.mValues[i + 1], - /* aUseSVGMode */ false, - toValues)) { - if (i + 1 == count - 1) { - // We need to throw for an invalid last value, since that would - // imply an additive animation, which we don't support yet. - aRv.Throw(NS_ERROR_DOM_ANIM_MISSING_PROPS_ERR); - return; - } - // Otherwise, skip the segment. - continue; - } - MOZ_ASSERT(toValues.Length() == fromValues.Length(), - "should get the same number of properties as the last time " - "we called ComputeValues for pair.mProperty"); - for (size_t j = 0, n = toValues.Length(); j < n; ++j) { - size_t index = animationPropertyIndexes[j]; - AnimationPropertySegment* segment = - aResult[index].mSegments.AppendElement(); - segment->mFromKey = fromKey; - segment->mFromValue = fromValues[j].mValue; - segment->mToKey = toKey; - segment->mToValue = toValues[j].mValue; - segment->mTimingFunction = easing; - } - fromValues = Move(toValues); - fromKey = toKey; - } - } -} - /** * Converts a JS object representing a property-indexed keyframe into * an array of Keyframe objects. @@ -1695,19 +1126,4 @@ RequiresAdditiveAnimation(const nsTArray& aKeyframes, !propertiesWithToValue.Equals(properties); } -already_AddRefed -LookupStyleContext(dom::Element* aElement, CSSPseudoElementType aPseudoType) -{ - nsIDocument* doc = aElement->GetCurrentDoc(); - nsIPresShell* shell = doc->GetShell(); - if (!shell) { - return nullptr; - } - - nsIAtom* pseudo = - aPseudoType < CSSPseudoElementType::Count ? - nsCSSPseudoElements::GetPseudoAtom(aPseudoType) : nullptr; - return nsComputedDOMStyle::GetStyleContextForElement(aElement, pseudo, shell); -} - } // namespace mozilla diff --git a/dom/animation/KeyframeUtils.h b/dom/animation/KeyframeUtils.h index c57f65e405..d925b0b2eb 100644 --- a/dom/animation/KeyframeUtils.h +++ b/dom/animation/KeyframeUtils.h @@ -33,25 +33,6 @@ namespace mozilla { class KeyframeUtils { public: - /** - * Converts a JS value to a property-indexed keyframe or a sequence of - * regular keyframes and builds an array of AnimationProperty objects for the - * keyframe animation that it specifies. - * - * @param aTarget The target of the animation, used to resolve style - * for a property's underlying value if needed. - * @param aFrames The JS value, provided as an optional IDL |object?| value, - * that is the keyframe list specification. - * @param aResult The array into which the resulting AnimationProperty - * objects will be appended. - */ - static void - BuildAnimationPropertyList(JSContext* aCx, Element* aTarget, - CSSPseudoElementType aPseudoType, - JS::Handle aFrames, - InfallibleTArray& aResult, - ErrorResult& aRv); - /** * Converts a JS value representing a property-indexed keyframe or a sequence * of keyframes to an array of Keyframe objects. diff --git a/dom/animation/TimingParams.h b/dom/animation/TimingParams.h index 3be6f80ca7..a97322569c 100644 --- a/dom/animation/TimingParams.h +++ b/dom/animation/TimingParams.h @@ -96,6 +96,26 @@ struct TimingParams dom::FillMode mFill = dom::FillMode::Auto; Maybe mFunction; + // Return the duration of the active interval calculated by duration and + // iteration count. + StickyTimeDuration ActiveDuration() const + { + // If either the iteration duration or iteration count is zero, + // Web Animations says that the active duration is zero. This is to + // ensure that the result is defined when the other argument is Infinity. + static const StickyTimeDuration zeroDuration; + if (!mDuration || *mDuration == zeroDuration || mIterations == 0.0) { + return zeroDuration; + } + + return mDuration->MultDouble(mIterations); + } + + StickyTimeDuration EndTime() const + { + return mDelay + ActiveDuration() + mEndDelay; + } + bool operator==(const TimingParams& aOther) const; bool operator!=(const TimingParams& aOther) const { diff --git a/dom/animation/test/css-animations/file_keyframeeffect-getframes.html b/dom/animation/test/css-animations/file_keyframeeffect-getframes.html index c7b755b7e5..fe3d2852bd 100644 --- a/dom/animation/test/css-animations/file_keyframeeffect-getframes.html +++ b/dom/animation/test/css-animations/file_keyframeeffect-getframes.html @@ -15,8 +15,8 @@ } @keyframes anim-only-non-animatable { - from { display: none; } - to { display: inline; } + from { animation-duration: 3s; } + to { animation-duration: 5s; } } @keyframes anim-simple { @@ -59,6 +59,13 @@ 50% { color: blue; } } +@keyframes anim-partially-omit-to { + from { margin-top: 50px; + margin-bottom: 100px; } + to { margin-top: 150px !important; /* ignored */ + margin-bottom: 200px; } +} + @keyframes anim-different-props { from { color: black; margin-top: 8px; } 25% { color: blue; } @@ -100,6 +107,27 @@ 50% { margin-bottom: 10px; animation-timing-function: step-end; } to { margin-top: 20px; margin-right: 20px; margin-bottom: 20px; } } + +@keyframes anim-overriding { + from { padding-top: 50px } + 50%, from { padding-top: 30px } /* wins: 0% */ + 75%, 85%, 50% { padding-top: 20px } /* wins: 75%, 50% */ + 100%, 85% { padding-top: 70px } /* wins: 100% */ + 85.1% { padding-top: 60px } /* wins: 85.1% */ + 85% { padding-top: 30px } /* wins: 85% */ +} + +@keyframes anim-filter { + to { filter: blur(5px) sepia(60%) saturate(30%); } +} + +@keyframes anim-text-shadow { + to { text-shadow: none; } +} + +@keyframes anim-background-size { + to { background-size: 50%, 6px, contain } +} diff --git a/dom/apps/PermissionsTable.jsm b/dom/apps/PermissionsTable.jsm index 835a7c382c..007beb07e0 100644 --- a/dom/apps/PermissionsTable.jsm +++ b/dom/apps/PermissionsTable.jsm @@ -487,11 +487,6 @@ this.PermissionsTable = { geolocation: { privileged: DENY_ACTION, certified: ALLOW_ACTION }, - "external-app": { - app: DENY_ACTION, - privileged: ALLOW_ACTION, - certified: ALLOW_ACTION - }, "system-update": { app: DENY_ACTION, privileged: DENY_ACTION, diff --git a/dom/base/AnonymousContent.cpp b/dom/base/AnonymousContent.cpp index 90aeab6bca..9d4e42b97b 100644 --- a/dom/base/AnonymousContent.cpp +++ b/dom/base/AnonymousContent.cpp @@ -141,7 +141,7 @@ Element* AnonymousContent::GetElementById(const nsAString& aElementId) { // This can be made faster in the future if needed. - nsCOMPtr elementId = do_GetAtom(aElementId); + nsCOMPtr elementId = NS_Atomize(aElementId); for (nsIContent* kid = mContentNode->GetFirstChild(); kid; kid = kid->GetNextNode(mContentNode)) { if (!kid->IsElement()) { diff --git a/dom/base/DOMImplementation.cpp b/dom/base/DOMImplementation.cpp index 3f99680ba7..dd8f1c479c 100644 --- a/dom/base/DOMImplementation.cpp +++ b/dom/base/DOMImplementation.cpp @@ -70,7 +70,7 @@ DOMImplementation::CreateDocumentType(const nsAString& aQualifiedName, return nullptr; } - nsCOMPtr name = do_GetAtom(aQualifiedName); + nsCOMPtr name = NS_Atomize(aQualifiedName); if (!name) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return nullptr; diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index 8309951856..0eab968932 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -206,7 +206,7 @@ Element::IntrinsicState() const void Element::NotifyStateChange(EventStates aStates) { - nsIDocument* doc = GetCrossShadowCurrentDoc(); + nsIDocument* doc = GetComposedDoc(); if (doc) { nsAutoScriptBlocker scriptBlocker; doc->ContentStateChanged(this, aStates); @@ -232,7 +232,7 @@ Element::UpdateState(bool aNotify) if (aNotify) { EventStates changedStates = oldState ^ mState; if (!changedStates.IsEmpty()) { - nsIDocument* doc = GetCrossShadowCurrentDoc(); + nsIDocument* doc = GetComposedDoc(); if (doc) { nsAutoScriptBlocker scriptBlocker; doc->ContentStateChanged(this, changedStates); @@ -1019,7 +1019,7 @@ Element::CreateShadowRoot(ErrorResult& aError) return nullptr; } - nsIDocument* doc = GetCrossShadowCurrentDoc(); + nsIDocument* doc = GetComposedDoc(); nsIContent* destroyedFramesFor = nullptr; if (doc) { nsIPresShell* shell = doc->GetShell(); @@ -1070,7 +1070,7 @@ Element::CreateShadowRoot(ErrorResult& aError) // Recreate the frame for the bound content because binding a ShadowRoot // changes how things are rendered. if (doc) { - MOZ_ASSERT(doc == GetCrossShadowCurrentDoc()); + MOZ_ASSERT(doc == GetComposedDoc()); nsIPresShell* shell = doc->GetShell(); if (shell) { shell->CreateFramesFor(destroyedFramesFor); @@ -2117,8 +2117,8 @@ Element::DispatchClickEvent(nsPresContext* aPresContext, NS_PRECONDITION(aStatus, "Null out param?"); WidgetMouseEvent event(aSourceEvent->IsTrusted(), eMouseClick, - aSourceEvent->widget, WidgetMouseEvent::eReal); - event.refPoint = aSourceEvent->refPoint; + aSourceEvent->mWidget, WidgetMouseEvent::eReal); + event.mRefPoint = aSourceEvent->mRefPoint; uint32_t clickCount = 1; float pressure = 0; uint16_t inputSource = 0; @@ -2133,7 +2133,7 @@ Element::DispatchClickEvent(nsPresContext* aPresContext, event.pressure = pressure; event.clickCount = clickCount; event.inputSource = inputSource; - event.modifiers = aSourceEvent->modifiers; + event.mModifiers = aSourceEvent->mModifiers; if (aExtraEventFlags) { // Be careful not to overwrite existing flags! event.mFlags.Union(*aExtraEventFlags); @@ -2497,7 +2497,7 @@ Element::SetAttrAndNotify(int32_t aNamespaceID, nsAutoString newValue; GetAttr(aNamespaceID, aName, newValue); if (!newValue.IsEmpty()) { - mutation.mNewAttrValue = do_GetAtom(newValue); + mutation.mNewAttrValue = NS_Atomize(newValue); } if (!oldValue->IsEmptyString()) { mutation.mPrevAttrValue = oldValue->GetAsAtom(); @@ -2735,7 +2735,7 @@ Element::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName, nsAutoString value; oldValue.ToString(value); if (!value.IsEmpty()) - mutation.mPrevAttrValue = do_GetAtom(value); + mutation.mPrevAttrValue = NS_Atomize(value); mutation.mAttrChange = nsIDOMMutationEvent::REMOVAL; mozAutoSubtreeModified subtree(OwnerDoc(), this); @@ -2974,7 +2974,7 @@ Element::PreHandleEventForLinks(EventChainPreVisitor& aVisitor) MOZ_FALLTHROUGH; case eFocus: { InternalFocusEvent* focusEvent = aVisitor.mEvent->AsFocusEvent(); - if (!focusEvent || !focusEvent->isRefocus) { + if (!focusEvent || !focusEvent->mIsRefocus) { nsAutoString target; GetLinkTarget(target); nsContentUtils::TriggerLink(this, aVisitor.mPresContext, absURI, target, @@ -3066,7 +3066,7 @@ Element::PostHandleEventForLinks(EventChainPostVisitor& aVisitor) // DOMActive event should be trusted since the activation is actually // occurred even if the cause is an untrusted click event. InternalUIEvent actEvent(true, eLegacyDOMActivate, mouseEvent); - actEvent.detail = 1; + actEvent.mDetail = 1; rv = shell->HandleDOMEventWithTarget(this, &actEvent, &status); if (NS_SUCCEEDED(rv)) { @@ -3078,7 +3078,7 @@ Element::PostHandleEventForLinks(EventChainPostVisitor& aVisitor) } case eLegacyDOMActivate: { - if (aVisitor.mEvent->originalTarget == this) { + if (aVisitor.mEvent->mOriginalTarget == this) { nsAutoString target; GetLinkTarget(target); const InternalUIEvent* activeEvent = aVisitor.mEvent->AsUIEvent(); diff --git a/dom/base/Element.h b/dom/base/Element.h index 9f303ad1ca..a75ded0f0d 100644 --- a/dom/base/Element.h +++ b/dom/base/Element.h @@ -715,7 +715,7 @@ public: aError.Throw(NS_ERROR_DOM_INVALID_POINTER_ERR); return; } - if (!IsInDoc()) { + if (!IsInUncomposedDoc()) { aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp index 65a2e51073..87a62a91d7 100644 --- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -275,7 +275,7 @@ nsIContent::LookupNamespaceURIInternal(const nsAString& aNamespacePrefix, nsCOMPtr name; if (!aNamespacePrefix.IsEmpty()) { - name = do_GetAtom(aNamespacePrefix); + name = NS_Atomize(aNamespacePrefix); NS_ENSURE_TRUE(name, NS_ERROR_OUT_OF_MEMORY); } else { @@ -637,7 +637,7 @@ FragmentOrElement::FragmentOrElement(already_AddRefed&& FragmentOrElement::~FragmentOrElement() { - NS_PRECONDITION(!IsInDoc(), + NS_PRECONDITION(!IsInUncomposedDoc(), "Please remove this from the document properly"); if (GetParent()) { NS_RELEASE(mParent); @@ -687,7 +687,7 @@ nsIContent::PreHandleEvent(EventChainPreVisitor& aVisitor) // dispatched or when we're about to propagate from // chrome access only subtree or if we are about to propagate out of // a shadow root to a shadow root host. - ((this == aVisitor.mEvent->originalTarget && + ((this == aVisitor.mEvent->mOriginalTarget && !ChromeOnlyAccess()) || isAnonForEvents || GetShadowRoot())) { nsCOMPtr relatedTarget = do_QueryInterface(aVisitor.mEvent->AsMouseEvent()->relatedTarget); @@ -712,7 +712,7 @@ nsIContent::PreHandleEvent(EventChainPreVisitor& aVisitor) // If this is the original target, aVisitor.mRelatedTargetIsInAnon // must be updated. if (isAnonForEvents || aVisitor.mRelatedTargetIsInAnon || - (aVisitor.mEvent->originalTarget == this && + (aVisitor.mEvent->mOriginalTarget == this && (aVisitor.mRelatedTargetIsInAnon = relatedTarget->ChromeOnlyAccess()))) { nsIContent* anonOwner = FindChromeAccessOnlySubtreeOwner(this); @@ -731,7 +731,7 @@ nsIContent::PreHandleEvent(EventChainPreVisitor& aVisitor) if (anonOwner == anonOwnerRelated) { #ifdef DEBUG_smaug nsCOMPtr originalTarget = - do_QueryInterface(aVisitor.mEvent->originalTarget); + do_QueryInterface(aVisitor.mEvent->mOriginalTarget); nsAutoString ot, ct, rt; if (originalTarget) { originalTarget->NodeInfo()->NameAtom()->ToString(ot); @@ -870,7 +870,8 @@ nsIContent::PreHandleEvent(EventChainPreVisitor& aVisitor) #ifdef DEBUG // If a DOM event is explicitly dispatched using node.dispatchEvent(), then // all the events are allowed even in the native anonymous content.. - nsCOMPtr t = do_QueryInterface(aVisitor.mEvent->originalTarget); + nsCOMPtr t = + do_QueryInterface(aVisitor.mEvent->mOriginalTarget); NS_ASSERTION(!t || !t->ChromeOnlyAccess() || aVisitor.mEvent->mClass != eMutationEventClass || aVisitor.mDOMEvent, @@ -878,7 +879,7 @@ nsIContent::PreHandleEvent(EventChainPreVisitor& aVisitor) #endif aVisitor.mEventTargetAtParent = parent; } else if (parent && aVisitor.mOriginalTargetIsInAnon) { - nsCOMPtr content(do_QueryInterface(aVisitor.mEvent->target)); + nsCOMPtr content(do_QueryInterface(aVisitor.mEvent->mTarget)); if (content && content->GetBindingParent() == parent) { aVisitor.mEventTargetAtParent = parent; } @@ -1870,7 +1871,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(FragmentOrElement) } nsAutoCString orphan; - if (!tmp->IsInDoc() && + if (!tmp->IsInUncomposedDoc() && // Ignore xbl:content, which is never in the document and hence always // appears to be orphaned. !tmp->NodeInfo()->Equals(nsGkAtoms::content, kNameSpaceID_XBL)) { diff --git a/dom/base/TextInputProcessor.cpp b/dom/base/TextInputProcessor.cpp index dd0c78317f..a552786c97 100644 --- a/dom/base/TextInputProcessor.cpp +++ b/dom/base/TextInputProcessor.cpp @@ -254,8 +254,9 @@ TextInputProcessor::IsValidEventTypeForComposition( return true; } if (aKeyboardEvent.mMessage == eUnidentifiedEvent && - aKeyboardEvent.userType && - nsDependentAtomString(aKeyboardEvent.userType).EqualsLiteral("on")) { + aKeyboardEvent.mSpecifiedEventType && + nsDependentAtomString( + aKeyboardEvent.mSpecifiedEventType).EqualsLiteral("on")) { return true; } return false; @@ -766,6 +767,8 @@ TextInputProcessor::PrepareKeyboardEventToDispatch( aKeyboardEvent.mKeyNameIndex); } + aKeyboardEvent.mIsSynthesizedByTIP = (mForTests)? false : true; + return NS_OK; } @@ -826,7 +829,7 @@ TextInputProcessor::KeydownInternal(const WidgetKeyboardEvent& aKeyboardEvent, } else if (NS_WARN_IF(aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT)) { return NS_ERROR_INVALID_ARG; } - keyEvent.modifiers = GetActiveModifiers(); + keyEvent.mModifiers = GetActiveModifiers(); RefPtr kungfuDeathGrip(mDispatcher); rv = IsValidStateForComposition(); @@ -906,7 +909,7 @@ TextInputProcessor::KeyupInternal(const WidgetKeyboardEvent& aKeyboardEvent, } else if (NS_WARN_IF(aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT)) { return NS_ERROR_INVALID_ARG; } - keyEvent.modifiers = GetActiveModifiers(); + keyEvent.mModifiers = GetActiveModifiers(); RefPtr kungfuDeathGrip(mDispatcher); rv = IsValidStateForComposition(); diff --git a/dom/base/domerr.msg b/dom/base/domerr.msg index ef8949ac55..5c8a63d87a 100644 --- a/dom/base/domerr.msg +++ b/dom/base/domerr.msg @@ -119,7 +119,6 @@ DOM4_MSG_DEF(BtAuthRejectedError, "Authentication rejected", NS_ERROR_DOM_BLUET DOM4_MSG_DEF(NotSupportedError, "Animation to or from an underlying value is not yet supported.", NS_ERROR_DOM_ANIM_MISSING_PROPS_ERR) DOM4_MSG_DEF(NotSupportedError, "Animation with no target is not yet supported.", NS_ERROR_DOM_ANIM_NO_TARGET_ERR) -DOM4_MSG_DEF(NotSupportedError, "Animation with a target not bound to a document is not yet supported.", NS_ERROR_DOM_ANIM_TARGET_NOT_IN_DOC_ERR) /* common global codes (from nsError.h) */ diff --git a/dom/base/nsAttrValue.cpp b/dom/base/nsAttrValue.cpp index 3ee8bd348c..bac84a9cbb 100644 --- a/dom/base/nsAttrValue.cpp +++ b/dom/base/nsAttrValue.cpp @@ -1228,7 +1228,7 @@ nsAttrValue::ParseAtom(const nsAString& aValue) { ResetIfSet(); - nsCOMPtr atom = NS_NewAtom(aValue); + nsCOMPtr atom = NS_Atomize(aValue); if (atom) { SetPtrValueAndType(atom.forget().take(), eAtomBase); } diff --git a/dom/base/nsContentList.cpp b/dom/base/nsContentList.cpp index f51b30ed8f..3dea767ca8 100644 --- a/dom/base/nsContentList.cpp +++ b/dom/base/nsContentList.cpp @@ -229,12 +229,12 @@ NS_GetContentList(nsINode* aRootNode, if (!list) { // We need to create a ContentList and add it to our new entry, if // we have an entry - nsCOMPtr xmlAtom = do_GetAtom(aTagname); + nsCOMPtr xmlAtom = NS_Atomize(aTagname); nsCOMPtr htmlAtom; if (aMatchNameSpaceId == kNameSpaceID_Unknown) { nsAutoString lowercaseName; nsContentUtils::ASCIIToLower(aTagname, lowercaseName); - htmlAtom = do_GetAtom(lowercaseName); + htmlAtom = NS_Atomize(lowercaseName); } else { htmlAtom = xmlAtom; } @@ -518,7 +518,7 @@ nsContentList::NamedItem(const nsAString& aName, bool aDoFlush) uint32_t i, count = mElements.Length(); // Typically IDs and names are atomized - nsCOMPtr name = do_GetAtom(aName); + nsCOMPtr name = NS_Atomize(aName); NS_ENSURE_TRUE(name, nullptr); for (i = 0; i < count; i++) { diff --git a/dom/base/nsContentSink.cpp b/dom/base/nsContentSink.cpp index 6289538af7..bc235fd64f 100644 --- a/dom/base/nsContentSink.cpp +++ b/dom/base/nsContentSink.cpp @@ -811,7 +811,7 @@ nsContentSink::ProcessMETATag(nsIContent* aContent) nsAutoString result; aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::content, result); if (!result.IsEmpty()) { - nsCOMPtr fieldAtom(do_GetAtom(header)); + nsCOMPtr fieldAtom(NS_Atomize(header)); rv = ProcessHeaderData(fieldAtom, result, aContent); } } diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index 4241c17188..d91c81c3f3 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -3888,7 +3888,7 @@ nsContentUtils::MatchElementId(nsIContent *aContent, const nsAString& aId) NS_PRECONDITION(!aId.IsEmpty(), "Will match random elements"); // ID attrs are generally stored as atoms, so just atomize this up front - nsCOMPtr id(do_GetAtom(aId)); + nsCOMPtr id(NS_Atomize(aId)); if (!id) { // OOM, so just bail return nullptr; @@ -4005,7 +4005,7 @@ nsContentUtils::HasMutationListeners(nsINode* aNode, doc->MayDispatchMutationEvent(aTargetForSubtreeModified); // If we have a window, we can check it for mutation listeners now. - if (aNode->IsInDoc()) { + if (aNode->IsInUncomposedDoc()) { nsCOMPtr piTarget(do_QueryInterface(window)); if (piTarget) { EventListenerManager* manager = piTarget->GetExistingListenerManager(); @@ -4103,7 +4103,7 @@ nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments() for (auto i = sEventListenerManagersHash->Iter(); !i.Done(); i.Next()) { auto entry = static_cast(i.Get()); nsINode* n = static_cast(entry->mListenerManager->GetTarget()); - if (n && n->IsInDoc() && + if (n && n->IsInUncomposedDoc() && nsCCUncollectableMarker::InGeneration(n->OwnerDoc()->GetMarkedCCGeneration())) { entry->mListenerManager->MarkForCC(); } @@ -5143,7 +5143,7 @@ static void ProcessViewportToken(nsIDocument *aDocument, /* Check for known keys. If we find a match, insert the appropriate * information into the document header. */ - nsCOMPtr key_atom = do_GetAtom(key); + nsCOMPtr key_atom = NS_Atomize(key); if (key_atom == nsGkAtoms::height) aDocument->SetHeaderData(nsGkAtoms::viewport_height, value); else if (key_atom == nsGkAtoms::width) @@ -5246,7 +5246,7 @@ nsContentUtils::GetDragSession() nsresult nsContentUtils::SetDataTransferInEvent(WidgetDragEvent* aDragEvent) { - if (aDragEvent->dataTransfer || !aDragEvent->IsTrusted()) { + if (aDragEvent->mDataTransfer || !aDragEvent->IsTrusted()) { return NS_OK; } @@ -5274,7 +5274,7 @@ nsContentUtils::SetDataTransferInEvent(WidgetDragEvent* aDragEvent) // from another application. In either case, a new dataTransfer should // be created that reflects the data. initialDataTransfer = - new DataTransfer(aDragEvent->target, aDragEvent->mMessage, true, -1); + new DataTransfer(aDragEvent->mTarget, aDragEvent->mMessage, true, -1); // now set it in the drag session so we don't need to create it again dragSession->SetDataTransfer(initialDataTransfer); @@ -5287,11 +5287,13 @@ nsContentUtils::SetDataTransferInEvent(WidgetDragEvent* aDragEvent) } // each event should use a clone of the original dataTransfer. - initialDataTransfer->Clone(aDragEvent->target, aDragEvent->mMessage, - aDragEvent->userCancelled, + initialDataTransfer->Clone(aDragEvent->mTarget, aDragEvent->mMessage, + aDragEvent->mUserCancelled, isCrossDomainSubFrameDrop, - getter_AddRefs(aDragEvent->dataTransfer)); - NS_ENSURE_TRUE(aDragEvent->dataTransfer, NS_ERROR_OUT_OF_MEMORY); + getter_AddRefs(aDragEvent->mDataTransfer)); + if (NS_WARN_IF(!aDragEvent->mDataTransfer)) { + return NS_ERROR_OUT_OF_MEMORY; + } // for the dragenter and dragover events, initialize the drop effect // from the drop action, which platform specific widget code sets before @@ -5299,8 +5301,9 @@ nsContentUtils::SetDataTransferInEvent(WidgetDragEvent* aDragEvent) if (aDragEvent->mMessage == eDragEnter || aDragEvent->mMessage == eDragOver) { uint32_t action, effectAllowed; dragSession->GetDragAction(&action); - aDragEvent->dataTransfer->GetEffectAllowedInt(&effectAllowed); - aDragEvent->dataTransfer->SetDropEffectInt(FilterDropEffect(action, effectAllowed)); + aDragEvent->mDataTransfer->GetEffectAllowedInt(&effectAllowed); + aDragEvent->mDataTransfer->SetDropEffectInt( + FilterDropEffect(action, effectAllowed)); } else if (aDragEvent->mMessage == eDrop || aDragEvent->mMessage == eLegacyDragDrop || @@ -5311,7 +5314,7 @@ nsContentUtils::SetDataTransferInEvent(WidgetDragEvent* aDragEvent) // dragover event. uint32_t dropEffect; initialDataTransfer->GetDropEffectInt(&dropEffect); - aDragEvent->dataTransfer->SetDropEffectInt(dropEffect); + aDragEvent->mDataTransfer->SetDropEffectInt(dropEffect); } return NS_OK; @@ -5355,7 +5358,7 @@ bool nsContentUtils::CheckForSubFrameDrop(nsIDragSession* aDragSession, WidgetDragEvent* aDropEvent) { - nsCOMPtr target = do_QueryInterface(aDropEvent->originalTarget); + nsCOMPtr target = do_QueryInterface(aDropEvent->mOriginalTarget); if (!target) { return true; } @@ -6682,7 +6685,7 @@ nsContentUtils::HasPluginWithUncontrolledEventDispatch(nsIContent* aContent) // We control dispatch to all mac plugins. return false; #else - if (!aContent || !aContent->IsInDoc()) { + if (!aContent || !aContent->IsInUncomposedDoc()) { return false; } @@ -7702,7 +7705,7 @@ nsContentUtils::SendKeyEvent(nsIWidget* aWidget, return NS_ERROR_FAILURE; WidgetKeyboardEvent event(true, msg, aWidget); - event.modifiers = GetWidgetModifiers(aModifiers); + event.mModifiers = GetWidgetModifiers(aModifiers); if (msg == eKeyPress) { event.keyCode = aCharCode ? 0 : aKeyCode; @@ -7765,8 +7768,8 @@ nsContentUtils::SendKeyEvent(nsIWidget* aWidget, break; } - event.refPoint.x = event.refPoint.y = 0; - event.time = PR_IntervalNow(); + event.mRefPoint = LayoutDeviceIntPoint(0, 0); + event.mTime = PR_IntervalNow(); if (!(aAdditionalFlags & nsIDOMWindowUtils::KEY_FLAG_NOT_SYNTHESIZED_FOR_TESTS)) { event.mFlags.mIsSynthesizedForTests = true; } @@ -7834,21 +7837,20 @@ nsContentUtils::SendMouseEvent(nsCOMPtr aPresShell, WidgetMouseEvent event(true, msg, widget, WidgetMouseEvent::eReal, contextMenuKey ? WidgetMouseEvent::eContextMenuKey : WidgetMouseEvent::eNormal); - event.modifiers = GetWidgetModifiers(aModifiers); + event.mModifiers = GetWidgetModifiers(aModifiers); event.button = aButton; event.buttons = GetButtonsFlagForButton(aButton); - event.widget = widget; event.pressure = aPressure; event.inputSource = aInputSourceArg; event.clickCount = aClickCount; - event.time = PR_IntervalNow(); + event.mTime = PR_IntervalNow(); event.mFlags.mIsSynthesizedForTests = aIsSynthesized; nsPresContext* presContext = aPresShell->GetPresContext(); if (!presContext) return NS_ERROR_FAILURE; - event.refPoint = ToWidgetPoint(CSSPoint(aX, aY), offset, presContext); + event.mRefPoint = ToWidgetPoint(CSSPoint(aX, aY), offset, presContext); event.ignoreRootScrollFrame = aIgnoreRootScrollFrame; nsEventStatus status = nsEventStatus_eIgnore; diff --git a/dom/base/nsCopySupport.cpp b/dom/base/nsCopySupport.cpp index d39781b1b9..4953a796e9 100644 --- a/dom/base/nsCopySupport.cpp +++ b/dom/base/nsCopySupport.cpp @@ -697,7 +697,7 @@ nsCopySupport::FireClipboardEvent(EventMessage aEventMessage, nsEventStatus status = nsEventStatus_eIgnore; InternalClipboardEvent evt(true, aEventMessage); - evt.clipboardData = clipboardData; + evt.mClipboardData = clipboardData; EventDispatcher::Dispatch(content, presShell->GetPresContext(), &evt, nullptr, &status); // If the event was cancelled, don't do the clipboard operation diff --git a/dom/base/nsDOMMutationObserver.cpp b/dom/base/nsDOMMutationObserver.cpp index 743c68162f..1a65cf2051 100644 --- a/dom/base/nsDOMMutationObserver.cpp +++ b/dom/base/nsDOMMutationObserver.cpp @@ -683,7 +683,7 @@ nsDOMMutationObserver::Observe(nsINode& aTarget, filters.SetCapacity(len); for (uint32_t i = 0; i < len; ++i) { - nsCOMPtr a = do_GetAtom(filtersAsString[i]); + nsCOMPtr a = NS_Atomize(filtersAsString[i]); filters.AppendObject(a); } } diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index f7537d40f1..262c5570a6 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -247,7 +247,7 @@ nsDOMWindowUtils::GetDocumentMetadata(const nsAString& aName, nsIDocument* doc = GetDocument(); if (doc) { - nsCOMPtr name = do_GetAtom(aName); + nsCOMPtr name = NS_Atomize(aName); doc->GetHeaderData(name, aValue); return NS_OK; } @@ -346,7 +346,7 @@ nsDOMWindowUtils::SetDisplayPortForElement(float aXPx, float aYPx, return NS_ERROR_INVALID_ARG; } - if (content->GetCurrentDoc() != presShell->GetDocument()) { + if (content->GetUncomposedDoc() != presShell->GetDocument()) { return NS_ERROR_INVALID_ARG; } @@ -428,7 +428,7 @@ nsDOMWindowUtils::SetDisplayPortMarginsForElement(float aLeftMargin, return NS_ERROR_INVALID_ARG; } - if (content->GetCurrentDoc() != presShell->GetDocument()) { + if (content->GetUncomposedDoc() != presShell->GetDocument()) { return NS_ERROR_INVALID_ARG; } @@ -470,7 +470,7 @@ nsDOMWindowUtils::SetDisplayPortBaseForElement(int32_t aX, return NS_ERROR_INVALID_ARG; } - if (content->GetCurrentDoc() != presShell->GetDocument()) { + if (content->GetUncomposedDoc() != presShell->GetDocument()) { return NS_ERROR_INVALID_ARG; } @@ -697,10 +697,9 @@ nsDOMWindowUtils::SendPointerEventCommon(const nsAString& aType, } WidgetPointerEvent event(true, msg, widget); - event.modifiers = nsContentUtils::GetWidgetModifiers(aModifiers); + event.mModifiers = nsContentUtils::GetWidgetModifiers(aModifiers); event.button = aButton; event.buttons = nsContentUtils::GetButtonsFlagForButton(aButton); - event.widget = widget; event.pressure = aPressure; event.inputSource = aInputSourceArg; event.pointerId = aPointerId; @@ -710,7 +709,7 @@ nsDOMWindowUtils::SendPointerEventCommon(const nsAString& aType, event.tiltY = aTiltY; event.isPrimary = (nsIDOMMouseEvent::MOZ_SOURCE_MOUSE == aInputSourceArg) ? true : aIsPrimary; event.clickCount = aClickCount; - event.time = PR_IntervalNow(); + event.mTime = PR_IntervalNow(); event.mFlags.mIsSynthesizedForTests = aOptionalArgCount >= 10 ? aIsSynthesized : true; nsPresContext* presContext = GetPresContext(); @@ -718,7 +717,8 @@ nsDOMWindowUtils::SendPointerEventCommon(const nsAString& aType, return NS_ERROR_FAILURE; } - event.refPoint = nsContentUtils::ToWidgetPoint(CSSPoint(aX, aY), offset, presContext); + event.mRefPoint = + nsContentUtils::ToWidgetPoint(CSSPoint(aX, aY), offset, presContext); event.ignoreRootScrollFrame = aIgnoreRootScrollFrame; nsEventStatus status; @@ -822,27 +822,27 @@ nsDOMWindowUtils::SendWheelEvent(float aX, } WidgetWheelEvent wheelEvent(true, eWheel, widget); - wheelEvent.modifiers = nsContentUtils::GetWidgetModifiers(aModifiers); - wheelEvent.deltaX = aDeltaX; - wheelEvent.deltaY = aDeltaY; - wheelEvent.deltaZ = aDeltaZ; - wheelEvent.deltaMode = aDeltaMode; - wheelEvent.isMomentum = + wheelEvent.mModifiers = nsContentUtils::GetWidgetModifiers(aModifiers); + wheelEvent.mDeltaX = aDeltaX; + wheelEvent.mDeltaY = aDeltaY; + wheelEvent.mDeltaZ = aDeltaZ; + wheelEvent.mDeltaMode = aDeltaMode; + wheelEvent.mIsMomentum = (aOptions & WHEEL_EVENT_CAUSED_BY_MOMENTUM) != 0; wheelEvent.mIsNoLineOrPageDelta = (aOptions & WHEEL_EVENT_CAUSED_BY_NO_LINE_OR_PAGE_DELTA_DEVICE) != 0; - wheelEvent.customizedByUserPrefs = + wheelEvent.mCustomizedByUserPrefs = (aOptions & WHEEL_EVENT_CUSTOMIZED_BY_USER_PREFS) != 0; - wheelEvent.lineOrPageDeltaX = aLineOrPageDeltaX; - wheelEvent.lineOrPageDeltaY = aLineOrPageDeltaY; - wheelEvent.widget = widget; + wheelEvent.mLineOrPageDeltaX = aLineOrPageDeltaX; + wheelEvent.mLineOrPageDeltaY = aLineOrPageDeltaY; - wheelEvent.time = PR_Now() / 1000; + wheelEvent.mTime = PR_Now() / 1000; nsPresContext* presContext = GetPresContext(); NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE); - wheelEvent.refPoint = nsContentUtils::ToWidgetPoint(CSSPoint(aX, aY), offset, presContext); + wheelEvent.mRefPoint = + nsContentUtils::ToWidgetPoint(CSSPoint(aX, aY), offset, presContext); widget->DispatchInputEvent(&wheelEvent); @@ -854,40 +854,40 @@ nsDOMWindowUtils::SendWheelEvent(float aX, bool failedX = false; if ((aOptions & WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_ZERO) && - wheelEvent.overflowDeltaX != 0) { + wheelEvent.mOverflowDeltaX != 0) { failedX = true; } if ((aOptions & WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_POSITIVE) && - wheelEvent.overflowDeltaX <= 0) { + wheelEvent.mOverflowDeltaX <= 0) { failedX = true; } if ((aOptions & WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_NEGATIVE) && - wheelEvent.overflowDeltaX >= 0) { + wheelEvent.mOverflowDeltaX >= 0) { failedX = true; } bool failedY = false; if ((aOptions & WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_ZERO) && - wheelEvent.overflowDeltaY != 0) { + wheelEvent.mOverflowDeltaY != 0) { failedY = true; } if ((aOptions & WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_POSITIVE) && - wheelEvent.overflowDeltaY <= 0) { + wheelEvent.mOverflowDeltaY <= 0) { failedY = true; } if ((aOptions & WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_NEGATIVE) && - wheelEvent.overflowDeltaY >= 0) { + wheelEvent.mOverflowDeltaY >= 0) { failedY = true; } #ifdef DEBUG if (failedX) { - nsPrintfCString debugMsg("SendWheelEvent(): unexpected overflowDeltaX: %f", - wheelEvent.overflowDeltaX); + nsPrintfCString debugMsg("SendWheelEvent(): unexpected mOverflowDeltaX: %f", + wheelEvent.mOverflowDeltaX); NS_WARNING(debugMsg.get()); } if (failedY) { - nsPrintfCString debugMsg("SendWheelEvent(): unexpected overflowDeltaY: %f", - wheelEvent.overflowDeltaY); + nsPrintfCString debugMsg("SendWheelEvent(): unexpected mOverflowDeltaY: %f", + wheelEvent.mOverflowDeltaY); NS_WARNING(debugMsg.get()); } #endif @@ -969,15 +969,14 @@ nsDOMWindowUtils::SendTouchEventCommon(const nsAString& aType, return NS_ERROR_UNEXPECTED; } WidgetTouchEvent event(true, msg, widget); - event.modifiers = nsContentUtils::GetWidgetModifiers(aModifiers); - event.widget = widget; - event.time = PR_Now(); + event.mModifiers = nsContentUtils::GetWidgetModifiers(aModifiers); + event.mTime = PR_Now(); nsPresContext* presContext = GetPresContext(); if (!presContext) { return NS_ERROR_FAILURE; } - event.touches.SetCapacity(aCount); + event.mTouches.SetCapacity(aCount); for (uint32_t i = 0; i < aCount; ++i) { LayoutDeviceIntPoint pt = nsContentUtils::ToWidgetPoint(CSSPoint(aXs[i], aYs[i]), offset, presContext); @@ -989,7 +988,7 @@ nsDOMWindowUtils::SendTouchEventCommon(const nsAString& aType, RefPtr t = new Touch(aIdentifiers[i], pt, radius, aRotationAngles[i], aForces[i]); - event.touches.AppendElement(t); + event.mTouches.AppendElement(t); } nsEventStatus status; @@ -1139,10 +1138,10 @@ nsDOMWindowUtils::SendNativeTouchPoint(uint32_t aPointerId, } NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs - + (widget, &nsIWidget::SynthesizeNativeTouchPoint, aPointerId, (nsIWidget::TouchPointerState)aTouchState, - ScreenIntPoint(aScreenX, aScreenY), + LayoutDeviceIntPoint(aScreenX, aScreenY), aPressure, aOrientation, aObserver)); return NS_OK; } @@ -1161,9 +1160,9 @@ nsDOMWindowUtils::SendNativeTouchTap(int32_t aScreenX, } NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs - + (widget, &nsIWidget::SynthesizeNativeTouchTap, - ScreenIntPoint(aScreenX, aScreenY), aLongTap, aObserver)); + LayoutDeviceIntPoint(aScreenX, aScreenY), aLongTap, aObserver)); return NS_OK; } @@ -1230,7 +1229,7 @@ nsDOMWindowUtils::GetWidgetForElement(nsIDOMElement* aElement) return GetWidget(); nsCOMPtr content = do_QueryInterface(aElement); - nsIDocument* doc = content->GetCurrentDoc(); + nsIDocument* doc = content->GetUncomposedDoc(); nsIPresShell* presShell = doc ? doc->GetShell() : nullptr; if (presShell) { @@ -1349,17 +1348,18 @@ nsDOMWindowUtils::SendSimpleGestureEvent(const nsAString& aType, } WidgetSimpleGestureEvent event(true, msg, widget); - event.modifiers = nsContentUtils::GetWidgetModifiers(aModifiers); + event.mModifiers = nsContentUtils::GetWidgetModifiers(aModifiers); event.direction = aDirection; event.delta = aDelta; event.clickCount = aClickCount; - event.time = PR_IntervalNow(); + event.mTime = PR_IntervalNow(); nsPresContext* presContext = GetPresContext(); if (!presContext) return NS_ERROR_FAILURE; - event.refPoint = nsContentUtils::ToWidgetPoint(CSSPoint(aX, aY), offset, presContext); + event.mRefPoint = + nsContentUtils::ToWidgetPoint(CSSPoint(aX, aY), offset, presContext); nsEventStatus status; return widget->DispatchEvent(&event, status); @@ -1874,7 +1874,7 @@ nsDOMWindowUtils::DispatchDOMEventViaPresShell(nsIDOMNode* aTarget, if (content->OwnerDoc()->GetWindow() != window) { return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; } - nsCOMPtr targetDoc = content->GetCurrentDoc(); + nsCOMPtr targetDoc = content->GetUncomposedDoc(); NS_ENSURE_STATE(targetDoc); RefPtr targetShell = targetDoc->GetShell(); NS_ENSURE_STATE(targetShell); @@ -1891,9 +1891,9 @@ static void InitEvent(WidgetGUIEvent& aEvent, LayoutDeviceIntPoint* aPt = nullptr) { if (aPt) { - aEvent.refPoint = *aPt; + aEvent.mRefPoint = *aPt; } - aEvent.time = PR_IntervalNow(); + aEvent.mTime = PR_IntervalNow(); } NS_IMETHODIMP @@ -2370,7 +2370,7 @@ ComputeAnimationValue(nsCSSProperty aProperty, const nsAString& aInput, StyleAnimationValue& aOutput) { - nsIDocument* doc = aElement->GetCurrentDoc(); + nsIDocument* doc = aElement->GetUncomposedDoc(); nsIPresShell* shell = doc->GetShell(); if (!shell) { return false; @@ -2383,15 +2383,6 @@ ComputeAnimationValue(nsCSSProperty aProperty, aInput, false, aOutput)) { return false; } - - // This matches TransExtractComputedValue in nsTransitionManager.cpp. - if (aProperty == eCSSProperty_visibility) { - MOZ_ASSERT(aOutput.GetUnit() == StyleAnimationValue::eUnit_Enumerated, - "unexpected unit"); - aOutput.SetIntValue(aOutput.GetIntValue(), - StyleAnimationValue::eUnit_Visibility); - } - return true; } diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 8a18ac2cd1..e7040a73c5 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -5506,7 +5506,7 @@ nsDocument::SetupCustomElement(Element* aElement, nsCOMPtr tagAtom = aElement->NodeInfo()->NameAtom(); nsCOMPtr typeAtom = aTypeExtension ? - do_GetAtom(*aTypeExtension) : tagAtom; + NS_Atomize(*aTypeExtension) : tagAtom; if (aTypeExtension && !aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::is)) { // Custom element setup in the parser happens after the "is" @@ -5833,7 +5833,7 @@ nsDocument::CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* return true; } - nsCOMPtr typeAtom(do_GetAtom(elemName)); + nsCOMPtr typeAtom(NS_Atomize(elemName)); CustomElementHashKey key(kNameSpaceID_Unknown, typeAtom); CustomElementDefinition* definition; if (!document->mRegistry || @@ -6127,7 +6127,7 @@ nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType, lcName.Assign(aOptions.mExtends); } - nsCOMPtr typeAtom(do_GetAtom(lcType)); + nsCOMPtr typeAtom(NS_Atomize(lcType)); if (!nsContentUtils::IsCustomElementName(typeAtom)) { rv.Throw(NS_ERROR_DOM_SYNTAX_ERR); return; @@ -6248,7 +6248,7 @@ nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType, if (!lcName.IsEmpty()) { // Let BASE be the element interface for NAME and NAMESPACE. bool known = false; - nameAtom = do_GetAtom(lcName); + nameAtom = NS_Atomize(lcName); if (namespaceID == kNameSpaceID_XHTML) { nsIParserService* ps = nsContentUtils::GetParserService(); if (!ps) { @@ -6865,7 +6865,7 @@ nsIDocument::GetAnonymousElementByAttribute(Element& aElement, const nsAString& aAttrName, const nsAString& aAttrValue) { - nsCOMPtr attribute = do_GetAtom(aAttrName); + nsCOMPtr attribute = NS_Atomize(aAttrName); return GetAnonymousElementByAttribute(&aElement, attribute, aAttrValue); } @@ -7789,7 +7789,7 @@ nsIDocument::AdoptNode(nsINode& aAdoptedNode, ErrorResult& rv) MOZ_ASSERT(idx >= 0); parent->RemoveChildAt(idx, true); } else { - MOZ_ASSERT(!adoptedNode->IsInDoc()); + MOZ_ASSERT(!adoptedNode->IsInUncomposedDoc()); // If we're adopting a node that's not in a document, it might still // have a binding applied. Remove the binding from the element now @@ -8609,7 +8609,7 @@ nsDocument::RetrieveRelevantHeaders(nsIChannel *aChannel) rv = httpChannel->GetResponseHeader(nsDependentCString(*name), headerVal); if (NS_SUCCEEDED(rv) && !headerVal.IsEmpty()) { - nsCOMPtr key = do_GetAtom(*name); + nsCOMPtr key = NS_Atomize(*name); SetHeaderData(key, NS_ConvertASCIItoUTF16(headerVal)); } ++name; @@ -11665,7 +11665,7 @@ nsDocument::FullScreenStackPop() // no longer in this document. while (!mFullScreenStack.IsEmpty()) { Element* element = FullScreenStackTop(); - if (!element || !element->IsInDoc() || element->OwnerDoc() != this) { + if (!element || !element->IsInUncomposedDoc() || element->OwnerDoc() != this) { NS_ASSERTION(!element->IsFullScreenAncestor(), "Should have already removed full-screen styles"); uint32_t last = mFullScreenStack.Length() - 1; @@ -11688,7 +11688,7 @@ nsDocument::FullScreenStackTop() uint32_t last = mFullScreenStack.Length() - 1; nsCOMPtr element(do_QueryReferent(mFullScreenStack[last])); NS_ASSERTION(element, "Should have full-screen element!"); - NS_ASSERTION(element->IsInDoc(), "Full-screen element should be in doc"); + NS_ASSERTION(element->IsInUncomposedDoc(), "Full-screen element should be in doc"); NS_ASSERTION(element->OwnerDoc() == this, "Full-screen element should be in this doc"); return element; } @@ -11799,7 +11799,7 @@ nsDocument::RequestFullScreen(Element* aElement, if (!aElement || aElement == GetFullScreenElement()) { return; } - if (!aElement->IsInDoc()) { + if (!aElement->IsInUncomposedDoc()) { LogFullScreenDenied(true, "FullScreenDeniedNotInDocument", this); return; } @@ -12491,7 +12491,7 @@ nsDocument::ShouldLockPointer(Element* aElement, Element* aCurrentLock, return false; } - if (!aElement->IsInDoc()) { + if (!aElement->IsInUncomposedDoc()) { NS_WARNING("ShouldLockPointer(): Element without Document"); return false; } @@ -12538,49 +12538,37 @@ nsDocument::ShouldLockPointer(Element* aElement, Element* aCurrentLock, bool nsDocument::SetPointerLock(Element* aElement, int aCursorStyle) { - // NOTE: aElement will be nullptr when unlocking. - nsCOMPtr window = GetWindow(); - if (!window) { - NS_WARNING("SetPointerLock(): No Window"); - return false; + MOZ_ASSERT(!aElement || aElement->OwnerDoc() == this, + "We should be either unlocking pointer (aElement is nullptr), " + "or locking pointer to an element in this document"); +#ifdef DEBUG + if (!aElement) { + nsCOMPtr pointerLockedDoc = + do_QueryReferent(EventStateManager::sPointerLockedDoc); + MOZ_ASSERT(pointerLockedDoc == this); } +#endif - nsIDocShell *docShell = window->GetDocShell(); - if (!docShell) { - NS_WARNING("SetPointerLock(): No DocShell (window already closed?)"); - return false; - } - - RefPtr presContext; - docShell->GetPresContext(getter_AddRefs(presContext)); - if (!presContext) { - NS_WARNING("SetPointerLock(): Unable to get presContext in \ - domWindow->GetDocShell()->GetPresContext()"); - return false; - } - - nsCOMPtr shell = presContext->PresShell(); + nsIPresShell* shell = GetShell(); if (!shell) { - NS_WARNING("SetPointerLock(): Unable to find presContext->PresShell()"); + NS_WARNING("SetPointerLock(): No PresShell"); + return false; + } + nsPresContext* presContext = shell->GetPresContext(); + if (!presContext) { + NS_WARNING("SetPointerLock(): Unable to get PresContext"); return false; } + nsCOMPtr widget; nsIFrame* rootFrame = shell->GetRootFrame(); - if (!rootFrame) { - NS_WARNING("SetPointerLock(): Unable to get root frame"); - return false; - } - - nsCOMPtr widget = rootFrame->GetNearestWidget(); - if (!widget) { - NS_WARNING("SetPointerLock(): Unable to find widget in \ - shell->GetRootFrame()->GetNearestWidget();"); - return false; - } - - if (aElement && (aElement->OwnerDoc() != this)) { - NS_WARNING("SetPointerLock(): Element not in this document."); - return false; + if (!NS_WARN_IF(!rootFrame)) { + widget = rootFrame->GetNearestWidget(); + NS_WARN_IF_FALSE(widget, "SetPointerLock(): Unable to find widget " + "in shell->GetRootFrame()->GetNearestWidget();"); + if (aElement && !widget) { + return false; + } } // Hide the cursor and set pointer lock for future mouse events diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp index a483aa6b6b..2c04aa1a95 100644 --- a/dom/base/nsFocusManager.cpp +++ b/dom/base/nsFocusManager.cpp @@ -1973,8 +1973,8 @@ public: InternalFocusEvent event(true, mEventMessage); event.mFlags.mBubbles = false; event.mFlags.mCancelable = false; - event.fromRaise = mWindowRaised; - event.isRefocus = mIsRefocus; + event.mFromRaise = mWindowRaised; + event.mIsRefocus = mIsRefocus; return EventDispatcher::Dispatch(mTarget, mContext, &event); } diff --git a/dom/base/nsGenericDOMDataNode.cpp b/dom/base/nsGenericDOMDataNode.cpp index 37a964a610..c80370bdea 100644 --- a/dom/base/nsGenericDOMDataNode.cpp +++ b/dom/base/nsGenericDOMDataNode.cpp @@ -64,7 +64,7 @@ nsGenericDOMDataNode::nsGenericDOMDataNode(already_AddRefed 0) { nsAutoString val; mText.AppendTo(val); - mutation.mNewAttrValue = do_GetAtom(val); + mutation.mNewAttrValue = NS_Atomize(val); } mozAutoSubtreeModified subtree(OwnerDoc(), this); @@ -467,7 +467,7 @@ nsGenericDOMDataNode::BindToTree(nsIDocument* aDocument, nsIContent* aParent, "Must have the same owner document"); NS_PRECONDITION(!aParent || aDocument == aParent->GetUncomposedDoc(), "aDocument must be current doc of aParent"); - NS_PRECONDITION(!GetUncomposedDoc() && !IsInDoc(), + NS_PRECONDITION(!GetUncomposedDoc() && !IsInUncomposedDoc(), "Already have a document. Unbind first!"); // Note that as we recurse into the kids, they'll have a non-null parent. So // only assert if our parent is _changing_ while we have a parent. @@ -1097,7 +1097,7 @@ nsGenericDOMDataNode::GetCurrentValueAtom() { nsAutoString val; GetData(val); - return NS_NewAtom(val); + return NS_Atomize(val); } NS_IMETHODIMP diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h index 16776eb8fa..6cc273c91d 100644 --- a/dom/base/nsGkAtomList.h +++ b/dom/base/nsGkAtomList.h @@ -372,7 +372,6 @@ GK_ATOM(excludeResultPrefixes, "exclude-result-prefixes") GK_ATOM(excludes, "excludes") GK_ATOM(expr, "expr") GK_ATOM(expectingSystemMessage, "expecting-system-message") -GK_ATOM(extapp, "extapp") GK_ATOM(extends, "extends") GK_ATOM(extensionElementPrefixes, "extension-element-prefixes") GK_ATOM(face, "face") diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 505bff7801..04a8be3807 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -3106,17 +3106,17 @@ nsGlobalWindow::PreHandleEvent(EventChainPreVisitor& aVisitor) //let's only take the lowest half of the point structure. int16_t myCoord[2]; - myCoord[0] = aVisitor.mEvent->refPoint.x; - myCoord[1] = aVisitor.mEvent->refPoint.y; + myCoord[0] = aVisitor.mEvent->mRefPoint.x; + myCoord[1] = aVisitor.mEvent->mRefPoint.y; gEntropyCollector->RandomUpdate((void*)myCoord, sizeof(myCoord)); - gEntropyCollector->RandomUpdate((void*)&(aVisitor.mEvent->time), + gEntropyCollector->RandomUpdate((void*)&(aVisitor.mEvent->mTime), sizeof(uint32_t)); } } else if (msg == eResize && aVisitor.mEvent->IsTrusted()) { // QIing to window so that we can keep the old behavior also in case // a child window is handling resize. nsCOMPtr window = - do_QueryInterface(aVisitor.mEvent->originalTarget); + do_QueryInterface(aVisitor.mEvent->mOriginalTarget); if (window) { mIsHandlingResizeEvent = true; } diff --git a/dom/base/nsIContent.h b/dom/base/nsIContent.h index 4f7ade14d3..e3e3bcbf8c 100644 --- a/dom/base/nsIContent.h +++ b/dom/base/nsIContent.h @@ -74,8 +74,8 @@ public: * @param aDocument The new document for the content node. May not be null * if aParent is null. Must match the current document of * aParent, if aParent is not null (note that - * aParent->GetCurrentDoc() can be null, in which case this - * must also be null). + * aParent->GetUncomposedDoc() can be null, in which case + * this must also be null). * @param aParent The new parent for the content node. May be null if the * node is being bound as a direct child of the document. * @param aBindingParent The new binding parent for the content node. @@ -229,7 +229,7 @@ public: bool IsInAnonymousSubtree() const { NS_ASSERTION(!IsInNativeAnonymousSubtree() || GetBindingParent() || - (!IsInDoc() && + (!IsInUncomposedDoc() && static_cast(SubtreeRoot())->IsInNativeAnonymousSubtree()), "Must have binding parent when in native anonymous subtree which is in document.\n" "Native anonymous subtree which is not in document must have native anonymous root."); @@ -897,7 +897,7 @@ public: */ nsIFrame* GetPrimaryFrame() const { - return (IsInDoc() || IsInShadowTree()) ? mPrimaryFrame : nullptr; + return (IsInUncomposedDoc() || IsInShadowTree()) ? mPrimaryFrame : nullptr; } // Defined in nsIContentInlines.h because it needs nsIFrame. diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp index 8c03e38ac4..206ac9ab25 100644 --- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp @@ -261,7 +261,7 @@ nsINode::SubtreeRoot() const // 4. nsIAttribute nodes - Are never in the document, and mSubtreeRoot // is always 'this' (as set in nsINode's ctor). nsINode* node; - if (IsInDoc()) { + if (IsInUncomposedDoc()) { node = OwnerDocAsNode(); } else if (IsContent()) { ShadowRoot* containingShadow = AsContent()->GetContainingShadow(); @@ -317,7 +317,7 @@ nsINode::GetSelectionRootContent(nsIPresShell* aPresShell) if (!IsNodeOfType(eCONTENT)) return nullptr; - if (GetCrossShadowCurrentDoc() != aPresShell->GetDocument()) { + if (GetComposedDoc() != aPresShell->GetDocument()) { return nullptr; } @@ -333,7 +333,7 @@ nsINode::GetSelectionRootContent(nsIPresShell* aPresShell) nsIEditor* editor = nsContentUtils::GetHTMLEditor(presContext); if (editor) { // This node is in HTML editor. - nsIDocument* doc = GetCrossShadowCurrentDoc(); + nsIDocument* doc = GetComposedDoc(); if (!doc || doc->HasFlag(NODE_IS_EDITABLE) || !HasFlag(NODE_IS_EDITABLE)) { nsIContent* editorRoot = GetEditorRootContent(editor); @@ -780,7 +780,7 @@ nsINode::SetUserData(const nsAString &aKey, nsIVariant *aData, nsIVariant **aRes OwnerDoc()->WarnOnceAbout(nsIDocument::eGetSetUserData); *aResult = nullptr; - nsCOMPtr key = do_GetAtom(aKey); + nsCOMPtr key = NS_Atomize(aKey); if (!key) { return NS_ERROR_OUT_OF_MEMORY; } @@ -833,7 +833,7 @@ nsIVariant* nsINode::GetUserData(const nsAString& aKey) { OwnerDoc()->WarnOnceAbout(nsIDocument::eGetSetUserData); - nsCOMPtr key = do_GetAtom(aKey); + nsCOMPtr key = NS_Atomize(aKey); if (!key) { return nullptr; } @@ -1568,7 +1568,7 @@ nsINode::doInsertChildAt(nsIContent* aKid, uint32_t aIndex, // Do this before checking the child-count since this could cause mutations nsIDocument* doc = GetUncomposedDoc(); - mozAutoDocUpdate updateBatch(GetCrossShadowCurrentDoc(), UPDATE_CONTENT_MODEL, aNotify); + mozAutoDocUpdate updateBatch(GetComposedDoc(), UPDATE_CONTENT_MODEL, aNotify); if (OwnerDoc() != aKid->OwnerDoc()) { rv = AdoptNodeIntoOwnerDoc(this, aKid); @@ -1896,7 +1896,7 @@ nsINode::doRemoveChildAt(uint32_t aIndex, bool aNotify, IndexOf(aKid) == (int32_t)aIndex, "Bogus aKid"); nsMutationGuard::DidMutate(); - mozAutoDocUpdate updateBatch(GetCrossShadowCurrentDoc(), UPDATE_CONTENT_MODEL, aNotify); + mozAutoDocUpdate updateBatch(GetComposedDoc(), UPDATE_CONTENT_MODEL, aNotify); nsIContent* previousSibling = aKid->GetPreviousSibling(); @@ -2334,7 +2334,7 @@ nsINode::ReplaceOrInsertBefore(bool aReplace, nsINode* aNewChild, } } - mozAutoDocUpdate batch(GetCrossShadowCurrentDoc(), UPDATE_CONTENT_MODEL, true); + mozAutoDocUpdate batch(GetComposedDoc(), UPDATE_CONTENT_MODEL, true); nsAutoMutationBatch mb; // Figure out which index we want to insert at. Note that we use @@ -2602,7 +2602,7 @@ nsINode::Contains(const nsINode* aOther) const } if (!aOther || OwnerDoc() != aOther->OwnerDoc() || - IsInDoc() != aOther->IsInDoc() || + IsInUncomposedDoc() != aOther->IsInUncomposedDoc() || !(aOther->IsElement() || aOther->IsNodeOfType(nsINode::eCONTENT)) || !GetFirstChild()) { @@ -2613,7 +2613,7 @@ nsINode::Contains(const nsINode* aOther) const if (this == OwnerDoc()) { // document.contains(aOther) returns true if aOther is in the document, // but is not in any anonymous subtree. - // IsInDoc() check is done already before this. + // IsInUncomposedDoc() check is done already before this. return !other->IsInAnonymousSubtree(); } @@ -2740,13 +2740,13 @@ FindMatchingElementsWithId(const nsAString& aId, nsINode* aRoot, SelectorMatchInfo* aMatchInfo, T& aList) { - MOZ_ASSERT(aRoot->IsInDoc(), + MOZ_ASSERT(aRoot->IsInUncomposedDoc(), "Don't call me if the root is not in the document"); MOZ_ASSERT(aRoot->IsElement() || aRoot->IsNodeOfType(nsINode::eDOCUMENT), "The optimization below to check ContentIsDescendantOf only for " "elements depends on aRoot being either an element or a " "document if it's in the document. Note that document fragments " - "can't be IsInDoc(), so should never show up here."); + "can't be IsInUncomposedDoc(), so should never show up here."); const nsTArray* elements = aRoot->OwnerDoc()->GetAllElementsForId(aId); if (!elements) { @@ -2797,11 +2797,11 @@ FindMatchingElements(nsINode* aRoot, nsCSSSelectorList* aSelectorList, T &aList, // this if aSelectorList only has one selector, because otherwise // ordering the elements correctly is a pain. NS_ASSERTION(aRoot->IsElement() || aRoot->IsNodeOfType(nsINode::eDOCUMENT) || - !aRoot->IsInDoc(), + !aRoot->IsInUncomposedDoc(), "The optimization below to check ContentIsDescendantOf only for " "elements depends on aRoot being either an element or a " "document if it's in the document."); - if (aRoot->IsInDoc() && + if (aRoot->IsInUncomposedDoc() && doc->GetCompatibilityMode() != eCompatibility_NavQuirks && !aSelectorList->mNext && aSelectorList->mSelectors->mIDList) { @@ -2909,7 +2909,7 @@ nsINode::GetElementById(const nsAString& aId) { MOZ_ASSERT(IsElement() || IsNodeOfType(eDOCUMENT_FRAGMENT), "Bogus this object for GetElementById call"); - if (IsInDoc()) { + if (IsInUncomposedDoc()) { ElementHolder holder; FindMatchingElementsWithId(aId, this, nullptr, holder); return holder.mElement; diff --git a/dom/base/nsINode.h b/dom/base/nsINode.h index 5333130ca5..9704e1921d 100644 --- a/dom/base/nsINode.h +++ b/dom/base/nsINode.h @@ -507,14 +507,6 @@ public: return GetBoolFlag(IsInDocument); } - /** - * @deprecated - */ - bool IsInDoc() const - { - return IsInUncomposedDoc(); - } - /** * Get the document that this content is currently in, if any. This will be * null if the content has no ancestor that is a document. @@ -527,14 +519,6 @@ public: return IsInUncomposedDoc() ? OwnerDoc() : nullptr; } - /** - * @deprecated - */ - nsIDocument *GetCurrentDoc() const - { - return GetUncomposedDoc(); - } - /** * This method returns the owner doc if the node is in the * composed document (as defined in the Shadow DOM spec), otherwise @@ -546,14 +530,6 @@ public: GetComposedDocInternal() : GetUncomposedDoc(); } - /** - * @deprecated - */ - nsIDocument* GetCrossShadowCurrentDoc() const - { - return GetComposedDoc(); - } - /** * Returns true if GetComposedDoc() would return a non-null value. */ @@ -1687,7 +1663,7 @@ protected: void SetSubtreeRootPointer(nsINode* aSubtreeRoot) { NS_ASSERTION(aSubtreeRoot, "aSubtreeRoot can never be null!"); - NS_ASSERTION(!(IsNodeOfType(eCONTENT) && IsInDoc()) && + NS_ASSERTION(!(IsNodeOfType(eCONTENT) && IsInUncomposedDoc()) && !IsInShadowTree(), "Shouldn't be here!"); mSubtreeRoot = aSubtreeRoot; } diff --git a/dom/base/nsNodeInfoManager.cpp b/dom/base/nsNodeInfoManager.cpp index 878b7495ce..9ee1cc017c 100644 --- a/dom/base/nsNodeInfoManager.cpp +++ b/dom/base/nsNodeInfoManager.cpp @@ -265,7 +265,7 @@ nsNodeInfoManager::GetNodeInfo(const nsAString& aName, nsIAtom *aPrefix, { #ifdef DEBUG { - nsCOMPtr nameAtom = do_GetAtom(aName); + nsCOMPtr nameAtom = NS_Atomize(aName); CheckValidNodeInfo(aNodeType, nameAtom, aNamespaceID, nullptr); } #endif @@ -282,7 +282,7 @@ nsNodeInfoManager::GetNodeInfo(const nsAString& aName, nsIAtom *aPrefix, return NS_OK; } - nsCOMPtr nameAtom = do_GetAtom(aName); + nsCOMPtr nameAtom = NS_Atomize(aName); NS_ENSURE_TRUE(nameAtom, NS_ERROR_OUT_OF_MEMORY); RefPtr newNodeInfo = diff --git a/dom/base/nsRange.cpp b/dom/base/nsRange.cpp index 1b6ea30aa4..37acf17337 100644 --- a/dom/base/nsRange.cpp +++ b/dom/base/nsRange.cpp @@ -2912,14 +2912,14 @@ nsRange::CollectClientRects(nsLayoutUtils::RectCallback* aCollector, nsCOMPtr endContainer = aEndParent; // Flush out layout so our frames are up to date. - if (!aStartParent->IsInDoc()) { + if (!aStartParent->IsInUncomposedDoc()) { return; } if (aFlushLayout) { aStartParent->OwnerDoc()->FlushPendingNotifications(Flush_Layout); // Recheck whether we're still in the document - if (!aStartParent->IsInDoc()) { + if (!aStartParent->IsInUncomposedDoc()) { return; } } @@ -3047,7 +3047,7 @@ nsRange::GetUsedFontFaces(nsIDOMFontFaceList** aResult) doc->FlushPendingNotifications(Flush_Frames); // Recheck whether we're still in the document - NS_ENSURE_TRUE(mStartParent->IsInDoc(), NS_ERROR_UNEXPECTED); + NS_ENSURE_TRUE(mStartParent->IsInUncomposedDoc(), NS_ERROR_UNEXPECTED); RefPtr fontFaceList = new nsFontFaceList(); diff --git a/dom/base/nsReferencedElement.cpp b/dom/base/nsReferencedElement.cpp index 961e591041..bd029c380f 100644 --- a/dom/base/nsReferencedElement.cpp +++ b/dom/base/nsReferencedElement.cpp @@ -109,7 +109,7 @@ nsReferencedElement::Reset(nsIContent* aFromContent, nsIURI* aURI, } if (aWatch) { - nsCOMPtr atom = do_GetAtom(ref); + nsCOMPtr atom = NS_Atomize(ref); if (!atom) return; atom.swap(mWatchID); @@ -131,7 +131,7 @@ nsReferencedElement::ResetWithID(nsIContent* aFromContent, const nsString& aID, // XXX Need to take care of XBL/XBL2 if (aWatch) { - nsCOMPtr atom = do_GetAtom(aID); + nsCOMPtr atom = NS_Atomize(aID); if (!atom) return; atom.swap(mWatchID); diff --git a/dom/base/nsScriptElement.cpp b/dom/base/nsScriptElement.cpp index c7f6449940..1c685f4e10 100644 --- a/dom/base/nsScriptElement.cpp +++ b/dom/base/nsScriptElement.cpp @@ -117,7 +117,7 @@ nsScriptElement::MaybeProcessScript() "You forgot to add self as observer"); if (mAlreadyStarted || !mDoneAddingChildren || - !cont->GetCrossShadowCurrentDoc() || mMalformed || !HasScriptContent()) { + !cont->GetComposedDoc() || mMalformed || !HasScriptContent()) { return false; } diff --git a/dom/base/nsStyledElement.cpp b/dom/base/nsStyledElement.cpp index af2d218866..2900fe4a7b 100644 --- a/dom/base/nsStyledElement.cpp +++ b/dom/base/nsStyledElement.cpp @@ -73,7 +73,7 @@ nsStyledElementNotElementCSSInlineStyle::SetInlineStyleDeclaration(css::Declarat oldValue.SetTo(oldValueStr); } } - else if (aNotify && IsInDoc()) { + else if (aNotify && IsInUncomposedDoc()) { modification = !!mAttrsAndChildren.GetAttr(nsGkAtoms::style); } diff --git a/dom/base/nsTreeSanitizer.cpp b/dom/base/nsTreeSanitizer.cpp index ec0c5716ca..52a23cc4a8 100644 --- a/dom/base/nsTreeSanitizer.cpp +++ b/dom/base/nsTreeSanitizer.cpp @@ -1336,7 +1336,7 @@ nsTreeSanitizer::Sanitize(nsIContent* aFragment) // in tree. NS_PRECONDITION(aFragment->IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT), "Argument was not DOM fragment."); - NS_PRECONDITION(!aFragment->IsInDoc(), "The fragment is in doc?"); + NS_PRECONDITION(!aFragment->IsInUncomposedDoc(), "The fragment is in doc?"); mFullDocument = false; SanitizeChildren(aFragment); diff --git a/dom/browser-element/BrowserElementParent.cpp b/dom/browser-element/BrowserElementParent.cpp index 4b07aa6dae..3b48ef0ba4 100644 --- a/dom/browser-element/BrowserElementParent.cpp +++ b/dom/browser-element/BrowserElementParent.cpp @@ -189,7 +189,7 @@ BrowserElementParent::DispatchOpenWindowEvent(Element* aOpenerFrameElement, val, &status); if (dispatchSucceeded) { - if (aPopupFrameElement->IsInDoc()) { + if (aPopupFrameElement->IsInUncomposedDoc()) { return BrowserElementParent::OPEN_WINDOW_ADDED; } else if (status == nsEventStatus_eConsumeNoDefault) { // If the frame was not added to a document, report to callers whether diff --git a/dom/camera/DOMCameraControl.cpp b/dom/camera/DOMCameraControl.cpp index 09ca635fe8..555a8405c1 100644 --- a/dom/camera/DOMCameraControl.cpp +++ b/dom/camera/DOMCameraControl.cpp @@ -250,7 +250,7 @@ nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId, const CameraConfiguration& aInitialConfig, Promise* aPromise, nsPIDOMWindow* aWindow) - : DOMMediaStream() + : DOMMediaStream(aWindow, nullptr) , mCameraControl(nullptr) , mAudioChannelAgent(nullptr) , mGetCameraPromise(aPromise) @@ -331,11 +331,6 @@ nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId, // we are not user/DOM facing anyway. CreateAndAddPlaybackStreamListener(mInput); - MOZ_ASSERT(mWindow, "Shouldn't be created with a null window!"); - if (mWindow->GetExtantDoc()) { - CombineWithPrincipal(mWindow->GetExtantDoc()->NodePrincipal()); - } - // Register a listener for camera events. mListener = new DOMCameraControlListener(this, mInput); mCameraControl->AddListener(mListener); @@ -526,10 +521,17 @@ nsDOMCameraControl::GetCameraStream() const void nsDOMCameraControl::TrackCreated(TrackID aTrackID) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_RELEASE_ASSERT(mWindow, "Shouldn't have been created with a null window!"); + nsIPrincipal* principal = mWindow->GetExtantDoc() + ? mWindow->GetExtantDoc()->NodePrincipal() + : nullptr; + // This track is not connected through a port. MediaInputPort* inputPort = nullptr; dom::VideoStreamTrack* track = - new dom::VideoStreamTrack(this, aTrackID, nsString()); + new dom::VideoStreamTrack(this, aTrackID, aTrackID, + new BasicUnstoppableTrackSource(principal)); RefPtr port = new TrackPort(inputPort, track, TrackPort::InputPortOwnership::OWNED); diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index 6db00e78d5..c03897f19b 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -1037,7 +1037,7 @@ CanvasRenderingContext2D::ParseColor(const nsAString& aString, // otherwise resolve it nsCOMPtr presShell = GetPresShell(); RefPtr parentContext; - if (mCanvasElement && mCanvasElement->IsInDoc()) { + if (mCanvasElement && mCanvasElement->IsInUncomposedDoc()) { // Inherit from the canvas element. parentContext = nsComputedDOMStyle::GetStyleContextForElement( mCanvasElement, nullptr, presShell); @@ -1560,7 +1560,7 @@ CanvasRenderingContext2D::ClearTarget(int32_t aWidth, int32_t aHeight) // For vertical writing-mode, unless text-orientation is sideways, // we'll modify the initial value of textBaseline to 'middle'. RefPtr canvasStyle; - if (mCanvasElement && mCanvasElement->IsInDoc()) { + if (mCanvasElement && mCanvasElement->IsInUncomposedDoc()) { nsCOMPtr presShell = GetPresShell(); if (presShell) { canvasStyle = @@ -2206,7 +2206,7 @@ static already_AddRefed GetFontParentStyleContext(Element* aElement, nsIPresShell* aPresShell, ErrorResult& aError) { - if (aElement && aElement->IsInDoc()) { + if (aElement && aElement->IsInUncomposedDoc()) { // Inherit from the canvas element. RefPtr result = nsComputedDOMStyle::GetStyleContextForElement(aElement, nullptr, @@ -3825,7 +3825,7 @@ CanvasRenderingContext2D::DrawOrMeasureText(const nsAString& aRawText, bool isRTL = false; RefPtr canvasStyle; - if (mCanvasElement && mCanvasElement->IsInDoc()) { + if (mCanvasElement && mCanvasElement->IsInUncomposedDoc()) { // try to find the closest context canvasStyle = nsComputedDOMStyle::GetStyleContextForElement(mCanvasElement, @@ -4514,7 +4514,7 @@ CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage, } // If it doesn't have a principal, just bail - nsCOMPtr principal = video->GetCurrentPrincipal(); + nsCOMPtr principal = video->GetCurrentVideoPrincipal(); if (!principal) { aError.Throw(NS_ERROR_NOT_AVAILABLE); return; diff --git a/dom/canvas/ImageBitmap.cpp b/dom/canvas/ImageBitmap.cpp index 98856168eb..4e7b85a595 100644 --- a/dom/canvas/ImageBitmap.cpp +++ b/dom/canvas/ImageBitmap.cpp @@ -627,7 +627,7 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLVideoElement& aVideoEl } // Check security. - nsCOMPtr principal = aVideoEl.GetCurrentPrincipal(); + nsCOMPtr principal = aVideoEl.GetCurrentVideoPrincipal(); bool CORSUsed = aVideoEl.GetCORSMode() != CORS_NONE; if (!CheckSecurityForHTMLElements(false, CORSUsed, principal)) { aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); diff --git a/dom/canvas/test/captureStream_common.js b/dom/canvas/test/captureStream_common.js index 91be55ed51..e0da067c9c 100644 --- a/dom/canvas/test/captureStream_common.js +++ b/dom/canvas/test/captureStream_common.js @@ -25,8 +25,11 @@ function CaptureStreamTestHelper(width, height) { CaptureStreamTestHelper.prototype = { /* Predefined colors for use in the methods below. */ black: { data: [0, 0, 0, 255], name: "black" }, + blackTransparent: { data: [0, 0, 0, 0], name: "blackTransparent" }, green: { data: [0, 255, 0, 255], name: "green" }, red: { data: [255, 0, 0, 255], name: "red" }, + blue: { data: [0, 0, 255, 255], name: "blue"}, + grey: { data: [128, 128, 128, 255], name: "grey" }, /* Default element size for createAndAppendElement() */ elemWidth: 100, @@ -52,55 +55,106 @@ CaptureStreamTestHelper.prototype = { video.srcObject.requestFrame(); }, - /* Tests the top left pixel of |video| against |refData|. Format [R,G,B,A]. */ - testPixel: function (video, refData, threshold) { + /* + * Returns the pixel at (|offsetX|, |offsetY|) (from top left corner) of + * |video| as an array of the pixel's color channels: [R,G,B,A]. Allows + * optional scaling of the drawImage() call (so that a 1x1 black image + * won't just draw 1 pixel in the corner) + */ + getPixel: function (video, offsetX, offsetY, width, height) { + offsetX = offsetX || 0; // Set to 0 if not passed in. + offsetY = offsetY || 0; // Set to 0 if not passed in. + width = width || 0; // Set to 0 if not passed in. + height = height || 0; // Set to 0 if not passed in. + + // Avoids old values in case of a transparent image. + CaptureStreamTestHelper2D.prototype.clear.call(this, this.cout); + var ctxout = this.cout.getContext('2d'); - ctxout.drawImage(video, 0, 0); - var pixel = ctxout.getImageData(0, 0, 1, 1).data; - return pixel.every((val, i) => Math.abs(val - refData[i]) <= threshold); + if (width != 0 || height != 0) { + ctxout.drawImage(video, 0, 0, width, height); + } else { + ctxout.drawImage(video, 0, 0); + } + return ctxout.getImageData(offsetX, offsetY, 1, 1).data; }, /* - * Returns a promise that resolves when the pixel matches. Use |threshold| - * for fuzzy matching the color on each channel, in the range [0,255]. + * Returns true if px lies within the per-channel |threshold| of the + * referenced color for all channels. px is on the form of an array of color + * channels, [R,G,B,A]. Each channel is in the range [0, 255]. */ - waitForPixel: function (video, refColor, threshold, infoString) { + isPixel: function (px, refColor, threshold) { + threshold = threshold || 0; // Default to 0 (exact match) if not passed in. + return px.every((ch, i) => Math.abs(ch - refColor.data[i]) <= threshold); + }, + + /* + * Returns true if px lies further away than |threshold| of the + * referenced color for any channel. px is on the form of an array of color + * channels, [R,G,B,A]. Each channel is in the range [0, 255]. + */ + isPixelNot: function (px, refColor, threshold) { + if (threshold === undefined) { + // Default to 127 (should be sufficiently far away) if not passed in. + threshold = 127; + } + return px.some((ch, i) => Math.abs(ch - refColor.data[i]) > threshold); + }, + + /* + * Returns a promise that resolves when the provided function |test| + * returns true. + */ + waitForPixel: function (video, offsetX, offsetY, test, timeout, width, height) { return new Promise(resolve => { - info("Testing " + video.id + " against [" + refColor.data.join(',') + "]"); - CaptureStreamTestHelper2D.prototype.clear.call(this, this.cout); - video.ontimeupdate = () => { - if (this.testPixel(video, refColor.data, threshold)) { - ok(true, video.id + " " + infoString); - video.ontimeupdate = null; - resolve(); + const startTime = video.currentTime; + var ontimeupdate = () => { + var pixelMatch = false; + try { + pixelMatch = test(this.getPixel(video, offsetX, offsetY, width, height)); + } catch (NS_ERROR_NOT_AVAILABLE) { + info("Waiting for pixel but no video available"); } + if (!pixelMatch && + (!timeout || video.currentTime < startTime + (timeout / 1000.0))) { + // No match yet and, + // No timeout (waiting indefinitely) or |timeout| has not passed yet. + return; + } + video.removeEventListener("timeupdate", ontimeupdate); + resolve(pixelMatch); }; + video.addEventListener("timeupdate", ontimeupdate); }); }, /* - * Returns a promise that resolves after |timeout| ms of playback or when a - * pixel of |video| becomes the color |refData|. The test is failed if the + * Returns a promise that resolves when the top left pixel of |video| matches + * on all channels. Use |threshold| for fuzzy matching the color on each + * channel, in the range [0,255]. + */ + waitForPixelColor: function (video, refColor, threshold, infoString) { + info("Waiting for video " + video.id + " to match [" + + refColor.data.join(',') + "] - " + refColor.name + + " (" + infoString + ")"); + return this.waitForPixel(video, 0, 0, + px => this.isPixel(px, refColor, threshold)) + .then(() => ok(true, video.id + " " + infoString)); + }, + + /* + * Returns a promise that resolves after |timeout| ms of playback or when the + * top left pixel of |video| becomes |refColor|. The test is failed if the * timeout is not reached. */ - waitForPixelToTimeout: function (video, refColor, threshold, timeout, infoString) { - return new Promise(resolve => { - info("Waiting for " + video.id + " to time out after " + timeout + - "ms against [" + refColor.data.join(',') + "] - " + refColor.name); - CaptureStreamTestHelper2D.prototype.clear.call(this, this.cout); - var startTime = video.currentTime; - video.ontimeupdate = () => { - if (this.testPixel(video, refColor.data, threshold)) { - ok(false, video.id + " " + infoString); - video.ontimeupdate = null; - resolve(); - } else if (video.currentTime > startTime + (timeout / 1000.0)) { - ok(true, video.id + " " + infoString); - video.ontimeupdate = null; - resolve(); - } - }; - }); + waitForPixelColorTimeout: function (video, refColor, threshold, timeout, infoString) { + info("Waiting for " + video.id + " to time out after " + timeout + + "ms against [" + refColor.data.join(',') + "] - " + refColor.name); + return this.waitForPixel(video, 0, 0, + px => this.isPixel(px, refColor, threshold), + timeout) + .then(result => ok(!result, video.id + " " + infoString)); }, /* Create an element of type |type| with id |id| and append it to the body. */ diff --git a/dom/canvas/test/test_capture.html b/dom/canvas/test/test_capture.html index ec2902b3ac..69222d75a9 100644 --- a/dom/canvas/test/test_capture.html +++ b/dom/canvas/test/test_capture.html @@ -23,15 +23,21 @@ function checkDrawColorInitialRed() { vmanual.srcObject = c.captureStream(0); vrate.srcObject = c.captureStream(10); - ok(h.testPixel(vauto, [0, 0, 0, 0], 0), "vauto hould not be drawn to before stable state"); - ok(h.testPixel(vrate, [0, 0, 0, 0], 0), "vrate Should not be drawn to before stable state"); - ok(h.testPixel(vmanual, [0, 0, 0, 0], 0), "vmanual Should not be drawn to before stable state"); + ok(h.isPixel(h.getPixel(vauto), h.blackTransparent, 0), + "vauto should not be drawn to before stable state"); + ok(h.isPixel(h.getPixel(vrate), h.blackTransparent, 0), + "vrate should not be drawn to before stable state"); + ok(h.isPixel(h.getPixel(vmanual), h.blackTransparent, 0), + "vmanual should not be drawn to before stable state"); return Promise.resolve() - .then(() => h.waitForPixel(vauto, h.red, 0, "should become red automatically")) - .then(() => h.waitForPixel(vrate, h.red, 0, "should become red automatically")) - .then(() => h.waitForPixel(vmanual, h.red, 0, "should become red when we get" + - " to stable state (first frame)")); + .then(() => h.waitForPixelColor(vauto, h.red, 0, + "should become red automatically")) + .then(() => h.waitForPixelColor(vrate, h.red, 0, + "should become red automatically")) + .then(() => h.waitForPixelColor(vmanual, h.red, 0, + "should become red when we get" + + " to stable state (first frame)")); } function checkDrawColorGreen() { @@ -40,11 +46,15 @@ function checkDrawColorGreen() { var drawing = h.startDrawing(() => h.drawColor(c, h.green)); return Promise.resolve() - .then(() => h.waitForPixel(vauto, h.green, 0, "should become green automatically")) - .then(() => h.waitForPixel(vrate, h.green, 0, "should become green automatically")) - .then(() => h.waitForPixel(vmanual, h.red, 0, "should still be red")) + .then(() => h.waitForPixelColor(vauto, h.green, 0, + "should become green automatically")) + .then(() => h.waitForPixelColor(vrate, h.green, 0, + "should become green automatically")) + .then(() => h.waitForPixelColor(vmanual, h.red, 0, + "should still be red")) .then(() => h.requestFrame(vmanual)) - .then(() => h.waitForPixel(vmanual, h.green, 0, "should become green after requstFrame()")) + .then(() => h.waitForPixelColor(vmanual, h.green, 0, + "should become green after requstFrame()")) .catch(err => ok(false, "checkDrawColorGreen failed: ", err)) .then(() => drawing.stop()); } @@ -54,10 +64,12 @@ function checkRequestFrameOrderGuarantee() { "call results in the expected frame seen in the stream."); return Promise.resolve() - .then(() => h.waitForPixel(vmanual, h.green, 0, "should still be green")) + .then(() => h.waitForPixelColor(vmanual, h.green, 0, + "should still be green")) .then(() => h.drawColor(c, h.red)) // 1. Draw canvas red .then(() => h.requestFrame(vmanual)) // 2. Immediately request a frame - .then(() => h.waitForPixel(vmanual, h.red, 0, "should become red after call order test")) + .then(() => h.waitForPixelColor(vmanual, h.red, 0, + "should become red after call order test")) } function checkDrawImageNotCleanRed() { @@ -74,11 +86,14 @@ function checkDrawImageNotCleanRed() { }) .then(() => drawing = h.startDrawing(() => ctx.drawImage(notCleanRed, 0, 0, c.width, c.height))) .then(() => h.testNotClean(c)) - .then(() => h.waitForPixelToTimeout(vauto, h.red, 0, 1000, "should not become red")) - .then(() => h.waitForPixelToTimeout(vrate, h.red, 0, 0, "should not become red")) - .then(() => h.waitForPixel(vmanual, h.green, 0, "should still be green")) + .then(() => h.waitForPixelColorTimeout(vauto, h.red, 0, 1000, + "should not become red")) + .then(() => h.isPixelNot(h.getPixel(vrate), h.red, 250, + "should not have become red")) + .then(() => h.waitForPixelColor(vmanual, h.green, 0, "should still be green")) .then(() => h.requestFrame(vmanual)) - .then(() => h.waitForPixelToTimeout(vmanual, h.red, 0, 1000, "should not become red")) + .then(() => h.waitForPixelColorTimeout(vmanual, h.red, 0, 1000, + "should not become red")) .catch(err => ok(false, "checkDrawImageNotCleanRed failed: ", err)) .then(() => drawing.stop()); } diff --git a/dom/canvas/test/webgl-mochitest/test_capture.html b/dom/canvas/test/webgl-mochitest/test_capture.html index 489d678723..fc27b35e73 100644 --- a/dom/canvas/test/webgl-mochitest/test_capture.html +++ b/dom/canvas/test/webgl-mochitest/test_capture.html @@ -54,14 +54,21 @@ function checkClearColorInitialRed() { vmanual.srcObject = c.captureStream(0); vrate.srcObject = c.captureStream(10); - ok(h.testPixel(vauto, [0, 0, 0, 0], 0), "Should not be drawn to before stable state"); - ok(h.testPixel(vrate, [0, 0, 0, 0], 0), "Should not be drawn to before stable state"); - ok(h.testPixel(vmanual, [0, 0, 0, 0], 0), "Should not be drawn to before stable state"); + ok(h.isPixel(h.getPixel(vauto), h.blackTransparent, 0, + "vauto should not be drawn to before stable state")); + ok(h.isPixel(h.getPixel(vrate), h.blackTransparent, 0, + "vrate should not be drawn to before stable state")); + ok(h.isPixel(h.getPixel(vmanual), h.blackTransparent, 0, + "vmanual should not be drawn to before stable state")); return Promise.resolve() - .then(() => h.waitForPixel(vauto, h.red, 0, "should become red automatically")) - .then(() => h.waitForPixel(vrate, h.red, 0, "should become red automatically")) - .then(() => h.waitForPixel(vmanual, h.red, 0, "should become red when we get to stable state (first frame)")) + .then(() => h.waitForPixelColor(vauto, h.red, 0, + "should become red automatically")) + .then(() => h.waitForPixelColor(vrate, h.red, 0, + "should become red automatically")) + .then(() => h.waitForPixelColor(vmanual, h.red, 0, + "should become red when we get to stable " + + "state (first frame)")) } function checkDrawColorGreen() { @@ -69,11 +76,15 @@ function checkDrawColorGreen() { var drawing = h.startDrawing(h.drawColor.bind(h, c, h.green)); checkGLError('after DrawColor'); return Promise.resolve() - .then(() => h.waitForPixel(vauto, h.green, 0, "should become green automatically")) - .then(() => h.waitForPixel(vrate, h.green, 0, "should become green automatically")) - .then(() => h.waitForPixel(vmanual, h.red, 0, "should still be red")) + .then(() => h.waitForPixelColor(vauto, h.green, 0, + "should become green automatically")) + .then(() => h.waitForPixelColor(vrate, h.green, 0, + "should become green automatically")) + .then(() => h.waitForPixelColor(vmanual, h.red, 0, + "should still be red")) .then(() => h.requestFrame(vmanual)) - .then(() => h.waitForPixel(vmanual, h.green, 0, "should become green after requstFrame()")) + .then(() => h.waitForPixelColor(vmanual, h.green, 0, + "should become green after requstFrame()")) .then(() => drawing.stop()); } @@ -81,11 +92,15 @@ function checkClearColorRed() { info("Checking that clearing to red works."); var drawing = h.startDrawing(h.clearColor.bind(h, c, h.red)); return Promise.resolve() - .then(() => h.waitForPixel(vauto, h.red, 0, "should become red automatically")) - .then(() => h.waitForPixel(vrate, h.red, 0, "should become red automatically")) - .then(() => h.waitForPixel(vmanual, h.green, 0, "should still be green")) + .then(() => h.waitForPixelColor(vauto, h.red, 0, + "should become red automatically")) + .then(() => h.waitForPixelColor(vrate, h.red, 0, + "should become red automatically")) + .then(() => h.waitForPixelColor(vmanual, h.green, 0, + "should still be green")) .then(() => h.requestFrame(vmanual)) - .then(() => h.waitForPixel(vmanual, h.red, 0, "should become red after requestFrame()")) + .then(() => h.waitForPixelColor(vmanual, h.red, 0, + "should become red after requestFrame()")) .then(() => drawing.stop()); } @@ -93,10 +108,11 @@ function checkRequestFrameOrderGuarantee() { info("Checking that requestFrame() immediately after a draw " + "call results in the expected frame seen in the stream."); return Promise.resolve() - .then(() => h.waitForPixel(vmanual, h.red, 0, "should still be red")) + .then(() => h.waitForPixelColor(vmanual, h.red, 0, "should still be red")) .then(() => h.drawColor(c, h.green)) // 1. Draw canvas green .then(() => h.requestFrame(vmanual)) // 2. Immediately request a frame - .then(() => h.waitForPixel(vmanual, h.green, 0, "should become green after call order test")) + .then(() => h.waitForPixelColor(vmanual, h.green, 0, + "should become green after call order test")) } function finish() { diff --git a/dom/events/AnimationEvent.cpp b/dom/events/AnimationEvent.cpp index 0a46606716..16c36ee54c 100644 --- a/dom/events/AnimationEvent.cpp +++ b/dom/events/AnimationEvent.cpp @@ -22,7 +22,7 @@ AnimationEvent::AnimationEvent(EventTarget* aOwner, } else { mEventIsInternal = true; - mEvent->time = PR_Now(); + mEvent->mTime = PR_Now(); } } diff --git a/dom/events/BeforeAfterKeyboardEvent.cpp b/dom/events/BeforeAfterKeyboardEvent.cpp index 612ad53557..1fc4017095 100644 --- a/dom/events/BeforeAfterKeyboardEvent.cpp +++ b/dom/events/BeforeAfterKeyboardEvent.cpp @@ -26,7 +26,7 @@ BeforeAfterKeyboardEvent::BeforeAfterKeyboardEvent( if (!aEvent) { mEventIsInternal = true; - mEvent->time = PR_Now(); + mEvent->mTime = PR_Now(); } } diff --git a/dom/events/ClipboardEvent.cpp b/dom/events/ClipboardEvent.cpp index 5038622ae8..c77cd91930 100644 --- a/dom/events/ClipboardEvent.cpp +++ b/dom/events/ClipboardEvent.cpp @@ -22,7 +22,7 @@ ClipboardEvent::ClipboardEvent(EventTarget* aOwner, mEventIsInternal = false; } else { mEventIsInternal = true; - mEvent->time = PR_Now(); + mEvent->mTime = PR_Now(); } } @@ -54,7 +54,7 @@ ClipboardEvent::InitClipboardEvent(const nsAString& aType, bool aCanBubble, DataTransfer* aClipboardData) { Event::InitEvent(aType, aCanBubble, aCancelable); - mEvent->AsClipboardEvent()->clipboardData = aClipboardData; + mEvent->AsClipboardEvent()->mClipboardData = aClipboardData; } already_AddRefed @@ -98,19 +98,19 @@ ClipboardEvent::GetClipboardData() { InternalClipboardEvent* event = mEvent->AsClipboardEvent(); - if (!event->clipboardData) { + if (!event->mClipboardData) { if (mEventIsInternal) { - event->clipboardData = + event->mClipboardData = new DataTransfer(ToSupports(this), eCopy, false, -1); } else { - event->clipboardData = + event->mClipboardData = new DataTransfer(ToSupports(this), event->mMessage, event->mMessage == ePaste, nsIClipboard::kGlobalClipboard); } } - return event->clipboardData; + return event->mClipboardData; } } // namespace dom diff --git a/dom/events/CommandEvent.cpp b/dom/events/CommandEvent.cpp index 5248a2629a..e548c89e0b 100644 --- a/dom/events/CommandEvent.cpp +++ b/dom/events/CommandEvent.cpp @@ -18,7 +18,7 @@ CommandEvent::CommandEvent(EventTarget* aOwner, aEvent ? aEvent : new WidgetCommandEvent(false, nullptr, nullptr, nullptr)) { - mEvent->time = PR_Now(); + mEvent->mTime = PR_Now(); if (aEvent) { mEventIsInternal = false; } else { @@ -53,7 +53,7 @@ CommandEvent::InitCommandEvent(const nsAString& aTypeArg, { Event::InitEvent(aTypeArg, aCanBubbleArg, aCancelableArg); - mEvent->AsCommandEvent()->command = do_GetAtom(aCommand); + mEvent->AsCommandEvent()->command = NS_Atomize(aCommand); return NS_OK; } diff --git a/dom/events/CompositionEvent.cpp b/dom/events/CompositionEvent.cpp index dd13caa253..d6ad4911fb 100644 --- a/dom/events/CompositionEvent.cpp +++ b/dom/events/CompositionEvent.cpp @@ -25,7 +25,7 @@ CompositionEvent::CompositionEvent(EventTarget* aOwner, mEventIsInternal = false; } else { mEventIsInternal = true; - mEvent->time = PR_Now(); + mEvent->mTime = PR_Now(); // XXX compositionstart is cancelable in draft of DOM3 Events. // However, it doesn't make sence for us, we cannot cancel composition diff --git a/dom/events/ContentEventHandler.cpp b/dom/events/ContentEventHandler.cpp index f2603306b7..c75c4cfbbd 100644 --- a/dom/events/ContentEventHandler.cpp +++ b/dom/events/ContentEventHandler.cpp @@ -183,9 +183,9 @@ ContentEventHandler::InitCommon() NS_ENSURE_TRUE(endNode, NS_ERROR_FAILURE); // See bug 537041 comment 5, the range could have removed node. - NS_ENSURE_TRUE(startNode->GetCurrentDoc() == mPresShell->GetDocument(), + NS_ENSURE_TRUE(startNode->GetUncomposedDoc() == mPresShell->GetDocument(), NS_ERROR_NOT_AVAILABLE); - NS_ASSERTION(startNode->GetCurrentDoc() == endNode->GetCurrentDoc(), + NS_ASSERTION(startNode->GetUncomposedDoc() == endNode->GetUncomposedDoc(), "mFirstSelectedRange crosses the document boundary"); mRootContent = startNode->GetSelectionRootContent(mPresShell); @@ -1525,9 +1525,9 @@ ContentEventHandler::OnQueryCharacterAtPoint(WidgetQueryContentEvent* aEvent) // The root frame's widget might be different, e.g., the event was fired on // a popup but the rootFrame is the document root. - if (rootWidget != aEvent->widget) { - NS_PRECONDITION(aEvent->widget, "The event must have the widget"); - nsView* view = nsView::GetViewFor(aEvent->widget); + if (rootWidget != aEvent->mWidget) { + NS_PRECONDITION(aEvent->mWidget, "The event must have the widget"); + nsView* view = nsView::GetViewFor(aEvent->mWidget); NS_ENSURE_TRUE(view, NS_ERROR_FAILURE); rootFrame = view->GetFrame(); NS_ENSURE_TRUE(rootFrame, NS_ERROR_FAILURE); @@ -1538,9 +1538,9 @@ ContentEventHandler::OnQueryCharacterAtPoint(WidgetQueryContentEvent* aEvent) WidgetQueryContentEvent eventOnRoot(true, eQueryCharacterAtPoint, rootWidget); eventOnRoot.mUseNativeLineBreak = aEvent->mUseNativeLineBreak; - eventOnRoot.refPoint = aEvent->refPoint; - if (rootWidget != aEvent->widget) { - eventOnRoot.refPoint += aEvent->widget->WidgetToScreenOffset() - + eventOnRoot.mRefPoint = aEvent->mRefPoint; + if (rootWidget != aEvent->mWidget) { + eventOnRoot.mRefPoint += aEvent->mWidget->WidgetToScreenOffset() - rootWidget->WidgetToScreenOffset(); } nsPoint ptInRoot = @@ -1602,7 +1602,7 @@ ContentEventHandler::OnQueryCharacterAtPoint(WidgetQueryContentEvent* aEvent) return rv; } - WidgetQueryContentEvent textRect(true, eQueryTextRect, aEvent->widget); + WidgetQueryContentEvent textRect(true, eQueryTextRect, aEvent->mWidget); textRect.InitForQueryTextRect(offset, 1, aEvent->mUseNativeLineBreak); rv = OnQueryTextRect(&textRect); NS_ENSURE_SUCCESS(rv, rv); @@ -1628,14 +1628,15 @@ ContentEventHandler::OnQueryDOMWidgetHittest(WidgetQueryContentEvent* aEvent) aEvent->mSucceeded = false; aEvent->mReply.mWidgetIsHit = false; - NS_ENSURE_TRUE(aEvent->widget, NS_ERROR_FAILURE); + NS_ENSURE_TRUE(aEvent->mWidget, NS_ERROR_FAILURE); nsIDocument* doc = mPresShell->GetDocument(); NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); nsIFrame* docFrame = mPresShell->GetRootFrame(); NS_ENSURE_TRUE(docFrame, NS_ERROR_FAILURE); - LayoutDeviceIntPoint eventLoc = aEvent->refPoint + aEvent->widget->WidgetToScreenOffset(); + LayoutDeviceIntPoint eventLoc = + aEvent->mRefPoint + aEvent->mWidget->WidgetToScreenOffset(); nsIntRect docFrameRect = docFrame->GetScreenRect(); // Returns CSS pixels CSSIntPoint eventLocCSS( mPresContext->DevPixelsToIntCSSPixels(eventLoc.x) - docFrameRect.x, @@ -1652,7 +1653,7 @@ ContentEventHandler::OnQueryDOMWidgetHittest(WidgetQueryContentEvent* aEvent) } else if (targetFrame) { targetWidget = targetFrame->GetNearestWidget(); } - if (aEvent->widget == targetWidget) { + if (aEvent->mWidget == targetWidget) { aEvent->mReply.mWidgetIsHit = true; } } diff --git a/dom/events/DataTransfer.cpp b/dom/events/DataTransfer.cpp index 0e3c627c64..02bb542204 100644 --- a/dom/events/DataTransfer.cpp +++ b/dom/events/DataTransfer.cpp @@ -162,7 +162,7 @@ DataTransfer::Constructor(const GlobalObject& aGlobal, { nsAutoCString onEventType("on"); AppendUTF16toUTF8(aEventType, onEventType); - nsCOMPtr eventTypeAtom = do_GetAtom(onEventType); + nsCOMPtr eventTypeAtom = NS_Atomize(onEventType); if (!eventTypeAtom) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return nullptr; @@ -320,13 +320,19 @@ DataTransfer::GetFileListInternal(ErrorResult& aRv, RefPtr domFile; if (file) { -#ifdef DEBUG - if (XRE_GetProcessType() == GeckoProcessType_Default) { - bool isDir; - file->IsDirectory(&isDir); - MOZ_ASSERT(!isDir, "How did we get here?"); + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default, + "nsIFile objects are not expected on the content process"); + + bool isDir; + aRv = file->IsDirectory(&isDir); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; } -#endif + + if (isDir) { + continue; + } + domFile = File::CreateFromFile(GetParentObject(), file); } else { nsCOMPtr blobImpl = do_QueryInterface(supports); @@ -940,8 +946,8 @@ DataTransfer::GetTransferables(nsIDOMNode* aDragTarget) if (!dragNode) { return nullptr; } - - nsIDocument* doc = dragNode->GetCurrentDoc(); + + nsIDocument* doc = dragNode->GetUncomposedDoc(); if (!doc) { return nullptr; } @@ -1052,7 +1058,7 @@ DataTransfer::ConvertFromVariant(nsIVariant* aVariant, nsCOMPtr data; if (NS_FAILED(aVariant->GetAsISupports(getter_AddRefs(data)))) return false; - + nsCOMPtr fdp = do_QueryInterface(data); if (fdp) { // for flavour data providers, use kFlavorHasDataProvider (which has the diff --git a/dom/events/DragEvent.cpp b/dom/events/DragEvent.cpp index bb39a1ea14..fefb4748d3 100644 --- a/dom/events/DragEvent.cpp +++ b/dom/events/DragEvent.cpp @@ -24,8 +24,8 @@ DragEvent::DragEvent(EventTarget* aOwner, } else { mEventIsInternal = true; - mEvent->time = PR_Now(); - mEvent->refPoint.x = mEvent->refPoint.y = 0; + mEvent->mTime = PR_Now(); + mEvent->mRefPoint = LayoutDeviceIntPoint(0, 0); mEvent->AsMouseEvent()->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN; } } @@ -66,7 +66,7 @@ DragEvent::InitDragEvent(const nsAString& aType, } if (mEventIsInternal && mEvent) { - mEvent->AsDragEvent()->dataTransfer = aDataTransfer; + mEvent->AsDragEvent()->mDataTransfer = aDataTransfer; } } @@ -98,7 +98,7 @@ DragEvent::InitDragEvent(const nsAString& aType, NS_ENSURE_SUCCESS(rv, rv); if (mEventIsInternal && mEvent) { - mEvent->AsDragEvent()->dataTransfer = dataTransfer; + mEvent->AsDragEvent()->mDataTransfer = dataTransfer; } return NS_OK; @@ -130,7 +130,7 @@ DragEvent::GetDataTransfer() NS_ENSURE_SUCCESS(rv, nullptr); } - return dragEvent->dataTransfer; + return dragEvent->mDataTransfer; } // static diff --git a/dom/events/Event.cpp b/dom/events/Event.cpp index 2cb3760b1c..7084d3f8cd 100644 --- a/dom/events/Event.cpp +++ b/dom/events/Event.cpp @@ -107,7 +107,7 @@ Event::ConstructorInit(EventTarget* aOwner, } */ mEvent = new WidgetEvent(false, eVoidEvent); - mEvent->time = PR_Now(); + mEvent->mTime = PR_Now(); } InitPresContextData(aPresContext); @@ -153,9 +153,9 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Event) if (tmp->mEventIsInternal) { - tmp->mEvent->target = nullptr; - tmp->mEvent->currentTarget = nullptr; - tmp->mEvent->originalTarget = nullptr; + tmp->mEvent->mTarget = nullptr; + tmp->mEvent->mCurrentTarget = nullptr; + tmp->mEvent->mOriginalTarget = nullptr; switch (tmp->mEvent->mClass) { case eMouseEventClass: case eMouseScrollEventClass: @@ -166,18 +166,18 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Event) break; case eDragEventClass: { WidgetDragEvent* dragEvent = tmp->mEvent->AsDragEvent(); - dragEvent->dataTransfer = nullptr; + dragEvent->mDataTransfer = nullptr; dragEvent->relatedTarget = nullptr; break; } case eClipboardEventClass: - tmp->mEvent->AsClipboardEvent()->clipboardData = nullptr; + tmp->mEvent->AsClipboardEvent()->mClipboardData = nullptr; break; case eMutationEventClass: tmp->mEvent->AsMutationEvent()->mRelatedNode = nullptr; break; case eFocusEventClass: - tmp->mEvent->AsFocusEvent()->relatedTarget = nullptr; + tmp->mEvent->AsFocusEvent()->mRelatedTarget = nullptr; break; default: break; @@ -191,9 +191,9 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Event) if (tmp->mEventIsInternal) { - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->target) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->currentTarget) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->originalTarget) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mTarget) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mCurrentTarget) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mOriginalTarget) switch (tmp->mEvent->mClass) { case eMouseEventClass: case eMouseScrollEventClass: @@ -205,23 +205,23 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Event) break; case eDragEventClass: { WidgetDragEvent* dragEvent = tmp->mEvent->AsDragEvent(); - NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->dataTransfer"); - cb.NoteXPCOMChild(dragEvent->dataTransfer); + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->mDataTransfer"); + cb.NoteXPCOMChild(dragEvent->mDataTransfer); NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->relatedTarget"); cb.NoteXPCOMChild(dragEvent->relatedTarget); break; } case eClipboardEventClass: - NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->clipboardData"); - cb.NoteXPCOMChild(tmp->mEvent->AsClipboardEvent()->clipboardData); + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->mClipboardData"); + cb.NoteXPCOMChild(tmp->mEvent->AsClipboardEvent()->mClipboardData); break; case eMutationEventClass: NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->mRelatedNode"); cb.NoteXPCOMChild(tmp->mEvent->AsMutationEvent()->mRelatedNode); break; case eFocusEventClass: - NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->relatedTarget"); - cb.NoteXPCOMChild(tmp->mEvent->AsFocusEvent()->relatedTarget); + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->mRelatedTarget"); + cb.NoteXPCOMChild(tmp->mEvent->AsFocusEvent()->mRelatedTarget); break; default: break; @@ -258,8 +258,8 @@ Event::IsChrome(JSContext* aCx) const NS_METHOD Event::GetType(nsAString& aType) { - if (!mIsMainThreadEvent || !mEvent->typeString.IsEmpty()) { - aType = mEvent->typeString; + if (!mIsMainThreadEvent || !mEvent->mSpecifiedEventTypeString.IsEmpty()) { + aType = mEvent->mSpecifiedEventTypeString; return NS_OK; } const char* name = GetEventName(mEvent->mMessage); @@ -267,9 +267,11 @@ Event::GetType(nsAString& aType) if (name) { CopyASCIItoUTF16(name, aType); return NS_OK; - } else if (mEvent->mMessage == eUnidentifiedEvent && mEvent->userType) { - aType = Substring(nsDependentAtomString(mEvent->userType), 2); // Remove "on" - mEvent->typeString = aType; + } else if (mEvent->mMessage == eUnidentifiedEvent && + mEvent->mSpecifiedEventType) { + // Remove "on" + aType = Substring(nsDependentAtomString(mEvent->mSpecifiedEventType), 2); + mEvent->mSpecifiedEventTypeString = aType; return NS_OK; } @@ -286,7 +288,7 @@ GetDOMEventTarget(nsIDOMEventTarget* aTarget) EventTarget* Event::GetTarget() const { - return GetDOMEventTarget(mEvent->target); + return GetDOMEventTarget(mEvent->mTarget); } NS_METHOD @@ -299,7 +301,7 @@ Event::GetTarget(nsIDOMEventTarget** aTarget) EventTarget* Event::GetCurrentTarget() const { - return GetDOMEventTarget(mEvent->currentTarget); + return GetDOMEventTarget(mEvent->mCurrentTarget); } NS_IMETHODIMP @@ -317,7 +319,7 @@ Event::GetTargetFromFrame() { if (!mPresContext) { return nullptr; } - // Get the target frame (have to get the ESM first) + // Get the mTarget frame (have to get the ESM first) nsIFrame* targetFrame = mPresContext->EventStateManager()->GetEventTarget(); if (!targetFrame) { return nullptr; } @@ -346,8 +348,8 @@ Event::GetExplicitOriginalTarget(nsIDOMEventTarget** aRealEventTarget) EventTarget* Event::GetOriginalTarget() const { - if (mEvent->originalTarget) { - return GetDOMEventTarget(mEvent->originalTarget); + if (mEvent->mOriginalTarget) { + return GetDOMEventTarget(mEvent->mOriginalTarget); } return GetTarget(); @@ -421,8 +423,8 @@ Event::EventPhase() const { // Note, remember to check that this works also // if or when Bug 235441 is fixed. - if ((mEvent->currentTarget && - mEvent->currentTarget == mEvent->target) || + if ((mEvent->mCurrentTarget && + mEvent->mCurrentTarget == mEvent->mTarget) || mEvent->mFlags.InTargetPhase()) { return nsIDOMEvent::AT_TARGET; } @@ -459,7 +461,7 @@ Event::GetCancelable(bool* aCancelable) NS_IMETHODIMP Event::GetTimeStamp(uint64_t* aTimeStamp) { - *aTimeStamp = mEvent->time; + *aTimeStamp = mEvent->mTime; return NS_OK; } @@ -530,9 +532,9 @@ Event::PreventDefaultInternal(bool aCalledByDefaultHandler) return; } - nsCOMPtr node = do_QueryInterface(mEvent->currentTarget); + nsCOMPtr node = do_QueryInterface(mEvent->mCurrentTarget); if (!node) { - nsCOMPtr win = do_QueryInterface(mEvent->currentTarget); + nsCOMPtr win = do_QueryInterface(mEvent->mCurrentTarget); if (!win) { return; } @@ -547,14 +549,14 @@ void Event::SetEventType(const nsAString& aEventTypeArg) { if (mIsMainThreadEvent) { - mEvent->typeString.Truncate(); - mEvent->userType = + mEvent->mSpecifiedEventTypeString.Truncate(); + mEvent->mSpecifiedEventType = nsContentUtils::GetEventMessageAndAtom(aEventTypeArg, mEvent->mClass, &(mEvent->mMessage)); } else { - mEvent->userType = nullptr; + mEvent->mSpecifiedEventType = nullptr; mEvent->mMessage = eUnidentifiedEvent; - mEvent->typeString = aEventTypeArg; + mEvent->mSpecifiedEventTypeString = aEventTypeArg; } } @@ -584,8 +586,8 @@ Event::InitEvent(const nsAString& aEventTypeArg, // Clearing the old targets, so that the event is targeted correctly when // re-dispatching it. - mEvent->target = nullptr; - mEvent->originalTarget = nullptr; + mEvent->mTarget = nullptr; + mEvent->mOriginalTarget = nullptr; } NS_IMETHODIMP @@ -616,14 +618,14 @@ Event::SetTarget(nsIDOMEventTarget* aTarget) } #endif - mEvent->target = do_QueryInterface(aTarget); + mEvent->mTarget = do_QueryInterface(aTarget); return NS_OK; } NS_IMETHODIMP_(bool) Event::IsDispatchStopped() { - return mEvent->mFlags.mPropagationStopped; + return mEvent->PropagationStopped(); } NS_IMETHODIMP_(WidgetEvent*) @@ -914,18 +916,18 @@ Event::GetScreenCoords(nsPresContext* aPresContext, // Doing a straight conversion from LayoutDeviceIntPoint to CSSIntPoint // seem incorrect, but it is needed to maintain legacy functionality. WidgetGUIEvent* guiEvent = aEvent->AsGUIEvent(); - if (!aPresContext || !(guiEvent && guiEvent->widget)) { + if (!aPresContext || !(guiEvent && guiEvent->mWidget)) { return CSSIntPoint(aPoint.x, aPoint.y); } nsPoint pt = LayoutDevicePixel::ToAppUnits(aPoint, aPresContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom()); - if (aPresContext->PresShell()) { - pt = pt.RemoveResolution(nsLayoutUtils::GetCurrentAPZResolutionScale(aPresContext->PresShell())); + if (nsIPresShell* ps = aPresContext->GetPresShell()) { + pt = pt.RemoveResolution(nsLayoutUtils::GetCurrentAPZResolutionScale(ps)); } - pt += LayoutDevicePixel::ToAppUnits(guiEvent->widget->WidgetToScreenOffset(), + pt += LayoutDevicePixel::ToAppUnits(guiEvent->mWidget->WidgetToScreenOffset(), aPresContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom()); return CSSPixel::FromAppUnitsRounded(pt); @@ -973,7 +975,7 @@ Event::GetClientCoords(nsPresContext* aPresContext, aEvent->mClass != ePointerEventClass && aEvent->mClass != eSimpleGestureEventClass) || !aPresContext || - !aEvent->AsGUIEvent()->widget) { + !aEvent->AsGUIEvent()->mWidget) { return aDefaultPoint; } @@ -998,22 +1000,10 @@ Event::GetOffsetCoords(nsPresContext* aPresContext, LayoutDeviceIntPoint aPoint, CSSIntPoint aDefaultPoint) { - // XXX: Known spec bug (WD 3 August 2016): - // The behavior in the spec doesn't do "during and after the dispatch". - // It just does "during the dispatch", and goes back to "pageX" after the - // dispatch. This is not expected (and cf. implemented) behavior, so we - // don't follow the spec here to align the property with all other browsers. - // Spec-compliant would be: - // if (!aEvent->mFlags.mIsBeingDispatched) { - // return GetPageCoords(aPresContext, aEvent, aPoint, aDefaultPoint); - // } - // See also: - // https://www.w3.org/Bugs/Public/show_bug.cgi?id=16673 - if (!aEvent->target) { + if (!aEvent->mTarget) { return GetPageCoords(aPresContext, aEvent, aPoint, aDefaultPoint); } - - nsCOMPtr content = do_QueryInterface(aEvent->target); + nsCOMPtr content = do_QueryInterface(aEvent->mTarget); if (!content || !aPresContext) { return CSSIntPoint(0, 0); } @@ -1085,10 +1075,10 @@ double Event::TimeStamp() const { if (!sReturnHighResTimeStamp) { - return static_cast(mEvent->time); + return static_cast(mEvent->mTime); } - if (mEvent->timeStamp.IsNull()) { + if (mEvent->mTimeStamp.IsNull()) { return 0.0; } @@ -1106,7 +1096,7 @@ Event::TimeStamp() const return 0.0; } - return perf->GetDOMTiming()->TimeStampToDOMHighRes(mEvent->timeStamp); + return perf->GetDOMTiming()->TimeStampToDOMHighRes(mEvent->mTimeStamp); } // For dedicated workers, we should make times relative to the navigation @@ -1117,7 +1107,7 @@ Event::TimeStamp() const MOZ_ASSERT(workerPrivate); TimeDuration duration = - mEvent->timeStamp - workerPrivate->NowBaseTimeStamp(); + mEvent->mTimeStamp - workerPrivate->NowBaseTimeStamp(); return duration.ToMilliseconds(); } diff --git a/dom/events/Event.h b/dom/events/Event.h index cf0fb1f274..78d9da156d 100644 --- a/dom/events/Event.h +++ b/dom/events/Event.h @@ -189,6 +189,16 @@ public: return mEvent->DefaultPrevented(); } + bool DefaultPreventedByChrome() const + { + return mEvent->mFlags.mDefaultPreventedByChrome; + } + + bool DefaultPreventedByContent() const + { + return mEvent->mFlags.mDefaultPreventedByContent; + } + bool MultipleActionsPrevented() const { return mEvent->mFlags.mMultipleActionsPrevented; diff --git a/dom/events/EventDispatcher.cpp b/dom/events/EventDispatcher.cpp index 24d3d272f3..15dc8ebf13 100644 --- a/dom/events/EventDispatcher.cpp +++ b/dom/events/EventDispatcher.cpp @@ -244,7 +244,7 @@ public: if (WantsWillHandleEvent()) { mTarget->WillHandleEvent(aVisitor); } - if (aVisitor.mEvent->mFlags.mPropagationStopped) { + if (aVisitor.mEvent->PropagationStopped()) { return; } if (aVisitor.mEvent->mFlags.mOnlySystemGroupDispatchInContent && @@ -259,13 +259,13 @@ public: mManager = mTarget->GetExistingListenerManager(); } if (mManager) { - NS_ASSERTION(aVisitor.mEvent->currentTarget == nullptr, + NS_ASSERTION(aVisitor.mEvent->mCurrentTarget == nullptr, "CurrentTarget should be null!"); mManager->HandleEvent(aVisitor.mPresContext, aVisitor.mEvent, &aVisitor.mDOMEvent, CurrentTarget(), &aVisitor.mEventStatus); - NS_ASSERTION(aVisitor.mEvent->currentTarget == nullptr, + NS_ASSERTION(aVisitor.mEvent->mCurrentTarget == nullptr, "CurrentTarget should be null!"); } } @@ -333,7 +333,7 @@ EventTargetChainItem::HandleEventTargetChain( ELMCreationDetector& aCd) { // Save the target so that it can be restored later. - nsCOMPtr firstTarget = aVisitor.mEvent->target; + nsCOMPtr firstTarget = aVisitor.mEvent->mTarget; uint32_t chainLength = aChain.Length(); // Capture @@ -343,7 +343,7 @@ EventTargetChainItem::HandleEventTargetChain( EventTargetChainItem& item = aChain[i]; if ((!aVisitor.mEvent->mFlags.mNoContentDispatch || item.ForceContentDispatch()) && - !aVisitor.mEvent->mFlags.mPropagationStopped) { + !aVisitor.mEvent->PropagationStopped()) { item.HandleEvent(aVisitor, aCd); } @@ -353,7 +353,7 @@ EventTargetChainItem::HandleEventTargetChain( uint32_t childIndex = j - 1; EventTarget* newTarget = aChain[childIndex].GetNewTarget(); if (newTarget) { - aVisitor.mEvent->target = newTarget; + aVisitor.mEvent->mTarget = newTarget; break; } } @@ -363,7 +363,7 @@ EventTargetChainItem::HandleEventTargetChain( // Target aVisitor.mEvent->mFlags.mInBubblingPhase = true; EventTargetChainItem& targetItem = aChain[0]; - if (!aVisitor.mEvent->mFlags.mPropagationStopped && + if (!aVisitor.mEvent->PropagationStopped() && (!aVisitor.mEvent->mFlags.mNoContentDispatch || targetItem.ForceContentDispatch())) { targetItem.HandleEvent(aVisitor, aCd); @@ -380,13 +380,13 @@ EventTargetChainItem::HandleEventTargetChain( if (newTarget) { // Item is at anonymous boundary. Need to retarget for the current item // and for parent items. - aVisitor.mEvent->target = newTarget; + aVisitor.mEvent->mTarget = newTarget; } if (aVisitor.mEvent->mFlags.mBubbles || newTarget) { if ((!aVisitor.mEvent->mFlags.mNoContentDispatch || item.ForceContentDispatch()) && - !aVisitor.mEvent->mFlags.mPropagationStopped) { + !aVisitor.mEvent->PropagationStopped()) { item.HandleEvent(aVisitor, aCd); } if (aVisitor.mEvent->mFlags.mInSystemGroup) { @@ -403,7 +403,7 @@ EventTargetChainItem::HandleEventTargetChain( aVisitor.mEvent->mFlags.mImmediatePropagationStopped = false; // Setting back the original target of the event. - aVisitor.mEvent->target = aVisitor.mEvent->originalTarget; + aVisitor.mEvent->mTarget = aVisitor.mEvent->mOriginalTarget; // Special handling if PresShell (or some other caller) // used a callback object. @@ -413,7 +413,7 @@ EventTargetChainItem::HandleEventTargetChain( // Retarget for system event group (which does the default handling too). // Setting back the target which was used also for default event group. - aVisitor.mEvent->target = firstTarget; + aVisitor.mEvent->mTarget = firstTarget; aVisitor.mEvent->mFlags.mInSystemGroup = true; HandleEventTargetChain(aChain, aVisitor, @@ -515,7 +515,7 @@ EventDispatcher::Dispatch(nsISupports* aTarget, do_QueryInterface(content->FindFirstNonChromeOnlyAccessContent()); NS_ENSURE_STATE(newTarget); - aEvent->originalTarget = target; + aEvent->mOriginalTarget = target; target = newTarget; retargeted = true; } @@ -532,7 +532,7 @@ EventDispatcher::Dispatch(nsISupports* aTarget, } // Set the target to be the original dispatch target, - aEvent->target = target; + aEvent->mTarget = target; // but use chrome event handler or TabChildGlobal for event target chain. target = piTarget; } else if (NS_WARN_IF(!doc)) { @@ -590,30 +590,30 @@ EventDispatcher::Dispatch(nsISupports* aTarget, // Make sure that nsIDOMEvent::target and nsIDOMEvent::originalTarget // point to the last item in the chain. - if (!aEvent->target) { + if (!aEvent->mTarget) { // Note, CurrentTarget() points always to the object returned by // GetTargetForEventTargetChain(). - aEvent->target = targetEtci->CurrentTarget(); + aEvent->mTarget = targetEtci->CurrentTarget(); } else { // XXX But if the target is already set, use that. This is a hack // for the 'load', 'beforeunload' and 'unload' events, // which are dispatched to |window| but have document as their target. // // Make sure that the event target points to the right object. - aEvent->target = aEvent->target->GetTargetForEventTargetChain(); - NS_ENSURE_STATE(aEvent->target); + aEvent->mTarget = aEvent->mTarget->GetTargetForEventTargetChain(); + NS_ENSURE_STATE(aEvent->mTarget); } if (retargeted) { - aEvent->originalTarget = - aEvent->originalTarget->GetTargetForEventTargetChain(); - NS_ENSURE_STATE(aEvent->originalTarget); + aEvent->mOriginalTarget = + aEvent->mOriginalTarget->GetTargetForEventTargetChain(); + NS_ENSURE_STATE(aEvent->mOriginalTarget); } else { - aEvent->originalTarget = aEvent->target; + aEvent->mOriginalTarget = aEvent->mTarget; } - nsCOMPtr content = do_QueryInterface(aEvent->originalTarget); + nsCOMPtr content = do_QueryInterface(aEvent->mOriginalTarget); bool isInAnon = (content && (content->IsInAnonymousSubtree() || content->IsInShadowTree())); @@ -637,7 +637,7 @@ EventDispatcher::Dispatch(nsISupports* aTarget, if (preVisitor.mCanHandle) { // At least the original target can handle the event. // Setting the retarget to the |target| simplifies retargeting code. - nsCOMPtr t = do_QueryInterface(aEvent->target); + nsCOMPtr t = do_QueryInterface(aEvent->mTarget); targetEtci->SetNewTarget(t); EventTargetChainItem* topEtci = targetEtci; targetEtci = nullptr; @@ -655,7 +655,7 @@ EventDispatcher::Dispatch(nsISupports* aTarget, if (preVisitor.mEventTargetAtParent) { // Need to set the target of the event // so that also the next retargeting works. - preVisitor.mEvent->target = preVisitor.mEventTargetAtParent; + preVisitor.mEvent->mTarget = preVisitor.mEventTargetAtParent; parentEtci->SetNewTarget(preVisitor.mEventTargetAtParent); } @@ -750,8 +750,8 @@ EventDispatcher::DispatchDOMEvent(nsISupports* aTarget, bool dontResetTrusted = false; if (innerEvent->mFlags.mDispatchedAtLeastOnce) { - innerEvent->target = nullptr; - innerEvent->originalTarget = nullptr; + innerEvent->mTarget = nullptr; + innerEvent->mOriginalTarget = nullptr; } else { aDOMEvent->GetIsTrusted(&dontResetTrusted); } diff --git a/dom/events/EventDispatcher.h b/dom/events/EventDispatcher.h index 8680de32a9..3c754033db 100644 --- a/dom/events/EventDispatcher.h +++ b/dom/events/EventDispatcher.h @@ -240,8 +240,8 @@ public: * aEvent is used as the target (unless there is event * retargeting) and the originalTarget of the DOM Event. * aTarget is always used as the starting point for constructing the event - * target chain, no matter what the value of aEvent->target is. - * In other words, aEvent->target is only a property of the event and it has + * target chain, no matter what the value of aEvent->mTarget is. + * In other words, aEvent->mTarget is only a property of the event and it has * nothing to do with the construction of the event target chain. * Neither aTarget nor aEvent is allowed to be nullptr. * diff --git a/dom/events/EventListenerManager.cpp b/dom/events/EventListenerManager.cpp index b7cba95bbf..b43b9605ec 100644 --- a/dom/events/EventListenerManager.cpp +++ b/dom/events/EventListenerManager.cpp @@ -666,9 +666,9 @@ EventListenerManager::ListenerCanHandle(Listener* aListener, } if (aEvent->mMessage == eUnidentifiedEvent) { if (mIsMainThreadELM) { - return aListener->mTypeAtom == aEvent->userType; + return aListener->mTypeAtom == aEvent->mSpecifiedEventType; } - return aListener->mTypeString.Equals(aEvent->typeString); + return aListener->mTypeString.Equals(aEvent->mSpecifiedEventTypeString); } MOZ_ASSERT(mIsMainThreadELM); return aListener->mEventMessage == aEvent->mMessage; @@ -681,7 +681,7 @@ EventListenerManager::AddEventListenerByType( const EventListenerFlags& aFlags) { nsCOMPtr atom = - mIsMainThreadELM ? do_GetAtom(NS_LITERAL_STRING("on") + aType) : nullptr; + mIsMainThreadELM ? NS_Atomize(NS_LITERAL_STRING("on") + aType) : nullptr; EventMessage message = nsContentUtils::GetEventMessage(atom); AddEventListenerInternal(aListenerHolder, message, atom, aType, aFlags); } @@ -693,7 +693,7 @@ EventListenerManager::RemoveEventListenerByType( const EventListenerFlags& aFlags) { nsCOMPtr atom = - mIsMainThreadELM ? do_GetAtom(NS_LITERAL_STRING("on") + aType) : nullptr; + mIsMainThreadELM ? NS_Atomize(NS_LITERAL_STRING("on") + aType) : nullptr; EventMessage message = nsContentUtils::GetEventMessage(atom); RemoveEventListenerInternal(aListenerHolder, message, atom, aType, aFlags); } @@ -1181,16 +1181,16 @@ EventListenerManager::HandleEventInternal(nsPresContext* aPresContext, if (!*aDOMEvent) { // This is tiny bit slow, but happens only once per event. nsCOMPtr et = - do_QueryInterface(aEvent->originalTarget); + do_QueryInterface(aEvent->mOriginalTarget); RefPtr event = EventDispatcher::CreateEvent(et, aPresContext, aEvent, EmptyString()); event.forget(aDOMEvent); } if (*aDOMEvent) { - if (!aEvent->currentTarget) { - aEvent->currentTarget = aCurrentTarget->GetTargetForDOMEvent(); - if (!aEvent->currentTarget) { + if (!aEvent->mCurrentTarget) { + aEvent->mCurrentTarget = aCurrentTarget->GetTargetForDOMEvent(); + if (!aEvent->mCurrentTarget) { break; } } @@ -1232,11 +1232,11 @@ EventListenerManager::HandleEventInternal(nsPresContext* aPresContext, } } - aEvent->currentTarget = nullptr; + aEvent->mCurrentTarget = nullptr; if (mIsMainThreadELM && !hasListener) { mNoListenerForEvent = aEvent->mMessage; - mNoListenerForEventAtom = aEvent->userType; + mNoListenerForEventAtom = aEvent->mSpecifiedEventType; } if (aEvent->DefaultPrevented()) { @@ -1345,7 +1345,7 @@ bool EventListenerManager::HasListenersFor(const nsAString& aEventName) { if (mIsMainThreadELM) { - nsCOMPtr atom = do_GetAtom(NS_LITERAL_STRING("on") + aEventName); + nsCOMPtr atom = NS_Atomize(NS_LITERAL_STRING("on") + aEventName); return HasListenersFor(atom); } diff --git a/dom/events/EventListenerManager.h b/dom/events/EventListenerManager.h index d955c3611c..3c1c8200f8 100644 --- a/dom/events/EventListenerManager.h +++ b/dom/events/EventListenerManager.h @@ -328,7 +328,7 @@ public: dom::EventTarget* aCurrentTarget, nsEventStatus* aEventStatus) { - if (mListeners.IsEmpty() || aEvent->mFlags.mPropagationStopped) { + if (mListeners.IsEmpty() || aEvent->PropagationStopped()) { return; } @@ -343,7 +343,7 @@ public: // Check if we already know that there is no event listener for the event. if (mNoListenerForEvent == aEvent->mMessage && (mNoListenerForEvent != eUnidentifiedEvent || - mNoListenerForEventAtom == aEvent->userType)) { + mNoListenerForEventAtom == aEvent->mSpecifiedEventType)) { return; } HandleEventInternal(aPresContext, aEvent, aDOMEvent, aCurrentTarget, diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index bc44bfbb25..0c34aa7f36 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -51,6 +51,7 @@ #include "nsFrameManager.h" #include "nsITabChild.h" #include "nsPluginFrame.h" +#include "nsMenuPopupFrame.h" #include "nsIDOMXULElement.h" #include "nsIDOMKeyEvent.h" @@ -285,6 +286,7 @@ TimeStamp EventStateManager::sHandlingInputStart; EventStateManager::WheelPrefs* EventStateManager::WheelPrefs::sInstance = nullptr; +bool EventStateManager::WheelPrefs::sWheelEventsEnabledOnPlugins = true; EventStateManager::DeltaAccumulator* EventStateManager::DeltaAccumulator::sInstance = nullptr; @@ -590,9 +592,10 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext, aEvent->mClass == eWheelEventClass) && !sIsPointerLocked) { sLastScreenPoint = - Event::GetScreenCoords(aPresContext, aEvent, aEvent->refPoint); + Event::GetScreenCoords(aPresContext, aEvent, aEvent->mRefPoint); sLastClientPoint = - Event::GetClientCoords(aPresContext, aEvent, aEvent->refPoint, CSSIntPoint(0, 0)); + Event::GetClientCoords(aPresContext, aEvent, aEvent->mRefPoint, + CSSIntPoint(0, 0)); } *aStatus = nsEventStatus_eIgnore; @@ -830,7 +833,7 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext, // composition event. WidgetCompositionEvent* compositionEvent = aEvent->AsCompositionEvent(); WidgetQueryContentEvent selectedText(true, eQuerySelectedText, - compositionEvent->widget); + compositionEvent->mWidget); HandleQueryContentEvent(&selectedText); NS_ASSERTION(selectedText.mSucceeded, "Failed to get selected text"); compositionEvent->mData = selectedText.mReply.mString; @@ -1305,7 +1308,8 @@ EventStateManager::HandleCrossProcessEvent(WidgetEvent* aEvent, if (aEvent->mClass != eTouchEventClass || aEvent->mMessage == eTouchStart) { // If this event only has one target, and it's remote, add it to // the array. - nsIFrame* frame = GetEventTarget(); + nsIFrame* frame = + aEvent->mMessage == eDragExit ? sLastDragOverFrame.GetFrame() : GetEventTarget(); nsIContent* target = frame ? frame->GetContent() : nullptr; if (IsRemoteTarget(target)) { targets.AppendElement(target); @@ -1319,7 +1323,7 @@ EventStateManager::HandleCrossProcessEvent(WidgetEvent* aEvent, // This loop is similar to the one used in // PresShell::DispatchTouchEvent(). const WidgetTouchEvent::TouchArray& touches = - aEvent->AsTouchEvent()->touches; + aEvent->AsTouchEvent()->mTouches; for (uint32_t i = 0; i < touches.Length(); ++i) { Touch* touch = touches[i]; // NB: the |mChanged| check is an optimization, subprocesses can @@ -1549,7 +1553,7 @@ EventStateManager::FireContextClick() } } - nsIDocument* doc = mGestureDownContent->GetCrossShadowCurrentDoc(); + nsIDocument* doc = mGestureDownContent->GetComposedDoc(); AutoHandlingUserInputStatePusher userInpStatePusher(true, &event, doc); // dispatch to DOM @@ -1589,12 +1593,14 @@ EventStateManager::BeginTrackingDragGesture(nsPresContext* aPresContext, WidgetMouseEvent* inDownEvent, nsIFrame* inDownFrame) { - if (!inDownEvent->widget) + if (!inDownEvent->mWidget) { return; + } // Note that |inDownEvent| could be either a mouse down event or a // synthesized mouse move event. - mGestureDownPoint = inDownEvent->refPoint + inDownEvent->widget->WidgetToScreenOffset(); + mGestureDownPoint = + inDownEvent->mRefPoint + inDownEvent->mWidget->WidgetToScreenOffset(); if (inDownFrame) { inDownFrame->GetContentForEvent(inDownEvent, @@ -1605,7 +1611,7 @@ EventStateManager::BeginTrackingDragGesture(nsPresContext* aPresContext, mGestureDownFrameOwner = mGestureDownContent; } } - mGestureModifiers = inDownEvent->modifiers; + mGestureModifiers = inDownEvent->mModifiers; mGestureDownButtons = inDownEvent->buttons; if (Prefs::ClickHoldContextMenu()) { @@ -1637,14 +1643,15 @@ EventStateManager::StopTrackingDragGesture() void EventStateManager::FillInEventFromGestureDown(WidgetMouseEvent* aEvent) { - NS_ASSERTION(aEvent->widget == mCurrentTarget->GetNearestWidget(), + NS_ASSERTION(aEvent->mWidget == mCurrentTarget->GetNearestWidget(), "Incorrect widget in event"); // Set the coordinates in the new event to the coordinates of // the old event, adjusted for the fact that the widget might be // different - aEvent->refPoint = mGestureDownPoint - aEvent->widget->WidgetToScreenOffset(); - aEvent->modifiers = mGestureModifiers; + aEvent->mRefPoint = + mGestureDownPoint - aEvent->mWidget->WidgetToScreenOffset(); + aEvent->mModifiers = mGestureModifiers; aEvent->buttons = mGestureDownButtons; } @@ -1699,7 +1706,8 @@ EventStateManager::GenerateDragGesture(nsPresContext* aPresContext, } // fire drag gesture if mouse has moved enough - LayoutDeviceIntPoint pt = aEvent->refPoint + aEvent->widget->WidgetToScreenOffset(); + LayoutDeviceIntPoint pt = + aEvent->mRefPoint + aEvent->mWidget->WidgetToScreenOffset(); LayoutDeviceIntPoint distance = pt - mGestureDownPoint; if (Abs(distance.x) > AssertedCast(pixelThresholdX) || Abs(distance.y) > AssertedCast(pixelThresholdY)) { @@ -1747,7 +1755,7 @@ EventStateManager::GenerateDragGesture(nsPresContext* aPresContext, eLegacyDragGesture, widget); FillInEventFromGestureDown(&gestureEvent); - startEvent.dataTransfer = gestureEvent.dataTransfer = dataTransfer; + startEvent.mDataTransfer = gestureEvent.mDataTransfer = dataTransfer; startEvent.inputSource = gestureEvent.inputSource = aEvent->inputSource; // Dispatch to the DOM. By setting mCurrentTarget we are faking @@ -2190,32 +2198,32 @@ EventStateManager::DispatchLegacyMouseScrollEvents(nsIFrame* aTargetFrame, // It doesn't make sense to implement such code for legacy events and // rare cases. int32_t scrollDeltaX, scrollDeltaY, pixelDeltaX, pixelDeltaY; - switch (aEvent->deltaMode) { + switch (aEvent->mDeltaMode) { case nsIDOMWheelEvent::DOM_DELTA_PAGE: scrollDeltaX = - !aEvent->lineOrPageDeltaX ? 0 : - (aEvent->lineOrPageDeltaX > 0 ? nsIDOMUIEvent::SCROLL_PAGE_DOWN : - nsIDOMUIEvent::SCROLL_PAGE_UP); + !aEvent->mLineOrPageDeltaX ? 0 : + (aEvent->mLineOrPageDeltaX > 0 ? nsIDOMUIEvent::SCROLL_PAGE_DOWN : + nsIDOMUIEvent::SCROLL_PAGE_UP); scrollDeltaY = - !aEvent->lineOrPageDeltaY ? 0 : - (aEvent->lineOrPageDeltaY > 0 ? nsIDOMUIEvent::SCROLL_PAGE_DOWN : - nsIDOMUIEvent::SCROLL_PAGE_UP); - pixelDeltaX = RoundDown(aEvent->deltaX * scrollAmountInCSSPixels.width); - pixelDeltaY = RoundDown(aEvent->deltaY * scrollAmountInCSSPixels.height); + !aEvent->mLineOrPageDeltaY ? 0 : + (aEvent->mLineOrPageDeltaY > 0 ? nsIDOMUIEvent::SCROLL_PAGE_DOWN : + nsIDOMUIEvent::SCROLL_PAGE_UP); + pixelDeltaX = RoundDown(aEvent->mDeltaX * scrollAmountInCSSPixels.width); + pixelDeltaY = RoundDown(aEvent->mDeltaY * scrollAmountInCSSPixels.height); break; case nsIDOMWheelEvent::DOM_DELTA_LINE: - scrollDeltaX = aEvent->lineOrPageDeltaX; - scrollDeltaY = aEvent->lineOrPageDeltaY; - pixelDeltaX = RoundDown(aEvent->deltaX * scrollAmountInCSSPixels.width); - pixelDeltaY = RoundDown(aEvent->deltaY * scrollAmountInCSSPixels.height); + scrollDeltaX = aEvent->mLineOrPageDeltaX; + scrollDeltaY = aEvent->mLineOrPageDeltaY; + pixelDeltaX = RoundDown(aEvent->mDeltaX * scrollAmountInCSSPixels.width); + pixelDeltaY = RoundDown(aEvent->mDeltaY * scrollAmountInCSSPixels.height); break; case nsIDOMWheelEvent::DOM_DELTA_PIXEL: - scrollDeltaX = aEvent->lineOrPageDeltaX; - scrollDeltaY = aEvent->lineOrPageDeltaY; - pixelDeltaX = RoundDown(aEvent->deltaX); - pixelDeltaY = RoundDown(aEvent->deltaY); + scrollDeltaX = aEvent->mLineOrPageDeltaX; + scrollDeltaY = aEvent->mLineOrPageDeltaY; + pixelDeltaX = RoundDown(aEvent->mDeltaX); + pixelDeltaY = RoundDown(aEvent->mDeltaY); break; default: @@ -2300,17 +2308,16 @@ EventStateManager::SendLineScrollEvent(nsIFrame* aTargetFrame, } WidgetMouseScrollEvent event(aEvent->IsTrusted(), - eLegacyMouseLineOrPageScroll, aEvent->widget); + eLegacyMouseLineOrPageScroll, aEvent->mWidget); event.mFlags.mDefaultPrevented = aState.mDefaultPrevented; event.mFlags.mDefaultPreventedByContent = aState.mDefaultPreventedByContent; - event.refPoint = aEvent->refPoint; - event.widget = aEvent->widget; - event.time = aEvent->time; - event.timeStamp = aEvent->timeStamp; - event.modifiers = aEvent->modifiers; + event.mRefPoint = aEvent->mRefPoint; + event.mTime = aEvent->mTime; + event.mTimeStamp = aEvent->mTimeStamp; + event.mModifiers = aEvent->mModifiers; event.buttons = aEvent->buttons; - event.isHorizontal = (aDeltaDirection == DELTA_DIRECTION_X); - event.delta = aDelta; + event.mIsHorizontal = (aDeltaDirection == DELTA_DIRECTION_X); + event.mDelta = aDelta; event.inputSource = aEvent->inputSource; nsEventStatus status = nsEventStatus_eIgnore; @@ -2340,17 +2347,16 @@ EventStateManager::SendPixelScrollEvent(nsIFrame* aTargetFrame, } WidgetMouseScrollEvent event(aEvent->IsTrusted(), - eLegacyMousePixelScroll, aEvent->widget); + eLegacyMousePixelScroll, aEvent->mWidget); event.mFlags.mDefaultPrevented = aState.mDefaultPrevented; event.mFlags.mDefaultPreventedByContent = aState.mDefaultPreventedByContent; - event.refPoint = aEvent->refPoint; - event.widget = aEvent->widget; - event.time = aEvent->time; - event.timeStamp = aEvent->timeStamp; - event.modifiers = aEvent->modifiers; + event.mRefPoint = aEvent->mRefPoint; + event.mTime = aEvent->mTime; + event.mTimeStamp = aEvent->mTimeStamp; + event.mModifiers = aEvent->mModifiers; event.buttons = aEvent->buttons; - event.isHorizontal = (aDeltaDirection == DELTA_DIRECTION_X); - event.delta = aPixelDelta; + event.mIsHorizontal = (aDeltaDirection == DELTA_DIRECTION_X); + event.mDelta = aPixelDelta; event.inputSource = aEvent->inputSource; nsEventStatus status = nsEventStatus_eIgnore; @@ -2366,7 +2372,7 @@ EventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame, WidgetWheelEvent* aEvent, ComputeScrollTargetOptions aOptions) { - return ComputeScrollTarget(aTargetFrame, aEvent->deltaX, aEvent->deltaY, + return ComputeScrollTarget(aTargetFrame, aEvent->mDeltaX, aEvent->mDeltaY, aEvent, aOptions); } @@ -2380,6 +2386,11 @@ EventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame, WidgetWheelEvent* aEvent, ComputeScrollTargetOptions aOptions) { + if ((aOptions & INCLUDE_PLUGIN_AS_TARGET) && + !WheelPrefs::WheelEventsEnabledOnPlugins()) { + aOptions = RemovePluginFromTarget(aOptions); + } + if (aOptions & PREFER_MOUSE_WHEEL_TRANSACTION) { // If the user recently scrolled with the mousewheel, then they probably // want to scroll the same view as before instead of the view under the @@ -2440,6 +2451,10 @@ EventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame, return scrollFrame; } } + nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(scrollFrame); + if (menuPopupFrame) { + return nullptr; + } continue; } @@ -2501,7 +2516,7 @@ EventStateManager::GetScrollAmount(nsPresContext* aPresContext, MOZ_ASSERT(aPresContext); MOZ_ASSERT(aEvent); - bool isPage = (aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_PAGE); + bool isPage = (aEvent->mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PAGE); if (aScrollableFrame) { return isPage ? aScrollableFrame->GetPageScrollAmount() : aScrollableFrame->GetLineScrollAmount(); @@ -2560,7 +2575,7 @@ EventStateManager::DoScrollText(nsIScrollableFrame* aScrollableFrame, nsIScrollbarMediator::ScrollSnapMode snapMode = nsIScrollbarMediator::DISABLE_SNAP; nsIAtom* origin = nullptr; - switch (aEvent->deltaMode) { + switch (aEvent->mDeltaMode) { case nsIDOMWheelEvent::DOM_DELTA_LINE: origin = nsGkAtoms::mouseWheel; snapMode = nsIScrollableFrame::ENABLE_SNAP; @@ -2596,10 +2611,10 @@ EventStateManager::DoScrollText(nsIScrollableFrame* aScrollableFrame, } bool isDeltaModePixel = - (aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL); + (aEvent->mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL); nsIScrollableFrame::ScrollMode mode; - switch (aEvent->scrollType) { + switch (aEvent->mScrollType) { case WidgetWheelEvent::SCROLL_DEFAULT: if (isDeltaModePixel) { mode = nsIScrollableFrame::NORMAL; @@ -2619,12 +2634,12 @@ EventStateManager::DoScrollText(nsIScrollableFrame* aScrollableFrame, mode = nsIScrollableFrame::SMOOTH; break; default: - MOZ_CRASH("Invalid scrollType value comes"); + MOZ_CRASH("Invalid mScrollType value comes"); } nsIScrollableFrame::ScrollMomentum momentum = - aEvent->isMomentum ? nsIScrollableFrame::SYNTHESIZED_MOMENTUM_EVENT - : nsIScrollableFrame::NOT_MOMENTUM; + aEvent->mIsMomentum ? nsIScrollableFrame::SYNTHESIZED_MOMENTUM_EVENT + : nsIScrollableFrame::NOT_MOMENTUM; nsIntPoint overflow; aScrollableFrame->ScrollBy(actualDevPixelScrollAmount, @@ -2635,14 +2650,14 @@ EventStateManager::DoScrollText(nsIScrollableFrame* aScrollableFrame, // If the scroll causes changing the layout, we can think that the event // has been completely consumed by the content. Then, users probably don't // want additional action. - aEvent->overflowDeltaX = aEvent->overflowDeltaY = 0; + aEvent->mOverflowDeltaX = aEvent->mOverflowDeltaY = 0; } else if (isDeltaModePixel) { - aEvent->overflowDeltaX = overflow.x; - aEvent->overflowDeltaY = overflow.y; + aEvent->mOverflowDeltaX = overflow.x; + aEvent->mOverflowDeltaY = overflow.y; } else { - aEvent->overflowDeltaX = + aEvent->mOverflowDeltaX = static_cast(overflow.x) / scrollAmountInDevPixels.width; - aEvent->overflowDeltaY = + aEvent->mOverflowDeltaY = static_cast(overflow.y) / scrollAmountInDevPixels.height; } @@ -2653,26 +2668,26 @@ EventStateManager::DoScrollText(nsIScrollableFrame* aScrollableFrame, // additional action such as moving history. In such case, overflowDelta // values should stay zero. if (scrollFrameWeak.IsAlive()) { - if (aEvent->deltaX && + if (aEvent->mDeltaX && overflowStyle.mHorizontal == NS_STYLE_OVERFLOW_HIDDEN && !ComputeScrollTarget(scrollFrame, aEvent, COMPUTE_SCROLLABLE_ANCESTOR_ALONG_X_AXIS)) { - aEvent->overflowDeltaX = aEvent->deltaX; + aEvent->mOverflowDeltaX = aEvent->mDeltaX; } - if (aEvent->deltaY && + if (aEvent->mDeltaY && overflowStyle.mVertical == NS_STYLE_OVERFLOW_HIDDEN && !ComputeScrollTarget(scrollFrame, aEvent, COMPUTE_SCROLLABLE_ANCESTOR_ALONG_Y_AXIS)) { - aEvent->overflowDeltaY = aEvent->deltaY; + aEvent->mOverflowDeltaY = aEvent->mDeltaY; } } - NS_ASSERTION(aEvent->overflowDeltaX == 0 || - (aEvent->overflowDeltaX > 0) == (aEvent->deltaX > 0), - "The sign of overflowDeltaX is different from the scroll direction"); - NS_ASSERTION(aEvent->overflowDeltaY == 0 || - (aEvent->overflowDeltaY > 0) == (aEvent->deltaY > 0), - "The sign of overflowDeltaY is different from the scroll direction"); + NS_ASSERTION(aEvent->mOverflowDeltaX == 0 || + (aEvent->mOverflowDeltaX > 0) == (aEvent->mDeltaX > 0), + "The sign of mOverflowDeltaX is different from the scroll direction"); + NS_ASSERTION(aEvent->mOverflowDeltaY == 0 || + (aEvent->mOverflowDeltaY > 0) == (aEvent->mDeltaY > 0), + "The sign of mOverflowDeltaY is different from the scroll direction"); WheelPrefs::GetInstance()->CancelApplyingUserPrefsFromOverflowDelta(aEvent); } @@ -2811,7 +2826,8 @@ EventStateManager::PostHandleKeyboardEvent(WidgetKeyboardEvent* aKeyboardEvent, nsEventStatus& aStatus, bool dispatchedToContentProcess) { - if (aStatus == nsEventStatus_eConsumeNoDefault) { + if (aStatus == nsEventStatus_eConsumeNoDefault || + aKeyboardEvent->mInputMethodAppState == WidgetKeyboardEvent::eHandling) { return; } @@ -2935,19 +2951,19 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext, // we click on a visibility: none element. // We can't use nsIContent::IsFocusable() because we want to blur when // we click on a non-focusable element like a
. - // We have to use |aEvent->target| to not make sure we do not check an - // anonymous node of the targeted element. + // We have to use |aEvent->mTarget| to not make sure we do not check + // an anonymous node of the targeted element. suppressBlur = (ui->mUserFocus == NS_STYLE_USER_FOCUS_IGNORE); if (!suppressBlur) { - nsCOMPtr element = do_QueryInterface(aEvent->target); + nsCOMPtr element = do_QueryInterface(aEvent->mTarget); suppressBlur = element && element->State().HasState(NS_EVENT_STATE_DISABLED); } if (!suppressBlur) { nsCOMPtr xulControl = - do_QueryInterface(aEvent->target); + do_QueryInterface(aEvent->mTarget); if (xulControl) { bool disabled; xulControl->GetDisabled(&disabled); @@ -2971,7 +2987,7 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext, // NOTE: The newFocus isn't editable that also means it's not in // designMode. In designMode, all contents are not focusable. if (newFocus && !newFocus->IsEditable()) { - nsIDocument *doc = newFocus->GetCrossShadowCurrentDoc(); + nsIDocument *doc = newFocus->GetComposedDoc(); if (doc && newFocus == doc->GetRootElement()) { nsIContent *bodyContent = nsLayoutUtils::GetEditableRootContentByContentEditable(doc); @@ -3172,19 +3188,6 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext, if (pluginFrame) { MOZ_ASSERT(pluginFrame->WantsToHandleWheelEventAsDefaultAction()); action = WheelPrefs::ACTION_SEND_TO_PLUGIN; - } else if (!wheelEvent->mayHaveMomentum && - nsLayoutUtils::IsScrollFrameWithSnapping(frameToScroll)) { - // If the target has scroll-snapping points then we want to handle - // the wheel event on the main thread even if we have APZ enabled. Do - // so and let the APZ know that it should ignore this event. However, - // if the wheel event is synthesized from a Mac trackpad or other device - // that can generate additional momentum events, then we should allow - // APZ to handle it, because it will track the velocity and predicted - // destination from the momentum. - if (wheelEvent->mFlags.mHandledByAPZ) { - wheelEvent->PreventDefault(); - } - action = WheelPrefs::GetInstance()->ComputeActionFor(wheelEvent); } else if (wheelEvent->mFlags.mHandledByAPZ) { action = WheelPrefs::ACTION_NONE; } else { @@ -3198,7 +3201,7 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext, ScrollbarsForWheel::PrepareToScrollText(this, aTargetFrame, wheelEvent); if (aEvent->mMessage != eWheel || - (!wheelEvent->deltaX && !wheelEvent->deltaY)) { + (!wheelEvent->mDeltaX && !wheelEvent->mDeltaY)) { break; } @@ -3214,8 +3217,8 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext, if (!scrollTarget || scrollTarget == rootScrollableFrame) { wheelEvent->mViewPortIsOverscrolled = true; } - wheelEvent->overflowDeltaX = wheelEvent->deltaX; - wheelEvent->overflowDeltaY = wheelEvent->deltaY; + wheelEvent->mOverflowDeltaX = wheelEvent->mDeltaX; + wheelEvent->mOverflowDeltaY = wheelEvent->mDeltaY; WheelPrefs::GetInstance()-> CancelApplyingUserPrefsFromOverflowDelta(wheelEvent); if (scrollTarget) { @@ -3250,7 +3253,7 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext, MOZ_ASSERT(pluginFrame); if (wheelEvent->mMessage != eWheel || - (!wheelEvent->deltaX && !wheelEvent->deltaY)) { + (!wheelEvent->mDeltaX && !wheelEvent->mDeltaY)) { break; } @@ -3285,8 +3288,8 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext, if (!allDeltaOverflown) { break; } - wheelEvent->overflowDeltaX = wheelEvent->deltaX; - wheelEvent->overflowDeltaY = wheelEvent->deltaY; + wheelEvent->mOverflowDeltaX = wheelEvent->mDeltaX; + wheelEvent->mOverflowDeltaY = wheelEvent->mDeltaY; WheelPrefs::GetInstance()-> CancelApplyingUserPrefsFromOverflowDelta(wheelEvent); wheelEvent->mViewPortIsOverscrolled = true; @@ -3345,13 +3348,13 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext, uint32_t action = nsIDragService::DRAGDROP_ACTION_NONE; if (nsEventStatus_eConsumeNoDefault == *aStatus) { // if the event has a dataTransfer set, use it. - if (dragEvent->dataTransfer) { + if (dragEvent->mDataTransfer) { // get the dataTransfer and the dropEffect that was set on it - dataTransfer = do_QueryInterface(dragEvent->dataTransfer); + dataTransfer = do_QueryInterface(dragEvent->mDataTransfer); dataTransfer->GetDropEffectInt(&dropEffect); } else { - // if dragEvent->dataTransfer is null, it means that no attempt was + // if dragEvent->mDataTransfer is null, it means that no attempt was // made to access the dataTransfer during the event, yet the event // was cancelled. Instead, use the initial data transfer available // from the drag session. The drop effect would not have been @@ -3431,12 +3434,12 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext, WidgetDragEvent event(aEvent->IsTrusted(), eLegacyDragDrop, widget); WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); - event.refPoint = mouseEvent->refPoint; - if (mouseEvent->widget) { - event.refPoint += mouseEvent->widget->WidgetToScreenOffset(); + event.mRefPoint = mouseEvent->mRefPoint; + if (mouseEvent->mWidget) { + event.mRefPoint += mouseEvent->mWidget->WidgetToScreenOffset(); } - event.refPoint -= widget->WidgetToScreenOffset(); - event.modifiers = mouseEvent->modifiers; + event.mRefPoint -= widget->WidgetToScreenOffset(); + event.mModifiers = mouseEvent->mModifiers; event.buttons = mouseEvent->buttons; event.inputSource = mouseEvent->inputSource; @@ -3855,7 +3858,7 @@ CreateMouseOrPointerWidgetEvent(WidgetMouseEvent* aMouseEvent, nsAutoPtr newPointerEvent; newPointerEvent = new WidgetPointerEvent(aMouseEvent->IsTrusted(), aMessage, - aMouseEvent->widget); + aMouseEvent->mWidget); newPointerEvent->isPrimary = sourcePointer->isPrimary; newPointerEvent->pointerId = sourcePointer->pointerId; newPointerEvent->width = sourcePointer->width; @@ -3869,11 +3872,11 @@ CreateMouseOrPointerWidgetEvent(WidgetMouseEvent* aMouseEvent, } else { aNewEvent = new WidgetMouseEvent(aMouseEvent->IsTrusted(), aMessage, - aMouseEvent->widget, WidgetMouseEvent::eReal); + aMouseEvent->mWidget, WidgetMouseEvent::eReal); aNewEvent->relatedTarget = aRelatedContent; } - aNewEvent->refPoint = aMouseEvent->refPoint; - aNewEvent->modifiers = aMouseEvent->modifiers; + aNewEvent->mRefPoint = aMouseEvent->mRefPoint; + aNewEvent->mModifiers = aMouseEvent->mModifiers; aNewEvent->button = aMouseEvent->button; aNewEvent->buttons = aMouseEvent->buttons; aNewEvent->pressure = aMouseEvent->pressure; @@ -4173,47 +4176,24 @@ EventStateManager::NotifyMouseOver(WidgetMouseEvent* aMouseEvent, wrapper->mFirstOverEventElement = nullptr; } -// Returns the center point of the window's inner content area. -// This is in widget coordinates, i.e. relative to the widget's top -// left corner, not in screen coordinates, the same units that -// UIEvent::refPoint is in. -// -// XXX Hack alert: XXX -// However, we do the computation in integer CSS pixels, NOT device pix, -// in order to fudge around the one-pixel error in innerHeight in fullscreen -// mode (see bug 799523 comment 35, and bug 729011). Using integer CSS pix -// makes us throw away the fractional error that results, rather than having -// it manifest as a potential one-device-pix discrepancy. +// Returns the center point of the window's client area. This is +// in widget coordinates, i.e. relative to the widget's top-left +// corner, not in screen coordinates, the same units that UIEvent:: +// refpoint is in. It may not be the exact center of the window if +// the platform requires rounding the coordinate. static LayoutDeviceIntPoint -GetWindowInnerRectCenter(nsPIDOMWindow* aWindow, - nsIWidget* aWidget, - nsPresContext* aContext) +GetWindowClientRectCenter(nsIWidget* aWidget) { - NS_ENSURE_TRUE(aWindow && aWidget && aContext, LayoutDeviceIntPoint(0, 0)); + NS_ENSURE_TRUE(aWidget, LayoutDeviceIntPoint(0, 0)); - float cssInnerX = 0.0; - aWindow->GetMozInnerScreenX(&cssInnerX); - int32_t innerX = int32_t(NS_round(cssInnerX)); - - float cssInnerY = 0.0; - aWindow->GetMozInnerScreenY(&cssInnerY); - int32_t innerY = int32_t(NS_round(cssInnerY)); - - int32_t innerWidth = 0; - aWindow->GetInnerWidth(&innerWidth); - - int32_t innerHeight = 0; - aWindow->GetInnerHeight(&innerHeight); - - LayoutDeviceIntRect screen; - aWidget->GetScreenBounds(screen); - - int32_t cssScreenX = aContext->DevPixelsToIntCSSPixels(screen.x); - int32_t cssScreenY = aContext->DevPixelsToIntCSSPixels(screen.y); - - return LayoutDeviceIntPoint( - aContext->CSSPixelsToDevPixels(innerX - cssScreenX + innerWidth / 2), - aContext->CSSPixelsToDevPixels(innerY - cssScreenY + innerHeight / 2)); + LayoutDeviceIntRect rect; + aWidget->GetClientBounds(rect); + LayoutDeviceIntPoint point(rect.x + rect.width / 2, + rect.y + rect.height / 2); + int32_t round = aWidget->RoundsWidgetCoordinatesTo(); + point.x = point.x / round * round; + point.y = point.y / round * round; + return point - aWidget->WidgetToScreenOffset(); } void @@ -4240,8 +4220,8 @@ EventStateManager::GenerateMouseEnterExit(WidgetMouseEvent* aMouseEvent) { // Mouse movement is reported on the MouseEvent.movement{X,Y} fields. // Movement is calculated in UIEvent::GetMovementPoint() as: - // previous_mousemove_refPoint - current_mousemove_refPoint. - if (sIsPointerLocked && aMouseEvent->widget) { + // previous_mousemove_mRefPoint - current_mousemove_mRefPoint. + if (sIsPointerLocked && aMouseEvent->mWidget) { // The pointer is locked. If the pointer is not located at the center of // the window, dispatch a synthetic mousemove to return the pointer there. // Doing this between "real" pointer moves gives the impression that the @@ -4249,19 +4229,18 @@ EventStateManager::GenerateMouseEnterExit(WidgetMouseEvent* aMouseEvent) // boundary. We cancel the synthetic event so that we don't end up // dispatching the centering move event to content. LayoutDeviceIntPoint center = - GetWindowInnerRectCenter(mDocument->GetWindow(), aMouseEvent->widget, - mPresContext); - aMouseEvent->lastRefPoint = center; - if (aMouseEvent->refPoint != center) { + GetWindowClientRectCenter(aMouseEvent->mWidget); + aMouseEvent->mLastRefPoint = center; + if (aMouseEvent->mRefPoint != center) { // Mouse move doesn't finish at the center of the window. Dispatch a // synthetic native mouse event to move the pointer back to the center // of the window, to faciliate more movement. But first, record that // we've dispatched a synthetic mouse movement, so we can cancel it // in the other branch here. sSynthCenteringPoint = center; - aMouseEvent->widget->SynthesizeNativeMouseMove( - center + aMouseEvent->widget->WidgetToScreenOffset(), nullptr); - } else if (aMouseEvent->refPoint == sSynthCenteringPoint) { + aMouseEvent->mWidget->SynthesizeNativeMouseMove( + center + aMouseEvent->mWidget->WidgetToScreenOffset(), nullptr); + } else if (aMouseEvent->mRefPoint == sSynthCenteringPoint) { // This is the "synthetic native" event we dispatched to re-center the // pointer. Cancel it so we don't expose the centering move to content. aMouseEvent->StopPropagation(); @@ -4270,17 +4249,17 @@ EventStateManager::GenerateMouseEnterExit(WidgetMouseEvent* aMouseEvent) sSynthCenteringPoint = kInvalidRefPoint; } } else if (sLastRefPoint == kInvalidRefPoint) { - // We don't have a valid previous mousemove refPoint. This is either + // We don't have a valid previous mousemove mRefPoint. This is either // the first move we've encountered, or the mouse has just re-entered // the application window. We should report (0,0) movement for this - // case, so make the current and previous refPoints the same. - aMouseEvent->lastRefPoint = aMouseEvent->refPoint; + // case, so make the current and previous mRefPoints the same. + aMouseEvent->mLastRefPoint = aMouseEvent->mRefPoint; } else { - aMouseEvent->lastRefPoint = sLastRefPoint; + aMouseEvent->mLastRefPoint = sLastRefPoint; } - // Update the last known refPoint with the current refPoint. - sLastRefPoint = aMouseEvent->refPoint; + // Update the last known mRefPoint with the current mRefPoint. + sLastRefPoint = aMouseEvent->mRefPoint; } MOZ_FALLTHROUGH; case ePointerMove: @@ -4327,7 +4306,7 @@ EventStateManager::GenerateMouseEnterExit(WidgetMouseEvent* aMouseEvent) OverOutElementsWrapper* helper = GetWrapperByEventID(aMouseEvent); if (helper->mLastOverFrame && - nsContentUtils::GetTopLevelWidget(aMouseEvent->widget) != + nsContentUtils::GetTopLevelWidget(aMouseEvent->mWidget) != nsContentUtils::GetTopLevelWidget(helper->mLastOverFrame->GetNearestWidget())) { // the Mouse/PointerOut event widget doesn't have same top widget with // mLastOverFrame, it's a spurious event for mLastOverFrame @@ -4376,10 +4355,6 @@ EventStateManager::SetPointerLock(nsIWidget* aWidget, // NOTE: aElement will be nullptr when unlocking. sIsPointerLocked = !!aElement; - if (!aWidget) { - return; - } - // Reset mouse wheel transaction WheelTransaction::EndTransaction(); @@ -4388,17 +4363,17 @@ EventStateManager::SetPointerLock(nsIWidget* aWidget, do_GetService("@mozilla.org/widget/dragservice;1"); if (sIsPointerLocked) { + MOZ_ASSERT(aWidget, "Locking pointer requires a widget"); + // Store the last known ref point so we can reposition the pointer after unlock. mPreLockPoint = sLastRefPoint; // Fire a synthetic mouse move to ensure event state is updated. We first // set the mouse to the center of the window, so that the mouse event // doesn't report any movement. - sLastRefPoint = GetWindowInnerRectCenter(aElement->OwnerDoc()->GetWindow(), - aWidget, - mPresContext); - aWidget->SynthesizeNativeMouseMove(sLastRefPoint + aWidget->WidgetToScreenOffset(), - nullptr); + sLastRefPoint = GetWindowClientRectCenter(aWidget); + aWidget->SynthesizeNativeMouseMove( + sLastRefPoint + aWidget->WidgetToScreenOffset(), nullptr); // Retarget all events to this element via capture. nsIPresShell::SetCapturingContent(aElement, CAPTURE_POINTERLOCK); @@ -4413,8 +4388,10 @@ EventStateManager::SetPointerLock(nsIWidget* aWidget, // pre-pointerlock position, so that the synthetic mouse event reports // no movement. sLastRefPoint = mPreLockPoint; - aWidget->SynthesizeNativeMouseMove(mPreLockPoint + aWidget->WidgetToScreenOffset(), - nullptr); + if (aWidget) { + aWidget->SynthesizeNativeMouseMove( + mPreLockPoint + aWidget->WidgetToScreenOffset(), nullptr); + } // Don't retarget events to this element any more. nsIPresShell::SetCapturingContent(nullptr, CAPTURE_POINTERLOCK); @@ -4510,9 +4487,9 @@ EventStateManager::FireDragEnterOrExit(nsPresContext* aPresContext, nsWeakFrame& aTargetFrame) { nsEventStatus status = nsEventStatus_eIgnore; - WidgetDragEvent event(aDragEvent->IsTrusted(), aMessage, aDragEvent->widget); - event.refPoint = aDragEvent->refPoint; - event.modifiers = aDragEvent->modifiers; + WidgetDragEvent event(aDragEvent->IsTrusted(), aMessage, aDragEvent->mWidget); + event.mRefPoint = aDragEvent->mRefPoint; + event.mModifiers = aDragEvent->mModifiers; event.buttons = aDragEvent->buttons; event.relatedTarget = aRelatedTarget; event.inputSource = aDragEvent->inputSource; @@ -4543,14 +4520,23 @@ EventStateManager::FireDragEnterOrExit(nsPresContext* aPresContext, // Finally dispatch the event to the frame if (aTargetFrame) aTargetFrame->HandleEvent(aPresContext, &event, &status); + + if (aMessage == eDragExit && IsRemoteTarget(aTargetContent)) { + nsEventStatus status = nsEventStatus_eIgnore; + WidgetDragEvent remoteEvent(aDragEvent->IsTrusted(), aMessage, + aDragEvent->mWidget); + remoteEvent.AssignDragEventData(*aDragEvent, true); + HandleCrossProcessEvent(&remoteEvent, &status); + } } void EventStateManager::UpdateDragDataTransfer(WidgetDragEvent* dragEvent) { NS_ASSERTION(dragEvent, "drag event is null in UpdateDragDataTransfer!"); - if (!dragEvent->dataTransfer) + if (!dragEvent->mDataTransfer) { return; + } nsCOMPtr dragSession = nsContentUtils::GetDragSession(); @@ -4562,7 +4548,7 @@ EventStateManager::UpdateDragDataTransfer(WidgetDragEvent* dragEvent) if (initialDataTransfer) { // retrieve the current moz cursor setting and save it. nsAutoString mozCursor; - dragEvent->dataTransfer->GetMozCursor(mozCursor); + dragEvent->mDataTransfer->GetMozCursor(mozCursor); initialDataTransfer->SetMozCursor(mozCursor); } } @@ -4655,14 +4641,14 @@ EventStateManager::InitAndDispatchClickEvent(WidgetMouseEvent* aEvent, bool aNoContentDispatch) { WidgetMouseEvent event(aEvent->IsTrusted(), aMessage, - aEvent->widget, WidgetMouseEvent::eReal); + aEvent->mWidget, WidgetMouseEvent::eReal); - event.refPoint = aEvent->refPoint; + event.mRefPoint = aEvent->mRefPoint; event.clickCount = aEvent->clickCount; - event.modifiers = aEvent->modifiers; + event.mModifiers = aEvent->mModifiers; event.buttons = aEvent->buttons; - event.time = aEvent->time; - event.timeStamp = aEvent->timeStamp; + event.mTime = aEvent->mTime; + event.mTimeStamp = aEvent->mTimeStamp; event.mFlags.mNoContentDispatch = aNoContentDispatch; event.button = aEvent->button; event.inputSource = aEvent->inputSource; @@ -4682,7 +4668,7 @@ EventStateManager::CheckForAndDispatchClick(WidgetMouseEvent* aEvent, if (0 != aEvent->clickCount) { //Check that the window isn't disabled before firing a click //(see bug 366544). - if (aEvent->widget && !aEvent->widget->IsEnabled()) { + if (aEvent->mWidget && !aEvent->mWidget->IsEnabled()) { return ret; } //fire click @@ -4973,7 +4959,7 @@ EventStateManager::SetContentState(nsIContent* aContent, EventStates aState) newHover = aContent; } else { NS_ASSERTION(!aContent || - aContent->GetCrossShadowCurrentDoc() == + aContent->GetComposedDoc() == mPresContext->PresShell()->GetDocument(), "Unexpected document"); nsIFrame *frame = aContent ? aContent->GetPrimaryFrame() : nullptr; @@ -5383,22 +5369,22 @@ EventStateManager::DeltaAccumulator::InitLineOrPageDelta( // If we have accumulated delta, we may need to reset it. if (IsInTransaction()) { // If wheel event type is changed, reset the values. - if (mHandlingDeltaMode != aEvent->deltaMode || + if (mHandlingDeltaMode != aEvent->mDeltaMode || mIsNoLineOrPageDeltaDevice != aEvent->mIsNoLineOrPageDelta) { Reset(); } else { // If the delta direction is changed, we should reset only the // accumulated values. - if (mX && aEvent->deltaX && ((aEvent->deltaX > 0.0) != (mX > 0.0))) { + if (mX && aEvent->mDeltaX && ((aEvent->mDeltaX > 0.0) != (mX > 0.0))) { mX = mPendingScrollAmountX = 0.0; } - if (mY && aEvent->deltaY && ((aEvent->deltaY > 0.0) != (mY > 0.0))) { + if (mY && aEvent->mDeltaY && ((aEvent->mDeltaY > 0.0) != (mY > 0.0))) { mY = mPendingScrollAmountY = 0.0; } } } - mHandlingDeltaMode = aEvent->deltaMode; + mHandlingDeltaMode = aEvent->mDeltaMode; mIsNoLineOrPageDeltaDevice = aEvent->mIsNoLineOrPageDelta; // If it's handling neither a device that does not provide line or page deltas @@ -5412,22 +5398,22 @@ EventStateManager::DeltaAccumulator::InitLineOrPageDelta( // is changed. // NOTE: We shouldn't accumulate the delta values, it might could cause // overflow even though it's not a realistic situation. - if (aEvent->deltaX) { - mX = aEvent->deltaX; + if (aEvent->mDeltaX) { + mX = aEvent->mDeltaX; } - if (aEvent->deltaY) { - mY = aEvent->deltaY; + if (aEvent->mDeltaY) { + mY = aEvent->mDeltaY; } mLastTime = TimeStamp::Now(); return; } - mX += aEvent->deltaX; - mY += aEvent->deltaY; + mX += aEvent->mDeltaX; + mY += aEvent->mDeltaY; if (mHandlingDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL) { - // Records pixel delta values and init lineOrPageDeltaX and - // lineOrPageDeltaY for wheel events which are caused by pixel only + // Records pixel delta values and init mLineOrPageDeltaX and + // mLineOrPageDeltaY for wheel events which are caused by pixel only // devices. Ignore mouse wheel transaction for computing this. The // lineOrPageDelta values will be used by dispatching legacy // eMouseScrollEventClass (DOMMouseScroll) but not be used for scrolling @@ -5444,16 +5430,16 @@ EventStateManager::DeltaAccumulator::InitLineOrPageDelta( nsPresContext::AppUnitsToIntCSSPixels(scrollAmount.width), nsPresContext::AppUnitsToIntCSSPixels(scrollAmount.height)); - aEvent->lineOrPageDeltaX = RoundDown(mX) / scrollAmountInCSSPixels.width; - aEvent->lineOrPageDeltaY = RoundDown(mY) / scrollAmountInCSSPixels.height; + aEvent->mLineOrPageDeltaX = RoundDown(mX) / scrollAmountInCSSPixels.width; + aEvent->mLineOrPageDeltaY = RoundDown(mY) / scrollAmountInCSSPixels.height; - mX -= aEvent->lineOrPageDeltaX * scrollAmountInCSSPixels.width; - mY -= aEvent->lineOrPageDeltaY * scrollAmountInCSSPixels.height; + mX -= aEvent->mLineOrPageDeltaX * scrollAmountInCSSPixels.width; + mY -= aEvent->mLineOrPageDeltaY * scrollAmountInCSSPixels.height; } else { - aEvent->lineOrPageDeltaX = RoundDown(mX); - aEvent->lineOrPageDeltaY = RoundDown(mY); - mX -= aEvent->lineOrPageDeltaX; - mY -= aEvent->lineOrPageDeltaY; + aEvent->mLineOrPageDeltaX = RoundDown(mX); + aEvent->mLineOrPageDeltaY = RoundDown(mY); + mX -= aEvent->mLineOrPageDeltaX; + mY -= aEvent->mLineOrPageDeltaY; } mLastTime = TimeStamp::Now(); @@ -5478,13 +5464,13 @@ EventStateManager::DeltaAccumulator::ComputeScrollAmountForDefaultAction( // If the wheel event is line scroll and the delta value is computed from // system settings, allow to override the system speed. bool allowScrollSpeedOverride = - (!aEvent->customizedByUserPrefs && - aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE); + (!aEvent->mCustomizedByUserPrefs && + aEvent->mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE); DeltaValues acceleratedDelta = WheelTransaction::AccelerateWheelDelta(aEvent, allowScrollSpeedOverride); nsIntPoint result(0, 0); - if (aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL) { + if (aEvent->mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL) { mPendingScrollAmountX += acceleratedDelta.deltaX; mPendingScrollAmountY += acceleratedDelta.deltaY; } else { @@ -5537,6 +5523,9 @@ EventStateManager::WheelPrefs::WheelPrefs() { Reset(); Preferences::RegisterCallback(OnPrefChanged, "mousewheel.", nullptr); + Preferences::AddBoolVarCache(&sWheelEventsEnabledOnPlugins, + "plugin.mousewheel.enabled", + true); } EventStateManager::WheelPrefs::~WheelPrefs() @@ -5559,11 +5548,11 @@ EventStateManager::WheelPrefs::GetIndexFor(WidgetWheelEvent* aEvent) } Modifiers modifiers = - (aEvent->modifiers & (MODIFIER_ALT | - MODIFIER_CONTROL | - MODIFIER_META | - MODIFIER_SHIFT | - MODIFIER_OS)); + (aEvent->mModifiers & (MODIFIER_ALT | + MODIFIER_CONTROL | + MODIFIER_META | + MODIFIER_SHIFT | + MODIFIER_OS)); switch (modifiers) { case MODIFIER_ALT: @@ -5664,25 +5653,29 @@ EventStateManager::WheelPrefs::Init(EventStateManager::WheelPrefs::Index aIndex) void EventStateManager::WheelPrefs::ApplyUserPrefsToDelta(WidgetWheelEvent* aEvent) { + if (aEvent->mCustomizedByUserPrefs) { + return; + } + Index index = GetIndexFor(aEvent); Init(index); - aEvent->deltaX *= mMultiplierX[index]; - aEvent->deltaY *= mMultiplierY[index]; - aEvent->deltaZ *= mMultiplierZ[index]; + aEvent->mDeltaX *= mMultiplierX[index]; + aEvent->mDeltaY *= mMultiplierY[index]; + aEvent->mDeltaZ *= mMultiplierZ[index]; // If the multiplier is 1.0 or -1.0, i.e., it doesn't change the absolute // value, we should use lineOrPageDelta values which were set by widget. // Otherwise, we need to compute them from accumulated delta values. if (!NeedToComputeLineOrPageDelta(aEvent)) { - aEvent->lineOrPageDeltaX *= static_cast(mMultiplierX[index]); - aEvent->lineOrPageDeltaY *= static_cast(mMultiplierY[index]); + aEvent->mLineOrPageDeltaX *= static_cast(mMultiplierX[index]); + aEvent->mLineOrPageDeltaY *= static_cast(mMultiplierY[index]); } else { - aEvent->lineOrPageDeltaX = 0; - aEvent->lineOrPageDeltaY = 0; + aEvent->mLineOrPageDeltaX = 0; + aEvent->mLineOrPageDeltaY = 0; } - aEvent->customizedByUserPrefs = + aEvent->mCustomizedByUserPrefs = ((mMultiplierX[index] != 1.0) || (mMultiplierY[index] != 1.0) || (mMultiplierZ[index] != 1.0)); } @@ -5701,10 +5694,10 @@ EventStateManager::WheelPrefs::CancelApplyingUserPrefsFromOverflowDelta( // referrencing the deltaX and deltaY of the event. if (mMultiplierX[index]) { - aEvent->overflowDeltaX /= mMultiplierX[index]; + aEvent->mOverflowDeltaX /= mMultiplierX[index]; } if (mMultiplierY[index]) { - aEvent->overflowDeltaY /= mMultiplierY[index]; + aEvent->mOverflowDeltaY /= mMultiplierY[index]; } } @@ -5715,15 +5708,15 @@ EventStateManager::WheelPrefs::ComputeActionFor(WidgetWheelEvent* aEvent) Init(index); bool deltaXPreferred = - (Abs(aEvent->deltaX) > Abs(aEvent->deltaY) && - Abs(aEvent->deltaX) > Abs(aEvent->deltaZ)); + (Abs(aEvent->mDeltaX) > Abs(aEvent->mDeltaY) && + Abs(aEvent->mDeltaX) > Abs(aEvent->mDeltaZ)); Action* actions = deltaXPreferred ? mOverriddenActionsX : mActions; if (actions[index] == ACTION_NONE || actions[index] == ACTION_SCROLL) { return actions[index]; } // Momentum events shouldn't run special actions. - if (aEvent->isMomentum) { + if (aEvent->mIsMomentum) { // Use the default action. Note that user might kill the wheel scrolling. Init(INDEX_DEFAULT); return (actions[INDEX_DEFAULT] == ACTION_SCROLL) ? ACTION_SCROLL : @@ -5756,6 +5749,16 @@ EventStateManager::WheelPrefs::GetUserPrefsForEvent(WidgetWheelEvent* aEvent, *aOutMultiplierY = mMultiplierY[index]; } +// static +bool +EventStateManager::WheelPrefs::WheelEventsEnabledOnPlugins() +{ + if (!sInstance) { + GetInstance(); // initializing sWheelEventsEnabledOnPlugins + } + return sWheelEventsEnabledOnPlugins; +} + bool EventStateManager::WheelEventIsScrollAction(WidgetWheelEvent* aEvent) { diff --git a/dom/events/EventStateManager.h b/dom/events/EventStateManager.h index c7992fa5e6..7786a38790 100644 --- a/dom/events/EventStateManager.h +++ b/dom/events/EventStateManager.h @@ -108,6 +108,10 @@ public: nsIFrame* aTargetFrame, nsEventStatus* aStatus); + void PostHandleKeyboardEvent(WidgetKeyboardEvent* aKeyboardEvent, + nsEventStatus& aStatus, + bool dispatchedToContentProcess); + /** * DispatchLegacyMouseScrollEvents() dispatches eLegacyMouseLineOrPageScroll * event and eLegacyMousePixelScroll event for compatibility with old Gecko. @@ -516,6 +520,12 @@ protected: bool IsOverOnePageScrollAllowedX(WidgetWheelEvent* aEvent); bool IsOverOnePageScrollAllowedY(WidgetWheelEvent* aEvent); + /** + * WheelEventsEnabledOnPlugins() returns true if user wants to use mouse + * wheel on plugins. + */ + static bool WheelEventsEnabledOnPlugins(); + private: WheelPrefs(); ~WheelPrefs(); @@ -579,6 +589,8 @@ protected: Action mOverriddenActionsX[COUNT_OF_MULTIPLIERS]; static WheelPrefs* sInstance; + + static bool sWheelEventsEnabledOnPlugins; }; /** @@ -669,12 +681,14 @@ protected: // Default action prefers the scrolled element immediately before if it's // still under the mouse cursor. Otherwise, it prefers the nearest // scrollable ancestor which will be scrolled actually. + COMPUTE_DEFAULT_ACTION_TARGET_EXCEPT_PLUGIN = + (PREFER_MOUSE_WHEEL_TRANSACTION | + PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_X_AXIS | + PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_Y_AXIS), // When this is specified, the result may be nsPluginFrame. In such case, // the frame doesn't have nsIScrollableFrame interface. COMPUTE_DEFAULT_ACTION_TARGET = - (PREFER_MOUSE_WHEEL_TRANSACTION | - PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_X_AXIS | - PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_Y_AXIS | + (COMPUTE_DEFAULT_ACTION_TARGET_EXCEPT_PLUGIN | INCLUDE_PLUGIN_AS_TARGET), // Look for the nearest scrollable ancestor which can be scrollable with // aEvent. @@ -683,6 +697,17 @@ protected: COMPUTE_SCROLLABLE_ANCESTOR_ALONG_Y_AXIS = (PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_Y_AXIS | START_FROM_PARENT) }; + static ComputeScrollTargetOptions RemovePluginFromTarget( + ComputeScrollTargetOptions aOptions) + { + switch (aOptions) { + case COMPUTE_DEFAULT_ACTION_TARGET: + return COMPUTE_DEFAULT_ACTION_TARGET_EXCEPT_PLUGIN; + default: + MOZ_ASSERT(!(aOptions & INCLUDE_PLUGIN_AS_TARGET)); + return aOptions; + } + } nsIFrame* ComputeScrollTarget(nsIFrame* aTargetFrame, WidgetWheelEvent* aEvent, ComputeScrollTargetOptions aOptions); @@ -852,7 +877,7 @@ protected: /** * Set the fields of aEvent to reflect the mouse position and modifier keys * that were set when the user first pressed the mouse button (stored by - * BeginTrackingDragGesture). aEvent->widget must be + * BeginTrackingDragGesture). aEvent->mWidget must be * mCurrentTarget->GetNearestWidget(). */ void FillInEventFromGestureDown(WidgetMouseEvent* aEvent); @@ -885,19 +910,16 @@ private: static void ResetLastOverForContent(const uint32_t& aIdx, RefPtr& aChunk, nsIContent* aClosure); - void PostHandleKeyboardEvent(WidgetKeyboardEvent* aKeyboardEvent, - nsEventStatus& aStatus, - bool dispatchedToContentProcess); int32_t mLockCursor; bool mLastFrameConsumedSetCursor; - // Last mouse event refPoint (the offset from the widget's origin in + // Last mouse event mRefPoint (the offset from the widget's origin in // device pixels) when mouse was locked, used to restore mouse position // after unlocking. LayoutDeviceIntPoint mPreLockPoint; - // Stores the refPoint of the last synthetic mouse move we dispatched + // Stores the mRefPoint of the last synthetic mouse move we dispatched // to re-center the mouse when we were pointer locked. If this is (-1,-1) it // means we've not recently dispatched a centering event. We use this to // detect when we receive the synth event, so we can cancel and not send it @@ -908,7 +930,7 @@ private: nsCOMPtr mCurrentTargetContent; static nsWeakFrame sLastDragOverFrame; - // Stores the refPoint (the offset from the widget's origin in device + // Stores the mRefPoint (the offset from the widget's origin in device // pixels) of the last mouse event. static LayoutDeviceIntPoint sLastRefPoint; diff --git a/dom/events/EventTarget.cpp b/dom/events/EventTarget.cpp index d8a91c27da..34220623b8 100644 --- a/dom/events/EventTarget.cpp +++ b/dom/events/EventTarget.cpp @@ -40,7 +40,7 @@ EventTarget::SetEventHandler(const nsAString& aType, return; } if (NS_IsMainThread()) { - nsCOMPtr type = do_GetAtom(aType); + nsCOMPtr type = NS_Atomize(aType); SetEventHandler(type, EmptyString(), aHandler); return; } diff --git a/dom/events/EventTarget.h b/dom/events/EventTarget.h index 2e4895b8af..48d09d5592 100644 --- a/dom/events/EventTarget.h +++ b/dom/events/EventTarget.h @@ -55,7 +55,7 @@ public: // Note, this takes the type in onfoo form! EventHandlerNonNull* GetEventHandler(const nsAString& aType) { - nsCOMPtr type = do_GetAtom(aType); + nsCOMPtr type = NS_Atomize(aType); return GetEventHandler(type, EmptyString()); } diff --git a/dom/events/FocusEvent.cpp b/dom/events/FocusEvent.cpp index d829938400..fc9e32f68e 100644 --- a/dom/events/FocusEvent.cpp +++ b/dom/events/FocusEvent.cpp @@ -23,7 +23,7 @@ FocusEvent::FocusEvent(EventTarget* aOwner, mEventIsInternal = false; } else { mEventIsInternal = true; - mEvent->time = PR_Now(); + mEvent->mTime = PR_Now(); } } @@ -38,7 +38,7 @@ FocusEvent::GetRelatedTarget(nsIDOMEventTarget** aRelatedTarget) EventTarget* FocusEvent::GetRelatedTarget() { - return mEvent->AsFocusEvent()->relatedTarget; + return mEvent->AsFocusEvent()->mRelatedTarget; } nsresult @@ -52,7 +52,7 @@ FocusEvent::InitFocusEvent(const nsAString& aType, nsresult rv = UIEvent::InitUIEvent(aType, aCanBubble, aCancelable, aView, aDetail); NS_ENSURE_SUCCESS(rv, rv); - mEvent->AsFocusEvent()->relatedTarget = aRelatedTarget; + mEvent->AsFocusEvent()->mRelatedTarget = aRelatedTarget; return NS_OK; } diff --git a/dom/events/IMEContentObserver.cpp b/dom/events/IMEContentObserver.cpp index 90282ca8cc..fa50fac528 100644 --- a/dom/events/IMEContentObserver.cpp +++ b/dom/events/IMEContentObserver.cpp @@ -757,7 +757,7 @@ IMEContentObserver::OnMouseButtonEvent(nsPresContext* aPresContext, } if (!aMouseEvent->IsTrusted() || aMouseEvent->DefaultPrevented() || - !aMouseEvent->widget) { + !aMouseEvent->mWidget) { return false; } // Now, we need to notify only mouse down and mouse up event. @@ -775,8 +775,8 @@ IMEContentObserver::OnMouseButtonEvent(nsPresContext* aPresContext, RefPtr kungFuDeathGrip(this); WidgetQueryContentEvent charAtPt(true, eQueryCharacterAtPoint, - aMouseEvent->widget); - charAtPt.refPoint = aMouseEvent->refPoint; + aMouseEvent->mWidget); + charAtPt.mRefPoint = aMouseEvent->mRefPoint; ContentEventHandler handler(aPresContext); handler.OnQueryCharacterAtPoint(&charAtPt); if (NS_WARN_IF(!charAtPt.mSucceeded) || @@ -800,8 +800,8 @@ IMEContentObserver::OnMouseButtonEvent(nsPresContext* aPresContext, } // The refPt is relative to its widget. // We should notify it with offset in the widget. - if (aMouseEvent->widget != mWidget) { - charAtPt.refPoint += aMouseEvent->widget->WidgetToScreenOffset() - + if (aMouseEvent->mWidget != mWidget) { + charAtPt.mRefPoint += aMouseEvent->mWidget->WidgetToScreenOffset() - mWidget->WidgetToScreenOffset(); } @@ -809,12 +809,12 @@ IMEContentObserver::OnMouseButtonEvent(nsPresContext* aPresContext, notification.mMouseButtonEventData.mEventMessage = aMouseEvent->mMessage; notification.mMouseButtonEventData.mOffset = charAtPt.mReply.mOffset; notification.mMouseButtonEventData.mCursorPos.Set( - charAtPt.refPoint.ToUnknownPoint()); + charAtPt.mRefPoint.ToUnknownPoint()); notification.mMouseButtonEventData.mCharRect.Set( charAtPt.mReply.mRect.ToUnknownRect()); notification.mMouseButtonEventData.mButton = aMouseEvent->button; notification.mMouseButtonEventData.mButtons = aMouseEvent->buttons; - notification.mMouseButtonEventData.mModifiers = aMouseEvent->modifiers; + notification.mMouseButtonEventData.mModifiers = aMouseEvent->mModifiers; nsresult rv = IMEStateManager::NotifyIME(notification, mWidget); if (NS_WARN_IF(NS_FAILED(rv))) { diff --git a/dom/events/IMEStateManager.cpp b/dom/events/IMEStateManager.cpp index 54f2a9f215..1aca8bd234 100644 --- a/dom/events/IMEStateManager.cpp +++ b/dom/events/IMEStateManager.cpp @@ -1147,7 +1147,7 @@ IMEStateManager::DispatchCompositionEvent( ("ISM: IMEStateManager::DispatchCompositionEvent(aNode=0x%p, " "aPresContext=0x%p, aCompositionEvent={ mMessage=%s, " "mNativeIMEContext={ mRawNativeIMEContext=0x%X, " - "mOriginProcessID=0x%X }, widget(0x%p)={ " + "mOriginProcessID=0x%X }, mWidget(0x%p)={ " "GetNativeIMEContext()={ mRawNativeIMEContext=0x%X, " "mOriginProcessID=0x%X }, Destroyed()=%s }, " "mFlags={ mIsTrusted=%s, mPropagationStopped=%s } }, " @@ -1156,16 +1156,16 @@ IMEStateManager::DispatchCompositionEvent( ToChar(aCompositionEvent->mMessage), aCompositionEvent->mNativeIMEContext.mRawNativeIMEContext, aCompositionEvent->mNativeIMEContext.mOriginProcessID, - aCompositionEvent->widget.get(), - aCompositionEvent->widget->GetNativeIMEContext().mRawNativeIMEContext, - aCompositionEvent->widget->GetNativeIMEContext().mOriginProcessID, - GetBoolName(aCompositionEvent->widget->Destroyed()), + aCompositionEvent->mWidget.get(), + aCompositionEvent->mWidget->GetNativeIMEContext().mRawNativeIMEContext, + aCompositionEvent->mWidget->GetNativeIMEContext().mOriginProcessID, + GetBoolName(aCompositionEvent->mWidget->Destroyed()), GetBoolName(aCompositionEvent->mFlags.mIsTrusted), GetBoolName(aCompositionEvent->mFlags.mPropagationStopped), GetBoolName(aIsSynthesized), tabParent.get())); if (!aCompositionEvent->IsTrusted() || - aCompositionEvent->mFlags.mPropagationStopped) { + aCompositionEvent->PropagationStopped()) { return; } @@ -1217,7 +1217,7 @@ IMEStateManager::DispatchCompositionEvent( composition->WasNativeCompositionEndEventDiscarded()) && aCompositionEvent->CausesDOMCompositionEndEvent()) { TextCompositionArray::index_type i = - sTextCompositions->IndexOf(aCompositionEvent->widget); + sTextCompositions->IndexOf(aCompositionEvent->mWidget); if (i != TextCompositionArray::NoIndex) { MOZ_LOG(sISMLog, LogLevel::Debug, ("ISM: IMEStateManager::DispatchCompositionEvent(), " @@ -1266,7 +1266,7 @@ IMEStateManager::HandleSelectionEvent(nsPresContext* aPresContext, } RefPtr composition = sTextCompositions ? - sTextCompositions->GetCompositionFor(aSelectionEvent->widget) : nullptr; + sTextCompositions->GetCompositionFor(aSelectionEvent->mWidget) : nullptr; if (composition) { // When there is a composition, TextComposition should guarantee that the // selection event will be handled in same target as composition events. @@ -1290,17 +1290,17 @@ IMEStateManager::OnCompositionEventDiscarded( MOZ_LOG(sISMLog, LogLevel::Info, ("ISM: IMEStateManager::OnCompositionEventDiscarded(aCompositionEvent={ " "mMessage=%s, mNativeIMEContext={ mRawNativeIMEContext=0x%X, " - "mOriginProcessID=0x%X }, widget(0x%p)={ " + "mOriginProcessID=0x%X }, mWidget(0x%p)={ " "GetNativeIMEContext()={ mRawNativeIMEContext=0x%X, " "mOriginProcessID=0x%X }, Destroyed()=%s }, " "mFlags={ mIsTrusted=%s } })", ToChar(aCompositionEvent->mMessage), aCompositionEvent->mNativeIMEContext.mRawNativeIMEContext, aCompositionEvent->mNativeIMEContext.mOriginProcessID, - aCompositionEvent->widget.get(), - aCompositionEvent->widget->GetNativeIMEContext().mRawNativeIMEContext, - aCompositionEvent->widget->GetNativeIMEContext().mOriginProcessID, - GetBoolName(aCompositionEvent->widget->Destroyed()), + aCompositionEvent->mWidget.get(), + aCompositionEvent->mWidget->GetNativeIMEContext().mRawNativeIMEContext, + aCompositionEvent->mWidget->GetNativeIMEContext().mOriginProcessID, + GetBoolName(aCompositionEvent->mWidget->Destroyed()), GetBoolName(aCompositionEvent->mFlags.mIsTrusted))); if (!aCompositionEvent->IsTrusted()) { @@ -1314,7 +1314,7 @@ IMEStateManager::OnCompositionEventDiscarded( } RefPtr composition = - sTextCompositions->GetCompositionFor(aCompositionEvent->widget); + sTextCompositions->GetCompositionFor(aCompositionEvent->mWidget); if (!composition) { // If the PresShell has been being destroyed during composition, // a TextComposition instance for the composition was already removed from diff --git a/dom/events/InputEvent.cpp b/dom/events/InputEvent.cpp index f2b8a25f07..ffaa3fd21e 100644 --- a/dom/events/InputEvent.cpp +++ b/dom/events/InputEvent.cpp @@ -25,7 +25,7 @@ InputEvent::InputEvent(EventTarget* aOwner, mEventIsInternal = false; } else { mEventIsInternal = true; - mEvent->time = PR_Now(); + mEvent->mTime = PR_Now(); } } diff --git a/dom/events/KeyboardEvent.cpp b/dom/events/KeyboardEvent.cpp index f737d45b72..d4a7d5edf3 100644 --- a/dom/events/KeyboardEvent.cpp +++ b/dom/events/KeyboardEvent.cpp @@ -25,7 +25,7 @@ KeyboardEvent::KeyboardEvent(EventTarget* aOwner, } else { mEventIsInternal = true; - mEvent->time = PR_Now(); + mEvent->mTime = PR_Now(); mEvent->AsKeyboardEvent()->mKeyNameIndex = KEY_NAME_INDEX_USE_STRING; } } @@ -136,6 +136,41 @@ KeyboardEvent::GetCode(nsAString& aCodeName) mEvent->AsKeyboardEvent()->GetDOMCodeName(aCodeName); } +void KeyboardEvent::GetInitDict(KeyboardEventInit& aParam) +{ + GetKey(aParam.mKey); + GetCode(aParam.mCode); + aParam.mLocation = Location(); + aParam.mRepeat = Repeat(); + aParam.mIsComposing = IsComposing(); + + // legacy attributes + aParam.mKeyCode = KeyCode(); + aParam.mCharCode = CharCode(); + aParam.mWhich = Which(); + + // modifiers from EventModifierInit + aParam.mCtrlKey = CtrlKey(); + aParam.mShiftKey = ShiftKey(); + aParam.mAltKey = AltKey(); + aParam.mMetaKey = MetaKey(); + + WidgetKeyboardEvent* internalEvent = mEvent->AsKeyboardEvent(); + aParam.mModifierAltGraph = internalEvent->IsAltGraph(); + aParam.mModifierCapsLock = internalEvent->IsCapsLocked(); + aParam.mModifierFn = internalEvent->IsFn(); + aParam.mModifierFnLock = internalEvent->IsFnLocked(); + aParam.mModifierNumLock = internalEvent->IsNumLocked(); + aParam.mModifierOS = internalEvent->IsOS(); + aParam.mModifierScrollLock = internalEvent->IsScrollLocked(); + aParam.mModifierSymbol = internalEvent->IsSymbol(); + aParam.mModifierSymbolLock = internalEvent->IsSymbolLocked(); + + // EventInit + aParam.mBubbles = internalEvent->mFlags.mBubbles; + aParam.mCancelable = internalEvent->mFlags.mCancelable; +} + NS_IMETHODIMP KeyboardEvent::GetCharCode(uint32_t* aCharCode) { diff --git a/dom/events/KeyboardEvent.h b/dom/events/KeyboardEvent.h index 2772225cdb..a8dadd3800 100644 --- a/dom/events/KeyboardEvent.h +++ b/dom/events/KeyboardEvent.h @@ -60,6 +60,7 @@ public: uint32_t Location(); void GetCode(nsAString& aCode); + void GetInitDict(KeyboardEventInit& aParam); void InitKeyEvent(const nsAString& aType, bool aCanBubble, bool aCancelable, nsIDOMWindow* aView, bool aCtrlKey, bool aAltKey, diff --git a/dom/events/MouseEvent.cpp b/dom/events/MouseEvent.cpp index 472396ed4c..6263a71a06 100644 --- a/dom/events/MouseEvent.cpp +++ b/dom/events/MouseEvent.cpp @@ -31,8 +31,8 @@ MouseEvent::MouseEvent(EventTarget* aOwner, } else { mEventIsInternal = true; - mEvent->time = PR_Now(); - mEvent->refPoint.x = mEvent->refPoint.y = 0; + mEvent->mTime = PR_Now(); + mEvent->mRefPoint = LayoutDeviceIntPoint(0, 0); mouseEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN; } @@ -84,8 +84,8 @@ MouseEvent::InitMouseEvent(const nsAString& aType, mouseEventBase->InitBasicModifiers(aCtrlKey, aAltKey, aShiftKey, aMetaKey); mClientPoint.x = aClientX; mClientPoint.y = aClientY; - mouseEventBase->refPoint.x = aScreenX; - mouseEventBase->refPoint.y = aScreenY; + mouseEventBase->mRefPoint.x = aScreenX; + mouseEventBase->mRefPoint.y = aScreenY; WidgetMouseEvent* mouseEvent = mEvent->AsMouseEvent(); if (mouseEvent) { @@ -132,7 +132,7 @@ MouseEvent::InitMouseEvent(const nsAString& aType, case eDragEventClass: case ePointerEventClass: case eSimpleGestureEventClass: - mEvent->AsInputEvent()->modifiers = modifiers; + mEvent->AsInputEvent()->mModifiers = modifiers; return NS_OK; default: MOZ_CRASH("There is no space to store the modifiers"); @@ -278,7 +278,8 @@ MouseEvent::GetRelatedTarget() if (relatedTarget) { nsCOMPtr content = do_QueryInterface(relatedTarget); - nsCOMPtr currentTarget = do_QueryInterface(mEvent->currentTarget); + nsCOMPtr currentTarget = + do_QueryInterface(mEvent->mCurrentTarget); nsIContent* shadowRelatedTarget = GetShadowRelatedTarget(currentTarget, content); if (shadowRelatedTarget) { @@ -337,7 +338,7 @@ MouseEvent::GetScreenX(int32_t* aScreenX) int32_t MouseEvent::ScreenX() { - return Event::GetScreenCoords(mPresContext, mEvent, mEvent->refPoint).x; + return Event::GetScreenCoords(mPresContext, mEvent, mEvent->mRefPoint).x; } NS_IMETHODIMP @@ -351,7 +352,7 @@ MouseEvent::GetScreenY(int32_t* aScreenY) int32_t MouseEvent::ScreenY() { - return Event::GetScreenCoords(mPresContext, mEvent, mEvent->refPoint).y; + return Event::GetScreenCoords(mPresContext, mEvent, mEvent->mRefPoint).y; } @@ -366,7 +367,7 @@ MouseEvent::GetClientX(int32_t* aClientX) int32_t MouseEvent::ClientX() { - return Event::GetClientCoords(mPresContext, mEvent, mEvent->refPoint, + return Event::GetClientCoords(mPresContext, mEvent, mEvent->mRefPoint, mClientPoint).x; } @@ -381,21 +382,21 @@ MouseEvent::GetClientY(int32_t* aClientY) int32_t MouseEvent::ClientY() { - return Event::GetClientCoords(mPresContext, mEvent, mEvent->refPoint, + return Event::GetClientCoords(mPresContext, mEvent, mEvent->mRefPoint, mClientPoint).y; } int32_t MouseEvent::OffsetX() { - return Event::GetOffsetCoords(mPresContext, mEvent, mEvent->refPoint, + return Event::GetOffsetCoords(mPresContext, mEvent, mEvent->mRefPoint, mClientPoint).x; } int32_t MouseEvent::OffsetY() { - return Event::GetOffsetCoords(mPresContext, mEvent, mEvent->refPoint, + return Event::GetOffsetCoords(mPresContext, mEvent, mEvent->mRefPoint, mClientPoint).y; } diff --git a/dom/events/MouseScrollEvent.cpp b/dom/events/MouseScrollEvent.cpp index e1fd83b4c6..4dba8f101c 100644 --- a/dom/events/MouseScrollEvent.cpp +++ b/dom/events/MouseScrollEvent.cpp @@ -22,13 +22,13 @@ MouseScrollEvent::MouseScrollEvent(EventTarget* aOwner, mEventIsInternal = false; } else { mEventIsInternal = true; - mEvent->time = PR_Now(); - mEvent->refPoint.x = mEvent->refPoint.y = 0; + mEvent->mTime = PR_Now(); + mEvent->mRefPoint = LayoutDeviceIntPoint(0, 0); static_cast(mEvent)->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN; } - mDetail = mEvent->AsMouseScrollEvent()->delta; + mDetail = mEvent->AsMouseScrollEvent()->mDelta; } NS_IMPL_ADDREF_INHERITED(MouseScrollEvent, MouseEvent) @@ -62,7 +62,8 @@ MouseScrollEvent::InitMouseScrollEvent(const nsAString& aType, aCtrlKey, aAltKey, aShiftKey, aMetaKey, aButton, aRelatedTarget); NS_ENSURE_SUCCESS(rv, rv); - mEvent->AsMouseScrollEvent()->isHorizontal = (aAxis == HORIZONTAL_AXIS); + mEvent->AsMouseScrollEvent()->mIsHorizontal = + (aAxis == nsIDOMMouseScrollEvent::HORIZONTAL_AXIS); return NS_OK; } @@ -78,7 +79,7 @@ MouseScrollEvent::GetAxis(int32_t* aResult) int32_t MouseScrollEvent::Axis() { - return mEvent->AsMouseScrollEvent()->isHorizontal ? + return mEvent->AsMouseScrollEvent()->mIsHorizontal ? static_cast(HORIZONTAL_AXIS) : static_cast(VERTICAL_AXIS); } diff --git a/dom/events/MutationEvent.cpp b/dom/events/MutationEvent.cpp index 8020d4978f..b379eafe61 100644 --- a/dom/events/MutationEvent.cpp +++ b/dom/events/MutationEvent.cpp @@ -101,11 +101,11 @@ MutationEvent::InitMutationEvent(const nsAString& aTypeArg, InternalMutationEvent* mutation = mEvent->AsMutationEvent(); mutation->mRelatedNode = aRelatedNodeArg; if (!aPrevValueArg.IsEmpty()) - mutation->mPrevAttrValue = do_GetAtom(aPrevValueArg); + mutation->mPrevAttrValue = NS_Atomize(aPrevValueArg); if (!aNewValueArg.IsEmpty()) - mutation->mNewAttrValue = do_GetAtom(aNewValueArg); + mutation->mNewAttrValue = NS_Atomize(aNewValueArg); if (!aAttrNameArg.IsEmpty()) { - mutation->mAttrName = do_GetAtom(aAttrNameArg); + mutation->mAttrName = NS_Atomize(aAttrNameArg); } mutation->mAttrChange = aAttrChangeArg; diff --git a/dom/events/PointerEvent.cpp b/dom/events/PointerEvent.cpp index 9385f3692f..aa1aae4026 100644 --- a/dom/events/PointerEvent.cpp +++ b/dom/events/PointerEvent.cpp @@ -28,8 +28,8 @@ PointerEvent::PointerEvent(EventTarget* aOwner, mEventIsInternal = false; } else { mEventIsInternal = true; - mEvent->time = PR_Now(); - mEvent->refPoint.x = mEvent->refPoint.y = 0; + mEvent->mTime = PR_Now(); + mEvent->mRefPoint = LayoutDeviceIntPoint(0, 0); mouseEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN; } } diff --git a/dom/events/SimpleGestureEvent.cpp b/dom/events/SimpleGestureEvent.cpp index a165a8eb2b..b52425bba5 100644 --- a/dom/events/SimpleGestureEvent.cpp +++ b/dom/events/SimpleGestureEvent.cpp @@ -26,8 +26,8 @@ SimpleGestureEvent::SimpleGestureEvent(EventTarget* aOwner, mEventIsInternal = false; } else { mEventIsInternal = true; - mEvent->time = PR_Now(); - mEvent->refPoint.x = mEvent->refPoint.y = 0; + mEvent->mTime = PR_Now(); + mEvent->mRefPoint = LayoutDeviceIntPoint(0, 0); static_cast(mEvent)->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN; } diff --git a/dom/events/TextComposition.cpp b/dom/events/TextComposition.cpp index 036ce5d3c7..b93724f6eb 100644 --- a/dom/events/TextComposition.cpp +++ b/dom/events/TextComposition.cpp @@ -95,7 +95,7 @@ TextComposition::MaybeDispatchCompositionUpdate( { MOZ_RELEASE_ASSERT(!mTabParent); - if (!IsValidStateForComposition(aCompositionEvent->widget)) { + if (!IsValidStateForComposition(aCompositionEvent->mWidget)) { return false; } @@ -103,7 +103,7 @@ TextComposition::MaybeDispatchCompositionUpdate( return true; } CloneAndDispatchAs(aCompositionEvent, eCompositionUpdate); - return IsValidStateForComposition(aCompositionEvent->widget); + return IsValidStateForComposition(aCompositionEvent->mWidget); } BaseEventFlags @@ -115,13 +115,13 @@ TextComposition::CloneAndDispatchAs( { MOZ_RELEASE_ASSERT(!mTabParent); - MOZ_ASSERT(IsValidStateForComposition(aCompositionEvent->widget), + MOZ_ASSERT(IsValidStateForComposition(aCompositionEvent->mWidget), "Should be called only when it's safe to dispatch an event"); WidgetCompositionEvent compositionEvent(aCompositionEvent->IsTrusted(), - aMessage, aCompositionEvent->widget); - compositionEvent.time = aCompositionEvent->time; - compositionEvent.timeStamp = aCompositionEvent->timeStamp; + aMessage, aCompositionEvent->mWidget); + compositionEvent.mTime = aCompositionEvent->mTime; + compositionEvent.mTimeStamp = aCompositionEvent->mTimeStamp; compositionEvent.mData = aCompositionEvent->mData; compositionEvent.mNativeIMEContext = aCompositionEvent->mNativeIMEContext; compositionEvent.mOriginalMessage = aCompositionEvent->mMessage; @@ -279,7 +279,7 @@ TextComposition::DispatchCompositionEvent( aCompositionEvent->mRanges = nullptr; } - if (!IsValidStateForComposition(aCompositionEvent->widget)) { + if (!IsValidStateForComposition(aCompositionEvent->mWidget)) { *aStatus = nsEventStatus_eConsumeNoDefault; return; } @@ -377,7 +377,7 @@ TextComposition::DispatchCompositionEvent( *aStatus = nsEventStatus_eConsumeNoDefault; } - if (!IsValidStateForComposition(aCompositionEvent->widget)) { + if (!IsValidStateForComposition(aCompositionEvent->mWidget)) { return; } diff --git a/dom/events/TouchEvent.cpp b/dom/events/TouchEvent.cpp index 3af71ce3e1..d2be9ea2e3 100644 --- a/dom/events/TouchEvent.cpp +++ b/dom/events/TouchEvent.cpp @@ -69,13 +69,13 @@ TouchEvent::TouchEvent(EventTarget* aOwner, if (aEvent) { mEventIsInternal = false; - for (uint32_t i = 0; i < aEvent->touches.Length(); ++i) { - Touch* touch = aEvent->touches[i]; + for (uint32_t i = 0; i < aEvent->mTouches.Length(); ++i) { + Touch* touch = aEvent->mTouches[i]; touch->InitializePoints(mPresContext, aEvent); } } else { mEventIsInternal = true; - mEvent->time = PR_Now(); + mEvent->mTime = PR_Now(); } } @@ -118,9 +118,9 @@ TouchEvent::Touches() if (!mTouches) { WidgetTouchEvent* touchEvent = mEvent->AsTouchEvent(); if (mEvent->mMessage == eTouchEnd || mEvent->mMessage == eTouchCancel) { - // for touchend events, remove any changed touches from the touches array + // for touchend events, remove any changed touches from mTouches WidgetTouchEvent::AutoTouchArray unchangedTouches; - const WidgetTouchEvent::TouchArray& touches = touchEvent->touches; + const WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches; for (uint32_t i = 0; i < touches.Length(); ++i) { if (!touches[i]->mChanged) { unchangedTouches.AppendElement(touches[i]); @@ -128,7 +128,7 @@ TouchEvent::Touches() } mTouches = new TouchList(ToSupports(this), unchangedTouches); } else { - mTouches = new TouchList(ToSupports(this), touchEvent->touches); + mTouches = new TouchList(ToSupports(this), touchEvent->mTouches); } } return mTouches; @@ -140,13 +140,13 @@ TouchEvent::TargetTouches() if (!mTargetTouches) { WidgetTouchEvent::AutoTouchArray targetTouches; WidgetTouchEvent* touchEvent = mEvent->AsTouchEvent(); - const WidgetTouchEvent::TouchArray& touches = touchEvent->touches; + const WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches; for (uint32_t i = 0; i < touches.Length(); ++i) { // for touchend/cancel events, don't append to the target list if this is a // touch that is ending if ((mEvent->mMessage != eTouchEnd && mEvent->mMessage != eTouchCancel) || !touches[i]->mChanged) { - if (touches[i]->mTarget == mEvent->originalTarget) { + if (touches[i]->mTarget == mEvent->mOriginalTarget) { targetTouches.AppendElement(touches[i]); } } @@ -162,7 +162,7 @@ TouchEvent::ChangedTouches() if (!mChangedTouches) { WidgetTouchEvent::AutoTouchArray changedTouches; WidgetTouchEvent* touchEvent = mEvent->AsTouchEvent(); - const WidgetTouchEvent::TouchArray& touches = touchEvent->touches; + const WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches; for (uint32_t i = 0; i < touches.Length(); ++i) { if (touches[i]->mChanged) { changedTouches.AppendElement(touches[i]); diff --git a/dom/events/TransitionEvent.cpp b/dom/events/TransitionEvent.cpp index 590f57d366..8bcc048640 100644 --- a/dom/events/TransitionEvent.cpp +++ b/dom/events/TransitionEvent.cpp @@ -22,7 +22,7 @@ TransitionEvent::TransitionEvent(EventTarget* aOwner, } else { mEventIsInternal = true; - mEvent->time = PR_Now(); + mEvent->mTime = PR_Now(); } } diff --git a/dom/events/UIEvent.cpp b/dom/events/UIEvent.cpp index e6ae6015af..5aeb23917b 100644 --- a/dom/events/UIEvent.cpp +++ b/dom/events/UIEvent.cpp @@ -42,7 +42,7 @@ UIEvent::UIEvent(EventTarget* aOwner, } else { mEventIsInternal = true; - mEvent->time = PR_Now(); + mEvent->mTime = PR_Now(); } // Fill mDetail and mView according to the mEvent (widget-generated @@ -50,7 +50,7 @@ UIEvent::UIEvent(EventTarget* aOwner, switch(mEvent->mClass) { case eUIEventClass: { - mDetail = mEvent->AsUIEvent()->detail; + mDetail = mEvent->AsUIEvent()->mDetail; break; } @@ -125,13 +125,13 @@ UIEvent::GetMovementPoint() mEvent->mClass != eDragEventClass && mEvent->mClass != ePointerEventClass && mEvent->mClass != eSimpleGestureEventClass) || - !mEvent->AsGUIEvent()->widget) { + !mEvent->AsGUIEvent()->mWidget) { return nsIntPoint(0, 0); } // Calculate the delta between the last screen point and the current one. - nsIntPoint current = DevPixelsToCSSPixels(mEvent->refPoint, mPresContext); - nsIntPoint last = DevPixelsToCSSPixels(mEvent->lastRefPoint, mPresContext); + nsIntPoint current = DevPixelsToCSSPixels(mEvent->mRefPoint, mPresContext); + nsIntPoint last = DevPixelsToCSSPixels(mEvent->mLastRefPoint, mPresContext); return current - last; } @@ -183,7 +183,7 @@ UIEvent::PageX() const return mPagePoint.x; } - return Event::GetPageCoords(mPresContext, mEvent, mEvent->refPoint, + return Event::GetPageCoords(mPresContext, mEvent, mEvent->mRefPoint, mClientPoint).x; } @@ -202,7 +202,7 @@ UIEvent::PageY() const return mPagePoint.y; } - return Event::GetPageCoords(mPresContext, mEvent, mEvent->refPoint, + return Event::GetPageCoords(mPresContext, mEvent, mEvent->mRefPoint, mClientPoint).y; } @@ -356,20 +356,20 @@ NS_IMETHODIMP UIEvent::DuplicatePrivateData() { mClientPoint = - Event::GetClientCoords(mPresContext, mEvent, mEvent->refPoint, + Event::GetClientCoords(mPresContext, mEvent, mEvent->mRefPoint, mClientPoint); mMovementPoint = GetMovementPoint(); mLayerPoint = GetLayerPoint(); mPagePoint = - Event::GetPageCoords(mPresContext, mEvent, mEvent->refPoint, mClientPoint); - // GetScreenPoint converts mEvent->refPoint to right coordinates. + Event::GetPageCoords(mPresContext, mEvent, mEvent->mRefPoint, mClientPoint); + // GetScreenPoint converts mEvent->mRefPoint to right coordinates. CSSIntPoint screenPoint = - Event::GetScreenCoords(mPresContext, mEvent, mEvent->refPoint); + Event::GetScreenCoords(mPresContext, mEvent, mEvent->mRefPoint); nsresult rv = Event::DuplicatePrivateData(); if (NS_SUCCEEDED(rv)) { CSSToLayoutDeviceScale scale = mPresContext ? mPresContext->CSSToDevPixelScale() : CSSToLayoutDeviceScale(1); - mEvent->refPoint = RoundedToInt(screenPoint * scale); + mEvent->mRefPoint = RoundedToInt(screenPoint * scale); } return rv; } @@ -459,7 +459,7 @@ UIEvent::GetModifierStateInternal(const nsAString& aKey) { WidgetInputEvent* inputEvent = mEvent->AsInputEvent(); MOZ_ASSERT(inputEvent, "mEvent must be WidgetInputEvent or derived class"); - return ((inputEvent->modifiers & WidgetInputEvent::GetModifier(aKey)) != 0); + return ((inputEvent->mModifiers & WidgetInputEvent::GetModifier(aKey)) != 0); } void @@ -475,11 +475,11 @@ UIEvent::InitModifiers(const EventModifierInit& aParam) return; } - inputEvent->modifiers = MODIFIER_NONE; + inputEvent->mModifiers = MODIFIER_NONE; #define SET_MODIFIER(aName, aValue) \ if (aParam.m##aName) { \ - inputEvent->modifiers |= aValue; \ + inputEvent->mModifiers |= aValue; \ } \ SET_MODIFIER(CtrlKey, MODIFIER_CONTROL) diff --git a/dom/events/UIEvent.h b/dom/events/UIEvent.h index a130744d7d..89d43bba4b 100644 --- a/dom/events/UIEvent.h +++ b/dom/events/UIEvent.h @@ -89,7 +89,7 @@ public: bool CancelBubble() const { - return mEvent->mFlags.mPropagationStopped; + return mEvent->PropagationStopped(); } bool IsChar() const; @@ -104,7 +104,7 @@ protected: nsCOMPtr mView; int32_t mDetail; CSSIntPoint mClientPoint; - // Screenpoint is mEvent->refPoint. + // Screenpoint is mEvent->mRefPoint. nsIntPoint mLayerPoint; CSSIntPoint mPagePoint; nsIntPoint mMovementPoint; diff --git a/dom/events/WheelEvent.cpp b/dom/events/WheelEvent.cpp index 8ca4efaee4..c26fc9d11d 100644 --- a/dom/events/WheelEvent.cpp +++ b/dom/events/WheelEvent.cpp @@ -25,13 +25,13 @@ WheelEvent::WheelEvent(EventTarget* aOwner, // device pixels. However, JS contents need the delta values in CSS pixels. // We should store the value of mAppUnitsPerDevPixel here because // it might be changed by changing zoom or something. - if (aWheelEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL) { + if (aWheelEvent->mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL) { mAppUnitsPerDevPixel = aPresContext->AppUnitsPerDevPixel(); } } else { mEventIsInternal = true; - mEvent->time = PR_Now(); - mEvent->refPoint.x = mEvent->refPoint.y = 0; + mEvent->mTime = PR_Now(); + mEvent->mRefPoint = LayoutDeviceIntPoint(0, 0); mEvent->AsWheelEvent()->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN; } } @@ -68,10 +68,10 @@ WheelEvent::InitWheelEvent(const nsAString& aType, NS_ENSURE_SUCCESS(rv, rv); WidgetWheelEvent* wheelEvent = mEvent->AsWheelEvent(); - wheelEvent->deltaX = aDeltaX; - wheelEvent->deltaY = aDeltaY; - wheelEvent->deltaZ = aDeltaZ; - wheelEvent->deltaMode = aDeltaMode; + wheelEvent->mDeltaX = aDeltaX; + wheelEvent->mDeltaY = aDeltaY; + wheelEvent->mDeltaZ = aDeltaZ; + wheelEvent->mDeltaMode = aDeltaMode; return NS_OK; } @@ -80,9 +80,9 @@ double WheelEvent::DeltaX() { if (!mAppUnitsPerDevPixel) { - return mEvent->AsWheelEvent()->deltaX; + return mEvent->AsWheelEvent()->mDeltaX; } - return mEvent->AsWheelEvent()->deltaX * + return mEvent->AsWheelEvent()->mDeltaX * mAppUnitsPerDevPixel / nsPresContext::AppUnitsPerCSSPixel(); } @@ -99,9 +99,9 @@ double WheelEvent::DeltaY() { if (!mAppUnitsPerDevPixel) { - return mEvent->AsWheelEvent()->deltaY; + return mEvent->AsWheelEvent()->mDeltaY; } - return mEvent->AsWheelEvent()->deltaY * + return mEvent->AsWheelEvent()->mDeltaY * mAppUnitsPerDevPixel / nsPresContext::AppUnitsPerCSSPixel(); } @@ -118,9 +118,9 @@ double WheelEvent::DeltaZ() { if (!mAppUnitsPerDevPixel) { - return mEvent->AsWheelEvent()->deltaZ; + return mEvent->AsWheelEvent()->mDeltaZ; } - return mEvent->AsWheelEvent()->deltaZ * + return mEvent->AsWheelEvent()->mDeltaZ * mAppUnitsPerDevPixel / nsPresContext::AppUnitsPerCSSPixel(); } @@ -136,7 +136,7 @@ WheelEvent::GetDeltaZ(double* aDeltaZ) uint32_t WheelEvent::DeltaMode() { - return mEvent->AsWheelEvent()->deltaMode; + return mEvent->AsWheelEvent()->mDeltaMode; } NS_IMETHODIMP diff --git a/dom/events/WheelHandlingHelper.cpp b/dom/events/WheelHandlingHelper.cpp index 0ac297d882..7eb114104d 100644 --- a/dom/events/WheelHandlingHelper.cpp +++ b/dom/events/WheelHandlingHelper.cpp @@ -30,8 +30,8 @@ namespace mozilla { /******************************************************************/ DeltaValues::DeltaValues(WidgetWheelEvent* aEvent) - : deltaX(aEvent->deltaX) - , deltaY(aEvent->deltaY) + : deltaX(aEvent->mDeltaX) + , deltaY(aEvent->mDeltaY) { } @@ -129,7 +129,7 @@ WheelTransaction::UpdateTransaction(WidgetWheelEvent* aEvent) } if (!WheelHandlingUtils::CanScrollOn(scrollToFrame, - aEvent->deltaX, aEvent->deltaY)) { + aEvent->mDeltaX, aEvent->mDeltaY)) { OnFailToScrollTarget(); // We should not modify the transaction state when the view will not be // scrolled actually. @@ -235,7 +235,7 @@ WheelTransaction::OnEvent(WidgetEvent* aEvent) // If the cursor is moving to be outside the frame, // terminate the scrollwheel transaction. nsIntPoint pt = GetScreenPoint(mouseEvent); - nsIntRect r = sTargetFrame->GetScreenRectExternal(); + nsIntRect r = sTargetFrame->GetScreenRect(); if (!r.Contains(pt)) { EndTransaction(); return; @@ -338,8 +338,8 @@ WheelTransaction::SetTimeout() WheelTransaction::GetScreenPoint(WidgetGUIEvent* aEvent) { NS_ASSERTION(aEvent, "aEvent is null"); - NS_ASSERTION(aEvent->widget, "aEvent-widget is null"); - return (aEvent->refPoint + aEvent->widget->WidgetToScreenOffset()) + NS_ASSERTION(aEvent->mWidget, "aEvent-mWidget is null"); + return (aEvent->mRefPoint + aEvent->mWidget->WidgetToScreenOffset()) .ToUnknownPoint(); } @@ -362,7 +362,7 @@ WheelTransaction::AccelerateWheelDelta(WidgetWheelEvent* aEvent, DeltaValues result(aEvent); // Don't accelerate the delta values if the event isn't line scrolling. - if (aEvent->deltaMode != nsIDOMWheelEvent::DOM_DELTA_LINE) { + if (aEvent->mDeltaMode != nsIDOMWheelEvent::DOM_DELTA_LINE) { return result; } @@ -405,11 +405,11 @@ WheelTransaction::GetAccelerationFactor() WheelTransaction::OverrideSystemScrollSpeed(WidgetWheelEvent* aEvent) { MOZ_ASSERT(sTargetFrame, "We don't have mouse scrolling transaction"); - MOZ_ASSERT(aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE); + MOZ_ASSERT(aEvent->mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE); // If the event doesn't scroll to both X and Y, we don't need to do anything // here. - if (!aEvent->deltaX && !aEvent->deltaY) { + if (!aEvent->mDeltaX && !aEvent->mDeltaY) { return DeltaValues(aEvent); } diff --git a/dom/events/XULCommandEvent.cpp b/dom/events/XULCommandEvent.cpp index b6ef6759a5..ad82291c04 100644 --- a/dom/events/XULCommandEvent.cpp +++ b/dom/events/XULCommandEvent.cpp @@ -22,7 +22,7 @@ XULCommandEvent::XULCommandEvent(EventTarget* aOwner, } else { mEventIsInternal = true; - mEvent->time = PR_Now(); + mEvent->mTime = PR_Now(); } } diff --git a/dom/html/HTMLAllCollection.cpp b/dom/html/HTMLAllCollection.cpp index 1e3d704362..edf9bbe658 100644 --- a/dom/html/HTMLAllCollection.cpp +++ b/dom/html/HTMLAllCollection.cpp @@ -114,7 +114,7 @@ HTMLAllCollection::GetDocumentAllList(const nsAString& aID) return docAllList; } - nsCOMPtr id = do_GetAtom(aID); + nsCOMPtr id = NS_Atomize(aID); RefPtr docAllList = new nsContentList(mDocument, DocAllResultMatch, nullptr, nullptr, true, id); mNamedMap.Put(aID, docAllList); diff --git a/dom/html/HTMLBodyElement.cpp b/dom/html/HTMLBodyElement.cpp index 104b6d091b..f17b56560a 100644 --- a/dom/html/HTMLBodyElement.cpp +++ b/dom/html/HTMLBodyElement.cpp @@ -410,7 +410,7 @@ HTMLBodyElement::WalkContentStyleRules(nsRuleWalker* aRuleWalker) { nsGenericHTMLElement::WalkContentStyleRules(aRuleWalker); - if (!mContentStyleRule && IsInDoc()) { + if (!mContentStyleRule && IsInUncomposedDoc()) { // XXXbz should this use OwnerDoc() or GetComposedDoc()? // sXBL/XBL2 issue! mContentStyleRule = new BodyRule(this); diff --git a/dom/html/HTMLButtonElement.cpp b/dom/html/HTMLButtonElement.cpp index 2c649a20ee..229af9c483 100644 --- a/dom/html/HTMLButtonElement.cpp +++ b/dom/html/HTMLButtonElement.cpp @@ -258,7 +258,7 @@ HTMLButtonElement::PostHandleEvent(EventChainPostVisitor& aVisitor) // DOMActive event should be trusted since the activation is actually // occurred even if the cause is an untrusted click event. InternalUIEvent actEvent(true, eLegacyDOMActivate, mouseEvent); - actEvent.detail = 1; + actEvent.mDetail = 1; nsCOMPtr shell = aVisitor.mPresContext->GetPresShell(); if (shell) { diff --git a/dom/html/HTMLCanvasElement.cpp b/dom/html/HTMLCanvasElement.cpp index 1fa5deaf2c..0c7fa0c4c5 100644 --- a/dom/html/HTMLCanvasElement.cpp +++ b/dom/html/HTMLCanvasElement.cpp @@ -18,6 +18,7 @@ #include "mozilla/dom/CanvasRenderingContext2D.h" #include "mozilla/dom/File.h" #include "mozilla/dom/HTMLCanvasElementBinding.h" +#include "mozilla/dom/MediaStreamTrack.h" #include "mozilla/dom/MouseEvent.h" #include "mozilla/dom/OffscreenCanvas.h" #include "mozilla/EventDispatcher.h" @@ -654,7 +655,7 @@ HTMLCanvasElement::CaptureStream(const Optional& aFrameRate, return nullptr; } - nsIDOMWindow* window = OwnerDoc()->GetInnerWindow(); + nsPIDOMWindow* window = OwnerDoc()->GetInnerWindow(); if (!window) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; @@ -672,17 +673,17 @@ HTMLCanvasElement::CaptureStream(const Optional& aFrameRate, return nullptr; } - RefPtr principal = NodePrincipal(); - stream->CombineWithPrincipal(principal); - TrackID videoTrackId = 1; - nsresult rv = stream->Init(aFrameRate, videoTrackId); + nsCOMPtr principal = NodePrincipal(); + nsresult rv = + stream->Init(aFrameRate, videoTrackId, principal); if (NS_FAILED(rv)) { aRv.Throw(rv); return nullptr; } - stream->CreateOwnDOMTrack(videoTrackId, MediaSegment::VIDEO, nsString()); + stream->CreateDOMTrack(videoTrackId, MediaSegment::VIDEO, + new BasicUnstoppableTrackSource(principal)); rv = RegisterFrameCaptureListener(stream->FrameCaptureListener()); if (NS_FAILED(rv)) { diff --git a/dom/html/HTMLExtAppElement.cpp b/dom/html/HTMLExtAppElement.cpp deleted file mode 100644 index 3291d612ac..0000000000 --- a/dom/html/HTMLExtAppElement.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "mozilla/dom/HTMLExtAppElement.h" -#include "mozilla/dom/HTMLUnknownElement.h" -#include "mozilla/dom/HTMLExtAppElementBinding.h" -#include "mozilla/dom/ExternalAppEvent.h" - -#include "nsGkAtoms.h" -#include "nsIAtom.h" -#include "nsIPermissionManager.h" -#include "nsStyleConsts.h" -#include "nsRuleData.h" - -nsGenericHTMLElement* -NS_NewHTMLExtAppElement(already_AddRefed&& aNodeInfo, - mozilla::dom::FromParser aFromParser) { - // Return HTMLUnknownElement if the document doesn't have the 'external-app' permission. - nsCOMPtr permissionManager = - mozilla::services::GetPermissionManager(); - RefPtr ni = aNodeInfo; - nsIPrincipal* principal = ni->GetDocument()->NodePrincipal(); - - already_AddRefed aarni = ni.forget(); - - if (!permissionManager) { - return new mozilla::dom::HTMLUnknownElement(aarni); - } - - uint32_t perm = nsIPermissionManager::UNKNOWN_ACTION; - permissionManager->TestExactPermissionFromPrincipal(principal, - "external-app", - &perm); - if (perm != nsIPermissionManager::ALLOW_ACTION) { - return new mozilla::dom::HTMLUnknownElement(aarni); - } - - return new mozilla::dom::HTMLExtAppElement(aarni); -} - -namespace mozilla { -namespace dom { - -HTMLExtAppElement::HTMLExtAppElement(already_AddRefed& aNodeInfo) - : nsGenericHTMLElement(aNodeInfo) -{ - mCustomEventDispatch = new nsCustomEventDispatch(this); - mCustomPropertyBag = new nsCustomPropertyBag(); - - nsCOMPtr app = - do_CreateInstance(NS_EXTERNALAPP_CONTRACTID); - if (app) { - nsresult rv = app->Init(OwnerDoc()->GetInnerWindow(), mCustomPropertyBag, mCustomEventDispatch); - if (NS_SUCCEEDED(rv)) { - mApp = app; - } - } -} - -HTMLExtAppElement::~HTMLExtAppElement() -{ - mCustomEventDispatch->ClearEventTarget(); -} - -void -HTMLExtAppElement::GetCustomProperty(const nsAString& aName, nsString& aReturn) -{ - mCustomPropertyBag->GetCustomProperty(aName, aReturn); -} - -void -HTMLExtAppElement::PostMessage(const nsAString& aMessage, ErrorResult& aRv) -{ - if (!mApp) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return; - } - - aRv = mApp->PostMessage(aMessage); -} - -NS_IMPL_ADDREF_INHERITED(HTMLExtAppElement, Element) -NS_IMPL_RELEASE_INHERITED(HTMLExtAppElement, Element) - -NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLExtAppElement) - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLExtAppElement, - nsGenericHTMLElement) -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLExtAppElement, - nsGenericHTMLElement) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -// QueryInterface implementation for HTMLExtAppElement -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLExtAppElement) -NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement) - -NS_IMPL_ELEMENT_CLONE(HTMLExtAppElement) - -JSObject* -HTMLExtAppElement::WrapNode(JSContext *aCx, JS::Handle aGivenProto) -{ - return HTMLExtAppElementBinding::Wrap(aCx, this, aGivenProto); -} - -} // namespace dom -} // namespace mozilla - -NS_IMPL_ISUPPORTS(nsCustomPropertyBag, nsICustomPropertyBag) - -nsCustomPropertyBag::nsCustomPropertyBag() -{ -} - -nsCustomPropertyBag::~nsCustomPropertyBag() -{ -} - -NS_IMETHODIMP -nsCustomPropertyBag::SetProperty(const nsAString& aName, const nsAString& aValue) -{ - mBag.Put(nsString(aName), new nsString(aValue)); - return NS_OK; -} - -NS_IMETHODIMP -nsCustomPropertyBag::RemoveProperty(const nsAString& aName) -{ - mBag.Remove(nsString(aName)); - return NS_OK; -} - -void -nsCustomPropertyBag::GetCustomProperty(const nsAString& aName, nsString& aReturn) -{ - nsString* value; - if (!mBag.Get(nsString(aName), &value)) { - aReturn.Truncate(); - return; - } - - MOZ_ASSERT(value); - aReturn.Assign(*value); -} - -NS_IMPL_ISUPPORTS(nsCustomEventDispatch, nsICustomEventDispatch) - -nsCustomEventDispatch::nsCustomEventDispatch(mozilla::dom::EventTarget* aEventTarget) - : mEventTarget(aEventTarget) -{ - MOZ_ASSERT(mEventTarget); -} - -void -nsCustomEventDispatch::ClearEventTarget() -{ - mEventTarget = nullptr; -} - -nsCustomEventDispatch::~nsCustomEventDispatch() -{ -} - -NS_IMETHODIMP -nsCustomEventDispatch::DispatchExternalEvent(const nsAString& value) -{ - if (!mEventTarget) { - return NS_OK; - } - - mozilla::dom::ExternalAppEventInit init; - init.mData = value; - - RefPtr event = - mozilla::dom::ExternalAppEvent::Constructor(mEventTarget, - NS_LITERAL_STRING("externalappevent"), - init); - - bool defaultActionEnabled; - return mEventTarget->DispatchEvent(event, &defaultActionEnabled); -} diff --git a/dom/html/HTMLExtAppElement.h b/dom/html/HTMLExtAppElement.h deleted file mode 100644 index 572bd47b60..0000000000 --- a/dom/html/HTMLExtAppElement.h +++ /dev/null @@ -1,79 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_HTMLExtAppElement_h -#define mozilla_dom_HTMLExtAppElement_h - -#include "nsGenericHTMLElement.h" -#include "nsIExternalApplication.h" - -class nsCustomEventDispatch; -class nsCustomPropertyBag; - -namespace mozilla { -namespace dom { - -class HTMLExtAppElement final : public nsGenericHTMLElement -{ -public: - explicit HTMLExtAppElement(already_AddRefed& aNodeInfo); - - // nsISupports - NS_DECL_ISUPPORTS_INHERITED - - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLExtAppElement, - nsGenericHTMLElement) - - virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; - - void GetCustomProperty(const nsAString& aName, nsString& aReturn); - void PostMessage(const nsAString& aMessage, ErrorResult& aRv); - -protected: - virtual ~HTMLExtAppElement(); - - virtual JSObject* WrapNode(JSContext *aCx, JS::Handle aGivenProto) override; - - RefPtr mCustomEventDispatch; - RefPtr mCustomPropertyBag; - nsCOMPtr mApp; -}; - -} // namespace dom -} // namespace mozilla - -class nsCustomEventDispatch final : public nsICustomEventDispatch -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSICUSTOMEVENTDISPATCH - - explicit nsCustomEventDispatch(mozilla::dom::EventTarget* aEventTarget); - void ClearEventTarget(); - -private: - ~nsCustomEventDispatch(); - - // Weak pointer, this object is owned by the event target. - mozilla::dom::EventTarget* mEventTarget; -}; - -class nsCustomPropertyBag final : public nsICustomPropertyBag -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSICUSTOMPROPERTYBAG - - nsCustomPropertyBag(); - void GetCustomProperty(const nsAString& aName, nsString& aReturn); - -private: - ~nsCustomPropertyBag(); - - nsClassHashtable mBag; -}; - -#endif // mozilla_dom_HTMLExtAppElement_h diff --git a/dom/html/HTMLFormElement.cpp b/dom/html/HTMLFormElement.cpp index b23223793a..189a2b83ad 100644 --- a/dom/html/HTMLFormElement.cpp +++ b/dom/html/HTMLFormElement.cpp @@ -493,7 +493,7 @@ nsresult HTMLFormElement::PreHandleEvent(EventChainPreVisitor& aVisitor) { aVisitor.mWantsWillHandleEvent = true; - if (aVisitor.mEvent->originalTarget == static_cast(this)) { + if (aVisitor.mEvent->mOriginalTarget == static_cast(this)) { uint32_t msg = aVisitor.mEvent->mMessage; if (msg == eFormSubmit) { if (mGeneratingSubmit) { @@ -526,7 +526,7 @@ HTMLFormElement::WillHandleEvent(EventChainPostVisitor& aVisitor) if ((aVisitor.mEvent->mMessage == eFormSubmit || aVisitor.mEvent->mMessage == eFormReset) && aVisitor.mEvent->mFlags.mInBubblingPhase && - aVisitor.mEvent->originalTarget != static_cast(this)) { + aVisitor.mEvent->mOriginalTarget != static_cast(this)) { aVisitor.mEvent->StopPropagation(); } return NS_OK; @@ -535,7 +535,7 @@ HTMLFormElement::WillHandleEvent(EventChainPostVisitor& aVisitor) nsresult HTMLFormElement::PostHandleEvent(EventChainPostVisitor& aVisitor) { - if (aVisitor.mEvent->originalTarget == static_cast(this)) { + if (aVisitor.mEvent->mOriginalTarget == static_cast(this)) { EventMessage msg = aVisitor.mEvent->mMessage; if (msg == eFormSubmit) { // let the form know not to defer subsequent submissions @@ -1671,7 +1671,7 @@ HTMLFormElement::GetActionURL(nsIURI** aActionURL, // Get the document to form the URL. // We'll also need it later to get the DOM window when notifying form submit // observers (bug 33203) - if (!IsInDoc()) { + if (!IsInUncomposedDoc()) { return NS_OK; // No doc means don't submit, see Bug 28988 } diff --git a/dom/html/HTMLImageElement.cpp b/dom/html/HTMLImageElement.cpp index b2257eb185..d0d4cbb490 100644 --- a/dom/html/HTMLImageElement.cpp +++ b/dom/html/HTMLImageElement.cpp @@ -484,7 +484,7 @@ HTMLImageElement::IsHTMLFocusable(bool aWithMouse, { int32_t tabIndex = TabIndex(); - if (IsInDoc()) { + if (IsInUncomposedDoc()) { nsAutoString usemap; GetUseMap(usemap); // XXXbz which document should this be using? sXBL/XBL2 issue! I diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index 18792ad5a8..3ad434d7e1 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -3442,7 +3442,7 @@ HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor) // nsIContent::PreHandleEvent doesn't reset any change we make to mCanHandle. if (mType == NS_FORM_INPUT_NUMBER && aVisitor.mEvent->IsTrusted() && - aVisitor.mEvent->originalTarget != this) { + aVisitor.mEvent->mOriginalTarget != this) { // has an anonymous descendant. If // 'input' or 'change' events are fired at that text control then we need // to do some special handling here. @@ -3452,7 +3452,7 @@ HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor) if (numberControlFrame) { textControl = numberControlFrame->GetAnonTextControl(); } - if (textControl && aVisitor.mEvent->originalTarget == textControl) { + if (textControl && aVisitor.mEvent->mOriginalTarget == textControl) { if (aVisitor.mEvent->mMessage == eEditorInput) { // Propogate the anon text control's new value to our HTMLInputElement: nsAutoString value; @@ -3730,7 +3730,7 @@ HTMLInputElement::MaybeInitPickers(EventChainPostVisitor& aVisitor) // directory picker, else we open the file picker. FilePickerType type = FILE_PICKER_FILE; nsCOMPtr target = - do_QueryInterface(aVisitor.mEvent->originalTarget); + do_QueryInterface(aVisitor.mEvent->mOriginalTarget); if (target && target->GetParent() == this && target->IsRootOfNativeAnonymousSubtree() && @@ -3797,11 +3797,11 @@ HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor) mType != NS_FORM_INPUT_NUMBER) { WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent(); if (mouseEvent && mouseEvent->IsLeftClickEvent() && - !ShouldPreventDOMActivateDispatch(aVisitor.mEvent->originalTarget)) { + !ShouldPreventDOMActivateDispatch(aVisitor.mEvent->mOriginalTarget)) { // DOMActive event should be trusted since the activation is actually // occurred even if the cause is an untrusted click event. InternalUIEvent actEvent(true, eLegacyDOMActivate, mouseEvent); - actEvent.detail = 1; + actEvent.mDetail = 1; nsCOMPtr shell = aVisitor.mPresContext->GetPresShell(); if (shell) { @@ -3922,7 +3922,7 @@ HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor) // just because we raised a window. nsIFocusManager* fm = nsFocusManager::GetFocusManager(); if (fm && IsSingleLineTextControl(false) && - !aVisitor.mEvent->AsFocusEvent()->fromRaise && + !aVisitor.mEvent->AsFocusEvent()->mFromRaise && SelectTextFieldOnFocus()) { nsIDocument* document = GetComposedDoc(); if (document) { @@ -4262,7 +4262,7 @@ HTMLInputElement::PostHandleEventForRangeThumb(EventChainPostVisitor& aVisitor) CancelRangeThumbDrag(); } } else { - if (aVisitor.mEvent->AsTouchEvent()->touches.Length() == 1) { + if (aVisitor.mEvent->AsTouchEvent()->mTouches.Length() == 1) { StartRangeThumbDrag(inputEvent); } else if (mIsDraggingRange) { CancelRangeThumbDrag(); @@ -5999,7 +5999,7 @@ HTMLInputElement::AddedToRadioGroup() { // If the element is neither in a form nor a document, there is no group so we // should just stop here. - if (!mForm && !IsInDoc()) { + if (!mForm && !IsInUncomposedDoc()) { return; } diff --git a/dom/html/HTMLLabelElement.cpp b/dom/html/HTMLLabelElement.cpp index 6af96b080d..87c7f43f19 100644 --- a/dom/html/HTMLLabelElement.cpp +++ b/dom/html/HTMLLabelElement.cpp @@ -112,7 +112,7 @@ HTMLLabelElement::PostHandleEvent(EventChainPostVisitor& aVisitor) return NS_OK; } - nsCOMPtr target = do_QueryInterface(aVisitor.mEvent->target); + nsCOMPtr target = do_QueryInterface(aVisitor.mEvent->mTarget); if (InInteractiveHTMLContent(target, this)) { return NS_OK; } @@ -128,7 +128,7 @@ HTMLLabelElement::PostHandleEvent(EventChainPostVisitor& aVisitor) // We reset the mouse-down point on every event because there is // no guarantee we will reach the eMouseClick code below. LayoutDeviceIntPoint* curPoint = - new LayoutDeviceIntPoint(mouseEvent->refPoint); + new LayoutDeviceIntPoint(mouseEvent->mRefPoint); SetProperty(nsGkAtoms::labelMouseDownPtProperty, static_cast(curPoint), nsINode::DeleteProperty); @@ -146,7 +146,7 @@ HTMLLabelElement::PostHandleEvent(EventChainPostVisitor& aVisitor) LayoutDeviceIntPoint dragDistance = *mouseDownPoint; DeleteProperty(nsGkAtoms::labelMouseDownPtProperty); - dragDistance -= mouseEvent->refPoint; + dragDistance -= mouseEvent->mRefPoint; const int CLICK_DISTANCE = 2; dragSelect = dragDistance.x > CLICK_DISTANCE || dragDistance.x < -CLICK_DISTANCE || diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index d3786922c2..97d66c692d 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -1856,11 +1856,125 @@ NS_IMETHODIMP HTMLMediaElement::SetMuted(bool aMuted) return NS_OK; } +class HTMLMediaElement::CaptureStreamTrackSource : + public MediaStreamTrackSource, + public DecoderPrincipalChangeObserver +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CaptureStreamTrackSource, + MediaStreamTrackSource) + + explicit CaptureStreamTrackSource(HTMLMediaElement* aElement) + : MediaStreamTrackSource(nsCOMPtr(aElement->GetCurrentPrincipal()), + true, + nsString()) + , mElement(aElement) + { + MOZ_ASSERT(mElement); + mElement->AddDecoderPrincipalChangeObserver(this); + } + + MediaSourceEnum GetMediaSource() const override + { + return MediaSourceEnum::Other; + } + + CORSMode GetCORSMode() const override + { + return mElement->GetCORSMode(); + } + + already_AddRefed + ApplyConstraints(nsPIDOMWindow* aWindow, + const dom::MediaTrackConstraints& aConstraints, + ErrorResult &aRv) override + { + NS_ERROR("ApplyConstraints not implemented for media element capture"); + return nullptr; + } + + void Stop() override + { + NS_ERROR("We're reporting remote=true to not be stoppable. " + "Stop() should not be called."); + } + + void NotifyDecoderPrincipalChanged() override + { + nsCOMPtr newPrincipal = mElement->GetCurrentPrincipal(); + if (nsContentUtils::CombineResourcePrincipals(&mPrincipal, newPrincipal)) { + PrincipalChanged(); + } + } + +protected: + virtual ~CaptureStreamTrackSource() + { + if (mElement) { + DebugOnly res = mElement->RemoveDecoderPrincipalChangeObserver(this); + NS_ASSERTION(res, "Removing decoder principal changed observer failed. " + "Had it already been removed?"); + } + } + + RefPtr mElement; +}; + +NS_IMPL_ADDREF_INHERITED(HTMLMediaElement::CaptureStreamTrackSource, + MediaStreamTrackSource) +NS_IMPL_RELEASE_INHERITED(HTMLMediaElement::CaptureStreamTrackSource, + MediaStreamTrackSource) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLMediaElement::CaptureStreamTrackSource) +NS_INTERFACE_MAP_END_INHERITING(MediaStreamTrackSource) +NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLMediaElement::CaptureStreamTrackSource, + MediaStreamTrackSource, + mElement) + +class HTMLMediaElement::CaptureStreamTrackSourceGetter : + public MediaStreamTrackSourceGetter +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CaptureStreamTrackSourceGetter, + MediaStreamTrackSourceGetter) + + explicit CaptureStreamTrackSourceGetter(HTMLMediaElement* aElement) + : mElement(aElement) {} + + already_AddRefed + GetMediaStreamTrackSource(TrackID aInputTrackID) override + { + // We can return a new source each time here, even for different streams, + // since the sources don't keep any internal state and all of them call + // through to the same HTMLMediaElement. + // If this changes (after implementing Stop()?) we'll have to ensure we + // return the same source for all requests to the same TrackID, and only + // have one getter. + return do_AddRef(new CaptureStreamTrackSource(mElement)); + } + +protected: + virtual ~CaptureStreamTrackSourceGetter() {} + + RefPtr mElement; +}; + +NS_IMPL_ADDREF_INHERITED(HTMLMediaElement::CaptureStreamTrackSourceGetter, + MediaStreamTrackSourceGetter) +NS_IMPL_RELEASE_INHERITED(HTMLMediaElement::CaptureStreamTrackSourceGetter, + MediaStreamTrackSourceGetter) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLMediaElement::CaptureStreamTrackSourceGetter) +NS_INTERFACE_MAP_END_INHERITING(MediaStreamTrackSourceGetter) +NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLMediaElement::CaptureStreamTrackSourceGetter, + MediaStreamTrackSourceGetter, + mElement) + already_AddRefed HTMLMediaElement::CaptureStreamInternal(bool aFinishWhenEnded, MediaStreamGraph* aGraph) { - nsIDOMWindow* window = OwnerDoc()->GetInnerWindow(); + nsPIDOMWindow* window = OwnerDoc()->GetInnerWindow(); if (!window) { return nullptr; } @@ -1878,10 +1992,8 @@ HTMLMediaElement::CaptureStreamInternal(bool aFinishWhenEnded, } OutputMediaStream* out = mOutputStreams.AppendElement(); - out->mStream = DOMMediaStream::CreateTrackUnionStream(window, aGraph); - RefPtr principal = GetCurrentPrincipal(); - out->mStream->CombineWithPrincipal(principal); - out->mStream->SetCORSMode(mCORSMode); + MediaStreamTrackSourceGetter* getter = new CaptureStreamTrackSourceGetter(this); + out->mStream = DOMMediaStream::CreateTrackUnionStream(window, aGraph, getter); out->mFinishWhenEnded = aFinishWhenEnded; mAudioCaptured = true; @@ -1892,11 +2004,17 @@ HTMLMediaElement::CaptureStreamInternal(bool aFinishWhenEnded, // Expose the tracks to JS directly. if (HasAudio()) { TrackID audioTrackId = mMediaInfo.mAudio.mTrackId; - out->mStream->CreateOwnDOMTrack(audioTrackId, MediaSegment::AUDIO, nsString()); + RefPtr trackSource = + getter->GetMediaStreamTrackSource(audioTrackId); + out->mStream->CreateDOMTrack(audioTrackId, MediaSegment::AUDIO, + trackSource); } if (HasVideo()) { TrackID videoTrackId = mMediaInfo.mVideo.mTrackId; - out->mStream->CreateOwnDOMTrack(videoTrackId, MediaSegment::VIDEO, nsString()); + RefPtr trackSource = + getter->GetMediaStreamTrackSource(videoTrackId); + out->mStream->CreateDOMTrack(videoTrackId, MediaSegment::VIDEO, + trackSource); } } } @@ -2111,7 +2229,6 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed& aNo mPlayingThroughTheAudioChannel(false), mDisableVideo(false), mPlayBlockedBecauseHidden(false), - mMediaStreamTrackListener(nullptr), mElementInTreeState(ELEMENT_NOT_INTREE), mHasUserInteraction(false), mFirstFrameLoaded(false), @@ -3097,12 +3214,11 @@ private: }; class HTMLMediaElement::MediaStreamTracksAvailableCallback: - public DOMMediaStream::OnTracksAvailableCallback + public OnTracksAvailableCallback { public: explicit MediaStreamTracksAvailableCallback(HTMLMediaElement* aElement): - DOMMediaStream::OnTracksAvailableCallback(), - mElement(aElement) + OnTracksAvailableCallback(), mElement(aElement) {} virtual void NotifyTracksAvailable(DOMMediaStream* aStream) { @@ -3135,8 +3251,6 @@ public: } protected: - ~MediaStreamTrackListener() {} - HTMLMediaElement* const mElement; }; @@ -3212,7 +3326,7 @@ void HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream) mSrcStream = aStream; - nsIDOMWindow* window = OwnerDoc()->GetInnerWindow(); + nsPIDOMWindow* window = OwnerDoc()->GetInnerWindow(); if (!window) { return; } @@ -3236,6 +3350,9 @@ void HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream) mMediaStreamTrackListener = new MediaStreamTrackListener(this); mSrcStream->RegisterTrackListener(mMediaStreamTrackListener); + mSrcStream->AddPrincipalChangeObserver(this); + mSrcStreamVideoPrincipal = mSrcStream->GetVideoPrincipal(); + ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE); ChangeDelayLoadStatus(false); CheckAutoplayDataReady(); @@ -3261,6 +3378,9 @@ void HTMLMediaElement::EndSrcMediaStreamPlayback() mSrcStream->UnregisterTrackListener(mMediaStreamTrackListener); mMediaStreamTrackListener = nullptr; + mSrcStream->RemovePrincipalChangeObserver(this); + mSrcStreamVideoPrincipal = nullptr; + mSrcStream = nullptr; } @@ -3418,15 +3538,7 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo, } } - // If this element had a video track, but consists only of an audio track now, - // delete the VideoFrameContainer. This happens when the src is changed to an - // audio only file. - // Else update its dimensions. - if (!aInfo->HasVideo()) { - ResetState(); - } else { - mWatchManager.ManualNotify(&HTMLMediaElement::UpdateReadyStateInternal); - } + mWatchManager.ManualNotify(&HTMLMediaElement::UpdateReadyStateInternal); if (IsVideo() && aInfo->HasVideo()) { // We are a video element playing video so update the screen wakelock @@ -4043,6 +4155,66 @@ VideoFrameContainer* HTMLMediaElement::GetVideoFrameContainer() return mVideoFrameContainer; } +void +HTMLMediaElement::PrincipalChanged(DOMMediaStream* aStream) +{ + LOG(LogLevel::Info, ("HTMLMediaElement %p Stream principal changed.", this)); + nsContentUtils::CombineResourcePrincipals(&mSrcStreamVideoPrincipal, + aStream->GetVideoPrincipal()); + + LOG(LogLevel::Debug, ("HTMLMediaElement %p Stream video principal changed to " + "%p. Waiting for it to reach VideoFrameContainer before " + "setting.", this, aStream->GetVideoPrincipal())); + if (mVideoFrameContainer) { + UpdateSrcStreamVideoPrincipal(mVideoFrameContainer->GetLastPrincipalHandle()); + } +} + +void +HTMLMediaElement::UpdateSrcStreamVideoPrincipal(const PrincipalHandle& aPrincipalHandle) +{ + nsTArray> videoTracks; + mSrcStream->GetVideoTracks(videoTracks); + + PrincipalHandle handle(aPrincipalHandle); + bool matchesTrackPrincipal = false; + for (const RefPtr& track : videoTracks) { + if (PrincipalHandleMatches(handle, + track->GetPrincipal()) && + !track->Ended()) { + // When the PrincipalHandle for the VideoFrameContainer changes to that of + // a track in mSrcStream we know that a removed track was displayed but + // is no longer so. + matchesTrackPrincipal = true; + LOG(LogLevel::Debug, ("HTMLMediaElement %p VideoFrameContainer's " + "PrincipalHandle matches track %p. That's all we " + "need.", this, track.get())); + break; + } + } + + if (matchesTrackPrincipal) { + mSrcStreamVideoPrincipal = mSrcStream->GetVideoPrincipal(); + } +} + +void +HTMLMediaElement::PrincipalHandleChangedForVideoFrameContainer(VideoFrameContainer* aContainer, + const PrincipalHandle& aNewPrincipalHandle) +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (!mSrcStream) { + return; + } + + LOG(LogLevel::Debug, ("HTMLMediaElement %p PrincipalHandle changed in " + "VideoFrameContainer.", + this)); + + UpdateSrcStreamVideoPrincipal(aNewPrincipalHandle); +} + nsresult HTMLMediaElement::DispatchEvent(const nsAString& aName) { LOG_EVENT(LogLevel::Debug, ("%p Dispatching event %s", this, @@ -4136,7 +4308,19 @@ already_AddRefed HTMLMediaElement::GetCurrentPrincipal() return mDecoder->GetCurrentPrincipal(); } if (mSrcStream) { - RefPtr principal = mSrcStream->GetPrincipal(); + nsCOMPtr principal = mSrcStream->GetPrincipal(); + return principal.forget(); + } + return nullptr; +} + +already_AddRefed HTMLMediaElement::GetCurrentVideoPrincipal() +{ + if (mDecoder) { + return mDecoder->GetCurrentPrincipal(); + } + if (mSrcStream) { + nsCOMPtr principal = mSrcStreamVideoPrincipal; return principal.forget(); } return nullptr; @@ -4148,13 +4332,22 @@ void HTMLMediaElement::NotifyDecoderPrincipalChanged() mDecoder->UpdateSameOriginStatus(!principal || IsCORSSameOrigin()); - for (uint32_t i = 0; i < mOutputStreams.Length(); ++i) { - OutputMediaStream* ms = &mOutputStreams[i]; - ms->mStream->SetCORSMode(mCORSMode); - ms->mStream->CombineWithPrincipal(principal); + for (DecoderPrincipalChangeObserver* observer : + mDecoderPrincipalChangeObservers) { + observer->NotifyDecoderPrincipalChanged(); } } +void HTMLMediaElement::AddDecoderPrincipalChangeObserver(DecoderPrincipalChangeObserver* aObserver) +{ + mDecoderPrincipalChangeObservers.AppendElement(aObserver); +} + +bool HTMLMediaElement::RemoveDecoderPrincipalChangeObserver(DecoderPrincipalChangeObserver* aObserver) +{ + return mDecoderPrincipalChangeObservers.RemoveElement(aObserver); +} + void HTMLMediaElement::UpdateMediaSize(const nsIntSize& aSize) { if (IsVideo() && mReadyState != HAVE_NOTHING && diff --git a/dom/html/HTMLMediaElement.h b/dom/html/HTMLMediaElement.h index f45d025786..9b60447a10 100644 --- a/dom/html/HTMLMediaElement.h +++ b/dom/html/HTMLMediaElement.h @@ -22,6 +22,7 @@ #include "MediaDecoder.h" #include "mozilla/StateWatching.h" #include "nsGkAtoms.h" +#include "PrincipalChangeObserver.h" // X.h on Linux #defines CurrentTime as 0L, so we have to #undef it here. #ifdef CurrentTime @@ -75,6 +76,7 @@ class HTMLMediaElement : public nsGenericHTMLElement, public nsIObserver, public MediaDecoderOwner, public nsIAudioChannelAgentCallback, + public PrincipalChangeObserver, public SupportsWeakPtr { friend AutoNotifyAudioChannelAgent; @@ -224,9 +226,19 @@ public: // Called by the media decoder and the video frame to get the // ImageContainer containing the video data. - B2G_ACL_EXPORT virtual VideoFrameContainer* GetVideoFrameContainer() final override; + virtual VideoFrameContainer* GetVideoFrameContainer() final override; layers::ImageContainer* GetImageContainer(); + // From PrincipalChangeObserver. + void PrincipalChanged(DOMMediaStream* aStream) override; + + void UpdateSrcStreamVideoPrincipal(const PrincipalHandle& aPrincipalHandle); + + // Called after the MediaStream we're playing rendered a frame to aContainer + // with a different principalHandle than the previous frame. + void PrincipalHandleChangedForVideoFrameContainer(VideoFrameContainer* aContainer, + const PrincipalHandle& aNewPrincipalHandle); + // Dispatch events virtual nsresult DispatchAsyncEvent(const nsAString& aName) final override; @@ -265,8 +277,41 @@ public: // Returns null if nothing is playing. already_AddRefed GetCurrentPrincipal(); + // Principal of the currently playing video resource. Anything accessing the + // image container of this element must have a principal that subsumes this + // principal. If there are no live video tracks but content has been rendered + // to the image container, we return the last video principal we had. Should + // the image container be empty with no live video tracks, we return nullptr. + already_AddRefed GetCurrentVideoPrincipal(); + // called to notify that the principal of the decoder's media resource has changed. - virtual void NotifyDecoderPrincipalChanged() final override; + void NotifyDecoderPrincipalChanged() final override; + + // An interface for observing principal changes on the media elements + // MediaDecoder. This will also be notified if the active CORSMode changes. + class DecoderPrincipalChangeObserver + { + public: + virtual void NotifyDecoderPrincipalChanged() = 0; + }; + + /** + * Add a DecoderPrincipalChangeObserver to this media element. + * + * Ownership of the DecoderPrincipalChangeObserver remains with the caller, + * and it's the caller's responsibility to remove the observer before it dies. + */ + void AddDecoderPrincipalChangeObserver(DecoderPrincipalChangeObserver* aObserver); + + /** + * Remove an added DecoderPrincipalChangeObserver from this media element. + * + * Returns true if it was successfully removed. + */ + bool RemoveDecoderPrincipalChangeObserver(DecoderPrincipalChangeObserver* aObserver); + + class CaptureStreamTrackSource; + class CaptureStreamTrackSourceGetter; // Update the visual size of the media. Called from the decoder on the // main thread when/if the size changes. @@ -1109,6 +1154,10 @@ protected: // At most one of mDecoder and mSrcStream can be non-null. RefPtr mDecoder; + // Observers listening to changes to the mDecoder principal. + // Used by streams captured from this element. + nsTArray mDecoderPrincipalChangeObservers; + // State-watching manager. WatchManager mWatchManager; @@ -1451,7 +1500,11 @@ protected: RefPtr mVideoTrackList; - RefPtr mMediaStreamTrackListener; + nsAutoPtr mMediaStreamTrackListener; + + // The principal guarding mVideoFrameContainer access when playing a + // MediaStream. + nsCOMPtr mSrcStreamVideoPrincipal; enum ElementInTreeState { // The MediaElement is not in the DOM tree now. diff --git a/dom/html/HTMLMenuItemElement.cpp b/dom/html/HTMLMenuItemElement.cpp index a40645c4c2..e30b06b297 100644 --- a/dom/html/HTMLMenuItemElement.cpp +++ b/dom/html/HTMLMenuItemElement.cpp @@ -386,7 +386,7 @@ HTMLMenuItemElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, if ((aName == nsGkAtoms::radiogroup || aName == nsGkAtoms::type) && mType == CMD_TYPE_RADIO && !mParserCreating) { - if (IsInDoc() && GetParent()) { + if (IsInUncomposedDoc() && GetParent()) { AddedToRadioGroup(); } } diff --git a/dom/html/HTMLScriptElement.cpp b/dom/html/HTMLScriptElement.cpp index ee47ad8640..6c8b8bfd5e 100644 --- a/dom/html/HTMLScriptElement.cpp +++ b/dom/html/HTMLScriptElement.cpp @@ -62,7 +62,7 @@ HTMLScriptElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, aCompileEventHandlers); NS_ENSURE_SUCCESS(rv, rv); - if (GetCrossShadowCurrentDoc()) { + if (GetComposedDoc()) { MaybeProcessScript(); } diff --git a/dom/html/HTMLSharedElement.cpp b/dom/html/HTMLSharedElement.cpp index a2c6fc6d1e..ce0ba8b9a4 100644 --- a/dom/html/HTMLSharedElement.cpp +++ b/dom/html/HTMLSharedElement.cpp @@ -226,7 +226,7 @@ HTMLSharedElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, // similarly need to change the base target. if (mNodeInfo->Equals(nsGkAtoms::base) && aNameSpaceID == kNameSpaceID_None && - IsInDoc()) { + IsInUncomposedDoc()) { if (aName == nsGkAtoms::href) { SetBaseURIUsingFirstBaseWithHref(GetUncomposedDoc(), this); } else if (aName == nsGkAtoms::target) { @@ -249,7 +249,7 @@ HTMLSharedElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName, // find the new one. Similar for target. if (mNodeInfo->Equals(nsGkAtoms::base) && aNameSpaceID == kNameSpaceID_None && - IsInDoc()) { + IsInUncomposedDoc()) { if (aName == nsGkAtoms::href) { SetBaseURIUsingFirstBaseWithHref(GetUncomposedDoc(), nullptr); } else if (aName == nsGkAtoms::target) { diff --git a/dom/html/moz.build b/dom/html/moz.build index 603ac274d6..038a4f8dd2 100644 --- a/dom/html/moz.build +++ b/dom/html/moz.build @@ -58,7 +58,6 @@ EXPORTS.mozilla.dom += [ 'HTMLDataListElement.h', 'HTMLDetailsElement.h', 'HTMLDivElement.h', - 'HTMLExtAppElement.h', 'HTMLFieldSetElement.h', 'HTMLFontElement.h', 'HTMLFormControlsCollection.h', @@ -138,7 +137,6 @@ UNIFIED_SOURCES += [ 'HTMLDetailsElement.cpp', 'HTMLDivElement.cpp', 'HTMLElement.cpp', - 'HTMLExtAppElement.cpp', 'HTMLFieldSetElement.cpp', 'HTMLFontElement.cpp', 'HTMLFormControlsCollection.cpp', diff --git a/dom/html/nsDOMStringMap.cpp b/dom/html/nsDOMStringMap.cpp index 831fb83ef0..a476ab1e86 100644 --- a/dom/html/nsDOMStringMap.cpp +++ b/dom/html/nsDOMStringMap.cpp @@ -112,7 +112,7 @@ nsDOMStringMap::NamedSetter(const nsAString& aProp, return; } - nsCOMPtr attrAtom = do_GetAtom(attr); + nsCOMPtr attrAtom = NS_Atomize(attr); MOZ_ASSERT(attrAtom, "Should be infallible"); res = mElement->SetAttr(kNameSpaceID_None, attrAtom, aValue, true); @@ -136,7 +136,7 @@ nsDOMStringMap::NamedDeleter(const nsAString& aProp, bool& found) return; } - nsCOMPtr attrAtom = do_GetAtom(attr); + nsCOMPtr attrAtom = NS_Atomize(attr); MOZ_ASSERT(attrAtom, "Should be infallible"); found = mElement->HasAttr(kNameSpaceID_None, attrAtom); diff --git a/dom/html/nsFormSubmission.cpp b/dom/html/nsFormSubmission.cpp index 8c1b8c815c..7e5e36e7a5 100644 --- a/dom/html/nsFormSubmission.cpp +++ b/dom/html/nsFormSubmission.cpp @@ -40,7 +40,9 @@ #include "mozilla/dom/File.h" using namespace mozilla; +using mozilla::dom::Blob; using mozilla::dom::EncodingUtils; +using mozilla::dom::File; static void SendJSWarning(nsIDocument* aDocument, diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp index bf80b69f03..225ceb298c 100644 --- a/dom/html/nsGenericHTMLElement.cpp +++ b/dom/html/nsGenericHTMLElement.cpp @@ -573,7 +573,7 @@ nsGenericHTMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, void nsGenericHTMLElement::UnbindFromTree(bool aDeep, bool aNullParent) { - if (IsInDoc()) { + if (IsInUncomposedDoc()) { UnregAccessKey(); } @@ -2283,7 +2283,7 @@ nsGenericHTMLFormElement::PreHandleEvent(EventChainPreVisitor& aVisitor) // this parent file control -- leave focus on the child. nsIFormControlFrame* formControlFrame = GetFormControlFrame(true); if (formControlFrame && - aVisitor.mEvent->originalTarget == static_cast(this)) + aVisitor.mEvent->mOriginalTarget == static_cast(this)) formControlFrame->SetFocus(true, true); break; } @@ -2416,7 +2416,7 @@ nsGenericHTMLFormElement::AddFormIdObserver() GetAttr(kNameSpaceID_None, nsGkAtoms::form, formId); NS_ASSERTION(!formId.IsEmpty(), "@form value should not be the empty string!"); - nsCOMPtr atom = do_GetAtom(formId); + nsCOMPtr atom = NS_Atomize(formId); return doc->AddIDTargetObserver(atom, FormIdUpdated, this, false); } @@ -2445,7 +2445,7 @@ nsGenericHTMLFormElement::RemoveFormIdObserver() GetAttr(kNameSpaceID_None, nsGkAtoms::form, formId); NS_ASSERTION(!formId.IsEmpty(), "@form value should not be the empty string!"); - nsCOMPtr atom = do_GetAtom(formId); + nsCOMPtr atom = NS_Atomize(formId); doc->RemoveIDTargetObserver(atom, FormIdUpdated, this, false); } diff --git a/dom/html/nsGenericHTMLElement.h b/dom/html/nsGenericHTMLElement.h index b035c9ace8..82481c83e5 100644 --- a/dom/html/nsGenericHTMLElement.h +++ b/dom/html/nsGenericHTMLElement.h @@ -981,14 +981,14 @@ protected: */ void AddToNameTable(nsIAtom* aName) { NS_ASSERTION(HasName(), "Node doesn't have name?"); - nsIDocument* doc = GetCurrentDoc(); + nsIDocument* doc = GetUncomposedDoc(); if (doc && !IsInAnonymousSubtree()) { doc->AddToNameTable(this, aName); } } void RemoveFromNameTable() { if (HasName()) { - nsIDocument* doc = GetCurrentDoc(); + nsIDocument* doc = GetUncomposedDoc(); if (doc) { doc->RemoveFromNameTable(this, GetParsedAttr(nsGkAtoms::name)-> GetAtomValue()); @@ -1398,7 +1398,7 @@ protected: * with the id in @form. Otherwise, it *must* be null. * * @note Callers of UpdateFormOwner have to be sure the element is in a - * document (GetCurrentDoc() != nullptr). + * document (GetUncomposedDoc() != nullptr). */ void UpdateFormOwner(bool aBindToTree, Element* aFormIdElement); @@ -1776,7 +1776,6 @@ NS_DECLARE_NS_NEW_HTML_ELEMENT(Data) NS_DECLARE_NS_NEW_HTML_ELEMENT(DataList) NS_DECLARE_NS_NEW_HTML_ELEMENT(Details) NS_DECLARE_NS_NEW_HTML_ELEMENT(Div) -NS_DECLARE_NS_NEW_HTML_ELEMENT(ExtApp) NS_DECLARE_NS_NEW_HTML_ELEMENT(FieldSet) NS_DECLARE_NS_NEW_HTML_ELEMENT(Font) NS_DECLARE_NS_NEW_HTML_ELEMENT(Form) diff --git a/dom/html/nsHTMLDocument.cpp b/dom/html/nsHTMLDocument.cpp index 4296902c87..dd6e3c4911 100644 --- a/dom/html/nsHTMLDocument.cpp +++ b/dom/html/nsHTMLDocument.cpp @@ -1107,7 +1107,7 @@ nsHTMLDocument::MatchLinks(nsIContent *aContent, int32_t aNamespaceID, nsIDocument* doc = aContent->GetUncomposedDoc(); if (doc) { - NS_ASSERTION(aContent->IsInDoc(), + NS_ASSERTION(aContent->IsInUncomposedDoc(), "This method should never be called on content nodes that " "are not in a document!"); #ifdef DEBUG @@ -1152,7 +1152,7 @@ bool nsHTMLDocument::MatchAnchors(nsIContent *aContent, int32_t aNamespaceID, nsIAtom* aAtom, void* aData) { - NS_ASSERTION(aContent->IsInDoc(), + NS_ASSERTION(aContent->IsInUncomposedDoc(), "This method should never be called on content nodes that " "are not in a document!"); #ifdef DEBUG @@ -2048,7 +2048,7 @@ static void* CreateTokens(nsINode* aRootNode, const nsString* types) ++iter; } while (iter != end && !nsContentUtils::IsHTMLWhitespace(*iter)); - tokens->AppendElement(do_GetAtom(Substring(start, iter))); + tokens->AppendElement(NS_Atomize(Substring(start, iter))); // skip whitespace while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) { diff --git a/dom/html/nsTextEditorState.cpp b/dom/html/nsTextEditorState.cpp index 2036ad3de5..d79c08eaee 100644 --- a/dom/html/nsTextEditorState.cpp +++ b/dom/html/nsTextEditorState.cpp @@ -903,7 +903,7 @@ nsTextInputListener::HandleEvent(nsIDOMEvent* aEvent) mTxtCtrlElement->IsTextArea() ? nsIWidget::NativeKeyBindingsForMultiLineEditor : nsIWidget::NativeKeyBindingsForSingleLineEditor; - nsIWidget* widget = keyEvent->widget; + nsIWidget* widget = keyEvent->mWidget; // If the event is created by chrome script, the widget is nullptr. if (!widget) { widget = mFrame->GetNearestWidget(); diff --git a/dom/html/test/mochitest.ini b/dom/html/test/mochitest.ini index 51787b8e85..7b49498dba 100644 --- a/dom/html/test/mochitest.ini +++ b/dom/html/test/mochitest.ini @@ -595,7 +595,6 @@ support-files = file_bug871161-1.html file_bug871161-2.html skip-if = buildapp == 'b2g' # bug 1129014 [test_img_complete.html] [test_viewport_resize.html] -[test_extapp.html] [test_image_clone_load.html] [test_bug1203668.html] [test_bug1166138.html] diff --git a/dom/html/test/test_extapp.html b/dom/html/test/test_extapp.html deleted file mode 100644 index b23f40bb7f..0000000000 --- a/dom/html/test/test_extapp.html +++ /dev/null @@ -1,11 +0,0 @@ - - -Test for extapp element being HTMLUnknownElement when permission is not available - - -
- diff --git a/dom/imptests/failures/editing/conformancetest/test_runtest.html.json b/dom/imptests/failures/editing/conformancetest/test_runtest.html.json index b12c23570f..7e5599199e 100644 --- a/dom/imptests/failures/editing/conformancetest/test_runtest.html.json +++ b/dom/imptests/failures/editing/conformancetest/test_runtest.html.json @@ -6416,6 +6416,7 @@ "[[\"stylewithcss\",\"true\"],[\"inserttext\",\"a\"]] \"[foobar]baz\" compare innerHTML":true, "[[\"stylewithcss\",\"false\"],[\"inserttext\",\"a\"]] \"[foobar]baz\" compare innerHTML":true, "[[\"inserttext\",\"a\"]] \"[foobar]baz\" compare innerHTML":true, + "[[\"inserttext\",\"a\"]] \"foo[bar]baz\" compare innerHTML":true, "[[\"stylewithcss\",\"true\"],[\"inserttext\",\"a\"]] \"[foobar]baz\" compare innerHTML":true, "[[\"stylewithcss\",\"false\"],[\"inserttext\",\"a\"]] \"[foobar]baz\" compare innerHTML":true, "[[\"stylewithcss\",\"true\"],[\"inserttext\",\"a\"]] \"[foobar]baz\" compare innerHTML":true, diff --git a/dom/inputmethod/HardwareKeyHandler.cpp b/dom/inputmethod/HardwareKeyHandler.cpp new file mode 100644 index 0000000000..8e20fee22e --- /dev/null +++ b/dom/inputmethod/HardwareKeyHandler.cpp @@ -0,0 +1,566 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "HardwareKeyHandler.h" +#include "mozilla/BasicEvents.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/dom/KeyboardEvent.h" +#include "mozilla/dom/TabParent.h" +#include "mozilla/EventDispatcher.h" +#include "mozilla/EventStateManager.h" +#include "mozilla/TextEvents.h" +#include "nsDeque.h" +#include "nsFocusManager.h" +#include "nsFrameLoader.h" +#include "nsIContent.h" +#include "nsIDOMHTMLDocument.h" +#include "nsIDOMHTMLElement.h" +#include "nsPIDOMWindow.h" +#include "nsPresContext.h" +#include "nsPresShell.h" + +namespace mozilla { + +using namespace dom; + +NS_IMPL_ISUPPORTS(HardwareKeyHandler, nsIHardwareKeyHandler) + +StaticRefPtr HardwareKeyHandler::sInstance; + +HardwareKeyHandler::HardwareKeyHandler() + : mInputMethodAppConnected(false) +{ +} + +HardwareKeyHandler::~HardwareKeyHandler() +{ +} + +NS_IMETHODIMP +HardwareKeyHandler::OnInputMethodAppConnected() +{ + if (NS_WARN_IF(mInputMethodAppConnected)) { + return NS_ERROR_UNEXPECTED; + } + + mInputMethodAppConnected = true; + + return NS_OK; +} + +NS_IMETHODIMP +HardwareKeyHandler::OnInputMethodAppDisconnected() +{ + if (NS_WARN_IF(!mInputMethodAppConnected)) { + return NS_ERROR_UNEXPECTED; + } + + mInputMethodAppConnected = false; + return NS_OK; +} + +NS_IMETHODIMP +HardwareKeyHandler::RegisterListener(nsIHardwareKeyEventListener* aListener) +{ + // Make sure the listener is not nullptr and there is no available + // hardwareKeyEventListener now + if (NS_WARN_IF(!aListener)) { + return NS_ERROR_NULL_POINTER; + } + + if (NS_WARN_IF(mHardwareKeyEventListener)) { + return NS_ERROR_ALREADY_INITIALIZED; + } + + mHardwareKeyEventListener = do_GetWeakReference(aListener); + + if (NS_WARN_IF(!mHardwareKeyEventListener)) { + return NS_ERROR_NULL_POINTER; + } + + return NS_OK; +} + +NS_IMETHODIMP +HardwareKeyHandler::UnregisterListener() +{ + // Clear the HardwareKeyEventListener + mHardwareKeyEventListener = nullptr; + return NS_OK; +} + +bool +HardwareKeyHandler::ForwardKeyToInputMethodApp(nsINode* aTarget, + WidgetKeyboardEvent* aEvent, + nsEventStatus* aEventStatus) +{ + MOZ_ASSERT(aTarget, "No target provided"); + MOZ_ASSERT(aEvent, "No event provided"); + + // No need to forward hardware key event to IME + // if key's defaultPrevented is true + if (aEvent->mFlags.mDefaultPrevented) { + return false; + } + + // No need to forward hardware key event to IME if IME is disabled + if (!mInputMethodAppConnected) { + return false; + } + + // No need to forward hardware key event to IME + // if this key event is generated by IME itself(from nsITextInputProcessor) + if (aEvent->mIsSynthesizedByTIP) { + return false; + } + + // No need to forward hardware key event to IME + // if the key event is handling or already handled + if (aEvent->mInputMethodAppState != WidgetKeyboardEvent::eNotHandled) { + return false; + } + + // No need to forward hardware key event to IME + // if there is no nsIHardwareKeyEventListener in use + nsCOMPtr + keyHandler(do_QueryReferent(mHardwareKeyEventListener)); + if (!keyHandler) { + return false; + } + + // Set the flags to specify the keyboard event is in forwarding phase. + aEvent->mInputMethodAppState = WidgetKeyboardEvent::eHandling; + + // For those keypress events coming after their heading keydown's reply + // already arrives, they should be dispatched directly instead of + // being stored into the event queue. Otherwise, without the heading keydown + // in the event queue, the stored keypress will never be withdrawn to be fired. + if (aEvent->mMessage == eKeyPress && mEventQueue.IsEmpty()) { + DispatchKeyPress(aTarget, *aEvent, *aEventStatus); + return true; + } + + // Push the key event into queue for reuse when its reply arrives. + KeyboardInfo* copiedInfo = + new KeyboardInfo(aTarget, + *aEvent, + aEventStatus ? *aEventStatus : nsEventStatus_eIgnore); + + // No need to forward hardware key event to IME if the event queue is full + if (!mEventQueue.Push(copiedInfo)) { + delete copiedInfo; + return false; + } + + // We only forward keydown and keyup event to input-method-app + // because input-method-app will generate keypress by itself. + if (aEvent->mMessage == eKeyPress) { + return true; + } + + // Create a keyboard event to pass into + // nsIHardwareKeyEventListener.onHardwareKey + nsCOMPtr eventTarget = do_QueryInterface(aTarget); + nsPresContext* presContext = GetPresContext(aTarget); + RefPtr keyboardEvent = + NS_NewDOMKeyboardEvent(eventTarget, presContext, aEvent->AsKeyboardEvent()); + // Duplicate the internal event data in the heap for the keyboardEvent, + // or the internal data from |aEvent| in the stack may be destroyed by others. + keyboardEvent->DuplicatePrivateData(); + + // Forward the created keyboard event to input-method-app + bool isSent = false; + keyHandler->OnHardwareKey(keyboardEvent, &isSent); + + // Pop the pending key event if it can't be forwarded + if (!isSent) { + mEventQueue.RemoveFront(); + } + + return isSent; +} + +NS_IMETHODIMP +HardwareKeyHandler::OnHandledByInputMethodApp(const nsAString& aType, + uint16_t aDefaultPrevented) +{ + // We can not handle this reply because the pending events had been already + // removed from the forwarding queue before this reply arrives. + if (mEventQueue.IsEmpty()) { + return NS_OK; + } + + RefPtr keyInfo = mEventQueue.PopFront(); + + // Only allow keydown and keyup to call this method + if (NS_WARN_IF(aType.EqualsLiteral("keydown") && + keyInfo->mEvent.mMessage != eKeyDown) || + NS_WARN_IF(aType.EqualsLiteral("keyup") && + keyInfo->mEvent.mMessage != eKeyUp)) { + return NS_ERROR_INVALID_ARG; + } + + // The value of defaultPrevented depends on whether or not + // the key is consumed by input-method-app + SetDefaultPrevented(keyInfo->mEvent, aDefaultPrevented); + + // Set the flag to specify the reply phase + keyInfo->mEvent.mInputMethodAppState = WidgetKeyboardEvent::eHandled; + + // Check whether the event is still valid to be fired + if (CanDispatchEvent(keyInfo->mTarget, keyInfo->mEvent)) { + // If the key's defaultPrevented is true, it means that the + // input-method-app has already consumed this key, + // so we can dispatch |mozbrowserafterkey*| directly if + // preference "dom.beforeAfterKeyboardEvent.enabled" is enabled. + if (keyInfo->mEvent.mFlags.mDefaultPrevented) { + DispatchAfterKeyEvent(keyInfo->mTarget, keyInfo->mEvent); + // Otherwise, it means that input-method-app doesn't handle this key, + // so we need to dispatch it to its current event target. + } else { + DispatchToTargetApp(keyInfo->mTarget, + keyInfo->mEvent, + keyInfo->mStatus); + } + } + + // No need to do further processing if the event is not keydown + if (keyInfo->mEvent.mMessage != eKeyDown) { + return NS_OK; + } + + // Update the latest keydown data: + // Release the holding reference to the previous keydown's data and + // add a reference count to the current keydown's data. + mLatestKeyDownInfo = keyInfo; + + // Handle the pending keypress event once keydown's reply arrives: + // It may have many keypress events per keydown on some platforms, + // so we use loop to dispatch keypress events. + // (But Gonk dispatch only one keypress per keydown) + // However, if there is no keypress after this keydown, + // then those following keypress will be handled in + // ForwardKeyToInputMethodApp directly. + for (KeyboardInfo* keypressInfo; + !mEventQueue.IsEmpty() && + (keypressInfo = mEventQueue.PeekFront()) && + keypressInfo->mEvent.mMessage == eKeyPress; + mEventQueue.RemoveFront()) { + DispatchKeyPress(keypressInfo->mTarget, + keypressInfo->mEvent, + keypressInfo->mStatus); + } + + return NS_OK; +} + +bool +HardwareKeyHandler::DispatchKeyPress(nsINode* aTarget, + WidgetKeyboardEvent& aEvent, + nsEventStatus& aStatus) +{ + MOZ_ASSERT(aTarget, "No target provided"); + MOZ_ASSERT(aEvent, "No event provided"); + MOZ_ASSERT(aEvent.mMessage == eKeyPress, "Event is not keypress"); + + // No need to dispatch keypress to the event target + // if the keydown event is consumed by the input-method-app. + if (mLatestKeyDownInfo && + mLatestKeyDownInfo->mEvent.mFlags.mDefaultPrevented) { + return false; + } + + // No need to dispatch keypress to the event target + // if the previous keydown event is modifier key's + if (mLatestKeyDownInfo && + mLatestKeyDownInfo->mEvent.IsModifierKeyEvent()) { + return false; + } + + // No need to dispatch keypress to the event target + // if it's invalid to be dispatched + if (!CanDispatchEvent(aTarget, aEvent)) { + return false; + } + + // Set the flag to specify the reply phase. + aEvent.mInputMethodAppState = WidgetKeyboardEvent::eHandled; + + // Dispatch the pending keypress event + bool ret = DispatchToTargetApp(aTarget, aEvent, aStatus); + + // Re-trigger EventStateManager::PostHandleKeyboardEvent for keypress + PostHandleKeyboardEvent(aTarget, aEvent, aStatus); + + return ret; +} + +void +HardwareKeyHandler::DispatchAfterKeyEvent(nsINode* aTarget, + WidgetKeyboardEvent& aEvent) +{ + MOZ_ASSERT(aTarget, "No target provided"); + MOZ_ASSERT(aEvent, "No event provided"); + + if (!PresShell::BeforeAfterKeyboardEventEnabled() || + aEvent.mMessage == eKeyPress) { + return; + } + + nsCOMPtr presShell = GetPresShell(aTarget); + if (NS_WARN_IF(presShell)) { + presShell->DispatchAfterKeyboardEvent(aTarget, + aEvent, + aEvent.mFlags.mDefaultPrevented); + } +} + +bool +HardwareKeyHandler::DispatchToTargetApp(nsINode* aTarget, + WidgetKeyboardEvent& aEvent, + nsEventStatus& aStatus) +{ + MOZ_ASSERT(aTarget, "No target provided"); + MOZ_ASSERT(aEvent, "No event provided"); + + // Get current focused element as the event target + nsCOMPtr currentTarget = GetCurrentTarget(); + if (NS_WARN_IF(!currentTarget)) { + return false; + } + + // The event target should be set to the current focused element. + // However, it might have security issue if the event is dispatched to + // the unexpected application, and it might cause unexpected operation + // in the new app. + nsCOMPtr originalRootWindow = GetRootWindow(aTarget); + nsCOMPtr currentRootWindow = GetRootWindow(currentTarget); + if (currentRootWindow != originalRootWindow) { + NS_WARNING("The root window is changed during the event is dispatching"); + return false; + } + + // If the current focused element is still in the same app, + // then we can use it as the current target to dispatch event. + nsCOMPtr presShell = GetPresShell(currentTarget); + if (!presShell) { + return false; + } + + if (!presShell->CanDispatchEvent(&aEvent)) { + return false; + } + + // In-process case: the event target is in the current process + if (!PresShell::IsTargetIframe(currentTarget)) { + DispatchToCurrentProcess(presShell, currentTarget, aEvent, aStatus); + + if (presShell->CanDispatchEvent(&aEvent)) { + DispatchAfterKeyEvent(aTarget, aEvent); + } + + return true; + } + + // OOP case: the event target is in the child process + return DispatchToCrossProcess(aTarget, aEvent); + + // After the oop target receives the event from TabChild::RecvRealKeyEvent + // and return the result through TabChild::SendDispatchAfterKeyboardEvent, + // the |mozbrowserafterkey*| will be fired from + // TabParent::RecvDispatchAfterKeyboardEvent, so we don't need to dispatch + // |mozbrowserafterkey*| by ourselves in this module. +} + +void +HardwareKeyHandler::DispatchToCurrentProcess(nsIPresShell* presShell, + nsIContent* aTarget, + WidgetKeyboardEvent& aEvent, + nsEventStatus& aStatus) +{ + EventDispatcher::Dispatch(aTarget, presShell->GetPresContext(), + &aEvent, nullptr, &aStatus, nullptr); +} + +bool +HardwareKeyHandler::DispatchToCrossProcess(nsINode* aTarget, + WidgetKeyboardEvent& aEvent) +{ + nsCOMPtr remoteLoaderOwner = do_QueryInterface(aTarget); + if (NS_WARN_IF(!remoteLoaderOwner)) { + return false; + } + + RefPtr remoteFrameLoader = + remoteLoaderOwner->GetFrameLoader(); + if (NS_WARN_IF(!remoteFrameLoader)) { + return false; + } + + uint32_t eventMode; + remoteFrameLoader->GetEventMode(&eventMode); + if (eventMode == nsIFrameLoader::EVENT_MODE_DONT_FORWARD_TO_CHILD) { + return false; + } + + PBrowserParent* remoteBrowser = remoteFrameLoader->GetRemoteBrowser(); + TabParent* remote = static_cast(remoteBrowser); + if (NS_WARN_IF(!remote)) { + return false; + } + + return remote->SendRealKeyEvent(aEvent); +} + +void +HardwareKeyHandler::PostHandleKeyboardEvent(nsINode* aTarget, + WidgetKeyboardEvent& aEvent, + nsEventStatus& aStatus) +{ + MOZ_ASSERT(aTarget, "No target provided"); + MOZ_ASSERT(aEvent, "No event provided"); + + nsPresContext* presContext = GetPresContext(aTarget); + + RefPtr esm = presContext->EventStateManager(); + bool dispatchedToChildProcess = PresShell::IsTargetIframe(aTarget); + esm->PostHandleKeyboardEvent(&aEvent, aStatus, dispatchedToChildProcess); +} + +void +HardwareKeyHandler::SetDefaultPrevented(WidgetKeyboardEvent& aEvent, + uint16_t aDefaultPrevented) { + if (aDefaultPrevented & DEFAULT_PREVENTED) { + aEvent.mFlags.mDefaultPrevented = true; + } + + if (aDefaultPrevented & DEFAULT_PREVENTED_BY_CHROME) { + aEvent.mFlags.mDefaultPreventedByChrome = true; + } + + if (aDefaultPrevented & DEFAULT_PREVENTED_BY_CONTENT) { + aEvent.mFlags.mDefaultPreventedByContent = true; + } +} + +bool +HardwareKeyHandler::CanDispatchEvent(nsINode* aTarget, + WidgetKeyboardEvent& aEvent) +{ + nsCOMPtr presShell = GetPresShell(aTarget); + if (NS_WARN_IF(!presShell)) { + return false; + } + return presShell->CanDispatchEvent(&aEvent); +} + +already_AddRefed +HardwareKeyHandler::GetRootWindow(nsINode* aNode) +{ + // Get nsIPresShell's pointer first + nsCOMPtr presShell = GetPresShell(aNode); + if (NS_WARN_IF(!presShell)) { + return nullptr; + } + nsCOMPtr rootWindow = presShell->GetRootWindow(); + return rootWindow.forget(); +} + +already_AddRefed +HardwareKeyHandler::GetCurrentTarget() +{ + nsFocusManager* fm = nsFocusManager::GetFocusManager(); + if (NS_WARN_IF(!fm)) { + return nullptr; + } + + nsCOMPtr focusedWindow; + fm->GetFocusedWindow(getter_AddRefs(focusedWindow)); + if (NS_WARN_IF(!focusedWindow)) { + return nullptr; + } + + auto* ourWindow = nsPIDOMWindowOuter::From(focusedWindow); + + nsCOMPtr rootWindow = ourWindow->GetPrivateRoot(); + if (NS_WARN_IF(!rootWindow)) { + return nullptr; + } + + nsCOMPtr focusedFrame; + nsCOMPtr focusedContent = + fm->GetFocusedDescendant(rootWindow, true, getter_AddRefs(focusedFrame)); + + // If there is no focus, then we use document body instead + if (NS_WARN_IF(!focusedContent || !focusedContent->GetPrimaryFrame())) { + nsIDocument* document = ourWindow->GetExtantDoc(); + if (NS_WARN_IF(!document)) { + return nullptr; + } + + focusedContent = document->GetRootElement(); + + nsCOMPtr htmlDocument = do_QueryInterface(document); + if (htmlDocument) { + nsCOMPtr body; + htmlDocument->GetBody(getter_AddRefs(body)); + nsCOMPtr bodyContent = do_QueryInterface(body); + if (bodyContent) { + focusedContent = bodyContent; + } + } + } + + return focusedContent ? focusedContent.forget() : nullptr; +} + +nsPresContext* +HardwareKeyHandler::GetPresContext(nsINode* aNode) +{ + // Get nsIPresShell's pointer first + nsCOMPtr presShell = GetPresShell(aNode); + if (NS_WARN_IF(!presShell)) { + return nullptr; + } + + // then use nsIPresShell to get nsPresContext's pointer + return presShell->GetPresContext(); +} + +already_AddRefed +HardwareKeyHandler::GetPresShell(nsINode* aNode) +{ + nsIDocument* doc = aNode->OwnerDoc(); + if (NS_WARN_IF(!doc)) { + return nullptr; + } + + nsCOMPtr presShell = doc->GetShell(); + if (NS_WARN_IF(!presShell)) { + return nullptr; + } + + return presShell.forget(); +} + +/* static */ +already_AddRefed +HardwareKeyHandler::GetInstance() +{ + if (!XRE_IsParentProcess()) { + return nullptr; + } + + if (!sInstance) { + sInstance = new HardwareKeyHandler(); + ClearOnShutdown(&sInstance); + } + + RefPtr service = sInstance.get(); + return service.forget(); +} + +} // namespace mozilla diff --git a/dom/inputmethod/HardwareKeyHandler.h b/dom/inputmethod/HardwareKeyHandler.h new file mode 100644 index 0000000000..88b9a1cd39 --- /dev/null +++ b/dom/inputmethod/HardwareKeyHandler.h @@ -0,0 +1,222 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_HardwareKeyHandler_h_ +#define mozilla_HardwareKeyHandler_h_ + +#include "mozilla/EventForwards.h" // for nsEventStatus +#include "mozilla/StaticPtr.h" +#include "mozilla/TextEvents.h" +#include "nsCOMPtr.h" +#include "nsDeque.h" +#include "nsIHardwareKeyHandler.h" +#include "nsIWeakReferenceUtils.h" // for nsWeakPtr + +class nsIContent; +class nsINode; +class nsIPresShell; +class nsPIDOMWindowOuter; +class nsPresContext; + +namespace mozilla { + +// This module will copy the events' data into its event queue for reuse +// after receiving input-method-app's reply, so we use the following struct +// for storing these information. +// RefCounted is a helper class for adding reference counting mechanism. +struct KeyboardInfo : public RefCounted +{ + nsINode* mTarget; + WidgetKeyboardEvent mEvent; + nsEventStatus mStatus; + + KeyboardInfo(nsINode* aTarget, + WidgetKeyboardEvent& aEvent, + nsEventStatus aStatus) + : mTarget(aTarget) + , mEvent(aEvent) + , mStatus(aStatus) + { + } +}; + +// The following is the type-safe wrapper around nsDeque +// for storing events' data. +// The T must be one class that supports reference counting mechanism. +// The EventQueueDeallocator will be called in nsDeque::~nsDeque() or +// nsDeque::Erase() to deallocate the objects. nsDeque::Erase() will remove +// and delete all items in the queue. See more from nsDeque.h. +template +class EventQueueDeallocator : public nsDequeFunctor +{ + virtual void* operator() (void* aObject) + { + RefPtr releaseMe = dont_AddRef(static_cast(aObject)); + return nullptr; + } +}; + +// The type-safe queue to be used to store the KeyboardInfo data +template +class EventQueue : private nsDeque +{ +public: + EventQueue() + : nsDeque(new EventQueueDeallocator()) + { + }; + + ~EventQueue() + { + Clear(); + } + + inline size_t GetSize() + { + return nsDeque::GetSize(); + } + + bool IsEmpty() + { + return !nsDeque::GetSize(); + } + + inline bool Push(T* aItem) + { + MOZ_ASSERT(aItem); + NS_ADDREF(aItem); + size_t sizeBefore = GetSize(); + nsDeque::Push(aItem); + if (GetSize() != sizeBefore + 1) { + NS_RELEASE(aItem); + return false; + } + return true; + } + + inline already_AddRefed PopFront() + { + RefPtr rv = dont_AddRef(static_cast(nsDeque::PopFront())); + return rv.forget(); + } + + inline void RemoveFront() + { + RefPtr releaseMe = PopFront(); + } + + inline T* PeekFront() + { + return static_cast(nsDeque::PeekFront()); + } + + void Clear() + { + while (GetSize() > 0) { + RemoveFront(); + } + } +}; + +class HardwareKeyHandler : public nsIHardwareKeyHandler +{ +public: + HardwareKeyHandler(); + + NS_DECL_ISUPPORTS + NS_DECL_NSIHARDWAREKEYHANDLER + + static already_AddRefed GetInstance(); + + virtual bool ForwardKeyToInputMethodApp(nsINode* aTarget, + WidgetKeyboardEvent* aEvent, + nsEventStatus* aEventStatus) override; + +private: + virtual ~HardwareKeyHandler(); + + // Return true if the keypress is successfully dispatched. + // Otherwise, return false. + bool DispatchKeyPress(nsINode* aTarget, + WidgetKeyboardEvent& aEvent, + nsEventStatus& aStatus); + + void DispatchAfterKeyEvent(nsINode* aTarget, WidgetKeyboardEvent& aEvent); + + void DispatchToCurrentProcess(nsIPresShell* aPresShell, + nsIContent* aTarget, + WidgetKeyboardEvent& aEvent, + nsEventStatus& aStatus); + + bool DispatchToCrossProcess(nsINode* aTarget, WidgetKeyboardEvent& aEvent); + + // This method will dispatch not only key* event to its event target, + // no mather it's in the current process or in its child process, + // but also mozbrowserafterkey* to the corresponding target if it needs. + // Return true if the key is successfully dispatched. + // Otherwise, return false. + bool DispatchToTargetApp(nsINode* aTarget, + WidgetKeyboardEvent& aEvent, + nsEventStatus& aStatus); + + // This method will be called after dispatching keypress to its target, + // if the input-method-app doesn't handle the key. + // In normal dispatching path, EventStateManager::PostHandleKeyboardEvent + // will be called when event is keypress. + // However, the ::PostHandleKeyboardEvent mentioned above will be aborted + // when we try to forward key event to the input-method-app. + // If the input-method-app consumes the key, then we don't need to do anything + // because the input-method-app will generate a new key event by itself. + // On the other hand, if the input-method-app doesn't consume the key, + // then we need to dispatch the key event by ourselves + // and call ::PostHandleKeyboardEvent again after the event is forwarded. + // Note that the EventStateManager::PreHandleEvent is already called before + // forwarding, so we don't need to call it in this module. + void PostHandleKeyboardEvent(nsINode* aTarget, + WidgetKeyboardEvent& aEvent, + nsEventStatus& aStatus); + + void SetDefaultPrevented(WidgetKeyboardEvent& aEvent, + uint16_t aDefaultPrevented); + + // Check whether the event is valid to be fired. + // This method should be called every time before dispatching next event. + bool CanDispatchEvent(nsINode* aTarget, + WidgetKeyboardEvent& aEvent); + + already_AddRefed GetRootWindow(nsINode* aNode); + + already_AddRefed GetCurrentTarget(); + + nsPresContext* GetPresContext(nsINode* aNode); + + already_AddRefed GetPresShell(nsINode* aNode); + + static StaticRefPtr sInstance; + + // The event queue is used to store the forwarded keyboard events. + // Those stored events will be dispatched if input-method-app doesn't + // consume them. + EventQueue mEventQueue; + + // Hold the pointer to the latest keydown's data + RefPtr mLatestKeyDownInfo; + + // input-method-app needs to register a listener by + // |nsIHardwareKeyHandler.registerListener| to receive + // the hardware keyboard event, and |nsIHardwareKeyHandler.registerListener| + // will set an nsIHardwareKeyEventListener to mHardwareKeyEventListener. + // Then, mHardwareKeyEventListener is used to forward the event + // to the input-method-app. + nsWeakPtr mHardwareKeyEventListener; + + // To keep tracking the input-method-app is active or disabled. + bool mInputMethodAppConnected; +}; + +} // namespace mozilla + +#endif // #ifndef mozilla_HardwareKeyHandler_h_ diff --git a/dom/inputmethod/Keyboard.jsm b/dom/inputmethod/Keyboard.jsm index b117b62435..ec1f8d15a6 100644 --- a/dom/inputmethod/Keyboard.jsm +++ b/dom/inputmethod/Keyboard.jsm @@ -23,6 +23,15 @@ XPCOMUtils.defineLazyGetter(this, "appsService", function() { return Cc["@mozilla.org/AppsService;1"].getService(Ci.nsIAppsService); }); +XPCOMUtils.defineLazyGetter(this, "hardwareKeyHandler", function() { +#ifdef MOZ_B2G + return Cc["@mozilla.org/HardwareKeyHandler;1"] + .getService(Ci.nsIHardwareKeyHandler); +#else + return null; +#endif +}); + var Utils = { getMMFromMessage: function u_getMMFromMessage(msg) { let mm; @@ -41,6 +50,18 @@ var Utils = { }; this.Keyboard = { +#ifdef MOZ_B2G + // For receving keyboard event fired from hardware before it's dispatched, + // |this| object is used to be the listener to get the forwarded event. + // As the listener, |this| object must implement nsIHardwareKeyEventListener + // and nsSupportsWeakReference. + // Please see nsIHardwareKeyHandler.idl to get more information. + QueryInterface: XPCOMUtils.generateQI([ + Ci.nsIHardwareKeyEventListener, + Ci.nsISupportsWeakReference + ]), +#endif + _isConnectedToHardwareKeyHandler: false, _formMM: null, // The current web page message manager. _keyboardMM: null, // The keyboard app message manager. _keyboardID: -1, // The keyboard app's ID number. -1 = invalid @@ -59,7 +80,8 @@ this.Keyboard = { 'SwitchToNextInputMethod', 'HideInputMethod', 'SendKey', 'GetContext', 'SetComposition', 'EndComposition', - 'RegisterSync', 'Unregister' + 'RegisterSync', 'Unregister', + 'ReplyHardwareKeyEvent' ], get formMM() { @@ -88,7 +110,10 @@ this.Keyboard = { sendToKeyboard: function(name, data) { try { this._keyboardMM.sendAsyncMessage(name, data); - } catch(e) { } + } catch(e) { + return false; + } + return true; }, sendToSystem: function(name, data) { @@ -111,6 +136,11 @@ this.Keyboard = { Services.obs.addObserver(this, 'oop-frameloader-crashed', false); Services.obs.addObserver(this, 'message-manager-close', false); + // For receiving the native hardware keyboard event + if (hardwareKeyHandler) { + hardwareKeyHandler.registerListener(this); + } + for (let name of this._messageNames) { ppmm.addMessageListener('Keyboard:' + name, this); } @@ -122,6 +152,16 @@ this.Keyboard = { this.inputRegistryGlue = new InputRegistryGlue(); }, + // This method will be registered into nsIHardwareKeyHandler: + // Send the initialized dictionary retrieved from the native keyboard event + // to input-method-app for generating a new event. + onHardwareKey: function onHardwareKeyReceived(evt) { + return this.sendToKeyboard('Keyboard:ReceiveHardwareKeyEvent', { + type: evt.type, + keyDict: evt.initDict + }); + }, + observe: function keyboardObserve(subject, topic, data) { let frameLoader = null; let mm = null; @@ -315,6 +355,13 @@ this.Keyboard = { this._keyboardMM = null; this._keyboardID = -1; break; + case 'Keyboard:ReplyHardwareKeyEvent': + if (hardwareKeyHandler) { + let reply = msg.data; + hardwareKeyHandler.onHandledByInputMethodApp(reply.type, + reply.defaultPrevented); + } + break; } }, @@ -324,6 +371,12 @@ this.Keyboard = { .frameLoader.messageManager; this.formMM = mm; + // Notify the nsIHardwareKeyHandler that the input-method-app is active now. + if (hardwareKeyHandler && !this._isConnectedToHardwareKeyHandler) { + this._isConnectedToHardwareKeyHandler = true; + hardwareKeyHandler.onInputMethodAppConnected(); + } + // Notify the current active input app to gain focus. this.forwardEvent('Keyboard:Focus', msg); @@ -356,6 +409,13 @@ this.Keyboard = { // unset formMM this.formMM = null; + // Notify the nsIHardwareKeyHandler that + // the input-method-app is disabled now. + if (hardwareKeyHandler && this._isConnectedToHardwareKeyHandler) { + this._isConnectedToHardwareKeyHandler = false; + hardwareKeyHandler.onInputMethodAppDisconnected(); + } + this.forwardEvent('Keyboard:Blur', msg); this.sendToSystem('System:Blur', {}); diff --git a/dom/inputmethod/MozKeyboard.js b/dom/inputmethod/MozKeyboard.js index c9b5e610ae..aa6ba67cd3 100644 --- a/dom/inputmethod/MozKeyboard.js +++ b/dom/inputmethod/MozKeyboard.js @@ -431,6 +431,7 @@ MozInputMethod.prototype = { cpmm.addWeakMessageListener('Keyboard:SelectionChange', this); cpmm.addWeakMessageListener('Keyboard:GetContext:Result:OK', this); cpmm.addWeakMessageListener('Keyboard:SupportsSwitchingTypesChange', this); + cpmm.addWeakMessageListener('Keyboard:ReceiveHardwareKeyEvent', this); cpmm.addWeakMessageListener('InputRegistry:Result:OK', this); cpmm.addWeakMessageListener('InputRegistry:Result:Error', this); @@ -455,6 +456,7 @@ MozInputMethod.prototype = { cpmm.removeWeakMessageListener('Keyboard:SelectionChange', this); cpmm.removeWeakMessageListener('Keyboard:GetContext:Result:OK', this); cpmm.removeWeakMessageListener('Keyboard:SupportsSwitchingTypesChange', this); + cpmm.removeWeakMessageListener('Keyboard:ReceiveHardwareKeyEvent', this); cpmm.removeWeakMessageListener('InputRegistry:Result:OK', this); cpmm.removeWeakMessageListener('InputRegistry:Result:Error', this); this.setActive(false); @@ -508,7 +510,24 @@ MozInputMethod.prototype = { case 'Keyboard:SupportsSwitchingTypesChange': this._supportsSwitchingTypes = data.types; break; + case 'Keyboard:ReceiveHardwareKeyEvent': + if (!Ci.nsIHardwareKeyHandler) { + break; + } + let defaultPrevented = Ci.nsIHardwareKeyHandler.NO_DEFAULT_PREVENTED; + + // |event.preventDefault()| is allowed to be called only when + // |event.cancelable| is true + if (this._inputcontext && data.keyDict.cancelable) { + defaultPrevented |= this._inputcontext.forwardHardwareKeyEvent(data); + } + + cpmmSendAsyncMessageWithKbID(this, 'Keyboard:ReplyHardwareKeyEvent', { + type: data.type, + defaultPrevented: defaultPrevented + }); + break; case 'InputRegistry:Result:OK': resolver.resolve(); @@ -684,7 +703,7 @@ MozInputMethod.prototype = { } }; - /** +/** * ============================================== * InputContextDOMRequestIpcHelper * ============================================== @@ -783,7 +802,20 @@ MozInputContextSurroundingTextChangeEventDetail.prototype = { } }; - /** +/** + * ============================================== + * HardwareInput + * ============================================== + */ +function MozHardwareInput() { +} + +MozHardwareInput.prototype = { + classID: Components.ID("{1e38633d-d08b-4867-9944-afa5c648adb6}"), + QueryInterface: XPCOMUtils.generateQI([]), +}; + +/** * ============================================== * InputContext * ============================================== @@ -807,6 +839,8 @@ MozInputContext.prototype = { _context: null, _contextId: -1, _ipcHelper: null, + _hardwareinput: null, + _wrappedhardwareinput: null, classID: Components.ID("{1e38633d-d08b-4867-9944-afa5c648adb6}"), @@ -820,6 +854,9 @@ MozInputContext.prototype = { this._ipcHelper = WindowMap.getInputContextIpcHelper(win); this._ipcHelper.attachInputContext(this); + this._hardwareinput = new MozHardwareInput(); + this._wrappedhardwareinput = + this._window.MozHardwareInput._create(this._window, this._hardwareinput); }, destroy: function ic_destroy() { @@ -837,6 +874,8 @@ MozInputContext.prototype = { this._ipcHelper = null; this._window = null; + this._hardwareinput = null; + this._wrappedhardwareinput = null; }, receiveMessage: function ic_receiveMessage(msg) { @@ -989,6 +1028,10 @@ MozInputContext.prototype = { return text.substr(start, end - start + 100); }, + get hardwareinput() { + return this._wrappedhardwareinput; + }, + setSelectionRange: function ic_setSelectionRange(start, length) { let self = this; return this._sendPromise(function(resolverId) { @@ -1111,6 +1154,45 @@ MozInputContext.prototype = { }); }, + // Generate a new keyboard event by the received keyboard dictionary + // and return defaultPrevented's result of the event after dispatching. + forwardHardwareKeyEvent: function ic_forwardHardwareKeyEvent(data) { + if (!Ci.nsIHardwareKeyHandler) { + return; + } + + if (!this._context) { + return Ci.nsIHardwareKeyHandler.NO_DEFAULT_PREVENTED; + } + let evt = new this._window.KeyboardEvent(data.type, + Cu.cloneInto(data.keyDict, + this._window)); + this._hardwareinput.__DOM_IMPL__.dispatchEvent(evt); + return this._getDefaultPreventedValue(evt); + }, + + _getDefaultPreventedValue: function(evt) { + if (!Ci.nsIHardwareKeyHandler) { + return; + } + + let flags = Ci.nsIHardwareKeyHandler.NO_DEFAULT_PREVENTED; + + if (evt.defaultPrevented) { + flags |= Ci.nsIHardwareKeyHandler.DEFAULT_PREVENTED; + } + + if (evt.defaultPreventedByChrome) { + flags |= Ci.nsIHardwareKeyHandler.DEFAULT_PREVENTED_BY_CHROME; + } + + if (evt.defaultPreventedByContent) { + flags |= Ci.nsIHardwareKeyHandler.DEFAULT_PREVENTED_BY_CONTENT; + } + + return flags; + }, + _sendPromise: function(callback) { let self = this; return this._ipcHelper.createPromiseWithId(function(aResolverId) { diff --git a/dom/inputmethod/moz.build b/dom/inputmethod/moz.build index b867207a83..53eb4a733f 100644 --- a/dom/inputmethod/moz.build +++ b/dom/inputmethod/moz.build @@ -4,12 +4,35 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +if CONFIG['MOZ_B2G']: + XPIDL_SOURCES += [ + 'nsIHardwareKeyHandler.idl', + ] + + XPIDL_MODULE = 'inputmethod' + + EXPORTS.mozilla += [ + 'HardwareKeyHandler.h', + ] + + SOURCES += [ + 'HardwareKeyHandler.cpp' + ] + + include('/ipc/chromium/chromium-config.mozbuild') + + FINAL_LIBRARY = 'xul' + LOCAL_INCLUDES += [ + '/dom/base', + '/layout/base', + ] + EXTRA_COMPONENTS += [ 'InputMethod.manifest', 'MozKeyboard.js', ] -EXTRA_JS_MODULES += [ +EXTRA_PP_JS_MODULES += [ 'Keyboard.jsm', ] diff --git a/dom/inputmethod/nsIHardwareKeyHandler.idl b/dom/inputmethod/nsIHardwareKeyHandler.idl new file mode 100644 index 0000000000..5bce4d9805 --- /dev/null +++ b/dom/inputmethod/nsIHardwareKeyHandler.idl @@ -0,0 +1,142 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsIDOMKeyEvent; + +%{C++ +#define NS_HARDWARE_KEY_HANDLER_CID \ + { 0xfb45921b, 0xe0a5, 0x45c6, \ + { 0x90, 0xd0, 0xa6, 0x97, 0xa7, 0x72, 0xc4, 0x2a } } +#define NS_HARDWARE_KEY_HANDLER_CONTRACTID \ + "@mozilla.org/HardwareKeyHandler;1" + +#include "mozilla/EventForwards.h" /* For nsEventStatus */ + +namespace mozilla { +class WidgetKeyboardEvent; +} + +using mozilla::WidgetKeyboardEvent; + +class nsINode; +%} + +/** + * This interface is used to be registered to the nsIHardwareKeyHandler through + * |nsIHardwareKeyHandler.registerListener|. + */ +[scriptable, function, uuid(cd5aeee3-b4b9-459d-85e7-c0671c7a8a2e)] +interface nsIHardwareKeyEventListener : nsISupports +{ + /** + * This method will be invoked by nsIHardwareKeyHandler to forward the native + * keyboard event to the active input method + */ + bool onHardwareKey(in nsIDOMKeyEvent aEvent); +}; + +/** + * This interface has two main roles. One is to send a hardware keyboard event + * to the active input method app and the other is to receive its reply result. + * If a keyboard event is triggered from a hardware keyboard when an editor has + * focus, the event target should be the editor. However, the text input + * processor algorithm is implemented in an input method app and it should + * handle the event earlier than the real event target to do the mapping such + * as character conversion according to the language setting or the type of a + * hardware keyboard. + */ +[scriptable, builtinclass, uuid(25b34270-caad-4d18-a910-860351690639)] +interface nsIHardwareKeyHandler : nsISupports +{ + /** + * Flags used to set the defaultPrevented's result. The default result + * from input-method-app should be set to NO_DEFAULT_PREVENTED. + * (It means the forwarded event isn't consumed by input-method-app.) + * If the input-method-app consumes the forwarded event, + * then the result should be set by DEFAULT_PREVENTED* before reply. + */ + const unsigned short NO_DEFAULT_PREVENTED = 0x0000; + const unsigned short DEFAULT_PREVENTED = 0x0001; + const unsigned short DEFAULT_PREVENTED_BY_CHROME = 0x0002; + const unsigned short DEFAULT_PREVENTED_BY_CONTENT = 0x0004; + + /** + * Registers a listener in input-method-app to receive + * the forwarded hardware keyboard events + * + * @param aListener Listener object to be notified for receiving + * the keyboard event fired from hardware + * @note A listener object must implement + * nsIHardwareKeyEventListener and + * nsSupportsWeakReference + * @see nsIHardwareKeyEventListener + * @see nsSupportsWeakReference + */ + void registerListener(in nsIHardwareKeyEventListener aListener); + + /** + * Unregisters the current listener from input-method-app + */ + void unregisterListener(); + + /** + * Notifies nsIHardwareKeyHandler that input-method-app is active. + */ + void onInputMethodAppConnected(); + + /** + * Notifies nsIHardwareKeyHandler that input-method-app is disabled. + */ + void onInputMethodAppDisconnected(); + + /** + * Input-method-app will pass the processing result that the forwarded + * event is handled or not through this method, and the nsIHardwareKeyHandler + * can use this to receive the reply of |forwardKeyToInputMethodApp| + * from the active input method. + * + * The result should contain the original event type and the info whether + * the default is prevented, also, it is prevented by chrome or content. + * + * @param aEventType The type of an original event. + * @param aDefaultPrevented State that |evt.preventDefault| + * is called by content, chrome or not. + */ + void onHandledByInputMethodApp(in DOMString aType, + in unsigned short aDefaultPrevented); + + /** + * Sends the native keyboard events triggered from hardware to the + * active input method before dispatching to its event target. + * This method only forwards keydown and keyup events. + * If the event isn't allowed to be forwarded, we should continue the + * normal event processing. For those forwarded keydown and keyup events + * We will pause the further event processing to wait for the completion + * of the event handling in the active input method app. + * Once |onHandledByInputMethodApp| is called by the input method app, + * the pending event processing can be resumed according to its reply. + * On the other hand, the keypress will never be sent to the input-method-app. + * Depending on whether the keydown's reply arrives before the keypress event + * comes, the keypress event will be handled directly or pushed into + * the event queue to wait for its heading keydown's reply. + * + * This implementation will call |nsIHardwareKeyEventListener.onHardwareKey|, + * which is registered through |nsIHardwareKeyEventListener.registerListener|, + * to forward the events. + * + * Returns true, if the event is handled in this module. + * Returns false, otherwise. + * + * If it returns false, we should continue the normal event processing. + */ + %{C++ + virtual bool ForwardKeyToInputMethodApp(nsINode* aTarget, + WidgetKeyboardEvent* aEvent, + nsEventStatus* aEventStatus) = 0; + %} +}; diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index 14149ec67d..d4ebe11048 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -460,11 +460,11 @@ parent: uint64_t aObserverId); async SynthesizeNativeTouchPoint(uint32_t aPointerId, TouchPointerState aPointerState, - ScreenIntPoint aPointerScreenPoint, + LayoutDeviceIntPoint aPoint, double aPointerPressure, uint32_t aPointerOrientation, uint64_t aObserverId); - async SynthesizeNativeTouchTap(ScreenIntPoint aPointerScreenPoint, + async SynthesizeNativeTouchTap(LayoutDeviceIntPoint aPoint, bool aLongTap, uint64_t aObserverId); async ClearNativeTouchSequence(uint64_t aObserverId); diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index bcc9ea74c6..6183280a13 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -1862,7 +1862,7 @@ TabChild::RecvRealMouseButtonEvent(const WidgetMouseEvent& aEvent, InputAPZContext context(aGuid, aInputBlockId, unused); WidgetMouseEvent localEvent(aEvent); - localEvent.widget = mPuppetWidget; + localEvent.mWidget = mPuppetWidget; APZCCallbackHelper::ApplyCallbackTransform(localEvent, aGuid, mPuppetWidget->GetDefaultScale()); APZCCallbackHelper::DispatchWidgetEvent(localEvent); @@ -1885,7 +1885,7 @@ TabChild::RecvMouseWheelEvent(const WidgetWheelEvent& aEvent, } WidgetWheelEvent localEvent(aEvent); - localEvent.widget = mPuppetWidget; + localEvent.mWidget = mPuppetWidget; APZCCallbackHelper::ApplyCallbackTransform(localEvent, aGuid, mPuppetWidget->GetDefaultScale()); APZCCallbackHelper::DispatchWidgetEvent(localEvent); @@ -1929,7 +1929,7 @@ TabChild::RecvRealTouchEvent(const WidgetTouchEvent& aEvent, TABC_LOG("Receiving touch event of type %d\n", aEvent.mMessage); WidgetTouchEvent localEvent(aEvent); - localEvent.widget = mPuppetWidget; + localEvent.mWidget = mPuppetWidget; APZCCallbackHelper::ApplyCallbackTransform(localEvent, aGuid, mPuppetWidget->GetDefaultScale()); @@ -1974,7 +1974,7 @@ TabChild::RecvRealDragEvent(const WidgetDragEvent& aEvent, const uint32_t& aDropEffect) { WidgetDragEvent localEvent(aEvent); - localEvent.widget = mPuppetWidget; + localEvent.mWidget = mPuppetWidget; nsCOMPtr dragSession = nsContentUtils::GetDragSession(); if (dragSession) { @@ -2010,7 +2010,7 @@ bool TabChild::RecvPluginEvent(const WidgetPluginEvent& aEvent) { WidgetPluginEvent localEvent(aEvent); - localEvent.widget = mPuppetWidget; + localEvent.mWidget = mPuppetWidget; nsEventStatus status = APZCCallbackHelper::DispatchWidgetEvent(localEvent); if (status != nsEventStatus_eConsumeNoDefault) { // If not consumed, we should call default action @@ -2069,7 +2069,7 @@ TabChild::RecvRealKeyEvent(const WidgetKeyboardEvent& event, } WidgetKeyboardEvent localEvent(event); - localEvent.widget = mPuppetWidget; + localEvent.mWidget = mPuppetWidget; nsEventStatus status = APZCCallbackHelper::DispatchWidgetEvent(localEvent); if (event.mMessage == eKeyDown) { @@ -2104,7 +2104,7 @@ bool TabChild::RecvCompositionEvent(const WidgetCompositionEvent& event) { WidgetCompositionEvent localEvent(event); - localEvent.widget = mPuppetWidget; + localEvent.mWidget = mPuppetWidget; APZCCallbackHelper::DispatchWidgetEvent(localEvent); Unused << SendOnEventNeedingAckHandled(event.mMessage); return true; @@ -2114,7 +2114,7 @@ bool TabChild::RecvSelectionEvent(const WidgetSelectionEvent& event) { WidgetSelectionEvent localEvent(event); - localEvent.widget = mPuppetWidget; + localEvent.mWidget = mPuppetWidget; APZCCallbackHelper::DispatchWidgetEvent(localEvent); Unused << SendOnEventNeedingAckHandled(event.mMessage); return true; diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 558daca49e..c83368bcdd 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -1163,7 +1163,7 @@ bool TabParent::SendRealMouseEvent(WidgetMouseEvent& event) if (mIsDestroyed) { return false; } - event.refPoint += GetChildProcessOffset(); + event.mRefPoint += GetChildProcessOffset(); nsCOMPtr widget = GetWidget(); if (widget) { @@ -1215,7 +1215,7 @@ TabParent::SendRealDragEvent(WidgetDragEvent& event, uint32_t aDragAction, if (mIsDestroyed) { return false; } - event.refPoint += GetChildProcessOffset(); + event.mRefPoint += GetChildProcessOffset(); return PBrowserParent::SendRealDragEvent(event, aDragAction, aDropEffect); } @@ -1233,7 +1233,7 @@ bool TabParent::SendMouseWheelEvent(WidgetWheelEvent& event) ScrollableLayerGuid guid; uint64_t blockId; ApzAwareEventRoutingToChild(&guid, &blockId, nullptr); - event.refPoint += GetChildProcessOffset(); + event.mRefPoint += GetChildProcessOffset(); return PBrowserParent::SendMouseWheelEvent(event, guid, blockId); } @@ -1245,8 +1245,8 @@ bool TabParent::RecvDispatchWheelEvent(const mozilla::WidgetWheelEvent& aEvent) } WidgetWheelEvent localEvent(aEvent); - localEvent.widget = widget; - localEvent.refPoint -= GetChildProcessOffset(); + localEvent.mWidget = widget; + localEvent.mRefPoint -= GetChildProcessOffset(); widget->DispatchInputEvent(&localEvent); return true; @@ -1261,8 +1261,8 @@ TabParent::RecvDispatchMouseEvent(const mozilla::WidgetMouseEvent& aEvent) } WidgetMouseEvent localEvent(aEvent); - localEvent.widget = widget; - localEvent.refPoint -= GetChildProcessOffset(); + localEvent.mWidget = widget; + localEvent.mRefPoint -= GetChildProcessOffset(); widget->DispatchInputEvent(&localEvent); return true; @@ -1277,8 +1277,8 @@ TabParent::RecvDispatchKeyboardEvent(const mozilla::WidgetKeyboardEvent& aEvent) } WidgetKeyboardEvent localEvent(aEvent); - localEvent.widget = widget; - localEvent.refPoint -= GetChildProcessOffset(); + localEvent.mWidget = widget; + localEvent.mRefPoint -= GetChildProcessOffset(); widget->DispatchInputEvent(&localEvent); return true; @@ -1457,7 +1457,7 @@ TabParent::RecvSynthesizeNativeMouseScrollEvent(const LayoutDeviceIntPoint& aPoi bool TabParent::RecvSynthesizeNativeTouchPoint(const uint32_t& aPointerId, const TouchPointerState& aPointerState, - const ScreenIntPoint& aPointerScreenPoint, + const LayoutDeviceIntPoint& aPoint, const double& aPointerPressure, const uint32_t& aPointerOrientation, const uint64_t& aObserverId) @@ -1465,22 +1465,21 @@ TabParent::RecvSynthesizeNativeTouchPoint(const uint32_t& aPointerId, AutoSynthesizedEventResponder responder(this, aObserverId, "touchpoint"); nsCOMPtr widget = GetWidget(); if (widget) { - widget->SynthesizeNativeTouchPoint(aPointerId, aPointerState, aPointerScreenPoint, + widget->SynthesizeNativeTouchPoint(aPointerId, aPointerState, aPoint, aPointerPressure, aPointerOrientation, responder.GetObserver()); } return true; } bool -TabParent::RecvSynthesizeNativeTouchTap(const ScreenIntPoint& aPointerScreenPoint, +TabParent::RecvSynthesizeNativeTouchTap(const LayoutDeviceIntPoint& aPoint, const bool& aLongTap, const uint64_t& aObserverId) { AutoSynthesizedEventResponder responder(this, aObserverId, "touchtap"); nsCOMPtr widget = GetWidget(); if (widget) { - widget->SynthesizeNativeTouchTap(aPointerScreenPoint, aLongTap, - responder.GetObserver()); + widget->SynthesizeNativeTouchTap(aPoint, aLongTap, responder.GetObserver()); } return true; } @@ -1501,7 +1500,7 @@ bool TabParent::SendRealKeyEvent(WidgetKeyboardEvent& event) if (mIsDestroyed) { return false; } - event.refPoint += GetChildProcessOffset(); + event.mRefPoint += GetChildProcessOffset(); MaybeNativeKeyBinding bindings; bindings = void_t(); @@ -1538,9 +1537,9 @@ bool TabParent::SendRealTouchEvent(WidgetTouchEvent& event) // that the added touches are part of the touchend/cancel, when actually // they're not. if (event.mMessage == eTouchEnd || event.mMessage == eTouchCancel) { - for (int i = event.touches.Length() - 1; i >= 0; i--) { - if (!event.touches[i]->mChanged) { - event.touches.RemoveElementAt(i); + for (int i = event.mTouches.Length() - 1; i >= 0; i--) { + if (!event.mTouches[i]->mChanged) { + event.mTouches.RemoveElementAt(i); } } } @@ -1555,8 +1554,8 @@ bool TabParent::SendRealTouchEvent(WidgetTouchEvent& event) } LayoutDeviceIntPoint offset = GetChildProcessOffset(); - for (uint32_t i = 0; i < event.touches.Length(); i++) { - event.touches[i]->mRefPoint += offset; + for (uint32_t i = 0; i < event.mTouches.Length(); i++) { + event.mTouches[i]->mRefPoint += offset; } return (event.mMessage == eTouchMove) ? @@ -2015,7 +2014,7 @@ TabParent::RecvDispatchAfterKeyboardEvent(const WidgetKeyboardEvent& aEvent) NS_ENSURE_TRUE(mFrameElement, true); WidgetKeyboardEvent localEvent(aEvent); - localEvent.widget = GetWidget(); + localEvent.mWidget = GetWidget(); nsIDocument* doc = mFrameElement->OwnerDoc(); nsCOMPtr presShell = doc->GetShell(); @@ -2669,8 +2668,8 @@ TabParent::InjectTouchEvent(const nsAString& aType, } WidgetTouchEvent event(true, msg, widget); - event.modifiers = aModifiers; - event.time = PR_IntervalNow(); + event.mModifiers = aModifiers; + event.mTime = PR_IntervalNow(); nsCOMPtr content = do_QueryInterface(mFrameElement); if (!content || !content->OwnerDoc()) { @@ -2683,7 +2682,7 @@ TabParent::InjectTouchEvent(const nsAString& aType, } nsPresContext* presContext = doc->GetShell()->GetPresContext(); - event.touches.SetCapacity(aCount); + event.mTouches.SetCapacity(aCount); for (uint32_t i = 0; i < aCount; ++i) { LayoutDeviceIntPoint pt = LayoutDeviceIntPoint::FromAppUnitsRounded( @@ -2702,7 +2701,7 @@ TabParent::InjectTouchEvent(const nsAString& aType, // about the meaning of changedTouches for each event, see // https://developer.mozilla.org/docs/Web/API/TouchEvent.changedTouches t->mChanged = true; - event.touches.AppendElement(t); + event.mTouches.AppendElement(t); } SendRealTouchEvent(event); diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index ce0e3a2e83..8c18ff533b 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -393,13 +393,13 @@ public: virtual bool RecvSynthesizeNativeTouchPoint(const uint32_t& aPointerId, const TouchPointerState& aPointerState, - const ScreenIntPoint& aPointerScreenPoint, + const LayoutDeviceIntPoint& aPoint, const double& aPointerPressure, const uint32_t& aPointerOrientation, const uint64_t& aObserverId) override; virtual bool - RecvSynthesizeNativeTouchTap(const ScreenIntPoint& aPointerScreenPoint, + RecvSynthesizeNativeTouchTap(const LayoutDeviceIntPoint& aPoint, const bool& aLongTap, const uint64_t& aObserverId) override; diff --git a/dom/locales/en-US/chrome/dom/dom.properties b/dom/locales/en-US/chrome/dom/dom.properties index 8bef829a82..3c77a8c974 100644 --- a/dom/locales/en-US/chrome/dom/dom.properties +++ b/dom/locales/en-US/chrome/dom/dom.properties @@ -109,6 +109,8 @@ MediaLoadSourceMediaNotMatched=Specified "media" attribute of "%1$S" does not ma MediaLoadUnsupportedMimeType=HTTP "Content-Type" of "%1$S" is not supported. Load of media resource %2$S failed. # LOCALIZATION NOTE: %S is the URL of the media resource which failed to load because of error in decoding. MediaLoadDecodeError=Media resource %S could not be decoded. +# LOCALIZATION NOTE: Do not translate "MediaRecorder". +MediaRecorderMultiTracksNotSupported=MediaRecorder does not support recording multiple tracks of the same type at this time. # LOCALIZATION NOTE: %S is the ID of the MediaStreamTrack passed to MediaStream.addTrack(). Do not translate "MediaStreamTrack" and "AudioChannel". MediaStreamAddTrackDifferentAudioChannel=MediaStreamTrack %S could not be added since it belongs to a different AudioChannel. # LOCALIZATION NOTE: Do not translate "MediaStream", "stop()" and "MediaStreamTrack" diff --git a/dom/mathml/nsMathMLElement.cpp b/dom/mathml/nsMathMLElement.cpp index 581b9cfe22..1882050e9d 100644 --- a/dom/mathml/nsMathMLElement.cpp +++ b/dom/mathml/nsMathMLElement.cpp @@ -136,7 +136,7 @@ nsMathMLElement::UnbindFromTree(bool aDeep, bool aNullParent) // be under a different xml:base, so forget the cached state now. Link::ResetLinkState(false, Link::ElementHasHref()); - nsIDocument* doc = GetCurrentDoc(); + nsIDocument* doc = GetUncomposedDoc(); if (doc) { doc->UnregisterPendingLinkUpdate(this); } diff --git a/dom/media/AudioCaptureStream.cpp b/dom/media/AudioCaptureStream.cpp index 5d192d8320..96a0903d81 100644 --- a/dom/media/AudioCaptureStream.cpp +++ b/dom/media/AudioCaptureStream.cpp @@ -30,7 +30,7 @@ namespace mozilla static const uint32_t MONO = 1; AudioCaptureStream::AudioCaptureStream(DOMMediaStream* aWrapper, TrackID aTrackId) - : ProcessedMediaStream(aWrapper), mTrackId(aTrackId), mTrackCreated(false) + : ProcessedMediaStream(aWrapper), mTrackId(aTrackId), mStarted(false), mTrackCreated(false) { MOZ_ASSERT(NS_IsMainThread()); MOZ_COUNT_CTOR(AudioCaptureStream); @@ -43,10 +43,33 @@ AudioCaptureStream::~AudioCaptureStream() mMixer.RemoveCallback(this); } +void +AudioCaptureStream::Start() +{ + class Message : public ControlMessage { + public: + explicit Message(AudioCaptureStream* aStream) + : ControlMessage(aStream), mStream(aStream) {} + + virtual void Run() + { + mStream->mStarted = true; + } + + protected: + AudioCaptureStream* mStream; + }; + GraphImpl()->AppendMessage(MakeUnique(this)); +} + void AudioCaptureStream::ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) { + if (!mStarted) { + return; + } + uint32_t inputCount = mInputs.Length(); StreamBuffer::Track* track = EnsureTrack(mTrackId); // Notify the DOM everything is in order. @@ -61,11 +84,15 @@ AudioCaptureStream::ProcessInput(GraphTime aFrom, GraphTime aTo, mTrackCreated = true; } + if (IsFinishedOnGraphThread()) { + return; + } + // If the captured stream is connected back to a object on the page (be it an // HTMLMediaElement with a stream as source, or an AudioContext), a cycle // situation occur. This can work if it's an AudioContext with at least one // DelayNode, but the MSG will mute the whole cycle otherwise. - if (mFinished || InMutedCycle() || inputCount == 0) { + if (InMutedCycle() || inputCount == 0) { track->Get()->AppendNullData(aTo - aFrom); } else { // We mix down all the tracks of all inputs, to a stereo track. Everything diff --git a/dom/media/AudioCaptureStream.h b/dom/media/AudioCaptureStream.h index 752a3b63f2..27dab3aef5 100644 --- a/dom/media/AudioCaptureStream.h +++ b/dom/media/AudioCaptureStream.h @@ -26,6 +26,8 @@ public: explicit AudioCaptureStream(DOMMediaStream* aWrapper, TrackID aTrackId); virtual ~AudioCaptureStream(); + void Start(); + void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) override; protected: @@ -34,6 +36,7 @@ protected: uint32_t aSampleRate) override; AudioMixer mMixer; TrackID mTrackId; + bool mStarted; bool mTrackCreated; }; } diff --git a/dom/media/AudioSegment.cpp b/dom/media/AudioSegment.cpp index 1f24215732..b906f3462b 100644 --- a/dom/media/AudioSegment.cpp +++ b/dom/media/AudioSegment.cpp @@ -199,14 +199,12 @@ AudioSegment::WriteTo(uint64_t aID, AudioMixer& aMixer, uint32_t aOutputChannels offset += c.mDuration * aOutputChannels; -#if !defined(MOZILLA_XPCOMRT_API) if (!c.mTimeStamp.IsNull()) { TimeStamp now = TimeStamp::Now(); // would be more efficient to c.mTimeStamp to ms on create time then pass here LogTime(AsyncLatencyLogger::AudioMediaStreamTrack, aID, (now - c.mTimeStamp).ToMilliseconds(), c.mTimeStamp); } -#endif // !defined(MOZILLA_XPCOMRT_API) } if (offset) { diff --git a/dom/media/AudioSegment.h b/dom/media/AudioSegment.h index 252b7e5747..4ff75e10af 100644 --- a/dom/media/AudioSegment.h +++ b/dom/media/AudioSegment.h @@ -145,6 +145,8 @@ DownmixAndInterleave(const nsTArray& aChannelData, struct AudioChunk { typedef mozilla::AudioSampleFormat SampleFormat; + AudioChunk() : mPrincipalHandle(PRINCIPAL_HANDLE_NONE) {} + // Generic methods void SliceTo(StreamTime aStart, StreamTime aEnd) { @@ -190,6 +192,7 @@ struct AudioChunk { mDuration = aDuration; mVolume = 1.0f; mBufferFormat = AUDIO_FORMAT_SILENCE; + mPrincipalHandle = PRINCIPAL_HANDLE_NONE; } size_t ChannelCount() const { return mChannelData.Length(); } @@ -224,6 +227,8 @@ struct AudioChunk { return *reinterpret_cast*>(&mChannelData); } + PrincipalHandle GetPrincipalHandle() const { return mPrincipalHandle; } + StreamTime mDuration; // in frames within the buffer RefPtr mBuffer; // the buffer object whose lifetime is managed; null means data is all zeroes nsTArray mChannelData; // one pointer per channel; empty if and only if mBuffer is null @@ -232,6 +237,9 @@ struct AudioChunk { #ifdef MOZILLA_INTERNAL_API mozilla::TimeStamp mTimeStamp; // time at which this has been fetched from the MediaEngine #endif + // principalHandle for the data in this chunk. + // This can be compared to an nsIPrincipal* when back on main thread. + PrincipalHandle mPrincipalHandle; }; /** @@ -267,10 +275,7 @@ public: MOZ_ASSERT(channels == segmentChannelCount); output.SetLength(channels); bufferPtrs.SetLength(channels); -#if !defined(MOZILLA_XPCOMRT_API) -// FIXME Bug 1126414 - XPCOMRT does not support dom::WebAudioUtils::SpeexResamplerProcess uint32_t inFrames = c.mDuration; -#endif // !defined(MOZILLA_XPCOMRT_API) // Round up to allocate; the last frame may not be used. NS_ASSERTION((UINT32_MAX - aInRate + 1) / c.mDuration >= aOutRate, "Dropping samples"); @@ -279,14 +284,11 @@ public: T* out = output[i].AppendElements(outSize); uint32_t outFrames = outSize; -#if !defined(MOZILLA_XPCOMRT_API) -// FIXME Bug 1126414 - XPCOMRT does not support dom::WebAudioUtils::SpeexResamplerProcess const T* in = static_cast(c.mChannelData[i]); dom::WebAudioUtils::SpeexResamplerProcess(aResampler, i, in, &inFrames, out, &outFrames); MOZ_ASSERT(inFrames == c.mDuration); -#endif // !defined(MOZILLA_XPCOMRT_API) bufferPtrs[i] = out; output[i].SetLength(outFrames); @@ -307,7 +309,7 @@ public: void AppendFrames(already_AddRefed aBuffer, const nsTArray& aChannelData, - int32_t aDuration) + int32_t aDuration, const PrincipalHandle& aPrincipalHandle) { AudioChunk* chunk = AppendChunk(aDuration); chunk->mBuffer = aBuffer; @@ -319,10 +321,11 @@ public: #ifdef MOZILLA_INTERNAL_API chunk->mTimeStamp = TimeStamp::Now(); #endif + chunk->mPrincipalHandle = aPrincipalHandle; } void AppendFrames(already_AddRefed aBuffer, const nsTArray& aChannelData, - int32_t aDuration) + int32_t aDuration, const PrincipalHandle& aPrincipalHandle) { AudioChunk* chunk = AppendChunk(aDuration); chunk->mBuffer = aBuffer; @@ -334,6 +337,7 @@ public: #ifdef MOZILLA_INTERNAL_API chunk->mTimeStamp = TimeStamp::Now(); #endif + chunk->mPrincipalHandle = aPrincipalHandle; } // Consumes aChunk, and returns a pointer to the persistent copy of aChunk // in the segment. @@ -347,6 +351,7 @@ public: #ifdef MOZILLA_INTERNAL_API chunk->mTimeStamp = TimeStamp::Now(); #endif + chunk->mPrincipalHandle = aChunk->mPrincipalHandle; return chunk; } void ApplyVolume(float aVolume); diff --git a/dom/media/AudioStreamTrack.h b/dom/media/AudioStreamTrack.h index cca21b5f42..9a9a0bde9f 100644 --- a/dom/media/AudioStreamTrack.h +++ b/dom/media/AudioStreamTrack.h @@ -14,15 +14,29 @@ namespace dom { class AudioStreamTrack : public MediaStreamTrack { public: - AudioStreamTrack(DOMMediaStream* aStream, TrackID aTrackID, const nsString& aLabel) - : MediaStreamTrack(aStream, aTrackID, aLabel) {} + AudioStreamTrack(DOMMediaStream* aStream, TrackID aTrackID, + TrackID aInputTrackID, + MediaStreamTrackSource* aSource) + : MediaStreamTrack(aStream, aTrackID, aInputTrackID, aSource) {} JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; AudioStreamTrack* AsAudioStreamTrack() override { return this; } + const AudioStreamTrack* AsAudioStreamTrack() const override { return this; } + // WebIDL void GetKind(nsAString& aKind) override { aKind.AssignLiteral("audio"); } + +protected: + already_AddRefed CloneInternal(DOMMediaStream* aOwningStream, + TrackID aTrackID) override + { + return do_AddRef(new AudioStreamTrack(aOwningStream, + aTrackID, + mInputTrackID, + mSource)); + } }; } // namespace dom diff --git a/dom/media/CanvasCaptureMediaStream.cpp b/dom/media/CanvasCaptureMediaStream.cpp index 96322742b6..5fcaaa65f3 100644 --- a/dom/media/CanvasCaptureMediaStream.cpp +++ b/dom/media/CanvasCaptureMediaStream.cpp @@ -24,10 +24,12 @@ class OutputStreamDriver::StreamListener : public MediaStreamListener public: explicit StreamListener(OutputStreamDriver* aDriver, TrackID aTrackId, + PrincipalHandle aPrincipalHandle, SourceMediaStream* aSourceStream) : mEnded(false) , mSourceStream(aSourceStream) , mTrackId(aTrackId) + , mPrincipalHandle(aPrincipalHandle) , mMutex("CanvasCaptureMediaStream OutputStreamDriver::StreamListener") , mImage(nullptr) { @@ -55,7 +57,7 @@ public: RefPtr image = mImage; IntSize size = image ? image->GetSize() : IntSize(0, 0); VideoSegment segment; - segment.AppendFrame(image.forget(), delta, size); + segment.AppendFrame(image.forget(), delta, size, mPrincipalHandle); mSourceStream->AppendToTrack(mTrackId, &segment); } @@ -72,6 +74,7 @@ private: Atomic mEnded; const RefPtr mSourceStream; const TrackID mTrackId; + const PrincipalHandle mPrincipalHandle; Mutex mMutex; // The below members are protected by mMutex. @@ -79,10 +82,12 @@ private: }; OutputStreamDriver::OutputStreamDriver(SourceMediaStream* aSourceStream, - const TrackID& aTrackId) + const TrackID& aTrackId, + const PrincipalHandle& aPrincipalHandle) : FrameCaptureListener() , mSourceStream(aSourceStream) - , mStreamListener(new StreamListener(this, aTrackId, aSourceStream)) + , mStreamListener(new StreamListener(this, aTrackId, aPrincipalHandle, + aSourceStream)) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(mSourceStream); @@ -120,8 +125,9 @@ class TimerDriver : public OutputStreamDriver public: explicit TimerDriver(SourceMediaStream* aSourceStream, const double& aFPS, - const TrackID& aTrackId) - : OutputStreamDriver(aSourceStream, aTrackId) + const TrackID& aTrackId, + const PrincipalHandle& aPrincipalHandle) + : OutputStreamDriver(aSourceStream, aTrackId, aPrincipalHandle) , mFPS(aFPS) , mTimer(nullptr) { @@ -178,8 +184,9 @@ class AutoDriver : public OutputStreamDriver { public: explicit AutoDriver(SourceMediaStream* aSourceStream, - const TrackID& aTrackId) - : OutputStreamDriver(aSourceStream, aTrackId) {} + const TrackID& aTrackId, + const PrincipalHandle& aPrincipalHandle) + : OutputStreamDriver(aSourceStream, aTrackId, aPrincipalHandle) {} void NewFrame(already_AddRefed aImage) override { @@ -207,8 +214,10 @@ NS_IMPL_RELEASE_INHERITED(CanvasCaptureMediaStream, DOMMediaStream) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(CanvasCaptureMediaStream) NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream) -CanvasCaptureMediaStream::CanvasCaptureMediaStream(HTMLCanvasElement* aCanvas) - : mCanvas(aCanvas) +CanvasCaptureMediaStream::CanvasCaptureMediaStream(nsPIDOMWindow* aWindow, + HTMLCanvasElement* aCanvas) + : DOMMediaStream(aWindow, nullptr) + , mCanvas(aCanvas) , mOutputStreamDriver(nullptr) { } @@ -237,31 +246,34 @@ CanvasCaptureMediaStream::RequestFrame() nsresult CanvasCaptureMediaStream::Init(const dom::Optional& aFPS, - const TrackID& aTrackId) + const TrackID& aTrackId, + nsIPrincipal* aPrincipal) { + PrincipalHandle principalHandle = MakePrincipalHandle(aPrincipal); + if (!aFPS.WasPassed()) { mOutputStreamDriver = - new AutoDriver(GetInputStream()->AsSourceStream(), aTrackId); + new AutoDriver(GetInputStream()->AsSourceStream(), aTrackId, principalHandle); } else if (aFPS.Value() < 0) { return NS_ERROR_ILLEGAL_VALUE; } else { // Cap frame rate to 60 FPS for sanity double fps = std::min(60.0, aFPS.Value()); mOutputStreamDriver = - new TimerDriver(GetInputStream()->AsSourceStream(), fps, aTrackId); + new TimerDriver(GetInputStream()->AsSourceStream(), fps, aTrackId, principalHandle); } return NS_OK; } already_AddRefed -CanvasCaptureMediaStream::CreateSourceStream(nsIDOMWindow* aWindow, +CanvasCaptureMediaStream::CreateSourceStream(nsPIDOMWindow* aWindow, HTMLCanvasElement* aCanvas) { - RefPtr stream = new CanvasCaptureMediaStream(aCanvas); + RefPtr stream = new CanvasCaptureMediaStream(aWindow, aCanvas); MediaStreamGraph* graph = MediaStreamGraph::GetInstance(MediaStreamGraph::SYSTEM_THREAD_DRIVER, AudioChannel::Normal); - stream->InitSourceStream(aWindow, graph); + stream->InitSourceStream(graph); return stream.forget(); } diff --git a/dom/media/CanvasCaptureMediaStream.h b/dom/media/CanvasCaptureMediaStream.h index 8ec00e1d1f..72209cf45a 100644 --- a/dom/media/CanvasCaptureMediaStream.h +++ b/dom/media/CanvasCaptureMediaStream.h @@ -10,6 +10,8 @@ #include "mozilla/dom/HTMLCanvasElement.h" #include "StreamBuffer.h" +class nsIPrincipal; + namespace mozilla { class DOMMediaStream; class MediaStreamListener; @@ -68,7 +70,8 @@ class OutputStreamDriver : public FrameCaptureListener { public: OutputStreamDriver(SourceMediaStream* aSourceStream, - const TrackID& aTrackId); + const TrackID& aTrackId, + const PrincipalHandle& aPrincipalHandle); NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OutputStreamDriver); @@ -96,12 +99,13 @@ private: class CanvasCaptureMediaStream : public DOMMediaStream { public: - explicit CanvasCaptureMediaStream(HTMLCanvasElement* aCanvas); + CanvasCaptureMediaStream(nsPIDOMWindow* aWindow, HTMLCanvasElement* aCanvas); NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CanvasCaptureMediaStream, DOMMediaStream) - nsresult Init(const dom::Optional& aFPS, const TrackID& aTrackId); + nsresult Init(const dom::Optional& aFPS, const TrackID& aTrackId, + nsIPrincipal* aPrincipal); JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; @@ -114,7 +118,7 @@ public: * Create a CanvasCaptureMediaStream whose underlying stream is a SourceMediaStream. */ static already_AddRefed - CreateSourceStream(nsIDOMWindow* aWindow, + CreateSourceStream(nsPIDOMWindow* aWindow, HTMLCanvasElement* aCanvas); protected: diff --git a/dom/media/DOMMediaStream.cpp b/dom/media/DOMMediaStream.cpp index 3f5abffaf3..a5d72b5955 100644 --- a/dom/media/DOMMediaStream.cpp +++ b/dom/media/DOMMediaStream.cpp @@ -18,13 +18,19 @@ #include "mozilla/dom/VideoTrack.h" #include "mozilla/dom/VideoTrackList.h" #include "mozilla/dom/HTMLCanvasElement.h" -#include "mozilla/dom/MediaStreamError.h" -#include "mozilla/dom/Promise.h" +#include "mozilla/media/MediaUtils.h" #include "MediaStreamGraph.h" #include "AudioStreamTrack.h" #include "VideoStreamTrack.h" #include "Layers.h" +// GetCurrentTime is defined in winbase.h as zero argument macro forwarding to +// GetTickCount() and conflicts with NS_DECL_NSIDOMMEDIASTREAM, containing +// currentTime getter. +#ifdef GetCurrentTime +#undef GetCurrentTime +#endif + #ifdef LOG #undef LOG #endif @@ -32,6 +38,7 @@ using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::layers; +using namespace mozilla::media; static LazyLogModule gMediaStreamLog("MediaStream"); #define LOG(type, msg) MOZ_LOG(gMediaStreamLog, type, msg) @@ -85,18 +92,27 @@ DOMMediaStream::TrackPort::GetSourceTrackId() const return mInputPort ? mInputPort->GetSourceTrackId() : TRACK_INVALID; } -void -DOMMediaStream::TrackPort::BlockTrackId(TrackID aTrackId) +already_AddRefed> +DOMMediaStream::TrackPort::BlockSourceTrackId(TrackID aTrackId) { if (mInputPort) { - mInputPort->BlockTrackId(aTrackId); + return mInputPort->BlockSourceTrackId(aTrackId); } + RefPtr> rejected = new Pledge(); + rejected->Reject(NS_ERROR_FAILURE); + return rejected.forget(); } NS_IMPL_CYCLE_COLLECTION(DOMMediaStream::TrackPort, mTrack) NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DOMMediaStream::TrackPort, AddRef) NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(DOMMediaStream::TrackPort, Release) +NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaStreamTrackSourceGetter) +NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaStreamTrackSourceGetter) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaStreamTrackSourceGetter) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END +NS_IMPL_CYCLE_COLLECTION_0(MediaStreamTrackSourceGetter) /** * Listener registered on the Owned stream to detect added and ended owned @@ -111,7 +127,8 @@ public: void Forget() { mStream = nullptr; } - void DoNotifyTrackCreated(TrackID aTrackId, MediaSegment::Type aType) + void DoNotifyTrackCreated(TrackID aTrackID, MediaSegment::Type aType, + MediaStream* aInputStream, TrackID aInputTrackID) { MOZ_ASSERT(NS_IsMainThread()); @@ -119,20 +136,31 @@ public: return; } - MediaStreamTrack* track = mStream->FindOwnedDOMTrack( - mStream->GetOwnedStream(), aTrackId); - if (track) { - // This track has already been manually created. Abort. - return; + MediaStreamTrack* track = + mStream->FindOwnedDOMTrack(aInputStream, aInputTrackID); + if (!track) { + // Track had not been created on main thread before, create it now. + NS_WARN_IF_FALSE(!mStream->mTracks.IsEmpty(), + "A new track was detected on the input stream; creating " + "a corresponding MediaStreamTrack. Initial tracks " + "should be added manually to immediately and " + "synchronously be available to JS."); + RefPtr source; + if (mStream->mTrackSourceGetter) { + source = mStream->mTrackSourceGetter->GetMediaStreamTrackSource(aTrackID); + } + if (!source) { + NS_ASSERTION(false, "Dynamic track created without an explicit TrackSource"); + nsPIDOMWindow* window = mStream->GetParentObject(); + nsIDocument* doc = window ? window->GetExtantDoc() : nullptr; + nsIPrincipal* principal = doc ? doc->NodePrincipal() : nullptr; + source = new BasicUnstoppableTrackSource(principal); + } + track = mStream->CreateDOMTrack(aTrackID, aType, source); } - - NS_WARN_IF_FALSE(!mStream->mTracks.IsEmpty(), - "A new track was detected on the input stream; creating a corresponding MediaStreamTrack. " - "Initial tracks should be added manually to immediately and synchronously be available to JS."); - mStream->CreateOwnDOMTrack(aTrackId, aType, nsString()); } - void DoNotifyTrackEnded(TrackID aTrackId) + void DoNotifyTrackEnded(MediaStream* aInputStream, TrackID aInputTrackID) { MOZ_ASSERT(NS_IsMainThread()); @@ -141,7 +169,7 @@ public: } RefPtr track = - mStream->FindOwnedDOMTrack(mStream->GetOwnedStream(), aTrackId); + mStream->FindOwnedDOMTrack(aInputStream, aInputTrackID); NS_ASSERTION(track, "Owned MediaStreamTracks must be known by the DOMMediaStream"); if (track) { LOG(LogLevel::Debug, ("DOMMediaStream %p MediaStreamTrack %p ended at the source. Marking it ended.", @@ -158,14 +186,15 @@ public: { if (aTrackEvents & TRACK_EVENT_CREATED) { nsCOMPtr runnable = - NS_NewRunnableMethodWithArgs( + NS_NewRunnableMethodWithArgs( this, &OwnedStreamListener::DoNotifyTrackCreated, - aID, aQueuedMedia.GetType()); + aID, aQueuedMedia.GetType(), aInputStream, aInputTrackID); aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget()); } else if (aTrackEvents & TRACK_EVENT_ENDED) { nsCOMPtr runnable = - NS_NewRunnableMethodWithArgs( - this, &OwnedStreamListener::DoNotifyTrackEnded, aID); + NS_NewRunnableMethodWithArgs( + this, &OwnedStreamListener::DoNotifyTrackEnded, + aInputStream, aInputTrackID); aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget()); } } @@ -217,10 +246,7 @@ public: RefPtr endedPort = mStream->FindPlaybackTrackPort(*track); NS_ASSERTION(endedPort, "Playback track should have a TrackPort"); - if (endedPort && - endedPort->GetSourceTrackId() != TRACK_ANY && - endedPort->GetSourceTrackId() != TRACK_INVALID && - endedPort->GetSourceTrackId() != TRACK_NONE) { + if (endedPort && IsTrackIDExplicit(endedPort->GetSourceTrackId())) { // If a track connected to a locked-track input port ends, we destroy the // port to allow our playback stream to finish. // XXX (bug 1208316) This should not be necessary when MediaStreams don't @@ -277,6 +303,9 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMMediaStream, NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwnedTracks) NS_IMPL_CYCLE_COLLECTION_UNLINK(mTracks) NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsumersToKeepAlive) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mTrackSourceGetter) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrincipal) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mVideoPrincipal) NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMMediaStream, @@ -285,6 +314,9 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMMediaStream, NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwnedTracks) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTracks) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsumersToKeepAlive) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTrackSourceGetter) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrincipal) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVideoPrincipal) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_ADDREF_INHERITED(DOMMediaStream, DOMEventTargetHelper) @@ -310,11 +342,12 @@ NS_IMPL_RELEASE_INHERITED(DOMAudioNodeMediaStream, DOMMediaStream) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DOMAudioNodeMediaStream) NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream) -DOMMediaStream::DOMMediaStream() - : mLogicalStreamStartTime(0), mInputStream(nullptr), mOwnedStream(nullptr), - mPlaybackStream(nullptr), mOwnedPort(nullptr), mPlaybackPort(nullptr), - mTracksCreated(false), mNotifiedOfMediaStreamGraphShutdown(false), - mCORSMode(CORS_NONE) +DOMMediaStream::DOMMediaStream(nsPIDOMWindow* aWindow, + MediaStreamTrackSourceGetter* aTrackSourceGetter) + : mLogicalStreamStartTime(0), mWindow(aWindow), + mInputStream(nullptr), mOwnedStream(nullptr), mPlaybackStream(nullptr), + mTracksPendingRemoval(0), mTrackSourceGetter(aTrackSourceGetter), + mTracksCreated(false), mNotifiedOfMediaStreamGraphShutdown(false) { nsresult rv; nsCOMPtr uuidgen = @@ -349,6 +382,13 @@ DOMMediaStream::Destroy() mPlaybackListener->Forget(); mPlaybackListener = nullptr; } + for (const RefPtr& info : mTracks) { + // We must remove ourselves from each track's principal change observer list + // before we die. CC may have cleared info->mTrack so guard against it. + if (info->GetTrack()) { + info->GetTrack()->RemovePrincipalChangeObserver(this); + } + } if (mPlaybackPort) { mPlaybackPort->Destroy(); mPlaybackPort = nullptr; @@ -358,15 +398,15 @@ DOMMediaStream::Destroy() mOwnedPort = nullptr; } if (mPlaybackStream) { - mPlaybackStream->Destroy(); + mPlaybackStream->UnregisterUser(); mPlaybackStream = nullptr; } if (mOwnedStream) { - mOwnedStream->Destroy(); + mOwnedStream->UnregisterUser(); mOwnedStream = nullptr; } if (mInputStream) { - mInputStream->Destroy(); + mInputStream->UnregisterUser(); mInputStream = nullptr; } } @@ -412,22 +452,20 @@ DOMMediaStream::Constructor(const GlobalObject& aGlobal, const Sequence>& aTracks, ErrorResult& aRv) { - nsCOMPtr ownerWindow = do_QueryInterface(aGlobal.GetAsSupports()); + nsCOMPtr ownerWindow = do_QueryInterface(aGlobal.GetAsSupports()); if (!ownerWindow) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } - RefPtr newStream = new DOMMediaStream(); - newStream->mWindow = ownerWindow; + // Streams created from JS cannot have dynamically created tracks. + MediaStreamTrackSourceGetter* getter = nullptr; + RefPtr newStream = new DOMMediaStream(ownerWindow, getter); for (MediaStreamTrack& track : aTracks) { if (!newStream->GetPlaybackStream()) { - MOZ_RELEASE_ASSERT(track.GetStream()); - MOZ_RELEASE_ASSERT(track.GetStream()->GetPlaybackStream()); - MOZ_RELEASE_ASSERT(track.GetStream()->GetPlaybackStream()->Graph()); - MediaStreamGraph* graph = track.GetStream()->GetPlaybackStream()->Graph(); - newStream->InitPlaybackStreamCommon(graph); + MOZ_RELEASE_ASSERT(track.Graph()); + newStream->InitPlaybackStreamCommon(track.Graph()); } newStream->AddTrack(track); } @@ -501,14 +539,12 @@ DOMMediaStream::AddTrack(MediaStreamTrack& aTrack) } LOG(LogLevel::Info, ("DOMMediaStream %p Adding track %p (from stream %p with ID %d)", - this, &aTrack, aTrack.GetStream(), aTrack.GetTrackID())); + this, &aTrack, aTrack.mOwningStream.get(), aTrack.mTrackID)); - if (mPlaybackStream->Graph() != - aTrack.GetStream()->mPlaybackStream->Graph()) { + if (mPlaybackStream->Graph() != aTrack.Graph()) { NS_ASSERTION(false, "Cannot combine tracks from different MediaStreamGraphs"); LOG(LogLevel::Error, ("DOMMediaStream %p Own MSG %p != aTrack's MSG %p", - this, mPlaybackStream->Graph(), - aTrack.GetStream()->mPlaybackStream->Graph())); + this, mPlaybackStream->Graph(), aTrack.Graph())); nsAutoString trackId; aTrack.GetId(trackId); @@ -529,17 +565,10 @@ DOMMediaStream::AddTrack(MediaStreamTrack& aTrack) return; } - RefPtr addedDOMStream = aTrack.GetStream(); - MOZ_RELEASE_ASSERT(addedDOMStream); - - RefPtr owningStream = addedDOMStream->GetOwnedStream(); - MOZ_RELEASE_ASSERT(owningStream); - - CombineWithPrincipal(addedDOMStream->mPrincipal); - // Hook up the underlying track with our underlying playback stream. RefPtr inputPort = - GetPlaybackStream()->AllocateInputPort(owningStream, aTrack.GetTrackID()); + GetPlaybackStream()->AllocateInputPort(aTrack.GetOwnedStream(), + aTrack.mTrackID); RefPtr trackPort = new TrackPort(inputPort, &aTrack, TrackPort::InputPortOwnership::OWNED); mTracks.AppendElement(trackPort.forget()); @@ -552,7 +581,7 @@ void DOMMediaStream::RemoveTrack(MediaStreamTrack& aTrack) { LOG(LogLevel::Info, ("DOMMediaStream %p Removing track %p (from stream %p with ID %d)", - this, &aTrack, aTrack.GetStream(), aTrack.GetTrackID())); + this, &aTrack, aTrack.mOwningStream.get(), aTrack.mTrackID)); RefPtr toRemove = FindPlaybackTrackPort(aTrack); if (!toRemove) { @@ -564,13 +593,144 @@ DOMMediaStream::RemoveTrack(MediaStreamTrack& aTrack) // to block it in the port. Doing this for a locked track is still OK as it // will first block the track, then destroy the port. Both cause the track to // end. - toRemove->BlockTrackId(aTrack.GetTrackID()); + BlockPlaybackTrack(toRemove); DebugOnly removed = mTracks.RemoveElement(toRemove); MOZ_ASSERT(removed); + + NotifyTrackRemoved(&aTrack); + LOG(LogLevel::Debug, ("DOMMediaStream %p Removed track %p", this, &aTrack)); } +class ClonedStreamSourceGetter : + public MediaStreamTrackSourceGetter +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ClonedStreamSourceGetter, + MediaStreamTrackSourceGetter) + + explicit ClonedStreamSourceGetter(DOMMediaStream* aStream) + : mStream(aStream) {} + + already_AddRefed + GetMediaStreamTrackSource(TrackID aInputTrackID) override + { + MediaStreamTrack* sourceTrack = + mStream->FindOwnedDOMTrack(mStream->GetOwnedStream(), aInputTrackID); + MOZ_RELEASE_ASSERT(sourceTrack); + + return do_AddRef(&sourceTrack->GetSource()); + } + +protected: + virtual ~ClonedStreamSourceGetter() {} + + RefPtr mStream; +}; + +NS_IMPL_ADDREF_INHERITED(ClonedStreamSourceGetter, + MediaStreamTrackSourceGetter) +NS_IMPL_RELEASE_INHERITED(ClonedStreamSourceGetter, + MediaStreamTrackSourceGetter) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ClonedStreamSourceGetter) +NS_INTERFACE_MAP_END_INHERITING(MediaStreamTrackSourceGetter) +NS_IMPL_CYCLE_COLLECTION_INHERITED(ClonedStreamSourceGetter, + MediaStreamTrackSourceGetter, + mStream) + +already_AddRefed +DOMMediaStream::Clone() +{ + return CloneInternal(TrackForwardingOption::CURRENT); +} + +already_AddRefed +DOMMediaStream::CloneInternal(TrackForwardingOption aForwarding) +{ + RefPtr newStream = + new DOMMediaStream(GetParentObject(), new ClonedStreamSourceGetter(this)); + + LOG(LogLevel::Info, ("DOMMediaStream %p created clone %p, forwarding %s tracks", + this, newStream.get(), + aForwarding == TrackForwardingOption::ALL + ? "all" : "current")); + + MOZ_RELEASE_ASSERT(mPlaybackStream); + MOZ_RELEASE_ASSERT(mPlaybackStream->Graph()); + MediaStreamGraph* graph = mPlaybackStream->Graph(); + + // We initiate the owned and playback streams first, since we need to create + // all existing DOM tracks before we add the generic input port from + // mInputStream to mOwnedStream (see AllocateInputPort wrt. destination + // TrackID as to why). + newStream->InitOwnedStreamCommon(graph); + newStream->InitPlaybackStreamCommon(graph); + + // Set up existing DOM tracks. + TrackID allocatedTrackID = 1; + for (const RefPtr& info : mTracks) { + MediaStreamTrack& track = *info->GetTrack(); + + LOG(LogLevel::Debug, ("DOMMediaStream %p forwarding external track %p to clone %p", + this, &track, newStream.get())); + RefPtr trackClone = + newStream->CloneDOMTrack(track, allocatedTrackID++); + } + + if (aForwarding == TrackForwardingOption::ALL) { + // Set up an input port from our input stream to the new DOM stream's owned + // stream, to allow for dynamically added tracks at the source to appear in + // the clone. The clone may treat mInputStream as its own mInputStream but + // ownership remains with us. + newStream->mInputStream = mInputStream; + if (mInputStream) { + // We have already set up track-locked input ports for all existing DOM + // tracks, so now we need to block those in the generic input port to + // avoid ending up with double instances of them. + nsTArray tracksToBlock; + for (const RefPtr& info : mOwnedTracks) { + tracksToBlock.AppendElement(info->GetTrack()->mTrackID); + } + + newStream->mInputStream->RegisterUser(); + newStream->mOwnedPort = + newStream->mOwnedStream->AllocateInputPort(mInputStream, + TRACK_ANY, TRACK_ANY, 0, 0, + &tracksToBlock); + } + } + + return newStream.forget(); +} + +MediaStreamTrack* +DOMMediaStream::GetTrackById(const nsString& aId) +{ + for (const RefPtr& info : mTracks) { + nsString id; + info->GetTrack()->GetId(id); + if (id == aId) { + return info->GetTrack(); + } + } + return nullptr; +} + +MediaStreamTrack* +DOMMediaStream::GetOwnedTrackById(const nsString& aId) +{ + for (const RefPtr& info : mOwnedTracks) { + nsString id; + info->GetTrack()->GetId(id); + if (id == aId) { + return info->GetTrack(); + } + } + return nullptr; +} + bool DOMMediaStream::HasTrack(const MediaStreamTrack& aTrack) const { @@ -580,7 +740,25 @@ DOMMediaStream::HasTrack(const MediaStreamTrack& aTrack) const bool DOMMediaStream::OwnsTrack(const MediaStreamTrack& aTrack) const { - return (aTrack.GetStream() == this) && HasTrack(aTrack); + return !!FindOwnedTrackPort(aTrack); +} + +bool +DOMMediaStream::AddDirectListener(MediaStreamDirectListener* aListener) +{ + if (GetInputStream() && GetInputStream()->AsSourceStream()) { + GetInputStream()->AsSourceStream()->AddDirectListener(aListener); + return true; // application should ignore NotifyQueuedTrackData + } + return false; +} + +void +DOMMediaStream::RemoveDirectListener(MediaStreamDirectListener* aListener) +{ + if (GetInputStream() && GetInputStream()->AsSourceStream()) { + GetInputStream()->AsSourceStream()->RemoveDirectListener(aListener); + } } bool @@ -590,37 +768,36 @@ DOMMediaStream::IsFinished() } void -DOMMediaStream::InitSourceStream(nsIDOMWindow* aWindow, - MediaStreamGraph* aGraph) +DOMMediaStream::InitSourceStream(MediaStreamGraph* aGraph) { - mWindow = aWindow; InitInputStreamCommon(aGraph->CreateSourceStream(nullptr), aGraph); InitOwnedStreamCommon(aGraph); InitPlaybackStreamCommon(aGraph); } void -DOMMediaStream::InitTrackUnionStream(nsIDOMWindow* aWindow, - MediaStreamGraph* aGraph) +DOMMediaStream::InitTrackUnionStream(MediaStreamGraph* aGraph) { - mWindow = aWindow; InitInputStreamCommon(aGraph->CreateTrackUnionStream(nullptr), aGraph); InitOwnedStreamCommon(aGraph); InitPlaybackStreamCommon(aGraph); } void -DOMMediaStream::InitAudioCaptureStream(nsIDOMWindow* aWindow, - MediaStreamGraph* aGraph) +DOMMediaStream::InitAudioCaptureStream(nsIPrincipal* aPrincipal, MediaStreamGraph* aGraph) { - mWindow = aWindow; - const TrackID AUDIO_TRACK = 1; - InitInputStreamCommon(aGraph->CreateAudioCaptureStream(this, AUDIO_TRACK), aGraph); + RefPtr audioCaptureSource = + new BasicUnstoppableTrackSource(aPrincipal, MediaSourceEnum::AudioCapture); + + AudioCaptureStream* audioCaptureStream = + static_cast(aGraph->CreateAudioCaptureStream(this, AUDIO_TRACK)); + InitInputStreamCommon(audioCaptureStream, aGraph); InitOwnedStreamCommon(aGraph); InitPlaybackStreamCommon(aGraph); - CreateOwnDOMTrack(AUDIO_TRACK, MediaSegment::AUDIO, nsString()); + CreateDOMTrack(AUDIO_TRACK, MediaSegment::AUDIO, audioCaptureSource); + audioCaptureStream->Start(); } void @@ -630,6 +807,7 @@ DOMMediaStream::InitInputStreamCommon(MediaStream* aStream, MOZ_ASSERT(!mOwnedStream, "Input stream must be initialized before owned stream"); mInputStream = aStream; + mInputStream->RegisterUser(); } void @@ -641,6 +819,7 @@ DOMMediaStream::InitOwnedStreamCommon(MediaStreamGraph* aGraph) // streams. This is only needed for the playback stream. mOwnedStream = aGraph->CreateTrackUnionStream(nullptr); mOwnedStream->SetAutofinish(true); + mOwnedStream->RegisterUser(); if (mInputStream) { mOwnedPort = mOwnedStream->AllocateInputPort(mInputStream); } @@ -655,6 +834,7 @@ DOMMediaStream::InitPlaybackStreamCommon(MediaStreamGraph* aGraph) { mPlaybackStream = aGraph->CreateTrackUnionStream(this); mPlaybackStream->SetAutofinish(true); + mPlaybackStream->RegisterUser(); if (mOwnedStream) { mPlaybackPort = mPlaybackStream->AllocateInputPort(mOwnedStream); } @@ -667,101 +847,105 @@ DOMMediaStream::InitPlaybackStreamCommon(MediaStreamGraph* aGraph) } already_AddRefed -DOMMediaStream::CreateSourceStream(nsIDOMWindow* aWindow, - MediaStreamGraph* aGraph) +DOMMediaStream::CreateSourceStream(nsPIDOMWindow* aWindow, + MediaStreamGraph* aGraph, + MediaStreamTrackSourceGetter* aTrackSourceGetter) { - RefPtr stream = new DOMMediaStream(); - stream->InitSourceStream(aWindow, aGraph); + RefPtr stream = new DOMMediaStream(aWindow, aTrackSourceGetter); + stream->InitSourceStream(aGraph); return stream.forget(); } already_AddRefed -DOMMediaStream::CreateTrackUnionStream(nsIDOMWindow* aWindow, - MediaStreamGraph* aGraph) +DOMMediaStream::CreateTrackUnionStream(nsPIDOMWindow* aWindow, + MediaStreamGraph* aGraph, + MediaStreamTrackSourceGetter* aTrackSourceGetter) { - RefPtr stream = new DOMMediaStream(); - stream->InitTrackUnionStream(aWindow, aGraph); + RefPtr stream = new DOMMediaStream(aWindow, aTrackSourceGetter); + stream->InitTrackUnionStream(aGraph); return stream.forget(); } already_AddRefed -DOMMediaStream::CreateAudioCaptureStream(nsIDOMWindow* aWindow, +DOMMediaStream::CreateAudioCaptureStream(nsPIDOMWindow* aWindow, + nsIPrincipal* aPrincipal, MediaStreamGraph* aGraph) { - RefPtr stream = new DOMMediaStream(); - stream->InitAudioCaptureStream(aWindow, aGraph); + // Audio capture doesn't create tracks dynamically + MediaStreamTrackSourceGetter* getter = nullptr; + RefPtr stream = new DOMMediaStream(aWindow, getter); + stream->InitAudioCaptureStream(aPrincipal, aGraph); return stream.forget(); } void -DOMMediaStream::SetTrackEnabled(TrackID aTrackID, bool aEnabled) +DOMMediaStream::PrincipalChanged(MediaStreamTrack* aTrack) { - // XXX Bug 1208371 - This enables/disables the track across clones. - if (mInputStream) { - mInputStream->SetTrackEnabled(aTrackID, aEnabled); - } + MOZ_ASSERT(aTrack); + NS_ASSERTION(HasTrack(*aTrack), "Principal changed for an unknown track"); + LOG(LogLevel::Info, ("DOMMediaStream %p Principal changed for track %p", + this, aTrack)); + RecomputePrincipal(); } void -DOMMediaStream::StopTrack(TrackID aTrackID) +DOMMediaStream::RecomputePrincipal() { - if (mInputStream && mInputStream->AsSourceStream()) { - mInputStream->AsSourceStream()->EndTrack(aTrackID); + nsCOMPtr previousPrincipal = mPrincipal.forget(); + nsCOMPtr previousVideoPrincipal = mVideoPrincipal.forget(); + + if (mTracksPendingRemoval > 0) { + LOG(LogLevel::Info, ("DOMMediaStream %p RecomputePrincipal() Cannot " + "recompute stream principal with tracks pending " + "removal.", this)); + return; } -} -already_AddRefed -DOMMediaStream::ApplyConstraintsToTrack(TrackID aTrackID, - const MediaTrackConstraints& aConstraints, - ErrorResult &aRv) -{ - nsCOMPtr go = do_QueryInterface(mWindow); - RefPtr promise = Promise::Create(go, aRv); - MOZ_RELEASE_ASSERT(!aRv.Failed()); + LOG(LogLevel::Debug, ("DOMMediaStream %p Recomputing principal. " + "Old principal was %p.", this, previousPrincipal.get())); - promise->MaybeReject(new MediaStreamError( - static_cast(mWindow.get()), - NS_LITERAL_STRING("OverconstrainedError"), - NS_LITERAL_STRING(""), - NS_LITERAL_STRING(""))); - return promise.forget(); -} + // mPrincipal is recomputed based on all current tracks, and tracks that have + // not ended in our playback stream. + for (const RefPtr& info : mTracks) { + if (info->GetTrack()->Ended()) { + continue; + } + LOG(LogLevel::Debug, ("DOMMediaStream %p Taking live track %p with " + "principal %p into account.", this, + info->GetTrack(), info->GetTrack()->GetPrincipal())); + nsContentUtils::CombineResourcePrincipals(&mPrincipal, + info->GetTrack()->GetPrincipal()); + if (info->GetTrack()->AsVideoStreamTrack()) { + nsContentUtils::CombineResourcePrincipals(&mVideoPrincipal, + info->GetTrack()->GetPrincipal()); + } + } -bool -DOMMediaStream::CombineWithPrincipal(nsIPrincipal* aPrincipal) -{ - bool changed = - nsContentUtils::CombineResourcePrincipals(&mPrincipal, aPrincipal); - if (changed) { + LOG(LogLevel::Debug, ("DOMMediaStream %p new principal is %p.", + this, mPrincipal.get())); + + if (previousPrincipal != mPrincipal || + previousVideoPrincipal != mVideoPrincipal) { NotifyPrincipalChanged(); } - return changed; -} - -void -DOMMediaStream::SetPrincipal(nsIPrincipal* aPrincipal) -{ - mPrincipal = aPrincipal; - NotifyPrincipalChanged(); -} - -void -DOMMediaStream::SetCORSMode(CORSMode aCORSMode) -{ - MOZ_ASSERT(NS_IsMainThread()); - mCORSMode = aCORSMode; -} - -CORSMode -DOMMediaStream::GetCORSMode() -{ - MOZ_ASSERT(NS_IsMainThread()); - return mCORSMode; } void DOMMediaStream::NotifyPrincipalChanged() { + if (!mPrincipal) { + // When all tracks are removed, mPrincipal will change to nullptr. + LOG(LogLevel::Info, ("DOMMediaStream %p Principal changed to nothing.", + this)); + } else { + LOG(LogLevel::Info, ("DOMMediaStream %p Principal changed. Now: " + "null=%d, codebase=%d, expanded=%d, system=%d", this, + mPrincipal->GetIsNullPrincipal(), + mPrincipal->GetIsCodebasePrincipal(), + mPrincipal->GetIsExpandedPrincipal(), + mPrincipal->GetIsSystemPrincipal())); + } + for (uint32_t i = 0; i < mPrincipalChangeObservers.Length(); ++i) { mPrincipalChangeObservers[i]->PrincipalChanged(this); } @@ -769,32 +953,35 @@ DOMMediaStream::NotifyPrincipalChanged() bool -DOMMediaStream::AddPrincipalChangeObserver(PrincipalChangeObserver* aObserver) +DOMMediaStream::AddPrincipalChangeObserver( + PrincipalChangeObserver* aObserver) { return mPrincipalChangeObservers.AppendElement(aObserver) != nullptr; } bool -DOMMediaStream::RemovePrincipalChangeObserver(PrincipalChangeObserver* aObserver) +DOMMediaStream::RemovePrincipalChangeObserver( + PrincipalChangeObserver* aObserver) { return mPrincipalChangeObservers.RemoveElement(aObserver); } MediaStreamTrack* -DOMMediaStream::CreateOwnDOMTrack(TrackID aTrackID, MediaSegment::Type aType, const nsString& aLabel) +DOMMediaStream::CreateDOMTrack(TrackID aTrackID, MediaSegment::Type aType, + MediaStreamTrackSource* aSource) { MOZ_RELEASE_ASSERT(mInputStream); MOZ_RELEASE_ASSERT(mOwnedStream); - MOZ_ASSERT(FindOwnedDOMTrack(GetOwnedStream(), aTrackID) == nullptr); + MOZ_ASSERT(FindOwnedDOMTrack(GetInputStream(), aTrackID) == nullptr); MediaStreamTrack* track; switch (aType) { case MediaSegment::AUDIO: - track = new AudioStreamTrack(this, aTrackID, aLabel); + track = new AudioStreamTrack(this, aTrackID, aTrackID, aSource); break; case MediaSegment::VIDEO: - track = new VideoStreamTrack(this, aTrackID, aLabel); + track = new VideoStreamTrack(this, aTrackID, aTrackID, aSource); break; default: MOZ_CRASH("Unhandled track type"); @@ -802,44 +989,100 @@ DOMMediaStream::CreateOwnDOMTrack(TrackID aTrackID, MediaSegment::Type aType, co LOG(LogLevel::Debug, ("DOMMediaStream %p Created new track %p with ID %u", this, track, aTrackID)); - RefPtr ownedTrackPort = - new TrackPort(mOwnedPort, track, TrackPort::InputPortOwnership::EXTERNAL); - mOwnedTracks.AppendElement(ownedTrackPort.forget()); + mOwnedTracks.AppendElement( + new TrackPort(mOwnedPort, track, TrackPort::InputPortOwnership::EXTERNAL)); - RefPtr playbackTrackPort = - new TrackPort(mPlaybackPort, track, TrackPort::InputPortOwnership::EXTERNAL); - mTracks.AppendElement(playbackTrackPort.forget()); + mTracks.AppendElement( + new TrackPort(mPlaybackPort, track, TrackPort::InputPortOwnership::EXTERNAL)); NotifyTrackAdded(track); return track; } -MediaStreamTrack* -DOMMediaStream::FindOwnedDOMTrack(MediaStream* aOwningStream, TrackID aTrackID) const +already_AddRefed +DOMMediaStream::CloneDOMTrack(MediaStreamTrack& aTrack, + TrackID aCloneTrackID) { MOZ_RELEASE_ASSERT(mOwnedStream); + MOZ_RELEASE_ASSERT(mPlaybackStream); + MOZ_RELEASE_ASSERT(IsTrackIDExplicit(aCloneTrackID)); - if (aOwningStream != mOwnedStream) { - return nullptr; - } + TrackID inputTrackID = aTrack.mInputTrackID; + MediaStream* inputStream = aTrack.GetInputStream(); - for (const RefPtr& info : mOwnedTracks) { - if (info->GetTrack()->GetTrackID() == aTrackID) { - return info->GetTrack(); + RefPtr newTrack = aTrack.CloneInternal(this, aCloneTrackID); + + newTrack->mOriginalTrack = + aTrack.mOriginalTrack ? aTrack.mOriginalTrack.get() : &aTrack; + + LOG(LogLevel::Debug, ("DOMMediaStream %p Created new track %p cloned from stream %p track %d", + this, newTrack.get(), inputStream, inputTrackID)); + + RefPtr inputPort = + mOwnedStream->AllocateInputPort(inputStream, inputTrackID, aCloneTrackID); + + mOwnedTracks.AppendElement( + new TrackPort(inputPort, newTrack, TrackPort::InputPortOwnership::OWNED)); + + mTracks.AppendElement( + new TrackPort(mPlaybackPort, newTrack, TrackPort::InputPortOwnership::EXTERNAL)); + + NotifyTrackAdded(newTrack); + + newTrack->SetEnabled(aTrack.Enabled()); + + return newTrack.forget(); +} + +static DOMMediaStream::TrackPort* +FindTrackPortAmongTracks(const MediaStreamTrack& aTrack, + const nsTArray>& aTracks) +{ + for (const RefPtr& info : aTracks) { + if (info->GetTrack() == &aTrack) { + return info; } } return nullptr; } +MediaStreamTrack* +DOMMediaStream::FindOwnedDOMTrack(MediaStream* aInputStream, + TrackID aInputTrackID) const +{ + MOZ_RELEASE_ASSERT(mOwnedStream); + + for (const RefPtr& info : mOwnedTracks) { + if (info->GetInputPort() && + info->GetInputPort()->GetSource() == aInputStream && + info->GetTrack()->mInputTrackID == aInputTrackID) { + // This track is owned externally but in our playback stream. + return info->GetTrack(); + } + } + return nullptr; +} + +DOMMediaStream::TrackPort* +DOMMediaStream::FindOwnedTrackPort(const MediaStreamTrack& aTrack) const +{ + return FindTrackPortAmongTracks(aTrack, mOwnedTracks); +} + + MediaStreamTrack* DOMMediaStream::FindPlaybackDOMTrack(MediaStream* aInputStream, TrackID aInputTrackID) const { - MOZ_RELEASE_ASSERT(mPlaybackStream); + if (!mPlaybackStream) { + // One would think we can assert mPlaybackStream here, but track clones have + // a dummy DOMMediaStream that doesn't have a playback stream, so we can't. + return nullptr; + } for (const RefPtr& info : mTracks) { if (info->GetInputPort() == mPlaybackPort && aInputStream == mOwnedStream && - aInputTrackID == info->GetTrack()->GetTrackID()) { + info->GetTrack()->mInputTrackID == aInputTrackID) { // This track is in our owned and playback streams. return info->GetTrack(); } @@ -847,9 +1090,7 @@ DOMMediaStream::FindPlaybackDOMTrack(MediaStream* aInputStream, TrackID aInputTr info->GetInputPort()->GetSource() == aInputStream && info->GetSourceTrackId() == aInputTrackID) { // This track is owned externally but in our playback stream. - MOZ_ASSERT(aInputTrackID != TRACK_NONE); - MOZ_ASSERT(aInputTrackID != TRACK_INVALID); - MOZ_ASSERT(aInputTrackID != TRACK_ANY); + MOZ_ASSERT(IsTrackIDExplicit(aInputTrackID)); return info->GetTrack(); } } @@ -859,12 +1100,7 @@ DOMMediaStream::FindPlaybackDOMTrack(MediaStream* aInputStream, TrackID aInputTr DOMMediaStream::TrackPort* DOMMediaStream::FindPlaybackTrackPort(const MediaStreamTrack& aTrack) const { - for (const RefPtr& info : mTracks) { - if (info->GetTrack() == &aTrack) { - return info; - } - } - return nullptr; + return FindTrackPortAmongTracks(aTrack, mTracks); } void @@ -938,27 +1174,52 @@ DOMMediaStream::UnregisterTrackListener(TrackListener* aListener) } void -DOMMediaStream::NotifyTrackAdded( - const RefPtr& aTrack) +DOMMediaStream::NotifyTrackAdded(const RefPtr& aTrack) { MOZ_ASSERT(NS_IsMainThread()); + if (mTracksPendingRemoval > 0) { + // If there are tracks pending removal we may not degrade the current + // principals until those tracks have been confirmed removed from the + // playback stream. Instead combine with the new track and the (potentially) + // degraded principal will be calculated when it's safe. + nsContentUtils::CombineResourcePrincipals(&mPrincipal, + aTrack->GetPrincipal()); + LOG(LogLevel::Debug, ("DOMMediaStream %p saw a track get added. Combining " + "its principal %p into our while waiting for pending " + "tracks to be removed. New principal is %p.", + this, aTrack->GetPrincipal(), mPrincipal.get())); + if (aTrack->AsVideoStreamTrack()) { + nsContentUtils::CombineResourcePrincipals(&mVideoPrincipal, + aTrack->GetPrincipal()); + } + } else { + LOG(LogLevel::Debug, ("DOMMediaStream %p saw a track get added. " + "Recomputing principal.", this)); + RecomputePrincipal(); + } + + aTrack->AddPrincipalChangeObserver(this); + for (int32_t i = mTrackListeners.Length() - 1; i >= 0; --i) { - const RefPtr& listener = mTrackListeners[i]; - listener->NotifyTrackAdded(aTrack); + mTrackListeners[i]->NotifyTrackAdded(aTrack); } } void -DOMMediaStream::NotifyTrackRemoved( - const RefPtr& aTrack) +DOMMediaStream::NotifyTrackRemoved(const RefPtr& aTrack) { MOZ_ASSERT(NS_IsMainThread()); + aTrack->RemovePrincipalChangeObserver(this); + for (int32_t i = mTrackListeners.Length() - 1; i >= 0; --i) { - const RefPtr& listener = mTrackListeners[i]; - listener->NotifyTrackRemoved(aTrack); + mTrackListeners[i]->NotifyTrackRemoved(aTrack); } + + // Don't call RecomputePrincipal here as the track may still exist in the + // playback stream in the MediaStreamGraph. It will instead be called when the + // track has been confirmed removed by the graph. See BlockPlaybackTrack(). } void @@ -969,6 +1230,33 @@ DOMMediaStream::CreateAndAddPlaybackStreamListener(MediaStream* aStream) aStream->AddListener(mPlaybackListener); } +void +DOMMediaStream::BlockPlaybackTrack(TrackPort* aTrack) +{ + MOZ_ASSERT(aTrack); + ++mTracksPendingRemoval; + RefPtr> p = aTrack->BlockSourceTrackId(aTrack->GetTrack()->mTrackID); + RefPtr self = this; + p->Then([self] (const bool& aIgnore) { self->NotifyPlaybackTrackBlocked(); }, + [] (const nsresult& aIgnore) { NS_ERROR("Could not remove track from MSG"); } + ); +} + +void +DOMMediaStream::NotifyPlaybackTrackBlocked() +{ + MOZ_ASSERT(mTracksPendingRemoval > 0, + "A track reported finished blocking more times than we asked for"); + if (--mTracksPendingRemoval == 0) { + // The MediaStreamGraph has reported a track was blocked and we are not + // waiting for any further tracks to get blocked. It is now safe to + // recompute the principal based on our main thread track set state. + LOG(LogLevel::Debug, ("DOMMediaStream %p saw all tracks pending removal " + "finish. Recomputing principal.", this)); + RecomputePrincipal(); + } +} + DOMLocalMediaStream::~DOMLocalMediaStream() { if (mInputStream) { @@ -1006,34 +1294,30 @@ DOMLocalMediaStream::StopImpl() } already_AddRefed -DOMLocalMediaStream::CreateSourceStream(nsIDOMWindow* aWindow, - MediaStreamGraph* aGraph) +DOMLocalMediaStream::CreateSourceStream(nsPIDOMWindow* aWindow, + MediaStreamGraph* aGraph, + MediaStreamTrackSourceGetter* aTrackSourceGetter) { - RefPtr stream = new DOMLocalMediaStream(); - stream->InitSourceStream(aWindow, aGraph); + RefPtr stream = + new DOMLocalMediaStream(aWindow, aTrackSourceGetter); + stream->InitSourceStream(aGraph); return stream.forget(); } already_AddRefed -DOMLocalMediaStream::CreateTrackUnionStream(nsIDOMWindow* aWindow, - MediaStreamGraph* aGraph) +DOMLocalMediaStream::CreateTrackUnionStream(nsPIDOMWindow* aWindow, + MediaStreamGraph* aGraph, + MediaStreamTrackSourceGetter* aTrackSourceGetter) { - RefPtr stream = new DOMLocalMediaStream(); - stream->InitTrackUnionStream(aWindow, aGraph); + RefPtr stream = + new DOMLocalMediaStream(aWindow, aTrackSourceGetter); + stream->InitTrackUnionStream(aGraph); return stream.forget(); } -already_AddRefed -DOMLocalMediaStream::CreateAudioCaptureStream(nsIDOMWindow* aWindow, - MediaStreamGraph* aGraph) -{ - RefPtr stream = new DOMLocalMediaStream(); - stream->InitAudioCaptureStream(aWindow, aGraph); - return stream.forget(); -} - -DOMAudioNodeMediaStream::DOMAudioNodeMediaStream(AudioNode* aNode) -: mStreamNode(aNode) +DOMAudioNodeMediaStream::DOMAudioNodeMediaStream(nsPIDOMWindow* aWindow, AudioNode* aNode) + : DOMMediaStream(aWindow, nullptr), + mStreamNode(aNode) { } @@ -1042,17 +1326,32 @@ DOMAudioNodeMediaStream::~DOMAudioNodeMediaStream() } already_AddRefed -DOMAudioNodeMediaStream::CreateTrackUnionStream(nsIDOMWindow* aWindow, +DOMAudioNodeMediaStream::CreateTrackUnionStream(nsPIDOMWindow* aWindow, AudioNode* aNode, MediaStreamGraph* aGraph) { - RefPtr stream = new DOMAudioNodeMediaStream(aNode); - stream->InitTrackUnionStream(aWindow, aGraph); + RefPtr stream = new DOMAudioNodeMediaStream(aWindow, aNode); + stream->InitTrackUnionStream(aGraph); return stream.forget(); } -DOMHwMediaStream::DOMHwMediaStream() +DOMHwMediaStream::DOMHwMediaStream(nsPIDOMWindow* aWindow) + : DOMLocalMediaStream(aWindow, nullptr) { +#ifdef MOZ_WIDGET_GONK + if (!mWindow) { + NS_ERROR("Expected window here."); + mPrincipalHandle = PRINCIPAL_HANDLE_NONE; + return; + } + nsIDocument* doc = mWindow->GetExtantDoc(); + if (!doc) { + NS_ERROR("Expected document here."); + mPrincipalHandle = PRINCIPAL_HANDLE_NONE; + return; + } + mPrincipalHandle = MakePrincipalHandle(doc->NodePrincipal()); +#endif } DOMHwMediaStream::~DOMHwMediaStream() @@ -1060,14 +1359,15 @@ DOMHwMediaStream::~DOMHwMediaStream() } already_AddRefed -DOMHwMediaStream::CreateHwStream(nsIDOMWindow* aWindow, OverlayImage* aImage) +DOMHwMediaStream::CreateHwStream(nsPIDOMWindow* aWindow, + OverlayImage* aImage) { - RefPtr stream = new DOMHwMediaStream(); + RefPtr stream = new DOMHwMediaStream(aWindow); MediaStreamGraph* graph = MediaStreamGraph::GetInstance(MediaStreamGraph::SYSTEM_THREAD_DRIVER, AudioChannel::Normal); - stream->InitSourceStream(aWindow, graph); + stream->InitSourceStream(graph); stream->Init(stream->GetInputStream(), aImage); return stream.forget(); @@ -1101,7 +1401,7 @@ DOMHwMediaStream::Init(MediaStream* stream, OverlayImage* aImage) RefPtr image = static_cast(mOverlayImage.get()); mozilla::gfx::IntSize size = image->GetSize(); - segment.AppendFrame(image.forget(), delta, size); + segment.AppendFrame(image.forget(), delta, size, mPrincipalHandle); #endif srcStream->AddTrack(TRACK_VIDEO_PRIMARY, 0, new VideoSegment()); srcStream->AppendToTrack(TRACK_VIDEO_PRIMARY, &segment); @@ -1158,7 +1458,7 @@ DOMHwMediaStream::SetImageSize(uint32_t width, uint32_t height) mozilla::gfx::IntSize size = image->GetSize(); VideoSegment segment; - segment.AppendFrame(image.forget(), delta, size); + segment.AppendFrame(image.forget(), delta, size, mPrincipalHandle); srcStream->AppendToTrack(TRACK_VIDEO_PRIMARY, &segment); #endif } @@ -1194,7 +1494,7 @@ DOMHwMediaStream::SetOverlayImage(OverlayImage* aImage) mozilla::gfx::IntSize size = image->GetSize(); VideoSegment segment; - segment.AppendFrame(image.forget(), delta, size); + segment.AppendFrame(image.forget(), delta, size, mPrincipalHandle); srcStream->AppendToTrack(TRACK_VIDEO_PRIMARY, &segment); #endif } diff --git a/dom/media/DOMMediaStream.h b/dom/media/DOMMediaStream.h index 2d7e182185..434ca6c747 100644 --- a/dom/media/DOMMediaStream.h +++ b/dom/media/DOMMediaStream.h @@ -13,9 +13,8 @@ #include "StreamBuffer.h" #include "nsIDOMWindow.h" #include "nsIPrincipal.h" -#include "mozilla/PeerIdentity.h" #include "mozilla/DOMEventTargetHelper.h" -#include "mozilla/CORSMode.h" +#include "PrincipalChangeObserver.h" // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to // GetTickCount() and conflicts with NS_DECL_NSIDOMMEDIASTREAM, containing @@ -33,9 +32,10 @@ namespace mozilla { class DOMHwMediaStream; class DOMLocalMediaStream; +class DOMMediaStream; class MediaStream; -class MediaEngineSource; class MediaInputPort; +class MediaStreamDirectListener; class MediaStreamGraph; class ProcessedMediaStream; @@ -43,6 +43,7 @@ namespace dom { class AudioNode; class HTMLCanvasElement; class MediaStreamTrack; +class MediaStreamTrackSource; class AudioStreamTrack; class VideoStreamTrack; class AudioTrack; @@ -58,12 +59,47 @@ class ImageContainer; class OverlayImage; } // namespace layers -class MediaStreamDirectListener; +namespace media { +template class Pledge; +} // namespace media #define NS_DOMMEDIASTREAM_IID \ { 0x8cb65468, 0x66c0, 0x444e, \ { 0x89, 0x9f, 0x89, 0x1d, 0x9e, 0xd2, 0xbe, 0x7c } } +class OnTracksAvailableCallback { +public: + virtual ~OnTracksAvailableCallback() {} + virtual void NotifyTracksAvailable(DOMMediaStream* aStream) = 0; +}; + +/** + * Interface through which a DOMMediaStream can query its producer for a + * MediaStreamTrackSource. This will be used whenever a track occurs in the + * DOMMediaStream's owned stream that has not yet been created on the main + * thread (see DOMMediaStream::CreateOwnDOMTrack). + */ +class MediaStreamTrackSourceGetter : public nsISupports +{ + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS(MediaStreamTrackSourceGetter) + +public: + MediaStreamTrackSourceGetter() + { + MOZ_COUNT_CTOR(MediaStreamTrackSourceGetter); + } + + virtual already_AddRefed + GetMediaStreamTrackSource(TrackID aInputTrackID) = 0; + +protected: + virtual ~MediaStreamTrackSourceGetter() + { + MOZ_COUNT_DTOR(MediaStreamTrackSourceGetter); + } +}; + /** * DOM wrapper for MediaStreams. * @@ -171,12 +207,15 @@ class MediaStreamDirectListener; * ----> t2 ------------> t2 <- MediaStreamTrack Z' * (pointing to t2 in A') */ -class DOMMediaStream : public DOMEventTargetHelper +class DOMMediaStream : public DOMEventTargetHelper, + public dom::PrincipalChangeObserver { friend class DOMLocalMediaStream; + friend class dom::MediaStreamTrack; typedef dom::MediaStreamTrack MediaStreamTrack; typedef dom::AudioStreamTrack AudioStreamTrack; typedef dom::VideoStreamTrack VideoStreamTrack; + typedef dom::MediaStreamTrackSource MediaStreamTrackSource; typedef dom::AudioTrack AudioTrack; typedef dom::VideoTrack VideoTrack; typedef dom::AudioTrackList AudioTrackList; @@ -186,9 +225,9 @@ public: typedef dom::MediaTrackConstraints MediaTrackConstraints; class TrackListener { - NS_INLINE_DECL_REFCOUNTING(TrackListener) - public: + virtual ~TrackListener() {} + /** * Called when the DOMMediaStream has a new track added, either by * JS (addTrack()) or the source creating one. @@ -202,9 +241,6 @@ public: */ virtual void NotifyTrackRemoved(const RefPtr& aTrack) {}; - - protected: - virtual ~TrackListener() {} }; /** @@ -270,9 +306,10 @@ public: /** * Blocks aTrackId from going into mInputPort unless the port has been - * destroyed. + * destroyed. Returns a pledge that gets resolved when the MediaStreamGraph + * has applied the block in the playback stream. */ - void BlockTrackId(TrackID aTrackId); + already_AddRefed> BlockSourceTrackId(TrackID aTrackId); private: RefPtr mInputPort; @@ -283,7 +320,8 @@ public: const InputPortOwnership mOwnership; }; - DOMMediaStream(); + DOMMediaStream(nsPIDOMWindow* aWindow, + MediaStreamTrackSourceGetter* aTrackSourceGetter); NS_DECL_ISUPPORTS_INHERITED NS_REALLY_FORWARD_NSIDOMEVENTTARGET(DOMEventTargetHelper) @@ -291,7 +329,7 @@ public: DOMEventTargetHelper) NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOMMEDIASTREAM_IID) - nsIDOMWindow* GetParentObject() const + nsPIDOMWindow* GetParentObject() const { return mWindow; } @@ -323,8 +361,29 @@ public: void AddTrack(MediaStreamTrack& aTrack); void RemoveTrack(MediaStreamTrack& aTrack); + /** Identical to CloneInternal(TrackForwardingOption::EXPLICIT) */ + already_AddRefed Clone(); + // NON-WebIDL + /** + * Option to provide to CloneInternal() of which tracks should be forwarded + * from the source stream (`this`) to the returned stream clone. + * + * CURRENT forwards the tracks currently in the source stream's track set. + * ALL forwards like EXPLICIT plus any and all future tracks originating + * from the same input stream as the source DOMMediaStream (`this`). + */ + enum class TrackForwardingOption { + CURRENT, + ALL + }; + already_AddRefed CloneInternal(TrackForwardingOption aForwarding); + + MediaStreamTrack* GetTrackById(const nsString& aId); + + MediaStreamTrack* GetOwnedTrackById(const nsString& aId); + /** * Returns true if this DOMMediaStream has aTrack in its mPlaybackStream. */ @@ -337,13 +396,23 @@ public: /** * Returns the corresponding MediaStreamTrack if it's in our mOwnedStream. + * aInputTrackID should match the track's TrackID in its input stream. */ - MediaStreamTrack* FindOwnedDOMTrack(MediaStream* aOwningStream, TrackID aTrackID) const; + MediaStreamTrack* FindOwnedDOMTrack(MediaStream* aInputStream, + TrackID aInputTrackID) const; + + /** + * Returns the TrackPort connecting aTrack's input stream to mOwnedStream, + * or nullptr if aTrack is not owned by this DOMMediaStream. + */ + TrackPort* FindOwnedTrackPort(const MediaStreamTrack& aTrack) const; /** * Returns the corresponding MediaStreamTrack if it's in our mPlaybackStream. + * aInputTrackID should match the track's TrackID in its owned stream. */ - MediaStreamTrack* FindPlaybackDOMTrack(MediaStream* aOwningStream, TrackID aTrackID) const; + MediaStreamTrack* FindPlaybackDOMTrack(MediaStream* aInputStream, + TrackID aInputTrackID) const; /** * Returns the TrackPort connecting mOwnedStream to mPlaybackStream for aTrack. @@ -361,25 +430,11 @@ public: virtual MediaStream* GetCameraStream() const { return nullptr; } /** - * Overridden in DOMLocalMediaStreams to allow getUserMedia to pass - * data directly to RTCPeerConnection without going through graph queuing. - * Returns a bool to let us know if direct data will be delivered. + * Allows users to get access to media data without going through graph + * queuing. Returns a bool to let us know if direct data will be delivered. */ - virtual bool AddDirectListener(MediaStreamDirectListener *aListener) { return false; } - virtual void RemoveDirectListener(MediaStreamDirectListener *aListener) {} - - /** - * Overridden in DOMLocalMediaStreams to allow getUserMedia to disable - * media at the SourceMediaStream. - */ - virtual void SetTrackEnabled(TrackID aTrackID, bool aEnabled); - - virtual void StopTrack(TrackID aTrackID); - - virtual already_AddRefed - ApplyConstraintsToTrack(TrackID aTrackID, - const MediaTrackConstraints& aConstraints, - ErrorResult &aRv); + bool AddDirectListener(MediaStreamDirectListener *aListener); + void RemoveDirectListener(MediaStreamDirectListener *aListener); virtual DOMLocalMediaStream* AsDOMLocalMediaStream() { return nullptr; } virtual DOMHwMediaStream* AsDOMHwMediaStream() { return nullptr; } @@ -390,46 +445,32 @@ public: * can only be accessed by principals subsuming this principal. */ nsIPrincipal* GetPrincipal() { return mPrincipal; } - mozilla::CORSMode GetCORSMode(); - void SetCORSMode(mozilla::CORSMode aCORSMode); /** - * These are used in WebRTC. A peerIdentity constrained MediaStream cannot be sent - * across the network to anything other than a peer with the provided identity. - * If this is set, then mPrincipal should be an instance of nsNullPrincipal. + * Returns a principal indicating who may access video data of this stream. + * The video principal will be a combination of all live video tracks. */ - PeerIdentity* GetPeerIdentity() const { return mPeerIdentity; } - void SetPeerIdentity(PeerIdentity* aPeerIdentity) - { - mPeerIdentity = aPeerIdentity; - } + nsIPrincipal* GetVideoPrincipal() { return mVideoPrincipal; } + + // From PrincipalChangeObserver. + void PrincipalChanged(MediaStreamTrack* aTrack) override; /** - * Indicate that data will be contributed to this stream from origin aPrincipal. - * If aPrincipal is null, this is ignored. Otherwise, from now on the contents - * of this stream can only be accessed by principals that subsume aPrincipal. - * Returns true if the stream's principal changed. + * Add a PrincipalChangeObserver to this stream. + * + * Returns true if it was successfully added. + * + * Ownership of the PrincipalChangeObserver remains with the caller, and it's + * the caller's responsibility to remove the observer before it dies. */ - bool CombineWithPrincipal(nsIPrincipal* aPrincipal); + bool AddPrincipalChangeObserver(dom::PrincipalChangeObserver* aObserver); /** - * This is used in WebRTC to move from a protected state (nsNullPrincipal) to - * one where the stream is accessible to script. Don't call this. - * CombineWithPrincipal is almost certainly more appropriate. + * Remove an added PrincipalChangeObserver from this stream. + * + * Returns true if it was successfully removed. */ - void SetPrincipal(nsIPrincipal* aPrincipal); - - /** - * Used to learn about dynamic changes in principal occur. - * Operations relating to these observers must be confined to the main thread. - */ - class PrincipalChangeObserver - { - public: - virtual void PrincipalChanged(DOMMediaStream* aMediaStream) = 0; - }; - bool AddPrincipalChangeObserver(PrincipalChangeObserver* aObserver); - bool RemovePrincipalChangeObserver(PrincipalChangeObserver* aObserver); + bool RemovePrincipalChangeObserver(dom::PrincipalChangeObserver* aObserver); /** * Called when this stream's MediaStreamGraph has been shut down. Normally @@ -449,21 +490,25 @@ public: /** * Create a DOMMediaStream whose underlying input stream is a SourceMediaStream. */ - static already_AddRefed CreateSourceStream(nsIDOMWindow* aWindow, - MediaStreamGraph* aGraph); + static already_AddRefed CreateSourceStream(nsPIDOMWindow* aWindow, + MediaStreamGraph* aGraph, + MediaStreamTrackSourceGetter* aTrackSourceGetter = nullptr); /** * Create a DOMMediaStream whose underlying input stream is a TrackUnionStream. */ - static already_AddRefed CreateTrackUnionStream(nsIDOMWindow* aWindow, - MediaStreamGraph* aGraph); + static already_AddRefed CreateTrackUnionStream(nsPIDOMWindow* aWindow, + MediaStreamGraph* aGraph, + MediaStreamTrackSourceGetter* aTrackSourceGetter = nullptr); /** * Create an DOMMediaStream whose underlying input stream is an * AudioCaptureStream. */ - static already_AddRefed CreateAudioCaptureStream( - nsIDOMWindow* aWindow, MediaStreamGraph* aGraph); + static already_AddRefed + CreateAudioCaptureStream(nsPIDOMWindow* aWindow, + nsIPrincipal* aPrincipal, + MediaStreamGraph* aGraph); void SetLogicalStreamStartTime(StreamTime aTime) { @@ -476,13 +521,17 @@ public: * * Creates a MediaStreamTrack, adds it to mTracks and returns it. */ - MediaStreamTrack* CreateOwnDOMTrack(TrackID aTrackID, MediaSegment::Type aType, const nsString& aLabel); + MediaStreamTrack* CreateDOMTrack(TrackID aTrackID, MediaSegment::Type aType, + MediaStreamTrackSource* aSource); + + /** + * Creates a MediaStreamTrack cloned from aTrack, adds it to mTracks and + * returns it. + * aCloneTrackID is the TrackID the new track will get in mOwnedStream. + */ + already_AddRefed CloneDOMTrack(MediaStreamTrack& aTrack, + TrackID aCloneTrackID); - class OnTracksAvailableCallback { - public: - virtual ~OnTracksAvailableCallback() {} - virtual void NotifyTracksAvailable(DOMMediaStream* aStream) = 0; - }; // When the initial set of tracks has been added, run // aCallback->NotifyTracksAvailable. // It is allowed to do anything, including run script. @@ -504,16 +553,23 @@ public: } } + // Registers a track listener to this MediaStream, for listening to changes + // to our track set. The caller must call UnregisterTrackListener before + // being destroyed, so we don't hold on to a dead pointer. Main thread only. void RegisterTrackListener(TrackListener* aListener); + + // Unregisters a track listener from this MediaStream. The caller must call + // UnregisterTrackListener before being destroyed, so we don't hold on to + // a dead pointer. Main thread only. void UnregisterTrackListener(TrackListener* aListener); protected: virtual ~DOMMediaStream(); void Destroy(); - void InitSourceStream(nsIDOMWindow* aWindow, MediaStreamGraph* aGraph); - void InitTrackUnionStream(nsIDOMWindow* aWindow, MediaStreamGraph* aGraph); - void InitAudioCaptureStream(nsIDOMWindow* aWindow, MediaStreamGraph* aGraph); + void InitSourceStream(MediaStreamGraph* aGraph); + void InitTrackUnionStream(MediaStreamGraph* aGraph); + void InitAudioCaptureStream(nsIPrincipal* aPrincipal, MediaStreamGraph* aGraph); // Sets up aStream as mInputStream. A producer may append data to a // SourceMediaStream input stream, or connect another stream to a @@ -553,11 +609,29 @@ protected: // XXX Bug 1124630. Remove with CameraPreviewMediaStream. void CreateAndAddPlaybackStreamListener(MediaStream*); + /** + * Block a track in our playback stream. Calls NotifyPlaybackTrackBlocked() + * after the MediaStreamGraph has applied the block and the track is no longer + * live. + */ + void BlockPlaybackTrack(TrackPort* aTrack); + + /** + * Called on main thread after MediaStreamGraph has applied a track block in + * our playback stream. + */ + void NotifyPlaybackTrackBlocked(); + + // Recomputes the current principal of this stream based on the set of tracks + // it currently contains. PrincipalChangeObservers will be notified only if + // the principal changes. + void RecomputePrincipal(); + // StreamTime at which the currentTime attribute would return 0. StreamTime mLogicalStreamStartTime; // We need this to track our parent object. - nsCOMPtr mWindow; + nsCOMPtr mWindow; // MediaStreams are owned by the graph, but we tell them when to die, // and they won't die until we let them. @@ -588,6 +662,14 @@ protected: // MediaStreamTracks corresponding to tracks in our mPlaybackStream. AutoTArray, 2> mTracks; + // Number of MediaStreamTracks that have been removed on main thread but are + // waiting to be removed on MediaStreamGraph thread. + size_t mTracksPendingRemoval; + + // The interface through which we can query the stream producer for + // track sources. + RefPtr mTrackSourceGetter; + RefPtr mOwnedListener; RefPtr mPlaybackListener; @@ -604,18 +686,17 @@ protected: bool mNotifiedOfMediaStreamGraphShutdown; // The track listeners subscribe to changes in this stream's track set. - nsTArray> mTrackListeners; + nsTArray mTrackListeners; private: void NotifyPrincipalChanged(); - - // Principal identifying who may access the contents of this stream. + // Principal identifying who may access the collected contents of this stream. // If null, this stream can be used by anyone because it has no content yet. nsCOMPtr mPrincipal; - nsTArray mPrincipalChangeObservers; - // this is used in gUM and WebRTC to identify peers that this stream - // is allowed to be sent to - nsAutoPtr mPeerIdentity; + // Video principal is used by video element as access is requested to its + // image data. + nsCOMPtr mVideoPrincipal; + nsTArray*> mPrincipalChangeObservers; CORSMode mCORSMode; }; @@ -629,7 +710,9 @@ NS_DEFINE_STATIC_IID_ACCESSOR(DOMMediaStream, class DOMLocalMediaStream : public DOMMediaStream { public: - DOMLocalMediaStream() {} + explicit DOMLocalMediaStream(nsPIDOMWindow* aWindow, + MediaStreamTrackSourceGetter* aTrackSourceGetter) + : DOMMediaStream(aWindow, aTrackSourceGetter) {} NS_DECL_ISUPPORTS_INHERITED NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOMLOCALMEDIASTREAM_IID) @@ -638,27 +721,21 @@ public: void Stop(); - virtual MediaEngineSource* GetMediaEngine(TrackID aTrackID) { return nullptr; } - /** * Create an nsDOMLocalMediaStream whose underlying stream is a SourceMediaStream. */ static already_AddRefed - CreateSourceStream(nsIDOMWindow* aWindow, - MediaStreamGraph* aGraph); + CreateSourceStream(nsPIDOMWindow* aWindow, + MediaStreamGraph* aGraph, + MediaStreamTrackSourceGetter* aTrackSourceGetter = nullptr); /** * Create an nsDOMLocalMediaStream whose underlying stream is a TrackUnionStream. */ static already_AddRefed - CreateTrackUnionStream(nsIDOMWindow* aWindow, - MediaStreamGraph* aGraph); - - /** - * Create an nsDOMLocalMediaStream whose underlying stream is an - * AudioCaptureStream. */ - static already_AddRefed CreateAudioCaptureStream( - nsIDOMWindow* aWindow, MediaStreamGraph* aGraph); + CreateTrackUnionStream(nsPIDOMWindow* aWindow, + MediaStreamGraph* aGraph, + MediaStreamTrackSourceGetter* aTrackSourceGetter = nullptr); protected: virtual ~DOMLocalMediaStream(); @@ -673,7 +750,7 @@ class DOMAudioNodeMediaStream : public DOMMediaStream { typedef dom::AudioNode AudioNode; public: - explicit DOMAudioNodeMediaStream(AudioNode* aNode); + DOMAudioNodeMediaStream(nsPIDOMWindow* aWindow, AudioNode* aNode); NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DOMAudioNodeMediaStream, DOMMediaStream) @@ -682,7 +759,7 @@ public: * Create a DOMAudioNodeMediaStream whose underlying stream is a TrackUnionStream. */ static already_AddRefed - CreateTrackUnionStream(nsIDOMWindow* aWindow, + CreateTrackUnionStream(nsPIDOMWindow* aWindow, AudioNode* aNode, MediaStreamGraph* aGraph); @@ -704,9 +781,10 @@ class DOMHwMediaStream : public DOMLocalMediaStream #endif public: - DOMHwMediaStream(); + explicit DOMHwMediaStream(nsPIDOMWindow* aWindow); - static already_AddRefed CreateHwStream(nsIDOMWindow* aWindow, OverlayImage* aImage = nullptr); + static already_AddRefed CreateHwStream(nsPIDOMWindow* aWindow, + OverlayImage* aImage = nullptr); virtual DOMHwMediaStream* AsDOMHwMediaStream() override { return this; } int32_t RequestOverlayId(); void SetOverlayId(int32_t aOverlayId); @@ -724,6 +802,7 @@ private: const int DEFAULT_IMAGE_WIDTH = 400; const int DEFAULT_IMAGE_HEIGHT = 300; RefPtr mOverlayImage; + PrincipalHandle mPrincipalHandle; #endif }; diff --git a/dom/media/FrameStatistics.h b/dom/media/FrameStatistics.h index 9928719ab3..4485303225 100644 --- a/dom/media/FrameStatistics.h +++ b/dom/media/FrameStatistics.h @@ -20,8 +20,7 @@ public: mParsedFrames(0), mDecodedFrames(0), mPresentedFrames(0), - mDroppedFrames(0), - mCorruptFrames(0) {} + mDroppedFrames(0) {} // Returns number of frames which have been parsed from the media. // Can be called on any thread. @@ -49,12 +48,7 @@ public: // compoisition deadline. uint32_t GetDroppedFrames() { ReentrantMonitorAutoEnter mon(mReentrantMonitor); - return mDroppedFrames + mCorruptFrames; - } - - uint32_t GetCorruptedFrames() { - ReentrantMonitorAutoEnter mon(mReentrantMonitor); - return mCorruptFrames; + return mDroppedFrames; } // Increments the parsed and decoded frame counters by the passed in counts. @@ -76,11 +70,6 @@ public: ++mPresentedFrames; } - void NotifyCorruptFrame() { - ReentrantMonitorAutoEnter mon(mReentrantMonitor); - ++mCorruptFrames; - } - private: ~FrameStatistics() {} @@ -100,8 +89,6 @@ private: uint32_t mPresentedFrames; uint32_t mDroppedFrames; - - uint32_t mCorruptFrames; }; } // namespace mozilla diff --git a/dom/media/GraphDriver.cpp b/dom/media/GraphDriver.cpp index b2ab3a6bdb..68dfbd9e59 100644 --- a/dom/media/GraphDriver.cpp +++ b/dom/media/GraphDriver.cpp @@ -318,7 +318,7 @@ ThreadedDriver::RunThread() (long)stateComputedTime, (long)nextStateComputedTime)); nextStateComputedTime = stateComputedTime; } - STREAM_LOG(LogLevel::Debug, + STREAM_LOG(LogLevel::Verbose, ("interval[%ld; %ld] state[%ld; %ld]", (long)mIterationStart, (long)mIterationEnd, (long)stateComputedTime, (long)nextStateComputedTime)); diff --git a/dom/media/MediaDecoder.cpp b/dom/media/MediaDecoder.cpp index 092a5c8f69..647561925c 100644 --- a/dom/media/MediaDecoder.cpp +++ b/dom/media/MediaDecoder.cpp @@ -552,6 +552,8 @@ MediaDecoder::MediaDecoder(MediaDecoderOwner* aOwner) "MediaDecoder::mLogicallySeeking (Canonical)") , mSameOriginMedia(AbstractThread::MainThread(), false, "MediaDecoder::mSameOriginMedia (Canonical)") + , mMediaPrincipalHandle(AbstractThread::MainThread(), PRINCIPAL_HANDLE_NONE, + "MediaDecoder::mMediaPrincipalHandle (Canonical)") , mPlaybackBytesPerSecond(AbstractThread::MainThread(), 0.0, "MediaDecoder::mPlaybackBytesPerSecond (Canonical)") , mPlaybackRateReliable(AbstractThread::MainThread(), true, @@ -1187,6 +1189,8 @@ MediaDecoder::NotifyPrincipalChanged() { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!mShuttingDown); + nsCOMPtr newPrincipal = GetCurrentPrincipal(); + mMediaPrincipalHandle = MakePrincipalHandle(newPrincipal); mOwner->NotifyDecoderPrincipalChanged(); } diff --git a/dom/media/MediaDecoder.h b/dom/media/MediaDecoder.h index 0c9f077bb4..2725ae08c2 100644 --- a/dom/media/MediaDecoder.h +++ b/dom/media/MediaDecoder.h @@ -954,6 +954,10 @@ protected: // passed to MediaStreams when this is true. Canonical mSameOriginMedia; + // An identifier for the principal of the media. Used to track when + // main-thread induced principal changes get reflected on MSG thread. + Canonical mMediaPrincipalHandle; + // Estimate of the current playback rate (bytes/second). Canonical mPlaybackBytesPerSecond; @@ -998,6 +1002,9 @@ public: AbstractCanonical* CanonicalSameOriginMedia() { return &mSameOriginMedia; } + AbstractCanonical* CanonicalMediaPrincipalHandle() { + return &mMediaPrincipalHandle; + } AbstractCanonical* CanonicalPlaybackBytesPerSecond() { return &mPlaybackBytesPerSecond; } diff --git a/dom/media/MediaDecoderReader.h b/dom/media/MediaDecoderReader.h index 30d5543860..b2e7b29a75 100644 --- a/dom/media/MediaDecoderReader.h +++ b/dom/media/MediaDecoderReader.h @@ -88,6 +88,8 @@ public: using WaitForDataPromise = MozPromise; + using BufferedUpdatePromise = MozPromise; + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDecoderReader) // The caller must ensure that Shutdown() is called before aDecoder is @@ -232,6 +234,16 @@ public: UpdateBuffered(); } + // Update the buffered ranges and upon doing so return a promise + // to indicate success. Overrides may need to do extra work to ensure + // buffered is up to date. + virtual RefPtr UpdateBufferedWithPromise() + { + MOZ_ASSERT(OnTaskQueue()); + UpdateBuffered(); + return BufferedUpdatePromise::CreateAndResolve(true, __func__); + } + virtual MediaQueue& AudioQueue() { return mAudioQueue; } virtual MediaQueue& VideoQueue() { return mVideoQueue; } @@ -269,8 +281,6 @@ public: // decoding. virtual bool VideoIsHardwareAccelerated() const { return false; } - virtual void DisableHardwareAcceleration() {} - TimedMetadataEventSource& TimedMetadataEvent() { return mTimedMetadataEvent; diff --git a/dom/media/MediaDecoderReaderWrapper.cpp b/dom/media/MediaDecoderReaderWrapper.cpp new file mode 100644 index 0000000000..6d7a9a6ac2 --- /dev/null +++ b/dom/media/MediaDecoderReaderWrapper.cpp @@ -0,0 +1,296 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/MozPromise.h" +#include "MediaDecoderReaderWrapper.h" + +namespace mozilla { + +extern LazyLogModule gMediaDecoderLog; + +#undef LOG +#define LOG(...) \ + MOZ_LOG(gMediaDecoderLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) + +// StartTimeRendezvous is a helper class that quarantines the first sample +// until it gets a sample from both channels, such that we can be guaranteed +// to know the start time by the time On{Audio,Video}Decoded is called on MDSM. +class StartTimeRendezvous { + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(StartTimeRendezvous); + typedef MediaDecoderReader::AudioDataPromise AudioDataPromise; + typedef MediaDecoderReader::VideoDataPromise VideoDataPromise; + +public: + StartTimeRendezvous(AbstractThread* aOwnerThread, + bool aHasAudio, + bool aHasVideo, + bool aForceZeroStartTime) + : mOwnerThread(aOwnerThread) + { + if (aForceZeroStartTime) { + mAudioStartTime.emplace(0); + mVideoStartTime.emplace(0); + return; + } + if (!aHasAudio) { + mAudioStartTime.emplace(INT64_MAX); + } + if (!aHasVideo) { + mVideoStartTime.emplace(INT64_MAX); + } + } + + void Destroy() + { + mAudioStartTime = Some(mAudioStartTime.refOr(INT64_MAX)); + mVideoStartTime = Some(mVideoStartTime.refOr(INT64_MAX)); + mHaveStartTimePromise.RejectIfExists(false, __func__); + } + + RefPtr AwaitStartTime() + { + if (HaveStartTime()) { + return HaveStartTimePromise::CreateAndResolve(true, __func__); + } + return mHaveStartTimePromise.Ensure(__func__); + } + + template + struct PromiseSampleType { + typedef typename PromiseType::ResolveValueType::element_type Type; + }; + + template + RefPtr + ProcessFirstSample(typename PromiseSampleType::Type* aData) + { + typedef typename PromiseSampleType::Type DataType; + typedef typename PromiseType::Private PromisePrivate; + MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); + + MaybeSetChannelStartTime(aData->mTime); + + RefPtr p = new PromisePrivate(__func__); + RefPtr data = aData; + RefPtr self = this; + AwaitStartTime()->Then( + mOwnerThread, __func__, + [p, data, self] () { + MOZ_ASSERT(self->mOwnerThread->IsCurrentThreadIn()); + p->Resolve(data, __func__); + }, + [p] () { + p->Reject(MediaDecoderReader::CANCELED, __func__); + }); + + return p.forget(); + } + + template + void FirstSampleRejected(MediaDecoderReader::NotDecodedReason aReason) + { + MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); + if (aReason == MediaDecoderReader::DECODE_ERROR) { + mHaveStartTimePromise.RejectIfExists(false, __func__); + } else if (aReason == MediaDecoderReader::END_OF_STREAM) { + LOG("StartTimeRendezvous=%p SampleType(%d) Has no samples.", + this, SampleType); + MaybeSetChannelStartTime(INT64_MAX); + } + } + + bool HaveStartTime() const + { + return mAudioStartTime.isSome() && mVideoStartTime.isSome(); + } + + int64_t StartTime() const + { + int64_t time = std::min(mAudioStartTime.ref(), mVideoStartTime.ref()); + return time == INT64_MAX ? 0 : time; + } + +private: + ~StartTimeRendezvous() {} + + template + void MaybeSetChannelStartTime(int64_t aStartTime) + { + if (ChannelStartTime(SampleType).isSome()) { + // If we're initialized with aForceZeroStartTime=true, the channel start + // times are already set. + return; + } + + LOG("StartTimeRendezvous=%p Setting SampleType(%d) start time to %lld", + this, SampleType, aStartTime); + + ChannelStartTime(SampleType).emplace(aStartTime); + if (HaveStartTime()) { + mHaveStartTimePromise.ResolveIfExists(true, __func__); + } + } + + Maybe& ChannelStartTime(MediaData::Type aType) + { + return aType == MediaData::AUDIO_DATA ? mAudioStartTime : mVideoStartTime; + } + + MozPromiseHolder mHaveStartTimePromise; + RefPtr mOwnerThread; + Maybe mAudioStartTime; + Maybe mVideoStartTime; +}; + +MediaDecoderReaderWrapper::MediaDecoderReaderWrapper(bool aIsRealTime, + AbstractThread* aOwnerThread, + MediaDecoderReader* aReader) + : mForceZeroStartTime(aIsRealTime || aReader->ForceZeroStartTime()) + , mOwnerThread(aOwnerThread) + , mReader(aReader) +{} + +MediaDecoderReaderWrapper::~MediaDecoderReaderWrapper() +{} + +media::TimeUnit +MediaDecoderReaderWrapper::StartTime() const +{ + MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); + MOZ_ASSERT(!mShutdown); + return media::TimeUnit::FromMicroseconds(mStartTimeRendezvous->StartTime()); +} + +RefPtr +MediaDecoderReaderWrapper::ReadMetadata() +{ + MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); + MOZ_ASSERT(!mShutdown); + return InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__, + &MediaDecoderReader::AsyncReadMetadata) + ->Then(mOwnerThread, __func__, this, + &MediaDecoderReaderWrapper::OnMetadataRead, + &MediaDecoderReaderWrapper::OnMetadataNotRead) + ->CompletionPromise(); +} + +RefPtr +MediaDecoderReaderWrapper::AwaitStartTime() +{ + MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); + MOZ_ASSERT(!mShutdown); + return mStartTimeRendezvous->AwaitStartTime(); +} + +RefPtr +MediaDecoderReaderWrapper::RequestAudioData() +{ + MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); + MOZ_ASSERT(!mShutdown); + + auto p = InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__, + &MediaDecoderReader::RequestAudioData); + + if (!mStartTimeRendezvous->HaveStartTime()) { + p = p->Then(mOwnerThread, __func__, mStartTimeRendezvous.get(), + &StartTimeRendezvous::ProcessFirstSample, + &StartTimeRendezvous::FirstSampleRejected) + ->CompletionPromise(); + } + + return p->Then(mOwnerThread, __func__, this, + &MediaDecoderReaderWrapper::OnSampleDecoded, + &MediaDecoderReaderWrapper::OnNotDecoded) + ->CompletionPromise(); +} + +RefPtr +MediaDecoderReaderWrapper::RequestVideoData(bool aSkipToNextKeyframe, + media::TimeUnit aTimeThreshold) +{ + MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); + MOZ_ASSERT(!mShutdown); + + if (aTimeThreshold.ToMicroseconds() > 0 && + mStartTimeRendezvous->HaveStartTime()) { + aTimeThreshold += StartTime(); + } + + auto p = InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__, + &MediaDecoderReader::RequestVideoData, + aSkipToNextKeyframe, aTimeThreshold.ToMicroseconds()); + + if (!mStartTimeRendezvous->HaveStartTime()) { + p = p->Then(mOwnerThread, __func__, mStartTimeRendezvous.get(), + &StartTimeRendezvous::ProcessFirstSample, + &StartTimeRendezvous::FirstSampleRejected) + ->CompletionPromise(); + } + + return p->Then(mOwnerThread, __func__, this, + &MediaDecoderReaderWrapper::OnSampleDecoded, + &MediaDecoderReaderWrapper::OnNotDecoded) + ->CompletionPromise(); +} + +RefPtr +MediaDecoderReaderWrapper::Seek(SeekTarget aTarget, media::TimeUnit aEndTime) +{ + MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); + aTarget.SetTime(aTarget.GetTime() + StartTime()); + return InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__, + &MediaDecoderReader::Seek, aTarget, + aEndTime.ToMicroseconds()); +} + +void +MediaDecoderReaderWrapper::Shutdown() +{ + MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); + mShutdown = true; + if (mStartTimeRendezvous) { + mStartTimeRendezvous->Destroy(); + mStartTimeRendezvous = nullptr; + } +} + +void +MediaDecoderReaderWrapper::OnMetadataRead(MetadataHolder* aMetadata) +{ + MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); + if (mShutdown) { + return; + } + // Set up the start time rendezvous if it doesn't already exist (which is + // generally the case, unless we're coming out of dormant mode). + if (!mStartTimeRendezvous) { + mStartTimeRendezvous = new StartTimeRendezvous( + mOwnerThread, aMetadata->mInfo.HasAudio(), + aMetadata->mInfo.HasVideo(), mForceZeroStartTime); + + RefPtr self = this; + mStartTimeRendezvous->AwaitStartTime()->Then( + mOwnerThread, __func__, + [self] () { + NS_ENSURE_TRUE_VOID(!self->mShutdown); + self->mReader->DispatchSetStartTime(self->StartTime().ToMicroseconds()); + }, + [] () { + NS_WARNING("Setting start time on reader failed"); + }); + } +} + +void +MediaDecoderReaderWrapper::OnSampleDecoded(MediaData* aSample) +{ + MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); + if (!mShutdown) { + aSample->AdjustForStartTime(StartTime().ToMicroseconds()); + } +} + +} // namespace mozilla diff --git a/dom/media/MediaDecoderReaderWrapper.h b/dom/media/MediaDecoderReaderWrapper.h new file mode 100644 index 0000000000..5eb5a77aca --- /dev/null +++ b/dom/media/MediaDecoderReaderWrapper.h @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MediaDecoderReaderWrapper_h_ +#define MediaDecoderReaderWrapper_h_ + +#include "mozilla/AbstractThread.h" +#include "mozilla/RefPtr.h" +#include "nsISupportsImpl.h" + +#include "MediaDecoderReader.h" + +namespace mozilla { + +class StartTimeRendezvous; + +typedef MozPromise HaveStartTimePromise; + +/** + * A wrapper around MediaDecoderReader to offset the timestamps of Audio/Video + * samples by the start time to ensure MDSM can always assume zero start time. + * It also adjusts the seek target passed to Seek() to ensure correct seek time + * is passed to the underlying reader. + */ +class MediaDecoderReaderWrapper { + typedef MediaDecoderReader::MetadataPromise MetadataPromise; + typedef MediaDecoderReader::AudioDataPromise AudioDataPromise; + typedef MediaDecoderReader::VideoDataPromise VideoDataPromise; + typedef MediaDecoderReader::SeekPromise SeekPromise; + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDecoderReaderWrapper); + +public: + MediaDecoderReaderWrapper(bool aIsRealTime, + AbstractThread* aOwnerThread, + MediaDecoderReader* aReader); + + media::TimeUnit StartTime() const; + RefPtr ReadMetadata(); + RefPtr AwaitStartTime(); + RefPtr RequestAudioData(); + RefPtr RequestVideoData(bool aSkipToNextKeyframe, + media::TimeUnit aTimeThreshold); + RefPtr Seek(SeekTarget aTarget, media::TimeUnit aEndTime); + void Shutdown(); + +private: + ~MediaDecoderReaderWrapper(); + + void OnMetadataRead(MetadataHolder* aMetadata); + void OnMetadataNotRead() {} + void OnSampleDecoded(MediaData* aSample); + void OnNotDecoded() {} + + const bool mForceZeroStartTime; + const RefPtr mOwnerThread; + const RefPtr mReader; + + bool mShutdown = false; + RefPtr mStartTimeRendezvous; +}; + +} // namespace mozilla + +#endif // MediaDecoderReaderWrapper_h_ diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp index b1dfc673fe..34eba13745 100644 --- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -93,7 +93,7 @@ static const uint32_t LOW_AUDIO_USECS = 300000; // decoding more audio. If we increase the low audio threshold (see // LOW_AUDIO_USECS above) we'll also increase this value to ensure it's not // less than the low audio threshold. -const int64_t AMPLE_AUDIO_USECS = 1000000; +static const int64_t AMPLE_AUDIO_USECS = 2000000; } // namespace detail @@ -176,10 +176,15 @@ static int64_t DurationToUsecs(TimeDuration aDuration) { static const uint32_t MIN_VIDEO_QUEUE_SIZE = 3; static const uint32_t MAX_VIDEO_QUEUE_SIZE = 10; +#ifdef MOZ_APPLEMEDIA +static const uint32_t HW_VIDEO_QUEUE_SIZE = 10; +#else +static const uint32_t HW_VIDEO_QUEUE_SIZE = 3; +#endif static const uint32_t VIDEO_QUEUE_SEND_TO_COMPOSITOR_SIZE = 9999; static uint32_t sVideoQueueDefaultSize = MAX_VIDEO_QUEUE_SIZE; -static uint32_t sVideoQueueHWAccelSize = MIN_VIDEO_QUEUE_SIZE; +static uint32_t sVideoQueueHWAccelSize = HW_VIDEO_QUEUE_SIZE; static uint32_t sVideoQueueSendToCompositorSize = VIDEO_QUEUE_SEND_TO_COMPOSITOR_SIZE; static void InitVideoQueuePrefs() { @@ -190,7 +195,7 @@ static void InitVideoQueuePrefs() { sVideoQueueDefaultSize = Preferences::GetUint( "media.video-queue.default-size", MAX_VIDEO_QUEUE_SIZE); sVideoQueueHWAccelSize = Preferences::GetUint( - "media.video-queue.hw-accel-size", MIN_VIDEO_QUEUE_SIZE); + "media.video-queue.hw-accel-size", HW_VIDEO_QUEUE_SIZE); sVideoQueueSendToCompositorSize = Preferences::GetUint( "media.video-queue.send-to-compositor-size", VIDEO_QUEUE_SEND_TO_COMPOSITOR_SIZE); } @@ -209,13 +214,13 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder, mRealTime(aRealTime), mDispatchedStateMachine(false), mDelayedScheduler(mTaskQueue), - mState(DECODER_STATE_DECODING_NONE, "MediaDecoderStateMachine::mState"), + mState(DECODER_STATE_DECODING_METADATA, "MediaDecoderStateMachine::mState"), mCurrentFrameID(0), mObservedDuration(TimeUnit(), "MediaDecoderStateMachine::mObservedDuration"), mFragmentEndTime(-1), mReader(aReader), - mDecodedAudioEndTime(-1), - mDecodedVideoEndTime(-1), + mDecodedAudioEndTime(0), + mDecodedVideoEndTime(0), mPlaybackRate(1.0), mLowAudioThresholdUsecs(detail::LOW_AUDIO_USECS), mAmpleAudioThresholdUsecs(detail::AMPLE_AUDIO_USECS), @@ -232,9 +237,7 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder, mDecodeThreadWaiting(false), mDropAudioUntilNextDiscontinuity(false), mDropVideoUntilNextDiscontinuity(false), - mDecodeToSeekTarget(false), mCurrentTimeBeforeSeek(0), - mCorruptFrames(60), mDecodingFirstFrame(true), mSentLoadedMetadataEvent(false), mSentFirstFrameLoadedEvent(false), @@ -262,6 +265,8 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder, "MediaDecoderStateMachine::mPreservesPitch (Mirror)"), mSameOriginMedia(mTaskQueue, false, "MediaDecoderStateMachine::mSameOriginMedia (Mirror)"), + mMediaPrincipalHandle(mTaskQueue, PRINCIPAL_HANDLE_NONE, + "MediaDecoderStateMachine::mMediaPrincipalHandle (Mirror)"), mPlaybackBytesPerSecond(mTaskQueue, 0.0, "MediaDecoderStateMachine::mPlaybackBytesPerSecond (Mirror)"), mPlaybackRateReliable(mTaskQueue, true, @@ -339,9 +344,6 @@ MediaDecoderStateMachine::InitializationTask(MediaDecoder* aDecoder) { MOZ_ASSERT(OnTaskQueue()); - mStreamSink = new DecodedStream(mTaskQueue, mAudioQueue, mVideoQueue, - mOutputStreamManager, mSameOriginMedia.Ref()); - // Connect mirrors. mBuffered.Connect(mReader->CanonicalBuffered()); mEstimatedDuration.Connect(aDecoder->CanonicalEstimatedDuration()); @@ -353,6 +355,7 @@ MediaDecoderStateMachine::InitializationTask(MediaDecoder* aDecoder) mLogicalPlaybackRate.Connect(aDecoder->CanonicalPlaybackRate()); mPreservesPitch.Connect(aDecoder->CanonicalPreservesPitch()); mSameOriginMedia.Connect(aDecoder->CanonicalSameOriginMedia()); + mMediaPrincipalHandle.Connect(aDecoder->CanonicalMediaPrincipalHandle()); mPlaybackBytesPerSecond.Connect(aDecoder->CanonicalPlaybackBytesPerSecond()); mPlaybackRateReliable.Connect(aDecoder->CanonicalPlaybackRateReliable()); mDecoderPosition.Connect(aDecoder->CanonicalDecoderPosition()); @@ -389,11 +392,11 @@ MediaDecoderStateMachine::CreateAudioSink() already_AddRefed MediaDecoderStateMachine::CreateMediaSink(bool aAudioCaptured) { - // TODO: We can't really create a new DecodedStream until OutputStreamManager - // is extracted. It is tricky that the implementation of DecodedStream - // happens to allow reuse after shutdown without creating a new one. - RefPtr audioSink = aAudioCaptured ? - mStreamSink : CreateAudioSink(); + RefPtr audioSink = aAudioCaptured + ? new DecodedStream(mTaskQueue, mAudioQueue, mVideoQueue, + mOutputStreamManager, mSameOriginMedia.Ref(), + mMediaPrincipalHandle.Ref()) + : CreateAudioSink(); RefPtr mediaSink = new VideoSink(mTaskQueue, audioSink, mVideoQueue, @@ -430,9 +433,10 @@ MediaDecoderStateMachine::GetDecodedAudioDuration() { MOZ_ASSERT(OnTaskQueue()); if (mMediaSink->IsStarted()) { - // |mDecodedAudioEndTime == -1| means no decoded audio at all so the - // returned duration is 0. - return mDecodedAudioEndTime != -1 ? mDecodedAudioEndTime - GetClock() : 0; + // mDecodedAudioEndTime might be smaller than GetClock() when there is + // overlap between 2 adjacent audio samples or when we are playing + // a chained ogg file. + return std::max(mDecodedAudioEndTime - GetClock(), 0); } // MediaSink not started. All audio samples are in the queue. return AudioQueue().Duration(); @@ -493,14 +497,11 @@ bool MediaDecoderStateMachine::NeedToDecodeVideo() { MOZ_ASSERT(OnTaskQueue()); - SAMPLE_LOG("NeedToDecodeVideo() isDec=%d decToTar=%d minPrl=%d seek=%d enufVid=%d", - IsVideoDecoding(), mDecodeToSeekTarget, mMinimizePreroll, - mState == DECODER_STATE_SEEKING, - HaveEnoughDecodedVideo()); + SAMPLE_LOG("NeedToDecodeVideo() isDec=%d minPrl=%d enufVid=%d", + IsVideoDecoding(), mMinimizePreroll, HaveEnoughDecodedVideo()); return IsVideoDecoding() && - ((mState == DECODER_STATE_SEEKING && mDecodeToSeekTarget) || - (IsDecodingFirstFrame() && - IsVideoDecoding() && VideoQueue().GetSize() == 0) || + mState != DECODER_STATE_SEEKING && + ((IsDecodingFirstFrame() && VideoQueue().GetSize() == 0) || (!mMinimizePreroll && !HaveEnoughDecodedVideo())); } @@ -564,18 +565,15 @@ bool MediaDecoderStateMachine::NeedToDecodeAudio() { MOZ_ASSERT(OnTaskQueue()); - SAMPLE_LOG("NeedToDecodeAudio() isDec=%d decToTar=%d minPrl=%d seek=%d enufAud=%d", - IsAudioDecoding(), mDecodeToSeekTarget, mMinimizePreroll, - mState == DECODER_STATE_SEEKING, + SAMPLE_LOG("NeedToDecodeAudio() isDec=%d minPrl=%d enufAud=%d", + IsAudioDecoding(), mMinimizePreroll, HaveEnoughDecodedAudio(mAmpleAudioThresholdUsecs * mPlaybackRate)); return IsAudioDecoding() && - ((mState == DECODER_STATE_SEEKING && mDecodeToSeekTarget) || - (IsDecodingFirstFrame() && - IsAudioDecoding() && AudioQueue().GetSize() == 0) || + mState != DECODER_STATE_SEEKING && + ((IsDecodingFirstFrame() && AudioQueue().GetSize() == 0) || (!mMinimizePreroll && - !HaveEnoughDecodedAudio(mAmpleAudioThresholdUsecs * mPlaybackRate) && - (mState != DECODER_STATE_SEEKING || mDecodeToSeekTarget))); + !HaveEnoughDecodedAudio(mAmpleAudioThresholdUsecs * mPlaybackRate))); } bool @@ -612,7 +610,9 @@ MediaDecoderStateMachine::OnAudioDecoded(MediaData* aAudioSample) MOZ_ASSERT(audio); mAudioDataRequest.Complete(); aAudioSample->AdjustForStartTime(StartTime()); - mDecodedAudioEndTime = audio->GetEndTime(); + + // audio->GetEndTime() is not always mono-increasing in chained ogg. + mDecodedAudioEndTime = std::max(audio->GetEndTime(), mDecodedAudioEndTime); SAMPLE_LOG("OnAudioDecoded [%lld,%lld] disc=%d", (audio ? audio->mTime : -1), @@ -904,17 +904,15 @@ MediaDecoderStateMachine::OnVideoDecoded(MediaData* aVideoSample) MOZ_ASSERT(video); mVideoDataRequest.Complete(); aVideoSample->AdjustForStartTime(StartTime()); - mDecodedVideoEndTime = video ? video->GetEndTime() : mDecodedVideoEndTime; + + // Handle abnormal or negative timestamps. + mDecodedVideoEndTime = std::max(mDecodedVideoEndTime, video->GetEndTime()); SAMPLE_LOG("OnVideoDecoded [%lld,%lld] disc=%d", (video ? video->mTime : -1), (video ? video->GetEndTime() : -1), (video ? video->mDiscontinuity : 0)); - // Check frame validity here for every decoded frame in order to have a - // better chance to make the decision of turning off HW acceleration. - CheckFrameValidity(aVideoSample->As()); - switch (mState) { case DECODER_STATE_BUFFERING: { // If we're buffering, this may be the sample we need to stop buffering. @@ -1031,7 +1029,8 @@ MediaDecoderStateMachine::CheckIfSeekComplete() audioSeekComplete, videoSeekComplete); if (audioSeekComplete && videoSeekComplete) { - mDecodeToSeekTarget = false; + NS_ASSERTION(AudioQueue().GetSize() <= 1, "Should decode at most one sample"); + NS_ASSERTION(VideoQueue().GetSize() <= 1, "Should decode at most one sample"); SeekCompleted(); } } @@ -1084,7 +1083,11 @@ nsresult MediaDecoderStateMachine::Init() MOZ_ASSERT(NS_IsMainThread()); nsresult rv = mReader->Init(); NS_ENSURE_SUCCESS(rv, rv); - ScheduleStateMachineCrossThread(); + + nsCOMPtr r = NS_NewRunnableMethod( + this, &MediaDecoderStateMachine::ReadMetadata); + OwnerThread()->Dispatch(r.forget()); + return NS_OK; } @@ -1189,7 +1192,6 @@ void MediaDecoderStateMachine::UpdatePlaybackPosition(int64_t aTime) } static const char* const gMachineStateStr[] = { - "NONE", "DECODING_METADATA", "WAIT_FOR_CDM", "DORMANT", @@ -1332,9 +1334,9 @@ MediaDecoderStateMachine::SetDormant(bool aDormant) nsCOMPtr r = NS_NewRunnableMethod(mReader, &MediaDecoderReader::ReleaseMediaResources); DecodeTaskQueue()->Dispatch(r.forget()); } else if ((aDormant != true) && (mState == DECODER_STATE_DORMANT)) { - ScheduleStateMachine(); mDecodingFirstFrame = true; - SetState(DECODER_STATE_DECODING_NONE); + SetState(DECODER_STATE_DECODING_METADATA); + ReadMetadata(); } } @@ -1349,6 +1351,8 @@ MediaDecoderStateMachine::Shutdown() ScheduleStateMachine(); SetState(DECODER_STATE_SHUTDOWN); + mBufferedUpdateRequest.DisconnectIfExists(); + mQueuedSeek.RejectIfExists(__func__); mPendingSeek.RejectIfExists(__func__); mCurrentSeek.RejectIfExists(__func__); @@ -1495,6 +1499,24 @@ void MediaDecoderStateMachine::BufferedRangeUpdated() } } +void +MediaDecoderStateMachine::ReadMetadata() +{ + MOZ_ASSERT(OnTaskQueue()); + MOZ_ASSERT(!IsShutdown()); + MOZ_ASSERT(mState == DECODER_STATE_DECODING_METADATA); + MOZ_ASSERT(!mMetadataRequest.Exists()); + + DECODER_LOG("Dispatching AsyncReadMetadata"); + // Set mode to METADATA since we are about to read metadata. + mResource->SetReadMode(MediaCacheStream::MODE_METADATA); + mMetadataRequest.Begin(InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__, + &MediaDecoderReader::AsyncReadMetadata) + ->Then(OwnerThread(), __func__, this, + &MediaDecoderStateMachine::OnMetadataRead, + &MediaDecoderStateMachine::OnMetadataNotRead)); +} + RefPtr MediaDecoderStateMachine::Seek(SeekTarget aTarget) { @@ -1653,8 +1675,8 @@ MediaDecoderStateMachine::InitiateSeek() self->mSeekRequest.Complete(); // We must decode the first samples of active streams, so we can determine // the new stream time. So dispatch tasks to do that. - self->mDecodeToSeekTarget = true; - self->DispatchDecodeTasksIfNeeded(); + self->EnsureAudioDecodeTaskQueued(); + self->EnsureVideoDecodeTaskQueued(); }, [self] (nsresult aResult) -> void { self->mSeekRequest.Complete(); MOZ_ASSERT(NS_FAILED(aResult), "Cancels should also disconnect mSeekRequest"); @@ -1781,7 +1803,10 @@ MediaDecoderStateMachine::RequestVideoData() bool skipToNextKeyFrame = mSentFirstFrameLoadedEvent && NeedToSkipToNextKeyframe(); - int64_t currentTime = mState == DECODER_STATE_SEEKING ? 0 : GetMediaTime(); + + int64_t currentTime = + mState == DECODER_STATE_SEEKING || !mSentFirstFrameLoadedEvent + ? 0 : GetMediaTime() + StartTime(); SAMPLE_LOG("Queueing video task - queued=%i, decoder-queued=%o, skip=%i, time=%lld", VideoQueue().GetSize(), mReader->SizeOfVideoQueueInFrames(), skipToNextKeyFrame, @@ -1898,9 +1923,6 @@ bool MediaDecoderStateMachine::HasLowUndecodedData(int64_t aUsecs) } int64_t endOfDecodedAudioData = INT64_MAX; if (HasAudio() && !AudioQueue().AtEndOfStream()) { - // mDecodedAudioEndTime could be -1 when no audio samples are decoded. - // But that is fine since we consider ourself as low in decoded data when - // we don't have any decoded audio samples at all. endOfDecodedAudioData = mDecodedAudioEndTime; } int64_t endOfDecodedData = std::min(endOfDecodedVideoData, endOfDecodedAudioData); @@ -2044,12 +2066,25 @@ void MediaDecoderStateMachine::EnqueueFirstFrameLoadedEvent() { MOZ_ASSERT(OnTaskQueue()); - MediaDecoderEventVisibility visibility = - mSentFirstFrameLoadedEvent ? MediaDecoderEventVisibility::Suppressed - : MediaDecoderEventVisibility::Observable; - mFirstFrameLoadedEvent.Notify(nsAutoPtr(new MediaInfo(mInfo)), - Move(visibility)); + // Track value of mSentFirstFrameLoadedEvent from before updating it + bool firstFrameBeenLoaded = mSentFirstFrameLoadedEvent; mSentFirstFrameLoadedEvent = true; + RefPtr self = this; + mBufferedUpdateRequest.Begin(InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__, + &MediaDecoderReader::UpdateBufferedWithPromise) + ->Then(OwnerThread(), + __func__, + // Resolve + [self, firstFrameBeenLoaded]() { + self->mBufferedUpdateRequest.Complete(); + MediaDecoderEventVisibility visibility = + firstFrameBeenLoaded ? MediaDecoderEventVisibility::Suppressed + : MediaDecoderEventVisibility::Observable; + self->mFirstFrameLoadedEvent.Notify(nsAutoPtr(new MediaInfo(self->mInfo)), + Move(visibility)); + }, + // Reject + []() { MOZ_CRASH("Should not reach"); })); } bool @@ -2204,6 +2239,7 @@ MediaDecoderStateMachine::FinishShutdown() mLogicalPlaybackRate.DisconnectIfConnected(); mPreservesPitch.DisconnectIfConnected(); mSameOriginMedia.DisconnectIfConnected(); + mMediaPrincipalHandle.DisconnectIfConnected(); mPlaybackBytesPerSecond.DisconnectIfConnected(); mPlaybackRateReliable.DisconnectIfConnected(); mDecoderPosition.DisconnectIfConnected(); @@ -2240,29 +2276,9 @@ nsresult MediaDecoderStateMachine::RunStateMachine() case DECODER_STATE_SHUTDOWN: case DECODER_STATE_DORMANT: case DECODER_STATE_WAIT_FOR_CDM: + case DECODER_STATE_DECODING_METADATA: return NS_OK; - case DECODER_STATE_DECODING_NONE: { - SetState(DECODER_STATE_DECODING_METADATA); - ScheduleStateMachine(); - return NS_OK; - } - - case DECODER_STATE_DECODING_METADATA: { - if (!mMetadataRequest.Exists()) { - DECODER_LOG("Dispatching AsyncReadMetadata"); - // Set mode to METADATA since we are about to read metadata. - mResource->SetReadMode(MediaCacheStream::MODE_METADATA); - mMetadataRequest.Begin(InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__, - &MediaDecoderReader::AsyncReadMetadata) - ->Then(OwnerThread(), __func__, this, - &MediaDecoderStateMachine::OnMetadataRead, - &MediaDecoderStateMachine::OnMetadataNotRead)); - - } - return NS_OK; - } - case DECODER_STATE_DECODING: { if (IsDecodingFirstFrame()) { // We haven't completed decoding our first frames, we can't start @@ -2404,16 +2420,15 @@ MediaDecoderStateMachine::Reset() // hence the DECODING_NONE case below. MOZ_ASSERT(IsShutdown() || mState == DECODER_STATE_SEEKING || - mState == DECODER_STATE_DORMANT || - mState == DECODER_STATE_DECODING_NONE); + mState == DECODER_STATE_DORMANT); // Stop the audio thread. Otherwise, MediaSink might be accessing AudioQueue // outside of the decoder monitor while we are clearing the queue and causes // crash for no samples to be popped. StopMediaSink(); - mDecodedVideoEndTime = -1; - mDecodedAudioEndTime = -1; + mDecodedVideoEndTime = 0; + mDecodedAudioEndTime = 0; mAudioCompleted = false; mVideoCompleted = false; AudioQueue().Reset(); @@ -2421,7 +2436,6 @@ MediaDecoderStateMachine::Reset() mFirstVideoFrameAfterSeek = nullptr; mDropAudioUntilNextDiscontinuity = true; mDropVideoUntilNextDiscontinuity = true; - mDecodeToSeekTarget = false; mMetadataRequest.DisconnectIfExists(); mAudioDataRequest.DisconnectIfExists(); @@ -2437,33 +2451,6 @@ MediaDecoderStateMachine::Reset() DecodeTaskQueue()->Dispatch(resetTask.forget()); } -void -MediaDecoderStateMachine::CheckFrameValidity(VideoData* aData) -{ - MOZ_ASSERT(OnTaskQueue()); - - // Update corrupt-frames statistics - if (aData->mImage && !aData->mImage->IsValid() && !gfxPrefs::HardwareVideoDecodingForceEnabled()) { - FrameStatistics& frameStats = *mFrameStats; - frameStats.NotifyCorruptFrame(); - // If more than 10% of the last 30 frames have been corrupted, then try disabling - // hardware acceleration. We use 10 as the corrupt value because RollingMean<> - // only supports integer types. - mCorruptFrames.insert(10); - if (mReader->VideoIsHardwareAccelerated() && - frameStats.GetPresentedFrames() > 60 && - mCorruptFrames.mean() >= 2 /* 20% */) { - nsCOMPtr task = - NS_NewRunnableMethod(mReader, &MediaDecoderReader::DisableHardwareAcceleration); - DecodeTaskQueue()->Dispatch(task.forget()); - mCorruptFrames.clear(); - gfxCriticalNote << "Too many dropped/corrupted frames, disabling DXVA"; - } - } else { - mCorruptFrames.insert(0); - } -} - int64_t MediaDecoderStateMachine::GetClock(TimeStamp* aTimeStamp) const { @@ -2930,6 +2917,15 @@ MediaDecoderStateMachine::SetAudioCaptured(bool aCaptured) mAudioCaptured = aCaptured; ScheduleStateMachine(); + + // Don't buffer as much when audio is captured because we don't need to worry + // about high latency audio devices. + mAmpleAudioThresholdUsecs = mAudioCaptured ? + detail::AMPLE_AUDIO_USECS / 2 : + detail::AMPLE_AUDIO_USECS; + if (mIsAudioPrerolling && DonePrerollingAudio()) { + StopPrerollingAudio(); + } } uint32_t MediaDecoderStateMachine::GetAmpleVideoFrames() const diff --git a/dom/media/MediaDecoderStateMachine.h b/dom/media/MediaDecoderStateMachine.h index db43a946b6..989b209779 100644 --- a/dom/media/MediaDecoderStateMachine.h +++ b/dom/media/MediaDecoderStateMachine.h @@ -84,7 +84,6 @@ hardware (via AudioStream). #include "mozilla/Attributes.h" #include "mozilla/ReentrantMonitor.h" -#include "mozilla/RollingMean.h" #include "mozilla/StateMirroring.h" #include "nsThreadUtils.h" @@ -146,7 +145,6 @@ public: // Enumeration for the valid decoding states enum State { - DECODER_STATE_DECODING_NONE, DECODER_STATE_DECODING_METADATA, DECODER_STATE_WAIT_FOR_CDM, DECODER_STATE_DORMANT, @@ -275,6 +273,8 @@ private: void SetAudioCaptured(bool aCaptured); + void ReadMetadata(); + RefPtr Seek(SeekTarget aTarget); RefPtr Shutdown(); @@ -330,17 +330,7 @@ private: TaskQueue* OwnerThread() const { return mTaskQueue; } // Schedules the shared state machine thread to run the state machine. - // - // The first variant coalesces multiple calls into a single state machine - // cycle, the second variant does not. The second variant must be used when - // not already on the state machine task queue. void ScheduleStateMachine(); - void ScheduleStateMachineCrossThread() - { - nsCOMPtr task = - NS_NewRunnableMethod(this, &MediaDecoderStateMachine::RunStateMachine); - OwnerThread()->Dispatch(task.forget()); - } // Invokes ScheduleStateMachine to run in |aMicroseconds| microseconds, // unless it's already scheduled to run earlier, in which case the @@ -478,10 +468,6 @@ protected: // machine thread, caller must hold the decoder lock. void UpdatePlaybackPositionInternal(int64_t aTime); - // Decode monitor must be held. To determine if MDSM needs to turn off HW - // acceleration. - void CheckFrameValidity(VideoData* aData); - // Update playback position and trigger next update by default time period. // Called on the state machine thread. void UpdatePlaybackPositionPeriodically(); @@ -975,7 +961,7 @@ private: uint32_t AudioPrerollUsecs() const { MOZ_ASSERT(OnTaskQueue()); - return IsRealTime() ? 0 : mAmpleAudioThresholdUsecs; + return IsRealTime() ? 0 : mAmpleAudioThresholdUsecs / 2; } uint32_t VideoPrerollFrames() const @@ -1135,10 +1121,6 @@ private: bool mDropAudioUntilNextDiscontinuity; bool mDropVideoUntilNextDiscontinuity; - // True if we need to decode forwards to the seek target inside - // mCurrentSeekTarget. - bool mDecodeToSeekTarget; - // Track the current seek promise made by the reader. MozPromiseRequestHolder mSeekRequest; @@ -1158,7 +1140,8 @@ private: mozilla::MediaMetadataManager mMetadataManager; - mozilla::RollingMean mCorruptFrames; + // Track our request to update the buffered ranges + MozPromiseRequestHolder mBufferedUpdateRequest; // True if we need to call FinishDecodeFirstFrame() upon frame decoding // successeeding. @@ -1179,13 +1162,6 @@ private: // Data about MediaStreams that are being fed by the decoder. const RefPtr mOutputStreamManager; - // The SourceMediaStream we are using to feed the mOutputStreams. This stream - // is never exposed outside the decoder. - // Only written on the main thread while holding the monitor. Therefore it - // can be read on any thread while holding the monitor, or on the main thread - // without holding the monitor. - RefPtr mStreamSink; - // Media data resource from the decoder. RefPtr mResource; @@ -1249,6 +1225,10 @@ private: // passed to MediaStreams when this is true. Mirror mSameOriginMedia; + // An identifier for the principal of the media. Used to track when + // main-thread induced principal changes get reflected on MSG thread. + Mirror mMediaPrincipalHandle; + // Estimate of the current playback rate (bytes/second). Mirror mPlaybackBytesPerSecond; diff --git a/dom/media/MediaDevices.cpp b/dom/media/MediaDevices.cpp index d94a2697aa..8a72222984 100644 --- a/dom/media/MediaDevices.cpp +++ b/dom/media/MediaDevices.cpp @@ -28,7 +28,7 @@ public: NS_IMETHOD OnSuccess(nsISupports* aStream) override { - RefPtr stream = do_QueryObject(aStream); + RefPtr stream = do_QueryObject(aStream); if (!stream) { return NS_ERROR_FAILURE; } diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp index e4f8decf97..29aa8efcd2 100644 --- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -68,7 +68,6 @@ MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder, , mInitDone(false) , mIsEncrypted(false) , mTrackDemuxersMayBlock(false) - , mHardwareAccelerationDisabled(false) , mDemuxOnly(false) , mVideoFrameContainer(aVideoFrameContainer) { @@ -404,7 +403,6 @@ MediaFormatReader::EnsureDecoderCreated(TrackType aTrack) mInfo.mVideo, decoder.mTaskQueue, decoder.mCallback, - mHardwareAccelerationDisabled ? LayersBackend::LAYERS_NONE : mLayersBackendType, GetImageContainer()); break; @@ -469,23 +467,6 @@ MediaFormatReader::GetDecoderData(TrackType aTrack) return mVideo; } -void -MediaFormatReader::DisableHardwareAcceleration() -{ - MOZ_ASSERT(OnTaskQueue()); - if (HasVideo() && !mHardwareAccelerationDisabled) { - mHardwareAccelerationDisabled = true; - Flush(TrackInfo::kVideoTrack); - mVideo.ShutdownDecoder(); - if (!EnsureDecoderCreated(TrackType::kVideoTrack)) { - LOG("Unable to re-create decoder, aborting"); - NotifyError(TrackInfo::kVideoTrack); - return; - } - ScheduleUpdate(TrackInfo::kVideoTrack); - } -} - bool MediaFormatReader::ShouldSkip(bool aSkipToNextKeyframe, media::TimeUnit aTimeThreshold) { @@ -1619,6 +1600,20 @@ MediaFormatReader::GetBuffered() return intervals.Shift(media::TimeUnit::FromMicroseconds(-startTime)); } +// For the MediaFormatReader override we need to force an update to the +// buffered ranges, so we call NotifyDataArrive +RefPtr +MediaFormatReader::UpdateBufferedWithPromise() { + MOZ_ASSERT(OnTaskQueue()); + // Call NotifyDataArrive to force a recalculation of the buffered + // ranges. UpdateBuffered alone will not force a recalculation, so we + // use NotifyDataArrived which sets flags to force this recalculation. + // See MediaFormatReader::UpdateReceivedNewData for an example of where + // the new data flag is used. + NotifyDataArrived(); + return BufferedUpdatePromise::CreateAndResolve(true, __func__); +} + void MediaFormatReader::ReleaseMediaResources() { // Before freeing a video codec, all video buffers needed to be released diff --git a/dom/media/MediaFormatReader.h b/dom/media/MediaFormatReader.h index c2d8b10689..7e6726d97d 100644 --- a/dom/media/MediaFormatReader.h +++ b/dom/media/MediaFormatReader.h @@ -56,6 +56,8 @@ protected: public: media::TimeIntervals GetBuffered() override; + RefPtr UpdateBufferedWithPromise() override; + bool ForceZeroStartTime() const override; // For Media Resource Management @@ -69,8 +71,6 @@ public: bool VideoIsHardwareAccelerated() const override; - void DisableHardwareAcceleration() override; - bool IsWaitForDataSupported() override { return true; } RefPtr WaitForData(MediaData::Type aType) override; @@ -448,8 +448,6 @@ private: // Set to true if any of our track buffers may be blocking. bool mTrackDemuxersMayBlock; - bool mHardwareAccelerationDisabled; - // Set the demuxed-only flag. Atomic mDemuxOnly; diff --git a/dom/media/MediaManager.cpp b/dom/media/MediaManager.cpp index b25580c395..2631d02965 100644 --- a/dom/media/MediaManager.cpp +++ b/dom/media/MediaManager.cpp @@ -134,12 +134,15 @@ GetMediaManagerLog() } #define LOG(msg) MOZ_LOG(GetMediaManagerLog(), mozilla::LogLevel::Debug, msg) +using dom::BasicUnstoppableTrackSource; using dom::ConstrainDOMStringParameters; using dom::File; +using dom::GetUserMediaRequest; using dom::MediaSourceEnum; using dom::MediaStreamConstraints; using dom::MediaStreamError; using dom::MediaStreamTrack; +using dom::MediaStreamTrackSource; using dom::MediaTrackConstraints; using dom::MediaTrackConstraintSet; using dom::OwningBooleanOrMediaTrackConstraints; @@ -178,19 +181,19 @@ HostInDomain(const nsCString &aHost, const nsCString &aPattern) } static bool -HostHasPermission(nsIURI &docURI) +HostIsHttps(nsIURI &docURI) { - nsresult rv; - bool isHttps; - rv = docURI.SchemeIs("https",&isHttps); + nsresult rv = docURI.SchemeIs("https", &isHttps); if (NS_WARN_IF(NS_FAILED(rv))) { return false; } - if (!isHttps) { - return false; - } + return isHttps; +} +static bool +HostHasPermission(nsIURI &docURI) +{ nsAdoptingCString hostName; docURI.GetAsciiHost(hostName); //normalize UTF8 to ASCII equivalent nsAdoptingCString domainWhiteList = @@ -202,6 +205,7 @@ HostHasPermission(nsIURI &docURI) } // Get UTF8 to ASCII domain name normalization service + nsresult rv; nsCOMPtr idnService = do_GetService("@mozilla.org/network/idn-service;1", &rv); if (NS_WARN_IF(NS_FAILED(rv))) { @@ -249,7 +253,7 @@ public: MediaOperationTask(MediaOperation aType, GetUserMediaCallbackMediaStreamListener* aListener, DOMMediaStream* aStream, - DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback, + OnTracksAvailableCallback* aOnTracksAvailableCallback, AudioDevice* aAudioDevice, VideoDevice* aVideoDevice, bool aBool, @@ -292,14 +296,16 @@ public: nsresult rv; if (mAudioDevice) { - rv = mAudioDevice->GetSource()->Start(source, kAudioTrack); + rv = mAudioDevice->GetSource()->Start(source, kAudioTrack, + mListener->GetPrincipalHandle()); if (NS_FAILED(rv)) { ReturnCallbackError(rv, "Starting audio failed"); return; } } if (mVideoDevice) { - rv = mVideoDevice->GetSource()->Start(source, kVideoTrack); + rv = mVideoDevice->GetSource()->Start(source, kVideoTrack, + mListener->GetPrincipalHandle()); if (NS_FAILED(rv)) { ReturnCallbackError(rv, "Starting video failed"); return; @@ -376,7 +382,7 @@ public: private: MediaOperation mType; RefPtr mStream; - nsAutoPtr mOnTracksAvailableCallback; + nsAutoPtr mOnTracksAvailableCallback; RefPtr mAudioDevice; // threadsafe RefPtr mVideoDevice; // threadsafe RefPtr mListener; // threadsafe @@ -654,155 +660,6 @@ nsresult AudioDevice::Restart(const dom::MediaTrackConstraints &aConstraints, return GetSource()->Restart(aConstraints, aPrefs, mID); } -/** - * A subclass that we only use to stash internal pointers to MediaStreamGraph objects - * that need to be cleaned up. - */ -class nsDOMUserMediaStream : public DOMLocalMediaStream -{ -public: - static already_AddRefed - CreateSourceStream(nsIDOMWindow* aWindow, - GetUserMediaCallbackMediaStreamListener* aListener, - AudioDevice* aAudioDevice, - VideoDevice* aVideoDevice, - MediaStreamGraph* aMSG) - { - RefPtr stream = new nsDOMUserMediaStream(aListener, - aAudioDevice, - aVideoDevice); - stream->InitSourceStream(aWindow, aMSG); - return stream.forget(); - } - - nsDOMUserMediaStream(GetUserMediaCallbackMediaStreamListener* aListener, - AudioDevice *aAudioDevice, - VideoDevice *aVideoDevice) : - mListener(aListener), - mAudioDevice(aAudioDevice), - mVideoDevice(aVideoDevice) - {} - - virtual ~nsDOMUserMediaStream() - { - StopImpl(); - - if (GetSourceStream()) { - GetSourceStream()->Destroy(); - } - } - - already_AddRefed - ApplyConstraintsToTrack(TrackID aTrackID, - const MediaTrackConstraints& aConstraints, - ErrorResult &aRv) override - { - nsPIDOMWindow* window = static_cast(mWindow.get()); - nsCOMPtr go = do_QueryInterface(window); - RefPtr promise = Promise::Create(go, aRv); - - if (sInShutdown) { - RefPtr error = new MediaStreamError(window, - NS_LITERAL_STRING("AbortError"), - NS_LITERAL_STRING("In shutdown")); - promise->MaybeReject(error); - return promise.forget(); - } - if (!GetSourceStream()) { - RefPtr error = new MediaStreamError(window, - NS_LITERAL_STRING("InternalError"), - NS_LITERAL_STRING("No stream.")); - promise->MaybeReject(error); - return promise.forget(); - } - - RefPtr track = FindOwnedDOMTrack(mOwnedStream, aTrackID); - if (!track) { - LOG(("ApplyConstraintsToTrack(%d) on non-existent track", aTrackID)); - RefPtr error = new MediaStreamError(window, - NS_LITERAL_STRING("InternalError"), - NS_LITERAL_STRING("No track.")); - promise->MaybeReject(error); - return promise.forget(); - } - - typedef media::Pledge PledgeVoid; - - RefPtr p = mListener->ApplyConstraintsToTrack(window, - aTrackID, !!track->AsAudioStreamTrack(), aConstraints); - p->Then([promise](bool& aDummy) mutable { - promise->MaybeResolve(false); - }, [promise](MediaStreamError*& reason) mutable { - promise->MaybeReject(reason); - }); - return promise.forget(); - } - -#if 0 - virtual void NotifyMediaStreamTrackEnded(dom::MediaStreamTrack* aTrack) - { - TrackID trackID = aTrack->GetTrackID(); - // We override this so we can also tell the backend to stop capturing if the track ends - LOG(("track %d ending, type = %s", - trackID, aTrack->AsAudioStreamTrack() ? "audio" : "video")); - MOZ_ASSERT(aTrack->AsVideoStreamTrack() || aTrack->AsAudioStreamTrack()); - mListener->StopTrack(trackID, !!aTrack->AsAudioStreamTrack()); - - // forward to superclass - DOMLocalMediaStream::NotifyMediaStreamTrackEnded(aTrack); - } -#endif - - // Allow getUserMedia to pass input data directly to PeerConnection/MediaPipeline - bool AddDirectListener(MediaStreamDirectListener *aListener) override - { - if (GetSourceStream()) { - GetSourceStream()->AddDirectListener(aListener); - return true; // application should ignore NotifyQueuedTrackData - } - return false; - } - - void RemoveDirectListener(MediaStreamDirectListener *aListener) override - { - if (GetSourceStream()) { - GetSourceStream()->RemoveDirectListener(aListener); - } - } - - DOMLocalMediaStream* AsDOMLocalMediaStream() override - { - return this; - } - - MediaEngineSource* GetMediaEngine(TrackID aTrackID) override - { - // MediaEngine supports only one video and on video track now and TrackID is - // fixed in MediaEngine. - if (aTrackID == kVideoTrack) { - return mVideoDevice ? mVideoDevice->GetSource() : nullptr; - } - else if (aTrackID == kAudioTrack) { - return mAudioDevice ? mAudioDevice->GetSource() : nullptr; - } - - return nullptr; - } - - SourceMediaStream* GetSourceStream() - { - if (GetInputStream()) { - return GetInputStream()->AsSourceStream(); - } - return nullptr; - } - - RefPtr mListener; - RefPtr mAudioDevice; // so we can turn on AEC - RefPtr mVideoDevice; -}; - - void MediaOperationTask::ReturnCallbackError(nsresult rv, const char* errorLog) { @@ -822,6 +679,47 @@ MediaOperationTask::ReturnCallbackError(nsresult rv, const char* errorLog) mWindowID))); } +/** + * This class is only needed since fake tracks are added dynamically. + * Instead of refactoring to add them explicitly we let the DOMMediaStream + * query us for the source as they become available. + * Since they are used only for testing the API surface, we make them very + * simple. + */ +class FakeTrackSourceGetter : public MediaStreamTrackSourceGetter +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FakeTrackSourceGetter, + MediaStreamTrackSourceGetter) + + explicit FakeTrackSourceGetter(nsIPrincipal* aPrincipal) + : mPrincipal(aPrincipal) {} + + already_AddRefed + GetMediaStreamTrackSource(TrackID aInputTrackID) override + { + NS_ASSERTION(kAudioTrack != aInputTrackID, + "Only fake tracks should appear dynamically"); + NS_ASSERTION(kVideoTrack != aInputTrackID, + "Only fake tracks should appear dynamically"); + return do_AddRef(new BasicUnstoppableTrackSource(mPrincipal)); + } + +protected: + virtual ~FakeTrackSourceGetter() {} + + nsCOMPtr mPrincipal; +}; + +NS_IMPL_ADDREF_INHERITED(FakeTrackSourceGetter, MediaStreamTrackSourceGetter) +NS_IMPL_RELEASE_INHERITED(FakeTrackSourceGetter, MediaStreamTrackSourceGetter) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FakeTrackSourceGetter) +NS_INTERFACE_MAP_END_INHERITING(MediaStreamTrackSourceGetter) +NS_IMPL_CYCLE_COLLECTION_INHERITED(FakeTrackSourceGetter, + MediaStreamTrackSourceGetter, + mPrincipal) + /** * Creates a MediaStream, attaches a listener and fires off a success callback * to the DOM with the stream. We also pass in the error callback so it can @@ -862,7 +760,7 @@ public: ~GetUserMediaStreamRunnable() {} - class TracksAvailableCallback : public DOMMediaStream::OnTracksAvailableCallback + class TracksAvailableCallback : public OnTracksAvailableCallback { public: TracksAvailableCallback(MediaManager* aManager, @@ -923,7 +821,7 @@ public: MediaStreamGraph::GetInstance(graphDriverType, dom::AudioChannel::Normal); - RefPtr domStream; + RefPtr domStream; RefPtr stream; // AudioCapture is a special case, here, in the sense that we're not really // using the audio source and the SourceMediaStream, which acts as @@ -931,40 +829,119 @@ public: // them down instead. if (mAudioDevice && mAudioDevice->GetMediaSource() == MediaSourceEnum::AudioCapture) { - domStream = DOMLocalMediaStream::CreateAudioCaptureStream(window, msg); // It should be possible to pipe the capture stream to anything. CORS is // not a problem here, we got explicit user content. - domStream->SetPrincipal(window->GetExtantDoc()->NodePrincipal()); + nsCOMPtr principal = window->GetExtantDoc()->NodePrincipal(); + domStream = + DOMMediaStream::CreateAudioCaptureStream(window, principal, msg); + stream = msg->CreateSourceStream(nullptr); // Placeholder msg->RegisterCaptureStreamForWindow( mWindowID, domStream->GetInputStream()->AsProcessedStream()); window->SetAudioCapture(true); } else { - // Normal case, connect the source stream to the track union stream to - // avoid us blocking - domStream = nsDOMUserMediaStream::CreateSourceStream(window, mListener, - mAudioDevice, mVideoDevice, - msg); + class LocalTrackSource : public MediaStreamTrackSource + { + public: + LocalTrackSource(nsIPrincipal* aPrincipal, + const nsString& aLabel, + GetUserMediaCallbackMediaStreamListener* aListener, + const MediaSourceEnum aSource, + const TrackID aTrackID, + const PeerIdentity* aPeerIdentity) + : MediaStreamTrackSource(aPrincipal, false, aLabel), mListener(aListener), + mSource(aSource), mTrackID(aTrackID), mPeerIdentity(aPeerIdentity) {} - if (mAudioDevice) { - nsString audioDeviceName; - mAudioDevice->GetName(audioDeviceName); - domStream->CreateOwnDOMTrack(kAudioTrack, MediaSegment::AUDIO, audioDeviceName); - } - if (mVideoDevice) { - nsString videoDeviceName; - mVideoDevice->GetName(videoDeviceName); - domStream->CreateOwnDOMTrack(kVideoTrack, MediaSegment::VIDEO, videoDeviceName); - } + MediaSourceEnum GetMediaSource() const override + { + return mSource; + } + + const PeerIdentity* GetPeerIdentity() const override + { + return mPeerIdentity; + } + + already_AddRefed + ApplyConstraints(nsPIDOMWindow* aWindow, + const MediaTrackConstraints& aConstraints, + ErrorResult &aRv) override + { + nsCOMPtr go = do_QueryInterface(aWindow); + RefPtr promise = Promise::Create(go, aRv); + + if (sInShutdown) { + RefPtr error = new MediaStreamError(aWindow, + NS_LITERAL_STRING("AbortError"), + NS_LITERAL_STRING("In shutdown")); + promise->MaybeReject(error); + return promise.forget(); + } + + typedef media::Pledge PledgeVoid; + + RefPtr p = + mListener->ApplyConstraintsToTrack(aWindow, mTrackID, aConstraints); + p->Then([promise](bool& aDummy) mutable { + promise->MaybeResolve(false); + }, [promise](MediaStreamError*& reason) mutable { + promise->MaybeReject(reason); + }); + return promise.forget(); + } + + + void Stop() override + { + if (mListener) { + mListener->StopTrack(mTrackID); + mListener = nullptr; + } + } + + protected: + ~LocalTrackSource() {} + + RefPtr mListener; + const MediaSourceEnum mSource; + const TrackID mTrackID; + const RefPtr mPeerIdentity; + }; nsCOMPtr principal; if (mPeerIdentity) { principal = nsNullPrincipal::Create(); - domStream->SetPeerIdentity(mPeerIdentity.forget()); } else { principal = window->GetExtantDoc()->NodePrincipal(); } - domStream->CombineWithPrincipal(principal); + + // Normal case, connect the source stream to the track union stream to + // avoid us blocking. Pass a simple TrackSourceGetter for potential + // fake tracks. Apart from them gUM never adds tracks dynamically. + domStream = + DOMLocalMediaStream::CreateSourceStream(window, msg, + new FakeTrackSourceGetter(principal)); + + if (mAudioDevice) { + nsString audioDeviceName; + mAudioDevice->GetName(audioDeviceName); + const MediaSourceEnum source = + mAudioDevice->GetSource()->GetMediaSource(); + RefPtr audioSource = + new LocalTrackSource(principal, audioDeviceName, mListener, source, + kAudioTrack, mPeerIdentity); + domStream->CreateDOMTrack(kAudioTrack, MediaSegment::AUDIO, audioSource); + } + if (mVideoDevice) { + nsString videoDeviceName; + mVideoDevice->GetName(videoDeviceName); + const MediaSourceEnum source = + mVideoDevice->GetSource()->GetMediaSource(); + RefPtr videoSource = + new LocalTrackSource(principal, videoDeviceName, mListener, source, + kVideoTrack, mPeerIdentity); + domStream->CreateDOMTrack(kVideoTrack, MediaSegment::VIDEO, videoSource); + } stream = domStream->GetInputStream()->AsSourceStream(); } @@ -1022,7 +999,7 @@ private: uint64_t mWindowID; RefPtr mListener; nsCString mOrigin; - nsAutoPtr mPeerIdentity; + RefPtr mPeerIdentity; RefPtr mManager; // get ref to this when creating the runnable }; @@ -1441,6 +1418,8 @@ MediaManager::MediaManager() mPrefs.mAecOn = false; mPrefs.mAgcOn = false; mPrefs.mNoiseOn = false; + mPrefs.mExtendedFilter = true; + mPrefs.mDelayAgnostic = true; #ifdef MOZ_WEBRTC mPrefs.mAec = webrtc::kEcUnchanged; mPrefs.mAgc = webrtc::kAgcUnchanged; @@ -1462,10 +1441,12 @@ MediaManager::MediaManager() } LOG(("%s: default prefs: %dx%d @%dfps (min %d), %dHz test tones, aec: %s," "agc: %s, noise: %s, aec level: %d, agc level: %d, noise level: %d," - "playout delay: %d, %sfull_duplex", __FUNCTION__, mPrefs.mWidth, mPrefs.mHeight, + "playout delay: %d, %sfull_duplex, extended aec %s, delay_agnostic %s", + __FUNCTION__, mPrefs.mWidth, mPrefs.mHeight, mPrefs.mFPS, mPrefs.mMinFPS, mPrefs.mFreq, mPrefs.mAecOn ? "on" : "off", mPrefs.mAgcOn ? "on": "off", mPrefs.mNoiseOn ? "on": "off", mPrefs.mAec, - mPrefs.mAgc, mPrefs.mNoise, mPrefs.mPlayoutDelay, mPrefs.mFullDuplex ? "" : "not ")); + mPrefs.mAgc, mPrefs.mNoise, mPrefs.mPlayoutDelay, mPrefs.mFullDuplex ? "" : "not ", + mPrefs.mExtendedFilter ? "on" : "off", mPrefs.mDelayAgnostic ? "on" : "off")); } NS_IMPL_ISUPPORTS(MediaManager, nsIMediaManagerService, nsIObserver) @@ -1491,11 +1472,11 @@ MediaManager::IsInMediaThread() MediaManager::Get() { if (!sSingleton) { MOZ_ASSERT(NS_IsMainThread()); -#ifdef DEBUG + static int timesCreated = 0; timesCreated++; - MOZ_ASSERT(timesCreated == 1); -#endif + MOZ_RELEASE_ASSERT(timesCreated == 1); + sSingleton = new MediaManager(); sSingleton->mMediaThread = new base::Thread("MediaManager"); @@ -1875,7 +1856,8 @@ MediaManager::GetUserMedia(nsPIDOMWindow* aWindow, #endif ) || #endif - (!privileged && !HostHasPermission(*docURI))) { + (!privileged && !HostIsHttps(*docURI)) || + !(loop || HostHasPermission(*docURI))) { RefPtr error = new MediaStreamError(aWindow, NS_LITERAL_STRING("SecurityError")); @@ -1989,8 +1971,10 @@ MediaManager::GetUserMedia(nsPIDOMWindow* aWindow, StreamListeners* listeners = AddWindowID(windowID); // Create a disabled listener to act as a placeholder + nsIPrincipal* principal = aWindow->GetExtantDoc()->NodePrincipal(); RefPtr listener = - new GetUserMediaCallbackMediaStreamListener(mMediaThread, windowID); + new GetUserMediaCallbackMediaStreamListener(mMediaThread, windowID, + MakePrincipalHandle(principal)); // No need for locking because we always do this in the main thread. listeners->AppendElement(listener); @@ -2004,14 +1988,14 @@ MediaManager::GetUserMedia(nsPIDOMWindow* aWindow, uint32_t audioPerm = nsIPermissionManager::UNKNOWN_ACTION; if (IsOn(c.mAudio)) { rv = permManager->TestExactPermissionFromPrincipal( - aWindow->GetExtantDoc()->NodePrincipal(), "microphone", &audioPerm); + principal, "microphone", &audioPerm); NS_ENSURE_SUCCESS(rv, rv); } uint32_t videoPerm = nsIPermissionManager::UNKNOWN_ACTION; if (IsOn(c.mVideo)) { rv = permManager->TestExactPermissionFromPrincipal( - aWindow->GetExtantDoc()->NodePrincipal(), "camera", &videoPerm); + principal, "camera", &videoPerm); NS_ENSURE_SUCCESS(rv, rv); } @@ -2304,9 +2288,12 @@ MediaManager::EnumerateDevices(nsPIDOMWindow* aWindow, StreamListeners* listeners = AddWindowID(windowId); + nsIPrincipal* principal = aWindow->GetExtantDoc()->NodePrincipal(); + // Create a disabled listener to act as a placeholder RefPtr listener = - new GetUserMediaCallbackMediaStreamListener(mMediaThread, windowId); + new GetUserMediaCallbackMediaStreamListener(mMediaThread, windowId, + MakePrincipalHandle(principal)); // No need for locking because we always do this in the main thread. listeners->AppendElement(listener); @@ -2548,6 +2535,8 @@ MediaManager::GetPrefs(nsIPrefBranch *aBranch, const char *aData) GetPref(aBranch, "media.getusermedia.agc", aData, &mPrefs.mAgc); GetPref(aBranch, "media.getusermedia.noise", aData, &mPrefs.mNoise); GetPref(aBranch, "media.getusermedia.playout_delay", aData, &mPrefs.mPlayoutDelay); + GetPrefBool(aBranch, "media.getusermedia.aec_extended_filter", aData, &mPrefs.mExtendedFilter); + GetPrefBool(aBranch, "media.getusermedia.aec_aec_delay_agnostic", aData, &mPrefs.mDelayAgnostic); #endif GetPrefBool(aBranch, "media.navigator.audio.full_duplex", aData, &mPrefs.mFullDuplex); } @@ -3117,26 +3106,26 @@ already_AddRefed GetUserMediaCallbackMediaStreamListener::ApplyConstraintsToTrack( nsPIDOMWindow* aWindow, TrackID aTrackID, - bool aIsAudio, const MediaTrackConstraints& aConstraints) { MOZ_ASSERT(NS_IsMainThread()); RefPtr p = new PledgeVoid(); - if (!(((aIsAudio && mAudioDevice) || - (!aIsAudio && mVideoDevice)) && !mStopped)) + // XXX to support multiple tracks of a type in a stream, this should key off + // the TrackID and not just the type + RefPtr audioDevice = + aTrackID == kAudioTrack ? mAudioDevice.get() : nullptr; + RefPtr videoDevice = + aTrackID == kVideoTrack ? mVideoDevice.get() : nullptr; + + if (mStopped || (!audioDevice && !videoDevice)) { LOG(("gUM track %d applyConstraints, but we don't have type %s", - aTrackID, aIsAudio ? "audio" : "video")); + aTrackID, aTrackID == kAudioTrack ? "audio" : "video")); p->Resolve(false); return p.forget(); } - // XXX to support multiple tracks of a type in a stream, this should key off - // the TrackID and not just the type - RefPtr audioDevice = aIsAudio ? mAudioDevice.get() : nullptr; - RefPtr videoDevice = !aIsAudio ? mVideoDevice.get() : nullptr; - RefPtr mgr = MediaManager::GetInstance(); uint32_t id = mgr->mOutstandingVoidPledges.Append(*p); uint64_t windowId = aWindow->WindowID(); @@ -3254,8 +3243,12 @@ GetUserMediaCallbackMediaStreamListener::NotifyFinished() mFinished = true; Stop(); // we know it's been activated - RefPtr manager(MediaManager::GetInstance()); - manager->RemoveFromWindowList(mWindowID, this); + RefPtr manager(MediaManager::GetIfExists()); + if (manager) { + manager->RemoveFromWindowList(mWindowID, this); + } else { + NS_WARNING("Late NotifyFinished after MediaManager shutdown"); + } } // Called from the MediaStreamGraph thread diff --git a/dom/media/MediaManager.h b/dom/media/MediaManager.h index c23676c9d2..a2e4881a7e 100644 --- a/dom/media/MediaManager.h +++ b/dom/media/MediaManager.h @@ -126,9 +126,11 @@ class GetUserMediaCallbackMediaStreamListener : public MediaStreamListener public: // Create in an inactive state GetUserMediaCallbackMediaStreamListener(base::Thread *aThread, - uint64_t aWindowID) + uint64_t aWindowID, + const PrincipalHandle& aPrincipalHandle) : mMediaThread(aThread) , mWindowID(aWindowID) + , mPrincipalHandle(aPrincipalHandle) , mStopped(false) , mFinished(false) , mRemoved(false) @@ -175,7 +177,7 @@ public: already_AddRefed ApplyConstraintsToTrack(nsPIDOMWindow* aWindow, - TrackID aID, bool aIsAudio, + TrackID aID, const dom::MediaTrackConstraints& aConstraints); // mVideo/AudioDevice are set by Activate(), so we assume they're capturing @@ -260,11 +262,11 @@ public: // watch it especially for fake audio. if (mAudioDevice) { mAudioDevice->GetSource()->NotifyPull(aGraph, mStream, kAudioTrack, - aDesiredTime); + aDesiredTime, mPrincipalHandle); } if (mVideoDevice) { mVideoDevice->GetSource()->NotifyPull(aGraph, mStream, kVideoTrack, - aDesiredTime); + aDesiredTime, mPrincipalHandle); } } @@ -301,10 +303,13 @@ public: void NotifyDirectListeners(MediaStreamGraph* aGraph, bool aHasListeners); + PrincipalHandle GetPrincipalHandle() const { return mPrincipalHandle; } + private: // Set at construction base::Thread* mMediaThread; uint64_t mWindowID; + const PrincipalHandle mPrincipalHandle; // true after this listener has sent MEDIA_STOP. MainThread only. bool mStopped; @@ -350,7 +355,7 @@ class GetUserMediaNotificationEvent: public nsRunnable GetUserMediaNotificationEvent(GetUserMediaStatus aStatus, already_AddRefed aStream, - DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback, + OnTracksAvailableCallback* aOnTracksAvailableCallback, bool aIsAudio, bool aIsVideo, uint64_t aWindowID, already_AddRefed aError) : mStream(aStream), mOnTracksAvailableCallback(aOnTracksAvailableCallback), @@ -366,7 +371,7 @@ class GetUserMediaNotificationEvent: public nsRunnable protected: RefPtr mListener; // threadsafe RefPtr mStream; - nsAutoPtr mOnTracksAvailableCallback; + nsAutoPtr mOnTracksAvailableCallback; GetUserMediaStatus mStatus; bool mIsAudio; bool mIsVideo; @@ -388,13 +393,13 @@ class ReleaseMediaOperationResource : public nsRunnable { public: ReleaseMediaOperationResource(already_AddRefed aStream, - DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback): + OnTracksAvailableCallback* aOnTracksAvailableCallback): mStream(aStream), mOnTracksAvailableCallback(aOnTracksAvailableCallback) {} NS_IMETHOD Run() override {return NS_OK;} private: RefPtr mStream; - nsAutoPtr mOnTracksAvailableCallback; + nsAutoPtr mOnTracksAvailableCallback; }; typedef nsTArray > StreamListeners; diff --git a/dom/media/MediaRecorder.cpp b/dom/media/MediaRecorder.cpp index e952f49cf4..523197472e 100644 --- a/dom/media/MediaRecorder.cpp +++ b/dom/media/MediaRecorder.cpp @@ -9,6 +9,7 @@ #include "AudioNodeStream.h" #include "DOMMediaStream.h" #include "EncodedBufferCache.h" +#include "MediaDecoder.h" #include "MediaEncoder.h" #include "mozilla/StaticPtr.h" #include "mozilla/DOMEventTargetHelper.h" @@ -22,10 +23,13 @@ #include "nsIDocument.h" #include "nsIPermissionManager.h" #include "nsIPrincipal.h" +#include "nsIScriptError.h" #include "nsMimeTypes.h" #include "nsProxyRelease.h" #include "nsTArray.h" #include "GeckoProfiler.h" +#include "nsContentTypeParser.h" +#include "nsCharSeparatedTokenizer.h" #ifdef LOG #undef LOG @@ -167,7 +171,9 @@ NS_IMPL_RELEASE_INHERITED(MediaRecorder, DOMEventTargetHelper) * 3) MediaRecorder::Stop is called by user or the document is going to * inactive or invisible. */ -class MediaRecorder::Session: public nsIObserver +class MediaRecorder::Session: public nsIObserver, + public PrincipalChangeObserver, + public DOMMediaStream::TrackListener { NS_DECL_THREADSAFE_ISUPPORTS @@ -298,24 +304,58 @@ class MediaRecorder::Session: public nsIObserver }; // For Ensure recorder has tracks to record. - class TracksAvailableCallback : public DOMMediaStream::OnTracksAvailableCallback + class TracksAvailableCallback : public OnTracksAvailableCallback { public: explicit TracksAvailableCallback(Session *aSession) : mSession(aSession) {} virtual void NotifyTracksAvailable(DOMMediaStream* aStream) { + if (mSession->mStopIssued) { + return; + } + + MOZ_RELEASE_ASSERT(aStream); + mSession->MediaStreamReady(*aStream); + uint8_t trackTypes = 0; nsTArray> audioTracks; aStream->GetAudioTracks(audioTracks); if (!audioTracks.IsEmpty()) { trackTypes |= ContainerWriter::CREATE_AUDIO_TRACK; + mSession->ConnectMediaStreamTrack(*audioTracks[0]); } nsTArray> videoTracks; aStream->GetVideoTracks(videoTracks); if (!videoTracks.IsEmpty()) { trackTypes |= ContainerWriter::CREATE_VIDEO_TRACK; + mSession->ConnectMediaStreamTrack(*videoTracks[0]); + } + + if (audioTracks.Length() > 1 || + videoTracks.Length() > 1) { + // When MediaRecorder supports multiple tracks, we should set up a single + // MediaInputPort from the input stream, and let main thread check + // track principals async later. + nsPIDOMWindow* window = mSession->mRecorder->GetParentObject(); + nsIDocument* document = window ? window->GetExtantDoc() : nullptr; + nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, + NS_LITERAL_CSTRING("Media"), + document, + nsContentUtils::eDOM_PROPERTIES, + "MediaRecorderMultiTracksNotSupported"); + mSession->DoSessionEndTask(NS_ERROR_ABORT); + return; + } + + NS_ASSERTION(trackTypes != 0, "TracksAvailableCallback without any tracks available"); + + // Check that we may access the tracks' content. + if (!mSession->MediaStreamTracksPrincipalSubsumes()) { + LOG(LogLevel::Warning, ("Session.NotifyTracksAvailable MediaStreamTracks principal check failed")); + mSession->DoSessionEndTask(NS_ERROR_DOM_SECURITY_ERR); + return; } LOG(LogLevel::Debug, ("Session.NotifyTracksAvailable track type = (%d)", trackTypes)); @@ -336,7 +376,7 @@ class MediaRecorder::Session: public nsIObserver { LOG(LogLevel::Debug, ("Session.DestroyRunnable session refcnt = (%d) stopIssued %d s=(%p)", (int)mSession->mRefCnt, mSession->mStopIssued, mSession.get())); - MOZ_ASSERT(NS_IsMainThread() && mSession.get()); + MOZ_ASSERT(NS_IsMainThread() && mSession); RefPtr recorder = mSession->mRecorder; if (!recorder) { return NS_OK; @@ -378,13 +418,15 @@ class MediaRecorder::Session: public nsIObserver public: Session(MediaRecorder* aRecorder, int32_t aTimeSlice) - : mRecorder(aRecorder), - mTimeSlice(aTimeSlice), - mStopIssued(false), - mCanRetrieveData(false), - mIsRegisterProfiler(false) + : mRecorder(aRecorder) + , mTimeSlice(aTimeSlice) + , mStopIssued(false) + , mIsStartEventFired(false) + , mIsRegisterProfiler(false) + , mNeedSessionEndTask(true) { MOZ_ASSERT(NS_IsMainThread()); + MOZ_COUNT_CTOR(MediaRecorder::Session); uint32_t maxMem = Preferences::GetUint("media.recorder.max_memory", MAX_ALLOW_MEMORY_BUFFER); @@ -392,12 +434,79 @@ public: mLastBlobTimeStamp = TimeStamp::Now(); } + void PrincipalChanged(MediaStreamTrack* aTrack) override + { + NS_ASSERTION(mMediaStreamTracks.Contains(aTrack), + "Principal changed for unrecorded track"); + if (!MediaStreamTracksPrincipalSubsumes()) { + DoSessionEndTask(NS_ERROR_DOM_SECURITY_ERR); + } + } + + void NotifyTrackAdded(const RefPtr& aTrack) override + { + LOG(LogLevel::Warning, ("Session.NotifyTrackAdded %p Raising error due to track set change", this)); + DoSessionEndTask(NS_ERROR_ABORT); + } + + void NotifyTrackRemoved(const RefPtr& aTrack) override + { + RefPtr foundInputPort; + for (RefPtr inputPort : mInputPorts) { + if (aTrack->IsForwardedThrough(inputPort)) { + foundInputPort = inputPort; + break; + } + } + + if (foundInputPort) { + // A recorded track was removed or ended. End it in the recording. + // Don't raise an error. + foundInputPort->Destroy(); + DebugOnly removed = mInputPorts.RemoveElement(foundInputPort); + MOZ_ASSERT(removed); + return; + } + + LOG(LogLevel::Warning, ("Session.NotifyTrackRemoved %p Raising error due to track set change", this)); + DoSessionEndTask(NS_ERROR_ABORT); + } + void Start() { LOG(LogLevel::Debug, ("Session.Start %p", this)); MOZ_ASSERT(NS_IsMainThread()); - SetupStreams(); + // Create a Track Union Stream + MediaStreamGraph* gm = mRecorder->GetSourceMediaStream()->Graph(); + mTrackUnionStream = gm->CreateTrackUnionStream(nullptr); + MOZ_ASSERT(mTrackUnionStream, "CreateTrackUnionStream failed"); + + mTrackUnionStream->SetAutofinish(true); + + DOMMediaStream* domStream = mRecorder->Stream(); + if (domStream) { + // Get the available tracks from the DOMMediaStream. + // The callback will report back tracks that we have to connect to + // mTrackUnionStream and listen to principal changes on. + TracksAvailableCallback* tracksAvailableCallback = new TracksAvailableCallback(this); + domStream->OnTracksAvailable(tracksAvailableCallback); + } else { + // Check that we may access the audio node's content. + if (!AudioNodePrincipalSubsumes()) { + LOG(LogLevel::Warning, ("Session.Start AudioNode principal check failed")); + DoSessionEndTask(NS_ERROR_DOM_SECURITY_ERR); + return; + } + // Bind this Track Union Stream with Source Media. + RefPtr inputPort = + mTrackUnionStream->AllocateInputPort(mRecorder->GetSourceMediaStream()); + mInputPorts.AppendElement(inputPort.forget()); + MOZ_ASSERT(mInputPorts[mInputPorts.Length()-1]); + + // Web Audio node has only audio. + InitEncoder(ContainerWriter::CREATE_AUDIO_TRACK); + } } void Stop() @@ -406,6 +515,11 @@ public: MOZ_ASSERT(NS_IsMainThread()); mStopIssued = true; CleanupStreams(); + if (mNeedSessionEndTask) { + LOG(LogLevel::Debug, ("Session.Stop mNeedSessionEndTask %p", this)); + // End the Session directly if there is no ExtractRunnable. + DoSessionEndTask(NS_OK); + } nsContentUtils::UnregisterShutdownObserver(this); } @@ -416,6 +530,9 @@ public: NS_ENSURE_TRUE(mTrackUnionStream, NS_ERROR_FAILURE); mTrackUnionStream->Suspend(); + if (mEncoder) { + mEncoder->Suspend(); + } return NS_OK; } @@ -426,6 +543,9 @@ public: MOZ_ASSERT(NS_IsMainThread()); NS_ENSURE_TRUE(mTrackUnionStream, NS_ERROR_FAILURE); + if (mEncoder) { + mEncoder->Resume(); + } mTrackUnionStream->Resume(); return NS_OK; @@ -463,8 +583,7 @@ public: size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { - size_t amount = mEncoder->SizeOfExcludingThis(aMallocSizeOf); - return amount; + return (mEncoder ? mEncoder->SizeOfExcludingThis(aMallocSizeOf) : 0); } @@ -472,6 +591,7 @@ private: // Only DestroyRunnable is allowed to delete Session object. virtual ~Session() { + MOZ_COUNT_DTOR(MediaRecorder::Session); LOG(LogLevel::Debug, ("Session.~Session (%p)", this)); CleanupStreams(); } @@ -502,10 +622,10 @@ private: if (!encodedBuf[i].IsEmpty()) { mEncodedBufferCache->AppendBuffer(encodedBuf[i]); // Fire the start event when encoded data is available. - if (!mCanRetrieveData) { + if (!mIsStartEventFired) { NS_DispatchToMainThread( new DispatchStartEventRunnable(this, NS_LITERAL_STRING("start"))); - mCanRetrieveData = true; + mIsStartEventFired = true; } } } @@ -518,6 +638,12 @@ private: pushBlob = true; } if (pushBlob || aForceFlush) { + // Fire the start event before the blob. + if (!mIsStartEventFired) { + NS_DispatchToMainThread( + new DispatchStartEventRunnable(this, NS_LITERAL_STRING("start"))); + mIsStartEventFired = true; + } if (NS_FAILED(NS_DispatchToMainThread(new EncoderErrorNotifierRunnable(this)))) { MOZ_ASSERT(false, "NS_DispatchToMainThread EncoderErrorNotifierRunnable failed"); } @@ -529,30 +655,58 @@ private: } } - // Bind media source with MediaEncoder to receive raw media data. - void SetupStreams() + void MediaStreamReady(DOMMediaStream& aStream) { + mMediaStream = &aStream; + aStream.RegisterTrackListener(this); + } + + void ConnectMediaStreamTrack(MediaStreamTrack& aTrack) { - MOZ_ASSERT(NS_IsMainThread()); + mMediaStreamTracks.AppendElement(&aTrack); + aTrack.AddPrincipalChangeObserver(this); + RefPtr inputPort = + aTrack.ForwardTrackContentsTo(mTrackUnionStream); + MOZ_ASSERT(inputPort); + mInputPorts.AppendElement(inputPort.forget()); + MOZ_ASSERT(mInputPorts[mInputPorts.Length()-1]); + } - // Create a Track Union Stream - MediaStreamGraph* gm = mRecorder->GetSourceMediaStream()->Graph(); - mTrackUnionStream = gm->CreateTrackUnionStream(nullptr); - MOZ_ASSERT(mTrackUnionStream, "CreateTrackUnionStream failed"); - - mTrackUnionStream->SetAutofinish(true); - - // Bind this Track Union Stream with Source Media. - mInputPort = mTrackUnionStream->AllocateInputPort(mRecorder->GetSourceMediaStream()); - - DOMMediaStream* domStream = mRecorder->Stream(); - if (domStream) { - // Get the track type hint from DOM media stream. - TracksAvailableCallback* tracksAvailableCallback = new TracksAvailableCallback(this); - domStream->OnTracksAvailable(tracksAvailableCallback); - } else { - // Web Audio node has only audio. - InitEncoder(ContainerWriter::CREATE_AUDIO_TRACK); + bool PrincipalSubsumes(nsIPrincipal* aPrincipal) + { + if (!mRecorder->GetOwner()) + return false; + nsCOMPtr doc = mRecorder->GetOwner()->GetExtantDoc(); + if (!doc) { + return false; } + if (!aPrincipal) { + return false; + } + bool subsumes; + if (NS_FAILED(doc->NodePrincipal()->Subsumes(aPrincipal, &subsumes))) { + return false; + } + return subsumes; + } + + bool MediaStreamTracksPrincipalSubsumes() + { + MOZ_ASSERT(mRecorder->mDOMStream); + nsCOMPtr principal = nullptr; + for (RefPtr& track : mMediaStreamTracks) { + nsContentUtils::CombineResourcePrincipals(&principal, track->GetPrincipal()); + } + return PrincipalSubsumes(principal); + } + + bool AudioNodePrincipalSubsumes() + { + MOZ_ASSERT(mRecorder->mAudioNode != nullptr); + nsIDocument* doc = mRecorder->mAudioNode->GetOwner() + ? mRecorder->mAudioNode->GetOwner()->GetExtantDoc() + : nullptr; + nsCOMPtr principal = doc ? doc->NodePrincipal() : nullptr; + return PrincipalSubsumes(principal); } bool CheckPermission(const char* aType) @@ -591,6 +745,10 @@ private: LOG(LogLevel::Debug, ("Session.InitEncoder %p", this)); MOZ_ASSERT(NS_IsMainThread()); + if (!mRecorder) { + LOG(LogLevel::Debug, ("Session.InitEncoder failure, mRecorder is null %p", this)); + return; + } // Allocate encoder and bind with union stream. // At this stage, the API doesn't allow UA to choose the output mimeType format. @@ -616,6 +774,7 @@ private: } if (!mEncoder) { + LOG(LogLevel::Debug, ("Session.InitEncoder !mEncoder %p", this)); DoSessionEndTask(NS_ERROR_ABORT); return; } @@ -624,58 +783,105 @@ private: // The Session::stop would clean the mTrackUnionStream. If the AfterTracksAdded // comes after stop command, this function would crash. if (!mTrackUnionStream) { + LOG(LogLevel::Debug, ("Session.InitEncoder !mTrackUnionStream %p", this)); DoSessionEndTask(NS_OK); return; } mTrackUnionStream->AddListener(mEncoder); + // Try to use direct listeners if possible + DOMMediaStream* domStream = mRecorder->Stream(); + if (domStream && domStream->GetInputStream()) { + mInputStream = domStream->GetInputStream()->AsSourceStream(); + if (mInputStream) { + mInputStream->AddDirectListener(mEncoder); + mEncoder->SetDirectConnect(true); + } + } + // Create a thread to read encode media data from MediaEncoder. if (!mReadThread) { nsresult rv = NS_NewNamedThread("Media_Encoder", getter_AddRefs(mReadThread)); if (NS_FAILED(rv)) { + LOG(LogLevel::Debug, ("Session.InitEncoder !mReadThread %p", this)); DoSessionEndTask(rv); return; } } - // In case source media stream does not notify track end, recieve + // In case source media stream does not notify track end, receive // shutdown notification and stop Read Thread. nsContentUtils::RegisterShutdownObserver(this); nsCOMPtr event = new ExtractRunnable(this); if (NS_FAILED(mReadThread->Dispatch(event, NS_DISPATCH_NORMAL))) { NS_WARNING("Failed to dispatch ExtractRunnable at beginning"); + LOG(LogLevel::Debug, ("Session.InitEncoder !ReadThread->Dispatch %p", this)); + DoSessionEndTask(NS_ERROR_ABORT); } + // Set mNeedSessionEndTask to false because the + // ExtractRunnable/DestroyRunnable will take the response to + // end the session. + mNeedSessionEndTask = false; } // application should get blob and onstop event void DoSessionEndTask(nsresult rv) { MOZ_ASSERT(NS_IsMainThread()); - if (NS_FAILED(rv)) { - mRecorder->NotifyError(rv); - } - CleanupStreams(); + NS_DispatchToMainThread( + new DispatchStartEventRunnable(this, NS_LITERAL_STRING("start"))); + + if (NS_FAILED(rv)) { + nsCOMPtr runnable = + NS_NewRunnableMethodWithArg(mRecorder, + &MediaRecorder::NotifyError, rv); + NS_DispatchToMainThread(runnable); + } if (NS_FAILED(NS_DispatchToMainThread(new EncoderErrorNotifierRunnable(this)))) { MOZ_ASSERT(false, "NS_DispatchToMainThread EncoderErrorNotifierRunnable failed"); } - if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this)))) { - MOZ_ASSERT(false, "NS_DispatchToMainThread PushBlobRunnable failed"); + if (rv != NS_ERROR_DOM_SECURITY_ERR) { + // Don't push a blob if there was a security error. + if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this)))) { + MOZ_ASSERT(false, "NS_DispatchToMainThread PushBlobRunnable failed"); + } } if (NS_FAILED(NS_DispatchToMainThread(new DestroyRunnable(this)))) { MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed"); } + mNeedSessionEndTask = false; } void CleanupStreams() { - if (mInputPort.get()) { - mInputPort->Destroy(); - mInputPort = nullptr; + if (mInputStream) { + if (mEncoder) { + mInputStream->RemoveDirectListener(mEncoder); + } + mInputStream = nullptr; } + for (RefPtr& inputPort : mInputPorts) { + MOZ_ASSERT(inputPort); + inputPort->Destroy(); + } + mInputPorts.Clear(); - if (mTrackUnionStream.get()) { + if (mTrackUnionStream) { + if (mEncoder) { + mTrackUnionStream->RemoveListener(mEncoder); + } mTrackUnionStream->Destroy(); mTrackUnionStream = nullptr; } + + if (mMediaStream) { + mMediaStream->UnregisterTrackListener(this); + mMediaStream = nullptr; + } + + for (RefPtr& track : mMediaStreamTracks) { + track->RemovePrincipalChangeObserver(this); + } + mMediaStreamTracks.Clear(); } NS_IMETHODIMP Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData) override @@ -714,7 +920,15 @@ private: // Receive track data from source and dispatch to Encoder. // Pause/ Resume controller. RefPtr mTrackUnionStream; - RefPtr mInputPort; + RefPtr mInputStream; + nsTArray> mInputPorts; + + // Stream currently recorded. + RefPtr mMediaStream; + + // Tracks currently recorded. This should be a subset of mMediaStream's track + // set. + nsTArray> mMediaStreamTracks; // Runnable thread for read data from MediaEncode. nsCOMPtr mReadThread; @@ -733,10 +947,14 @@ private: const int32_t mTimeSlice; // Indicate this session's stop has been called. bool mStopIssued; - // Indicate session has encoded data. This can be changed in recording thread. - bool mCanRetrieveData; + // Indicate the session had fire start event. Encoding thread only. + bool mIsStartEventFired; // The register flag for "Media_Encoder" thread to profiler bool mIsRegisterProfiler; + // False if the InitEncoder called successfully, ensure the + // ExtractRunnable/DestroyRunnable will end the session. + // Main thread only. + bool mNeedSessionEndTask; }; NS_IMPL_ISUPPORTS(MediaRecorder::Session, nsIObserver) @@ -785,8 +1003,10 @@ MediaRecorder::MediaRecorder(AudioNode& aSrcAudioNode, mPipeStream = AudioNodeStream::Create(ctx, engine, flags); AudioNodeStream* ns = aSrcAudioNode.GetStream(); if (ns) { - mInputPort = mPipeStream->AllocateInputPort(aSrcAudioNode.GetStream(), - TRACK_ANY, 0, aSrcOutput); + mInputPort = + mPipeStream->AllocateInputPort(aSrcAudioNode.GetStream(), + TRACK_ANY, TRACK_ANY, + 0, aSrcOutput); } } mAudioNode = &aSrcAudioNode; @@ -842,15 +1062,24 @@ MediaRecorder::Start(const Optional& aTimeSlice, ErrorResult& aResult) return; } - // Check if source media stream is valid. See bug 919051. - if (mDOMStream && !mDOMStream->GetPrincipal()) { - aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); - return; + nsTArray> tracks; + if (mDOMStream) { + mDOMStream->GetTracks(tracks); } - - if (!CheckPrincipal()) { - aResult.Throw(NS_ERROR_DOM_SECURITY_ERR); - return; + if (!tracks.IsEmpty()) { + // If there are tracks already available that we're not allowed + // to record, we should throw a security error. + bool subsumes = false; + nsPIDOMWindow* window; + nsIDocument* doc; + if (!(window = GetOwner()) || + !(doc = window->GetExtantDoc()) || + NS_FAILED(doc->NodePrincipal()->Subsumes( + mDOMStream->GetPrincipal(), &subsumes)) || + !subsumes) { + aResult.Throw(NS_ERROR_DOM_SECURITY_ERR); + return; + } } int32_t timeSlice = 0; @@ -952,6 +1181,11 @@ MediaRecorder::Constructor(const GlobalObject& aGlobal, return nullptr; } + if (!IsTypeSupported(aInitDict.mMimeType)) { + aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return nullptr; + } + RefPtr object = new MediaRecorder(aStream, ownerWindow); object->SetOptions(aInitDict); return object.forget(); @@ -986,6 +1220,11 @@ MediaRecorder::Constructor(const GlobalObject& aGlobal, return nullptr; } + if (!IsTypeSupported(aInitDict.mMimeType)) { + aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return nullptr; + } + RefPtr object = new MediaRecorder(aSrcAudioNode, aSrcOutput, ownerWindow); @@ -1016,15 +1255,110 @@ MediaRecorder::SetOptions(const MediaRecorderOptions& aInitDict) } } +static char const *const gWebMAudioEncoderCodecs[2] = { + "opus", + // no VP9 yet + nullptr, +}; +static char const *const gWebMVideoEncoderCodecs[4] = { + "opus", + "vp8", + "vp8.0", + // no VP9 yet + nullptr, +}; +static char const *const gOggAudioEncoderCodecs[2] = { + "opus", + // we could support vorbis here too, but don't + nullptr, +}; + +template +static bool +CodecListContains(char const *const * aCodecs, const String& aCodec) +{ + for (int32_t i = 0; aCodecs[i]; ++i) { + if (aCodec.EqualsASCII(aCodecs[i])) + return true; + } + return false; +} + +/* static */ +bool +MediaRecorder::IsTypeSupported(GlobalObject& aGlobal, const nsAString& aMIMEType) +{ + return IsTypeSupported(aMIMEType); +} + +/* static */ +bool +MediaRecorder::IsTypeSupported(const nsAString& aMIMEType) +{ + char const* const* codeclist = nullptr; + + if (aMIMEType.IsEmpty()) { + return true; + } + + nsContentTypeParser parser(aMIMEType); + nsAutoString mimeType; + nsresult rv = parser.GetType(mimeType); + if (NS_FAILED(rv)) { + return false; + } + + // effectively a 'switch (mimeType) {' + if (mimeType.EqualsLiteral(AUDIO_OGG)) { + if (MediaDecoder::IsOggEnabled() && MediaDecoder::IsOpusEnabled()) { + codeclist = gOggAudioEncoderCodecs; + } + } +#ifdef MOZ_WEBM_ENCODER + else if (mimeType.EqualsLiteral(VIDEO_WEBM) && + MediaEncoder::IsWebMEncoderEnabled()) { + codeclist = gWebMVideoEncoderCodecs; + } +#endif +#ifdef MOZ_OMX_ENCODER + // We're working on MP4 encoder support for desktop + else if (mimeType.EqualsLiteral(VIDEO_MP4) || + mimeType.EqualsLiteral(AUDIO_3GPP) || + mimeType.EqualsLiteral(AUDIO_3GPP2)) { + if (MediaEncoder::IsOMXEncoderEnabled()) { + // XXX check codecs for MP4/3GPP + return true; + } + } +#endif + + // codecs don't matter if we don't support the container + if (!codeclist) { + return false; + } + // now filter on codecs, and if needed rescind support + nsAutoString codecstring; + rv = parser.GetParameter("codecs", codecstring); + + nsTArray codecs; + if (!ParseCodecsString(codecstring, codecs)) { + return false; + } + for (const nsString& codec : codecs) { + if (!CodecListContains(codeclist, codec)) { + // Totally unsupported codec + return false; + } + } + + return true; +} + nsresult MediaRecorder::CreateAndDispatchBlobEvent(already_AddRefed&& aBlob) { MOZ_ASSERT(NS_IsMainThread(), "Not running on main thread"); - if (!CheckPrincipal()) { - // Media is not same-origin, don't allow the data out. - RefPtr blob = aBlob; - return NS_ERROR_DOM_SECURITY_ERR; - } + BlobEventInit init; init.mBubbles = false; init.mCancelable = false; @@ -1097,29 +1431,6 @@ MediaRecorder::NotifyError(nsresult aRv) return; } -bool MediaRecorder::CheckPrincipal() -{ - MOZ_ASSERT(NS_IsMainThread(), "Not running on main thread"); - if (!mDOMStream && !mAudioNode) { - return false; - } - if (!GetOwner()) - return false; - nsCOMPtr doc = GetOwner()->GetExtantDoc(); - if (!doc) { - return false; - } - nsIPrincipal* srcPrincipal = GetSourcePrincipal(); - if (!srcPrincipal) { - return false; - } - bool subsumes; - if (NS_FAILED(doc->NodePrincipal()->Subsumes(srcPrincipal, &subsumes))) { - return false; - } - return subsumes; -} - void MediaRecorder::RemoveSession(Session* aSession) { @@ -1155,17 +1466,6 @@ MediaRecorder::GetSourceMediaStream() return mPipeStream ? mPipeStream.get() : mAudioNode->GetStream(); } -nsIPrincipal* -MediaRecorder::GetSourcePrincipal() -{ - if (mDOMStream != nullptr) { - return mDOMStream->GetPrincipal(); - } - MOZ_ASSERT(mAudioNode != nullptr); - nsIDocument* doc = mAudioNode->GetOwner() ? mAudioNode->GetOwner()->GetExtantDoc() : nullptr; - return doc ? doc->NodePrincipal() : nullptr; -} - size_t MediaRecorder::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { diff --git a/dom/media/MediaRecorder.h b/dom/media/MediaRecorder.h index 7fafeb2875..2b225ba8e0 100644 --- a/dom/media/MediaRecorder.h +++ b/dom/media/MediaRecorder.h @@ -22,6 +22,7 @@ class ErrorResult; class MediaInputPort; struct MediaRecorderOptions; class MediaStream; +class GlobalObject; namespace dom { @@ -76,6 +77,9 @@ public: // Return the current encoding MIME type selected by the MediaEncoder. void GetMimeType(nsString &aMimeType); + static bool IsTypeSupported(GlobalObject& aGlobal, const nsAString& aType); + static bool IsTypeSupported(const nsAString& aType); + // Construct a recorder with a DOM media stream object as its source. static already_AddRefed Constructor(const GlobalObject& aGlobal, @@ -117,8 +121,6 @@ protected: void DispatchSimpleEvent(const nsAString & aStr); // Creating a error event with message. void NotifyError(nsresult aRv); - // Check if the recorder's principal is the subsume of mediaStream - bool CheckPrincipal(); // Set encoded MIME type. void SetMimeType(const nsString &aMimeType); void SetOptions(const MediaRecorderOptions& aInitDict); @@ -128,7 +130,6 @@ protected: void RemoveSession(Session* aSession); // Functions for Session to query input source info. MediaStream* GetSourceMediaStream(); - nsIPrincipal* GetSourcePrincipal(); // DOM wrapper for source media stream. Will be null when input is audio node. RefPtr mDOMStream; // Source audio node. Will be null when input is a media stream. diff --git a/dom/media/MediaSegment.h b/dom/media/MediaSegment.h index 25546797ed..d6c048ef9c 100644 --- a/dom/media/MediaSegment.h +++ b/dom/media/MediaSegment.h @@ -7,6 +7,8 @@ #define MOZILLA_MEDIASEGMENT_H_ #include "nsTArray.h" +#include "nsIPrincipal.h" +#include "nsProxyRelease.h" #ifdef MOZILLA_INTERNAL_API #include "mozilla/TimeStamp.h" #endif @@ -54,6 +56,50 @@ const StreamTime STREAM_TIME_MAX = MEDIA_TIME_MAX; typedef MediaTime GraphTime; const GraphTime GRAPH_TIME_MAX = MEDIA_TIME_MAX; +/** + * We pass the principal through the MediaStreamGraph by wrapping it in a thread + * safe nsMainThreadPtrHandle, since it cannot be used directly off the main + * thread. We can compare two PrincipalHandles to each other on any thread, but + * they can only be created and converted back to nsIPrincipal* on main thread. + */ +typedef nsMainThreadPtrHandle PrincipalHandle; + +inline PrincipalHandle MakePrincipalHandle(nsIPrincipal* aPrincipal) +{ + RefPtr> holder = + new nsMainThreadPtrHolder(aPrincipal); + return PrincipalHandle(holder); +} + +const PrincipalHandle PRINCIPAL_HANDLE_NONE(nullptr); + +inline nsIPrincipal* GetPrincipalFromHandle(PrincipalHandle& aPrincipalHandle) +{ + MOZ_ASSERT(NS_IsMainThread()); + return aPrincipalHandle.get(); +} + +inline bool PrincipalHandleMatches(PrincipalHandle& aPrincipalHandle, + nsIPrincipal* aOther) +{ + if (!aOther) { + return false; + } + + nsIPrincipal* principal = GetPrincipalFromHandle(aPrincipalHandle); + if (!principal) { + return false; + } + + bool result; + if (NS_FAILED(principal->Equals(aOther, &result))) { + NS_ERROR("Principal check failed"); + return false; + } + + return result; +} + /** * A MediaSegment is a chunk of media data sequential in time. Different * types of data have different subclasses of MediaSegment, all inheriting @@ -85,6 +131,19 @@ public: StreamTime GetDuration() const { return mDuration; } Type GetType() const { return mType; } + /** + * Gets the last principal id that was appended to this segment. + */ + PrincipalHandle GetLastPrincipalHandle() const { return mLastPrincipalHandle; } + /** + * Called by the MediaStreamGraph as it appends a chunk with a different + * principal id than the current one. + */ + void SetLastPrincipalHandle(PrincipalHandle aLastPrincipalHandle) + { + mLastPrincipalHandle = aLastPrincipalHandle; + } + /** * Create a MediaSegment of the same type. */ @@ -134,13 +193,18 @@ public: } protected: - explicit MediaSegment(Type aType) : mDuration(0), mType(aType) + explicit MediaSegment(Type aType) + : mDuration(0), mType(aType), mLastPrincipalHandle(PRINCIPAL_HANDLE_NONE) { MOZ_COUNT_CTOR(MediaSegment); } StreamTime mDuration; // total of mDurations of all chunks Type mType; + + // The latest principal handle that the MediaStreamGraph has processed for + // this segment. + PrincipalHandle mLastPrincipalHandle; }; /** diff --git a/dom/media/MediaStreamGraph.cpp b/dom/media/MediaStreamGraph.cpp index 3faccf9326..74bad4366e 100644 --- a/dom/media/MediaStreamGraph.cpp +++ b/dom/media/MediaStreamGraph.cpp @@ -28,6 +28,7 @@ #include "DOMMediaStream.h" #include "GeckoProfiler.h" #include "mozilla/unused.h" +#include "mozilla/media/MediaUtils.h" #ifdef MOZ_WEBRTC #include "AudioOutputObserver.h" #endif @@ -38,6 +39,7 @@ using namespace mozilla::layers; using namespace mozilla::dom; using namespace mozilla::gfx; +using namespace mozilla::media; namespace mozilla { @@ -199,12 +201,21 @@ MediaStreamGraphImpl::ExtractPendingInput(SourceMediaStream* aStream, for (int32_t i = aStream->mUpdateTracks.Length() - 1; i >= 0; --i) { SourceMediaStream::TrackData* data = &aStream->mUpdateTracks[i]; aStream->ApplyTrackDisabling(data->mID, data->mData); + StreamTime offset = (data->mCommands & SourceMediaStream::TRACK_CREATE) + ? data->mStart : aStream->mBuffer.FindTrack(data->mID)->GetSegment()->GetDuration(); for (MediaStreamListener* l : aStream->mListeners) { - StreamTime offset = (data->mCommands & SourceMediaStream::TRACK_CREATE) - ? data->mStart : aStream->mBuffer.FindTrack(data->mID)->GetSegment()->GetDuration(); l->NotifyQueuedTrackChanges(this, data->mID, offset, data->mCommands, *data->mData); } + for (TrackBound& b : aStream->mTrackListeners) { + if (b.mTrackID != data->mID) { + continue; + } + b.mListener->NotifyQueuedChanges(this, offset, *data->mData); + if (data->mCommands & SourceMediaStream::TRACK_END) { + b.mListener->NotifyEnded(); + } + } if (data->mCommands & SourceMediaStream::TRACK_CREATE) { MediaSegment* segment = data->mData.forget(); STREAM_LOG(LogLevel::Debug, ("SourceMediaStream %p creating track %d, start %lld, initial end %lld", @@ -322,6 +333,72 @@ MediaStreamGraphImpl::UpdateCurrentTimeForStreams(GraphTime aPrevCurrentTime) } } +template +void +MediaStreamGraphImpl::ProcessChunkMetadataForInterval(MediaStream* aStream, + TrackID aTrackID, + C& aSegment, + StreamTime aStart, + StreamTime aEnd) +{ + MOZ_ASSERT(aStream); + MOZ_ASSERT(IsTrackIDExplicit(aTrackID)); + + StreamTime offset = 0; + for (typename C::ConstChunkIterator chunk(aSegment); + !chunk.IsEnded(); chunk.Next()) { + if (offset >= aEnd) { + break; + } + offset += chunk->GetDuration(); + if (chunk->IsNull() || offset < aStart) { + continue; + } + PrincipalHandle principalHandle = chunk->GetPrincipalHandle(); + if (principalHandle != aSegment.GetLastPrincipalHandle()) { + aSegment.SetLastPrincipalHandle(principalHandle); + STREAM_LOG(LogLevel::Debug, ("MediaStream %p track %d, principalHandle " + "changed in %sChunk with duration %lld", + aStream, aTrackID, + aSegment.GetType() == MediaSegment::AUDIO + ? "Audio" : "Video", + (long long) chunk->GetDuration())); + for (const TrackBound& listener : aStream->mTrackListeners) { + if (listener.mTrackID == aTrackID) { + listener.mListener->NotifyPrincipalHandleChanged(this, principalHandle); + } + } + } + } +} + +void +MediaStreamGraphImpl::ProcessChunkMetadata(GraphTime aPrevCurrentTime) +{ + for (MediaStream* stream : AllStreams()) { + StreamTime iterationStart = stream->GraphTimeToStreamTime(aPrevCurrentTime); + StreamTime iterationEnd = stream->GraphTimeToStreamTime(mProcessedTime); + for (StreamBuffer::TrackIter tracks(stream->mBuffer); + !tracks.IsEnded(); tracks.Next()) { + MediaSegment* segment = tracks->GetSegment(); + if (!segment) { + continue; + } + if (tracks->GetType() == MediaSegment::AUDIO) { + AudioSegment* audio = static_cast(segment); + ProcessChunkMetadataForInterval( + stream, tracks->GetID(), *audio, iterationStart, iterationEnd); + } else if (tracks->GetType() == MediaSegment::VIDEO) { + VideoSegment* video = static_cast(segment); + ProcessChunkMetadataForInterval( + stream, tracks->GetID(), *video, iterationStart, iterationEnd); + } else { + MOZ_CRASH("Unknown track type"); + } + } + } +} + GraphTime MediaStreamGraphImpl::WillUnderrun(MediaStream* aStream, GraphTime aEndBlockingDecisions) @@ -823,6 +900,7 @@ MediaStreamGraphImpl::PlayVideo(MediaStream* aStream) // Collect any new frames produced in this iteration. AutoTArray newImages; + PrincipalHandle lastPrincipalHandle = PRINCIPAL_HANDLE_NONE; RefPtr blackImage; MOZ_ASSERT(mProcessedTime >= aStream->mBufferStartTime, "frame position before buffer?"); @@ -886,6 +964,8 @@ MediaStreamGraphImpl::PlayVideo(MediaStream* aStream) } newImages.AppendElement(ImageContainer::NonOwningImage(image, targetTime)); + lastPrincipalHandle = chunk->GetPrincipalHandle(); + aStream->mLastPlayedVideoFrame = *frame; } @@ -898,6 +978,10 @@ MediaStreamGraphImpl::PlayVideo(MediaStream* aStream) for (uint32_t i = 0; i < aStream->mVideoOutputs.Length(); ++i) { VideoFrameContainer* output = aStream->mVideoOutputs[i]; + bool principalHandleChanged = + lastPrincipalHandle != PRINCIPAL_HANDLE_NONE && + lastPrincipalHandle != output->GetLastPrincipalHandle(); + // Find previous frames that may still be valid. AutoTArray previousImages; output->GetImageContainer()->GetCurrentImages(&previousImages); @@ -937,6 +1021,12 @@ MediaStreamGraphImpl::PlayVideo(MediaStream* aStream) image.mFrameID = output->NewFrameID(); images.AppendElement(image); } + + if (principalHandleChanged) { + output->UpdatePrincipalHandleForFrameID(lastPrincipalHandle, + newImages.LastElement().mFrameID); + } + output->SetCurrentFrames(aStream->mLastPlayedVideoFrame.GetIntrinsicSize(), images); @@ -1435,6 +1525,8 @@ MediaStreamGraphImpl::OneIteration(GraphTime aStateEnd) UpdateCurrentTimeForStreams(oldProcessedTime); + ProcessChunkMetadata(oldProcessedTime); + // Process graph messages queued from RunMessageAfterProcessing() on this // thread during the iteration. RunMessagesInQueue(); @@ -1827,6 +1919,7 @@ MediaStream::MediaStream(DOMMediaStream* aWrapper) , mMainThreadFinished(false) , mFinishedNotificationSent(false) , mMainThreadDestroyed(false) + , mNrOfMainThreadUsers(0) , mGraph(nullptr) , mAudioChannelType(dom::AudioChannel::Normal) { @@ -1928,6 +2021,12 @@ MediaStream::FinishOnGraphThread() GraphImpl()->FinishStream(this); } +StreamBuffer::Track* +MediaStream::FindTrack(TrackID aID) +{ + return mBuffer.FindTrack(aID); +} + StreamBuffer::Track* MediaStream::EnsureTrack(TrackID aTrackId) { @@ -1970,6 +2069,8 @@ MediaStream::DestroyImpl() void MediaStream::Destroy() { + NS_ASSERTION(mNrOfMainThreadUsers == 0, + "Do not mix Destroy() and RegisterUser()/UnregisterUser()"); // Keep this stream alive until we leave this method RefPtr kungFuDeathGrip = this; @@ -1994,6 +2095,26 @@ MediaStream::Destroy() mMainThreadDestroyed = true; } +void +MediaStream::RegisterUser() +{ + MOZ_ASSERT(NS_IsMainThread()); + ++mNrOfMainThreadUsers; +} + +void +MediaStream::UnregisterUser() +{ + MOZ_ASSERT(NS_IsMainThread()); + + --mNrOfMainThreadUsers; + NS_ASSERTION(mNrOfMainThreadUsers >= 0, "Double-removal of main thread user"); + NS_ASSERTION(!IsDestroyed(), "Do not mix Destroy() and RegisterUser()/UnregisterUser()"); + if (mNrOfMainThreadUsers == 0) { + Destroy(); + } +} + void MediaStream::AddAudioOutput(void* aKey) { @@ -2229,6 +2350,131 @@ MediaStream::RemoveListener(MediaStreamListener* aListener) } } +void +MediaStream::AddTrackListenerImpl(already_AddRefed aListener, + TrackID aTrackID) +{ + TrackBound* l = mTrackListeners.AppendElement(); + l->mListener = aListener; + l->mTrackID = aTrackID; + + StreamBuffer::Track* track = FindTrack(aTrackID); + if (!track) { + return; + } + PrincipalHandle lastPrincipalHandle = + track->GetSegment()->GetLastPrincipalHandle(); + l->mListener->NotifyPrincipalHandleChanged(Graph(), lastPrincipalHandle); +} + +void +MediaStream::AddTrackListener(MediaStreamTrackListener* aListener, + TrackID aTrackID) +{ + class Message : public ControlMessage { + public: + Message(MediaStream* aStream, MediaStreamTrackListener* aListener, + TrackID aTrackID) : + ControlMessage(aStream), mListener(aListener), mTrackID(aTrackID) {} + virtual void Run() + { + mStream->AddTrackListenerImpl(mListener.forget(), mTrackID); + } + RefPtr mListener; + TrackID mTrackID; + }; + GraphImpl()->AppendMessage(MakeUnique(this, aListener, aTrackID)); +} + +void +MediaStream::RemoveTrackListenerImpl(MediaStreamTrackListener* aListener, + TrackID aTrackID) +{ + for (size_t i = 0; i < mTrackListeners.Length(); ++i) { + if (mTrackListeners[i].mListener == aListener && + mTrackListeners[i].mTrackID == aTrackID) { + mTrackListeners[i].mListener->NotifyRemoved(); + mTrackListeners.RemoveElementAt(i); + return; + } + } +} + +void +MediaStream::RemoveTrackListener(MediaStreamTrackListener* aListener, + TrackID aTrackID) +{ + class Message : public ControlMessage { + public: + Message(MediaStream* aStream, MediaStreamTrackListener* aListener, + TrackID aTrackID) : + ControlMessage(aStream), mListener(aListener), mTrackID(aTrackID) {} + virtual void Run() + { + mStream->RemoveTrackListenerImpl(mListener, mTrackID); + } + RefPtr mListener; + TrackID mTrackID; + }; + GraphImpl()->AppendMessage(MakeUnique(this, aListener, aTrackID)); +} + +void +MediaStream::AddDirectTrackListenerImpl(already_AddRefed aListener, + TrackID aTrackID) +{ + // Base implementation, for streams that don't support direct track listeners. + RefPtr listener = aListener; + listener->NotifyDirectListenerInstalled( + MediaStreamTrackDirectListener::InstallationResult::STREAM_NOT_SUPPORTED); +} + +void +MediaStream::AddDirectTrackListener(MediaStreamTrackDirectListener* aListener, + TrackID aTrackID) +{ + class Message : public ControlMessage { + public: + Message(MediaStream* aStream, MediaStreamTrackDirectListener* aListener, + TrackID aTrackID) : + ControlMessage(aStream), mListener(aListener), mTrackID(aTrackID) {} + virtual void Run() + { + mStream->AddDirectTrackListenerImpl(mListener.forget(), mTrackID); + } + RefPtr mListener; + TrackID mTrackID; + }; + GraphImpl()->AppendMessage(MakeUnique(this, aListener, aTrackID)); +} + +void +MediaStream::RemoveDirectTrackListenerImpl(MediaStreamTrackDirectListener* aListener, + TrackID aTrackID) +{ + // Base implementation, the listener was never added so nothing to do. + RefPtr listener = aListener; +} + +void +MediaStream::RemoveDirectTrackListener(MediaStreamTrackDirectListener* aListener, + TrackID aTrackID) +{ + class Message : public ControlMessage { + public: + Message(MediaStream* aStream, MediaStreamTrackDirectListener* aListener, + TrackID aTrackID) : + ControlMessage(aStream), mListener(aListener), mTrackID(aTrackID) {} + virtual void Run() + { + mStream->RemoveDirectTrackListenerImpl(mListener, mTrackID); + } + RefPtr mListener; + TrackID mTrackID; + }; + GraphImpl()->AppendMessage(MakeUnique(this, aListener, aTrackID)); +} + void MediaStream::RunAfterPendingUpdates(already_AddRefed aRunnable) { @@ -2419,12 +2665,6 @@ SourceMediaStream::FinishAddTracks() } } -StreamBuffer::Track* -SourceMediaStream::FindTrack(TrackID aID) -{ - return mBuffer.FindTrack(aID); -} - void SourceMediaStream::ResampleAudioToGraphSampleRate(TrackData* aTrackData, MediaSegment* aSegment) { @@ -2490,7 +2730,7 @@ void SourceMediaStream::NotifyDirectConsumers(TrackData *aTrack, MediaSegment *aSegment) { - // Call with mMutex locked + mMutex.AssertCurrentThreadOwns(); MOZ_ASSERT(aTrack); for (uint32_t j = 0; j < mDirectListeners.Length(); ++j) { @@ -2499,6 +2739,15 @@ SourceMediaStream::NotifyDirectConsumers(TrackData *aTrack, l->NotifyRealtimeData(static_cast(GraphImpl()), aTrack->mID, offset, aTrack->mCommands, *aSegment); } + + for (const TrackBound& source + : mDirectTrackListeners) { + if (aTrack->mID != source.mTrackID) { + continue; + } + StreamTime offset = 0; // FIX! need a separate StreamTime.... or the end of the internal buffer + source.mListener->NotifyRealtimeTrackDataAndApplyTrackDisabling(Graph(), offset, *aSegment); + } } // These handle notifying all the listeners of an event @@ -2559,6 +2808,63 @@ SourceMediaStream::RemoveDirectListener(MediaStreamDirectListener* aListener) } } +void +SourceMediaStream::AddDirectTrackListenerImpl(already_AddRefed aListener, + TrackID aTrackID) +{ + MOZ_ASSERT(IsTrackIDExplicit(aTrackID)); + TrackData* data; + bool found; + bool isAudio; + RefPtr listener = aListener; + STREAM_LOG(LogLevel::Debug, ("Adding direct track listener %p bound to track %d to source stream %p", + listener.get(), aTrackID, this)); + { + MutexAutoLock lock(mMutex); + data = FindDataForTrack(aTrackID); + found = !!data; + isAudio = found && data->mData->GetType() == MediaSegment::AUDIO; + if (found && isAudio) { + TrackBound* sourceListener = + mDirectTrackListeners.AppendElement(); + sourceListener->mListener = listener; + sourceListener->mTrackID = aTrackID; + } + } + if (!found) { + STREAM_LOG(LogLevel::Warning, ("Couldn't find source track for direct track listener %p", + listener.get())); + listener->NotifyDirectListenerInstalled( + MediaStreamTrackDirectListener::InstallationResult::TRACK_NOT_FOUND_AT_SOURCE); + return; + } + if (!isAudio) { + STREAM_LOG(LogLevel::Warning, ("Source track for direct track listener %p is not audio", + listener.get())); + listener->NotifyDirectListenerInstalled( + MediaStreamTrackDirectListener::InstallationResult::TRACK_TYPE_NOT_SUPPORTED); + return; + } + STREAM_LOG(LogLevel::Debug, ("Added direct track listener %p", listener.get())); + listener->NotifyDirectListenerInstalled( + MediaStreamTrackDirectListener::InstallationResult::SUCCESS); +} + +void +SourceMediaStream::RemoveDirectTrackListenerImpl(MediaStreamTrackDirectListener* aListener, + TrackID aTrackID) +{ + MutexAutoLock lock(mMutex); + for (int32_t i = mDirectTrackListeners.Length() - 1; i >= 0; --i) { + const TrackBound& source = + mDirectTrackListeners[i]; + if (source.mListener == aListener && source.mTrackID == aTrackID) { + aListener->NotifyDirectListenerUninstalled(); + mDirectTrackListeners.RemoveElementAt(i); + } + } +} + StreamTime SourceMediaStream::GetEndOfAppendedData(TrackID aID) { @@ -2605,6 +2911,29 @@ SourceMediaStream::FinishWithLockHeld() } } +void +SourceMediaStream::SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled) +{ + MutexAutoLock lock(mMutex); + for (TrackBound& l: mDirectTrackListeners) { + if (l.mTrackID == aTrackID) { + bool oldEnabled = !mDisabledTrackIDs.Contains(aTrackID); + if (!oldEnabled && aEnabled) { + STREAM_LOG(LogLevel::Debug, ("SourceMediaStream %p track %d setting " + "direct listener enabled", + this, aTrackID)); + l.mListener->DecreaseDisabled(); + } else if (oldEnabled && !aEnabled) { + STREAM_LOG(LogLevel::Debug, ("SourceMediaStream %p track %d setting " + "direct listener disabled", + this, aTrackID)); + l.mListener->IncreaseDisabled(); + } + } + } + MediaStream::SetTrackEnabledImpl(aTrackID, aEnabled); +} + void SourceMediaStream::EndAllTrackAndFinish() { @@ -2719,22 +3048,27 @@ MediaInputPort::SetGraphImpl(MediaStreamGraphImpl* aGraph) } void -MediaInputPort::BlockTrackIdImpl(TrackID aTrackId) +MediaInputPort::BlockSourceTrackIdImpl(TrackID aTrackId) { mBlockedTracks.AppendElement(aTrackId); } -void -MediaInputPort::BlockTrackId(TrackID aTrackId) +already_AddRefed> +MediaInputPort::BlockSourceTrackId(TrackID aTrackId) { class Message : public ControlMessage { public: - explicit Message(MediaInputPort* aPort, TrackID aTrackId) + explicit Message(MediaInputPort* aPort, + TrackID aTrackId, + already_AddRefed aRunnable) : ControlMessage(aPort->GetDestination()), - mPort(aPort), mTrackId(aTrackId) {} + mPort(aPort), mTrackId(aTrackId), mRunnable(aRunnable) {} void Run() override { - mPort->BlockTrackIdImpl(mTrackId); + mPort->BlockSourceTrackIdImpl(mTrackId); + if (mRunnable) { + mStream->Graph()->DispatchToMainThreadAfterStreamStateUpdate(mRunnable.forget()); + } } void RunDuringShutdown() override { @@ -2742,16 +3076,27 @@ MediaInputPort::BlockTrackId(TrackID aTrackId) } RefPtr mPort; TrackID mTrackId; + nsCOMPtr mRunnable; }; - MOZ_ASSERT(aTrackId != TRACK_NONE && aTrackId != TRACK_INVALID && aTrackId != TRACK_ANY, + MOZ_ASSERT(IsTrackIDExplicit(aTrackId), "Only explicit TrackID is allowed"); - GraphImpl()->AppendMessage(MakeUnique(this, aTrackId)); + + RefPtr> pledge = new Pledge(); + nsCOMPtr runnable = NewRunnableFrom([pledge]() { + MOZ_ASSERT(NS_IsMainThread()); + pledge->Resolve(true); + return NS_OK; + }); + GraphImpl()->AppendMessage(MakeUnique(this, aTrackId, runnable.forget())); + return pledge.forget(); } already_AddRefed ProcessedMediaStream::AllocateInputPort(MediaStream* aStream, TrackID aTrackID, - uint16_t aInputNumber, uint16_t aOutputNumber) + TrackID aDestTrackID, + uint16_t aInputNumber, uint16_t aOutputNumber, + nsTArray* aBlockedTracks) { // This method creates two references to the MediaInputPort: one for // the main thread, and one for the MediaStreamGraph. @@ -2774,10 +3119,21 @@ ProcessedMediaStream::AllocateInputPort(MediaStream* aStream, TrackID aTrackID, RefPtr mPort; }; - MOZ_ASSERT(aTrackID != TRACK_NONE && aTrackID != TRACK_INVALID, - "Only TRACK_ANY and explicit ID are allowed"); - RefPtr port = new MediaInputPort(aStream, aTrackID, this, - aInputNumber, aOutputNumber); + MOZ_ASSERT(aStream->GraphImpl() == GraphImpl()); + MOZ_ASSERT(aTrackID == TRACK_ANY || IsTrackIDExplicit(aTrackID), + "Only TRACK_ANY and explicit ID are allowed for source track"); + MOZ_ASSERT(aDestTrackID == TRACK_ANY || IsTrackIDExplicit(aDestTrackID), + "Only TRACK_ANY and explicit ID are allowed for destination track"); + MOZ_ASSERT(aTrackID != TRACK_ANY || aDestTrackID == TRACK_ANY, + "Generic MediaInputPort cannot produce a single destination track"); + RefPtr port = + new MediaInputPort(aStream, aTrackID, this, aDestTrackID, + aInputNumber, aOutputNumber); + if (aBlockedTracks) { + for (TrackID trackID : *aBlockedTracks) { + port->BlockSourceTrackIdImpl(trackID); + } + } port->SetGraphImpl(GraphImpl()); GraphImpl()->AppendMessage(MakeUnique(port)); return port.forget(); diff --git a/dom/media/MediaStreamGraph.h b/dom/media/MediaStreamGraph.h index 052fe6e58e..cf2c365b61 100644 --- a/dom/media/MediaStreamGraph.h +++ b/dom/media/MediaStreamGraph.h @@ -41,6 +41,10 @@ namespace dom { enum class AudioContextOperation; } +namespace media { + template class Pledge; +} + /* * MediaStreamGraph is a framework for synchronized audio/video processing * and playback. It is designed to be used by other browser components such as @@ -216,6 +220,44 @@ public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioDataListener) }; +/** + * This is a base class for media graph thread listener callbacks locked to + * specific tracks. Override methods to be notified of audio or video data or + * changes in track state. + * + * All notification methods are called from the media graph thread. Overriders + * of these methods are responsible for all synchronization. Beware! + * These methods are called without the media graph monitor held, so + * reentry into media graph methods is possible, although very much discouraged! + * You should do something non-blocking and non-reentrant (e.g. dispatch an + * event to some thread) and return. + * The listener is not allowed to add/remove any listeners from the parent + * stream. + * + * If a listener is attached to a track that has already ended, we guarantee + * to call NotifyEnded. + */ +class MediaStreamTrackListener +{ + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStreamTrackListener) + +public: + virtual void NotifyQueuedChanges(MediaStreamGraph* aGraph, + StreamTime aTrackOffset, + const MediaSegment& aQueuedMedia) {} + + virtual void NotifyPrincipalHandleChanged(MediaStreamGraph* aGraph, + const PrincipalHandle& aNewPrincipalHandle) {} + + virtual void NotifyEnded() {} + + virtual void NotifyRemoved() {} + +protected: + virtual ~MediaStreamTrackListener() {} +}; + + /** * This is a base class for media graph thread listener direct callbacks * from within AppendToTrack(). Note that your regular listener will @@ -239,6 +281,117 @@ public: const MediaSegment& aMedia) {} }; +/** + * This is a base class for media graph thread listener direct callbacks from + * within AppendToTrack(). It is bound to a certain track and can only be + * installed on audio tracks. Once added to a track on any stream in the graph, + * the graph will try to install it at that track's source of media data. + * + * This works for TrackUnionStreams, which will forward the listener to the + * track's input track if it exists, or wait for it to be created before + * forwarding if it doesn't. + * Once it reaches a SourceMediaStream, it can be successfully installed. + * Other types of streams will fail installation since they are not supported. + * + * Note that this listener and others for the same track will still get + * NotifyQueuedChanges() callbacks from the MSG tread, so you must be careful + * to ignore them if this listener was successfully installed. + */ +class MediaStreamTrackDirectListener : public MediaStreamTrackListener +{ + friend class SourceMediaStream; + friend class TrackUnionStream; + +public: + /* + * This will be called on any MediaStreamTrackDirectListener added to a + * SourceMediaStream when AppendToTrack() is called for the listener's bound + * track. The MediaSegment will be the RawSegment (unresampled) if available + * in AppendToTrack(). + * If the track is enabled at the source but has been disabled in one of the + * streams in between the source and where it was originally added, aMedia + * will be a disabled version of the one passed to AppendToTrack() as well. + * Note that NotifyQueuedTrackChanges() calls will also + * still occur. + */ + virtual void NotifyRealtimeTrackData(MediaStreamGraph* aGraph, + StreamTime aTrackOffset, + const MediaSegment& aMedia) {} + + /** + * When a direct listener is processed for installation by the + * MediaStreamGraph it will be notified with whether the installation was + * successful or not. The results of this installation are the following: + * TRACK_NOT_FOUND_AT_SOURCE + * We found the source stream of media data for this track, but the track + * didn't exist. This should only happen if you try to install the listener + * directly to a SourceMediaStream that doesn't contain the given TrackID. + * TRACK_TYPE_NOT_SUPPORTED + * This is the failure when you install the listener to a non-audio track. + * STREAM_NOT_SUPPORTED + * While looking for the data source of this track, we found a MediaStream + * that is not a SourceMediaStream or a TrackUnionStream. + * SUCCESS + * Installation was successful and this listener will start receiving + * NotifyRealtimeData on the next AppendToTrack(). + */ + enum class InstallationResult { + TRACK_NOT_FOUND_AT_SOURCE, + TRACK_TYPE_NOT_SUPPORTED, + STREAM_NOT_SUPPORTED, + SUCCESS + }; + virtual void NotifyDirectListenerInstalled(InstallationResult aResult) {} + virtual void NotifyDirectListenerUninstalled() {} + +protected: + virtual ~MediaStreamTrackDirectListener() {} + + void MirrorAndDisableSegment(AudioSegment& aFrom, AudioSegment& aTo) + { + aTo.Clear(); + aTo.AppendNullData(aFrom.GetDuration()); + } + + void NotifyRealtimeTrackDataAndApplyTrackDisabling(MediaStreamGraph* aGraph, + StreamTime aTrackOffset, + MediaSegment& aMedia) + { + if (mDisabledCount == 0) { + NotifyRealtimeTrackData(aGraph, aTrackOffset, aMedia); + return; + } + + if (!mMedia) { + mMedia = aMedia.CreateEmptyClone(); + } + if (aMedia.GetType() == MediaSegment::AUDIO) { + MirrorAndDisableSegment(static_cast(aMedia), + static_cast(*mMedia)); + } else { + MOZ_CRASH("Unsupported media type"); + } + NotifyRealtimeTrackData(aGraph, aTrackOffset, *mMedia); + } + + void IncreaseDisabled() + { + ++mDisabledCount; + } + void DecreaseDisabled() + { + --mDisabledCount; + MOZ_ASSERT(mDisabledCount >= 0, "Double decrease"); + } + + // Matches the number of disabled streams to which this listener is attached. + // The number of streams are those between the stream the listener was added + // and the SourceMediaStream that is the input of the data. + Atomic mDisabledCount; + + nsAutoPtr mMedia; +}; + /** * This is a base class for main-thread listener callbacks. * This callback is invoked on the main thread when the main-thread-visible @@ -278,6 +431,16 @@ class AudioNodeExternalInputStream; class AudioNodeStream; class CameraPreviewMediaStream; +/** + * Helper struct for binding a track listener to a specific TrackID. + */ +template +struct TrackBound +{ + RefPtr mListener; + TrackID mTrackID; +}; + /** * A stream of synchronized audio and video data. All (not blocked) streams * progress at the same rate --- "real time". Streams cannot seek. The only @@ -400,6 +563,33 @@ public: // Events will be dispatched by calling methods of aListener. virtual void AddListener(MediaStreamListener* aListener); virtual void RemoveListener(MediaStreamListener* aListener); + virtual void AddTrackListener(MediaStreamTrackListener* aListener, + TrackID aTrackID); + virtual void RemoveTrackListener(MediaStreamTrackListener* aListener, + TrackID aTrackID); + + /** + * Adds aListener to the source stream of track aTrackID in this stream. + * When the MediaStreamGraph processes the added listener, it will traverse + * the graph and add it to the track's source stream (remapping the TrackID + * along the way). + * Note that the listener will be notified on the MediaStreamGraph thread + * with whether the installation of it at the source was successful or not. + */ + virtual void AddDirectTrackListener(MediaStreamTrackDirectListener* aListener, + TrackID aTrackID); + + /** + * Removes aListener from the source stream of track aTrackID in this stream. + * Note that the listener has already been removed if the link between the + * source of track aTrackID and this stream has been broken (and made track + * aTrackID end). The caller doesn't have to care about this, removing when + * the source cannot be found, or when the listener had already been removed + * does nothing. + */ + virtual void RemoveDirectTrackListener(MediaStreamTrackDirectListener* aListener, + TrackID aTrackID); + // A disabled track has video replaced by black, and audio replaced by // silence. void SetTrackEnabled(TrackID aTrackID, bool aEnabled); @@ -431,8 +621,19 @@ public: */ void RunAfterPendingUpdates(already_AddRefed aRunnable); - // Signal that the client is done with this MediaStream. It will be deleted later. + // Signal that the client is done with this MediaStream. It will be deleted + // later. Do not mix usage of Destroy() with RegisterUser()/UnregisterUser(). + // That will cause the MediaStream to be destroyed twice, which will cause + // some assertions to fail. virtual void Destroy(); + // Signal that a client is using this MediaStream. Useful to not have to + // explicitly manage ownership (responsibility to Destroy()) when there are + // multiple clients using a MediaStream. + void RegisterUser(); + // Signal that a client no longer needs this MediaStream. When the number of + // clients using this MediaStream reaches 0, it will be destroyed. + void UnregisterUser(); + // Returns the main-thread's view of how much data has been processed by // this stream. StreamTime GetCurrentTime() @@ -485,6 +686,14 @@ public: void AddListenerImpl(already_AddRefed aListener); void RemoveListenerImpl(MediaStreamListener* aListener); void RemoveAllListenersImpl(); + virtual void AddTrackListenerImpl(already_AddRefed aListener, + TrackID aTrackID); + virtual void RemoveTrackListenerImpl(MediaStreamTrackListener* aListener, + TrackID aTrackID); + virtual void AddDirectTrackListenerImpl(already_AddRefed aListener, + TrackID aTrackID); + virtual void RemoveDirectTrackListenerImpl(MediaStreamTrackDirectListener* aListener, + TrackID aTrackID); virtual void SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled); void AddConsumer(MediaInputPort* aPort) @@ -556,6 +765,11 @@ public: bool HasCurrentData() { return mHasCurrentData; } + /** + * Find track by track id. + */ + StreamBuffer::Track* FindTrack(TrackID aID); + StreamBuffer::Track* EnsureTrack(TrackID aTrack); virtual void ApplyTrackDisabling(TrackID aTrackID, MediaSegment* aSegment, MediaSegment* aRawSegment = nullptr); @@ -638,6 +852,7 @@ protected: // with a different frame id. VideoFrame mLastPlayedVideoFrame; nsTArray > mListeners; + nsTArray> mTrackListeners; nsTArray mMainThreadListeners; nsTArray mDisabledTrackIDs; @@ -706,6 +921,7 @@ protected: bool mMainThreadFinished; bool mFinishedNotificationSent; bool mMainThreadDestroyed; + int mNrOfMainThreadUsers; // Our media stream graph. null if destroyed on the graph thread. MediaStreamGraphImpl* mGraph; @@ -796,11 +1012,6 @@ public: */ void FinishAddTracks(); - /** - * Find track by track id. - */ - StreamBuffer::Track* FindTrack(TrackID aID); - /** * Append media data to a track. Ownership of aSegment remains with the caller, * but aSegment is emptied. @@ -839,11 +1050,7 @@ public: } // Overriding allows us to hold the mMutex lock while changing the track enable status - void - SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled) override { - MutexAutoLock lock(mMutex); - MediaStream::SetTrackEnabledImpl(aTrackID, aEnabled); - } + void SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled) override; // Overriding allows us to ensure mMutex is locked while changing the track enable status void @@ -906,6 +1113,11 @@ protected: void ResampleAudioToGraphSampleRate(TrackData* aTrackData, MediaSegment* aSegment); + void AddDirectTrackListenerImpl(already_AddRefed aListener, + TrackID aTrackID) override; + void RemoveDirectTrackListenerImpl(MediaStreamTrackDirectListener* aListener, + TrackID aTrackID) override; + void AddTrackInternal(TrackID aID, TrackRate aRate, StreamTime aStart, MediaSegment* aSegment, uint32_t aFlags); @@ -944,6 +1156,7 @@ protected: nsTArray mUpdateTracks; nsTArray mPendingTracks; nsTArray > mDirectListeners; + nsTArray> mDirectTrackListeners; bool mPullEnabled; bool mUpdateFinished; bool mNeedsMixing; @@ -959,6 +1172,14 @@ protected: * only this track will be forwarded to the destination stream. TRACK_ANY * can used to signal that all tracks shall be forwarded. * + * When a port is locked to a specific track in the source stream, it may also + * indicate a TrackID to map this source track to in the destination stream + * by setting aDestTrack to an explicit ID. When we do this, we must know + * that this TrackID in the destination stream is available. We assert during + * processing that the ID is available and that there are no generic input + * ports already attached to the destination stream. + * Note that this is currently only handled by TrackUnionStreams. + * * When a port's source or destination stream dies, the stream's DestroyImpl * calls MediaInputPort::Disconnect to disconnect the port from * the source and destination streams. @@ -975,11 +1196,12 @@ class MediaInputPort final private: // Do not call this constructor directly. Instead call aDest->AllocateInputPort. MediaInputPort(MediaStream* aSource, TrackID& aSourceTrack, - ProcessedMediaStream* aDest, + ProcessedMediaStream* aDest, TrackID& aDestTrack, uint16_t aInputNumber, uint16_t aOutputNumber) : mSource(aSource) , mSourceTrack(aSourceTrack) , mDest(aDest) + , mDestTrack(aDestTrack) , mInputNumber(aInputNumber) , mOutputNumber(aOutputNumber) , mGraph(nullptr) @@ -1013,11 +1235,17 @@ public: MediaStream* GetSource() { return mSource; } TrackID GetSourceTrackId() { return mSourceTrack; } ProcessedMediaStream* GetDestination() { return mDest; } + TrackID GetDestinationTrackId() { return mDestTrack; } - // Block aTrackId in the port. Consumers will interpret this track as ended. - void BlockTrackId(TrackID aTrackId); + /** + * Block aTrackId in the source stream from being passed through the port. + * Consumers will interpret this track as ended. + * Returns a pledge that resolves on the main thread after the track block has + * been applied by the MSG. + */ + already_AddRefed> BlockSourceTrackId(TrackID aTrackId); private: - void BlockTrackIdImpl(TrackID aTrackId); + void BlockSourceTrackIdImpl(TrackID aTrackId); public: // Returns true if aTrackId has not been blocked and this port has not @@ -1074,6 +1302,7 @@ private: MediaStream* mSource; TrackID mSourceTrack; ProcessedMediaStream* mDest; + TrackID mDestTrack; // The input and output numbers are optional, and are currently only used by // Web Audio. const uint16_t mInputNumber; @@ -1100,15 +1329,31 @@ public: /** * Allocates a new input port attached to source aStream. * This stream can be removed by calling MediaInputPort::Remove(). + * * The input port is tied to aTrackID in the source stream. * aTrackID can be set to TRACK_ANY to automatically forward all tracks from - * aStream. To end a track in the destination stream forwarded with TRACK_ANY, + * aStream. + * + * If aTrackID is an explicit ID, aDestTrackID can also be made explicit + * to ensure that the track is assigned this ID in the destination stream. + * To avoid intermittent TrackID collisions the destination stream may not + * have any existing generic input ports (with TRACK_ANY source track) when + * you allocate an input port with a destination TrackID. + * + * To end a track in the destination stream forwarded with TRACK_ANY, * it can be blocked in the input port through MediaInputPort::BlockTrackId(). + * + * Tracks in aBlockedTracks will be blocked in the input port initially. This + * ensures that they don't get created by the MSG-thread before we can + * BlockTrackId() on the main thread. */ - already_AddRefed AllocateInputPort(MediaStream* aStream, - TrackID aTrackID = TRACK_ANY, - uint16_t aInputNumber = 0, - uint16_t aOutputNumber = 0); + already_AddRefed + AllocateInputPort(MediaStream* aStream, + TrackID aTrackID = TRACK_ANY, + TrackID aDestTrackID = TRACK_ANY, + uint16_t aInputNumber = 0, + uint16_t aOutputNumber = 0, + nsTArray* aBlockedTracks = nullptr); /** * Force this stream into the finished state. */ diff --git a/dom/media/MediaStreamGraphImpl.h b/dom/media/MediaStreamGraphImpl.h index 5076daddcd..98e4006970 100644 --- a/dom/media/MediaStreamGraphImpl.h +++ b/dom/media/MediaStreamGraphImpl.h @@ -261,6 +261,21 @@ public: * Advance all stream state to mStateComputedTime. */ void UpdateCurrentTimeForStreams(GraphTime aPrevCurrentTime); + /** + * Process chunks for all streams and raise events for properties that have + * changed, such as principalId. + */ + void ProcessChunkMetadata(GraphTime aPrevCurrentTime); + /** + * Process chunks for the given stream and interval, and raise events for + * properties that have changed, such as principalId. + */ + template + void ProcessChunkMetadataForInterval(MediaStream* aStream, + TrackID aTrackID, + C& aSegment, + StreamTime aStart, + StreamTime aEnd); /** * Process graph messages in mFrontMessageQueue. */ diff --git a/dom/media/MediaStreamTrack.cpp b/dom/media/MediaStreamTrack.cpp index 81f8b294e4..c922c19f62 100644 --- a/dom/media/MediaStreamTrack.cpp +++ b/dom/media/MediaStreamTrack.cpp @@ -6,15 +6,116 @@ #include "MediaStreamTrack.h" #include "DOMMediaStream.h" +#include "MediaStreamGraph.h" #include "nsIUUIDGenerator.h" #include "nsServiceManagerUtils.h" +#ifdef LOG +#undef LOG +#endif + +static PRLogModuleInfo* gMediaStreamTrackLog; +#define LOG(type, msg) MOZ_LOG(gMediaStreamTrackLog, type, msg) + namespace mozilla { namespace dom { -MediaStreamTrack::MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID, const nsString& aLabel) - : mOwningStream(aStream), mTrackID(aTrackID), mLabel(aLabel), mEnded(false), mEnabled(true) +NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaStreamTrackSource) +NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaStreamTrackSource) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaStreamTrackSource) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END +NS_IMPL_CYCLE_COLLECTION(MediaStreamTrackSource, mPrincipal) + +already_AddRefed +MediaStreamTrackSource::ApplyConstraints(nsPIDOMWindow* aWindow, + const dom::MediaTrackConstraints& aConstraints, + ErrorResult &aRv) { + nsCOMPtr go = do_QueryInterface(aWindow); + RefPtr promise = Promise::Create(go, aRv); + MOZ_RELEASE_ASSERT(!aRv.Failed()); + + promise->MaybeReject(new MediaStreamError( + aWindow, + NS_LITERAL_STRING("OverconstrainedError"), + NS_LITERAL_STRING(""), + NS_LITERAL_STRING(""))); + return promise.forget(); +} + +/** + * PrincipalHandleListener monitors changes in PrincipalHandle of the media flowing + * through the MediaStreamGraph. + * + * When the main thread principal for a MediaStreamTrack changes, its principal + * will be set to the combination of the previous principal and the new one. + * + * As a PrincipalHandle change later happens on the MediaStreamGraph thread, we will + * be notified. If the latest principal on main thread matches the PrincipalHandle + * we just saw on MSG thread, we will set the track's principal to the new one. + * + * We know at this point that the old principal has been flushed out and data + * under it cannot leak to consumers. + * + * In case of multiple changes to the main thread state, the track's principal + * will be a combination of its old principal and all the new ones until the + * latest main thread principal matches the PrincipalHandle on the MSG thread. + */ +class MediaStreamTrack::PrincipalHandleListener : public MediaStreamTrackListener +{ +public: + explicit PrincipalHandleListener(MediaStreamTrack* aTrack) + : mTrack(aTrack) {} + + void Forget() + { + MOZ_ASSERT(NS_IsMainThread()); + mTrack = nullptr; + } + + void DoNotifyPrincipalHandleChanged(const PrincipalHandle& aNewPrincipalHandle) + { + MOZ_ASSERT(NS_IsMainThread()); + + if (!mTrack) { + return; + } + + mTrack->NotifyPrincipalHandleChanged(aNewPrincipalHandle); + } + + void NotifyPrincipalHandleChanged(MediaStreamGraph* aGraph, + const PrincipalHandle& aNewPrincipalHandle) override + { + nsCOMPtr runnable = + NS_NewRunnableMethodWithArgs>( + this, &PrincipalHandleListener::DoNotifyPrincipalHandleChanged, aNewPrincipalHandle); + aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget()); + } + +protected: + // These fields may only be accessed on the main thread + MediaStreamTrack* mTrack; +}; + +MediaStreamTrack::MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID, + TrackID aInputTrackID, + MediaStreamTrackSource* aSource) + : mOwningStream(aStream), mTrackID(aTrackID), + mInputTrackID(aInputTrackID), mSource(aSource), + mPrincipal(aSource->GetPrincipal()), + mEnded(false), mEnabled(true), mRemote(aSource->IsRemote()), mStopped(false) +{ + + if (!gMediaStreamTrackLog) { + gMediaStreamTrackLog = PR_NewLogModule("MediaStreamTrack"); + } + + GetSource().RegisterSink(this); + + mPrincipalHandleListener = new PrincipalHandleListener(this); + AddListener(mPrincipalHandleListener); nsresult rv; nsCOMPtr uuidgen = @@ -33,16 +134,57 @@ MediaStreamTrack::MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID, co MediaStreamTrack::~MediaStreamTrack() { + Destroy(); } -NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaStreamTrack, DOMEventTargetHelper, - mOwningStream) +void +MediaStreamTrack::Destroy() +{ + if (mSource) { + mSource->UnregisterSink(this); + } + if (mPrincipalHandleListener) { + if (GetOwnedStream()) { + RemoveListener(mPrincipalHandleListener); + } + mPrincipalHandleListener->Forget(); + mPrincipalHandleListener = nullptr; + } +} + +NS_IMPL_CYCLE_COLLECTION_CLASS(MediaStreamTrack) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MediaStreamTrack, + DOMEventTargetHelper) + tmp->Destroy(); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwningStream) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mSource) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mOriginalTrack) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrincipal) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingPrincipal) +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MediaStreamTrack, + DOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwningStream) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSource) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginalTrack) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrincipal) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingPrincipal) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_ADDREF_INHERITED(MediaStreamTrack, DOMEventTargetHelper) NS_IMPL_RELEASE_INHERITED(MediaStreamTrack, DOMEventTargetHelper) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaStreamTrack) NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) +nsPIDOMWindow* +MediaStreamTrack::GetParentObject() const +{ + MOZ_RELEASE_ASSERT(mOwningStream); + return mOwningStream->GetParentObject(); +} + void MediaStreamTrack::GetId(nsAString& aID) const { @@ -52,21 +194,226 @@ MediaStreamTrack::GetId(nsAString& aID) const void MediaStreamTrack::SetEnabled(bool aEnabled) { + LOG(LogLevel::Info, ("MediaStreamTrack %p %s", + this, aEnabled ? "Enabled" : "Disabled")); + mEnabled = aEnabled; - mOwningStream->SetTrackEnabled(mTrackID, aEnabled); + GetOwnedStream()->SetTrackEnabled(mTrackID, aEnabled); } void MediaStreamTrack::Stop() { - mOwningStream->StopTrack(mTrackID); + LOG(LogLevel::Info, ("MediaStreamTrack %p Stop()", this)); + + if (mStopped) { + LOG(LogLevel::Warning, ("MediaStreamTrack %p Already stopped", this)); + return; + } + + if (mRemote) { + LOG(LogLevel::Warning, ("MediaStreamTrack %p is remote. Can't be stopped.", this)); + return; + } + + if (!mSource) { + MOZ_ASSERT(false); + return; + } + + mSource->UnregisterSink(this); + mStopped = true; } already_AddRefed MediaStreamTrack::ApplyConstraints(const MediaTrackConstraints& aConstraints, ErrorResult &aRv) { - return GetStream()->ApplyConstraintsToTrack(mTrackID, aConstraints, aRv); + if (MOZ_LOG_TEST(gMediaStreamTrackLog, LogLevel::Info)) { + nsString str; + aConstraints.ToJSON(str); + + LOG(LogLevel::Info, ("MediaStreamTrack %p ApplyConstraints() with " + "constraints %s", this, NS_ConvertUTF16toUTF8(str).get())); + } + + nsPIDOMWindow* window = mOwningStream->GetParentObject(); + return GetSource().ApplyConstraints(window, aConstraints, aRv); +} + +MediaStreamGraph* +MediaStreamTrack::Graph() +{ + return GetOwnedStream()->Graph(); +} + +MediaStreamGraphImpl* +MediaStreamTrack::GraphImpl() +{ + return GetOwnedStream()->GraphImpl(); +} + +void +MediaStreamTrack::SetPrincipal(nsIPrincipal* aPrincipal) +{ + if (aPrincipal == mPrincipal) { + return; + } + mPrincipal = aPrincipal; + + LOG(LogLevel::Info, ("MediaStreamTrack %p principal changed to %p. Now: " + "null=%d, codebase=%d, expanded=%d, system=%d", + this, mPrincipal.get(), + mPrincipal->GetIsNullPrincipal(), + mPrincipal->GetIsCodebasePrincipal(), + mPrincipal->GetIsExpandedPrincipal(), + mPrincipal->GetIsSystemPrincipal())); + for (PrincipalChangeObserver* observer + : mPrincipalChangeObservers) { + observer->PrincipalChanged(this); + } +} + +void +MediaStreamTrack::PrincipalChanged() +{ + mPendingPrincipal = GetSource().GetPrincipal(); + nsCOMPtr newPrincipal = mPrincipal; + LOG(LogLevel::Info, ("MediaStreamTrack %p Principal changed on main thread " + "to %p (pending). Combining with existing principal %p.", + this, mPendingPrincipal.get(), mPrincipal.get())); + if (nsContentUtils::CombineResourcePrincipals(&newPrincipal, + mPendingPrincipal)) { + SetPrincipal(newPrincipal); + } +} + +void +MediaStreamTrack::NotifyPrincipalHandleChanged(const PrincipalHandle& aNewPrincipalHandle) +{ + PrincipalHandle handle(aNewPrincipalHandle); + LOG(LogLevel::Info, ("MediaStreamTrack %p principalHandle changed on " + "MediaStreamGraph thread to %p. Current principal: %p, " + "pending: %p", + this, GetPrincipalFromHandle(handle), + mPrincipal.get(), mPendingPrincipal.get())); + if (PrincipalHandleMatches(handle, mPendingPrincipal)) { + SetPrincipal(mPendingPrincipal); + mPendingPrincipal = nullptr; + } +} + +bool +MediaStreamTrack::AddPrincipalChangeObserver( + PrincipalChangeObserver* aObserver) +{ + return mPrincipalChangeObservers.AppendElement(aObserver) != nullptr; +} + +bool +MediaStreamTrack::RemovePrincipalChangeObserver( + PrincipalChangeObserver* aObserver) +{ + return mPrincipalChangeObservers.RemoveElement(aObserver); +} + +already_AddRefed +MediaStreamTrack::Clone() +{ + // MediaStreamTracks are currently governed by streams, so we need a dummy + // DOMMediaStream to own our track clone. The dummy will never see any + // dynamically created tracks (no input stream) so no need for a SourceGetter. + RefPtr newStream = + new DOMMediaStream(mOwningStream->GetParentObject(), nullptr); + + MediaStreamGraph* graph = Graph(); + newStream->InitOwnedStreamCommon(graph); + newStream->InitPlaybackStreamCommon(graph); + + return newStream->CloneDOMTrack(*this, mTrackID); +} + +DOMMediaStream* +MediaStreamTrack::GetInputDOMStream() +{ + MediaStreamTrack* originalTrack = + mOriginalTrack ? mOriginalTrack.get() : this; + MOZ_RELEASE_ASSERT(originalTrack->mOwningStream); + return originalTrack->mOwningStream; +} + +MediaStream* +MediaStreamTrack::GetInputStream() +{ + DOMMediaStream* inputDOMStream = GetInputDOMStream(); + MOZ_RELEASE_ASSERT(inputDOMStream->GetInputStream()); + return inputDOMStream->GetInputStream(); +} + +ProcessedMediaStream* +MediaStreamTrack::GetOwnedStream() +{ + return mOwningStream->GetOwnedStream(); +} + +void +MediaStreamTrack::AddListener(MediaStreamTrackListener* aListener) +{ + LOG(LogLevel::Debug, ("MediaStreamTrack %p adding listener %p", + this, aListener)); + + GetOwnedStream()->AddTrackListener(aListener, mTrackID); +} + +void +MediaStreamTrack::RemoveListener(MediaStreamTrackListener* aListener) +{ + LOG(LogLevel::Debug, ("MediaStreamTrack %p removing listener %p", + this, aListener)); + + GetOwnedStream()->RemoveTrackListener(aListener, mTrackID); +} + +void +MediaStreamTrack::AddDirectListener(MediaStreamTrackDirectListener *aListener) +{ + LOG(LogLevel::Debug, ("MediaStreamTrack %p (%s) adding direct listener %p to " + "stream %p, track %d", + this, AsAudioStreamTrack() ? "audio" : "video", + aListener, GetOwnedStream(), mTrackID)); + + GetOwnedStream()->AddDirectTrackListener(aListener, mTrackID); +} + +void +MediaStreamTrack::RemoveDirectListener(MediaStreamTrackDirectListener *aListener) +{ + LOG(LogLevel::Debug, ("MediaStreamTrack %p removing direct listener %p from stream %p", + this, aListener, GetOwnedStream())); + + GetOwnedStream()->RemoveDirectTrackListener(aListener, mTrackID); +} + +already_AddRefed +MediaStreamTrack::ForwardTrackContentsTo(ProcessedMediaStream* aStream) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_RELEASE_ASSERT(aStream); + RefPtr port = + aStream->AllocateInputPort(GetOwnedStream(), mTrackID); + return port.forget(); +} + +bool +MediaStreamTrack::IsForwardedThrough(MediaInputPort* aPort) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aPort); + if (!aPort) { + return false; + } + return aPort->GetSource() == GetOwnedStream() && + aPort->PassTrackThrough(mTrackID); } } // namespace dom diff --git a/dom/media/MediaStreamTrack.h b/dom/media/MediaStreamTrack.h index 8b7465c081..01da71cae6 100644 --- a/dom/media/MediaStreamTrack.h +++ b/dom/media/MediaStreamTrack.h @@ -7,77 +7,392 @@ #define MEDIASTREAMTRACK_H_ #include "mozilla/DOMEventTargetHelper.h" +#include "nsError.h" #include "nsID.h" +#include "nsIPrincipal.h" #include "StreamBuffer.h" #include "MediaTrackConstraints.h" +#include "mozilla/CORSMode.h" +#include "PrincipalChangeObserver.h" namespace mozilla { class DOMMediaStream; +class MediaEnginePhotoCallback; +class MediaInputPort; +class MediaStream; +class MediaStreamGraph; +class MediaStreamGraphImpl; +class MediaStreamTrackListener; +class MediaStreamTrackDirectListener; +class PeerConnectionImpl; +class PeerConnectionMedia; +class PeerIdentity; +class ProcessedMediaStream; +class RemoteSourceStreamInfo; namespace dom { class AudioStreamTrack; class VideoStreamTrack; +/** + * Common interface through which a MediaStreamTrack can communicate with its + * producer on the main thread. + * + * Kept alive by a strong ref in all MediaStreamTracks (original and clones) + * sharing this source. + */ +class MediaStreamTrackSource : public nsISupports +{ + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS(MediaStreamTrackSource) + +public: + class Sink + { + public: + virtual void PrincipalChanged() = 0; + }; + + MediaStreamTrackSource(nsIPrincipal* aPrincipal, + const bool aIsRemote, + const nsString& aLabel) + : mPrincipal(aPrincipal), + mIsRemote(aIsRemote), + mLabel(aLabel), + mStopped(false) + { + MOZ_COUNT_CTOR(MediaStreamTrackSource); + } + + /** + * Gets the source's MediaSourceEnum for usage by PeerConnections. + */ + virtual MediaSourceEnum GetMediaSource() const = 0; + + /** + * Get this TrackSource's principal. + */ + nsIPrincipal* GetPrincipal() const { return mPrincipal; } + + /** + * Get the source's current CORSMode. If not applicable CORS_NONE is returned. + * The sink will be notified of changes to our CORSMode through + * PrincipalChanged(). + */ + virtual CORSMode GetCORSMode() const { return CORS_NONE; } + + /** + * This is used in WebRTC. A peerIdentity constrained MediaStreamTrack cannot + * be sent across the network to anything other than a peer with the provided + * identity. If this is set, then GetPrincipal() should return an instance of + * nsNullPrincipal. + * + * A track's PeerIdentity is immutable and will not change during the track's + * lifetime. + */ + virtual const PeerIdentity* GetPeerIdentity() const { return nullptr; } + + /** + * Indicates whether the track is remote or not per the MediaCapture and + * Streams spec. + */ + virtual bool IsRemote() const { return mIsRemote; } + + /** + * MediaStreamTrack::GetLabel (see spec) calls through to here. + */ + void GetLabel(nsAString& aLabel) { aLabel.Assign(mLabel); } + + /** + * Forwards a photo request to backends that support it. Other backends return + * NS_ERROR_NOT_IMPLEMENTED to indicate that a MediaStreamGraph-based fallback + * should be used. + */ + virtual nsresult TakePhoto(MediaEnginePhotoCallback*) const { return NS_ERROR_NOT_IMPLEMENTED; } + + /** + * We provide a fallback solution to ApplyConstraints() here. + * Sources that support ApplyConstraints() will have to override it. + */ + virtual already_AddRefed + ApplyConstraints(nsPIDOMWindow* aWindow, + const dom::MediaTrackConstraints& aConstraints, + ErrorResult &aRv); + + /** + * Called by the source interface when all registered sinks have unregistered. + */ + virtual void Stop() = 0; + + /** + * Called by each MediaStreamTrack clone on initialization. + */ + void RegisterSink(Sink* aSink) + { + MOZ_ASSERT(NS_IsMainThread()); + if (mStopped) { + return; + } + mSinks.AppendElement(aSink); + } + + /** + * Called by each MediaStreamTrack clone on Stop() if supported by the + * source (us) or destruction. + */ + void UnregisterSink(Sink* aSink) + { + MOZ_ASSERT(NS_IsMainThread()); + if (mSinks.RemoveElement(aSink) && mSinks.IsEmpty() && !IsRemote()) { + Stop(); + mStopped = true; + } + } + +protected: + virtual ~MediaStreamTrackSource() + { + MOZ_COUNT_DTOR(MediaStreamTrackSource); + } + + /** + * Called by a sub class when the principal has changed. + * Notifies all sinks. + */ + void PrincipalChanged() + { + for (Sink* sink : mSinks) { + sink->PrincipalChanged(); + } + } + + // Principal identifying who may access the contents of this source. + nsCOMPtr mPrincipal; + + // Currently registered sinks. + nsTArray mSinks; + + // True if this is a remote track source, i.e., a PeerConnection. + const bool mIsRemote; + + // The label of the track we are the source of per the MediaStreamTrack spec. + const nsString mLabel; + + // True if this source is not remote, all MediaStreamTrack users have + // unregistered from this source and Stop() has been called. + bool mStopped; +}; + +/** + * Basic implementation of MediaStreamTrackSource that ignores Stop(). + */ +class BasicUnstoppableTrackSource : public MediaStreamTrackSource +{ +public: + explicit BasicUnstoppableTrackSource(nsIPrincipal* aPrincipal, + const MediaSourceEnum aMediaSource = + MediaSourceEnum::Other) + : MediaStreamTrackSource(aPrincipal, true, nsString()) + , mMediaSource(aMediaSource) + {} + + MediaSourceEnum GetMediaSource() const override { return mMediaSource; } + + void Stop() override {} + +protected: + ~BasicUnstoppableTrackSource() {} + + const MediaSourceEnum mMediaSource; +}; + /** * Class representing a track in a DOMMediaStream. */ -class MediaStreamTrack : public DOMEventTargetHelper { +class MediaStreamTrack : public DOMEventTargetHelper, + public MediaStreamTrackSource::Sink +{ + // DOMMediaStream owns MediaStreamTrack instances, and requires access to + // some internal state, e.g., GetInputStream(), GetOwnedStream(). + friend class mozilla::DOMMediaStream; + + // PeerConnection and friends need to know our owning DOMStream and track id. + friend class mozilla::PeerConnectionImpl; + friend class mozilla::PeerConnectionMedia; + friend class mozilla::RemoteSourceStreamInfo; + + class PrincipalHandleListener; + public: /** * aTrackID is the MediaStreamGraph track ID for the track in the * MediaStream owned by aStream. */ - MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID, const nsString& aLabel); + MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID, + TrackID aInputTrackID, + MediaStreamTrackSource* aSource); NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaStreamTrack, DOMEventTargetHelper) - DOMMediaStream* GetParentObject() const { return mOwningStream; } + nsPIDOMWindow* GetParentObject() const; virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override = 0; - /** - * Returns the DOMMediaStream owning this track. - */ - DOMMediaStream* GetStream() const { return mOwningStream; } - - /** - * Returns the TrackID this stream has in its owning DOMMediaStream's Owned - * stream. - */ - TrackID GetTrackID() const { return mTrackID; } virtual AudioStreamTrack* AsAudioStreamTrack() { return nullptr; } virtual VideoStreamTrack* AsVideoStreamTrack() { return nullptr; } + virtual const AudioStreamTrack* AsAudioStreamTrack() const { return nullptr; } + virtual const VideoStreamTrack* AsVideoStreamTrack() const { return nullptr; } + // WebIDL virtual void GetKind(nsAString& aKind) = 0; void GetId(nsAString& aID) const; - void GetLabel(nsAString& aLabel) { aLabel.Assign(mLabel); } + void GetLabel(nsAString& aLabel) { GetSource().GetLabel(aLabel); } bool Enabled() { return mEnabled; } void SetEnabled(bool aEnabled); void Stop(); already_AddRefed ApplyConstraints(const dom::MediaTrackConstraints& aConstraints, ErrorResult &aRv); + already_AddRefed Clone(); bool Ended() const { return mEnded; } // Notifications from the MediaStreamGraph void NotifyEnded() { mEnded = true; } + /** + * Get this track's principal. + */ + nsIPrincipal* GetPrincipal() const { return mPrincipal; } + + /** + * Called by the PrincipalHandleListener when this track's PrincipalHandle changes on + * the MediaStreamGraph thread. When the PrincipalHandle matches the pending + * principal we know that the principal change has propagated to consumers. + */ + void NotifyPrincipalHandleChanged(const PrincipalHandle& aPrincipalHandle); + + /** + * Get this track's CORS mode. + */ + CORSMode GetCORSMode() const { return GetSource().GetCORSMode(); } + + /** + * Get this track's PeerIdentity. + */ + const PeerIdentity* GetPeerIdentity() const { return GetSource().GetPeerIdentity(); } + + MediaStreamGraph* Graph(); + MediaStreamGraphImpl* GraphImpl(); + + MediaStreamTrackSource& GetSource() const + { + MOZ_RELEASE_ASSERT(mSource, "The track source is only removed on destruction"); + return *mSource; + } + // Webrtc allows the remote side to name tracks whatever it wants, and we // need to surface this to content. void AssignId(const nsAString& aID) { mID = aID; } + // Implementation of MediaStreamTrackSource::Sink + void PrincipalChanged() override; + + /** + * Add a PrincipalChangeObserver to this track. + * + * Returns true if it was successfully added. + * + * Ownership of the PrincipalChangeObserver remains with the caller, and it's + * the caller's responsibility to remove the observer before it dies. + */ + bool AddPrincipalChangeObserver(PrincipalChangeObserver* aObserver); + + /** + * Remove an added PrincipalChangeObserver from this track. + * + * Returns true if it was successfully removed. + */ + bool RemovePrincipalChangeObserver(PrincipalChangeObserver* aObserver); + + /** + * Adds a MediaStreamTrackListener to the MediaStreamGraph representation of + * this track. + */ + void AddListener(MediaStreamTrackListener* aListener); + + /** + * Removes a MediaStreamTrackListener from the MediaStreamGraph representation + * of this track. + */ + void RemoveListener(MediaStreamTrackListener* aListener); + + /** + * Attempts to add a direct track listener to this track. + * Callers must listen to the NotifyInstalled event to know if installing + * the listener succeeded (tracks originating from SourceMediaStreams) or + * failed (e.g., WebAudio originated tracks). + */ + void AddDirectListener(MediaStreamTrackDirectListener *aListener); + void RemoveDirectListener(MediaStreamTrackDirectListener *aListener); + + /** + * Sets up a MediaInputPort from the underlying track that this + * MediaStreamTrack represents, to aStream, and returns it. + */ + already_AddRefed ForwardTrackContentsTo(ProcessedMediaStream* aStream); + + /** + * Returns true if this track is connected to aPort and forwarded to aPort's + * output stream. + */ + bool IsForwardedThrough(MediaInputPort* aPort); + protected: virtual ~MediaStreamTrack(); + void Destroy(); + + // Returns the original DOMMediaStream's underlying input stream. + MediaStream* GetInputStream(); + + // Returns the owning DOMMediaStream's underlying owned stream. + ProcessedMediaStream* GetOwnedStream(); + + // Returns the original DOMMediaStream. If this track is a clone, + // the original track's owning DOMMediaStream is returned. + DOMMediaStream* GetInputDOMStream(); + + /** + * Sets the principal and notifies PrincipalChangeObservers if it changes. + */ + void SetPrincipal(nsIPrincipal* aPrincipal); + + /** + * Creates a new MediaStreamTrack with the same type, input track ID and + * source as this MediaStreamTrack. + * aTrackID is the TrackID the new track will have in its owned stream. + */ + virtual already_AddRefed CloneInternal(DOMMediaStream* aOwningStream, + TrackID aTrackID) = 0; + + nsTArray*> mPrincipalChangeObservers; + RefPtr mOwningStream; TrackID mTrackID; + TrackID mInputTrackID; + RefPtr mSource; + RefPtr mOriginalTrack; + nsCOMPtr mPrincipal; + nsCOMPtr mPendingPrincipal; + RefPtr mPrincipalHandleListener; nsString mID; - nsString mLabel; bool mEnded; bool mEnabled; + const bool mRemote; + bool mStopped; }; } // namespace dom diff --git a/dom/media/PeerConnection.js b/dom/media/PeerConnection.js index be4d072093..673410af6b 100644 --- a/dom/media/PeerConnection.js +++ b/dom/media/PeerConnection.js @@ -1060,7 +1060,13 @@ RTCPeerConnection.prototype = { "InvalidParameterError"); } }); - this._impl.addTrack(track, stream); + try { + this._impl.addTrack(track, stream); + } catch (e if (e.result == Cr.NS_ERROR_NOT_IMPLEMENTED)) { + throw new this._win.DOMException( + "track in constructed stream not yet supported (see Bug 1259236).", + "NotSupportedError"); + } let sender = this._win.RTCRtpSender._create(this._win, new RTCRtpSender(this, track, stream)); diff --git a/dom/media/PrincipalChangeObserver.h b/dom/media/PrincipalChangeObserver.h new file mode 100644 index 0000000000..2597813c52 --- /dev/null +++ b/dom/media/PrincipalChangeObserver.h @@ -0,0 +1,30 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_PRINCIPALCHANGEOBSERVER_H_ +#define MOZILLA_PRINCIPALCHANGEOBSERVER_H_ + +namespace mozilla { +namespace dom { + +/** + * A PrincipalChangeObserver for any type, but originating from DOMMediaStream, + * then expanded to MediaStreamTrack. + * + * Used to learn about dynamic changes to an object's principal. + * Operations relating to these observers must be confined to the main thread. + */ +template +class PrincipalChangeObserver +{ +public: + virtual void PrincipalChanged(T* aArg) = 0; +}; + +} // namespace dom +} // namespace mozilla + +#endif /* MOZILLA_PRINCIPALCHANGEOBSERVER_H_ */ diff --git a/dom/media/StreamBuffer.h b/dom/media/StreamBuffer.h index 3112aa6332..a0f0e83215 100644 --- a/dom/media/StreamBuffer.h +++ b/dom/media/StreamBuffer.h @@ -21,6 +21,10 @@ const TrackID TRACK_NONE = 0; const TrackID TRACK_INVALID = -1; const TrackID TRACK_ANY = -2; +inline bool IsTrackIDExplicit(const TrackID& aId) { + return aId > TRACK_NONE; +} + inline TrackTicks RateConvertTicksRoundDown(TrackRate aOutRate, TrackRate aInRate, TrackTicks aTicks) diff --git a/dom/media/TrackUnionStream.cpp b/dom/media/TrackUnionStream.cpp index 06ff80b513..464174f0ab 100644 --- a/dom/media/TrackUnionStream.cpp +++ b/dom/media/TrackUnionStream.cpp @@ -149,13 +149,35 @@ TrackUnionStream::TrackUnionStream(DOMMediaStream* aWrapper) : uint32_t TrackUnionStream::AddTrack(MediaInputPort* aPort, StreamBuffer::Track* aTrack, GraphTime aFrom) { - TrackID id = aTrack->GetID(); - if (id > mNextAvailableTrackID && - mUsedTracks.BinaryIndexOf(id) == mUsedTracks.NoIndex) { + STREAM_LOG(LogLevel::Verbose, ("TrackUnionStream %p adding track %d for " + "input stream %p track %d, desired id %d", + this, aTrack->GetID(), aPort->GetSource(), + aTrack->GetID(), + aPort->GetDestinationTrackId())); + + TrackID id; + if (IsTrackIDExplicit(id = aPort->GetDestinationTrackId())) { + MOZ_ASSERT(id >= mNextAvailableTrackID && + mUsedTracks.BinaryIndexOf(id) == mUsedTracks.NoIndex, + "Desired destination id taken. Only provide a destination ID " + "if you can assure its availability, or we may not be able " + "to bind to the correct DOM-side track."); +#ifdef DEBUG + for (size_t i = 0; mInputs[i] != aPort; ++i) { + MOZ_ASSERT(mInputs[i]->GetSourceTrackId() != TRACK_ANY, + "You are adding a MediaInputPort with a track mapping " + "while there already exist generic MediaInputPorts for this " + "destination stream. This can lead to TrackID collisions!"); + } +#endif + mUsedTracks.InsertElementSorted(id); + } else if ((id = aTrack->GetID()) && + id > mNextAvailableTrackID && + mUsedTracks.BinaryIndexOf(id) == mUsedTracks.NoIndex) { // Input id available. Mark it used in mUsedTracks. mUsedTracks.InsertElementSorted(id); } else { - // Input id taken, allocate a new one. + // No desired destination id and Input id taken, allocate a new one. id = mNextAvailableTrackID; // Update mNextAvailableTrackID and prune any mUsedTracks members it now @@ -185,9 +207,9 @@ TrackUnionStream::TrackUnionStream(DOMMediaStream* aWrapper) : segment->AppendNullData(outputStart); StreamBuffer::Track* track = &mBuffer.AddTrack(id, outputStart, segment.forget()); - STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p adding track %d for input stream %p track %d, start ticks %lld", - this, id, aPort->GetSource(), aTrack->GetID(), - (long long)outputStart)); + STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p added track %d for input stream %p track %d, start ticks %lld", + this, track->GetID(), aPort->GetSource(), aTrack->GetID(), + (long long)outputStart)); TrackMapEntry* map = mTrackMap.AppendElement(); map->mEndOfConsumedInputTicks = 0; @@ -197,6 +219,28 @@ TrackUnionStream::TrackUnionStream(DOMMediaStream* aWrapper) : map->mInputTrackID = aTrack->GetID(); map->mOutputTrackID = track->GetID(); map->mSegment = aTrack->GetSegment()->CreateEmptyClone(); + + for (int32_t i = mPendingDirectTrackListeners.Length() - 1; i >= 0; --i) { + TrackBound& bound = + mPendingDirectTrackListeners[i]; + if (bound.mTrackID != map->mOutputTrackID) { + continue; + } + MediaStream* source = map->mInputPort->GetSource(); + map->mOwnedDirectListeners.AppendElement(bound.mListener); + if (mDisabledTrackIDs.Contains(bound.mTrackID)) { + bound.mListener->IncreaseDisabled(); + } + STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p adding direct listener " + "%p for track %d. Forwarding to input " + "stream %p track %d.", + this, bound.mListener.get(), bound.mTrackID, + source, map->mInputTrackID)); + source->AddDirectTrackListenerImpl(bound.mListener.forget(), + map->mInputTrackID); + mPendingDirectTrackListeners.RemoveElementAt(i); + } + return mTrackMap.Length() - 1; } @@ -217,6 +261,11 @@ TrackUnionStream::TrackUnionStream(DOMMediaStream* aWrapper) : mTrackMap[aIndex].mInputPort->GetSource(), mTrackMap[aIndex].mInputTrackID); } + for (TrackBound& b : mTrackListeners) { + if (b.mTrackID == outputTrack->GetID()) { + b.mListener->NotifyEnded(); + } + } outputTrack->SetEnded(); } @@ -276,9 +325,113 @@ TrackUnionStream::TrackUnionStream(DOMMediaStream* aWrapper) : for (uint32_t j = 0; j < mListeners.Length(); ++j) { MediaStreamListener* l = mListeners[j]; l->NotifyQueuedTrackChanges(Graph(), outputTrack->GetID(), - outputStart, 0, *segment); + outputStart, 0, *segment, + map->mInputPort->GetSource(), + map->mInputTrackID); + } + for (TrackBound& b : mTrackListeners) { + if (b.mTrackID != outputTrack->GetID()) { + continue; + } + b.mListener->NotifyQueuedChanges(Graph(), outputStart, *segment); } outputTrack->GetSegment()->AppendFrom(segment); } } + +void +TrackUnionStream::SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled) { + for (TrackMapEntry& entry : mTrackMap) { + if (entry.mOutputTrackID == aTrackID) { + STREAM_LOG(LogLevel::Info, ("TrackUnionStream %p track %d was explicitly %s", + this, aTrackID, aEnabled ? "enabled" : "disabled")); + for (MediaStreamTrackDirectListener* listener : entry.mOwnedDirectListeners) { + bool oldEnabled = !mDisabledTrackIDs.Contains(aTrackID); + if (!oldEnabled && aEnabled) { + STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p track %d setting " + "direct listener enabled", + this, aTrackID)); + listener->DecreaseDisabled(); + } else if (oldEnabled && !aEnabled) { + STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p track %d setting " + "direct listener disabled", + this, aTrackID)); + listener->IncreaseDisabled(); + } + } + } + } + MediaStream::SetTrackEnabledImpl(aTrackID, aEnabled); +} + +void +TrackUnionStream::AddDirectTrackListenerImpl(already_AddRefed aListener, + TrackID aTrackID) +{ + RefPtr listener = aListener; + + for (TrackMapEntry& entry : mTrackMap) { + if (entry.mOutputTrackID == aTrackID) { + MediaStream* source = entry.mInputPort->GetSource(); + STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p adding direct listener " + "%p for track %d. Forwarding to input " + "stream %p track %d.", + this, listener.get(), aTrackID, source, + entry.mInputTrackID)); + entry.mOwnedDirectListeners.AppendElement(listener); + if (mDisabledTrackIDs.Contains(aTrackID)) { + listener->IncreaseDisabled(); + } + source->AddDirectTrackListenerImpl(listener.forget(), + entry.mInputTrackID); + return; + } + } + + TrackBound* bound = + mPendingDirectTrackListeners.AppendElement(); + bound->mListener = listener.forget(); + bound->mTrackID = aTrackID; +} + +void +TrackUnionStream::RemoveDirectTrackListenerImpl(MediaStreamTrackDirectListener* aListener, + TrackID aTrackID) +{ + for (TrackMapEntry& entry : mTrackMap) { + // OutputTrackID is unique to this stream so we only need to do this once. + if (entry.mOutputTrackID != aTrackID) { + continue; + } + for (size_t i = 0; i < entry.mOwnedDirectListeners.Length(); ++i) { + if (entry.mOwnedDirectListeners[i] == aListener) { + STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p removing direct " + "listener %p for track %d, forwarding " + "to input stream %p track %d", + this, aListener, aTrackID, + entry.mInputPort->GetSource(), + entry.mInputTrackID)); + if (mDisabledTrackIDs.Contains(aTrackID)) { + // Reset the listener's state. + aListener->DecreaseDisabled(); + } + entry.mOwnedDirectListeners.RemoveElementAt(i); + break; + } + } + // Forward to the input + MediaStream* source = entry.mInputPort->GetSource(); + source->RemoveDirectTrackListenerImpl(aListener, entry.mInputTrackID); + return; + } + + for (size_t i = 0; i < mPendingDirectTrackListeners.Length(); ++i) { + TrackBound& bound = + mPendingDirectTrackListeners[i]; + if (bound.mListener == aListener && bound.mTrackID == aTrackID) { + mPendingDirectTrackListeners.RemoveElementAt(i); + return; + } + } +} } // namespace mozilla diff --git a/dom/media/TrackUnionStream.h b/dom/media/TrackUnionStream.h index d7fb9efa32..a21686bd15 100644 --- a/dom/media/TrackUnionStream.h +++ b/dom/media/TrackUnionStream.h @@ -21,6 +21,8 @@ public: void RemoveInput(MediaInputPort* aPort) override; void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) override; + void SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled) override; + protected: // Only non-ended tracks are allowed to persist in this map. struct TrackMapEntry { @@ -44,6 +46,10 @@ protected: TrackID mInputTrackID; TrackID mOutputTrackID; nsAutoPtr mSegment; + // These are direct track listeners that have been added to this + // TrackUnionStream-track and forwarded to the input track. We will update + // these when this track's disabled status changes. + nsTArray> mOwnedDirectListeners; }; // Add the track to this stream, retaining its TrackID if it has never @@ -55,6 +61,11 @@ protected: uint32_t aMapIndex, GraphTime aFrom, GraphTime aTo, bool* aOutputTrackFinished); + void AddDirectTrackListenerImpl(already_AddRefed aListener, + TrackID aTrackID) override; + void RemoveDirectTrackListenerImpl(MediaStreamTrackDirectListener* aListener, + TrackID aTrackID) override; + nsTArray mTrackMap; // The next available TrackID, starting at 1 and progressing upwards. @@ -63,6 +74,10 @@ protected: // Sorted array of used TrackIDs that require manual tracking. nsTArray mUsedTracks; + + // Direct track listeners that have not been forwarded to their input stream + // yet. We'll forward these as their inputs become available. + nsTArray> mPendingDirectTrackListeners; }; } // namespace mozilla diff --git a/dom/media/VideoFrameContainer.cpp b/dom/media/VideoFrameContainer.cpp index e5a172e0ce..de4491b167 100644 --- a/dom/media/VideoFrameContainer.cpp +++ b/dom/media/VideoFrameContainer.cpp @@ -20,7 +20,8 @@ VideoFrameContainer::VideoFrameContainer(dom::HTMLMediaElement* aElement, : mElement(aElement), mImageContainer(aContainer), mMutex("nsVideoFrameContainer"), mFrameID(0), - mIntrinsicSizeChanged(false), mImageSizeChanged(false) + mIntrinsicSizeChanged(false), mImageSizeChanged(false), + mPendingPrincipalHandle(PRINCIPAL_HANDLE_NONE), mFrameIDForPendingPrincipalHandle(0) { NS_ASSERTION(aElement, "aElement must not be null"); NS_ASSERTION(mImageContainer, "aContainer must not be null"); @@ -29,6 +30,23 @@ VideoFrameContainer::VideoFrameContainer(dom::HTMLMediaElement* aElement, VideoFrameContainer::~VideoFrameContainer() {} +PrincipalHandle VideoFrameContainer::GetLastPrincipalHandle() +{ + MutexAutoLock lock(mMutex); + return mLastPrincipalHandle; +} + +void VideoFrameContainer::UpdatePrincipalHandleForFrameID(const PrincipalHandle& aPrincipalHandle, + const ImageContainer::FrameID& aFrameID) +{ + MutexAutoLock lock(mMutex); + if (mPendingPrincipalHandle == aPrincipalHandle) { + return; + } + mPendingPrincipalHandle = aPrincipalHandle; + mFrameIDForPendingPrincipalHandle = aFrameID; +} + void VideoFrameContainer::SetCurrentFrame(const gfx::IntSize& aIntrinsicSize, Image* aImage, const TimeStamp& aTargetTime) @@ -69,8 +87,33 @@ void VideoFrameContainer::SetCurrentFramesLocked(const gfx::IntSize& aIntrinsicS // composite it can then block on |mImageContainer|'s lock, causing a // deadlock. We use this hack to defer the destruction of the current image // until it is safe. - nsTArray kungFuDeathGrip; - mImageContainer->GetCurrentImages(&kungFuDeathGrip); + nsTArray oldImages; + mImageContainer->GetCurrentImages(&oldImages); + + ImageContainer::FrameID lastFrameIDForOldPrincipalHandle = + mFrameIDForPendingPrincipalHandle - 1; + if (mPendingPrincipalHandle != PRINCIPAL_HANDLE_NONE && + ((!oldImages.IsEmpty() && + oldImages.LastElement().mFrameID >= lastFrameIDForOldPrincipalHandle) || + (!aImages.IsEmpty() && + aImages[0].mFrameID > lastFrameIDForOldPrincipalHandle))) { + // We are releasing the last FrameID prior to `lastFrameIDForOldPrincipalHandle` + // OR + // there are no FrameIDs prior to `lastFrameIDForOldPrincipalHandle` in the new + // set of images. + // This means that the old principal handle has been flushed out and we can + // notify our video element about this change. + RefPtr self = this; + PrincipalHandle principalHandle = mPendingPrincipalHandle; + mLastPrincipalHandle = mPendingPrincipalHandle; + mPendingPrincipalHandle = PRINCIPAL_HANDLE_NONE; + mFrameIDForPendingPrincipalHandle = 0; + NS_DispatchToMainThread(NS_NewRunnableFunction([self, principalHandle]() { + if (self->mElement) { + self->mElement->PrincipalHandleChangedForVideoFrameContainer(self, principalHandle); + } + })); + } if (aImages.IsEmpty()) { mImageContainer->ClearAllImages(); diff --git a/dom/media/VideoFrameContainer.h b/dom/media/VideoFrameContainer.h index 7c07299b03..9c28e9b88f 100644 --- a/dom/media/VideoFrameContainer.h +++ b/dom/media/VideoFrameContainer.h @@ -13,6 +13,7 @@ #include "nsCOMPtr.h" #include "nsAutoPtr.h" #include "ImageContainer.h" +#include "MediaSegment.h" namespace mozilla { @@ -30,7 +31,7 @@ class HTMLMediaElement; * confusing. */ class VideoFrameContainer { - B2G_ACL_EXPORT ~VideoFrameContainer(); + ~VideoFrameContainer(); public: typedef layers::ImageContainer ImageContainer; @@ -42,7 +43,14 @@ public: already_AddRefed aContainer); // Call on any thread - B2G_ACL_EXPORT void SetCurrentFrame(const gfx::IntSize& aIntrinsicSize, Image* aImage, + // Returns the last principalHandle we notified mElement about. + PrincipalHandle GetLastPrincipalHandle(); + // We will notify mElement that aPrincipalHandle has been applied when all + // FrameIDs prior to aFrameID have been flushed out. + // aFrameID is ignored if aPrincipalHandle already is our pending principalHandle. + void UpdatePrincipalHandleForFrameID(const PrincipalHandle& aPrincipalHandle, + const ImageContainer::FrameID& aFrameID); + void SetCurrentFrame(const gfx::IntSize& aIntrinsicSize, Image* aImage, const TimeStamp& aTargetTime); void SetCurrentFrames(const gfx::IntSize& aIntrinsicSize, const nsTArray& aImages); @@ -74,8 +82,8 @@ public: INVALIDATE_FORCE }; void Invalidate() { InvalidateWithFlags(INVALIDATE_DEFAULT); } - B2G_ACL_EXPORT void InvalidateWithFlags(uint32_t aFlags); - B2G_ACL_EXPORT ImageContainer* GetImageContainer(); + void InvalidateWithFlags(uint32_t aFlags); + ImageContainer* GetImageContainer(); void ForgetElement() { mElement = nullptr; } uint32_t GetDroppedImageCount() { return mImageContainer->GetDroppedImageCount(); } @@ -111,6 +119,13 @@ protected: // frame is fully invalidated instead of just invalidating for the image change // in the ImageLayer. bool mImageSizeChanged; + // The last PrincipalHandle we notified mElement about. + PrincipalHandle mLastPrincipalHandle; + // The PrincipalHandle the client has notified us is changing with FrameID + // mFrameIDForPendingPrincipalHandle. + PrincipalHandle mPendingPrincipalHandle; + // The FrameID for which mPendingPrincipal is first valid. + ImageContainer::FrameID mFrameIDForPendingPrincipalHandle; }; } // namespace mozilla diff --git a/dom/media/VideoSegment.cpp b/dom/media/VideoSegment.cpp index 21ab56da28..48fa78eb10 100644 --- a/dom/media/VideoSegment.cpp +++ b/dom/media/VideoSegment.cpp @@ -16,11 +16,12 @@ using namespace layers; VideoFrame::VideoFrame(already_AddRefed& aImage, const gfx::IntSize& aIntrinsicSize) - : mImage(aImage), mIntrinsicSize(aIntrinsicSize), mForceBlack(false) + : mImage(aImage), mIntrinsicSize(aIntrinsicSize), mForceBlack(false), + mPrincipalHandle(PRINCIPAL_HANDLE_NONE) {} VideoFrame::VideoFrame() - : mIntrinsicSize(0, 0), mForceBlack(false) + : mIntrinsicSize(0, 0), mForceBlack(false), mPrincipalHandle(PRINCIPAL_HANDLE_NONE) {} VideoFrame::~VideoFrame() @@ -30,6 +31,7 @@ void VideoFrame::SetNull() { mImage = nullptr; mIntrinsicSize = gfx::IntSize(0, 0); + mPrincipalHandle = PRINCIPAL_HANDLE_NONE; } void @@ -38,9 +40,9 @@ VideoFrame::TakeFrom(VideoFrame* aFrame) mImage = aFrame->mImage.forget(); mIntrinsicSize = aFrame->mIntrinsicSize; mForceBlack = aFrame->GetForceBlack(); + mPrincipalHandle = aFrame->mPrincipalHandle; } -#if !defined(MOZILLA_XPCOMRT_API) /* static */ already_AddRefed VideoFrame::CreateBlackImage(const gfx::IntSize& aSize) { @@ -85,7 +87,6 @@ VideoFrame::CreateBlackImage(const gfx::IntSize& aSize) return image.forget(); } -#endif // !defined(MOZILLA_XPCOMRT_API) VideoChunk::VideoChunk() {} @@ -97,11 +98,13 @@ void VideoSegment::AppendFrame(already_AddRefed&& aImage, StreamTime aDuration, const IntSize& aIntrinsicSize, + const PrincipalHandle& aPrincipalHandle, bool aForceBlack) { VideoChunk* chunk = AppendChunk(aDuration); VideoFrame frame(aImage, aIntrinsicSize); frame.SetForceBlack(aForceBlack); + frame.SetPrincipalHandle(aPrincipalHandle); chunk->mFrame.TakeFrom(&frame); } diff --git a/dom/media/VideoSegment.h b/dom/media/VideoSegment.h index 9f90bd9c49..2eb1ce8408 100644 --- a/dom/media/VideoSegment.h +++ b/dom/media/VideoSegment.h @@ -10,11 +10,7 @@ #include "nsCOMPtr.h" #include "gfxPoint.h" #include "nsAutoPtr.h" -#if defined(MOZILLA_XPCOMRT_API) -#include "SimpleImageBuffer.h" -#else #include "ImageContainer.h" -#endif namespace mozilla { @@ -24,11 +20,7 @@ class Image; class VideoFrame { public: -#if defined(MOZILLA_XPCOMRT_API) - typedef mozilla::SimpleImageBuffer Image; -#else typedef mozilla::layers::Image Image; -#endif VideoFrame(already_AddRefed& aImage, const gfx::IntSize& aIntrinsicSize); VideoFrame(); @@ -48,14 +40,14 @@ public: Image* GetImage() const { return mImage; } void SetForceBlack(bool aForceBlack) { mForceBlack = aForceBlack; } bool GetForceBlack() const { return mForceBlack; } + void SetPrincipalHandle(const PrincipalHandle& aPrincipalHandle) { mPrincipalHandle = aPrincipalHandle; } + PrincipalHandle GetPrincipalHandle() const { return mPrincipalHandle; } const gfx::IntSize& GetIntrinsicSize() const { return mIntrinsicSize; } void SetNull(); void TakeFrom(VideoFrame* aFrame); -#if !defined(MOZILLA_XPCOMRT_API) // Create a planar YCbCr black image. static already_AddRefed CreateBlackImage(const gfx::IntSize& aSize); -#endif // !defined(MOZILLA_XPCOMRT_API) protected: // mImage can be null to indicate "no video" (aka "empty frame"). It can @@ -64,6 +56,9 @@ protected: // The desired size to render the video frame at. gfx::IntSize mIntrinsicSize; bool mForceBlack; + // principalHandle for the image in this frame. + // This can be compared to an nsIPrincipal when back on main thread. + PrincipalHandle mPrincipalHandle; }; struct VideoChunk { @@ -96,6 +91,8 @@ struct VideoChunk { return 0; } + PrincipalHandle GetPrincipalHandle() const { return mFrame.GetPrincipalHandle(); } + StreamTime mDuration; VideoFrame mFrame; mozilla::TimeStamp mTimeStamp; @@ -103,11 +100,7 @@ struct VideoChunk { class VideoSegment : public MediaSegmentBase { public: -#if defined(MOZILLA_XPCOMRT_API) - typedef mozilla::SimpleImageBuffer Image; -#else typedef mozilla::layers::Image Image; -#endif typedef mozilla::gfx::IntSize IntSize; VideoSegment(); @@ -116,6 +109,7 @@ public: void AppendFrame(already_AddRefed&& aImage, StreamTime aDuration, const IntSize& aIntrinsicSize, + const PrincipalHandle& aPrincipalHandle, bool aForceBlack = false); const VideoFrame* GetLastFrame(StreamTime* aStart = nullptr) { diff --git a/dom/media/VideoStreamTrack.h b/dom/media/VideoStreamTrack.h index 5d76bb6b2b..70451c38cb 100644 --- a/dom/media/VideoStreamTrack.h +++ b/dom/media/VideoStreamTrack.h @@ -14,15 +14,29 @@ namespace dom { class VideoStreamTrack : public MediaStreamTrack { public: - VideoStreamTrack(DOMMediaStream* aStream, TrackID aTrackID, const nsString& aLabel) - : MediaStreamTrack(aStream, aTrackID, aLabel) {} + VideoStreamTrack(DOMMediaStream* aStream, TrackID aTrackID, + TrackID aInputTrackID, + MediaStreamTrackSource* aSource) + : MediaStreamTrack(aStream, aTrackID, aInputTrackID, aSource) {} JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; VideoStreamTrack* AsVideoStreamTrack() override { return this; } + const VideoStreamTrack* AsVideoStreamTrack() const override { return this; } + // WebIDL void GetKind(nsAString& aKind) override { aKind.AssignLiteral("video"); } + +protected: + already_AddRefed CloneInternal(DOMMediaStream* aOwningStream, + TrackID aTrackID) override + { + return do_AddRef(new VideoStreamTrack(aOwningStream, + aTrackID, + mInputTrackID, + mSource)); + } }; } // namespace dom diff --git a/dom/media/encoder/MediaEncoder.cpp b/dom/media/encoder/MediaEncoder.cpp index 2d37598c40..2a0ff44476 100644 --- a/dom/media/encoder/MediaEncoder.cpp +++ b/dom/media/encoder/MediaEncoder.cpp @@ -9,6 +9,7 @@ #include "mozilla/Logging.h" #include "mozilla/Preferences.h" #include "mozilla/StaticPtr.h" +#include "mozilla/gfx/Point.h" // IntSize #include"GeckoProfiler.h" #include "OggWriter.h" @@ -36,6 +37,35 @@ mozilla::LazyLogModule gMediaEncoderLog("MediaEncoder"); namespace mozilla { +void +MediaEncoder::SetDirectConnect(bool aConnected) +{ + mDirectConnected = aConnected; +} + +void +MediaEncoder::NotifyRealtimeData(MediaStreamGraph* aGraph, + TrackID aID, + StreamTime aTrackOffset, + uint32_t aTrackEvents, + const MediaSegment& aRealtimeMedia) +{ + if (mSuspended == RECORD_NOT_SUSPENDED) { + // Process the incoming raw track data from MediaStreamGraph, called on the + // thread of MediaStreamGraph. + if (mAudioEncoder && aRealtimeMedia.GetType() == MediaSegment::AUDIO) { + mAudioEncoder->NotifyQueuedTrackChanges(aGraph, aID, + aTrackOffset, aTrackEvents, + aRealtimeMedia); + + } else if (mVideoEncoder && aRealtimeMedia.GetType() == MediaSegment::VIDEO) { + mVideoEncoder->NotifyQueuedTrackChanges(aGraph, aID, + aTrackOffset, aTrackEvents, + aRealtimeMedia); + } + } +} + void MediaEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID, @@ -45,17 +75,27 @@ MediaEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, MediaStream* aInputStream, TrackID aInputTrackID) { - // Process the incoming raw track data from MediaStreamGraph, called on the - // thread of MediaStreamGraph. - if (mAudioEncoder && aQueuedMedia.GetType() == MediaSegment::AUDIO) { - mAudioEncoder->NotifyQueuedTrackChanges(aGraph, aID, - aTrackOffset, aTrackEvents, - aQueuedMedia); - - } else if (mVideoEncoder && aQueuedMedia.GetType() == MediaSegment::VIDEO) { - mVideoEncoder->NotifyQueuedTrackChanges(aGraph, aID, - aTrackOffset, aTrackEvents, - aQueuedMedia); + if (!mDirectConnected) { + NotifyRealtimeData(aGraph, aID, aTrackOffset, aTrackEvents, aQueuedMedia); + } else { + if (mSuspended == RECORD_RESUMED) { + if (mVideoEncoder) { + if (aQueuedMedia.GetType() == MediaSegment::VIDEO) { + // insert a null frame of duration equal to the first segment passed + // after Resume(), so it'll get added to one of the DirectListener frames + VideoSegment segment; + gfx::IntSize size(0,0); + segment.AppendFrame(nullptr, aQueuedMedia.GetDuration(), size, + PRINCIPAL_HANDLE_NONE); + mVideoEncoder->NotifyQueuedTrackChanges(aGraph, aID, + aTrackOffset, aTrackEvents, + segment); + mSuspended = RECORD_NOT_SUSPENDED; + } + } else { + mSuspended = RECORD_NOT_SUSPENDED; // no video + } + } } } diff --git a/dom/media/encoder/MediaEncoder.h b/dom/media/encoder/MediaEncoder.h index 6e632250f4..43d311808f 100644 --- a/dom/media/encoder/MediaEncoder.h +++ b/dom/media/encoder/MediaEncoder.h @@ -12,6 +12,7 @@ #include "MediaStreamGraph.h" #include "nsIMemoryReporter.h" #include "mozilla/MemoryReporting.h" +#include "mozilla/Atomics.h" namespace mozilla { @@ -49,7 +50,7 @@ namespace mozilla { * 4) To stop encoding, remove this component from its source stream. * => sourceStream->RemoveListener(encoder); */ -class MediaEncoder : public MediaStreamListener +class MediaEncoder : public MediaStreamDirectListener { public : enum { @@ -74,10 +75,49 @@ public : , mSizeOfBuffer(0) , mState(MediaEncoder::ENCODE_METADDATA) , mShutdown(false) - {} + , mDirectConnected(false) + , mSuspended(false) +{} ~MediaEncoder() {}; + enum SuspendState { + RECORD_NOT_SUSPENDED, + RECORD_SUSPENDED, + RECORD_RESUMED + }; + + /* Note - called from control code, not on MSG threads. */ + void Suspend() + { + mSuspended = RECORD_SUSPENDED; + } + + /** + * Note - called from control code, not on MSG threads. + * Arm to collect the Duration of the next video frame and give it + * to the next frame, in order to avoid any possible loss of sync. */ + void Resume() + { + if (mSuspended == RECORD_SUSPENDED) { + mSuspended = RECORD_RESUMED; + } + } + + /** + * Tells us which Notify to pay attention to for media + */ + void SetDirectConnect(bool aConnected); + + /** + * Notified by the AppendToTrack in MediaStreamGraph; aRealtimeMedia is the raw + * track data in form of MediaSegment. + */ + void NotifyRealtimeData(MediaStreamGraph* aGraph, TrackID aID, + StreamTime aTrackOffset, + uint32_t aTrackEvents, + const MediaSegment& aRealtimeMedia) override; + /** * Notified by the control loop of MediaStreamGraph; aQueueMedia is the raw * track data in form of MediaSegment. @@ -169,6 +209,8 @@ private: int64_t mSizeOfBuffer; int mState; bool mShutdown; + bool mDirectConnected; + Atomic mSuspended; // Get duration from create encoder, for logging purpose double GetEncodeTimeStamp() { diff --git a/dom/media/encoder/TrackEncoder.cpp b/dom/media/encoder/TrackEncoder.cpp index 6fbceea8c3..edc9a5d973 100644 --- a/dom/media/encoder/TrackEncoder.cpp +++ b/dom/media/encoder/TrackEncoder.cpp @@ -249,11 +249,30 @@ VideoTrackEncoder::AppendVideoSegment(const VideoSegment& aSegment) VideoSegment::ChunkIterator iter(const_cast(aSegment)); while (!iter.IsEnded()) { VideoChunk chunk = *iter; - RefPtr image = chunk.mFrame.GetImage(); - mRawSegment.AppendFrame(image.forget(), - chunk.GetDuration(), - chunk.mFrame.GetIntrinsicSize(), - chunk.mFrame.GetForceBlack()); + mTotalFrameDuration += chunk.GetDuration(); + mLastFrameDuration += chunk.GetDuration(); + // Send only the unique video frames for encoding. + // Or if we got the same video chunks more than 1 seconds, + // force to send into encoder. + if ((mLastFrame != chunk.mFrame) || + (mLastFrameDuration >= mTrackRate)) { + RefPtr image = chunk.mFrame.GetImage(); + // Because we may get chunks with a null image (due to input blocking), + // accumulate duration and give it to the next frame that arrives. + // Canonically incorrect - the duration should go to the previous frame + // - but that would require delaying until the next frame arrives. + // Best would be to do like OMXEncoder and pass an effective timestamp + // in with each frame. + if (image) { + mRawSegment.AppendFrame(image.forget(), + mLastFrameDuration, + chunk.mFrame.GetIntrinsicSize(), + PRINCIPAL_HANDLE_NONE, + chunk.mFrame.GetForceBlack()); + mLastFrameDuration = 0; + } + } + mLastFrame.TakeFrom(&chunk.mFrame); iter.Next(); } diff --git a/dom/media/encoder/TrackEncoder.h b/dom/media/encoder/TrackEncoder.h index 6b3b9dc6e7..5f11620431 100644 --- a/dom/media/encoder/TrackEncoder.h +++ b/dom/media/encoder/TrackEncoder.h @@ -261,6 +261,7 @@ public: , mDisplayHeight(0) , mTrackRate(0) , mTotalFrameDuration(0) + , mLastFrameDuration(0) , mVideoBitrate(0) {} @@ -337,10 +338,11 @@ protected: StreamTime mTotalFrameDuration; /** - * The last unique frame we've sent to track encoder, kept track of in - * subclasses. + * The last unique frame and duration we've sent to track encoder, + * kept track of in subclasses. */ VideoFrame mLastFrame; + StreamTime mLastFrameDuration; /** * A segment queue of audio track data, protected by mReentrantMonitor. diff --git a/dom/media/encoder/VorbisTrackEncoder.cpp b/dom/media/encoder/VorbisTrackEncoder.cpp index d53228631c..bf4a6b84b2 100644 --- a/dom/media/encoder/VorbisTrackEncoder.cpp +++ b/dom/media/encoder/VorbisTrackEncoder.cpp @@ -56,7 +56,7 @@ VorbisTrackEncoder::Init(int aChannels, int aSamplingRate) double quality = mAudioBitrate ? (double)mAudioBitrate/aSamplingRate : BASE_QUALITY; - printf("quality %f \n", quality); + VORBISLOG("quality %f", quality); ret = vorbis_encode_init_vbr(&mVorbisInfo, mChannels, mSamplingRate, quality); diff --git a/dom/media/gtest/TestVideoSegment.cpp b/dom/media/gtest/TestVideoSegment.cpp index 932ff1a67e..f7d5aed213 100644 --- a/dom/media/gtest/TestVideoSegment.cpp +++ b/dom/media/gtest/TestVideoSegment.cpp @@ -21,6 +21,7 @@ TEST(VideoSegment, TestAppendFrameForceBlack) segment.AppendFrame(testImage.forget(), mozilla::StreamTime(90000), mozilla::gfx::IntSize(640, 480), + PRINCIPAL_HANDLE_NONE, true); VideoSegment::ChunkIterator iter(segment); @@ -38,7 +39,8 @@ TEST(VideoSegment, TestAppendFrameNotForceBlack) VideoSegment segment; segment.AppendFrame(testImage.forget(), mozilla::StreamTime(90000), - mozilla::gfx::IntSize(640, 480)); + mozilla::gfx::IntSize(640, 480), + PRINCIPAL_HANDLE_NONE); VideoSegment::ChunkIterator iter(segment); while (!iter.IsEnded()) { diff --git a/dom/media/gtest/TestVideoTrackEncoder.cpp b/dom/media/gtest/TestVideoTrackEncoder.cpp index b5c258a850..ab742e4d99 100644 --- a/dom/media/gtest/TestVideoTrackEncoder.cpp +++ b/dom/media/gtest/TestVideoTrackEncoder.cpp @@ -271,7 +271,10 @@ TEST(VP8VideoTrackEncoder, FrameEncode) for (nsTArray>::size_type i = 0; i < images.Length(); i++) { RefPtr image = images[i]; - segment.AppendFrame(image.forget(), mozilla::StreamTime(90000), generator.GetSize()); + segment.AppendFrame(image.forget(), + mozilla::StreamTime(90000), + generator.GetSize(), + PRINCIPAL_HANDLE_NONE); } // track change notification. diff --git a/dom/media/imagecapture/CaptureTask.cpp b/dom/media/imagecapture/CaptureTask.cpp index 7de057a644..d7a38023a0 100644 --- a/dom/media/imagecapture/CaptureTask.cpp +++ b/dom/media/imagecapture/CaptureTask.cpp @@ -8,6 +8,7 @@ #include "mozilla/dom/ImageCapture.h" #include "mozilla/dom/ImageCaptureError.h" #include "mozilla/dom/ImageEncoder.h" +#include "mozilla/dom/MediaStreamTrack.h" #include "mozilla/dom/VideoStreamTrack.h" #include "gfxUtils.h" #include "nsThreadUtils.h" @@ -19,7 +20,7 @@ CaptureTask::TaskComplete(already_AddRefed aBlob, nsresult aRv) { MOZ_ASSERT(NS_IsMainThread()); - DetachStream(); + DetachTrack(); nsresult rv; RefPtr blob(aBlob); @@ -48,56 +49,42 @@ CaptureTask::TaskComplete(already_AddRefed aBlob, nsresult aRv) } void -CaptureTask::AttachStream() +CaptureTask::AttachTrack() { MOZ_ASSERT(NS_IsMainThread()); - RefPtr track = mImageCapture->GetVideoStreamTrack(); - - RefPtr domStream = track->GetStream(); - domStream->AddPrincipalChangeObserver(this); - - RefPtr stream = domStream->GetPlaybackStream(); - stream->AddListener(this); + dom::VideoStreamTrack* track = mImageCapture->GetVideoStreamTrack(); + track->AddPrincipalChangeObserver(this); + track->AddListener(this); } void -CaptureTask::DetachStream() +CaptureTask::DetachTrack() { MOZ_ASSERT(NS_IsMainThread()); - RefPtr track = mImageCapture->GetVideoStreamTrack(); - - RefPtr domStream = track->GetStream(); - domStream->RemovePrincipalChangeObserver(this); - - RefPtr stream = domStream->GetPlaybackStream(); - stream->RemoveListener(this); + dom::VideoStreamTrack* track = mImageCapture->GetVideoStreamTrack(); + track->RemovePrincipalChangeObserver(this); + track->RemoveListener(this); } void -CaptureTask::PrincipalChanged(DOMMediaStream* aMediaStream) +CaptureTask::PrincipalChanged(dom::MediaStreamTrack* aMediaStreamTrack) { MOZ_ASSERT(NS_IsMainThread()); mPrincipalChanged = true; } void -CaptureTask::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID, - StreamTime aTrackOffset, - uint32_t aTrackEvents, - const MediaSegment& aQueuedMedia, - MediaStream* aInputStream, - TrackID aInputTrackID) +CaptureTask::NotifyQueuedChanges(MediaStreamGraph* aGraph, + StreamTime aTrackOffset, + const MediaSegment& aQueuedMedia) { if (mImageGrabbedOrTrackEnd) { return; } - if (aTrackEvents == MediaStreamListener::TRACK_EVENT_ENDED) { - PostTrackEndEvent(); - return; - } + MOZ_ASSERT(aQueuedMedia.GetType() == MediaSegment::VIDEO); // Callback for encoding complete, it calls on main thread. class EncodeComplete : public dom::EncodeCompleteCallback @@ -117,50 +104,47 @@ CaptureTask::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID, RefPtr mTask; }; - if (aQueuedMedia.GetType() == MediaSegment::VIDEO && mTrackID == aID) { - VideoSegment* video = - const_cast (static_cast(&aQueuedMedia)); - VideoSegment::ChunkIterator iter(*video); - while (!iter.IsEnded()) { - VideoChunk chunk = *iter; - // Extract the first valid video frame. - VideoFrame frame; - if (!chunk.IsNull()) { - RefPtr image; - if (chunk.mFrame.GetForceBlack()) { - // Create a black image. - image = VideoFrame::CreateBlackImage(chunk.mFrame.GetIntrinsicSize()); - } else { - image = chunk.mFrame.GetImage(); - } - MOZ_ASSERT(image); - mImageGrabbedOrTrackEnd = true; - - // Encode image. - nsresult rv; - nsAutoString type(NS_LITERAL_STRING("image/jpeg")); - nsAutoString options; - rv = dom::ImageEncoder::ExtractDataFromLayersImageAsync( - type, - options, - false, - image, - new EncodeComplete(this)); - if (NS_FAILED(rv)) { - PostTrackEndEvent(); - } - return; + VideoSegment* video = + const_cast (static_cast(&aQueuedMedia)); + VideoSegment::ChunkIterator iter(*video); + while (!iter.IsEnded()) { + VideoChunk chunk = *iter; + // Extract the first valid video frame. + VideoFrame frame; + if (!chunk.IsNull()) { + RefPtr image; + if (chunk.mFrame.GetForceBlack()) { + // Create a black image. + image = VideoFrame::CreateBlackImage(chunk.mFrame.GetIntrinsicSize()); + } else { + image = chunk.mFrame.GetImage(); } - iter.Next(); + MOZ_ASSERT(image); + mImageGrabbedOrTrackEnd = true; + + // Encode image. + nsresult rv; + nsAutoString type(NS_LITERAL_STRING("image/jpeg")); + nsAutoString options; + rv = dom::ImageEncoder::ExtractDataFromLayersImageAsync( + type, + options, + false, + image, + new EncodeComplete(this)); + if (NS_FAILED(rv)) { + PostTrackEndEvent(); + } + return; } + iter.Next(); } } void -CaptureTask::NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent aEvent) +CaptureTask::NotifyEnded() { - if (((aEvent == EVENT_FINISHED) || (aEvent == EVENT_REMOVED)) && - !mImageGrabbedOrTrackEnd) { + if(!mImageGrabbedOrTrackEnd) { PostTrackEndEvent(); } } diff --git a/dom/media/imagecapture/CaptureTask.h b/dom/media/imagecapture/CaptureTask.h index 396ff53296..0413507eec 100644 --- a/dom/media/imagecapture/CaptureTask.h +++ b/dom/media/imagecapture/CaptureTask.h @@ -7,19 +7,20 @@ #ifndef CAPTURETASK_H #define CAPTURETASK_H -#include "DOMMediaStream.h" #include "MediaStreamGraph.h" +#include "PrincipalChangeObserver.h" namespace mozilla { namespace dom { class Blob; class ImageCapture; +class MediaStreamTrack; } // namespace dom /** * CaptureTask retrieves image from MediaStream and encodes the image to jpeg in - * ImageEncoder. The whole procedures start at AttachStream(), it will add this + * ImageEncoder. The whole procedures start at AttachTrack(), it will add this * class into MediaStream and retrieves an image in MediaStreamGraph thread. * Once the image is retrieved, it will be sent to ImageEncoder and the encoded * blob will be sent out via encoder callback in main thread. @@ -27,23 +28,19 @@ class ImageCapture; * CaptureTask holds a reference of ImageCapture to ensure ImageCapture won't be * released during the period of the capturing process described above. */ -class CaptureTask : public MediaStreamListener, - public DOMMediaStream::PrincipalChangeObserver +class CaptureTask : public MediaStreamTrackListener, + public dom::PrincipalChangeObserver { public: - // MediaStreamListener methods. - void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID, - StreamTime aTrackOffset, - uint32_t aTrackEvents, - const MediaSegment& aQueuedMedia, - MediaStream* aInputStream, - TrackID aInputTrackID) override; + // MediaStreamTrackListener methods. + void NotifyQueuedChanges(MediaStreamGraph* aGraph, + StreamTime aTrackOffset, + const MediaSegment& aQueuedMedia) override; - void NotifyEvent(MediaStreamGraph* aGraph, - MediaStreamGraphEvent aEvent) override; + void NotifyEnded() override; - // DOMMediaStream::PrincipalChangeObserver method. - void PrincipalChanged(DOMMediaStream* aMediaStream) override; + // PrincipalChangeObserver method. + void PrincipalChanged(dom::MediaStreamTrack* aMediaStreamTrack) override; // CaptureTask methods. @@ -54,18 +51,17 @@ public: // this function should be called on main thread. nsresult TaskComplete(already_AddRefed aBlob, nsresult aRv); - // Add listeners into MediaStream and PrincipalChangeObserver. It should be on - // main thread only. - void AttachStream(); + // Add listeners into MediaStreamTrack and PrincipalChangeObserver. + // It should be on main thread only. + void AttachTrack(); - // Remove listeners from MediaStream and PrincipalChangeObserver. It should be - // on main thread only. - void DetachStream(); + // Remove listeners from MediaStreamTrack and PrincipalChangeObserver. + // It should be on main thread only. + void DetachTrack(); // CaptureTask should be created on main thread. - CaptureTask(dom::ImageCapture* aImageCapture, TrackID aTrackID) + explicit CaptureTask(dom::ImageCapture* aImageCapture) : mImageCapture(aImageCapture) - , mTrackID(aTrackID) , mImageGrabbedOrTrackEnd(false) , mPrincipalChanged(false) {} @@ -81,14 +77,12 @@ protected: // event to script. RefPtr mImageCapture; - TrackID mTrackID; - // True when an image is retrieved from MediaStreamGraph or MediaStreamGraph // sends a track finish, end, or removed event. bool mImageGrabbedOrTrackEnd; - // True when MediaStream principal is changed in the period of taking photo - // and it causes a NS_ERROR_DOM_SECURITY_ERR error to script. + // True after MediaStreamTrack principal changes while waiting for a photo + // to finish and we should raise a security error. bool mPrincipalChanged; }; diff --git a/dom/media/imagecapture/ImageCapture.cpp b/dom/media/imagecapture/ImageCapture.cpp index a169d9c380..1a09019e2a 100644 --- a/dom/media/imagecapture/ImageCapture.cpp +++ b/dom/media/imagecapture/ImageCapture.cpp @@ -77,20 +77,20 @@ ImageCapture::TakePhotoByMediaEngine() { // Callback for TakPhoto(), it also monitor the principal. If principal // changes, it returns PHOTO_ERROR with security error. - class TakePhotoCallback : public MediaEngineSource::PhotoCallback, - public DOMMediaStream::PrincipalChangeObserver + class TakePhotoCallback : public MediaEnginePhotoCallback, + public PrincipalChangeObserver { public: - TakePhotoCallback(DOMMediaStream* aStream, ImageCapture* aImageCapture) - : mStream(aStream) + TakePhotoCallback(VideoStreamTrack* aVideoTrack, ImageCapture* aImageCapture) + : mVideoTrack(aVideoTrack) , mImageCapture(aImageCapture) , mPrincipalChanged(false) { MOZ_ASSERT(NS_IsMainThread()); - mStream->AddPrincipalChangeObserver(this); + mVideoTrack->AddPrincipalChangeObserver(this); } - void PrincipalChanged(DOMMediaStream* aMediaStream) override + void PrincipalChanged(MediaStreamTrack* aMediaStream) override { mPrincipalChanged = true; } @@ -114,25 +114,17 @@ ImageCapture::TakePhotoByMediaEngine() ~TakePhotoCallback() { MOZ_ASSERT(NS_IsMainThread()); - mStream->RemovePrincipalChangeObserver(this); + mVideoTrack->RemovePrincipalChangeObserver(this); } - RefPtr mStream; + RefPtr mVideoTrack; RefPtr mImageCapture; bool mPrincipalChanged; }; - RefPtr domStream = mVideoStreamTrack->GetStream(); - DOMLocalMediaStream* domLocalStream = domStream->AsDOMLocalMediaStream(); - if (domLocalStream) { - RefPtr mediaEngine = - domLocalStream->GetMediaEngine(mVideoStreamTrack->GetTrackID()); - RefPtr callback = - new TakePhotoCallback(domStream, this); - return mediaEngine->TakePhoto(callback); - } - - return NS_ERROR_NOT_IMPLEMENTED; + RefPtr callback = + new TakePhotoCallback(mVideoStreamTrack, this); + return mVideoStreamTrack->GetSource().TakePhoto(callback); } void @@ -156,11 +148,11 @@ ImageCapture::TakePhoto(ErrorResult& aResult) if (rv == NS_ERROR_NOT_IMPLEMENTED) { IC_LOG("MediaEngine doesn't support TakePhoto(), it falls back to MediaStreamGraph."); RefPtr task = - new CaptureTask(this, mVideoStreamTrack->GetTrackID()); + new CaptureTask(this); // It adds itself into MediaStreamGraph, so ImageCapture doesn't need to hold // the reference. - task->AttachStream(); + task->AttachTrack(); } } @@ -219,11 +211,7 @@ ImageCapture::CheckPrincipal() { MOZ_ASSERT(NS_IsMainThread()); - RefPtr ms = mVideoStreamTrack->GetStream(); - if (!ms) { - return false; - } - nsCOMPtr principal = ms->GetPrincipal(); + nsCOMPtr principal = mVideoStreamTrack->GetPrincipal(); if (!GetOwner()) { return false; diff --git a/dom/media/mediasink/DecodedStream.cpp b/dom/media/mediasink/DecodedStream.cpp index 662583aa55..73536719c6 100644 --- a/dom/media/mediasink/DecodedStream.cpp +++ b/dom/media/mediasink/DecodedStream.cpp @@ -6,6 +6,7 @@ #include "mozilla/CheckedInt.h" #include "mozilla/gfx/Point.h" +#include "mozilla/SyncRunnable.h" #include "AudioSegment.h" #include "DecodedStream.h" @@ -210,11 +211,13 @@ DecodedStream::DecodedStream(AbstractThread* aOwnerThread, MediaQueue& aAudioQueue, MediaQueue& aVideoQueue, OutputStreamManager* aOutputStreamManager, - const bool& aSameOrigin) + const bool& aSameOrigin, + const PrincipalHandle& aPrincipalHandle) : mOwnerThread(aOwnerThread) , mOutputStreamManager(aOutputStreamManager) , mPlaying(false) , mSameOrigin(aSameOrigin) + , mPrincipalHandle(aPrincipalHandle) , mAudioQueue(aAudioQueue) , mVideoQueue(aVideoQueue) { @@ -269,23 +272,35 @@ DecodedStream::Start(int64_t aStartTime, const MediaInfo& aInfo) class R : public nsRunnable { typedef MozPromiseHolder Promise; - typedef decltype(&DecodedStream::CreateData) Method; public: - R(DecodedStream* aThis, Method aMethod, PlaybackInfoInit&& aInit, Promise&& aPromise) - : mThis(aThis), mMethod(aMethod), mInit(Move(aInit)) + R(PlaybackInfoInit&& aInit, Promise&& aPromise, OutputStreamManager* aManager) + : mInit(Move(aInit)), mOutputStreamManager(aManager) { mPromise = Move(aPromise); } NS_IMETHOD Run() override { - (mThis->*mMethod)(Move(mInit), Move(mPromise)); + MOZ_ASSERT(NS_IsMainThread()); + // No need to create a source stream when there are no output streams. This + // happens when RemoveOutput() is called immediately after StartPlayback(). + if (!mOutputStreamManager->Graph()) { + // Resolve the promise to indicate the end of playback. + mPromise.Resolve(true, __func__); + return NS_OK; + } + mData = MakeUnique( + mOutputStreamManager, Move(mInit), Move(mPromise)); return NS_OK; } + UniquePtr ReleaseData() + { + return Move(mData); + } private: - RefPtr mThis; - Method mMethod; PlaybackInfoInit mInit; Promise mPromise; + RefPtr mOutputStreamManager; + UniquePtr mData; }; MozPromiseHolder promise; @@ -293,8 +308,15 @@ DecodedStream::Start(int64_t aStartTime, const MediaInfo& aInfo) PlaybackInfoInit init { aStartTime, aInfo }; - nsCOMPtr r = new R(this, &DecodedStream::CreateData, Move(init), Move(promise)); - AbstractThread::MainThread()->Dispatch(r.forget()); + nsCOMPtr r = new R(Move(init), Move(promise), mOutputStreamManager); + nsCOMPtr mainThread = do_GetMainThread(); + SyncRunnable::DispatchToThread(mainThread, r); + mData = static_cast(r.get())->ReleaseData(); + + if (mData) { + mData->SetPlaying(mPlaying); + SendData(); + } } void @@ -342,77 +364,6 @@ DecodedStream::DestroyData(UniquePtr aData) AbstractThread::MainThread()->Dispatch(r.forget()); } -void -DecodedStream::CreateData(PlaybackInfoInit&& aInit, MozPromiseHolder&& aPromise) -{ - MOZ_ASSERT(NS_IsMainThread()); - - // No need to create a source stream when there are no output streams. This - // happens when RemoveOutput() is called immediately after StartPlayback(). - if (!mOutputStreamManager->Graph()) { - // Resolve the promise to indicate the end of playback. - aPromise.Resolve(true, __func__); - return; - } - - auto data = new DecodedStreamData(mOutputStreamManager, Move(aInit), Move(aPromise)); - - class R : public nsRunnable { - typedef void(DecodedStream::*Method)(UniquePtr); - public: - R(DecodedStream* aThis, Method aMethod, DecodedStreamData* aData) - : mThis(aThis), mMethod(aMethod), mData(aData) {} - NS_IMETHOD Run() override - { - (mThis->*mMethod)(Move(mData)); - return NS_OK; - } - private: - virtual ~R() - { - // mData is not transferred when dispatch fails and Run() is not called. - // We need to dispatch a task to ensure DecodedStreamData is destroyed - // properly on the main thread. - if (mData) { - DecodedStreamData* data = mData.release(); - nsCOMPtr r = NS_NewRunnableFunction([=] () { - delete data; - }); - // We are in tail dispatching phase. Don't call - // AbstractThread::MainThread()->Dispatch() to avoid reentrant - // AutoTaskDispatcher. - NS_DispatchToMainThread(r.forget()); - } - } - RefPtr mThis; - Method mMethod; - UniquePtr mData; - }; - - // Post a message to ensure |mData| is only updated on the worker thread. - // Note this could fail when MDSM begin to shut down the worker thread. - nsCOMPtr r = new R(this, &DecodedStream::OnDataCreated, data); - mOwnerThread->Dispatch(r.forget(), AbstractThread::DontAssertDispatchSuccess); -} - -void -DecodedStream::OnDataCreated(UniquePtr aData) -{ - AssertOwnerThread(); - MOZ_ASSERT(!mData, "Already created."); - - // Start to send data to the stream immediately - if (mStartTime.isSome()) { - aData->SetPlaying(mPlaying); - mData = Move(aData); - SendData(); - return; - } - - // Playback has ended. Destroy aData which is not needed anymore. - DestroyData(Move(aData)); -} - void DecodedStream::SetPlaying(bool aPlaying) { @@ -452,8 +403,8 @@ DecodedStream::SetPreservesPitch(bool aPreservesPitch) static void SendStreamAudio(DecodedStreamData* aStream, int64_t aStartTime, - MediaData* aData, AudioSegment* aOutput, - uint32_t aRate, double aVolume) + MediaData* aData, AudioSegment* aOutput, uint32_t aRate, + const PrincipalHandle& aPrincipalHandle) { // The amount of audio frames that is used to fuzz rounding errors. static const int64_t AUDIO_FUZZ_FRAMES = 1; @@ -492,15 +443,15 @@ SendStreamAudio(DecodedStreamData* aStream, int64_t aStartTime, for (uint32_t i = 0; i < audio->mChannels; ++i) { channels.AppendElement(bufferData + i * audio->mFrames); } - aOutput->AppendFrames(buffer.forget(), channels, audio->mFrames); + aOutput->AppendFrames(buffer.forget(), channels, audio->mFrames, aPrincipalHandle); aStream->mAudioFramesWritten += audio->mFrames; - aOutput->ApplyVolume(aVolume); aStream->mNextAudioTime = audio->GetEndTime(); } void -DecodedStream::SendAudio(double aVolume, bool aIsSameOrigin) +DecodedStream::SendAudio(double aVolume, bool aIsSameOrigin, + const PrincipalHandle& aPrincipalHandle) { AssertOwnerThread(); @@ -518,9 +469,12 @@ DecodedStream::SendAudio(double aVolume, bool aIsSameOrigin) // is ref-counted. mAudioQueue.GetElementsAfter(mData->mNextAudioTime, &audio); for (uint32_t i = 0; i < audio.Length(); ++i) { - SendStreamAudio(mData.get(), mStartTime.ref(), audio[i], &output, rate, aVolume); + SendStreamAudio(mData.get(), mStartTime.ref(), audio[i], &output, rate, + aPrincipalHandle); } + output.ApplyVolume(aVolume); + if (!aIsSameOrigin) { output.ReplaceWithDisabled(); } @@ -544,13 +498,14 @@ WriteVideoToMediaStream(MediaStream* aStream, int64_t aEndMicroseconds, int64_t aStartMicroseconds, const mozilla::gfx::IntSize& aIntrinsicSize, - VideoSegment* aOutput) + VideoSegment* aOutput, + const PrincipalHandle& aPrincipalHandle) { RefPtr image = aImage; StreamTime duration = aStream->MicrosecondsToStreamTimeRoundDown(aEndMicroseconds) - aStream->MicrosecondsToStreamTimeRoundDown(aStartMicroseconds); - aOutput->AppendFrame(image.forget(), duration, aIntrinsicSize); + aOutput->AppendFrame(image.forget(), duration, aIntrinsicSize, aPrincipalHandle); } static bool @@ -565,7 +520,7 @@ ZeroDurationAtLastChunk(VideoSegment& aInput) } void -DecodedStream::SendVideo(bool aIsSameOrigin) +DecodedStream::SendVideo(bool aIsSameOrigin, const PrincipalHandle& aPrincipalHandle) { AssertOwnerThread(); @@ -596,13 +551,14 @@ DecodedStream::SendVideo(bool aIsSameOrigin) // and capture happens at 15 sec, we'll have to append a black frame // that is 15 sec long. WriteVideoToMediaStream(sourceStream, mData->mLastVideoImage, v->mTime, - mData->mNextVideoTime, mData->mLastVideoImageDisplaySize, &output); + mData->mNextVideoTime, mData->mLastVideoImageDisplaySize, &output, + aPrincipalHandle); mData->mNextVideoTime = v->mTime; } if (mData->mNextVideoTime < v->GetEndTime()) { - WriteVideoToMediaStream(sourceStream, v->mImage, - v->GetEndTime(), mData->mNextVideoTime, v->mDisplay, &output); + WriteVideoToMediaStream(sourceStream, v->mImage, v->GetEndTime(), + mData->mNextVideoTime, v->mDisplay, &output, aPrincipalHandle); mData->mNextVideoTime = v->GetEndTime(); mData->mLastVideoImage = v->mImage; mData->mLastVideoImageDisplaySize = v->mDisplay; @@ -629,7 +585,7 @@ DecodedStream::SendVideo(bool aIsSameOrigin) int64_t deviation_usec = sourceStream->StreamTimeToMicroseconds(1); WriteVideoToMediaStream(sourceStream, mData->mLastVideoImage, mData->mNextVideoTime + deviation_usec, mData->mNextVideoTime, - mData->mLastVideoImageDisplaySize, &endSegment); + mData->mLastVideoImageDisplaySize, &endSegment, aPrincipalHandle); mData->mNextVideoTime += deviation_usec; MOZ_ASSERT(endSegment.GetDuration() > 0); if (!aIsSameOrigin) { @@ -682,8 +638,8 @@ DecodedStream::SendData() return; } - SendAudio(mParams.mVolume, mSameOrigin); - SendVideo(mSameOrigin); + SendAudio(mParams.mVolume, mSameOrigin, mPrincipalHandle); + SendVideo(mSameOrigin, mPrincipalHandle); AdvanceTracks(); bool finished = (!mInfo.HasAudio() || mAudioQueue.IsFinished()) && diff --git a/dom/media/mediasink/DecodedStream.h b/dom/media/mediasink/DecodedStream.h index 3bda109f40..c13a5ad5df 100644 --- a/dom/media/mediasink/DecodedStream.h +++ b/dom/media/mediasink/DecodedStream.h @@ -37,7 +37,8 @@ public: MediaQueue& aAudioQueue, MediaQueue& aVideoQueue, OutputStreamManager* aOutputStreamManager, - const bool& aSameOrigin); + const bool& aSameOrigin, + const PrincipalHandle& aPrincipalHandle); // MediaSink functions. const PlaybackParams& GetPlaybackParams() const override; @@ -66,12 +67,10 @@ protected: virtual ~DecodedStream(); private: - void CreateData(PlaybackInfoInit&& aInit, MozPromiseHolder&& aPromise); void DestroyData(UniquePtr aData); - void OnDataCreated(UniquePtr aData); void AdvanceTracks(); - void SendAudio(double aVolume, bool aIsSameOrigin); - void SendVideo(bool aIsSameOrigin); + void SendAudio(double aVolume, bool aIsSameOrigin, const PrincipalHandle& aPrincipalHandle); + void SendVideo(bool aIsSameOrigin, const PrincipalHandle& aPrincipalHandle); void SendData(); void AssertOwnerThread() const { @@ -97,6 +96,8 @@ private: bool mPlaying; const bool& mSameOrigin; // valid until Shutdown() is called. + const PrincipalHandle& mPrincipalHandle; // valid until Shutdown() is called. + PlaybackParams mParams; Maybe mStartTime; diff --git a/dom/media/moz.build b/dom/media/moz.build index 7775b4ba2a..e9a6d86318 100644 --- a/dom/media/moz.build +++ b/dom/media/moz.build @@ -128,6 +128,7 @@ EXPORTS += [ 'MP3Demuxer.h', 'MP3FrameParser.h', 'nsIDocumentActivity.h', + 'PrincipalChangeObserver.h', 'RtspMediaResource.h', 'SeekTarget.h', 'SelfRef.h', @@ -210,6 +211,7 @@ UNIFIED_SOURCES += [ 'MediaData.cpp', 'MediaDecoder.cpp', 'MediaDecoderReader.cpp', + 'MediaDecoderReaderWrapper.cpp', 'MediaDecoderStateMachine.cpp', 'MediaDeviceInfo.cpp', 'MediaDevices.cpp', diff --git a/dom/media/platforms/apple/AppleVDADecoder.cpp b/dom/media/platforms/apple/AppleVDADecoder.cpp index 7f4a9923dc..89b5e66c8d 100644 --- a/dom/media/platforms/apple/AppleVDADecoder.cpp +++ b/dom/media/platforms/apple/AppleVDADecoder.cpp @@ -595,14 +595,14 @@ AppleVDADecoder::CreateDecoderSpecification() CFDictionaryRef AppleVDADecoder::CreateOutputConfiguration() { - // Output format type: - SInt32 PixelFormatTypeValue = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange; - AutoCFRelease PixelFormatTypeNumber = - CFNumberCreate(kCFAllocatorDefault, - kCFNumberSInt32Type, - &PixelFormatTypeValue); - if (mUseSoftwareImages) { + // Output format type: + SInt32 PixelFormatTypeValue = + kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange; + AutoCFRelease PixelFormatTypeNumber = + CFNumberCreate(kCFAllocatorDefault, + kCFNumberSInt32Type, + &PixelFormatTypeValue); const void* outputKeys[] = { kCVPixelBufferPixelFormatTypeKey }; const void* outputValues[] = { PixelFormatTypeNumber }; static_assert(ArrayLength(outputKeys) == ArrayLength(outputValues), @@ -617,6 +617,12 @@ AppleVDADecoder::CreateOutputConfiguration() } #ifndef MOZ_WIDGET_UIKIT + // Output format type: + SInt32 PixelFormatTypeValue = kCVPixelFormatType_422YpCbCr8; + AutoCFRelease PixelFormatTypeNumber = + CFNumberCreate(kCFAllocatorDefault, + kCFNumberSInt32Type, + &PixelFormatTypeValue); // Construct IOSurface Properties const void* IOSurfaceKeys[] = { MacIOSurfaceLib::kPropIsGlobal }; const void* IOSurfaceValues[] = { kCFBooleanTrue }; diff --git a/dom/media/standalone/moz.build b/dom/media/standalone/moz.build index a0427dbe01..2a5baf8a12 100644 --- a/dom/media/standalone/moz.build +++ b/dom/media/standalone/moz.build @@ -33,7 +33,6 @@ if CONFIG['MOZ_WEBRTC']: ] DEFINES['MOZILLA_INTERNAL_API'] = True -DEFINES['MOZILLA_XPCOMRT_API'] = True DEFINES['MOZILLA_EXTERNAL_LINKAGE'] = True include('/ipc/chromium/chromium-config.mozbuild') diff --git a/dom/media/systemservices/LoadManager.h b/dom/media/systemservices/LoadManager.h index 6918200284..142630820e 100644 --- a/dom/media/systemservices/LoadManager.h +++ b/dom/media/systemservices/LoadManager.h @@ -7,6 +7,7 @@ #define _LOADMANAGER_H_ #include "LoadMonitor.h" +#include "mozilla/WeakPtr.h" #include "mozilla/StaticPtr.h" #include "mozilla/TimeStamp.h" #include "mozilla/Services.h" @@ -23,12 +24,15 @@ namespace mozilla { class LoadManagerSingleton : public LoadNotificationCallback, public webrtc::CPULoadStateCallbackInvoker, public webrtc::CpuOveruseObserver, + public SupportsWeakPtr, public nsIObserver { public: static LoadManagerSingleton* Get(); + MOZ_DECLARE_WEAKREFERENCE_TYPENAME(LoadManagerSingleton) + NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIOBSERVER @@ -86,23 +90,31 @@ public: void AddObserver(webrtc::CPULoadStateObserver * aObserver) override { - mManager->AddObserver(aObserver); + if (mManager) { + mManager->AddObserver(aObserver); + } } void RemoveObserver(webrtc::CPULoadStateObserver * aObserver) override { - mManager->RemoveObserver(aObserver); + if (mManager) { + mManager->RemoveObserver(aObserver); + } } void OveruseDetected() override { - mManager->OveruseDetected(); + if (mManager) { + mManager->OveruseDetected(); + } } void NormalUsage() override { - mManager->NormalUsage(); + if (mManager) { + mManager->NormalUsage(); + } } private: - LoadManagerSingleton* mManager; + WeakPtr mManager; }; } //namespace diff --git a/dom/media/test/mochitest.ini b/dom/media/test/mochitest.ini index 0382adb99c..d1cb446380 100644 --- a/dom/media/test/mochitest.ini +++ b/dom/media/test/mochitest.ini @@ -409,31 +409,36 @@ tags=msg tags=msg [test_mediarecorder_record_audionode.html] tags=msg +[test_mediarecorder_record_canvas_captureStream.html] +tags=msg +[test_mediarecorder_record_changing_video_resolution.html] +tags=msg [test_mediarecorder_record_gum_video_timeslice.html] tags=msg [test_mediarecorder_record_immediate_stop.html] tags=msg capturestream [test_mediarecorder_record_no_timeslice.html] tags=msg capturestream -skip-if = toolkit == 'gonk' && debug -[test_mediarecorder_record_nosrc.html] -tags=msg capturestream [test_mediarecorder_record_session.html] tags=msg capturestream [test_mediarecorder_record_startstopstart.html] tags=msg -skip-if = toolkit == 'gonk' && debug [test_mediarecorder_record_stopms.html] tags=msg [test_mediarecorder_record_timeslice.html] -tags=msg -skip-if = toolkit == 'gonk' && debug +tags=msg capturestream [test_mediarecorder_reload_crash.html] tags=msg capturestream [test_mediarecorder_unsupported_src.html] +tags=msg +[test_mediarecorder_webm_support.html] +skip-if = os == 'android' || arch == 'arm' || arch == 'arm64' +tags=msg +[test_mediarecorder_mp4_support.html] +skip-if = toolkit != 'gonk' || android_version < '17' # Android/Gonk before SDK version 17 does not have the OMX Encoder API. +tags=msg [test_mediarecorder_record_getdata_afterstart.html] tags=msg capturestream -skip-if = toolkit == 'gonk' && debug [test_mediatrack_consuming_mediaresource.html] [test_mediatrack_consuming_mediastream.html] tags=msg diff --git a/dom/media/test/test_bug879717.html b/dom/media/test/test_bug879717.html index f9a6fcbbc0..0737e2b1b9 100644 --- a/dom/media/test/test_bug879717.html +++ b/dom/media/test/test_bug879717.html @@ -1,4 +1,4 @@ - + Test for bug 879717, check that a video element can be drawn into a canvas at various states of playback diff --git a/dom/media/test/test_mediarecorder_creation.html b/dom/media/test/test_mediarecorder_creation.html index 25fa502738..4ce2b65608 100644 --- a/dom/media/test/test_mediarecorder_creation.html +++ b/dom/media/test/test_mediarecorder_creation.html @@ -1,4 +1,4 @@ - + Test MediaRecorder Creation diff --git a/dom/media/test/test_mediarecorder_creation_fail.html b/dom/media/test/test_mediarecorder_creation_fail.html index a98a9990e9..903bf1cc70 100644 --- a/dom/media/test/test_mediarecorder_creation_fail.html +++ b/dom/media/test/test_mediarecorder_creation_fail.html @@ -1,4 +1,4 @@ - + Test MediaRecorder Record with media.ogg.enabled = false diff --git a/dom/media/test/test_mediarecorder_getencodeddata.html b/dom/media/test/test_mediarecorder_getencodeddata.html index 90e210b28d..426552ce73 100644 --- a/dom/media/test/test_mediarecorder_getencodeddata.html +++ b/dom/media/test/test_mediarecorder_getencodeddata.html @@ -1,4 +1,4 @@ - + Bug 957452 Test GetEncodedData problem on asan build @@ -58,7 +58,6 @@ SpecialPowers.pushPrefEnv({"set": [["media.ogg.enabled", false]]}, is(mediaRecorder.stream, stream, 'Media recorder stream = element stream at the start of recording'); mediaRecorder.requestData(); - mediaRecorder.stop(); }, 100); } ); diff --git a/dom/media/test/test_mediarecorder_mp4_support.html b/dom/media/test/test_mediarecorder_mp4_support.html new file mode 100644 index 0000000000..118cbaf25e --- /dev/null +++ b/dom/media/test/test_mediarecorder_mp4_support.html @@ -0,0 +1,16 @@ + + + + Media Recording - test mp4 MIME support + + + + +
+
+
+
diff --git a/dom/media/test/test_mediarecorder_record_4ch_audiocontext.html b/dom/media/test/test_mediarecorder_record_4ch_audiocontext.html
index fa61fe09ca..2ea3d867d3 100644
--- a/dom/media/test/test_mediarecorder_record_4ch_audiocontext.html
+++ b/dom/media/test/test_mediarecorder_record_4ch_audiocontext.html
@@ -1,4 +1,4 @@
-
+
 
 
   Test MediaRecorder Record AudioContext with four channels
diff --git a/dom/media/test/test_mediarecorder_record_audiocontext.html b/dom/media/test/test_mediarecorder_record_audiocontext.html
index 19b350f83a..2697ad8d0e 100644
--- a/dom/media/test/test_mediarecorder_record_audiocontext.html
+++ b/dom/media/test/test_mediarecorder_record_audiocontext.html
@@ -1,4 +1,4 @@
-
+
 
 
   Test MediaRecorder Record AudioContext
diff --git a/dom/media/test/test_mediarecorder_record_canvas_captureStream.html b/dom/media/test/test_mediarecorder_record_canvas_captureStream.html
new file mode 100644
index 0000000000..7f440fba9d
--- /dev/null
+++ b/dom/media/test/test_mediarecorder_record_canvas_captureStream.html
@@ -0,0 +1,73 @@
+
+
+
+  Test MediaRecorder Recording canvas stream
+  
+  
+  
+
+
+
+
+
+ +
+ + diff --git a/dom/media/test/test_mediarecorder_record_changing_video_resolution.html b/dom/media/test/test_mediarecorder_record_changing_video_resolution.html new file mode 100644 index 0000000000..96329ff2c9 --- /dev/null +++ b/dom/media/test/test_mediarecorder_record_changing_video_resolution.html @@ -0,0 +1,69 @@ + + + + Test MediaRecorder Recording canvas stream that dynamically changes resolution + + + + +
+
+
+ +
+ + diff --git a/dom/media/test/test_mediarecorder_record_getdata_afterstart.html b/dom/media/test/test_mediarecorder_record_getdata_afterstart.html index 2350793581..04e4ec7c83 100644 --- a/dom/media/test/test_mediarecorder_record_getdata_afterstart.html +++ b/dom/media/test/test_mediarecorder_record_getdata_afterstart.html @@ -1,4 +1,4 @@ - + Bug 951008 Test MediaRecorder Record has start event diff --git a/dom/media/test/test_mediarecorder_record_immediate_stop.html b/dom/media/test/test_mediarecorder_record_immediate_stop.html index 0f51fe300e..fd0f64c8a4 100644 --- a/dom/media/test/test_mediarecorder_record_immediate_stop.html +++ b/dom/media/test/test_mediarecorder_record_immediate_stop.html @@ -1,4 +1,4 @@ - + Test MediaRecorder Immediate Stop diff --git a/dom/media/test/test_mediarecorder_record_no_timeslice.html b/dom/media/test/test_mediarecorder_record_no_timeslice.html index 67321ceb4e..a408160a53 100644 --- a/dom/media/test/test_mediarecorder_record_no_timeslice.html +++ b/dom/media/test/test_mediarecorder_record_no_timeslice.html @@ -1,4 +1,4 @@ - + Test MediaRecorder Record No Timeslice diff --git a/dom/media/test/test_mediarecorder_record_nosrc.html b/dom/media/test/test_mediarecorder_record_nosrc.html deleted file mode 100644 index b7f59b0de5..0000000000 --- a/dom/media/test/test_mediarecorder_record_nosrc.html +++ /dev/null @@ -1,33 +0,0 @@ - - - Bug 919051 - Media Recording - memory leak when record a media stream without any tracks - - - - - -Mozill -a Bug 919051 -
-
-
-
-
diff --git a/dom/media/test/test_mediarecorder_record_timeslice.html b/dom/media/test/test_mediarecorder_record_timeslice.html
index 7fc503f471..728da5e5e1 100644
--- a/dom/media/test/test_mediarecorder_record_timeslice.html
+++ b/dom/media/test/test_mediarecorder_record_timeslice.html
@@ -1,4 +1,4 @@
-
+
 
 
   Test MediaRecorder Record Timeslice
@@ -25,75 +25,76 @@ function startTest(test, token) {
   element.src = test.name;
   element.test = test;
   element.stream = element.mozCaptureStream();
+  element.preload = "auto";
 
-  var mediaRecorder = new MediaRecorder(element.stream);
+  // Set up MediaRecorder once loadedmetadata fires and tracks are available.
+  element.onloadedmetadata = function() {
+    element.onloadedmetadata = null;
 
-  mediaRecorder.onerror = function () {
-    ok(false, 'Unexpected onerror callback fired');
-  };
+    var mediaRecorder = new MediaRecorder(element.stream);
 
-  mediaRecorder.onwarning = function () {
-    ok(false, 'Unexpected onwarning callback fired');
-  };
+    mediaRecorder.onerror = function () {
+      ok(false, 'Unexpected onerror callback fired');
+    };
 
-  mediaRecorder.onstop = function () {
-    ok(false, 'Unexpected onstop callback fired');
-  };
+    mediaRecorder.onwarning = function () {
+      ok(false, 'Unexpected onwarning callback fired');
+    };
 
-  var dataAvailableCount = 0;
-  var onDataAvailableFirst = false;
+    mediaRecorder.onstop = function () {
+      ok(false, 'Unexpected onstop callback fired');
+    };
 
-  // This handler fires every 250ms to generate a blob.
-  mediaRecorder.ondataavailable = function (evt) {
-    info('ondataavailable fired');
-    dataAvailableCount++;
+    var dataAvailableCount = 0;
+    var onDataAvailableFirst = false;
 
-    ok(evt instanceof BlobEvent,
-       'Events fired from ondataavailable should be BlobEvent');
-    is(evt.type, 'dataavailable',
-       'Event type should dataavailable');
-    ok(evt.data.size >= 0,
-       'Blob data size received is greater than or equal to zero');
+    // This handler fires every 250ms to generate a blob.
+    mediaRecorder.ondataavailable = function (evt) {
+      info('ondataavailable fired');
+      dataAvailableCount++;
 
-    is(evt.data.type, expectedMimeType,
-       'Blob data received should have type = ' + expectedMimeType);
-    is(mediaRecorder.mimeType, expectedMimeType,
-       'Mime type in ondataavailable = ' + mediaRecorder.mimeType);
+      ok(evt instanceof BlobEvent,
+         'Events fired from ondataavailable should be BlobEvent');
+      is(evt.type, 'dataavailable',
+         'Event type should dataavailable');
+      ok(evt.data.size >= 0,
+         'Blob data size received is greater than or equal to zero');
 
-    // We'll stop recording upon the 1st blob being received
-    if (dataAvailableCount === 1) {
-      mediaRecorder.onstop = function (evt) {
-        info('onstop fired');
+      is(evt.data.type, expectedMimeType,
+         'Blob data received should have type = ' + expectedMimeType);
+      is(mediaRecorder.mimeType, expectedMimeType,
+         'Mime type in ondataavailable = ' + mediaRecorder.mimeType);
 
-        if (!onDataAvailableFirst) {
-          ok(false, 'onstop unexpectedly fired before ondataavailable');
-        }
+      // We'll stop recording upon the 1st blob being received
+      if (dataAvailableCount === 1) {
+        mediaRecorder.onstop = function (evt) {
+          info('onstop fired');
 
-        manager.finished(token);
-      };
+          if (!onDataAvailableFirst) {
+            ok(false, 'onstop unexpectedly fired before ondataavailable');
+          }
+          element.pause();
+          manager.finished(token);
+        };
 
-      mediaRecorder.stop();
-      is(mediaRecorder.state, 'inactive',
-         'Media recorder is inactive after being stopped');
-      is(mediaRecorder.stream, element.stream,
-         'Media recorder stream = element stream post recording');
+        mediaRecorder.stop();
+        is(mediaRecorder.state, 'inactive',
+           'Media recorder is inactive after being stopped');
+        is(mediaRecorder.stream, element.stream,
+           'Media recorder stream = element stream post recording');
 
-    } else if (dataAvailableCount === 2) {
-      // Ensure we've received at least two ondataavailable events before onstop
-      onDataAvailableFirst = true;
-    }
-  };
+      } else if (dataAvailableCount === 2) {
+        // Ensure we've received at least two ondataavailable events before onstop
+        onDataAvailableFirst = true;
+      }
+    };
 
-  // Start recording once canplaythrough fires
-  element.oncanplaythrough = function() {
-    element.oncanplaythrough = null;
     mediaRecorder.start(250);
+    element.play();
     is(mediaRecorder.state, 'recording', 'Media recorder should be recording');
     is(mediaRecorder.stream, element.stream,
        'Media recorder stream = element stream at the start of recording');
   };
-
-  element.play();
 }
 
 manager.runTests(gMediaRecorderTests, startTest);
diff --git a/dom/media/test/test_mediarecorder_state_transition.html b/dom/media/test/test_mediarecorder_state_transition.html
index e9ed68eb19..7b224c5fc0 100644
--- a/dom/media/test/test_mediarecorder_state_transition.html
+++ b/dom/media/test/test_mediarecorder_state_transition.html
@@ -1,4 +1,4 @@
-
+
 
 
   Test MediaRecorder State Transition
diff --git a/dom/media/test/test_mediarecorder_unsupported_src.html b/dom/media/test/test_mediarecorder_unsupported_src.html
index 1e4aef30f5..3b57dcae4d 100644
--- a/dom/media/test/test_mediarecorder_unsupported_src.html
+++ b/dom/media/test/test_mediarecorder_unsupported_src.html
@@ -10,7 +10,15 @@
 
 
+  
+
+
+
+
+
+
diff --git a/dom/media/test/test_mediatrack_consuming_mediaresource.html b/dom/media/test/test_mediatrack_consuming_mediaresource.html
index 51a8ade4ce..c8ce3a8b46 100644
--- a/dom/media/test/test_mediatrack_consuming_mediaresource.html
+++ b/dom/media/test/test_mediatrack_consuming_mediaresource.html
@@ -1,4 +1,4 @@
-
+
 
 
   Test track interfaces when consuming media resources
diff --git a/dom/media/test/test_mediatrack_consuming_mediastream.html b/dom/media/test/test_mediatrack_consuming_mediastream.html
index 62021f4be4..a0961d0ff2 100644
--- a/dom/media/test/test_mediatrack_consuming_mediastream.html
+++ b/dom/media/test/test_mediatrack_consuming_mediastream.html
@@ -1,4 +1,4 @@
-
+
 
 
   Test track interfaces when consuming a MediaStream from gUM
diff --git a/dom/media/test/test_mediatrack_events.html b/dom/media/test/test_mediatrack_events.html
index 39d4d01cf0..2f96865986 100644
--- a/dom/media/test/test_mediatrack_events.html
+++ b/dom/media/test/test_mediatrack_events.html
@@ -1,4 +1,4 @@
-
+
 
 
   Test events of media track interfaces
diff --git a/dom/media/test/test_mediatrack_parsing_ogg.html b/dom/media/test/test_mediatrack_parsing_ogg.html
index 06f04a378e..e98636c2d2 100644
--- a/dom/media/test/test_mediatrack_parsing_ogg.html
+++ b/dom/media/test/test_mediatrack_parsing_ogg.html
@@ -1,4 +1,4 @@
-
+
 
 
   Test events of media track interfaces
diff --git a/dom/media/test/test_mediatrack_replay_from_end.html b/dom/media/test/test_mediatrack_replay_from_end.html
index 7a0d6bb097..584849a59f 100644
--- a/dom/media/test/test_mediatrack_replay_from_end.html
+++ b/dom/media/test/test_mediatrack_replay_from_end.html
@@ -1,4 +1,4 @@
-
+
 
 
   Test media tracks if replay after playback has ended
diff --git a/dom/media/test/test_multiple_mediastreamtracks.html b/dom/media/test/test_multiple_mediastreamtracks.html
index 35a75ee486..66d6866bf0 100644
--- a/dom/media/test/test_multiple_mediastreamtracks.html
+++ b/dom/media/test/test_multiple_mediastreamtracks.html
@@ -1,4 +1,4 @@
-
+
 
 
   Test the ability of MediaStream with multiple MediaStreamTracks
diff --git a/dom/media/test/test_streams_autoplay.html b/dom/media/test/test_streams_autoplay.html
index 2b8c85c198..bc63a0cab8 100644
--- a/dom/media/test/test_streams_autoplay.html
+++ b/dom/media/test/test_streams_autoplay.html
@@ -1,4 +1,4 @@
-
+
 
 
   Test that a MediaStream source triggers autoplay
diff --git a/dom/media/test/test_streams_individual_pause.html b/dom/media/test/test_streams_individual_pause.html
index e99e8ab39f..a6abc65ffb 100644
--- a/dom/media/test/test_streams_individual_pause.html
+++ b/dom/media/test/test_streams_individual_pause.html
@@ -25,11 +25,9 @@ var getVideoImagePixelData = function(v) {
 
 // This test does not appear to work with the "Dummy video source" provided on
 // linux through the "media.video_loopback_dev" pref in the tree test environment.
-// To force the built-in fake streams to always be used instead, we specify
-// fakeTracks, a feature solely of the built-in fake streams (even though we
-// don't use the extra tracks).
+// We force a stream always by requesting `fake: true` here.
 
-navigator.mozGetUserMedia({video: true, fake: true, fakeTracks: true },
+navigator.mozGetUserMedia({video: true, fake: true },
                           function(stream) {
   var stream = stream;
   var video1 = document.getElementById('video1');
diff --git a/dom/media/test/test_streams_tracks.html b/dom/media/test/test_streams_tracks.html
index 3ece1d4f26..4157f25d58 100644
--- a/dom/media/test/test_streams_tracks.html
+++ b/dom/media/test/test_streams_tracks.html
@@ -1,4 +1,4 @@
-
+
 
 
   Test MediaStreamTrack interfaces
diff --git a/dom/media/tests/mochitest/head.js b/dom/media/tests/mochitest/head.js
index af2f23d37c..6b7aa809e6 100644
--- a/dom/media/tests/mochitest/head.js
+++ b/dom/media/tests/mochitest/head.js
@@ -9,12 +9,14 @@ var Ci = SpecialPowers.Ci;
 
 // Specifies whether we are using fake streams to run this automation
 var FAKE_ENABLED = true;
+var TEST_AUDIO_FREQ = 1000;
 try {
   var audioDevice = SpecialPowers.getCharPref('media.audio_loopback_dev');
   var videoDevice = SpecialPowers.getCharPref('media.video_loopback_dev');
   dump('TEST DEVICES: Using media devices:\n');
   dump('audio: ' + audioDevice + '\nvideo: ' + videoDevice + '\n');
   FAKE_ENABLED = false;
+  TEST_AUDIO_FREQ = 440;
 } catch (e) {
   dump('TEST DEVICES: No test devices found (in media.{audio,video}_loopback_dev, using fake streams.\n');
   FAKE_ENABLED = true;
@@ -78,6 +80,17 @@ AudioStreamAnalyser.prototype = {
     requestAnimationFrame(render);
   },
 
+  /**
+   * Disconnects the input stream from our internal analyser node.
+   * Call this to reduce main thread processing, mostly necessary on slow
+   * devices.
+   */
+  disconnect: function() {
+    this.disableDebugCanvas();
+    this.sourceNodes.forEach(n => n.disconnect());
+    this.sourceNodes = [];
+  },
+
   /**
    * Return a Promise, that will be resolved when the function passed as
    * argument, when called, returns true (meaning the analysis was a
@@ -319,6 +332,34 @@ function checkMediaStreamTracks(constraints, mediaStream) {
     mediaStream.getVideoTracks());
 }
 
+function checkMediaStreamCloneAgainstOriginal(clone, original) {
+  isnot(clone.id.length, 0, "Stream clone should have an id string");
+  isnot(clone, original,
+        "Stream clone should be different from the original");
+  isnot(clone.id, original.id,
+        "Stream clone's id should be different from the original's");
+  is(clone.getAudioTracks().length, original.getAudioTracks().length,
+     "All audio tracks should get cloned");
+  is(clone.getVideoTracks().length, original.getVideoTracks().length,
+     "All video tracks should get cloned");
+  original.getTracks()
+          .forEach(t => ok(!clone.getTracks().includes(t),
+                           "The clone's tracks should be originals"));
+}
+
+function checkMediaStreamTrackCloneAgainstOriginal(clone, original) {
+  isnot(clone.id.length, 0,
+        "Track clone should have an id string");
+  isnot(clone, original,
+        "Track clone should be different from the original");
+  isnot(clone.id, original.id,
+        "Track clone's id should be different from the original's");
+  is(clone.kind, original.kind,
+     "Track clone's kind should be same as the original's");
+  is(clone.enabled, original.enabled,
+     "Track clone's kind should be same as the original's");
+}
+
 /*** Utility methods */
 
 /** The dreadful setTimeout, use sparingly */
diff --git a/dom/media/tests/mochitest/mediaStreamPlayback.js b/dom/media/tests/mochitest/mediaStreamPlayback.js
index 058b89b7e9..5a162be631 100644
--- a/dom/media/tests/mochitest/mediaStreamPlayback.js
+++ b/dom/media/tests/mochitest/mediaStreamPlayback.js
@@ -2,13 +2,12 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-const TIMEUPDATE_TIMEOUT_LENGTH = 10000;
 const ENDED_TIMEOUT_LENGTH = 30000;
 
-/* Time we wait for the canplaythrough event to fire
+/* The time we wait depends primarily on the canplaythrough event firing
  * Note: this needs to be at least 30s because the
  *       B2G emulator in VMs is really slow. */
-const CANPLAYTHROUGH_TIMEOUT_LENGTH = 60000;
+const VERIFYPLAYING_TIMEOUT_LENGTH = 60000;
 
 /**
  * This class manages playback of a HTMLMediaElement with a MediaStream.
@@ -26,19 +25,59 @@ function MediaStreamPlayback(mediaElement, mediaStream) {
 
 MediaStreamPlayback.prototype = {
 
+   /**
+   * Starts media element with a media stream, runs it until a canplaythrough
+   * and timeupdate event fires, and calls stop() on all its tracks.
+   *
+   * @param {Boolean} isResume specifies if this media element is being resumed
+   *                           from a previous run
+   */
+  playMediaWithMediaStreamTracksStop : function(isResume) {
+    this.startMedia(isResume);
+    return this.verifyPlaying()
+      .then(() => this.stopTracksForStreamInMediaPlayback())
+      .then(() => this.stopMediaElement());
+  },
+
+  /**
+   * Stops the local media stream's tracks while it's currently in playback in
+   * a media element.
+   *
+   * Precondition: The media stream and element should both be actively
+   *               being played. All the stream's tracks must be local.
+   */
+  stopTracksForStreamInMediaPlayback : function () {
+    var elem = this.mediaElement;
+    var waitForEnded = () => new Promise(resolve => {
+      elem.addEventListener('ended', function ended() {
+        elem.removeEventListener('ended', ended);
+        resolve();
+      });
+    });
+
+    // TODO (bug 910249) Also check that all the tracks are local.
+    this.mediaStream.getTracks().forEach(t => t.stop());
+
+    // XXX (bug 1208316) When we implement MediaStream.active, do not stop
+    // the stream. We just do it now so the media element will raise 'ended'.
+    if (!this.mediaStream.stop) {
+      return;
+    }
+    this.mediaStream.stop();
+    return timeout(waitForEnded(), ENDED_TIMEOUT_LENGTH, "ended event never fired")
+             .then(() => ok(true, "ended event successfully fired"));
+  },
+
   /**
    * Starts media with a media stream, runs it until a canplaythrough and
    * timeupdate event fires, and stops the media.
    *
    * @param {Boolean} isResume specifies if this media element is being resumed
    *                           from a previous run
-   * @param {Function} onSuccess the success callback if the media playback
-   *                             start and stop cycle completes successfully
-   * @param {Function} onError the error callback if the media playback
-   *                           start and stop cycle fails
    */
   playMedia : function(isResume) {
-    return this.startMedia(isResume)
+    this.startMedia(isResume);
+    return this.verifyPlaying()
       .then(() => this.stopMediaElement());
   },
 
@@ -49,27 +88,34 @@ MediaStreamPlayback.prototype = {
    *                           is being resumed from a previous run
    */
   startMedia : function(isResume) {
-    var canPlayThroughFired = false;
 
-    // If we're initially running this media, check that the time is zero
+    // If we're playing media element for the first time, check that time is zero.
     if (!isResume) {
-      is(this.mediaStream.currentTime, 0,
+      is(this.mediaElement.currentTime, 0,
          "Before starting the media element, currentTime = 0");
     }
+    this.canPlayThroughFired = listenUntil(this.mediaElement, 'canplaythrough',
+                                           () => true);
 
-    return new Promise((resolve, reject) => {
-      /**
-       * Callback fired when the canplaythrough event is fired. We only
-       * run the logic of this function once, as this event can fire
-       * multiple times while a HTMLMediaStream is playing content from
-       * a real-time MediaStream.
-       */
-      var canPlayThroughCallback = () => {
-        // Disable the canplaythrough event listener to prevent multiple calls
-        canPlayThroughFired = true;
-        this.mediaElement.removeEventListener('canplaythrough',
-                                              canPlayThroughCallback, false);
+    // Hooks up the media stream to the media element and starts playing it
+    this.mediaElement.srcObject = this.mediaStream;
+    this.mediaElement.play();
+  },
 
+  /**
+   * Verifies that media is playing.
+   */
+  verifyPlaying : function() {
+    var lastStreamTime = this.mediaStream.currentTime;
+    var lastElementTime = this.mediaElement.currentTime;
+
+    var mediaTimeProgressed = listenUntil(this.mediaElement, 'timeupdate',
+        () => this.mediaStream.currentTime > lastStreamTime &&
+              this.mediaElement.currentTime > lastElementTime);
+
+    return timeout(Promise.all([this.canPlayThroughFired, mediaTimeProgressed]),
+                   VERIFYPLAYING_TIMEOUT_LENGTH, "verifyPlaying timed out")
+      .then(() => {
         is(this.mediaElement.paused, false,
            "Media element should be playing");
         is(this.mediaElement.duration, Number.POSITIVE_INFINITY,
@@ -96,45 +142,7 @@ MediaStreamPlayback.prototype = {
         is(this.mediaElement.src, "", "No src should be defined");
         is(this.mediaElement.currentSrc, "",
            "Current src should still be an empty string");
-
-        var timeUpdateCallback = () => {
-          if (this.mediaStream.currentTime > 0 &&
-              this.mediaElement.currentTime > 0) {
-            this.mediaElement.removeEventListener('timeupdate',
-                                                  timeUpdateCallback, false);
-            resolve();
-          }
-        };
-
-        // When timeupdate fires, we validate time has passed and move
-        // onto the success condition
-        this.mediaElement.addEventListener('timeupdate', timeUpdateCallback,
-                                           false);
-
-        // If timeupdate doesn't fire in enough time, we fail the test
-        setTimeout(() => {
-          this.mediaElement.removeEventListener('timeupdate',
-                                                timeUpdateCallback, false);
-          reject(new Error("timeUpdate event never fired"));
-        }, TIMEUPDATE_TIMEOUT_LENGTH);
-      };
-
-      // Adds a listener intended to be fired when playback is available
-      // without further buffering.
-      this.mediaElement.addEventListener('canplaythrough', canPlayThroughCallback,
-                                         false);
-
-      // Hooks up the media stream to the media element and starts playing it
-      this.mediaElement.srcObject = this.mediaStream;
-      this.mediaElement.play();
-
-      // If canplaythrough doesn't fire in enough time, we fail the test
-      setTimeout(() => {
-        this.mediaElement.removeEventListener('canplaythrough',
-                                              canPlayThroughCallback, false);
-        reject(new Error("canplaythrough event never fired"));
-      }, CANPLAYTHROUGH_TIMEOUT_LENGTH);
-    });
+      });
   },
 
   /**
@@ -167,6 +175,8 @@ function LocalMediaStreamPlayback(mediaElement, mediaStream) {
 LocalMediaStreamPlayback.prototype = Object.create(MediaStreamPlayback.prototype, {
 
   /**
+   * DEPRECATED - MediaStream.stop() is going away. Use MediaStreamTrack.stop()!
+   *
    * Starts media with a media stream, runs it until a canplaythrough and
    * timeupdate event fires, and calls stop() on the stream.
    *
@@ -175,13 +185,16 @@ LocalMediaStreamPlayback.prototype = Object.create(MediaStreamPlayback.prototype
    */
   playMediaWithDeprecatedStreamStop : {
     value: function(isResume) {
-      return this.startMedia(isResume)
+      this.startMedia(isResume);
+      return this.verifyPlaying()
         .then(() => this.deprecatedStopStreamInMediaPlayback())
         .then(() => this.stopMediaElement());
     }
   },
 
   /**
+   * DEPRECATED - MediaStream.stop() is going away. Use MediaStreamTrack.stop()!
+   *
    * Stops the local media stream while it's currently in playback in
    * a media element.
    *
diff --git a/dom/media/tests/mochitest/mochitest.ini b/dom/media/tests/mochitest/mochitest.ini
index 767189a500..880cc52599 100644
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -52,10 +52,15 @@ skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' # no
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' # no windowshare on b2g/android # Bug 1141029 Mulet parity with B2G Desktop for TC
 [test_getUserMedia_basicVideoAudio.html]
 skip-if = (toolkit == 'gonk' || buildapp == 'mulet' && debug) # debug-only failure, turned an intermittent (bug 962579) into a permanant orange
+[test_getUserMedia_bug1223696.html]
 [test_getUserMedia_constraints.html]
 [test_getUserMedia_callbacks.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' || toolkit == 'android' || buildapp == 'mulet' # Bug 1063290, intermittent timeout # TC: Bug 1144079 - Re-enable Mulet mochitests and reftests taskcluster-specific disables.
 [test_getUserMedia_gumWithinGum.html]
+[test_getUserMedia_loadedmetadata.html]
+[test_getUserMedia_mediaStreamClone.html]
+[test_getUserMedia_mediaStreamConstructors.html]
+[test_getUserMedia_mediaStreamTrackClone.html]
 [test_getUserMedia_playAudioTwice.html]
 [test_getUserMedia_playVideoAudioTwice.html]
 [test_getUserMedia_playVideoTwice.html]
@@ -113,6 +118,8 @@ skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video suppo
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_captureStream_canvas_2d.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
+[test_peerConnection_multiple_captureStream_canvas_2d.html]
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18' && debug) # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_captureStream_canvas_webgl.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_close.html]
@@ -157,7 +164,10 @@ skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video suppo
 [test_peerConnection_throwInCallbacks.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_toJSON.html]
-
+[test_peerConnection_trackDisabling_clones.html]
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18' && debug) # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator)
+[test_peerConnection_trackDisabling.html]
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_twoAudioStreams.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_twoAudioTracksInOneStream.html]
@@ -198,6 +208,8 @@ skip-if = toolkit == 'gonk' # B2G emulator is too slow to finish a renegotiation
 skip-if = toolkit == 'gonk' # B2G emulator seems to be so slow that DTLS cannot establish properly
 [test_peerConnection_addDataChannelNoBundle.html]
 skip-if = toolkit == 'gonk' # B2G emulator seems to be so slow that DTLS cannot establish properly
+[test_peerConnection_verifyAudioAfterRenegotiation.html]
+skip-if = toolkit == 'gonk' || (android_version == '18' && debug) # B2G emulator is too slow to finish a renegotiation test in under 5 minutes, android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_webAudio.html]
 tags = webaudio webrtc
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
diff --git a/dom/media/tests/mochitest/test_getUserMedia_addTrackRemoveTrack.html b/dom/media/tests/mochitest/test_getUserMedia_addTrackRemoveTrack.html
new file mode 100644
index 0000000000..9f55b835cd
--- /dev/null
+++ b/dom/media/tests/mochitest/test_getUserMedia_addTrackRemoveTrack.html
@@ -0,0 +1,161 @@
+
+
+
+  
+
+
+
+
+
+ + diff --git a/dom/media/tests/mochitest/test_getUserMedia_bug1223696.html b/dom/media/tests/mochitest/test_getUserMedia_bug1223696.html new file mode 100644 index 0000000000..55b7b02d84 --- /dev/null +++ b/dom/media/tests/mochitest/test_getUserMedia_bug1223696.html @@ -0,0 +1,48 @@ + + + + + + + +
+
+
+ + diff --git a/dom/media/tests/mochitest/test_getUserMedia_loadedmetadata.html b/dom/media/tests/mochitest/test_getUserMedia_loadedmetadata.html new file mode 100644 index 0000000000..3af0ea5ba1 --- /dev/null +++ b/dom/media/tests/mochitest/test_getUserMedia_loadedmetadata.html @@ -0,0 +1,36 @@ + + + + + + +
+
+
+ + diff --git a/dom/media/tests/mochitest/test_getUserMedia_mediaStreamClone.html b/dom/media/tests/mochitest/test_getUserMedia_mediaStreamClone.html new file mode 100644 index 0000000000..217b182d86 --- /dev/null +++ b/dom/media/tests/mochitest/test_getUserMedia_mediaStreamClone.html @@ -0,0 +1,249 @@ + + + + + + +
+
+
+ + diff --git a/dom/media/tests/mochitest/test_getUserMedia_mediaStreamConstructors.html b/dom/media/tests/mochitest/test_getUserMedia_mediaStreamConstructors.html new file mode 100644 index 0000000000..163712a415 --- /dev/null +++ b/dom/media/tests/mochitest/test_getUserMedia_mediaStreamConstructors.html @@ -0,0 +1,137 @@ + + + + + + +
+
+
+ + diff --git a/dom/media/tests/mochitest/test_getUserMedia_mediaStreamTrackClone.html b/dom/media/tests/mochitest/test_getUserMedia_mediaStreamTrackClone.html new file mode 100644 index 0000000000..117c63549b --- /dev/null +++ b/dom/media/tests/mochitest/test_getUserMedia_mediaStreamTrackClone.html @@ -0,0 +1,163 @@ + + + + + + +
+
+
+ + diff --git a/dom/media/tests/mochitest/test_peerConnection_captureStream_canvas_2d.html b/dom/media/tests/mochitest/test_peerConnection_captureStream_canvas_2d.html index cf82d4e28a..624d641d11 100644 --- a/dom/media/tests/mochitest/test_peerConnection_captureStream_canvas_2d.html +++ b/dom/media/tests/mochitest/test_peerConnection_captureStream_canvas_2d.html @@ -39,7 +39,8 @@ runNetworkTest(() => { ok(!!vremote, "Should have remote video element for pcRemote"); }, function WAIT_FOR_REMOTE_GREEN() { - return h.waitForPixel(vremote, h.green, 128, "pcRemote's remote should become green"); + return h.waitForPixelColor(vremote, h.green, 128, + "pcRemote's remote should become green"); }, function DRAW_LOCAL_RED() { // After requesting a frame it will be captured at the time of next render. @@ -49,7 +50,8 @@ runNetworkTest(() => { h.drawColor(canvas, h.red); }, function WAIT_FOR_REMOTE_RED() { - return h.waitForPixel(vremote, h.red, 128, "pcRemote's remote should become red"); + return h.waitForPixelColor(vremote, h.red, 128, + "pcRemote's remote should become red"); } ]); test.run(); diff --git a/dom/media/tests/mochitest/test_peerConnection_captureStream_canvas_webgl.html b/dom/media/tests/mochitest/test_peerConnection_captureStream_canvas_webgl.html index 3b9377563d..02e4018d45 100644 --- a/dom/media/tests/mochitest/test_peerConnection_captureStream_canvas_webgl.html +++ b/dom/media/tests/mochitest/test_peerConnection_captureStream_canvas_webgl.html @@ -89,7 +89,8 @@ runNetworkTest(() => { ok(!!vremote, "Should have remote video element for pcRemote"); }, function WAIT_FOR_REMOTE_GREEN() { - return h.waitForPixel(vremote, h.green, 128, "pcRemote's remote should become green"); + return h.waitForPixelColor(vremote, h.green, 128, + "pcRemote's remote should become green"); }, function REQUEST_FRAME(test) { // After requesting a frame it will be captured at the time of next render. @@ -101,7 +102,8 @@ runNetworkTest(() => { h.drawColor(canvas, h.red); }, function WAIT_FOR_REMOTE_RED() { - return h.waitForPixel(vremote, h.red, 128, "pcRemote's remote should become red"); + return h.waitForPixelColor(vremote, h.red, 128, + "pcRemote's remote should become red"); } ]); test.run(); diff --git a/dom/media/tests/mochitest/test_peerConnection_multiple_captureStream_canvas_2d.html b/dom/media/tests/mochitest/test_peerConnection_multiple_captureStream_canvas_2d.html new file mode 100644 index 0000000000..0a1c8feee1 --- /dev/null +++ b/dom/media/tests/mochitest/test_peerConnection_multiple_captureStream_canvas_2d.html @@ -0,0 +1,100 @@ + + + + + + + +
+
+
+ + diff --git a/dom/media/tests/mochitest/test_peerConnection_trackDisabling.html b/dom/media/tests/mochitest/test_peerConnection_trackDisabling.html new file mode 100644 index 0000000000..50cfefa536 --- /dev/null +++ b/dom/media/tests/mochitest/test_peerConnection_trackDisabling.html @@ -0,0 +1,96 @@ + + + + + + + +
+
+
+ + diff --git a/dom/media/tests/mochitest/test_peerConnection_trackDisabling_clones.html b/dom/media/tests/mochitest/test_peerConnection_trackDisabling_clones.html new file mode 100644 index 0000000000..2111186b1c --- /dev/null +++ b/dom/media/tests/mochitest/test_peerConnection_trackDisabling_clones.html @@ -0,0 +1,147 @@ + + + + + + + +
+
+
+ + diff --git a/dom/media/tests/mochitest/test_peerConnection_verifyAudioAfterRenegotiation.html b/dom/media/tests/mochitest/test_peerConnection_verifyAudioAfterRenegotiation.html new file mode 100644 index 0000000000..7c1d7fd935 --- /dev/null +++ b/dom/media/tests/mochitest/test_peerConnection_verifyAudioAfterRenegotiation.html @@ -0,0 +1,123 @@ + + + + + + +
+
+
+ + diff --git a/dom/media/webaudio/AudioNode.cpp b/dom/media/webaudio/AudioNode.cpp index aa2c94902f..cd5cfffdf4 100644 --- a/dom/media/webaudio/AudioNode.cpp +++ b/dom/media/webaudio/AudioNode.cpp @@ -207,7 +207,7 @@ AudioNode::Connect(AudioNode& aDestination, uint32_t aOutput, MOZ_ASSERT(aInput <= UINT16_MAX, "Unexpected large input port number"); MOZ_ASSERT(aOutput <= UINT16_MAX, "Unexpected large output port number"); input->mStreamPort = destinationStream-> - AllocateInputPort(mStream, AudioNodeStream::AUDIO_TRACK, + AllocateInputPort(mStream, AudioNodeStream::AUDIO_TRACK, TRACK_ANY, static_cast(aInput), static_cast(aOutput)); } @@ -252,7 +252,7 @@ AudioNode::Connect(AudioParam& aDestination, uint32_t aOutput, // Setup our stream as an input to the AudioParam's stream MOZ_ASSERT(aOutput <= UINT16_MAX, "Unexpected large output port number"); input->mStreamPort = - ps->AllocateInputPort(mStream, AudioNodeStream::AUDIO_TRACK, + ps->AllocateInputPort(mStream, AudioNodeStream::AUDIO_TRACK, TRACK_ANY, 0, static_cast(aOutput)); } } diff --git a/dom/media/webaudio/MediaStreamAudioDestinationNode.cpp b/dom/media/webaudio/MediaStreamAudioDestinationNode.cpp index 1495b867fd..6b7b8358c9 100644 --- a/dom/media/webaudio/MediaStreamAudioDestinationNode.cpp +++ b/dom/media/webaudio/MediaStreamAudioDestinationNode.cpp @@ -10,6 +10,7 @@ #include "AudioNodeEngine.h" #include "AudioNodeStream.h" #include "DOMMediaStream.h" +#include "MediaStreamTrack.h" #include "TrackUnionStream.h" namespace mozilla { @@ -34,7 +35,12 @@ MediaStreamAudioDestinationNode::MediaStreamAudioDestinationNode(AudioContext* a aContext->Graph())) { // Ensure an audio track with the correct ID is exposed to JS - mDOMStream->CreateOwnDOMTrack(AudioNodeStream::AUDIO_TRACK, MediaSegment::AUDIO, nsString()); + nsIDocument* doc = aContext->GetParentObject()->GetExtantDoc(); + RefPtr source = + new BasicUnstoppableTrackSource(doc->NodePrincipal(), + MediaSourceEnum::AudioCapture); + mDOMStream->CreateDOMTrack(AudioNodeStream::AUDIO_TRACK, + MediaSegment::AUDIO, source); ProcessedMediaStream* outputStream = mDOMStream->GetInputStream()->AsProcessedStream(); MOZ_ASSERT(!!outputStream); @@ -42,11 +48,6 @@ MediaStreamAudioDestinationNode::MediaStreamAudioDestinationNode(AudioContext* a mStream = AudioNodeStream::Create(aContext, engine, AudioNodeStream::EXTERNAL_OUTPUT); mPort = outputStream->AllocateInputPort(mStream, AudioNodeStream::AUDIO_TRACK); - - nsIDocument* doc = aContext->GetParentObject()->GetExtantDoc(); - if (doc) { - mDOMStream->CombineWithPrincipal(doc->NodePrincipal()); - } } MediaStreamAudioDestinationNode::~MediaStreamAudioDestinationNode() diff --git a/dom/media/webaudio/MediaStreamAudioSourceNode.cpp b/dom/media/webaudio/MediaStreamAudioSourceNode.cpp index 834208eb7b..d5662aa0a8 100644 --- a/dom/media/webaudio/MediaStreamAudioSourceNode.cpp +++ b/dom/media/webaudio/MediaStreamAudioSourceNode.cpp @@ -18,11 +18,17 @@ namespace dom { NS_IMPL_CYCLE_COLLECTION_CLASS(MediaStreamAudioSourceNode) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MediaStreamAudioSourceNode) + if (tmp->mInputStream) { + tmp->mInputStream->UnregisterTrackListener(tmp); + } + tmp->DetachFromTrack(); NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputStream) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputTrack) NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(AudioNode) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MediaStreamAudioSourceNode, AudioNode) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputStream) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputTrack) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaStreamAudioSourceNode) @@ -68,53 +74,111 @@ MediaStreamAudioSourceNode::Init(DOMMediaStream* aMediaStream, ErrorResult& aRv) mInputStream = aMediaStream; AudioNodeEngine* engine = new MediaStreamAudioSourceNodeEngine(this); mStream = AudioNodeExternalInputStream::Create(graph, engine); - ProcessedMediaStream* outputStream = static_cast(mStream.get()); - mInputPort = outputStream->AllocateInputPort(aMediaStream->GetPlaybackStream()); mInputStream->AddConsumerToKeepAlive(static_cast(this)); - PrincipalChanged(mInputStream); // trigger enabling/disabling of the connector - mInputStream->AddPrincipalChangeObserver(this); + mInputStream->RegisterTrackListener(this); + AttachToFirstTrack(mInputStream); } -MediaStreamAudioSourceNode::~MediaStreamAudioSourceNode() +MediaStreamAudioSourceNode::~MediaStreamAudioSourceNode() {} + +void +MediaStreamAudioSourceNode::AttachToTrack(const RefPtr& aTrack) { - if (mInputStream) { - mInputStream->RemovePrincipalChangeObserver(this); + MOZ_ASSERT(!mInputTrack); + if (!mStream) { + return; + } + + mInputTrack = aTrack; + ProcessedMediaStream* outputStream = + static_cast(mStream.get()); + mInputPort = mInputTrack->ForwardTrackContentsTo(outputStream); + PrincipalChanged(mInputTrack); // trigger enabling/disabling of the connector + mInputTrack->AddPrincipalChangeObserver(this); +} + +void +MediaStreamAudioSourceNode::DetachFromTrack() +{ + if (mInputTrack) { + mInputTrack->RemovePrincipalChangeObserver(this); + mInputTrack = nullptr; + } + if (mInputPort) { + mInputPort->Destroy(); + mInputPort = nullptr; } } +void +MediaStreamAudioSourceNode::AttachToFirstTrack(const RefPtr& aMediaStream) +{ + nsTArray> tracks; + aMediaStream->GetAudioTracks(tracks); + + if (tracks.IsEmpty()) { + return; + } + + AttachToTrack(tracks[0]); +} + +void +MediaStreamAudioSourceNode::NotifyTrackAdded(const RefPtr& aTrack) +{ + if (mInputTrack) { + return; + } + + AttachToTrack(aTrack); +} + +void +MediaStreamAudioSourceNode::NotifyTrackRemoved(const RefPtr& aTrack) +{ + if (aTrack != mInputTrack) { + return; + } + + DetachFromTrack(); + AttachToFirstTrack(mInputStream); +} + /** - * Changes the principal. Note that this will be called on the main thread, but - * changes will be enacted on the MediaStreamGraph thread. If the principal + * Changes the principal. Note that this will be called on the main thread, but + * changes will be enacted on the MediaStreamGraph thread. If the principal * change results in the document principal losing access to the stream, then * there needs to be other measures in place to ensure that any media that is - * governed by the new stream principal is not available to the Media Stream - * Graph before this change completes. Otherwise, a site could get access to + * governed by the new stream principal is not available to the MediaStreamGraph + * before this change completes. Otherwise, a site could get access to * media that they are not authorized to receive. * * One solution is to block the altered content, call this method, then dispatch * another change request to the MediaStreamGraph thread that allows the content - * under the new principal to flow. This might be unnecessary if the principal + * under the new principal to flow. This might be unnecessary if the principal * change is changing to be the document principal. */ void -MediaStreamAudioSourceNode::PrincipalChanged(DOMMediaStream* aDOMMediaStream) +MediaStreamAudioSourceNode::PrincipalChanged(MediaStreamTrack* aMediaStreamTrack) { + MOZ_ASSERT(aMediaStreamTrack == mInputTrack); + bool subsumes = false; nsPIDOMWindow* parent = Context()->GetParentObject(); if (parent) { nsIDocument* doc = parent->GetExtantDoc(); if (doc) { nsIPrincipal* docPrincipal = doc->NodePrincipal(); - nsIPrincipal* streamPrincipal = mInputStream->GetPrincipal(); - if (!streamPrincipal || NS_FAILED(docPrincipal->Subsumes(streamPrincipal, &subsumes))) { + nsIPrincipal* trackPrincipal = aMediaStreamTrack->GetPrincipal(); + if (!trackPrincipal || NS_FAILED(docPrincipal->Subsumes(trackPrincipal, &subsumes))) { subsumes = false; } } } auto stream = static_cast(mStream.get()); stream->SetInt32Parameter(MediaStreamAudioSourceNodeEngine::ENABLE, - subsumes || aDOMMediaStream->GetCORSMode() != CORS_NONE); + subsumes || aMediaStreamTrack->GetCORSMode() != CORS_NONE); } size_t diff --git a/dom/media/webaudio/MediaStreamAudioSourceNode.h b/dom/media/webaudio/MediaStreamAudioSourceNode.h index c75fdb9e49..fc6049838e 100644 --- a/dom/media/webaudio/MediaStreamAudioSourceNode.h +++ b/dom/media/webaudio/MediaStreamAudioSourceNode.h @@ -41,7 +41,8 @@ private: }; class MediaStreamAudioSourceNode : public AudioNode, - public DOMMediaStream::PrincipalChangeObserver + public DOMMediaStream::TrackListener, + public PrincipalChangeObserver { public: static already_AddRefed @@ -64,7 +65,21 @@ public: size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override; size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override; - void PrincipalChanged(DOMMediaStream* aMediaStream) override; + // Attaches to aTrack so that its audio content will be used as input. + void AttachToTrack(const RefPtr& aTrack); + + // Detaches from the currently attached track if there is one. + void DetachFromTrack(); + + // Attaches to the first available audio track in aMediaStream. + void AttachToFirstTrack(const RefPtr& aMediaStream); + + // From DOMMediaStream::TrackListener. + void NotifyTrackAdded(const RefPtr& aTrack) override; + void NotifyTrackRemoved(const RefPtr& aTrack) override; + + // From PrincipalChangeObserver. + void PrincipalChanged(MediaStreamTrack* aMediaStreamTrack) override; protected: explicit MediaStreamAudioSourceNode(AudioContext* aContext); @@ -74,6 +89,9 @@ protected: private: RefPtr mInputPort; RefPtr mInputStream; + + // On construction we set this to the first audio track of mInputStream. + RefPtr mInputTrack; }; } // namespace dom diff --git a/dom/media/webaudio/test/test_bug867104.html b/dom/media/webaudio/test/test_bug867104.html index 04d0fa4af0..82852ba51b 100644 --- a/dom/media/webaudio/test/test_bug867104.html +++ b/dom/media/webaudio/test/test_bug867104.html @@ -15,7 +15,7 @@ addLoadEvent(function() { var source = ctx.createBufferSource(); var b0 = ctx.createBuffer(32,798,22050); var b1 = ctx.createBuffer(32,28,22050); - var sp = ctx.createScriptProcessor(); + var sp = ctx.createScriptProcessor(0, 2, 0); source.buffer = b0; source.connect(sp); source.start(0); diff --git a/dom/media/webaudio/test/test_delayNodeTailWithDisconnect.html b/dom/media/webaudio/test/test_delayNodeTailWithDisconnect.html index 0814cbd63c..fa431d61b2 100644 --- a/dom/media/webaudio/test/test_delayNodeTailWithDisconnect.html +++ b/dom/media/webaudio/test/test_delayNodeTailWithDisconnect.html @@ -63,8 +63,6 @@ function startTest() { var delayProcessor = ctx.createScriptProcessor(bufferSize, 1, 0); delayProcessor.onaudioprocess = onDelayOutput; - // Work around bug 916387. - delayProcessor.connect(ctx.destination); var delayDuration = delayLength / ctx.sampleRate; for (var i = 0; i < sourceCount; ++i) { diff --git a/dom/media/webaudio/test/test_delayNodeTailWithReconnect.html b/dom/media/webaudio/test/test_delayNodeTailWithReconnect.html index ede10a6c18..6c1cda5805 100644 --- a/dom/media/webaudio/test/test_delayNodeTailWithReconnect.html +++ b/dom/media/webaudio/test/test_delayNodeTailWithReconnect.html @@ -111,7 +111,6 @@ function startTest() { var processor1 = ctx.createScriptProcessor(bufferSize, 1, 0); delay.connect(processor1); processor1.onaudioprocess = onDelayOutput; - processor1.connect(ctx.destination); // work around bug 916387 // Signal to trigger initial tail time reference oscillator = ctx.createOscillator(); @@ -128,7 +127,6 @@ function startTest() { var processor2 = ctx.createScriptProcessor(bufferSize, 1, 0); source.connect(processor2); processor2.onaudioprocess = onSourceOutput; - processor2.connect(ctx.destination); // guard against bug 916387 }; startTest(); diff --git a/dom/media/webaudio/test/test_mediaElementAudioSourceNode.html b/dom/media/webaudio/test/test_mediaElementAudioSourceNode.html index bd9572a531..3e196735f7 100644 --- a/dom/media/webaudio/test/test_mediaElementAudioSourceNode.html +++ b/dom/media/webaudio/test/test_mediaElementAudioSourceNode.html @@ -14,9 +14,6 @@ SimpleTest.waitForExplicitFinish(); function test() { var audio = new Audio("small-shot.ogg"); var context = new AudioContext(); - var node = context.createMediaElementSource(audio); - var sp = context.createScriptProcessor(2048, 1); - node.connect(sp); var expectedMinNonzeroSampleCount; var expectedMaxNonzeroSampleCount; var nonzeroSampleCount = 0; @@ -58,7 +55,10 @@ function test() { } } - audio.oncanplaythrough = function() { + audio.onloadedmetadata = function() { + var node = context.createMediaElementSource(audio); + var sp = context.createScriptProcessor(2048, 1); + node.connect(sp); // Use a fuzz factor of 100 to account for samples that just happen to be zero expectedMinNonzeroSampleCount = Math.floor(audio.duration*context.sampleRate) - 100; expectedMaxNonzeroSampleCount = Math.floor(audio.duration*context.sampleRate) + 500; diff --git a/dom/media/webaudio/test/test_mediaElementAudioSourceNodeCrossOrigin.html b/dom/media/webaudio/test/test_mediaElementAudioSourceNodeCrossOrigin.html index 558a2d4404..7e03b7079c 100644 --- a/dom/media/webaudio/test/test_mediaElementAudioSourceNodeCrossOrigin.html +++ b/dom/media/webaudio/test/test_mediaElementAudioSourceNodeCrossOrigin.html @@ -35,26 +35,32 @@ var testsRemaining = tests.length; tests.forEach(function(e) { e.ac = new AudioContext(); var a = new Audio(); - a.loop = true; if (e.cors) { a.crossOrigin = e.cors; } a.src = e.url; - a.controls = true; - var measn = e.ac.createMediaElementSource(a); - var sp = e.ac.createScriptProcessor(2048, 1); - // Set a couple expandos to track the status of the test - sp.iterationsLeft = 200; - sp.seenSound = false; - - measn.connect(sp); - a.play(); document.body.appendChild(a); + a.onloadedmetadata = () => { + // Wait for "loadedmetadata" before capturing since tracks are then known + // directly. If we set up the capture before "loadedmetadata" we + // (internally) have to wait an extra async jump for tracks to become known + // to main thread, before setting up audio data forwarding to the node. + // As that happens, the audio resource may have already ended on slow test + // machines, causing failures. + a.onloadedmetadata = null; + var measn = e.ac.createMediaElementSource(a); + var sp = e.ac.createScriptProcessor(2048, 1); + sp.seenSound = false; + sp.onaudioprocess = checkBufferSilent; + + measn.connect(sp); + a.play(); + }; + function checkFinished(sp) { - if (--sp.iterationsLeft == 0) { + if (a.ended) { sp.onaudioprocess = null; - a.pause(); var not = e.expectSilence ? "" : "not"; is(e.expectSilence, !sp.seenSound, "Buffer is " + not + " silent as expected, for " + @@ -81,10 +87,6 @@ tests.forEach(function(e) { checkFinished(e.target); return silent; } - - a.onplaying = function () { - sp.onaudioprocess = checkBufferSilent; - } });
diff --git a/dom/media/webaudio/test/test_mediaStreamAudioSourceNodeResampling.html b/dom/media/webaudio/test/test_mediaStreamAudioSourceNodeResampling.html index d1f398cd81..4a4f03c53b 100644 --- a/dom/media/webaudio/test/test_mediaStreamAudioSourceNodeResampling.html +++ b/dom/media/webaudio/test/test_mediaStreamAudioSourceNodeResampling.html @@ -14,10 +14,6 @@ SimpleTest.waitForExplicitFinish(); function test() { var audio = new Audio("small-shot.ogg"); var context = new AudioContext(); - var node = context.createMediaStreamSource(audio.mozCaptureStreamUntilEnded()); - var sp = context.createScriptProcessor(2048, 1); - node.connect(sp); - sp.connect(context.destination); // work around bug 916387 var expectedMinNonzeroSampleCount; var expectedMaxNonzeroSampleCount; var nonzeroSampleCount = 0; @@ -59,7 +55,10 @@ function test() { } } - audio.oncanplaythrough = function() { + audio.onloadedmetadata = function() { + var node = context.createMediaStreamSource(audio.mozCaptureStreamUntilEnded()); + var sp = context.createScriptProcessor(2048, 1, 0); + node.connect(sp); // Use a fuzz factor of 100 to account for samples that just happen to be zero expectedMinNonzeroSampleCount = Math.floor(audio.duration*context.sampleRate) - 100; expectedMaxNonzeroSampleCount = Math.floor(audio.duration*context.sampleRate) + 500; diff --git a/dom/media/webaudio/test/test_oscillatorNodeStart.html b/dom/media/webaudio/test/test_oscillatorNodeStart.html index 6ce0eed3f8..c43219c993 100644 --- a/dom/media/webaudio/test/test_oscillatorNodeStart.html +++ b/dom/media/webaudio/test/test_oscillatorNodeStart.html @@ -14,11 +14,9 @@ addLoadEvent(function() { var context = new AudioContext(); var osc = context.createOscillator(); - var sp = context.createScriptProcessor(); + var sp = context.createScriptProcessor(0, 1, 0); osc.connect(sp); - // Work around bug 916387. - sp.connect(context.destination); sp.onaudioprocess = function (e) { var input = e.inputBuffer.getChannelData(0); diff --git a/dom/media/webaudio/test/webaudio.js b/dom/media/webaudio/test/webaudio.js index 4cf68a8f92..1a1a8efb74 100644 --- a/dom/media/webaudio/test/webaudio.js +++ b/dom/media/webaudio/test/webaudio.js @@ -206,9 +206,8 @@ function runTest() function testOnNormalContext(callback) { function testOutput(nodeToInspect, expectedBuffers, callback) { testLength = 0; - var sp = context.createScriptProcessor(expectedBuffers[0].length, gTest.numberOfChannels); + var sp = context.createScriptProcessor(expectedBuffers[0].length, gTest.numberOfChannels, 0); nodeToInspect.connect(sp); - sp.connect(context.destination); sp.onaudioprocess = function(e) { var expectedBuffer = expectedBuffers.shift(); testLength += expectedBuffer.length; diff --git a/dom/media/webrtc/MediaEngine.h b/dom/media/webrtc/MediaEngine.h index f7a3bc3125..4316d027bf 100644 --- a/dom/media/webrtc/MediaEngine.h +++ b/dom/media/webrtc/MediaEngine.h @@ -75,6 +75,25 @@ protected: virtual ~MediaEngine() {} }; +/** + * Callback interface for TakePhoto(). Either PhotoComplete() or PhotoError() + * should be called. + */ +class MediaEnginePhotoCallback { +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaEnginePhotoCallback) + + // aBlob is the image captured by MediaEngineSource. It is + // called on main thread. + virtual nsresult PhotoComplete(already_AddRefed aBlob) = 0; + + // It is called on main thread. aRv is the error code. + virtual nsresult PhotoError(nsresult aRv) = 0; + +protected: + virtual ~MediaEnginePhotoCallback() {} +}; + /** * Common abstract base class for audio and video sources. */ @@ -102,7 +121,7 @@ public: /* Start the device and add the track to the provided SourceMediaStream, with * the provided TrackID. You may start appending data to the track * immediately after. */ - virtual nsresult Start(SourceMediaStream*, TrackID) = 0; + virtual nsresult Start(SourceMediaStream*, TrackID, const PrincipalHandle&) = 0; /* tell the source if there are any direct listeners attached */ virtual void SetDirectListeners(bool) = 0; @@ -111,7 +130,8 @@ public: virtual void NotifyPull(MediaStreamGraph* aGraph, SourceMediaStream *aSource, TrackID aId, - StreamTime aDesiredTime) = 0; + StreamTime aDesiredTime, + const PrincipalHandle& aPrincipalHandle) = 0; /* Stop the device and release the corresponding MediaStream */ virtual nsresult Stop(SourceMediaStream *aSource, TrackID aID) = 0; @@ -129,28 +149,11 @@ public: /* Returns the type of media source (camera, microphone, screen, window, etc) */ virtual dom::MediaSourceEnum GetMediaSource() const = 0; - // Callback interface for TakePhoto(). Either PhotoComplete() or PhotoError() - // should be called. - class PhotoCallback { - public: - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PhotoCallback) - - // aBlob is the image captured by MediaEngineSource. It is - // called on main thread. - virtual nsresult PhotoComplete(already_AddRefed aBlob) = 0; - - // It is called on main thread. aRv is the error code. - virtual nsresult PhotoError(nsresult aRv) = 0; - - protected: - virtual ~PhotoCallback() {} - }; - /* If implementation of MediaEngineSource supports TakePhoto(), the picture * should be return via aCallback object. Otherwise, it returns NS_ERROR_NOT_IMPLEMENTED. * Currently, only Gonk MediaEngineSource implementation supports it. */ - virtual nsresult TakePhoto(PhotoCallback* aCallback) = 0; + virtual nsresult TakePhoto(MediaEnginePhotoCallback* aCallback) = 0; /* Return false if device is currently allocated or started */ bool IsAvailable() { @@ -219,6 +222,8 @@ public: , mNoise(0) , mPlayoutDelay(0) , mFullDuplex(false) + , mExtendedFilter(false) + , mDelayAgnostic(false) {} int32_t mWidth; @@ -234,6 +239,8 @@ public: int32_t mNoise; int32_t mPlayoutDelay; bool mFullDuplex; + bool mExtendedFilter; + bool mDelayAgnostic; // mWidth and/or mHeight may be zero (=adaptive default), so use functions. diff --git a/dom/media/webrtc/MediaEngineCameraVideoSource.cpp b/dom/media/webrtc/MediaEngineCameraVideoSource.cpp index 40c38f5699..f61aa8821a 100644 --- a/dom/media/webrtc/MediaEngineCameraVideoSource.cpp +++ b/dom/media/webrtc/MediaEngineCameraVideoSource.cpp @@ -19,14 +19,15 @@ extern LogModule* GetMediaManagerLog(); bool MediaEngineCameraVideoSource::AppendToTrack(SourceMediaStream* aSource, layers::Image* aImage, TrackID aID, - StreamTime delta) + StreamTime delta, + const PrincipalHandle& aPrincipalHandle) { MOZ_ASSERT(aSource); VideoSegment segment; RefPtr image = aImage; IntSize size(image ? mWidth : 0, image ? mHeight : 0); - segment.AppendFrame(image.forget(), delta, size); + segment.AppendFrame(image.forget(), delta, size, aPrincipalHandle); // This is safe from any thread, and is safe if the track is Finished // or Destroyed. diff --git a/dom/media/webrtc/MediaEngineCameraVideoSource.h b/dom/media/webrtc/MediaEngineCameraVideoSource.h index 4461d2b2d5..9d27653164 100644 --- a/dom/media/webrtc/MediaEngineCameraVideoSource.h +++ b/dom/media/webrtc/MediaEngineCameraVideoSource.h @@ -43,7 +43,7 @@ public: return false; } - nsresult TakePhoto(PhotoCallback* aCallback) override + nsresult TakePhoto(MediaEnginePhotoCallback* aCallback) override { return NS_ERROR_NOT_IMPLEMENTED; } @@ -70,7 +70,8 @@ protected: virtual bool AppendToTrack(SourceMediaStream* aSource, layers::Image* aImage, TrackID aID, - StreamTime delta); + StreamTime delta, + const PrincipalHandle& aPrincipalHandle); uint32_t GetFitnessDistance(const webrtc::CaptureCapability& aCandidate, const dom::MediaTrackConstraintSet &aConstraints, bool aAdvanced, @@ -95,12 +96,13 @@ protected: // mMonitor protects mImage access/changes, and transitions of mState // from kStarted to kStopped (which are combined with EndTrack() and // image changes). - // mMonitor also protects mSources[] access/changes. - // mSources[] is accessed from webrtc threads. + // mMonitor also protects mSources[] and mPrincipalHandles[] access/changes. + // mSources[] and mPrincipalHandles[] are accessed from webrtc threads. // All the mMonitor accesses are from the child classes. Monitor mMonitor; // Monitor for processing Camera frames. nsTArray> mSources; // When this goes empty, we shut down HW + nsTArray mPrincipalHandles; // Directly mapped to mSources. RefPtr mImage; RefPtr mImageContainer; int mWidth, mHeight; // protected with mMonitor on Gonk due to different threading diff --git a/dom/media/webrtc/MediaEngineDefault.cpp b/dom/media/webrtc/MediaEngineDefault.cpp index 589a2238f3..e4aefe11e8 100644 --- a/dom/media/webrtc/MediaEngineDefault.cpp +++ b/dom/media/webrtc/MediaEngineDefault.cpp @@ -147,7 +147,8 @@ static void ReleaseFrame(layers::PlanarYCbCrData& aData) } nsresult -MediaEngineDefaultVideoSource::Start(SourceMediaStream* aStream, TrackID aID) +MediaEngineDefaultVideoSource::Start(SourceMediaStream* aStream, TrackID aID, + const PrincipalHandle& aPrincipalHandle) { if (mState != kAllocated) { return NS_ERROR_FAILURE; @@ -274,7 +275,8 @@ void MediaEngineDefaultVideoSource::NotifyPull(MediaStreamGraph* aGraph, SourceMediaStream *aSource, TrackID aID, - StreamTime aDesiredTime) + StreamTime aDesiredTime, + const PrincipalHandle& aPrincipalHandle) { // AddTrack takes ownership of segment VideoSegment segment; @@ -290,7 +292,7 @@ MediaEngineDefaultVideoSource::NotifyPull(MediaStreamGraph* aGraph, if (delta > 0) { // nullptr images are allowed IntSize size(image ? mOpts.mWidth : 0, image ? mOpts.mHeight : 0); - segment.AppendFrame(image.forget(), delta, size); + segment.AppendFrame(image.forget(), delta, size, aPrincipalHandle); // This can fail if either a) we haven't added the track yet, or b) // we've removed or finished the track. aSource->AppendToTrack(aID, &segment); @@ -360,6 +362,7 @@ NS_IMPL_ISUPPORTS(MediaEngineDefaultAudioSource, nsITimerCallback) MediaEngineDefaultAudioSource::MediaEngineDefaultAudioSource() : MediaEngineAudioSource(kReleased) + , mPrincipalHandle(PRINCIPAL_HANDLE_NONE) , mTimer(nullptr) { } @@ -424,7 +427,8 @@ MediaEngineDefaultAudioSource::Deallocate() } nsresult -MediaEngineDefaultAudioSource::Start(SourceMediaStream* aStream, TrackID aID) +MediaEngineDefaultAudioSource::Start(SourceMediaStream* aStream, TrackID aID, + const PrincipalHandle& aPrincipalHandle) { if (mState != kAllocated) { return NS_ERROR_FAILURE; @@ -458,6 +462,9 @@ MediaEngineDefaultAudioSource::Start(SourceMediaStream* aStream, TrackID aID) // Remember TrackID so we can finish later mTrackID = aID; + // Remember PrincipalHandle since we don't append in NotifyPull. + mPrincipalHandle = aPrincipalHandle; + mLastNotify = TimeStamp::Now(); // 1 Audio frame per 10ms @@ -516,7 +523,7 @@ MediaEngineDefaultAudioSource::AppendToSegment(AudioSegment& aSegment, mSineGenerator->generate(dest, aSamples); AutoTArray channels; channels.AppendElement(dest); - aSegment.AppendFrames(buffer.forget(), channels, aSamples); + aSegment.AppendFrames(buffer.forget(), channels, aSamples, mPrincipalHandle); } NS_IMETHODIMP diff --git a/dom/media/webrtc/MediaEngineDefault.h b/dom/media/webrtc/MediaEngineDefault.h index cc3a25d339..97ee63907c 100644 --- a/dom/media/webrtc/MediaEngineDefault.h +++ b/dom/media/webrtc/MediaEngineDefault.h @@ -48,7 +48,7 @@ public: const nsString& aDeviceId, const nsACString& aOrigin) override; nsresult Deallocate() override; - nsresult Start(SourceMediaStream*, TrackID) override; + nsresult Start(SourceMediaStream*, TrackID, const PrincipalHandle&) override; nsresult Stop(SourceMediaStream*, TrackID) override; nsresult Restart(const dom::MediaTrackConstraints& aConstraints, const MediaEnginePrefs &aPrefs, @@ -57,7 +57,8 @@ public: void NotifyPull(MediaStreamGraph* aGraph, SourceMediaStream *aSource, TrackID aId, - StreamTime aDesiredTime) override; + StreamTime aDesiredTime, + const PrincipalHandle& aPrincipalHandle) override; uint32_t GetBestFitnessDistance( const nsTArray& aConstraintSets, const nsString& aDeviceId) override; @@ -70,7 +71,7 @@ public: return dom::MediaSourceEnum::Camera; } - nsresult TakePhoto(PhotoCallback* aCallback) override + nsresult TakePhoto(MediaEnginePhotoCallback* aCallback) override { return NS_ERROR_NOT_IMPLEMENTED; } @@ -118,17 +119,19 @@ public: const nsString& aDeviceId, const nsACString& aOrigin) override; nsresult Deallocate() override; - nsresult Start(SourceMediaStream*, TrackID) override; + nsresult Start(SourceMediaStream*, TrackID, const PrincipalHandle&) override; nsresult Stop(SourceMediaStream*, TrackID) override; nsresult Restart(const dom::MediaTrackConstraints& aConstraints, const MediaEnginePrefs &aPrefs, const nsString& aDeviceId) override; void SetDirectListeners(bool aHasDirectListeners) override {}; - void AppendToSegment(AudioSegment& aSegment, TrackTicks aSamples); + void AppendToSegment(AudioSegment& aSegment, + TrackTicks aSamples); void NotifyPull(MediaStreamGraph* aGraph, SourceMediaStream *aSource, TrackID aId, - StreamTime aDesiredTime) override + StreamTime aDesiredTime, + const PrincipalHandle& aPrincipalHandle) override { #ifdef DEBUG StreamBuffer::Track* data = aSource->FindTrack(aId); @@ -154,7 +157,7 @@ public: return dom::MediaSourceEnum::Microphone; } - nsresult TakePhoto(PhotoCallback* aCallback) override + nsresult TakePhoto(MediaEnginePhotoCallback* aCallback) override { return NS_ERROR_NOT_IMPLEMENTED; } @@ -170,6 +173,7 @@ protected: ~MediaEngineDefaultAudioSource(); TrackID mTrackID; + PrincipalHandle mPrincipalHandle; nsCOMPtr mTimer; TimeStamp mLastNotify; diff --git a/dom/media/webrtc/MediaEngineGonkVideoSource.cpp b/dom/media/webrtc/MediaEngineGonkVideoSource.cpp index b7525a842a..bb6b7cf1b2 100644 --- a/dom/media/webrtc/MediaEngineGonkVideoSource.cpp +++ b/dom/media/webrtc/MediaEngineGonkVideoSource.cpp @@ -67,7 +67,8 @@ void MediaEngineGonkVideoSource::NotifyPull(MediaStreamGraph* aGraph, SourceMediaStream* aSource, TrackID aID, - StreamTime aDesiredTime) + StreamTime aDesiredTime, + const PrincipalID& aPrincipalHandle) { VideoSegment segment; @@ -95,10 +96,10 @@ MediaEngineGonkVideoSource::NotifyPull(MediaStreamGraph* aGraph, if (delta > 0) { // nullptr images are allowed IntSize size(image ? mWidth : 0, image ? mHeight : 0); - segment.AppendFrame(image.forget(), delta, size); + segment.AppendFrame(image.forget(), delta, size, aPrincipalHandle); // This can fail if either a) we haven't added the track yet, or b) // we've removed or finished the track. - aSource->AppendToTrack(aID, &(segment)); + aSource->AppendToTrack(aID, &(segment), aPrincipalHandle); } } @@ -555,10 +556,11 @@ MediaEngineGonkVideoSource::OnUserError(UserContext aContext, nsresult aError) mCallbackMonitor.Notify(); } - // A main thread runnable to send error code to all queued PhotoCallbacks. + // A main thread runnable to send error code to all queued + // MediaEnginePhotoCallbacks. class TakePhotoError : public nsRunnable { public: - TakePhotoError(nsTArray>& aCallbacks, + TakePhotoError(nsTArray>& aCallbacks, nsresult aRv) : mRv(aRv) { @@ -571,13 +573,13 @@ MediaEngineGonkVideoSource::OnUserError(UserContext aContext, nsresult aError) for (uint8_t i = 0; i < callbackNumbers; i++) { mCallbacks[i]->PhotoError(mRv); } - // PhotoCallback needs to dereference on main thread. + // MediaEnginePhotoCallback needs to dereference on main thread. mCallbacks.Clear(); return NS_OK; } protected: - nsTArray> mCallbacks; + nsTArray> mCallbacks; nsresult mRv; }; @@ -597,10 +599,10 @@ MediaEngineGonkVideoSource::OnTakePictureComplete(const uint8_t* aData, uint32_t mCameraControl->StartPreview(); // Create a main thread runnable to generate a blob and call all current queued - // PhotoCallbacks. + // MediaEnginePhotoCallbacks. class GenerateBlobRunnable : public nsRunnable { public: - GenerateBlobRunnable(nsTArray>& aCallbacks, + GenerateBlobRunnable(nsTArray>& aCallbacks, const uint8_t* aData, uint32_t aLength, const nsAString& aMimeType) @@ -621,12 +623,12 @@ MediaEngineGonkVideoSource::OnTakePictureComplete(const uint8_t* aData, uint32_t RefPtr tempBlob = blob; mCallbacks[i]->PhotoComplete(tempBlob.forget()); } - // PhotoCallback needs to dereference on main thread. + // MediaEnginePhotoCallback needs to dereference on main thread. mCallbacks.Clear(); return NS_OK; } - nsTArray> mCallbacks; + nsTArray> mCallbacks; uint8_t* mPhotoData; nsString mMimeType; uint32_t mPhotoDataLength; @@ -634,7 +636,7 @@ MediaEngineGonkVideoSource::OnTakePictureComplete(const uint8_t* aData, uint32_t // All elements in mPhotoCallbacks will be swapped in GenerateBlobRunnable // constructor. This captured image will be sent to all the queued - // PhotoCallbacks in this runnable. + // MediaEnginePhotoCallbacks in this runnable. MonitorAutoLock lock(mMonitor); if (mPhotoCallbacks.Length()) { NS_DispatchToMainThread( @@ -643,7 +645,7 @@ MediaEngineGonkVideoSource::OnTakePictureComplete(const uint8_t* aData, uint32_t } nsresult -MediaEngineGonkVideoSource::TakePhoto(PhotoCallback* aCallback) +MediaEngineGonkVideoSource::TakePhoto(MediaEnginePhotoCallback* aCallback) { MOZ_ASSERT(NS_IsMainThread()); @@ -877,7 +879,7 @@ MediaEngineGonkVideoSource::OnNewMediaBufferFrame(MediaBuffer* aBuffer) // Unfortunately, clock in gonk camera looks like is a different one // comparing to MSG. As result, it causes time inaccurate. (frames be // queued in MSG longer and longer as time going by in device like Frame) - AppendToTrack(mSources[i], mImage, mTrackID, 1); + AppendToTrack(mSources[i], mImage, mTrackID, 1, mPrincipalHandles[i]); } } if (mImage->AsGrallocImage()) { diff --git a/dom/media/webrtc/MediaEngineGonkVideoSource.h b/dom/media/webrtc/MediaEngineGonkVideoSource.h index 203e6d82e1..700cc3054a 100644 --- a/dom/media/webrtc/MediaEngineGonkVideoSource.h +++ b/dom/media/webrtc/MediaEngineGonkVideoSource.h @@ -91,7 +91,7 @@ public: void RotateImage(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight); void Notify(const mozilla::hal::ScreenConfiguration& aConfiguration); - nsresult TakePhoto(PhotoCallback* aCallback) override; + nsresult TakePhoto(MediaEnginePhotoCallback* aCallback) override; // It sets the correct photo orientation via camera parameter according to // current screen orientation. @@ -124,7 +124,7 @@ protected: android::sp mCameraSource; // These are protected by mMonitor in parent class - nsTArray> mPhotoCallbacks; + nsTArray> mPhotoCallbacks; int mRotation; int mCameraAngle; // See dom/base/ScreenOrientation.h bool mBackCamera; diff --git a/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp b/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp index 54a3710f09..5a8e4dffa5 100644 --- a/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp +++ b/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp @@ -77,6 +77,7 @@ MediaEngineRemoteVideoSource::Shutdown() MonitorAutoLock lock(mMonitor); empty = mSources.IsEmpty(); if (empty) { + MOZ_ASSERT(mPrincipalHandles.IsEmpty()); break; } source = mSources[0]; @@ -130,6 +131,7 @@ MediaEngineRemoteVideoSource::Allocate(const dom::MediaTrackConstraints& aConstr } else if (MOZ_LOG_TEST(GetMediaManagerLog(), mozilla::LogLevel::Debug)) { MonitorAutoLock lock(mMonitor); if (mSources.IsEmpty()) { + MOZ_ASSERT(mPrincipalHandles.IsEmpty()); LOG(("Video device %d reallocated", mCaptureIndex)); } else { LOG(("Video device %d allocated shared", mCaptureIndex)); @@ -166,7 +168,8 @@ MediaEngineRemoteVideoSource::Deallocate() } nsresult -MediaEngineRemoteVideoSource::Start(SourceMediaStream* aStream, TrackID aID) +MediaEngineRemoteVideoSource::Start(SourceMediaStream* aStream, TrackID aID, + const PrincipalHandle& aPrincipalHandle) { LOG((__PRETTY_FUNCTION__)); AssertIsOnOwningThread(); @@ -178,6 +181,8 @@ MediaEngineRemoteVideoSource::Start(SourceMediaStream* aStream, TrackID aID) { MonitorAutoLock lock(mMonitor); mSources.AppendElement(aStream); + mPrincipalHandles.AppendElement(aPrincipalHandle); + MOZ_ASSERT(mSources.Length() == mPrincipalHandles.Length()); } aStream->AddTrack(aID, 0, new VideoSegment(), SourceMediaStream::ADDTRACK_QUEUED); @@ -209,11 +214,16 @@ MediaEngineRemoteVideoSource::Stop(mozilla::SourceMediaStream* aSource, { MonitorAutoLock lock(mMonitor); - if (!mSources.RemoveElement(aSource)) { + size_t i = mSources.IndexOf(aSource); + if (i == mSources.NoIndex) { // Already stopped - this is allowed return NS_OK; } + MOZ_ASSERT(mSources.Length() == mPrincipalHandles.Length()); + mSources.RemoveElementAt(i); + mPrincipalHandles.RemoveElementAt(i); + aSource->EndTrack(aID); if (!mSources.IsEmpty()) { @@ -268,7 +278,8 @@ MediaEngineRemoteVideoSource::Restart(const dom::MediaTrackConstraints& aConstra void MediaEngineRemoteVideoSource::NotifyPull(MediaStreamGraph* aGraph, SourceMediaStream* aSource, - TrackID aID, StreamTime aDesiredTime) + TrackID aID, StreamTime aDesiredTime, + const PrincipalHandle& aPrincipalHandle) { VideoSegment segment; @@ -277,7 +288,7 @@ MediaEngineRemoteVideoSource::NotifyPull(MediaStreamGraph* aGraph, if (delta > 0) { // nullptr images are allowed - AppendToTrack(aSource, mImage, aID, delta); + AppendToTrack(aSource, mImage, aID, delta, aPrincipalHandle); } } @@ -361,7 +372,8 @@ MediaEngineRemoteVideoSource::DeliverFrame(unsigned char* buffer, uint32_t len = mSources.Length(); for (uint32_t i = 0; i < len; i++) { if (mSources[i]) { - AppendToTrack(mSources[i], mImage, mTrackID, 1); // shortest possible duration + // shortest possible duration + AppendToTrack(mSources[i], mImage, mTrackID, 1, mPrincipalHandles[i]); } } diff --git a/dom/media/webrtc/MediaEngineRemoteVideoSource.h b/dom/media/webrtc/MediaEngineRemoteVideoSource.h index a0f5a9b6fa..d7776b619f 100644 --- a/dom/media/webrtc/MediaEngineRemoteVideoSource.h +++ b/dom/media/webrtc/MediaEngineRemoteVideoSource.h @@ -76,7 +76,7 @@ public: const nsString& aDeviceId, const nsACString& aOrigin) override; nsresult Deallocate() override;; - nsresult Start(SourceMediaStream*, TrackID) override; + nsresult Start(SourceMediaStream*, TrackID, const PrincipalHandle&) override; nsresult Stop(SourceMediaStream*, TrackID) override; nsresult Restart(const dom::MediaTrackConstraints& aConstraints, const MediaEnginePrefs &aPrefs, @@ -84,7 +84,8 @@ public: void NotifyPull(MediaStreamGraph* aGraph, SourceMediaStream* aSource, TrackID aId, - StreamTime aDesiredTime) override; + StreamTime aDesiredTime, + const PrincipalHandle& aPrincipalHandle) override; dom::MediaSourceEnum GetMediaSource() const override { return mMediaSource; } diff --git a/dom/media/webrtc/MediaEngineTabVideoSource.cpp b/dom/media/webrtc/MediaEngineTabVideoSource.cpp index 0264dd54a6..8980fe782c 100644 --- a/dom/media/webrtc/MediaEngineTabVideoSource.cpp +++ b/dom/media/webrtc/MediaEngineTabVideoSource.cpp @@ -175,7 +175,8 @@ MediaEngineTabVideoSource::Deallocate() } nsresult -MediaEngineTabVideoSource::Start(SourceMediaStream* aStream, TrackID aID) +MediaEngineTabVideoSource::Start(SourceMediaStream* aStream, TrackID aID, + const PrincipalHandle& aPrincipalHandle) { nsCOMPtr runnable; if (!mWindow) @@ -191,7 +192,8 @@ MediaEngineTabVideoSource::Start(SourceMediaStream* aStream, TrackID aID) void MediaEngineTabVideoSource::NotifyPull(MediaStreamGraph*, SourceMediaStream* aSource, - TrackID aID, StreamTime aDesiredTime) + TrackID aID, StreamTime aDesiredTime, + const PrincipalHandle& aPrincipalHandle) { VideoSegment segment; MonitorAutoLock mon(mMonitor); @@ -202,7 +204,8 @@ MediaEngineTabVideoSource::NotifyPull(MediaStreamGraph*, if (delta > 0) { // nullptr images are allowed gfx::IntSize size = image ? image->GetSize() : IntSize(0, 0); - segment.AppendFrame(image.forget().downcast(), delta, size); + segment.AppendFrame(image.forget().downcast(), delta, size, + aPrincipalHandle); // This can fail if either a) we haven't added the track yet, or b) // we've removed or finished the track. aSource->AppendToTrack(aID, &(segment)); diff --git a/dom/media/webrtc/MediaEngineTabVideoSource.h b/dom/media/webrtc/MediaEngineTabVideoSource.h index b9ad22de8d..1e92253ab7 100644 --- a/dom/media/webrtc/MediaEngineTabVideoSource.h +++ b/dom/media/webrtc/MediaEngineTabVideoSource.h @@ -26,9 +26,9 @@ class MediaEngineTabVideoSource : public MediaEngineVideoSource, nsIDOMEventList const nsString& aDeviceId, const nsACString& aOrigin) override; nsresult Deallocate() override; - nsresult Start(mozilla::SourceMediaStream*, mozilla::TrackID) override; + nsresult Start(mozilla::SourceMediaStream*, mozilla::TrackID, const mozilla::PrincipalHandle&) override; void SetDirectListeners(bool aHasDirectListeners) override {}; - void NotifyPull(mozilla::MediaStreamGraph*, mozilla::SourceMediaStream*, mozilla::TrackID, mozilla::StreamTime) override; + void NotifyPull(mozilla::MediaStreamGraph*, mozilla::SourceMediaStream*, mozilla::TrackID, mozilla::StreamTime, const mozilla::PrincipalHandle& aPrincipalHandle) override; nsresult Stop(mozilla::SourceMediaStream*, mozilla::TrackID) override; nsresult Restart(const dom::MediaTrackConstraints& aConstraints, const mozilla::MediaEnginePrefs& aPrefs, @@ -44,7 +44,7 @@ class MediaEngineTabVideoSource : public MediaEngineVideoSource, nsIDOMEventList return 0; } - nsresult TakePhoto(PhotoCallback* aCallback) override + nsresult TakePhoto(MediaEnginePhotoCallback* aCallback) override { return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/dom/media/webrtc/MediaEngineWebRTC.cpp b/dom/media/webrtc/MediaEngineWebRTC.cpp index 5de97a9b1b..d4f1090ea8 100644 --- a/dom/media/webrtc/MediaEngineWebRTC.cpp +++ b/dom/media/webrtc/MediaEngineWebRTC.cpp @@ -55,7 +55,9 @@ MediaEngineWebRTC::MediaEngineWebRTC(MediaEnginePrefs &aPrefs) mVoiceEngine(nullptr), mAudioInput(nullptr), mAudioEngineInit(false), - mFullDuplex(aPrefs.mFullDuplex) + mFullDuplex(aPrefs.mFullDuplex), + mExtendedFilter(aPrefs.mExtendedFilter), + mDelayAgnostic(aPrefs.mDelayAgnostic) { #ifndef MOZ_B2G_CAMERA nsCOMPtr compMgr; @@ -272,7 +274,10 @@ MediaEngineWebRTC::EnumerateAudioDevices(dom::MediaSourceEnum aMediaSource, #endif if (!mVoiceEngine) { - mVoiceEngine = webrtc::VoiceEngine::Create(); + mConfig.Set(new webrtc::ExtendedFilter(mExtendedFilter)); + mConfig.Set(new webrtc::DelayAgnostic(mDelayAgnostic)); + + mVoiceEngine = webrtc::VoiceEngine::Create(mConfig); if (!mVoiceEngine) { return; } diff --git a/dom/media/webrtc/MediaEngineWebRTC.h b/dom/media/webrtc/MediaEngineWebRTC.h index ffc603012f..0c86c3dbe6 100644 --- a/dom/media/webrtc/MediaEngineWebRTC.h +++ b/dom/media/webrtc/MediaEngineWebRTC.h @@ -45,6 +45,7 @@ #include "webrtc/voice_engine/include/voe_volume_control.h" #include "webrtc/voice_engine/include/voe_external_media.h" #include "webrtc/voice_engine/include/voe_audio_processing.h" +#include "webrtc/modules/audio_processing/include/audio_processing.h" // Video Engine // conflicts with #include of scoped_ptr.h @@ -88,7 +89,9 @@ public: { // Nothing to do here, everything is managed in MediaManager.cpp } - nsresult Start(SourceMediaStream* aMediaStream, TrackID aId) override; + nsresult Start(SourceMediaStream* aMediaStream, + TrackID aId, + const PrincipalHandle& aPrincipalHandle) override; nsresult Stop(SourceMediaStream* aMediaStream, TrackID aId) override; nsresult Restart(const dom::MediaTrackConstraints& aConstraints, const MediaEnginePrefs &aPrefs, @@ -103,8 +106,11 @@ public: const AudioDataValue* aBuffer, size_t aFrames, TrackRate aRate, uint32_t aChannels) override {} - void NotifyPull(MediaStreamGraph* aGraph, SourceMediaStream* aSource, - TrackID aID, StreamTime aDesiredTime) override + void NotifyPull(MediaStreamGraph* aGraph, + SourceMediaStream* aSource, + TrackID aID, + StreamTime aDesiredTime, + const PrincipalHandle& aPrincipalHandle) override {} dom::MediaSourceEnum GetMediaSource() const override { @@ -114,7 +120,7 @@ public: { return false; } - nsresult TakePhoto(PhotoCallback* aCallback) override + nsresult TakePhoto(MediaEnginePhotoCallback* aCallback) override { return NS_ERROR_NOT_IMPLEMENTED; } @@ -382,8 +388,9 @@ protected: virtual ~WebRTCAudioDataListener() {} public: - explicit WebRTCAudioDataListener(MediaEngineAudioSource* aAudioSource) : - mAudioSource(aAudioSource) + explicit WebRTCAudioDataListener(MediaEngineAudioSource* aAudioSource) + : mMutex("WebRTCAudioDataListener") + , mAudioSource(aAudioSource) {} // AudioDataListenerInterface methods @@ -391,16 +398,29 @@ public: AudioDataValue* aBuffer, size_t aFrames, TrackRate aRate, uint32_t aChannels) override { - mAudioSource->NotifyOutputData(aGraph, aBuffer, aFrames, aRate, aChannels); + MutexAutoLock lock(mMutex); + if (mAudioSource) { + mAudioSource->NotifyOutputData(aGraph, aBuffer, aFrames, aRate, aChannels); + } } virtual void NotifyInputData(MediaStreamGraph* aGraph, const AudioDataValue* aBuffer, size_t aFrames, TrackRate aRate, uint32_t aChannels) override { - mAudioSource->NotifyInputData(aGraph, aBuffer, aFrames, aRate, aChannels); + MutexAutoLock lock(mMutex); + if (mAudioSource) { + mAudioSource->NotifyInputData(aGraph, aBuffer, aFrames, aRate, aChannels); + } + } + + void Shutdown() + { + MutexAutoLock lock(mMutex); + mAudioSource = nullptr; } private: + Mutex mMutex; RefPtr mAudioSource; }; @@ -449,7 +469,9 @@ public: const nsString& aDeviceId, const nsACString& aOrigin) override; nsresult Deallocate() override; - nsresult Start(SourceMediaStream* aStream, TrackID aID) override; + nsresult Start(SourceMediaStream* aStream, + TrackID aID, + const PrincipalHandle& aPrincipalHandle) override; nsresult Stop(SourceMediaStream* aSource, TrackID aID) override; nsresult Restart(const dom::MediaTrackConstraints& aConstraints, const MediaEnginePrefs &aPrefs, @@ -459,7 +481,8 @@ public: void NotifyPull(MediaStreamGraph* aGraph, SourceMediaStream* aSource, TrackID aId, - StreamTime aDesiredTime) override; + StreamTime aDesiredTime, + const PrincipalHandle& aPrincipalHandle) override; // AudioDataListenerInterface methods void NotifyOutputData(MediaStreamGraph* aGraph, @@ -477,7 +500,7 @@ public: return dom::MediaSourceEnum::Microphone; } - nsresult TakePhoto(PhotoCallback* aCallback) override + nsresult TakePhoto(MediaEnginePhotoCallback* aCallback) override { return NS_ERROR_NOT_IMPLEMENTED; } @@ -496,7 +519,9 @@ public: NS_DECL_THREADSAFE_ISUPPORTS protected: - ~MediaEngineWebRTCMicrophoneSource() { Shutdown(); } + ~MediaEngineWebRTCMicrophoneSource() { + Shutdown(); + } private: void Init(); @@ -510,13 +535,17 @@ private: ScopedCustomReleasePtr mVoENetwork; ScopedCustomReleasePtr mVoEProcessing; + // accessed from the GraphDriver thread except for deletion nsAutoPtr> mPacketizer; + ScopedCustomReleasePtr mVoERenderListener; - // mMonitor protects mSources[] access/changes, and transitions of mState - // from kStarted to kStopped (which are combined with EndTrack()). - // mSources[] is accessed from webrtc threads. + // mMonitor protects mSources[] and mPrinicpalIds[] access/changes, and + // transitions of mState from kStarted to kStopped (which are combined with + // EndTrack()). mSources[] and mPrincipalHandles[] are accessed from webrtc + // threads. Monitor mMonitor; nsTArray> mSources; + nsTArray mPrincipalHandles; // Maps to mSources. nsCOMPtr mThread; int mCapIndex; int mChannel; @@ -569,9 +598,12 @@ private: // gUM runnables can e.g. Enumerate from multiple threads Mutex mMutex; webrtc::VoiceEngine* mVoiceEngine; + webrtc::Config mConfig; RefPtr mAudioInput; bool mAudioEngineInit; bool mFullDuplex; + bool mExtendedFilter; + bool mDelayAgnostic; bool mHasTabVideoSource; // Store devices we've already seen in a hashtable for quick return. diff --git a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp index 6b0fc2598e..ac144b1326 100644 --- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp +++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp @@ -338,7 +338,8 @@ MediaEngineWebRTCMicrophoneSource::Deallocate() nsresult MediaEngineWebRTCMicrophoneSource::Start(SourceMediaStream *aStream, - TrackID aID) + TrackID aID, + const PrincipalHandle& aPrincipalHandle) { AssertIsOnOwningThread(); if (!mInitDone || !aStream) { @@ -348,6 +349,8 @@ MediaEngineWebRTCMicrophoneSource::Start(SourceMediaStream *aStream, { MonitorAutoLock lock(mMonitor); mSources.AppendElement(aStream); + mPrincipalHandles.AppendElement(aPrincipalHandle); + MOZ_ASSERT(mSources.Length() == mPrincipalHandles.Length()); } AudioSegment* segment = new AudioSegment(); @@ -357,6 +360,9 @@ MediaEngineWebRTCMicrophoneSource::Start(SourceMediaStream *aStream, aStream->RegisterForAudioMixing(); LOG(("Start audio for stream %p", aStream)); + if (!mListener) { + mListener = new mozilla::WebRTCAudioDataListener(this); + } if (mState == kStarted) { MOZ_ASSERT(aID == mTrackID); // Make sure we're associated with this stream @@ -398,10 +404,14 @@ MediaEngineWebRTCMicrophoneSource::Stop(SourceMediaStream *aSource, TrackID aID) { MonitorAutoLock lock(mMonitor); - if (!mSources.RemoveElement(aSource)) { + size_t sourceIndex = mSources.IndexOf(aSource); + if (sourceIndex == mSources.NoIndex) { // Already stopped - this is allowed return NS_OK; } + mSources.RemoveElementAt(sourceIndex); + mPrincipalHandles.RemoveElementAt(sourceIndex); + MOZ_ASSERT(mSources.Length() == mPrincipalHandles.Length()); aSource->EndTrack(aID); @@ -418,6 +428,11 @@ MediaEngineWebRTCMicrophoneSource::Stop(SourceMediaStream *aSource, TrackID aID) mState = kStopped; } + if (mListener) { + // breaks a cycle, since the WebRTCAudioDataListener has a RefPtr to us + mListener->Shutdown(); + mListener = nullptr; + } mAudioInput->StopRecording(aSource); @@ -436,7 +451,8 @@ void MediaEngineWebRTCMicrophoneSource::NotifyPull(MediaStreamGraph *aGraph, SourceMediaStream *aSource, TrackID aID, - StreamTime aDesiredTime) + StreamTime aDesiredTime, + const PrincipalHandle& aPrincipalHandle) { // Ignore - we push audio data LOG_FRAMES(("NotifyPull, desired = %ld", (int64_t) aDesiredTime)); @@ -451,7 +467,8 @@ MediaEngineWebRTCMicrophoneSource::NotifyOutputData(MediaStreamGraph* aGraph, { } -// Called back on GraphDriver thread +// Called back on GraphDriver thread! +// Note this can be called back after ::Shutdown() void MediaEngineWebRTCMicrophoneSource::NotifyInputData(MediaStreamGraph* aGraph, const AudioDataValue* aBuffer, @@ -553,6 +570,13 @@ MediaEngineWebRTCMicrophoneSource::Init() void MediaEngineWebRTCMicrophoneSource::Shutdown() { + if (mListener) { + // breaks a cycle, since the WebRTCAudioDataListener has a RefPtr to us + mListener->Shutdown(); + // Don't release the webrtc.org pointers yet until the Listener is (async) shutdown + mListener = nullptr; + } + if (!mInitDone) { // duplicate these here in case we failed during Init() if (mChannel != -1 && mVoENetwork) { @@ -600,7 +624,6 @@ MediaEngineWebRTCMicrophoneSource::Shutdown() mVoEBase = nullptr; mAudioInput = nullptr; - mListener = nullptr; // breaks a cycle, since the WebRTCAudioDataListener has a RefPtr to us mState = kReleased; mInitDone = false; @@ -654,7 +677,8 @@ MediaEngineWebRTCMicrophoneSource::Process(int channel, nsAutoPtr segment(new AudioSegment()); AutoTArray channels; channels.AppendElement(dest); - segment->AppendFrames(buffer.forget(), channels, length); + segment->AppendFrames(buffer.forget(), channels, length, + mPrincipalHandles[i]); TimeStamp insertTime; segment->GetStartTime(insertTime); @@ -707,7 +731,8 @@ MediaEngineWebRTCAudioCaptureSource::GetUUID(nsACString &aUUID) nsresult MediaEngineWebRTCAudioCaptureSource::Start(SourceMediaStream *aMediaStream, - TrackID aId) + TrackID aId, + const PrincipalHandle& aPrincipalHandle) { AssertIsOnOwningThread(); aMediaStream->AddTrack(aId, 0, new AudioSegment()); diff --git a/dom/media/webrtc/PeerIdentity.h b/dom/media/webrtc/PeerIdentity.h index 96051061e7..bdfa1d2b37 100644 --- a/dom/media/webrtc/PeerIdentity.h +++ b/dom/media/webrtc/PeerIdentity.h @@ -27,9 +27,11 @@ namespace mozilla { * * See: http://tools.ietf.org/html/draft-ietf-rtcweb-security-arch-09#section-5.6.5.3.3.1 */ -class PeerIdentity final +class PeerIdentity final : public RefCounted { public: + MOZ_DECLARE_REFCOUNTED_TYPENAME(PeerIdentity) + explicit PeerIdentity(const nsAString& aPeerIdentity) : mPeerIdentity(aPeerIdentity) {} ~PeerIdentity() {} diff --git a/dom/media/webspeech/recognition/SpeechRecognition.cpp b/dom/media/webspeech/recognition/SpeechRecognition.cpp index b0b1b61681..b204e2bad9 100644 --- a/dom/media/webspeech/recognition/SpeechRecognition.cpp +++ b/dom/media/webspeech/recognition/SpeechRecognition.cpp @@ -160,7 +160,7 @@ bool SpeechRecognition::IsAuthorized(JSContext* aCx, JSObject* aGlobal) { nsCOMPtr principal = nsContentUtils::ObjectPrincipal(aGlobal); - + nsresult rv; nsCOMPtr mgr = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv); if (NS_WARN_IF(NS_FAILED(rv))) { @@ -934,7 +934,8 @@ SpeechRecognition::CreateAudioSegment(nsTArray>& aChunks) AutoTArray channels; channels.AppendElement(chunkData); - segment->AppendFrames(buffer.forget(), channels, mAudioSamplesPerChunk); + segment->AppendFrames(buffer.forget(), channels, mAudioSamplesPerChunk, + PRINCIPAL_HANDLE_NONE); } return segment; diff --git a/dom/media/webspeech/synth/nsSpeechTask.cpp b/dom/media/webspeech/synth/nsSpeechTask.cpp index aa038ceabe..7168f64cfc 100644 --- a/dom/media/webspeech/synth/nsSpeechTask.cpp +++ b/dom/media/webspeech/synth/nsSpeechTask.cpp @@ -325,7 +325,8 @@ nsSpeechTask::SendAudioImpl(RefPtr& aSamples, uint32_t aD AudioSegment segment; AutoTArray channelData; channelData.AppendElement(static_cast(aSamples->Data())); - segment.AppendFrames(aSamples.forget(), channelData, aDataLen); + segment.AppendFrames(aSamples.forget(), channelData, aDataLen, + PRINCIPAL_HANDLE_NONE); mStream->AppendToTrack(1, &segment); mStream->AdvanceKnownTracksTime(STREAM_TIME_MAX); } diff --git a/dom/plugins/base/nsPluginInstanceOwner.cpp b/dom/plugins/base/nsPluginInstanceOwner.cpp index 7709593c33..6ef188116d 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.cpp +++ b/dom/plugins/base/nsPluginInstanceOwner.cpp @@ -518,7 +518,7 @@ NS_IMETHODIMP nsPluginInstanceOwner::GetURL(const char *aURL, return NS_OK; } - nsIDocument *doc = content->GetCurrentDoc(); + nsIDocument *doc = content->GetUncomposedDoc(); if (!doc) { return NS_ERROR_FAILURE; } @@ -2193,8 +2193,8 @@ TranslateToNPCocoaEvent(WidgetGUIEvent* anEvent, nsIFrame* aObjectFrame) case eLegacyMouseLineOrPageScroll: { WidgetWheelEvent* wheelEvent = anEvent->AsWheelEvent(); if (wheelEvent) { - cocoaEvent.data.mouse.deltaX = wheelEvent->lineOrPageDeltaX; - cocoaEvent.data.mouse.deltaY = wheelEvent->lineOrPageDeltaY; + cocoaEvent.data.mouse.deltaX = wheelEvent->mLineOrPageDeltaX; + cocoaEvent.data.mouse.deltaY = wheelEvent->mLineOrPageDeltaY; } else { NS_WARNING("eLegacyMouseLineOrPageScroll is not a WidgetWheelEvent? " "(could be, haven't checked)"); @@ -2386,11 +2386,11 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent) case eWheel: { const WidgetWheelEvent* wheelEvent = anEvent.AsWheelEvent(); int32_t delta = 0; - if (wheelEvent->lineOrPageDeltaY) { - switch (wheelEvent->deltaMode) { + if (wheelEvent->mLineOrPageDeltaY) { + switch (wheelEvent->mDeltaMode) { case nsIDOMWheelEvent::DOM_DELTA_PAGE: pluginEvent.event = WM_MOUSEWHEEL; - delta = -WHEEL_DELTA * wheelEvent->lineOrPageDeltaY; + delta = -WHEEL_DELTA * wheelEvent->mLineOrPageDeltaY; break; case nsIDOMWheelEvent::DOM_DELTA_LINE: { UINT linesPerWheelDelta = 0; @@ -2404,8 +2404,8 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent) break; } pluginEvent.event = WM_MOUSEWHEEL; - delta = -WHEEL_DELTA / linesPerWheelDelta * - wheelEvent->lineOrPageDeltaY; + delta = -WHEEL_DELTA / linesPerWheelDelta; + delta *= wheelEvent->mLineOrPageDeltaY; break; } case nsIDOMWheelEvent::DOM_DELTA_PIXEL: @@ -2414,11 +2414,11 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent) MOZ_ASSERT(!pluginEvent.event); break; } - } else if (wheelEvent->lineOrPageDeltaX) { - switch (wheelEvent->deltaMode) { + } else if (wheelEvent->mLineOrPageDeltaX) { + switch (wheelEvent->mDeltaMode) { case nsIDOMWheelEvent::DOM_DELTA_PAGE: pluginEvent.event = WM_MOUSEHWHEEL; - delta = -WHEEL_DELTA * wheelEvent->lineOrPageDeltaX; + delta = -WHEEL_DELTA * wheelEvent->mLineOrPageDeltaX; break; case nsIDOMWheelEvent::DOM_DELTA_LINE: { pluginEvent.event = WM_MOUSEHWHEEL; @@ -2433,8 +2433,8 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent) if (!charsPerWheelDelta) { break; } - delta = - WHEEL_DELTA / charsPerWheelDelta * wheelEvent->lineOrPageDeltaX; + delta = WHEEL_DELTA / charsPerWheelDelta; + delta *= wheelEvent->mLineOrPageDeltaX; break; } case nsIDOMWheelEvent::DOM_DELTA_PIXEL: @@ -2540,7 +2540,7 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent) #ifdef MOZ_X11 // this code supports windowless plugins - nsIWidget* widget = anEvent.widget; + nsIWidget* widget = anEvent.mWidget; XEvent pluginEvent = XEvent(); pluginEvent.type = 0; @@ -2566,8 +2566,9 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent) const WidgetMouseEvent& mouseEvent = *anEvent.AsMouseEvent(); // Get reference point relative to screen: LayoutDeviceIntPoint rootPoint(-1, -1); - if (widget) - rootPoint = anEvent.refPoint + widget->WidgetToScreenOffset(); + if (widget) { + rootPoint = anEvent.mRefPoint + widget->WidgetToScreenOffset(); + } #ifdef MOZ_WIDGET_GTK Window root = GDK_ROOT_WINDOW(); #elif defined(MOZ_WIDGET_QT) @@ -2584,7 +2585,7 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent) event.type = anEvent.mMessage == eMouseOver ? EnterNotify : LeaveNotify; event.root = root; - event.time = anEvent.time; + event.time = anEvent.mTime; event.x = pluginPoint.x; event.y = pluginPoint.y; event.x_root = rootPoint.x; @@ -2603,7 +2604,7 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent) XMotionEvent& event = pluginEvent.xmotion; event.type = MotionNotify; event.root = root; - event.time = anEvent.time; + event.time = anEvent.mTime; event.x = pluginPoint.x; event.y = pluginPoint.y; event.x_root = rootPoint.x; @@ -2622,7 +2623,7 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent) event.type = anEvent.mMessage == eMouseDown ? ButtonPress : ButtonRelease; event.root = root; - event.time = anEvent.time; + event.time = anEvent.mTime; event.x = pluginPoint.x; event.y = pluginPoint.y; event.x_root = rootPoint.x; @@ -2659,7 +2660,7 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent) XKeyEvent &event = pluginEvent.xkey; #ifdef MOZ_WIDGET_GTK event.root = GDK_ROOT_WINDOW(); - event.time = anEvent.time; + event.time = anEvent.mTime; const GdkEventKey* gdkEvent = static_cast(anEvent.mPluginEvent); event.keycode = gdkEvent->hardware_keycode; diff --git a/dom/smil/TimeEvent.cpp b/dom/smil/TimeEvent.cpp index a824b9a950..1fd05c2b5c 100644 --- a/dom/smil/TimeEvent.cpp +++ b/dom/smil/TimeEvent.cpp @@ -18,7 +18,7 @@ TimeEvent::TimeEvent(EventTarget* aOwner, InternalSMILTimeEvent* aEvent) : Event(aOwner, aPresContext, aEvent ? aEvent : new InternalSMILTimeEvent(false, eVoidEvent)) - , mDetail(mEvent->AsSMILTimeEvent()->detail) + , mDetail(mEvent->AsSMILTimeEvent()->mDetail) { if (aEvent) { mEventIsInternal = false; diff --git a/dom/smil/nsSMILAnimationFunction.cpp b/dom/smil/nsSMILAnimationFunction.cpp index 276a85f48b..bf6547ddc3 100644 --- a/dom/smil/nsSMILAnimationFunction.cpp +++ b/dom/smil/nsSMILAnimationFunction.cpp @@ -9,6 +9,7 @@ #include "mozilla/dom/SVGAnimationElement.h" #include "mozilla/Move.h" #include "nsISMILAttr.h" +#include "nsSMILCSSValueType.h" #include "nsSMILParserUtils.h" #include "nsSMILNullType.h" #include "nsSMILTimedElement.h" @@ -384,6 +385,15 @@ nsSMILAnimationFunction::InterpolateResult(const nsSMILValueArray& aValues, nsresult rv = NS_OK; nsSMILCalcMode calcMode = GetCalcMode(); + + // Force discrete calcMode for visibility since StyleAnimationValue will + // try to interpolate it using the special clamping behavior defined for + // CSS. + if (nsSMILCSSValueType::PropertyFromValue(aValues[0]) + == eCSSProperty_visibility) { + calcMode = CALC_DISCRETE; + } + if (calcMode != CALC_DISCRETE) { // Get the normalised progress between adjacent values const nsSMILValue* from = nullptr; diff --git a/dom/smil/nsSMILCSSProperty.cpp b/dom/smil/nsSMILCSSProperty.cpp index 61a7ea29cb..72225399d4 100644 --- a/dom/smil/nsSMILCSSProperty.cpp +++ b/dom/smil/nsSMILCSSProperty.cpp @@ -30,7 +30,7 @@ GetCSSComputedValue(Element* aElem, MOZ_ASSERT(nsSMILCSSProperty::IsPropertyAnimatable(aPropID), "Shouldn't get here for non-animatable properties"); - nsIDocument* doc = aElem->GetCurrentDoc(); + nsIDocument* doc = aElem->GetUncomposedDoc(); if (!doc) { // This can happen if we process certain types of restyles mid-sample // and remove anonymous animated content from the document as a result. diff --git a/dom/smil/nsSMILCSSValueType.cpp b/dom/smil/nsSMILCSSValueType.cpp index d34b3c24e6..1197bbc63b 100644 --- a/dom/smil/nsSMILCSSValueType.cpp +++ b/dom/smil/nsSMILCSSValueType.cpp @@ -322,7 +322,7 @@ nsSMILCSSValueType::Interpolate(const nsSMILValue& aStartVal, static nsPresContext* GetPresContextForElement(Element* aElem) { - nsIDocument* doc = aElem->GetCurrentDoc(); + nsIDocument* doc = aElem->GetUncomposedDoc(); if (!doc) { // This can happen if we process certain types of restyles mid-sample // and remove anonymous animated content from the document as a result. @@ -400,7 +400,7 @@ nsSMILCSSValueType::ValueFromString(nsCSSProperty aPropID, return; } - nsIDocument* doc = aTargetElement->GetCurrentDoc(); + nsIDocument* doc = aTargetElement->GetUncomposedDoc(); if (doc && !nsStyleUtil::CSPAllowsInlineStyle(nullptr, doc->NodePrincipal(), doc->GetDocumentURI(), @@ -428,3 +428,19 @@ nsSMILCSSValueType::ValueToString(const nsSMILValue& aValue, StyleAnimationValue::UncomputeValue(wrapper->mPropID, wrapper->mCSSValue, aString); } + +// static +nsCSSProperty +nsSMILCSSValueType::PropertyFromValue(const nsSMILValue& aValue) +{ + if (aValue.mType != &nsSMILCSSValueType::sSingleton) { + return eCSSProperty_UNKNOWN; + } + + const ValueWrapper* wrapper = ExtractValueWrapper(aValue); + if (!wrapper) { + return eCSSProperty_UNKNOWN; + } + + return wrapper->mPropID; +} diff --git a/dom/smil/nsSMILCSSValueType.h b/dom/smil/nsSMILCSSValueType.h index e366c205a7..bb1f21e0cd 100644 --- a/dom/smil/nsSMILCSSValueType.h +++ b/dom/smil/nsSMILCSSValueType.h @@ -98,6 +98,16 @@ public: */ static bool ValueToString(const nsSMILValue& aValue, nsAString& aString); + /** + * Return the CSS property animated by the specified value. + * + * @param aValue The nsSMILValue to examine. + * @return The nsCSSProperty enum value of the property animated + * by |aValue|, or eCSSProperty_UNKNOWN if the type of + * |aValue| is not nsSMILCSSValueType. + */ + static nsCSSProperty PropertyFromValue(const nsSMILValue& aValue); + private: // Private constructor: prevent instances beyond my singleton. MOZ_CONSTEXPR nsSMILCSSValueType() {} diff --git a/dom/smil/nsSMILMappedAttribute.cpp b/dom/smil/nsSMILMappedAttribute.cpp index 110d74f3e2..b43469facb 100644 --- a/dom/smil/nsSMILMappedAttribute.cpp +++ b/dom/smil/nsSMILMappedAttribute.cpp @@ -132,7 +132,7 @@ nsSMILMappedAttribute::FlushChangesToTargetAttr() const // Clear animated content-style-rule mElement->DeleteProperty(SMIL_MAPPED_ATTR_ANIMVAL, SMIL_MAPPED_ATTR_STYLERULE_ATOM); - nsIDocument* doc = mElement->GetCurrentDoc(); + nsIDocument* doc = mElement->GetUncomposedDoc(); // Request animation restyle if (doc) { @@ -146,5 +146,5 @@ nsSMILMappedAttribute::FlushChangesToTargetAttr() const already_AddRefed nsSMILMappedAttribute::GetAttrNameAtom() const { - return do_GetAtom(nsCSSProps::GetStringValue(mPropID)); + return NS_Atomize(nsCSSProps::GetStringValue(mPropID)); } diff --git a/dom/smil/nsSMILParserUtils.cpp b/dom/smil/nsSMILParserUtils.cpp index 31e03e860f..95af90ee58 100644 --- a/dom/smil/nsSMILParserUtils.cpp +++ b/dom/smil/nsSMILParserUtils.cpp @@ -351,7 +351,7 @@ ConvertUnescapedTokenToAtom(const nsAString& aToken) // Whether the token is an id-ref or event-symbol it should be a valid NCName if (aToken.IsEmpty() || NS_FAILED(nsContentUtils::CheckQName(aToken, false))) return nullptr; - return do_GetAtom(aToken); + return NS_Atomize(aToken); } already_AddRefed diff --git a/dom/smil/nsSMILTimeValueSpec.cpp b/dom/smil/nsSMILTimeValueSpec.cpp index c3d3d1bcef..36ea542574 100644 --- a/dom/smil/nsSMILTimeValueSpec.cpp +++ b/dom/smil/nsSMILTimeValueSpec.cpp @@ -98,7 +98,7 @@ nsSMILTimeValueSpec::ResolveReferences(nsIContent* aContextNode) // If we're not bound to the document yet, don't worry, we'll get called again // when that happens - if (!aContextNode->IsInDoc()) + if (!aContextNode->IsInUncomposedDoc()) return; // Hold ref to the old element so that it isn't destroyed in between resetting @@ -113,7 +113,7 @@ nsSMILTimeValueSpec::ResolveReferences(nsIContent* aContextNode) Element* target = mOwner->GetTargetElement(); mReferencedElement.ResetWithElement(target); } else if (mParams.mType == nsSMILTimeValueSpecParams::ACCESSKEY) { - nsIDocument* doc = aContextNode->GetCurrentDoc(); + nsIDocument* doc = aContextNode->GetUncomposedDoc(); MOZ_ASSERT(doc, "We are in the document but current doc is null"); mReferencedElement.ResetWithElement(doc->GetRootElement()); } else { @@ -350,7 +350,7 @@ nsSMILTimeValueSpec::GetEventListenerManager(Element* aTarget) nsCOMPtr target; if (mParams.mType == nsSMILTimeValueSpecParams::ACCESSKEY) { - nsIDocument* doc = aTarget->GetCurrentDoc(); + nsIDocument* doc = aTarget->GetUncomposedDoc(); if (!doc) return nullptr; nsPIDOMWindow* win = doc->GetWindow(); diff --git a/dom/smil/nsSMILTimedElement.cpp b/dom/smil/nsSMILTimedElement.cpp index 5c0b31ea2d..d00ff04c06 100644 --- a/dom/smil/nsSMILTimedElement.cpp +++ b/dom/smil/nsSMILTimedElement.cpp @@ -97,10 +97,10 @@ namespace NS_IMETHOD Run() { InternalSMILTimeEvent event(true, mMsg); - event.detail = mDetail; + event.mDetail = mDetail; nsPresContext* context = nullptr; - nsIDocument* doc = mTarget->GetCurrentDoc(); + nsIDocument* doc = mTarget->GetUncomposedDoc(); if (doc) { nsCOMPtr shell = doc->GetShell(); if (shell) { diff --git a/dom/svg/SVGAnimationElement.cpp b/dom/svg/SVGAnimationElement.cpp index c9a7e882ec..ba9418efc9 100644 --- a/dom/svg/SVGAnimationElement.cpp +++ b/dom/svg/SVGAnimationElement.cpp @@ -314,7 +314,7 @@ SVGAnimationElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, if (!aValue) { mHrefTarget.Unlink(); AnimationTargetChanged(); - } else if (IsInDoc()) { + } else if (IsInUncomposedDoc()) { MOZ_ASSERT(aValue->Type() == nsAttrValue::eString, "Expected href attribute to be string type"); UpdateHrefTarget(this, aValue->GetStringValue()); diff --git a/dom/svg/SVGMPathElement.cpp b/dom/svg/SVGMPathElement.cpp index cc89c5ce36..2eae1caff3 100644 --- a/dom/svg/SVGMPathElement.cpp +++ b/dom/svg/SVGMPathElement.cpp @@ -123,7 +123,7 @@ SVGMPathElement::ParseAttribute(int32_t aNamespaceID, aValue, aResult); if (aNamespaceID == kNameSpaceID_XLink && aAttribute == nsGkAtoms::href && - IsInDoc()) { + IsInUncomposedDoc()) { // NOTE: If we fail the IsInDoc call, it's ok -- we'll update the target // on next BindToTree call. UpdateHrefTarget(GetParent(), aValue); diff --git a/dom/svg/SVGSVGElement.h b/dom/svg/SVGSVGElement.h index 0a011ffd61..ed471ae580 100644 --- a/dom/svg/SVGSVGElement.h +++ b/dom/svg/SVGSVGElement.h @@ -312,10 +312,10 @@ private: bool ClearPreserveAspectRatioProperty(); bool IsRoot() const { - NS_ASSERTION((IsInDoc() && !GetParent()) == + NS_ASSERTION((IsInUncomposedDoc() && !GetParent()) == (OwnerDoc() && (OwnerDoc()->GetRootElement() == this)), "Can't determine if we're root"); - return IsInDoc() && !GetParent(); + return IsInUncomposedDoc() && !GetParent(); } /** diff --git a/dom/svg/SVGZoomEvent.cpp b/dom/svg/SVGZoomEvent.cpp index b01d239b94..4856f2c389 100644 --- a/dom/svg/SVGZoomEvent.cpp +++ b/dom/svg/SVGZoomEvent.cpp @@ -40,7 +40,7 @@ SVGZoomEvent::SVGZoomEvent(EventTarget* aOwner, } else { mEventIsInternal = true; - mEvent->time = PR_Now(); + mEvent->mTime = PR_Now(); } // We must store the "Previous" and "New" values before this event is diff --git a/dom/svg/nsSVGElement.cpp b/dom/svg/nsSVGElement.cpp index fca581c976..720d0bb0d3 100644 --- a/dom/svg/nsSVGElement.cpp +++ b/dom/svg/nsSVGElement.cpp @@ -527,7 +527,7 @@ nsSVGElement::ParseAttribute(int32_t aNamespaceID, EnumAttributesInfo enumInfo = GetEnumInfo(); for (i = 0; i < enumInfo.mEnumCount; i++) { if (aAttribute == *enumInfo.mEnumInfo[i].mName) { - nsCOMPtr valAtom = do_GetAtom(aValue); + nsCOMPtr valAtom = NS_Atomize(aValue); rv = enumInfo.mEnums[i].SetBaseValueAtom(valAtom, this); if (NS_FAILED(rv)) { enumInfo.Reset(i); diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html index efa51198aa..3753a5abc3 100644 --- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -479,8 +479,6 @@ var interfaceNamesInGlobalScope = "EventTarget", // IMPORTANT: Do not change this list without review from a DOM peer! {name: "External", b2g: false}, -// IMPORTANT: Do not change this list without review from a DOM peer! - {name: "ExternalAppEvent", b2g: true, permission: ["external-app"]}, // IMPORTANT: Do not change this list without review from a DOM peer! "File", // IMPORTANT: Do not change this list without review from a DOM peer! @@ -563,8 +561,6 @@ var interfaceNamesInGlobalScope = "HTMLElement", // IMPORTANT: Do not change this list without review from a DOM peer! "HTMLEmbedElement", -// IMPORTANT: Do not change this list without review from a DOM peer! - {name: "HTMLExternalAppElement", b2g: true, permission: ["external-app"]}, // IMPORTANT: Do not change this list without review from a DOM peer! "HTMLFieldSetElement", // IMPORTANT: Do not change this list without review from a DOM peer! diff --git a/dom/tv/TVTuner.cpp b/dom/tv/TVTuner.cpp index 4976f46a05..5ea452eddb 100644 --- a/dom/tv/TVTuner.cpp +++ b/dom/tv/TVTuner.cpp @@ -209,7 +209,7 @@ TVTuner::ReloadMediaStream() nsresult TVTuner::InitMediaStream() { - nsCOMPtr window = do_QueryInterface(GetOwner()); + nsCOMPtr window = do_QueryInterface(GetOwner()); RefPtr stream = nullptr; if (mStreamType == nsITVTunerData::TV_STREAM_TYPE_HW) { stream = DOMHwMediaStream::CreateHwStream(window); diff --git a/dom/webidl/Event.webidl b/dom/webidl/Event.webidl index 452e8955de..b05640cfdb 100644 --- a/dom/webidl/Event.webidl +++ b/dom/webidl/Event.webidl @@ -39,6 +39,10 @@ interface Event { void preventDefault(); [Pure] readonly attribute boolean defaultPrevented; + [ChromeOnly, Pure] + readonly attribute boolean defaultPreventedByChrome; + [ChromeOnly, Pure] + readonly attribute boolean defaultPreventedByContent; [Unforgeable, Pure] readonly attribute boolean isTrusted; diff --git a/dom/webidl/ExternalAppEvent.webidl b/dom/webidl/ExternalAppEvent.webidl deleted file mode 100644 index acb6d947fb..0000000000 --- a/dom/webidl/ExternalAppEvent.webidl +++ /dev/null @@ -1,17 +0,0 @@ -/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -[Constructor(DOMString type, optional ExternalAppEventInit eventInitDict), - CheckAnyPermissions="external-app"] -interface ExternalAppEvent : Event -{ - readonly attribute DOMString data; -}; - -dictionary ExternalAppEventInit : EventInit -{ - DOMString data = ""; -}; diff --git a/dom/webidl/HTMLExtAppElement.webidl b/dom/webidl/HTMLExtAppElement.webidl deleted file mode 100644 index 6d690cd6f9..0000000000 --- a/dom/webidl/HTMLExtAppElement.webidl +++ /dev/null @@ -1,16 +0,0 @@ -/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -[CheckAnyPermissions="external-app"] -interface HTMLExtAppElement : HTMLElement { - // Gets the value of the property from a property bag - // that was provided to the external application. - DOMString getCustomProperty(DOMString name); - - // Posts a message to the external application. - [Throws] - void postMessage(DOMString name); -}; diff --git a/dom/webidl/InputMethod.webidl b/dom/webidl/InputMethod.webidl index f936d03b7e..f53c7fecd9 100644 --- a/dom/webidl/InputMethod.webidl +++ b/dom/webidl/InputMethod.webidl @@ -525,6 +525,24 @@ interface MozInputContext: EventTarget { */ Promise endComposition(optional DOMString text, optional MozInputMethodKeyboardEventDict dict); + + /** + * The interface used to receive the native events from hardware keyboard + */ + readonly attribute MozHardwareInput? hardwareinput; +}; + +/* + * This interface will be added into inputcontext and used to receive the + * events from the hardware keyboard. + * Example: + * mozInputMethod.inputcontext.hardwareinput.addEventListener('keyup', this); + * mozInputMethod.inputcontext.hardwareinput.removeEventListener('keyup', this); + */ +[JSImplementation="@mozilla.org/b2g-hardwareinput;1", + Pref="dom.mozInputMethod.enabled", + CheckAnyPermissions="input"] +interface MozHardwareInput: EventTarget { }; /** @@ -535,7 +553,7 @@ interface MozInputContext: EventTarget { CheckAnyPermissions="input"] interface MozInputContextSelectionChangeEventDetail { /** - * Indicate whether or not the change is due to our own action from, + * Indicate whether or not the change is due to our own action from, * for example, sendKey() call. * * Note: this property is untrustworthy because it would still be true even @@ -559,7 +577,7 @@ interface MozInputContextSelectionChangeEventDetail { CheckAnyPermissions="input"] interface MozInputContextSurroundingTextChangeEventDetail { /** - * Indicate whether or not the change is due to our own action from, + * Indicate whether or not the change is due to our own action from, * for example, sendKey() call. * * Note: this property is untrustworthy because it would still be true even diff --git a/dom/webidl/KeyboardEvent.webidl b/dom/webidl/KeyboardEvent.webidl index d63c82af82..f6b7b33c55 100644 --- a/dom/webidl/KeyboardEvent.webidl +++ b/dom/webidl/KeyboardEvent.webidl @@ -29,6 +29,11 @@ interface KeyboardEvent : UIEvent readonly attribute DOMString key; [Pref="dom.keyboardevent.code.enabled"] readonly attribute DOMString code; + + // This returns the initialized dictionary for generating a + // same-type keyboard event + [Cached, ChromeOnly, Constant] + readonly attribute KeyboardEventInit initDict; }; dictionary KeyboardEventInit : EventModifierInit diff --git a/dom/webidl/KeyframeEffect.webidl b/dom/webidl/KeyframeEffect.webidl index 47f3b351f0..9e5def3393 100644 --- a/dom/webidl/KeyframeEffect.webidl +++ b/dom/webidl/KeyframeEffect.webidl @@ -77,6 +77,6 @@ interface KeyframeEffect : KeyframeEffectReadOnly { // inherit attribute CompositeOperation composite; // Bug 1244590 - implement spacing modes // inherit attribute DOMString spacing; - // Bug 1244591 - implement setFrames - // void setFrames (object? frames); + [Throws] + void setFrames (object? frames); }; diff --git a/dom/webidl/MediaRecorder.webidl b/dom/webidl/MediaRecorder.webidl index 9202400a35..65d1893e9e 100644 --- a/dom/webidl/MediaRecorder.webidl +++ b/dom/webidl/MediaRecorder.webidl @@ -47,6 +47,8 @@ interface MediaRecorder : EventTarget { [Throws] void requestData(); + + static boolean isTypeSupported(DOMString type); }; dictionary MediaRecorderOptions { diff --git a/dom/webidl/MediaStream.webidl b/dom/webidl/MediaStream.webidl index 0dd779c60d..60f71022d0 100644 --- a/dom/webidl/MediaStream.webidl +++ b/dom/webidl/MediaStream.webidl @@ -39,7 +39,7 @@ interface MediaStream : EventTarget { // MediaStreamTrack? getTrackById (DOMString trackId); void addTrack (MediaStreamTrack track); void removeTrack (MediaStreamTrack track); - // MediaStream clone (); + MediaStream clone (); // readonly attribute boolean active; // attribute EventHandler onactive; // attribute EventHandler oninactive; diff --git a/dom/webidl/MediaStreamTrack.webidl b/dom/webidl/MediaStreamTrack.webidl index bea5228870..71a3350294 100644 --- a/dom/webidl/MediaStreamTrack.webidl +++ b/dom/webidl/MediaStreamTrack.webidl @@ -76,7 +76,7 @@ interface MediaStreamTrack : EventTarget { // readonly attribute boolean remote; // readonly attribute MediaStreamTrackState readyState; // attribute EventHandler onended; -// MediaStreamTrack clone (); + MediaStreamTrack clone (); void stop (); // MediaTrackCapabilities getCapabilities (); // MediaTrackConstraints getConstraints (); diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index d22f9025b9..49108c4b97 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -197,7 +197,6 @@ WEBIDL_FILES = [ 'HTMLDocument.webidl', 'HTMLElement.webidl', 'HTMLEmbedElement.webidl', - 'HTMLExtAppElement.webidl', 'HTMLFieldSetElement.webidl', 'HTMLFontElement.webidl', 'HTMLFormControlsCollection.webidl', @@ -790,7 +789,6 @@ GENERATED_EVENTS_WEBIDL_FILES = [ 'DOMTransactionEvent.webidl', 'DownloadEvent.webidl', 'ErrorEvent.webidl', - 'ExternalAppEvent.webidl', 'FontFaceSetLoadEvent.webidl', 'HashChangeEvent.webidl', 'IccChangeEvent.webidl', diff --git a/dom/xbl/XBLChildrenElement.cpp b/dom/xbl/XBLChildrenElement.cpp index a407b7b874..e4058a7890 100644 --- a/dom/xbl/XBLChildrenElement.cpp +++ b/dom/xbl/XBLChildrenElement.cpp @@ -50,7 +50,7 @@ XBLChildrenElement::ParseAttribute(int32_t aNamespaceID, nsCharSeparatedTokenizer tok(aValue, '|', nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL); while (tok.hasMoreTokens()) { - mIncludes.AppendElement(do_GetAtom(tok.nextToken())); + mIncludes.AppendElement(NS_Atomize(tok.nextToken())); } } diff --git a/dom/xbl/nsXBLBinding.cpp b/dom/xbl/nsXBLBinding.cpp index b41a03854e..3b63ff16f6 100644 --- a/dom/xbl/nsXBLBinding.cpp +++ b/dom/xbl/nsXBLBinding.cpp @@ -202,7 +202,7 @@ nsXBLBinding::InstallAnonymousContent(nsIContent* aAnonParent, nsIContent* aElem // aElement. // (2) The children's parent back pointer should not be to this synthetic root // but should instead point to the enclosing parent element. - nsIDocument* doc = aElement->GetCurrentDoc(); + nsIDocument* doc = aElement->GetUncomposedDoc(); bool allowScripts = AllowScripts(); nsAutoScriptBlocker scriptBlocker; diff --git a/dom/xbl/nsXBLPrototypeBinding.cpp b/dom/xbl/nsXBLPrototypeBinding.cpp index 774c90a887..12c2a7eff6 100644 --- a/dom/xbl/nsXBLPrototypeBinding.cpp +++ b/dom/xbl/nsXBLPrototypeBinding.cpp @@ -852,7 +852,7 @@ nsXBLPrototypeBinding::Read(nsIObjectInputStream* aStream, rv = aStream->ReadString(baseTag); NS_ENSURE_SUCCESS(rv, rv); if (!baseTag.IsEmpty()) { - mBaseTag = do_GetAtom(baseTag); + mBaseTag = NS_Atomize(baseTag); } mBinding = aDocument->CreateElem(NS_LITERAL_STRING("binding"), nullptr, @@ -968,8 +968,8 @@ nsXBLPrototypeBinding::Read(nsIObjectInputStream* aStream, rv = aStream->ReadString(attrValue); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr atomPrefix = do_GetAtom(attrPrefix); - nsCOMPtr atomName = do_GetAtom(attrName); + nsCOMPtr atomPrefix = NS_Atomize(attrPrefix); + nsCOMPtr atomName = NS_Atomize(attrName); mBinding->SetAttr(attrNamespace, atomName, atomPrefix, attrValue, false); } } @@ -1215,12 +1215,12 @@ nsXBLPrototypeBinding::ReadContentNode(nsIObjectInputStream* aStream, nsCOMPtr prefixAtom; if (!prefix.IsEmpty()) - prefixAtom = do_GetAtom(prefix); + prefixAtom = NS_Atomize(prefix); rv = aStream->ReadString(tag); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr tagAtom = do_GetAtom(tag); + nsCOMPtr tagAtom = NS_Atomize(tag); RefPtr nodeInfo = aNim->GetNodeInfo(tagAtom, prefixAtom, namespaceID, nsIDOMNode::ELEMENT_NODE); @@ -1258,14 +1258,14 @@ nsXBLPrototypeBinding::ReadContentNode(nsIObjectInputStream* aStream, rv = aStream->ReadString(val); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr nameAtom = do_GetAtom(name); + nsCOMPtr nameAtom = NS_Atomize(name); if (namespaceID == kNameSpaceID_None) { attrs[i].mName.SetTo(nameAtom); } else { nsCOMPtr prefixAtom; if (!prefix.IsEmpty()) - prefixAtom = do_GetAtom(prefix); + prefixAtom = NS_Atomize(prefix); RefPtr ni = aNim->GetNodeInfo(nameAtom, prefixAtom, @@ -1303,9 +1303,9 @@ nsXBLPrototypeBinding::ReadContentNode(nsIObjectInputStream* aStream, nsCOMPtr prefixAtom; if (!prefix.IsEmpty()) - prefixAtom = do_GetAtom(prefix); + prefixAtom = NS_Atomize(prefix); - nsCOMPtr nameAtom = do_GetAtom(name); + nsCOMPtr nameAtom = NS_Atomize(name); content->SetAttr(namespaceID, nameAtom, prefixAtom, val, false); } @@ -1328,8 +1328,8 @@ nsXBLPrototypeBinding::ReadContentNode(nsIObjectInputStream* aStream, rv = aStream->ReadString(destAttribute); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr srcAtom = do_GetAtom(srcAttribute); - nsCOMPtr destAtom = do_GetAtom(destAttribute); + nsCOMPtr srcAtom = NS_Atomize(srcAttribute); + nsCOMPtr destAtom = NS_Atomize(destAttribute); EnsureAttributeTable(); AddToAttributeTable(srcNamespaceID, srcAtom, destNamespaceID, destAtom, content); @@ -1597,7 +1597,7 @@ nsXBLPrototypeBinding::ResolveBaseBinding() int32_t nameSpaceID = nsContentUtils::NameSpaceManager()->GetNameSpaceID(nameSpace); - nsCOMPtr tagName = do_GetAtom(display); + nsCOMPtr tagName = NS_Atomize(display); // Check the white list if (!CheckTagNameWhiteList(nameSpaceID, tagName)) { const char16_t* params[] = { display.get() }; diff --git a/dom/xbl/nsXBLPrototypeHandler.cpp b/dom/xbl/nsXBLPrototypeHandler.cpp index 18f3b63b02..2f14df6530 100644 --- a/dom/xbl/nsXBLPrototypeHandler.cpp +++ b/dom/xbl/nsXBLPrototypeHandler.cpp @@ -226,7 +226,7 @@ nsXBLPrototypeHandler::ExecuteHandler(EventTarget* aTarget, // Look for a compiled handler on the element. // Should be compiled and bound with "on" in front of the name. - nsCOMPtr onEventAtom = do_GetAtom(NS_LITERAL_STRING("onxbl") + + nsCOMPtr onEventAtom = NS_Atomize(NS_LITERAL_STRING("onxbl") + nsDependentAtomString(mEventName)); // Compile the handler and bind it to the element. @@ -785,7 +785,7 @@ nsXBLPrototypeHandler::ConstructPrototype(nsIContent* aKeyElement, return; } - mEventName = do_GetAtom(event); + mEventName = NS_Atomize(event); if (aPhase) { const nsDependentString phase(aPhase); @@ -979,7 +979,7 @@ nsXBLPrototypeHandler::Read(nsIObjectInputStream* aStream) nsAutoString name; rv = aStream->ReadString(name); NS_ENSURE_SUCCESS(rv, rv); - mEventName = do_GetAtom(name); + mEventName = NS_Atomize(name); rv = aStream->Read32(&mLineNumber); NS_ENSURE_SUCCESS(rv, rv); diff --git a/dom/xbl/nsXBLResourceLoader.cpp b/dom/xbl/nsXBLResourceLoader.cpp index 7c15b6fa0c..aa6a15eb9a 100644 --- a/dom/xbl/nsXBLResourceLoader.cpp +++ b/dom/xbl/nsXBLResourceLoader.cpp @@ -233,7 +233,7 @@ nsXBLResourceLoader::NotifyBoundElements() if (ready) { // We need the document to flush out frame construction and // such, so we want to use the current document. - nsIDocument* doc = content->GetCurrentDoc(); + nsIDocument* doc = content->GetUncomposedDoc(); if (doc) { // Flush first to make sure we can get the frame for content diff --git a/dom/xbl/nsXBLService.cpp b/dom/xbl/nsXBLService.cpp index 7382bc0907..6b951a6c72 100644 --- a/dom/xbl/nsXBLService.cpp +++ b/dom/xbl/nsXBLService.cpp @@ -113,7 +113,7 @@ public: { // We only need the document here to cause frame construction, so // we need the current doc, not the owner doc. - nsIDocument* doc = mBoundElement->GetCurrentDoc(); + nsIDocument* doc = mBoundElement->GetUncomposedDoc(); if (!doc) return; @@ -318,7 +318,7 @@ nsXBLStreamListener::HandleEvent(nsIDOMEvent* aEvent) // ready. if (count > 0) { nsXBLBindingRequest* req = mBindingRequests.ElementAt(0); - nsIDocument* document = req->mBoundElement->GetCurrentDoc(); + nsIDocument* document = req->mBoundElement->GetUncomposedDoc(); if (document) document->FlushPendingNotifications(Flush_ContentAndNotify); } @@ -536,7 +536,7 @@ nsXBLService::AttachGlobalKeyHandler(EventTarget* aTarget) nsCOMPtr contentNode(do_QueryInterface(aTarget)); if (contentNode) { // Only attach if we're really in a document - nsCOMPtr doc = contentNode->GetCurrentDoc(); + nsCOMPtr doc = contentNode->GetUncomposedDoc(); if (doc) piTarget = doc; // We're a XUL keyset. Attach to our document. } @@ -582,7 +582,7 @@ nsXBLService::DetachGlobalKeyHandler(EventTarget* aTarget) return NS_ERROR_FAILURE; // Only attach if we're really in a document - nsCOMPtr doc = contentNode->GetCurrentDoc(); + nsCOMPtr doc = contentNode->GetUncomposedDoc(); if (doc) piTarget = do_QueryInterface(doc); diff --git a/dom/xbl/nsXBLWindowKeyHandler.cpp b/dom/xbl/nsXBLWindowKeyHandler.cpp index 4262dc7f8c..691a9f25bc 100644 --- a/dom/xbl/nsXBLWindowKeyHandler.cpp +++ b/dom/xbl/nsXBLWindowKeyHandler.cpp @@ -393,7 +393,7 @@ nsXBLWindowKeyHandler::HandleEvent(nsIDOMEvent* aEvent) nsAutoString eventType; aEvent->GetType(eventType); - nsCOMPtr eventTypeAtom = do_GetAtom(eventType); + nsCOMPtr eventTypeAtom = NS_Atomize(eventType); NS_ENSURE_TRUE(eventTypeAtom, NS_ERROR_OUT_OF_MEMORY); return WalkHandlers(keyEvent, eventTypeAtom); @@ -439,7 +439,7 @@ nsXBLWindowKeyHandler::HandleEventOnCaptureInSystemEventGroup( } nsCOMPtr originalTarget = - do_QueryInterface(aEvent->AsEvent()->WidgetEventPtr()->originalTarget); + do_QueryInterface(aEvent->AsEvent()->WidgetEventPtr()->mOriginalTarget); if (!EventStateManager::IsRemoteTarget(originalTarget)) { return; } @@ -695,7 +695,7 @@ nsXBLWindowKeyHandler::HasHandlerForEvent(nsIDOMKeyEvent* aEvent, nsAutoString eventType; aEvent->AsEvent()->GetType(eventType); - nsCOMPtr eventTypeAtom = do_GetAtom(eventType); + nsCOMPtr eventTypeAtom = NS_Atomize(eventType); NS_ENSURE_TRUE(eventTypeAtom, false); return WalkHandlersInternal(aEvent, eventTypeAtom, mHandler, false, @@ -727,7 +727,7 @@ nsXBLWindowKeyHandler::GetElementForHandler(nsXBLPrototypeHandler* aHandler, nsCOMPtr chromeHandlerElement = GetElement(); if (!chromeHandlerElement) { - NS_WARN_IF(!keyContent->IsInDoc()); + NS_WARN_IF(!keyContent->IsInUncomposedDoc()); nsCOMPtr keyElement = do_QueryInterface(keyContent); keyElement.swap(*aElementForHandler); return true; @@ -738,14 +738,14 @@ nsXBLWindowKeyHandler::GetElementForHandler(nsXBLPrototypeHandler* aHandler, keyContent->GetAttr(kNameSpaceID_None, nsGkAtoms::command, command); if (command.IsEmpty()) { // There is no command element associated with the key element. - NS_WARN_IF(!keyContent->IsInDoc()); + NS_WARN_IF(!keyContent->IsInUncomposedDoc()); nsCOMPtr keyElement = do_QueryInterface(keyContent); keyElement.swap(*aElementForHandler); return true; } // XXX Shouldn't we check this earlier? - nsIDocument* doc = keyContent->GetCurrentDoc(); + nsIDocument* doc = keyContent->GetUncomposedDoc(); if (NS_WARN_IF(!doc)) { return false; } diff --git a/dom/xml/ProcessingInstruction.cpp b/dom/xml/ProcessingInstruction.cpp index 0bea3acbc4..c341ff4e3f 100644 --- a/dom/xml/ProcessingInstruction.cpp +++ b/dom/xml/ProcessingInstruction.cpp @@ -22,7 +22,7 @@ NS_NewXMLProcessingInstruction(nsNodeInfoManager *aNodeInfoManager, NS_PRECONDITION(aNodeInfoManager, "Missing nodeinfo manager"); - nsCOMPtr target = do_GetAtom(aTarget); + nsCOMPtr target = NS_Atomize(aTarget); MOZ_ASSERT(target); if (target == nsGkAtoms::xml_stylesheet) { diff --git a/dom/xml/XMLStylesheetProcessingInstruction.cpp b/dom/xml/XMLStylesheetProcessingInstruction.cpp index fcb5a8bc73..cc5734c6a4 100644 --- a/dom/xml/XMLStylesheetProcessingInstruction.cpp +++ b/dom/xml/XMLStylesheetProcessingInstruction.cpp @@ -71,7 +71,7 @@ XMLStylesheetProcessingInstruction::BindToTree(nsIDocument* aDocument, void XMLStylesheetProcessingInstruction::UnbindFromTree(bool aDeep, bool aNullParent) { - nsCOMPtr oldDoc = GetCurrentDoc(); + nsCOMPtr oldDoc = GetUncomposedDoc(); ProcessingInstruction::UnbindFromTree(aDeep, aNullParent); UpdateStyleSheetInternal(oldDoc, nullptr); diff --git a/dom/xml/nsXMLContentSink.cpp b/dom/xml/nsXMLContentSink.cpp index 875c2c63e4..5fe107db05 100644 --- a/dom/xml/nsXMLContentSink.cpp +++ b/dom/xml/nsXMLContentSink.cpp @@ -1149,7 +1149,7 @@ nsXMLContentSink::HandleDoctypeDecl(const nsAString & aSubset, NS_ASSERTION(mDocument, "Shouldn't get here from a document fragment"); - nsCOMPtr name = do_GetAtom(aName); + nsCOMPtr name = NS_Atomize(aName); NS_ENSURE_TRUE(name, NS_ERROR_OUT_OF_MEMORY); // Create a new doctype node diff --git a/dom/xslt/base/txExpandedName.cpp b/dom/xslt/base/txExpandedName.cpp index 3cbc52a0f2..43283e4255 100644 --- a/dom/xslt/base/txExpandedName.cpp +++ b/dom/xslt/base/txExpandedName.cpp @@ -22,7 +22,7 @@ txExpandedName::init(const nsAString& aQName, txNamespaceMap* aResolver, } if (colon) { - nsCOMPtr prefix = do_GetAtom(Substring(qName.get(), colon)); + nsCOMPtr prefix = NS_Atomize(Substring(qName.get(), colon)); int32_t namespaceID = aResolver->lookupNamespace(prefix); if (namespaceID == kNameSpaceID_Unknown) return NS_ERROR_FAILURE; @@ -30,12 +30,12 @@ txExpandedName::init(const nsAString& aQName, txNamespaceMap* aResolver, const char16_t *end; qName.EndReading(end); - mLocalName = do_GetAtom(Substring(colon + 1, end)); + mLocalName = NS_Atomize(Substring(colon + 1, end)); } else { mNamespaceID = aUseDefault ? aResolver->lookupNamespace(nullptr) : kNameSpaceID_None; - mLocalName = do_GetAtom(aQName); + mLocalName = NS_Atomize(aQName); } return NS_OK; } diff --git a/dom/xslt/base/txNamespaceMap.cpp b/dom/xslt/base/txNamespaceMap.cpp index 7124245d86..cb6c4b1642 100644 --- a/dom/xslt/base/txNamespaceMap.cpp +++ b/dom/xslt/base/txNamespaceMap.cpp @@ -89,7 +89,7 @@ txNamespaceMap::lookupNamespace(nsIAtom* aPrefix) int32_t txNamespaceMap::lookupNamespaceWithDefault(const nsAString& aPrefix) { - nsCOMPtr prefix = do_GetAtom(aPrefix); + nsCOMPtr prefix = NS_Atomize(aPrefix); if (prefix != nsGkAtoms::_poundDefault) { return lookupNamespace(prefix); } diff --git a/dom/xslt/base/txStringUtils.h b/dom/xslt/base/txStringUtils.h index a91adcdfe4..6f45fe661e 100644 --- a/dom/xslt/base/txStringUtils.h +++ b/dom/xslt/base/txStringUtils.h @@ -28,7 +28,7 @@ TX_ToLowerCaseAtom(nsIAtom* aAtom) nsAutoString str; aAtom->ToString(str); nsContentUtils::ASCIIToLower(str); - return do_GetAtom(str); + return NS_Atomize(str); } #endif // txStringUtils_h__ diff --git a/dom/xslt/xml/txXMLUtils.cpp b/dom/xslt/xml/txXMLUtils.cpp index bc366e202d..58687568d9 100644 --- a/dom/xslt/xml/txXMLUtils.cpp +++ b/dom/xslt/xml/txXMLUtils.cpp @@ -58,7 +58,7 @@ XMLUtils::splitExpatName(const char16_t *aExpatName, nsIAtom **aPrefix, nameStart = (uriEnd + 1); if (nameEnd) { const char16_t *prefixStart = nameEnd + 1; - *aPrefix = NS_NewAtom(Substring(prefixStart, pos)).take(); + *aPrefix = NS_Atomize(Substring(prefixStart, pos)).take(); if (!*aPrefix) { return NS_ERROR_OUT_OF_MEMORY; } @@ -75,7 +75,7 @@ XMLUtils::splitExpatName(const char16_t *aExpatName, nsIAtom **aPrefix, *aPrefix = nullptr; } - *aLocalName = NS_NewAtom(Substring(nameStart, nameEnd)).take(); + *aLocalName = NS_Atomize(Substring(nameStart, nameEnd)).take(); return *aLocalName ? NS_OK : NS_ERROR_OUT_OF_MEMORY; } @@ -95,12 +95,12 @@ XMLUtils::splitQName(const nsAString& aName, nsIAtom** aPrefix, const char16_t *end; qName.EndReading(end); - *aPrefix = NS_NewAtom(Substring(qName.get(), colon)).take(); - *aLocalName = NS_NewAtom(Substring(colon + 1, end)).take(); + *aPrefix = NS_Atomize(Substring(qName.get(), colon)).take(); + *aLocalName = NS_Atomize(Substring(colon + 1, end)).take(); } else { *aPrefix = nullptr; - *aLocalName = NS_NewAtom(aName).take(); + *aLocalName = NS_Atomize(aName).take(); } return NS_OK; diff --git a/dom/xslt/xpath/txExpr.h b/dom/xslt/xpath/txExpr.h index 78a72d5b51..562fca7a32 100644 --- a/dom/xslt/xpath/txExpr.h +++ b/dom/xslt/xpath/txExpr.h @@ -478,7 +478,7 @@ public: */ void setNodeName(const nsAString& aName) { - mNodeName = do_GetAtom(aName); + mNodeName = NS_Atomize(aName); } NodeType getNodeTestType() diff --git a/dom/xslt/xpath/txExprParser.cpp b/dom/xslt/xpath/txExprParser.cpp index f35a0dc474..f8474004bd 100644 --- a/dom/xslt/xpath/txExprParser.cpp +++ b/dom/xslt/xpath/txExprParser.cpp @@ -482,7 +482,7 @@ txExprParser::createLocationStep(txExprLexer& lexer, txIParseContext* aContext, { //-- eat token lexer.nextToken(); - nsCOMPtr axis = do_GetAtom(tok->Value()); + nsCOMPtr axis = NS_Atomize(tok->Value()); if (axis == nsGkAtoms::ancestor) { axisIdentifier = LocationStep::ANCESTOR_AXIS; } @@ -894,11 +894,11 @@ txExprParser::resolveQName(const nsAString& aQName, aNamespace = kNameSpaceID_None; int32_t idx = aQName.FindChar(':'); if (idx > 0) { - *aPrefix = NS_NewAtom(StringHead(aQName, (uint32_t)idx)).take(); + *aPrefix = NS_Atomize(StringHead(aQName, (uint32_t)idx)).take(); if (!*aPrefix) { return NS_ERROR_OUT_OF_MEMORY; } - *aLocalName = NS_NewAtom(Substring(aQName, (uint32_t)idx + 1, + *aLocalName = NS_Atomize(Substring(aQName, (uint32_t)idx + 1, aQName.Length() - (idx + 1))).take(); if (!*aLocalName) { NS_RELEASE(*aPrefix); @@ -911,10 +911,10 @@ txExprParser::resolveQName(const nsAString& aQName, if (aIsNameTest && aContext->caseInsensitiveNameTests()) { nsAutoString lcname; nsContentUtils::ASCIIToLower(aQName, lcname); - *aLocalName = NS_NewAtom(lcname).take(); + *aLocalName = NS_Atomize(lcname).take(); } else { - *aLocalName = NS_NewAtom(aQName).take(); + *aLocalName = NS_Atomize(aQName).take(); } if (!*aLocalName) { return NS_ERROR_OUT_OF_MEMORY; diff --git a/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp b/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp index fee671ea59..de0a84c7d7 100644 --- a/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp +++ b/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp @@ -48,7 +48,7 @@ txXPathTreeWalker::moveToRoot() return; } - nsIDocument* root = mPosition.mNode->GetCurrentDoc(); + nsIDocument* root = mPosition.mNode->GetUncomposedDoc(); if (root) { mPosition.mIndex = txXPathNode::eDocument; mPosition.mNode = root; @@ -74,7 +74,7 @@ txXPathTreeWalker::moveToElementById(const nsAString& aID) return false; } - nsIDocument* doc = mPosition.mNode->GetCurrentDoc(); + nsIDocument* doc = mPosition.mNode->GetUncomposedDoc(); nsCOMPtr content; if (doc) { @@ -358,7 +358,7 @@ txXPathNodeUtils::getLocalName(const txXPathNode& aNode) nsAutoString target; node->GetNodeName(target); - return NS_NewAtom(target); + return NS_Atomize(target); } return nullptr; @@ -591,8 +591,8 @@ txXPathNodeUtils::comparePosition(const txXPathNode& aNode, } // Get document for both nodes. - nsIDocument* document = aNode.mNode->GetCurrentDoc(); - nsIDocument* otherDocument = aOtherNode.mNode->GetCurrentDoc(); + nsIDocument* document = aNode.mNode->GetUncomposedDoc(); + nsIDocument* otherDocument = aOtherNode.mNode->GetUncomposedDoc(); // If the nodes have different current documents, compare the document // pointers. diff --git a/dom/xslt/xslt/txInstructions.cpp b/dom/xslt/xslt/txInstructions.cpp index 56676fc390..d3b12f1525 100644 --- a/dom/xslt/xslt/txInstructions.cpp +++ b/dom/xslt/xslt/txInstructions.cpp @@ -114,7 +114,7 @@ txAttribute::execute(txExecutionState& aEs) nsCOMPtr prefix; uint32_t lnameStart = 0; if (colon) { - prefix = do_GetAtom(Substring(name.get(), colon)); + prefix = NS_Atomize(Substring(name.get(), colon)); lnameStart = colon - name.get() + 1; } @@ -830,7 +830,7 @@ txStartElement::execute(txExecutionState& aEs) const char16_t* colon; if (XMLUtils::isValidQName(name, &colon)) { if (colon) { - prefix = do_GetAtom(Substring(name.get(), colon)); + prefix = NS_Atomize(Substring(name.get(), colon)); lnameStart = colon - name.get() + 1; } diff --git a/dom/xslt/xslt/txMozillaXMLOutput.cpp b/dom/xslt/xslt/txMozillaXMLOutput.cpp index 227eb72642..018d863245 100644 --- a/dom/xslt/xslt/txMozillaXMLOutput.cpp +++ b/dom/xslt/xslt/txMozillaXMLOutput.cpp @@ -132,10 +132,10 @@ txMozillaXMLOutput::attribute(nsIAtom* aPrefix, if (mOpenedElementIsHTML && aNsID == kNameSpaceID_None) { nsAutoString lnameStr; nsContentUtils::ASCIIToLower(aLocalName, lnameStr); - lname = do_GetAtom(lnameStr); + lname = NS_Atomize(lnameStr); } else { - lname = do_GetAtom(aLocalName); + lname = NS_Atomize(aLocalName); } NS_ENSURE_TRUE(lname, NS_ERROR_OUT_OF_MEMORY); @@ -462,10 +462,10 @@ txMozillaXMLOutput::startElement(nsIAtom* aPrefix, nsAutoString lnameStr; nsContentUtils::ASCIIToLower(aLocalName, lnameStr); - lname = do_GetAtom(lnameStr); + lname = NS_Atomize(lnameStr); } else { - lname = do_GetAtom(aLocalName); + lname = NS_Atomize(aLocalName); } // No biggie if we lose the prefix due to OOM @@ -723,7 +723,7 @@ txMozillaXMLOutput::startHTMLElement(nsIContent* aElement, bool aIsHTML) NS_ENSURE_SUCCESS(rv, rv); // No need to notify since aElement hasn't been inserted yet - NS_ASSERTION(!aElement->IsInDoc(), "should not be in doc"); + NS_ASSERTION(!aElement->IsInUncomposedDoc(), "should not be in doc"); rv = aElement->AppendChildTo(meta, false); NS_ENSURE_SUCCESS(rv, rv); } @@ -756,7 +756,7 @@ txMozillaXMLOutput::endHTMLElement(nsIContent* aElement) aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::content, value); if (!value.IsEmpty()) { nsContentUtils::ASCIIToLower(httpEquiv); - nsCOMPtr header = do_GetAtom(httpEquiv); + nsCOMPtr header = NS_Atomize(httpEquiv); processHTTPEquiv(header, value); } } @@ -889,7 +889,7 @@ txMozillaXMLOutput::createResultDocument(const nsSubstring& aName, int32_t aNsID nsresult rv = nsContentUtils::CheckQName(qName); if (NS_SUCCEEDED(rv)) { - nsCOMPtr doctypeName = do_GetAtom(qName); + nsCOMPtr doctypeName = NS_Atomize(qName); if (!doctypeName) { return NS_ERROR_OUT_OF_MEMORY; } diff --git a/dom/xslt/xslt/txMozillaXSLTProcessor.cpp b/dom/xslt/xslt/txMozillaXSLTProcessor.cpp index 798aa3e021..462e34eaeb 100644 --- a/dom/xslt/xslt/txMozillaXSLTProcessor.cpp +++ b/dom/xslt/xslt/txMozillaXSLTProcessor.cpp @@ -406,7 +406,7 @@ NS_IMETHODIMP txMozillaXSLTProcessor::AddXSLTParamNamespace(const nsString& aPrefix, const nsString& aNamespace) { - nsCOMPtr pre = do_GetAtom(aPrefix); + nsCOMPtr pre = NS_Atomize(aPrefix); return mParamNamespaceMap.mapNamespace(pre, aNamespace); } @@ -528,7 +528,7 @@ txMozillaXSLTProcessor::AddXSLTParam(const nsString& aName, value = new StringResult(aValue, nullptr); } - nsCOMPtr name = do_GetAtom(aName); + nsCOMPtr name = NS_Atomize(aName); int32_t nsId = kNameSpaceID_Unknown; rv = nsContentUtils::NameSpaceManager()-> RegisterNameSpace(aNamespace, nsId); @@ -948,7 +948,7 @@ txMozillaXSLTProcessor::SetParameter(const nsAString & aNamespaceURI, nsresult rv = nsContentUtils::NameSpaceManager()-> RegisterNameSpace(aNamespaceURI, nsId); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr localName = do_GetAtom(aLocalName); + nsCOMPtr localName = NS_Atomize(aLocalName); txExpandedName varName(nsId, localName); RefPtr txValue; @@ -976,7 +976,7 @@ txMozillaXSLTProcessor::GetParameter(const nsAString& aNamespaceURI, nsresult rv = nsContentUtils::NameSpaceManager()-> RegisterNameSpace(aNamespaceURI, nsId); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr localName = do_GetAtom(aLocalName); + nsCOMPtr localName = NS_Atomize(aLocalName); txExpandedName varName(nsId, localName); txVariable* var = static_cast(mVariables.get(varName)); @@ -994,7 +994,7 @@ txMozillaXSLTProcessor::RemoveParameter(const nsAString& aNamespaceURI, nsresult rv = nsContentUtils::NameSpaceManager()-> RegisterNameSpace(aNamespaceURI, nsId); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr localName = do_GetAtom(aLocalName); + nsCOMPtr localName = NS_Atomize(aLocalName); txExpandedName varName(nsId, localName); mVariables.remove(varName); diff --git a/dom/xslt/xslt/txPatternParser.cpp b/dom/xslt/xslt/txPatternParser.cpp index 5dd6e0f491..f902aa22c9 100644 --- a/dom/xslt/xslt/txPatternParser.cpp +++ b/dom/xslt/xslt/txPatternParser.cpp @@ -130,7 +130,7 @@ nsresult txPatternParser::createLocPathPattern(txExprLexer& aLexer, // id(Literal) or key(Literal, Literal) { nsCOMPtr nameAtom = - do_GetAtom(aLexer.nextToken()->Value()); + NS_Atomize(aLexer.nextToken()->Value()); if (nameAtom == nsGkAtoms::id) { rv = createIdPattern(aLexer, stepPattern); } diff --git a/dom/xslt/xslt/txStylesheetCompileHandlers.cpp b/dom/xslt/xslt/txStylesheetCompileHandlers.cpp index 295604cd11..3a17098a04 100644 --- a/dom/xslt/xslt/txStylesheetCompileHandlers.cpp +++ b/dom/xslt/xslt/txStylesheetCompileHandlers.cpp @@ -303,7 +303,7 @@ getAtomAttr(txStylesheetAttr* aAttributes, return rv; } - *aAtom = NS_NewAtom(attr->mValue).take(); + *aAtom = NS_Atomize(attr->mValue).take(); NS_ENSURE_TRUE(*aAtom, NS_ERROR_OUT_OF_MEMORY); return NS_OK; @@ -2864,7 +2864,7 @@ txHandlerTable::init(const txElementHandler* aHandlers, uint32_t aCount) uint32_t i; for (i = 0; i < aCount; ++i) { - nsCOMPtr nameAtom = do_GetAtom(aHandlers->mLocalName); + nsCOMPtr nameAtom = NS_Atomize(aHandlers->mLocalName); txExpandedName name(aHandlers->mNamespaceID, nameAtom); rv = mHandlers.add(name, aHandlers); NS_ENSURE_SUCCESS(rv, rv); diff --git a/dom/xslt/xslt/txXSLTPatterns.cpp b/dom/xslt/xslt/txXSLTPatterns.cpp index 7d43e5db94..c8fe71f723 100644 --- a/dom/xslt/xslt/txXSLTPatterns.cpp +++ b/dom/xslt/xslt/txXSLTPatterns.cpp @@ -259,7 +259,7 @@ txIdPattern::txIdPattern(const nsSubstring& aString) nsWhitespaceTokenizer tokenizer(aString); while (tokenizer.hasMoreTokens()) { // this can fail, XXX move to a Init(aString) method - nsCOMPtr atom = do_GetAtom(tokenizer.nextToken()); + nsCOMPtr atom = NS_Atomize(tokenizer.nextToken()); mIds.AppendObject(atom); } } diff --git a/dom/xul/XULDocument.cpp b/dom/xul/XULDocument.cpp index 8640b954c8..839ee6acae 100644 --- a/dom/xul/XULDocument.cpp +++ b/dom/xul/XULDocument.cpp @@ -690,7 +690,7 @@ XULDocument::SynchronizeBroadcastListener(Element *aBroadcaster, } else { // Find out if the attribute is even present at all. - nsCOMPtr name = do_GetAtom(aAttr); + nsCOMPtr name = NS_Atomize(aAttr); nsAutoString value; if (aBroadcaster->GetAttr(kNameSpaceID_None, name, value)) { @@ -771,7 +771,7 @@ XULDocument::AddBroadcastListenerFor(Element& aBroadcaster, Element& aListener, } // Only add the listener if it's not there already! - nsCOMPtr attr = do_GetAtom(aAttr); + nsCOMPtr attr = NS_Atomize(aAttr); for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) { BroadcastListener* bl = entry->mListeners[i]; @@ -815,7 +815,7 @@ XULDocument::RemoveBroadcastListenerFor(Element& aBroadcaster, auto entry = static_cast (mBroadcasterMap->Search(&aBroadcaster)); if (entry) { - nsCOMPtr attr = do_GetAtom(aAttr); + nsCOMPtr attr = NS_Atomize(aAttr); for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) { BroadcastListener* bl = entry->mListeners[i]; nsCOMPtr blListener = do_QueryReferent(bl->mListener); @@ -1180,7 +1180,7 @@ already_AddRefed XULDocument::GetElementsByAttribute(const nsAString& aAttribute, const nsAString& aValue) { - nsCOMPtr attrAtom(do_GetAtom(aAttribute)); + nsCOMPtr attrAtom(NS_Atomize(aAttribute)); void* attrValue = new nsString(aValue); RefPtr list = new nsContentList(this, MatchAttribute, @@ -1211,7 +1211,7 @@ XULDocument::GetElementsByAttributeNS(const nsAString& aNamespaceURI, const nsAString& aValue, ErrorResult& aRv) { - nsCOMPtr attrAtom(do_GetAtom(aAttribute)); + nsCOMPtr attrAtom(NS_Atomize(aAttribute)); void* attrValue = new nsString(aValue); int32_t nameSpaceId = kNameSpaceID_Wildcard; @@ -1272,7 +1272,7 @@ XULDocument::Persist(const nsAString& aID, return NS_ERROR_NOT_IMPLEMENTED; } - tag = do_GetAtom(aAttr); + tag = NS_Atomize(aAttr); NS_ENSURE_TRUE(tag, NS_ERROR_OUT_OF_MEMORY); nameSpaceID = kNameSpaceID_None; @@ -2127,7 +2127,7 @@ XULDocument::ApplyPersistentAttributesToElements(const nsAString &aID, return rv; } - nsCOMPtr attr = do_GetAtom(attrstr); + nsCOMPtr attr = NS_Atomize(attrstr); if (NS_WARN_IF(!attr)) { return NS_ERROR_OUT_OF_MEMORY; } diff --git a/dom/xul/nsXULElement.cpp b/dom/xul/nsXULElement.cpp index 2a7cf70036..692ed7f198 100644 --- a/dom/xul/nsXULElement.cpp +++ b/dom/xul/nsXULElement.cpp @@ -436,7 +436,7 @@ already_AddRefed nsXULElement::GetElementsByAttribute(const nsAString& aAttribute, const nsAString& aValue) { - nsCOMPtr attrAtom(do_GetAtom(aAttribute)); + nsCOMPtr attrAtom(NS_Atomize(aAttribute)); void* attrValue = new nsString(aValue); RefPtr list = new nsContentList(this, @@ -467,7 +467,7 @@ nsXULElement::GetElementsByAttributeNS(const nsAString& aNamespaceURI, const nsAString& aValue, ErrorResult& rv) { - nsCOMPtr attrAtom(do_GetAtom(aAttribute)); + nsCOMPtr attrAtom(NS_Atomize(aAttribute)); int32_t nameSpaceId = kNameSpaceID_Wildcard; if (!aNamespaceURI.EqualsLiteral("*")) { @@ -1049,14 +1049,14 @@ nsXULElement::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, nsAttrValueOrString* aValue, bool aNotify) { if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::accesskey && - IsInDoc()) { + IsInUncomposedDoc()) { nsAutoString oldValue; if (GetAttr(aNamespaceID, aName, oldValue)) { UnregisterAccessKey(oldValue); } } else if (aNamespaceID == kNameSpaceID_None && (aName == nsGkAtoms::command || aName == nsGkAtoms::observes) && - IsInDoc()) { + IsInUncomposedDoc()) { // XXX sXBL/XBL2 issue! Owner or current document? nsAutoString oldValue; GetAttr(kNameSpaceID_None, nsGkAtoms::observes, oldValue); @@ -1287,7 +1287,7 @@ nsXULElement::PreHandleEvent(EventChainPreVisitor& aVisitor) } if (aVisitor.mEvent->mMessage == eXULCommand && aVisitor.mEvent->mClass == eInputEventClass && - aVisitor.mEvent->originalTarget == static_cast(this) && + aVisitor.mEvent->mOriginalTarget == static_cast(this) && !IsXULElement(nsGkAtoms::command)) { // Check that we really have an xul command event. That will be handled // in a special way. @@ -1570,7 +1570,7 @@ nsXULElement::LoadSrc() nsGkAtoms::iframe)) { return NS_OK; } - if (!IsInDoc() || + if (!IsInUncomposedDoc() || !OwnerDoc()->GetRootElement() || OwnerDoc()->GetRootElement()-> NodeInfo()->Equals(nsGkAtoms::overlay, kNameSpaceID_XUL)) { diff --git a/dom/xul/nsXULPrototypeDocument.cpp b/dom/xul/nsXULPrototypeDocument.cpp index ee3dbac27e..01d4794dbf 100644 --- a/dom/xul/nsXULPrototypeDocument.cpp +++ b/dom/xul/nsXULPrototypeDocument.cpp @@ -173,7 +173,7 @@ nsXULPrototypeDocument::Read(nsIObjectInputStream* aStream) if (NS_FAILED(tmp)) { rv = tmp; } - prefix = do_GetAtom(prefixStr); + prefix = NS_Atomize(prefixStr); } tmp = aStream->ReadString(localName); if (NS_FAILED(tmp)) { diff --git a/dom/xul/templates/nsXULContentBuilder.cpp b/dom/xul/templates/nsXULContentBuilder.cpp index 246494016f..71c285cc4c 100644 --- a/dom/xul/templates/nsXULContentBuilder.cpp +++ b/dom/xul/templates/nsXULContentBuilder.cpp @@ -823,7 +823,7 @@ nsXULContentBuilder::AddPersistentAttributes(Element* aTemplateNode, nameSpaceID = ni->NamespaceID(); } else { - tag = do_GetAtom(attribute); + tag = NS_Atomize(attribute); NS_ENSURE_TRUE(tag, NS_ERROR_OUT_OF_MEMORY); nameSpaceID = kNameSpaceID_None; diff --git a/dom/xul/templates/nsXULSortService.cpp b/dom/xul/templates/nsXULSortService.cpp index 291ab5c43a..ab3e134610 100644 --- a/dom/xul/templates/nsXULSortService.cpp +++ b/dom/xul/templates/nsXULSortService.cpp @@ -361,13 +361,13 @@ XULSortServiceImpl::InitializeSortState(nsIContent* aRootElement, nsAutoString sortResource, sortResource2; aRootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::sortResource, sortResource); if (!sortResource.IsEmpty()) { - nsCOMPtr sortkeyatom = do_GetAtom(sortResource); + nsCOMPtr sortkeyatom = NS_Atomize(sortResource); aSortState->sortKeys.AppendObject(sortkeyatom); sort.Append(sortResource); aRootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::sortResource2, sortResource2); if (!sortResource2.IsEmpty()) { - nsCOMPtr sortkeyatom2 = do_GetAtom(sortResource2); + nsCOMPtr sortkeyatom2 = NS_Atomize(sortResource2); aSortState->sortKeys.AppendObject(sortkeyatom2); sort.Append(' '); sort.Append(sortResource2); @@ -377,7 +377,7 @@ XULSortServiceImpl::InitializeSortState(nsIContent* aRootElement, else { nsWhitespaceTokenizer tokenizer(sort); while (tokenizer.hasMoreTokens()) { - nsCOMPtr keyatom = do_GetAtom(tokenizer.nextToken()); + nsCOMPtr keyatom = NS_Atomize(tokenizer.nextToken()); NS_ENSURE_TRUE(keyatom, NS_ERROR_OUT_OF_MEMORY); aSortState->sortKeys.AppendObject(keyatom); } diff --git a/dom/xul/templates/nsXULTemplateBuilder.cpp b/dom/xul/templates/nsXULTemplateBuilder.cpp index 9d4654e8c4..92285fb3c7 100644 --- a/dom/xul/templates/nsXULTemplateBuilder.cpp +++ b/dom/xul/templates/nsXULTemplateBuilder.cpp @@ -1589,7 +1589,7 @@ nsXULTemplateBuilder::SubstituteTextReplaceVariable(nsXULTemplateBuilder* aThis, } else { // Got a variable; get the value it's assigned to - nsCOMPtr var = do_GetAtom(aVariable); + nsCOMPtr var = NS_Atomize(aVariable); c->result->GetBindingFor(var, replacementText); } @@ -1726,9 +1726,9 @@ nsXULTemplateBuilder::CompileQueries() tmpl->GetAttr(kNameSpaceID_None, nsGkAtoms::container, containervar); if (containervar.IsEmpty()) - mRefVariable = do_GetAtom("?uri"); + mRefVariable = NS_Atomize("?uri"); else - mRefVariable = do_GetAtom(containervar); + mRefVariable = NS_Atomize(containervar); nsAutoString membervar; tmpl->GetAttr(kNameSpaceID_None, nsGkAtoms::member, membervar); @@ -1736,7 +1736,7 @@ nsXULTemplateBuilder::CompileQueries() if (membervar.IsEmpty()) mMemberVariable = nullptr; else - mMemberVariable = do_GetAtom(membervar); + mMemberVariable = NS_Atomize(membervar); nsTemplateQuerySet* queryset = new nsTemplateQuerySet(0); if (!mQuerySets.AppendElement(queryset)) { @@ -2049,7 +2049,7 @@ nsXULTemplateBuilder::DetermineMemberVariable(nsIContent* aElement) nsAutoString uri; child->GetAttr(kNameSpaceID_None, nsGkAtoms::uri, uri); if (!uri.IsEmpty() && uri[0] == char16_t('?')) { - return NS_NewAtom(uri); + return NS_Atomize(uri); } nsCOMPtr result = DetermineMemberVariable(child); @@ -2084,13 +2084,13 @@ nsXULTemplateBuilder::DetermineRDFQueryRef(nsIContent* aQueryElement, nsIAtom** content->GetAttr(kNameSpaceID_None, nsGkAtoms::uri, uri); if (!uri.IsEmpty()) - mRefVariable = do_GetAtom(uri); + mRefVariable = NS_Atomize(uri); nsAutoString tag; content->GetAttr(kNameSpaceID_None, nsGkAtoms::tag, tag); if (!tag.IsEmpty()) - *aTag = NS_NewAtom(tag).take(); + *aTag = NS_Atomize(tag).take(); } } @@ -2107,7 +2107,7 @@ nsXULTemplateBuilder::CompileSimpleQuery(nsIContent* aRuleElement, if (mMemberVariable) memberVariable = mMemberVariable; else - memberVariable = do_GetAtom("rdf:*"); + memberVariable = NS_Atomize("rdf:*"); // since there is no node for a simple query, the query node will // be either the node if multiple rules are used, or the