Bug 1357639 - part4 : add Fennec test for media control and audio focus. draft
authorAlastor Wu <alwu@mozilla.com>
Mon, 08 May 2017 14:10:46 +0800
changeset 573966 39e42b2a94e353c99d4576add43c72c4bfc277a3
parent 573965 9c98cf69f20f0212fadfa71076e30859d2e9510f
child 573967 a207eeb9c343a3d1a7d7e32a8616ec35b0ab34e2
push id57550
push useralwu@mozilla.com
push dateMon, 08 May 2017 07:06:13 +0000
bugs1357639
milestone55.0a1
Bug 1357639 - part4 : add Fennec test for media control and audio focus. Add robocop tests and mochitest chrome. MozReview-Commit-ID: JofkKRSNdB5
mobile/android/tests/browser/chrome/audio.ogg
mobile/android/tests/browser/chrome/chrome.ini
mobile/android/tests/browser/chrome/media_playback.html
mobile/android/tests/browser/chrome/test_media_playback.html
mobile/android/tests/browser/robocop/audio.ogg
mobile/android/tests/browser/robocop/robocop.ini
mobile/android/tests/browser/robocop/robocop_autophone.ini
mobile/android/tests/browser/robocop/robocop_media_playback_loop.html
mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/BaseTest.java
mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/StringHelper.java
mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAudioFocus.java
mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testMediaControl.java
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..7f1833508ab3fd9e6d4f0241f37cc2ee5a1679e5
GIT binary patch
literal 14290
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
POZKSwFdSr|SpUBOl(=Qu
--- a/mobile/android/tests/browser/chrome/chrome.ini
+++ b/mobile/android/tests/browser/chrome/chrome.ini
@@ -1,20 +1,22 @@
 [DEFAULT]
 skip-if = os != 'android'
 support-files =
+  audio.ogg
   basic_article.html
   basic_article_mobile.html
   basic_article_mobile_2x.html
   browser_scrollPositions_sample.html
   browser_scrollPositions_sample2.html
   desktopmode_user_agent.sjs
   devicesearch.xml
   head.js
   head_search.js
+  media_playback.html
   session_formdata_sample.html
   simpleservice.xml
   video_controls.html
   video_discovery.html
   video_discovery.sjs
   web_channel.html
   tp5/**
 
@@ -29,16 +31,17 @@ skip-if = debug
 [test_desktop_useragent.html]
 [test_device_search_engine.html]
 [test_get_last_visited.html]
 [test_home_provider.html]
 [test_hidden_select_option.html]
 [test_identity_mode.html]
 [test_java_addons.html]
 [test_jni.html]
+[test_media_playback.html]
 [test_migrate_ui.html]
 [test_network_manager.html]
 [test_offline_page.html]
 skip-if = true # Bug 1241478
 [test_reader_view.html]
 [test_resource_substitutions.html]
 [test_restricted_profiles.html]
 [test_select_disabled.html]
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/browser/chrome/media_playback.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<audio id="testAudio" src="audio.ogg" loop></audio>
+<script type="text/javascript">
+var audio = document.getElementById("testAudio");
+audio.oncanplay = function() {
+  audio.oncanplay = null;
+  audio.play();
+};
+</script>
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/browser/chrome/test_media_playback.html
@@ -0,0 +1,154 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for media playback</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript">
+
+"use strict";
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/Task.jsm");
+
+let gChromeWin;
+let gBrowserApp;
+
+const URL = "http://example.org/chrome/mobile/android/tests/browser/chrome/media_playback.html";
+
+// Waiting for a tab to load or restore can be slow on the emulator.
+SimpleTest.requestLongerTimeout(2);
+
+setup_browser();
+
+function getAudio(browser) {
+  return browser.contentWindow.document.getElementById("testAudio");
+}
+
+function playAudio(browser) {
+  let audio = getAudio(browser);
+  if (!audio) {
+    ok(false, "can't get the audio!")
+  }
+  return audio.play();
+}
+
+function stopAudio(browser) {
+  let audio = getAudio(browser);
+  if (!audio) {
+    ok(false, "can't get the audio!")
+  }
+  audio.pause();
+}
+
+function isAudioStarted(browser) {
+  let audio = getAudio(browser);
+  if (!audio) {
+    ok(false, "can't get the audio!")
+  }
+  return !audio.paused;
+}
+
+function setup_browser() {
+  gChromeWin = Services.wm.getMostRecentWindow("navigator:browser");
+  gBrowserApp = gChromeWin.BrowserApp;
+}
+
+add_task(function* test_media_control() {
+  info("- open a new tab -");
+  let tab = gBrowserApp.addTab(URL);
+  let browser = tab.browser;
+
+  info("- wait for loading tab's content -");
+  yield promiseBrowserEvent(browser, "load");
+
+  info("- check whether audio starts playing -");
+  yield promiseTabEvent(browser, "DOMAudioPlaybackStarted");
+  ok(isAudioStarted(browser), "audio has started playing.");
+  ok(tab.playingAudio, "tab is playing audio.");
+
+  info("- pause from control -");
+  Services.obs.notifyObservers(browser, "mediaControl", "mediaControlPaused");
+
+  info("- check whether audio stops playing -");
+  yield promiseTabEvent(browser, "DOMAudioPlaybackStopped");
+  ok(!isAudioStarted(browser), "audio has stopped playing.");
+  ok(!tab.playingAudio, "tab isn't playing audio.");
+
+  info("- resume from control -");
+  Services.obs.notifyObservers(browser, "mediaControl", "resumeMedia");
+
+  info("- check whether audio starts playing -");
+  yield promiseTabEvent(browser, "DOMAudioPlaybackStarted");
+  ok(isAudioStarted(browser), "audio has started playing.");
+  ok(tab.playingAudio, "tab is playing audio.");
+
+  info("- stop from control -");
+  Services.obs.notifyObservers(browser, "mediaControl", "mediaControlStopped");
+
+  info("- check whether audio stops playing -");
+  yield promiseTabEvent(browser, "DOMAudioPlaybackStopped");
+  ok(!isAudioStarted(browser), "audio has stopped playing.");
+  ok(!tab.playingAudio, "tab isn't playing audio.");
+
+  info("- remove tab -");
+  gBrowserApp.closeTab(tab);
+});
+
+add_task(function* test_audio_focus() {
+  info("- open a new tab -");
+  let tab = gBrowserApp.addTab(URL);
+  let browser = tab.browser;
+
+  info("- wait for loading tab's content -");
+  yield promiseBrowserEvent(browser, "load");
+
+  info("- check whether audio starts playing -");
+  yield promiseTabEvent(browser, "DOMAudioPlaybackStarted");
+  ok(isAudioStarted(browser), "audio has started playing.");
+  ok(tab.playingAudio, "tab is playing audio.");
+
+  info("- pause when transiently lossing audio focus -");
+  Services.obs.notifyObservers(browser, "audioFocusChanged", "lostAudioFocusTransiently");
+
+  info("- check whether audio stops playing -");
+  yield promiseTabEvent(browser, "DOMAudioPlaybackStopped");
+  ok(!isAudioStarted(browser), "audio has stopped playing.");
+  ok(!tab.playingAudio, "tab isn't playing audio.");
+
+  info("- resume when gain audio focus again -");
+  Services.obs.notifyObservers(browser, "audioFocusChanged", "gainAudioFocus");
+
+  info("- check whether audio starts playing -");
+  yield promiseTabEvent(browser, "DOMAudioPlaybackStarted");
+  ok(isAudioStarted(browser), "audio has started playing.");
+  ok(tab.playingAudio, "tab is playing audio.");
+
+  info("- pause when lossing audio focus -");
+  Services.obs.notifyObservers(browser, "audioFocusChanged", "lostAudioFocus");
+
+  info("- check whether audio stops playing -");
+  yield promiseTabEvent(browser, "DOMAudioPlaybackStopped");
+  ok(!isAudioStarted(browser), "audio has stopped playing.");
+  ok(!tab.playingAudio, "tab isn't playing audio.");
+
+  info("- remove tab -");
+  gBrowserApp.closeTab(tab);
+});
+
+  </script>
+</head>
+<body>
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..7f1833508ab3fd9e6d4f0241f37cc2ee5a1679e5
GIT binary patch
literal 14290
zc%1Edd0dl6*XV;qWfK7<EP@4u2qd5hOC_jPWC<al1QL?42xNh<C`*V%iz}dnMIa=S
zAhxnZNkV{xpaiuQWDh8TM3z#Ox2QqT78P50ZEvuBzwiEYfA^2?{(0y3%*-=0XXZR}
z&Y82!b2up}0$2z9lOA5P30kY4ZMywIXS)vLcvd1Ud#z*L3CQ0NH-G-S=IR_?n+g2y
zGj+7}yN642M!tXjSBm}O??u+8<7pYm?uWCI?9yn7xX-<I!FKT7F!*lxZVx->!|BIp
z8CjHU$}zgj+LAi!|DI~<cL=9lS_d$~Y_)f?ia?R{*cgn<dRx+w=pb9tgioM7*(Bmp
zuvO!!!>(*RYQPkOcL}hKKt}|#F&yn1X=?=1K0*znT+iMbQW&i7lol$cgF;?%NgP<9
zEoqd9swJsmS~v_Q_<FV_g6c%xiU{m|nfUE)1iQ}9`Ck9e7>!#2Gn#q#v30~Nw|vVG
z+I_u*>?N7%CsazWC8-3~*E<*CeEv~6cI`WX1G+k&zu5o)x{nwIgr8j-IIsaY3v3O@
z+}fXM6IAIqyzVo|0bq^#&SlS>D|ZA}k_j9Nfy2+`-0T$WS{1v$5-%;tjq?jdy1)Pc
zSUPRbG4`XCAWdAJH&~MLP5ksWoeMC}eniw0XCFZ+Oz@+2^9_FSK4g;%CFeGp`wjVQ
zbjU%uY;d}b^Xs!Qv6lD&dLzJYq?sMfTKoLNI^?wt$=CF>&p++T=Q_NXYY*^0Pj<Q6
zpB6c2pMToPb$Z`i)4#;m|EJA!UU$u9T^()R|7n5$87?iNZ|eRxTv|leH2*L5_5aiV
zPyd&sJx+QV+T3blu!&#_JkSUBXwN+W{FrGotQGhEOsF9MK!=r{0MH5z8UG*S(**#d
zJ=&`J$ObJ!eZXG_urvx72RQ3np=bFzo|~TL0%KoPg8>r&IPw$#OyV!_AIt#-Ys#fg
zJC`|!<jm*(x;J!7z&F?UMGpSsZ~aaAn%p`))w{jp`GS+zXx%vhW1i=nd3*pkyKbXa
zoo(}U0&R5mBs&2nW<dOrt-;HCA8rX4kHlgNUgTbLX4cUJf>+&d_a5G=orqQC*fXys
z@%T*w@hiDWz(bEvE#%{z*lNCZ$u-C!I67i$==d5W#Y*7H<kNV91-Zn?f6Oy90vj8}
zS;H-mi?x6zYgnRkVhJ0x%dG)Hh5?m!H8zE?;ilk~TnrudK7fsVoI{!7=xEVNpu-)T
ze$3Ho*Ys#D56*o795`!AuMDS{dh;@#>1g*50GO=T>e%|KM3hyZW4@W+gX;Vvem5Jc
zw6?}P-wfs2c>ahV@;VuVK{Ztw__;V0969LRSY@JB^1fO=`+nxLKJbeUV3Ke5Kx-6&
z^{q$>pBHxdC9vmLvoWYX%j{7f7bjAQ&x@;7N^OxHhSFTs_U&w>k7}DN%;#pX%jc#R
zFG35l4mfLIqqPxfTc@tZJKlU$&IS&+0%z}MLWkGL8XCcJ41KF5dMH-q7(D*D2b+8C
zMNyqIlP~zpjd0IU?7V!foh%Tb7y+L_V$Ih&w2Uwu-?~Q0#|6+DEoB9Z-5sG3URwH}
zd-a(g8L=ulcFG(8u4yGEGQMX)Z)X7M+-I4AYh~Nd1cS0~sPaR)6qI8O?6sQp3uCPX
z1XrEY8n<;olU6M4F({YDDvU#;c0<~?NJa2k6ZQYN@YA~v99T5ic(zt=8!l*%i?t#2
zvs7xo#`1y*$pn+))@NYfXji2q7I)(GU>mMisfi+3vckv3MW*G|LDFb&uuGq1>Lyq1
zPEy_ac%%%s{3>EGtZ({)i9iM-i8`O#<Y%}wTBeS8X?Z#dQ}a?s_iePNmucU!NB8O3
ze>)<3PRt%XsAr$H<fUbuv7ff@;lUTy^j{C^J<A@^ve*#VotpuiMYU!YvKyq`V^2Sm
zsZ$5YhoRPl<dJrAzV>6+1V_*Q^WGY&;C<D94`5KiO*x}$5_M#4)0LQ^>NRxN*WjEQ
zC)Nf`{63Qe)%5v0@;?)20;6a9p8*$biP-qh00tA{R$$`i-1rY7EeHP@K+&2SxBh3K
zO1W14huAKS98AbR1C^+?^1rxHMNDyF{}-HWV(p;(gDAyCRz-Aa`VXT2UXSLc9Z3CW
z+5|C0+r+s3y&eF3|HYb;+2QK;Ak0I&cU{m{IQoEA#_RyQA9YPE?VK$O><(D!{B6&5
z&Ta<IHUN5;E>TCH0`><J&RxI%Ovgd1X-y9NmyZ08)9UzlbIoa8^E~Eu$bWV^{mqHI
zr1Z4(EJjB5@ywK5Mkb9$qaIIBNlwj7P0PtVempZXqwvvlqeIErg?FcZ6g3DOM$opG
z=+iF4w2eygA+WjMG|Y5IcVVvz+Kh;@f;6`#-%sp#J*A$ezTwPbK+UaL3BAj6t&_j;
z&u;wY?1|E$x`0z#n}yp~<lL3>zs`VDgE#xmFftzX^p?QjQR1tHLTH-_wZESJMAjBy
z`^vUZ?Qs6O>{5J7@KcZXC-0VR@&V-x*wv*KmT%%#>k%9p#+%4=cx_l8-EXiw6j3n>
zhBFYM-O2^|5R=%Mm6_?I+AaoLm5yny{=4gro4fU=$6RiDjfNOnX`--ml|Q(5qNJ^T
z_mwDdOpf^F^0KQ{Hno?j3zQMxZ(GYJ*Gs)AYbl4Yb?B0Rz9smanVo5CK72HvUJ_`k
zcfZWQStx?*Lx^^}nnz=n=9aI<wk6IdzF5E!u0oP5B)gw8%YkP1Pc0p>tHj|B%26Nu
zDP!aMFV5{f_wLW(g2n`j)FHNgXnD@BHc=O-thO4?-dDtJ+xytUtWz-ftoEP-x73O?
z+F(`$BuLY_DivA2(%U=F7K-J!+j$~&3T|Z2-aS8oHUX0@`<e5-1qJTAO0gCoTx@dx
zy``CZOZJWL|L9tM^>jv~u*q&RlwtbtJ{JA0dqLgZ28M=-nVGp1FquJVcLA8(`F+k*
z&WCtWs5&YsU@|4&T%tp{3T7c2wiKns<cQ!`#<d|xLiMb=ecbs-B(aNOQ{5O1WvK+$
z9wjv>_WknB$7KDLuO!jq6V`<Eoi$~$!RgJM>7<#jBs<4xw??5=Ia*6BQJSqM(2CA)
z^LK`L3_&H<2|FQx?v|~gXKxQJcP}diupmSsnMQ@;d77bu;Qo#;>95G9*SJTo2i@Y2
zTvHMncx@H1X>vh%+7hY(F7dveb?<QQ{!*XT{MR!~A1%po?S?|5=L}12uu+FSU!Tl=
znk*tLyg!-WEEqJfH&+h&dz;#l;I*6Viy^pJ%Di$}IiwPuc8Lw{tt3ANb2Z+Yv{=~)
zksT^k|CzVi@=W=Ru>XW=wBKp;E8o;J4{6}yyXt$?)`#>u`GhGXD$Fos)5+aHS+$k0
z^YtZe8}Ah&>X4VHwY{}{!5~D#3=q0Q-3!RMUc%Ugm};wH!&*a9=DbaaP=s%5FaP<-
z$L75|pRE3I)P?z0ey<5CRZp@+(;^C$)GRyG`Q4xQLjp(QFik%&x*)A`<L8ff=hCzx
zOO4gb;4F_X_DwzD^%f&50t%fW0%4u}CMK5aaMnj)hDLDF6px~pk;I~gOe5wPtj}_5
z(VoCw=Y#Dpm<1)+F?-L=?MYpCzYJAZghqtY)aud7L(-PE7OpHU!9Kgq3c?A*O(Z-%
zH>mIqwXoR)lx>?qo&0GMrwq-1w%(*ddn9slMtCYp02W8Z=2VpLE)3g<x3}F=g!KN`
z_nt-mddLCevip{#XoK#9>k6toG*C%pAWs-ep=FLKZAv=`zo#BMwzBj4cD>cVT4XD-
z6J9g-)t@q;rS6v%c#2rg>jg=^0Lr*lLfTTbmZ<8J9tg<ILR%CPyrU`2p=OZphX8v(
zEI2P*hatE!ADouRinc^HRyF4tsOZcZv?&gc?D}L{*OqwH2mTh}FJDP^l2N`Ij^q$#
z_LF-rzhS>9^M|K)$mCx^WU|%$G9_>VDC01G-cOv)U3EOt@|YCy@bqND4b!tm=dT9i
znS+)XZkl(EXE1~nwviH{5sm9NoDFS{pShKXv_IKCb3CjmjX?H_<+2ysI#MJ_l!rW2
z(Zthh`^*u=R<lX89ZOpMHuRFo5Air8Sx~lm+8YvJhJZLS{x;^^vvXU`11OZp8}N0U
z6k*_DuFj7p(DmoP2rr`rBzBK6z<?du5dihIwpP;v0;BNwsV=3aVG2<R>wu2bk?8{I
zg828Uo;s5<@QZ8PHdahMKs?%m?y6>FicZrrb9R8DCM<=qIGW)#%Zt*Teb3T<;jtrH
zwwdx<{lFC{7gxut!F|=EeJ`tH@ts3Mjq{CcA?&+TU(Q~L_0@2!Pt`-BMJmUQnALmo
za%7<T7&XmWhU;qSSTG{}Ilq%MFrc(F#X-q#tn_VE_{;!fcdGAVoRTs3{g%%x4F1f*
z3B)a|7a@V{B_uSf8fc*B)k1J~F1y#gS)Q0X5QE=NqTmLHuvcnKkA`}{o2G(Ts-pQ9
zYTR)9zq|h&{rHn~DfCr|gP*~h*L6XOP;jkE-2vi=@GwV`k!Qo;hBXoGp1<AZ7V~jE
z91anrPKd%&OC_QTRXCPRp>Xw0CB*xEI`;i0jR!9ImmINk9Tnj_vsBnCH&jq4T^!TN
zY%DO3eTm-o4EwRxdn9g`!}(F(U(+kiv%IB8qGKLi?T;uBP~V)SprFlG;Vn%XZ>(fD
zPpt<kx6LG`W|5-P_L=D&YCS(fjfjnH7B~<45vnSx=O1Yt@%CIJa<EgxAicY3YZw^=
zGb|$n{CLpOcHQ7pOOo{#TbuJ2j_8FIR1Z=yrn=7OofM2Nir-z9$L8VbnKY?-h~w3-
zOtk0|7={dI_YM`%*fI;ClF%IYos*P!cmJytJzqG!v%K?TBeowu1&dO+JGM)BDrj}K
zcL=0!hz;T3eyCy#2}`aQS!|>*edckx=<Bv9R#;GF@X0@ae*U8J8|JCwrBq6(2>SEG
zN!j705HnVXh)}DT{t!ACb2Wqu0Rcc6uKrI1vGn6B$D5By6guu}f!kuM-zAt;K_%^m
zWQvTwk*Ei^uR-?uBf1hZ+rKY0IK)WpyC)~4N^3^jSDfDr8SX@p2HQlA{1K5O>D5#}
zVYXfhKPB-R#2G($_B?*(1JbS1Cm9e#{3=4Mf4IH8(K^6NMd0e|-Iz;X|KkR<5e!w1
zZq#ci-GaK<sJy`LkrnSdmE*a(NT?w{JXSg?dDp_q%TJIU`f@)T^WyI8#lb=xHN~D_
zr7WvpFG<|R5}y0z0;n5wtLsIQaTLH&#~b5`7kuK;Nh9#Z-F534SPh4ez17HzXm6JF
z;9U1IrYlipUR>`p3C51aBmY7e4^B+hXi$kqCFymk&3SL4qP9zFOVQD%Ah?j3!L;Bg
zb>*Zan34*GGN6xCV)+pk*KJm-$EvY;n2ZOa1=e{v9*%=YssKAayr~=muh>*Xr_1kT
zCD0NwGZVZZ&s*DHWQ2Sm?A!Ab>O)UYZ9|hXqSq@SX)&~rLBqc3Y`L-}=eOGC7vFNj
z{nKQpOmg>ze`=W_<4#rU0g`PIVQaag>0(ohh24cZi9jBhb6s9{(;cFXE`iBp0K(3P
z4q8@@_T?vf=3AzToP#dDOxBb9mSWLAu$<LgF0tzxW4t|KHuTPynzq_;IWJ{Jk<#UP
zMd}&lE{5Y`KP@Yi)v-DA%P}@O@@@4yXG_Oe0mm#m{g~L;T5JFX7YPbJ+d2loYSH8f
z4I1fMb4fP?se2=WaFGlq8Z(V6EPt`=@vfaZ!K&M){_yF;_zeoAwD<{s6+SMbs;^xT
z#|#LxZs0CL9ECy_$y@KV?!_@OGXXi?<FA}H>Yve^kMNLV`L=*_MH|}y#&t4<Urddk
zp+EVsc8Gl-oUTNfP!vPS9sCo&l=SfJnLAN6GMZY{#w~e$ibeAsXSdf6@))mhQ#Yj4
z5d*|C2l^C=B&RBm4GOs23+a~u33l;znz`@mmtVlM-e0hFSa&uJ*;o?lz#b%<k+=9e
zVVeBW6e;(eh%bN1oBa4&Yf{kWovE)9<kV8~VQB}yrXlX`=t|yiFrR&)Ia2|YSBbWC
zV-`v|vS^&*>$y6)S$&NVJtG5kL`>gVs`5Tg$^lEK1!F+RVmZpm%)VxzaB;yRs_RZN
zR59iLa6QD+N)y8kkQTF`kQMe~ts70Sf_8Xk5c=WRJ6%(5N&q#e>DPO5KFkN**FI8i
zF1eGXC@3(`cCU$*wBL~zv?bDX%N{?q$-J}S#U)b&#Cw4;8MuWkMXgsBk-_W?*P2*j
zPC`N;A+Bwq8=sqz(V9M1>22v%`xpkE<?|t|S@efqZ<(4XobC|H8KjS4kBOtFj)V&@
z;OW!|Zm(5rT&E;f`;6H`EGo(DF|p0M&$>yxrki4fHRe_|rGjbwAMZDq(biX>2u&4@
z(1+Nl*v{Ln<s#~Kk&I$CIK4uiUtS(!4~N#7@}^s2q9=N<Y<%)Tap<Svrq_ojBwJFa
zIwlqb{{jWB_@avW(H~|P6H^mPpgf3SrOk{V@0UrCGEV1{V1WsDb*;ZCau2XG-I!`*
zf9`-^x1){6V1v?FVhhVZVC3MuZNMZLtkkc_>YMVP);HaqEoMM(*%EBDn)N*dSJQUq
z_MRKPY+KW(zwc>h<ZX2hNN&nYX+)&b)!ysNcveH~dTL=7WxP#!@|?4)tE)gDaE-))
z%-Mu+0-5ZnS0gZ&pq$BUqb4KT@VaPSJB0J3vwxk#9^DqIs<U+u$l4ElBKyPJ3oV6@
z%SwW(#qT|9D!loPy<+@pyp2}s-J!d3Xq~CXNtcgJIZK;208D)I{)x{80~H@+nZCY&
zjG$03I>r8p_vi4|!@{GV7NWYQu~F{4=2IO#Z5`&RX21Q~9I9sCtshV0?^rtK<e`QW
zW~n(6_^>B#WjCLcfw5@{RW4R4ctUh>2uEZ|QaKKX$Xe~(y2Job2fCO!F}Hhr4I%H+
z5!I+2E7(zLb;A!yMI(24SWpt!DiKv{!)IN+gF`K5`GjD3F;6u?r8L@(H;pRk(IaN{
zKOc6Y9>Fym-@WGAczh;<gs6dC;apL;6Fbj0HWpy}nVqe7B1QNmfp8rlh#ZV$yK+yS
z6$zja14@p>Y+4<d1nlt-*t&GH+|k*Y6~?SaJHs{#YeVb|LoQH9*~4Brqr5bl6U!jy
zOn4pcqh1o2#rSgXVf|WvW?E`vphX{sSSu7X(lRrl^aLohSu)^v1t(^7+Ufv>kDvab
z`qg^x57wVf9}QIRxK-MaPv7m#a$t}02dMq{xY6zyyds9!-nwufNiB59$5)EIrUb*F
z)JC;)Q}K)2sv2&3wJLrt>BCf>qUo<QkEg`}Q4!;>mxjEfZ)xHPkgnFf2#bC^5sB!J
zJEH?IiOr`Yqb5?`*UJmaJJsH{pTg22ReF%}P$x+kaZ2l{XSsrvQ)q8)2{*Dtle~0|
zJTk;{5>kz_bLZ<EAMAn;r?Ik2_dPit;q^Ar@zsZ)e*f-ppR{rcJcx!{4Wf2K<R>JP
z(>(~JbVb$7)gHcB1?pd93#sz<PII=o3ac_VCxll}{QR!jUD@=0eN}uuC8#76>3hiW
zkn0xGfJvP)oZB9upYWp}?gx1%j}5_f2*JgHT@pyMd}#S)qD4RncMKm#Ns#HJ{Jww1
zN1+VQtuYr(B{img96SX8|J*U4{`-#M3vDe4{&FXnmYa6`SW;?Qa>}vfl=QUB<cy4C
zX~}76G)8V_=CO>-W7&+H?2O8q`CHjp(tk64cFFqH!H>h%Cc=cXD?}KnwI^0q`czei
zUtwB?!vi$J7Q+>Vwzf4oy993Z(X*-BnxPF;ssjSPKp(qt*V`*wHjCCh+IQm4$GFHO
zE^)lW(Fgy-@^)4)FR#}XC{we3y#6$KpT3O&;&Qpm1=TJwHK7+zG^J=`u|#uW?5C8j
zb{-aokIQK+fF>o(D{V$>JgQ`x%Y8qK_AQC<E3cQxN^eqgu<ul;@5o|=T0Jq%Gf(_0
z(vj|XO082X9(_smlDCs-us{oKL{iqvhVXk~V1N^WIYSH!{-m)(=TVN{7`Km9-5l3B
zU<7WExR@##4y@qDEu+p>%BD1s1E}M?&e<`mk)H=r5`%pSZGG<iJCeOa%bO}WQSH8a
ze=Q=m_aYgE6KTb?$2@E{x|pPZfk087^|R6rHq6IJfVoP?x`fF$eIjtvY-@EU8<d*z
zP|6`!7QRi6#fhkFERHZVSJ(J50ZiJ~kv^?vR!sH!yqFN*6;h{Zs_*{%w{O^my`|E6
z&#Uc~p7dTdOwA*-DXs-nHu?-;Qs!eU*$sra&KQ!I5w1N?lTQ_?mo|;wDGb}WdZ~4X
z%fX_m`4WjeYpBmZktRYuS7iA<Uv?c<-+Qxq4`tU-7o19Zvp^j#Nk>evRBq`?8Mtj4
zEDbeF`pMPFFun4Dgm~)y%WnHAW8=-z8I1Hxv5)Tyre_<^|F1P4l-T^|sI83nsdmOp
zbU`Ef2!8b*05O~o4w03PTOPcuikPx==q6M>2y@(GUwFX#kazleyjFHytkXRLGQYZ;
zm+aA5d!lm4_u6p@JeDsjRTpU9%$&@zP3dSzwV(YkNThF$TMuwznWa~>c?J#uCcoRW
z+W6lACJm94zymNVkFY@v+9+)2p#eS#`~ZC^_$)OcK_$j_wzp10q2%6_E_hEX9OO$q
zQPF_VEOu>43QJE5&536o3bNq4{hloBl{YL`g$6a&Tg47Afl8j{Q|$!$&6`>!UJ8GI
z@=V08VZl)OK|me2exSNR0%nypcuc)aZd8t8yji8A)s}UXy82d}w7i#F=2+nT=6KSZ
zhlAIUZc>jAo&8t(eqL0|WTYoJn`0MA?h0~(7$0}jCs8(Qwvn)&srpr?8NcbvM};Sk
z^c(lw1Yqov!AQ?0OApxa!i%h^8M?hxIxL)(8ynx^OZf+S>8LQ90cs7}t&1wK64vCj
zcfLu@qI`)XdwCuhNAtv!xGpD;)x^H#_%doiZvojjY6Q_r0K{0)!dmWo`C%0)NRK>Q
zJ%Xq~6{fj9bG{htdnGD%X(1{$h8T()NlYK#UKWY4pel{&y>q|5O%j(xy3NQ^c%DV#
zbY$oc-tz9*Sl?nryIE}>-CaB|*KJcAA{<f9C9G&{=uWx<%(tqwjOg~ZJ{v%qcZKvT
z1thl0-M!gzSSUU%6N=>vbM21f`Ld7^u(70tQKNZAY~rR-!7A0Lu3Ojl`X15kf(7*3
z!4sHM$+_<uG?Rl0s@pDiH!7Rft7KxkZtsbLUUtdODaHVW>ZiXvd1QU{-`i?q_|i$!
z6u|KaxMJu*wQ(@qUdEd@55>7ZoFZr18-Jj7SjDDa@_+qpzzO4D9wmTA=8oVbE9l1!
z7Zn{g-dye`o<`ySF($z6MhEg)|NDp)$BkNfZ!ErVBU-PO^1ohXPJBFidDD)!kKTN!
zAAd8^bG9H{-;9ek8a!L?p?^bZqO}-^XE!YmA)3-$D-@1q+%)jOZV(R2CE&_`yiq<o
z>CFA%Oz7hY@g1ATV>Azq*rP_ntIc*lQC4A2z$0<dvj)Ex108YZucBL+@6%a5)V;-*
z93YawNdFvN$N35>=H<I{W>?W%Iye|(fO6b{i}bYbv$Po}H<S;6r7k<`6OlI(y;&oA
z0NI@a6Gr2&>MtF8b?+-_DZ}ZD)Dta?T2UDl^>s&jP7bfNW8X<vpkw-g%~}>Wg_9?c
zF7>G11nJ;M-r|SVAG`G+CIKY|_IoDR?HuPuIyfdGOe<m6Up6d84uSeEro*EnE=k|M
z8~OU=U%%a7l-@Y0+OhrAiq{|84^O9us%=JjY(7cIF!L%xIIiEfcXB--=KS_3YWSO%
z7kL5U$nyAf|K|z99Kq%T4`4jYO9lq^HK8|1=%n+KB2R9)ewY;i;PIWFV~R1qx{8xS
z;&uYbql+D!8q(D$!KEP7i#upyaU83A4tLu0z518Ugf>Be7z623%qf@WJZqOs%5CM?
za`rKE^8Cx5OB%Y_^^|(_W8f^SJS%XNf2c$wVojBgl0A%_&9>yzF#zyLKNS+4qdwTx
z?2j8l-uo`*gJk&~EVty!crC$Kda>fkpC-p8iQhHU(EFAbU&d6Z7F@G?1!p!(w{>6_
zoo8GbRc(^Emdy#mzWB4|NQxP(kM7MK1p7IX{T%vCjPPI*pOo915+#*Da7znBDz5AD
zp6X-K1LUKcgH*R!;WR%r&;RSo8y_fxke6~m^BMm>qm5^#dlpxZLCf0qq|Bc$80}14
zF#v!{j+Ic(X;<J?G7o*d+OpoV@?d=}hZ2+qI0r|4P4$Rt0~+ni(l3%pN@Z_OIqAIn
zrpAlZJSf`Ec#KYJ>mL%KeKpqx7f@hrz$^=Q`_vpda}1r6<8g@33;00TD7P{!CbZpY
zbr|z8Yt!PJdMXWqZ~JxyZ?xliL5ai};wp5-VmapKu0qJL1iGcV6c_+|OgUOXY$L3$
zVvR6*;6Oq+7_UPzJ=ddgP37tCyZ<WU)S2<XH|jmde}8As{S<e!Ekur16Q`)6yeFq_
zw=4>UtCgiw6;OZ^!|Ytk-WdF~fPesO02WKY1qC=E*-5O#BAdJeXKT?(TQud&qVgQD
z|Hb%?WIsJvg+8p%`69g}_!-%|&&b{(aUBRc0_#r%H9`X4w+`MPnEa3z0=!i943k3F
zeYhU$#CjJ02Bh6tMjMp7$zqs+N}JQaROc+g1aQoQH?|9`b*+-Na4$um`p`pV_S!JT
zq~81xQg;hn<7w~Glfp>~PM$H^VZR+#b2$AZq)9#f*DcL*lV<WJ<W@^^?ho$K)b}<s
zEk$9j!M<G^fHKw7mvqsFpc@IK#2rhmrC?+)+Mz~~@9$bM)BsO-ZwQg^0W~%?VsV3~
zGj1=9ZM9!o^1U*pSlCW-BYAOxzp8brEez%Ht;PvM<j11we;*m<6Ikr~iItrxAP&f)
zvX@MDr_NsuNucQRF}2FI?9IOi7P^*_3-k(0wiNjXYS3A0(zoC$JW6!e*o+JML{RtY
z9f7iq7ss1to~cVuh8?8O|B*FC`<nY?tT|rO@Zf7sqsyuYQJy)uWVg|Hym?hG7S0mK
zyq-&mf{Lj(w2~275`e_OK+n#quDoZPbfh>sJ*^IVpvyp-`hi*Kq;Zs^zMqDi*5Mf%
zRoN^duI`toNg1`xa&=tsgP^-{cFod1rnYK68C(5ukomGsb$3kGdSjJ-Ste3IOGg=*
zP;vsBGK_Tr%A%V0vu#_5d9K^E2F!kQbSMtFhhI6EcBzQ&S`&$HYi-R6fGP?a>T5wz
zcLub1?IM4aI3t@7u-F=&A}cxcs~YqzNgazKO<JTzyl^=jdnUFe5ax2y`x%=ta*rny
zZ5Ma`)Z0)TOXSrLL*TME9)LEu{C)l=_uSErlJ&a=oQq2&b#j47#2i9t1v7lBsonGh
z4%z4L%}4sMi^jMmtw;32agMqH=ln|UhlkU}_d<quNVz(-B;PUb?k2i@%c+`F#!Z-o
z8zDx)EP(FXwW~}dN$h1dmXs8jhk&5bP||1vOJ)d^#R|$VYjeL+2hz%A3YZQ8;c%S*
z4ly&~C%o6uK|RpMCT6e-iHYO7!l!g_fRT3}!{~u!MxYfO<#@rq>Gtgf10e~@gW<Y9
z-t@|We6YQ)^S%b@YcW$<U=9I2PwZ-q&&^Hnf<(EyDf0GjtRyzawK&O$cjIRAo#x^_
zQW3Rb>#$w|Gvz@|b(mvBkw3hN40psd<$Jj7{DKQL81zT5ii|nwBv~SzS}m0bqLb)h
ze+2G-qc^+yxcrSJq;c^0>-kZzY>{N`Fz$y#UFlEj|CwNHl%QtJhdTph6Hg-%m<tiK
ziC5mz23tnHxe^SPjD25@q`-w=7Ka#SYv~s=)tEPPD-UJrG-0!RI;3OTX{7-|m~{+6
zG>0vXdsdnMap{I=p`%hnv*xmoY`j>?u%3#lnd+L)z)=r>xFB0;GgfHTsu>{0EIz_A
zZBHJcg0K|<;h?EtlMbyP{?r-LXHs;oe2DCE-$8#*%3<l@$9Rpp?Re<;qNJaUn6*Zv
z@=!#wuIU(m>iz&)UEW-0U1UXQ1E;3f_9TqbpirWXVRmkgVX??Eke<t6Tql)@W6(Nv
zk>|s!P`j<A)buQ=ezaH0hEb{q_h!7=AR5dnQWVT%aj|W;TW=@IgoL=^WC?xTd)%#5
zHNxY{Y9>z%dRM$S`q!<MXrbKy`m^)brFwPm-@BKv>2Dr%E4nSS-SI<se1%N)+HPZ@
ziKLZBSqBtGHJ^6f7AO1Il(ua{XM)h>3rRj1alN|CkM?}1k(Q8K!WN1c?D_TC*NaJm
z%YxzJ^cN!geX=t_>7u3<Zb#}HNzxTBG9^WBBg!e-H1+;chM@97*W<l+gZhqn5$X&L
zhrOc8O4w{&T$8T$T>Rnp?k0DazeU5PKMfrM2;s5VST+SqAcS8RL5CH(rh(D+h<Q4K
z9qJkiXL~cVj3gyCq!Q;1K?G<26FrR9QpviVwf&oYHM7Ku3XPQ*<<Zr=>OFg_QG0k3
z?29>3qmtHgC?58ru5m$k<LnloGFnmX+}T@F)R9kFA9A<uNURht7#b1`0ZrNyac~2S
z8{BXqxFNu3YAX!v_M;{eVQQXe6wcJSc+)b&)pUzJL??dOLx|sQ1+lO$CLBQCXqx|%
z7rDNl^uDIn($Fw8Y7~ZpirXi83BoUh(My$e*<ypge|9}v`%?m104D|9sAnQ^TF<#5
zLQ_3dXtG6v?N8z0+zB~e#Uv<WJ|(qT=0R9}Ccs7!I(OH2V>X%oHZ|w_m^}LC4^NiM
zIPA~Pk;+=W6x_70w%|JY)}XZ)E(K%(BCDY8Zf<xEmg#mebtAx3KmGU3<V~ZO!_Ko)
zcfjc>lYu-qu=N+`0{`y8G%#y~ul13tkq^kzT(tN4Ay+Hqy0h$%;tS42-ULZQ_$l>-
zd-GT@<smp7o<2^2jC0wquRngn?b%2AK|iNQyv2%iT+Pw1Y3q<V6j~b=Yw9|!1(72e
zSON8RDf`o-jszpTA>`~<KRV)Hkw_;G2X+QL7@1$bwP?O^zKq>7<R0cB6ekvRRuXyo
z<V<n<BrIp}rJAN}%b5UGKl+U`u*H<9iVYOZN1h=Hr--vPdA#bu?m4`5u{0z^xDW5B
z{QG`F7a;%l{e-TzzHyJ;Z+$H1_;E%y?bz9z6K7Mil1^lwI+1nk`032-G)}~!7PlLP
zaob?2X_&~K-7fYuVSQwW2a#~r09mSmD%$Q$bSA2>II(0Q1=n>;zo`M9Uc+@8^U~@(
zwqW38yWET0eu#Vg@b+!wVsfS9@?;fg42z3yTS`5C&F_yhds)`m;+Rj%6P9V~fx;_R
z7tSp7442&h`n~viy^fMy4C!MQV7prDq!3(facnXO7Yp+R1$DLV1>r^;%?UA`St|DM
zYUP23aT;AH@|yA;&ztIS6SyUPABwtac_>I(G034p)Q+iCwPZKQ;)OPp0pZ-;y-C`r
z#<HzE!F)?0&uXaRI<+v1e2zj$s2mdT1VbqE0Nw8k)6Q3WH~I6(<$ekzvoe!cUKCzr
zvJMw%#9X(seLAXA_GZcTDL0qxrN*;lzLu$8GmO?yv(X3r8vh!)<ng^byA@?*af}U#
zHg>hRIJQHXIPJap8qsKAETUM)XPR^JZro)H2M3S(n>Pgl!6lJ<ab092NdG~3p}8Gj
zZx59fKEyx2h%_=DD>LykGO06PG?BGC7?Kjo^*KMB**EgnH!Vq$KAx=EGxT`st?-!?
zFyq}x%(0(YnhyTBo6ltB6aaKo|F(B3vG46KrUfDc`*6Jj+ea-y>zwq0{C{K|V3vhD
zX27i~kOd}n@Zg;+dvFBTT^z&3N~YTqWf0Jg4_zm9U!-}&uJY1t5%^W=cVYrzhDS;F
z-BHl+8ox_*=bg{DgMnn@Ew;tYV<O2O(3KYgLrVY<VE18Vf*T(V0288C5k~^?UPdvK
zh_51h)hVgdE%K~P5gwl-rZU)UA++UXhdf0|k%LnuUy9g0=}(NuKj@vgoa+1Pue>Ff
zyzd8EPugyowtXFiMM{%sHCALOIWs`|GDC8F_EygA<E!36Lb@Svk9}no7k5n9LO^4&
zJsS^gV2+x>X%H)0t^RlX7um?3Pr}(#RU$<SPK=L<i@91nE{MJARW}wqo=}6Iy76d0
zuJ{WQ_%0xwSKc(aoSdFxaKouVk6=~$Dc(3=aZgQN=`{xh>H^m|-m1ePH4FRBw#q8t
zb`=t~5d(Qc$z5b(qqxIDjQ~d&>&0HV^7tN|Cqjp=jH>hAq|JEOSEkqaLtI89FFULo
zJQ<VJ^(4HE@huATGET!&X@-c4l;`mYVmOE6fpcewZAiKEb1_Y-b!8l@fgkEA8<vls
zIA37hF^1DCYDXe!!k+oNa)-JTab?;#n+D^x-Qlru(%aK*yhfPCl@CruuAx%?*i`?$
z`P~~|J?bD_tX+B<Khu$3V>Qj>@8rka-S^YaZ;mox!J-aXsb^23SuxZf=dMseb9ym-
zF)?kof13Z45nXr}U|P>4#!gIvUftdBivb-$!R8c5oZ$hc=MJ@-S8C|q+bYjhmpD`$
zxrEx_Yg!wudx=AF7_&zds-OyYad}yzI_-e!beiYt#G>(HQBHMwed<x+Hy=IWG4rXQ
zO}3n__QcULdhYykibYL{y<vUmExIiLFr(0a2*2sA`hgMVZu=YHz^q&Q$B-EDMs)hX
zrX8a4Ax8<58>!a>4GY?_#g-Cuy&OXf1GDsfDnd83oKB#b4;vB&zSlSW^%F&c>ys>X
zutn)3Jhqw6bp3%;s;Z%OaaK+}j-yA9U#QG!<trAvdk_jE;5^5QdHUe6QNRyf2nxmQ
zpqW<*G6D>2J%6aRt*(g7YI07}-vAi)oS$byx?0;4Y4{i)3vDD2rErJtf*Ly{&kUuJ
zmTEOaYFSQt7hz-SiVZcYaJpBB4ox2sX?Q*9>8Xs==$Y-o1W5WV+M?9YTeE>hrj+&u
zWV?$7%nHq;5mvY~O~WDc!p$YLFM(<#ctf<V8El(h`9a{v<?n;n?u-ONG2^}P=yoB2
zcunXLb?Y|j3Uy_O{supl!ccceZb<vz5qmy#q;jJKsfa)RKwo7})J7G5oS;Z6D<@Gi
zd3%!g4A}E9=Wn_=UGj#*>!(xb8vs;;vMCK*8yv7pM1?{by~??mt4nxFz!izSv4AnZ
z-0lfdbc}G?_~~6E-h&P1p;*QBc^6}Hr1*B0gywIxkby@_+d8H$!bs`?ybW&92Euuf
zHUlLf`?>`}?x3EQxVdGzZf|X@aA(X4o2@I(T}Amg^Oa5L0q}Tnu~3|15AuD^#9jgY
z^~8fc^Ie-ZPK^$1W*k3}64`h1O}OQ~*(wiWz3)Ry)%sm2N_R6gr#)RZ{&Lzo8oDY`
zs=RH7wT2D^oG1LX3ax(s!$XdfC?;hxWyr4CWE^Z2k%g>ICq_sW&**N@T_a-r622{K
zzS}kA`Ms7vXJr$gFtdLNI<uf4PD#`$&*Q<YLGVarm^yoJ|B3c}e(7bMwm!6RaO~i8
zrgKQhQX&Y%;dM@`#8-=#=H@JQfI{N(?`xS6cwl?H){jq`ax|X8Aq&ztj)UzIlq*Xw
z{2=TAd`~TB%b>plzt*XOX<sDg^)_l9ZhVX-O*bQ3NoW$$Zpjjli}JjJ?e02+lH^{R
ziEbNi-?D&!kIS<V$rl71IHry!x7{w%0nShUwts!wzw)MC)M=tGYDTCItB9iZ(gC}r
z1~{{+3Zw5-X%%F%P7{d)v+OJMrpT`4$cC#!jt65Y?B3pzUUGgrxEUvN@*tc_k|#|!
z*P7=(^w*s;^6p9Ddo9rqKR(#Cv~7@Dl79HZ_OOohrHiqY!6El%Ns1wG`tkcG7oOyN
z`VgO_+uGxe*?@AoaLAzn-h|#jgC@H*ONN+O9D&m7_aV=CWxLI>(4<C#y=U%Hmb@V|
z@Z!9cNLEEhExv>*41Rl$!o1^lnDIx|<Uk6?jgW`}FYzxz`?}{WA^k7AyN9%HyT$XX
zkSMF{t-$HXe<ggn+1G<$wPtbh$uos67a|pV*f=<tgr>r$A~EM8kxxs^n3MeCp1kRF
z3!!DPq0FY)Cx~Bf10AlsF$Z^>B@!HPzbg9CAsysDgAl1Rw-}X`*k`o3Zxa?0aa}|B
zYTOUjA+f~m!r0D7`amHja{LH01+g39_oZ$6%?kJ>rb{K4>>XHT{J_#?i@hnbGV90g
zm?`#{D(U_@5u(33J?KH=QdVzKm_u;L3CZfx^Gzy~UG34``LY(Q<H5)7GxtH2(;eiR
zv{35vm}Jg$FE*+xE7K=-)O9_OHSwE)&%!qacdhIqRlmZ@526#c4)mo}k?bCj;pYYm
zEf2X$v%G}jV={<j!8Kb|fPFqUx8F8p<eoP4hIc7O&xsJ(io99S<vS<QkY4*g4PA1p
z#`e`cedJhi!An?MG}WB3JFWJ$>=Cw^>H?gc7@2C?o|DZ;Nir5$7oxSMrMyT(cIBJH
zxo9daw-*C~%O~l(IToBKQOv>|v-4_W!Rys~>XhIbC+c^y_wnELG|#T2ty)7^Vrtmw
zhs<WlWpb&PAn&K$XC$V44f+;&W>a%MK~o(YLxDm^d&QPQs95~P#Tgp_V2tmN=%Rhs
zRJ$YasM6l|f-bi@2hmq;Zq>+fgBnxB1B;l=#@TybJ<UL9EpTS?6<q9Cr`%jx{Heh8
zUy9RL)n2J92_xG}kI1O0)0SV2^7TfhS3EcM*bv(9+(y+vMKRQ-Vv^6GR)p|)#T)<}
z3$K3SX7F~K{>|&w`%U=vKsinitgr9%3*Di@xX9m?eM#f*SX&{1)dE%k?&~PcP{RCz
z1*dNzs(lznweam%cB5kPu^u6+4yyT5@|M}ULpuqj4P6ff*eA>4mrix}th-%orIL;?
zmLmvdyxGPEpg!DweM%a1^X(DUHSwPY00u>YYua}MvG}-ZLr4@_>xpuA3~Pf#FQGfl
zgiIvnumfzpsFP0!Sx+?6YR~FC6n(nQcx6CvXP^0A57qGYhj)Z8ki(502dQw2=lHlb
POZKSwFdSr|SpUBOl(=Qu
--- a/mobile/android/tests/browser/robocop/robocop.ini
+++ b/mobile/android/tests/browser/robocop/robocop.ini
@@ -9,16 +9,19 @@ subsuite = robocop
 skip-if = android_version == "18"
 [src/org/mozilla/gecko/tests/testAddSearchEngine.java]
 # disabled on 4.3, bug 1120759
 skip-if = android_version == "18"
 [src/org/mozilla/gecko/tests/testAdobeFlash.java]
 # disabled on 4.3, bug 1146420
 skip-if = android_version == "18"
 [src/org/mozilla/gecko/tests/testANRReporter.java]
+[src/org/mozilla/gecko/tests/testAudioFocus.java]
+[src/org/mozilla/gecko/tests/testMediaControl.java]
+skip-if = android_version < "23"
 [src/org/mozilla/gecko/tests/testAxisLocking.java]
 # [src/org/mozilla/gecko/tests/testBookmark.java] # see bug 915350
 [src/org/mozilla/gecko/tests/testBookmarksPanel.java]
 # disabled on 4.3, bug 987930
 skip-if = android_version == "18"
 [src/org/mozilla/gecko/tests/testBookmarkFolders.java]
 # disabled on 4.3, bug 1144921
 skip-if = android_version == "18"
--- a/mobile/android/tests/browser/robocop/robocop_autophone.ini
+++ b/mobile/android/tests/browser/robocop/robocop_autophone.ini
@@ -1,1 +1,4 @@
 [testAdobeFlash]
+[testAudioFocus]
+[testMediaControl]
+skip-if = android_version < "23"
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/browser/robocop/robocop_media_playback_loop.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<audio id="testAudio" src="audio.ogg" loop></audio>
+<script type="text/javascript">
+var audio = document.getElementById("testAudio");
+audio.oncanplay = function() {
+  audio.oncanplay = null;
+  audio.play();
+};
+</script>
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/BaseTest.java
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/BaseTest.java
@@ -606,16 +606,20 @@ abstract class BaseTest extends BaseRobo
     }
 
     public void closeAddedTabs() {
         for(int tabID : mKnownTabIDs) {
             closeTab(tabID);
         }
     }
 
+    public void closeAllTabs() {
+        Tabs.getInstance().closeAll();
+    }
+
     public final void runOnUiThreadSync(Runnable runnable) {
         RobocopUtils.runOnUiThreadSync(getActivity(), runnable);
     }
 
     /* Tap the "star" (bookmark) button to bookmark or un-bookmark the current page */
     public void toggleBookmark() {
         mActions.sendSpecialKey(Actions.SpecialKey.MENU);
         waitForText("Settings");
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/StringHelper.java
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/StringHelper.java
@@ -80,16 +80,17 @@ public class StringHelper {
     public final String ROBOCOP_OFFLINE_STORAGE_URL = "/robocop/robocop_offline_storage.html";
     public final String ROBOCOP_PICTURE_LINK_URL = "/robocop/robocop_picture_link.html";
     public final String ROBOCOP_SEARCH_URL = "/robocop/robocop_search.html";
     public final String ROBOCOP_TEXT_PAGE_URL = "/robocop/robocop_text_page.html";
     public final String ROBOCOP_ADOBE_FLASH_URL = "/robocop/robocop_adobe_flash.html";
     public final String ROBOCOP_INPUT_URL = "/robocop/robocop_input.html";
     public final String ROBOCOP_READER_MODE_BASIC_ARTICLE = "/robocop/reader_mode_pages/basic_article.html";
     public final String ROBOCOP_LINK_TO_SLOW_LOADING = "/robocop/robocop_link_to_slow_loading.html";
+    public final String ROBOCOP_MEDIA_PLAYBACK_LOOP_URL = "/robocop/robocop_media_playback_loop.html";
 
     private final String ROBOCOP_JS_HARNESS_URL = "/robocop/robocop_javascript.html";
 
     // Robocop page images
     public final String ROBOCOP_PICTURE_URL = "/robocop/Firefox.jpg";
 
     // Robocop page titles
     public final String ROBOCOP_BIG_LINK_TITLE = "Big Link";
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAudioFocus.java
@@ -0,0 +1,183 @@
+/* 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/. */
+
+package org.mozilla.gecko.tests;
+
+import org.mozilla.gecko.Actions;
+import org.mozilla.gecko.Tab;
+import org.mozilla.gecko.Tabs;
+import org.mozilla.gecko.media.AudioFocusAgent;
+import org.mozilla.gecko.media.AudioFocusAgent.State;
+
+import android.media.AudioManager;
+
+import com.robotium.solo.Condition;
+
+public class testAudioFocus extends BaseTest {
+    private boolean mPrevTabAudioPlaying = false;
+
+    public void testAudioFocus() {
+        info("- wait for gecko ready -");
+        blockForGeckoReady();
+
+        info("- check audio focus in the beginning -");
+        mAsserter.is(getAudioFocusAgent().getAudioFocusState(),
+                     State.LOST_FOCUS,
+                     "Should not own audio focus.");
+
+        info("- request audio focus -");
+        requestAudioFocus();
+        mAsserter.ok(true,
+                     "Check audio focus state",
+                     "Should own audio focus.");
+
+        info("- simulate losing audio focus transiently -");
+        getAudioFocusAgent().changeAudioFocus(AudioManager.AUDIOFOCUS_LOSS_TRANSIENT);
+        mAsserter.is(getAudioFocusAgent().getAudioFocusState(),
+                     State.LOST_FOCUS_TRANSIENT,
+                     "Should lose audio focus.");
+
+        info("- simulate gaining audio focus again -");
+        getAudioFocusAgent().changeAudioFocus(AudioManager.AUDIOFOCUS_GAIN);
+        mAsserter.is(getAudioFocusAgent().getAudioFocusState(),
+                     State.OWN_FOCUS,
+                     "Should own audio focus.");
+
+        info("- simulate losing audio focus and can duck -");
+        getAudioFocusAgent().changeAudioFocus(AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK);
+        mAsserter.is(getAudioFocusAgent().getAudioFocusState(),
+                     State.LOST_FOCUS_TRANSIENT_CAN_DUCK,
+                     "Should lose audio focus and can duck.");
+
+        info("- simulate gaining audio focus again -");
+        getAudioFocusAgent().changeAudioFocus(AudioManager.AUDIOFOCUS_GAIN);
+        mAsserter.is(getAudioFocusAgent().getAudioFocusState(),
+                     State.OWN_FOCUS,
+                     "Should own audio focus.");
+
+        info("- simulate losing audio focus -");
+        getAudioFocusAgent().changeAudioFocus(AudioManager.AUDIOFOCUS_LOSS);
+        mAsserter.is(getAudioFocusAgent().getAudioFocusState(),
+                     State.LOST_FOCUS,
+                     "Should lose audio focus.");
+
+        info("- request audio focus -");
+        requestAudioFocus();
+        mAsserter.is(getAudioFocusAgent().getAudioFocusState(),
+                     State.OWN_FOCUS,
+                     "Should own audio focus.");
+
+        info("- abandon audio focus -");
+        getAudioFocusAgent().notifyStoppedPlaying();
+        mAsserter.is(getAudioFocusAgent().getAudioFocusState(),
+                     State.LOST_FOCUS,
+                     "Should lose audio focus.");
+
+        info("- run next test : testAudioFocusChanged -");
+        testAudioFocusChanged();
+    }
+
+    private void testAudioFocusChanged() {
+        info("- check audio focus in the beginning -");
+        mAsserter.is(getAudioFocusAgent().getAudioFocusState(),
+                     State.LOST_FOCUS,
+                     "Should not request audio focus before media starts.");
+
+        info("- load URL with looping audio file -");
+        final String MEDIA_URL = getAbsoluteUrl(mStringHelper.ROBOCOP_MEDIA_PLAYBACK_LOOP_URL);
+        loadUrlAndWait(MEDIA_URL);
+
+        info("- wait audio starts playing -");
+        waitUntilTabAudioPlayingStateChanged();
+        mAsserter.is(getAudioFocusAgent().getAudioFocusState(),
+                     State.OWN_FOCUS,
+                     "Should request audio focus after media started playing.");
+
+        info("- simulate losing audio focus transiently -");
+        getAudioFocusAgent().changeAudioFocus(AudioManager.AUDIOFOCUS_LOSS_TRANSIENT);
+        waitUntilTabAudioPlayingStateChanged();
+        mAsserter.is(getAudioFocusAgent().getAudioFocusState(),
+                     State.LOST_FOCUS_TRANSIENT,
+                     "Should lose audio focus.");
+
+        info("- simulate gaining audio focus again -");
+        getAudioFocusAgent().changeAudioFocus(AudioManager.AUDIOFOCUS_GAIN);
+        waitUntilTabAudioPlayingStateChanged();
+        mAsserter.is(getAudioFocusAgent().getAudioFocusState(),
+                     State.OWN_FOCUS,
+                     "Should own audio focus.");
+
+        info("- simulate losing audio focus -");
+        getAudioFocusAgent().changeAudioFocus(AudioManager.AUDIOFOCUS_LOSS);
+        waitUntilTabAudioPlayingStateChanged();
+        mAsserter.is(getAudioFocusAgent().getAudioFocusState(),
+                     State.LOST_FOCUS,
+                     "Should abandon audio focus after media stopped playing.");
+
+        info("- close tab -");
+        closeAllTabs();
+    }
+
+    private void testSwitchTab() {
+        info("- load URL -");
+        final String MEDIA_URL = getAbsoluteUrl(mStringHelper.ROBOCOP_MEDIA_PLAYBACK_LOOP_URL);
+        loadUrlAndWait(MEDIA_URL);
+
+        info("- check whether audio starts playing -");
+        waitUntilTabAudioPlayingStateChanged();
+
+        info("- switch to the another tab -");
+        final String BLANK_URL = getAbsoluteUrl(mStringHelper.ROBOCOP_BLANK_PAGE_01_URL);
+        addTab(BLANK_URL);
+
+        info("-  should still own the audio focus -");
+        mAsserter.is(getAudioFocusAgent().getAudioFocusState(),
+                     State.OWN_FOCUS,
+                     "Should own audio focus.");
+
+        info("- close tab -");
+        closeAllTabs();
+    }
+
+    /**
+     * Testing tool functions
+     */
+    private void info(String msg) {
+        mAsserter.dumpLog(msg);
+    }
+
+    private AudioFocusAgent getAudioFocusAgent() {
+        return AudioFocusAgent.getInstance();
+    }
+
+    private void requestAudioFocus() {
+        getAudioFocusAgent().notifyStartedPlaying();
+        if (getAudioFocusAgent().getAudioFocusState() == State.OWN_FOCUS) {
+            return;
+        }
+
+        // Request audio focus might fail, depend on the andriod's audio mode.
+        waitForCondition(new Condition() {
+            @Override
+            public boolean isSatisfied() {
+                getAudioFocusAgent().notifyStartedPlaying();
+                return getAudioFocusAgent().getAudioFocusState() == State.OWN_FOCUS;
+            }
+        }, MAX_WAIT_MS);
+    }
+
+    private void waitUntilTabAudioPlayingStateChanged() {
+        final Tab tab = Tabs.getInstance().getSelectedTab();
+        waitForCondition(new Condition() {
+            @Override
+            public boolean isSatisfied() {
+                if (tab.isAudioPlaying() != mPrevTabAudioPlaying) {
+                    mPrevTabAudioPlaying = tab.isAudioPlaying();
+                    return true;
+                }
+                return false;
+            }
+        }, MAX_WAIT_MS);
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testMediaControl.java
@@ -0,0 +1,273 @@
+/* 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/. */
+
+package org.mozilla.gecko.tests;
+
+import org.mozilla.gecko.Actions;
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.Tab;
+import org.mozilla.gecko.Tabs;
+import org.mozilla.gecko.media.AudioFocusAgent;
+import org.mozilla.gecko.media.AudioFocusAgent.State;
+import org.mozilla.gecko.media.MediaControlService;
+
+import android.content.Intent;
+import android.content.Context;
+import android.media.AudioManager;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.os.Build;
+import android.service.notification.StatusBarNotification;
+
+import com.robotium.solo.Condition;
+
+public class testMediaControl extends BaseTest {
+    private Context mContext;
+    private int mPrevIcon = 0;
+    private boolean mPrevTabAudioPlaying = false;
+
+    public void testMediaControl() {
+        // The API to check system notification is available after version 23.
+        if (Build.VERSION.SDK_INT < 23) {
+            return;
+        }
+
+        info("- wait for gecko ready -");
+        blockForGeckoReady();
+
+        info("- setup testing memeber variable -");
+        setupForTesting();
+
+        info("- load URL -");
+        final String MEDIA_URL = getAbsoluteUrl(mStringHelper.ROBOCOP_MEDIA_PLAYBACK_LOOP_URL);
+        loadUrlAndWait(MEDIA_URL);
+
+        info("- check whether audio starts playing -");
+        checkIfMediaPlayingSuccess(true /* playing */);
+
+        info("- simulate media control pause -");
+        notifyMediaControlService(MediaControlService.ACTION_PAUSE);
+        checkIfMediaPlayingSuccess(false /* paused */);
+
+        info("- simulate media control resume -");
+        notifyMediaControlService(MediaControlService.ACTION_RESUME);
+        checkIfMediaPlayingSuccess(true /* playing */);
+
+        info("- simulate media control stop -");
+        notifyMediaControlService(MediaControlService.ACTION_STOP);
+        checkIfMediaPlayingSuccess(false /* paused */, true /* clear notification */);
+
+        info("- close tab -");
+        closeAllTabs();
+
+        info("- run next test : testNavigateOutThePage -");
+        testNavigateOutThePage();
+    }
+
+    private void testNavigateOutThePage() {
+        info("- load URL -");
+        final String MEDIA_URL = getAbsoluteUrl(mStringHelper.ROBOCOP_MEDIA_PLAYBACK_LOOP_URL);
+        loadUrlAndWait(MEDIA_URL);
+
+        info("- check whether audio starts playing -");
+        checkIfMediaPlayingSuccess(true /* playing */);
+
+        info("- navigate out the present page -");
+        final String BLANK_URL = getAbsoluteUrl(mStringHelper.ROBOCOP_BLANK_PAGE_01_URL);
+        loadUrlAndWait(BLANK_URL);
+        checkIfMediaPlayingSuccess(false /* paused */, true /* clear notification */);
+
+        info("- close tab -");
+        closeAllTabs();
+
+        info("- run next test : testAudioFocusChanged -");
+        testAudioFocusChanged();
+    }
+
+    private void testAudioFocusChanged() {
+        info("- load URL -");
+        final String MEDIA_URL = getAbsoluteUrl(mStringHelper.ROBOCOP_MEDIA_PLAYBACK_LOOP_URL);
+        loadUrlAndWait(MEDIA_URL);
+
+        info("- check whether audio starts playing -");
+        checkIfMediaPlayingSuccess(true /* playing */);
+
+        info("- simulate lose audio focus transiently -");
+        getAudioFocusAgent().changeAudioFocus(AudioManager.AUDIOFOCUS_LOSS_TRANSIENT);
+        checkIfMediaPlayingSuccess(false /* paused */);
+
+        info("- simulate gain audio focus again -");
+        getAudioFocusAgent().changeAudioFocus(AudioManager.AUDIOFOCUS_GAIN);
+        checkIfMediaPlayingSuccess(true /* playing */);
+
+        info("- simulate lose audio focus -");
+        getAudioFocusAgent().changeAudioFocus(AudioManager.AUDIOFOCUS_LOSS);
+        checkIfMediaPlayingSuccess(false /* paused */);
+
+        info("- close tab -");
+        closeAllTabs();
+
+        info("- run next test : testSwitchTab -");
+        testSwitchTab();
+    }
+
+    private void testSwitchTab() {
+        info("- load URL -");
+        final String MEDIA_URL = getAbsoluteUrl(mStringHelper.ROBOCOP_MEDIA_PLAYBACK_LOOP_URL);
+        loadUrlAndWait(MEDIA_URL);
+
+        info("- check whether audio starts playing -");
+        checkIfMediaPlayingSuccess(true /* playing */);
+
+        info("- switch to the another tab -");
+        final String BLANK_URL = getAbsoluteUrl(mStringHelper.ROBOCOP_BLANK_PAGE_01_URL);
+        addTab(BLANK_URL);
+
+        info("- the media control should still be displayed in status bar -");
+        checkDisplayedNotificationStates(true /* playing */);
+
+        info("- close tab -");
+        closeAllTabs();
+    }
+
+    /**
+     * Testing tool functions
+     */
+    private void info(String msg) {
+        mAsserter.dumpLog(msg);
+    }
+
+    private void setupForTesting() {
+        mContext = getInstrumentation().getTargetContext();
+    }
+
+    private void waitUntilNotificationUIChanged() {
+        waitForCondition(new Condition() {
+            @Override
+            public boolean isSatisfied() {
+                NotificationManager notificationManager = (NotificationManager)
+                    mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+                StatusBarNotification[] sbns = notificationManager.getActiveNotifications();
+                if (sbns.length == 1 &&
+                    sbns[0].getNotification().actions.length == 1) {
+                    // Ensure the UI has been changed.
+                    if (sbns[0].getNotification().actions[0].icon != mPrevIcon) {
+                        mPrevIcon = sbns[0].getNotification().actions[0].icon ;
+                        return true;
+                    }
+                }
+                return false;
+            }
+        }, MAX_WAIT_MS);
+    }
+
+    private void waitUntilTabAudioPlayingStateChanged() {
+        final Tab tab = Tabs.getInstance().getSelectedTab();
+        waitForCondition(new Condition() {
+            @Override
+            public boolean isSatisfied() {
+                if (tab.isAudioPlaying() != mPrevTabAudioPlaying) {
+                    mPrevTabAudioPlaying = tab.isAudioPlaying();
+                    return true;
+                }
+                return false;
+            }
+        }, MAX_WAIT_MS);
+    }
+
+    private void notifyMediaControlService(String action) {
+        Intent intent = new Intent(mContext, MediaControlService.class);
+        intent.setAction(action);
+        mContext.startService(intent);
+    }
+
+    private void checkIfMediaPlayingSuccess(boolean isTabPlaying) {
+        checkIfMediaPlayingSuccess(isTabPlaying, false);
+    }
+
+    private void checkIfMediaPlayingSuccess(boolean isTabPlaying,
+                                            boolean clearNotification) {
+        checkAudioFocusStateChanged(isTabPlaying);
+        checkMediaNotificationStatesChanged(isTabPlaying, clearNotification);
+    }
+
+    /**
+     * This method is used to check whether notification states are correct or
+     * not after notification UI changed.
+     */
+    private void checkMediaNotificationStatesChanged(boolean isTabPlaying,
+                                                     boolean clearNotification) {
+        waitUntilNotificationUIChanged();
+
+        final Tab tab = Tabs.getInstance().getSelectedTab();
+        mAsserter.ok(isTabPlaying == tab.isMediaPlaying(),
+                     "Checking the media playing state of tab, isTabPlaying = " + isTabPlaying,
+                     "Tab's media playing state is correct.");
+
+        if (clearNotification) {
+            checkIfNotificationBeCleared();
+        } else {
+            checkDisplayedNotificationStates(isTabPlaying);
+        }
+    }
+
+    private void checkDisplayedNotificationStates(boolean isTabPlaying) {
+        NotificationManager notificationManager = (NotificationManager)
+            mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+
+        StatusBarNotification[] sbns = notificationManager.getActiveNotifications();
+        mAsserter.is(sbns.length, 1,
+                     "Should only have one notification in system's status bar.");
+
+        Notification notification = sbns[0].getNotification();
+        mAsserter.is(notification.actions.length, 1,
+                     "Only has one action in notification.");
+
+        mAsserter.is(notification.actions[0].title,
+                     mContext.getString(isTabPlaying ? R.string.media_pause : R.string.media_play),
+                     "Action has correct title.");
+        mAsserter.is(notification.actions[0].icon,
+                     isTabPlaying ? R.drawable.ic_media_pause : R.drawable.ic_media_play,
+                     "Action has correct icon.");
+    }
+
+    private void checkIfNotificationBeCleared() {
+        NotificationManager notificationManager = (NotificationManager)
+            mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+        StatusBarNotification[] sbns = notificationManager.getActiveNotifications();
+        mAsserter.is(sbns.length, 0,
+                     "Should not have notification in system's status bar.");
+    }
+
+    /**
+     * This method is used to check whether audio focus state are correct or
+     * not after tab's audio playing state changed.
+     */
+    private void checkAudioFocusStateChanged(boolean isTabPlaying) {
+        waitUntilTabAudioPlayingStateChanged();
+
+        final Tab tab = Tabs.getInstance().getSelectedTab();
+        mAsserter.ok(isTabPlaying == tab.isAudioPlaying(),
+                     "Checking the audio playing state of tab, isTabPlaying = " + isTabPlaying,
+                     "Tab's audio playing state is correct.");
+
+        if (isTabPlaying) {
+            mAsserter.is(AudioFocusAgent.getInstance().getAudioFocusState(),
+                         State.OWN_FOCUS,
+                         "Audio focus state is correct.");
+        } else {
+            boolean isLostFocus =
+                AudioFocusAgent.getInstance().getAudioFocusState().equals(State.LOST_FOCUS) ||
+                AudioFocusAgent.getInstance().getAudioFocusState().equals(State.LOST_FOCUS_TRANSIENT);
+            mAsserter.ok(isLostFocus,
+                         "Checking the audio focus when the tab is not playing",
+                         "Audio focus state is correct.");
+        }
+    }
+
+    private AudioFocusAgent getAudioFocusAgent() {
+        return AudioFocusAgent.getInstance();
+    }
+}